1 /* 2 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt; 27 28 import java.awt.event.*; 29 30 import java.awt.peer.ComponentPeer; 31 32 import java.lang.ref.WeakReference; 33 import java.lang.reflect.InvocationTargetException; 34 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 38 import java.util.EmptyStackException; 39 40 import sun.awt.dnd.SunDropTargetEvent; 41 import sun.util.logging.PlatformLogger; 42 43 import sun.awt.AppContext; 44 import sun.awt.AWTAutoShutdown; 45 import sun.awt.PeerEvent; 46 import sun.awt.SunToolkit; 47 import sun.awt.EventQueueItem; 48 import sun.awt.AWTAccessor; 49 50 import java.util.concurrent.locks.Condition; 51 import java.util.concurrent.locks.Lock; 52 import java.util.concurrent.atomic.AtomicInteger; 53 54 import java.security.AccessControlContext; 55 import java.security.ProtectionDomain; 56 57 import sun.misc.SharedSecrets; 58 import sun.misc.JavaSecurityAccess; 59 60 /** 61 * <code>EventQueue</code> is a platform-independent class 62 * that queues events, both from the underlying peer classes 63 * and from trusted application classes. 64 * <p> 65 * It encapsulates asynchronous event dispatch machinery which 66 * extracts events from the queue and dispatches them by calling 67 * {@link #dispatchEvent(AWTEvent) dispatchEvent(AWTEvent)} method 68 * on this <code>EventQueue</code> with the event to be dispatched 69 * as an argument. The particular behavior of this machinery is 70 * implementation-dependent. The only requirements are that events 71 * which were actually enqueued to this queue (note that events 72 * being posted to the <code>EventQueue</code> can be coalesced) 73 * are dispatched: 74 * <dl> 75 * <dt> Sequentially. 76 * <dd> That is, it is not permitted that several events from 77 * this queue are dispatched simultaneously. 78 * <dt> In the same order as they are enqueued. 79 * <dd> That is, if <code>AWTEvent</code> A is enqueued 80 * to the <code>EventQueue</code> before 81 * <code>AWTEvent</code> B then event B will not be 82 * dispatched before event A. 83 * </dl> 84 * <p> 85 * Some browsers partition applets in different code bases into 86 * separate contexts, and establish walls between these contexts. 87 * In such a scenario, there will be one <code>EventQueue</code> 88 * per context. Other browsers place all applets into the same 89 * context, implying that there will be only a single, global 90 * <code>EventQueue</code> for all applets. This behavior is 91 * implementation-dependent. Consult your browser's documentation 92 * for more information. 93 * <p> 94 * For information on the threading issues of the event dispatch 95 * machinery, see <a href="doc-files/AWTThreadIssues.html#Autoshutdown" 96 * >AWT Threading Issues</a>. 97 * 98 * @author Thomas Ball 99 * @author Fred Ecks 100 * @author David Mendenhall 101 * 102 * @since 1.1 103 */ 104 public class EventQueue { 105 private static final AtomicInteger threadInitNumber = new AtomicInteger(0); 106 107 private static final int LOW_PRIORITY = 0; 108 private static final int NORM_PRIORITY = 1; 109 private static final int HIGH_PRIORITY = 2; 110 private static final int ULTIMATE_PRIORITY = 3; 111 112 private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1; 113 114 /* 115 * We maintain one Queue for each priority that the EventQueue supports. 116 * That is, the EventQueue object is actually implemented as 117 * NUM_PRIORITIES queues and all Events on a particular internal Queue 118 * have identical priority. Events are pulled off the EventQueue starting 119 * with the Queue of highest priority. We progress in decreasing order 120 * across all Queues. 121 */ 122 private Queue[] queues = new Queue[NUM_PRIORITIES]; 123 124 /* 125 * The next EventQueue on the stack, or null if this EventQueue is 126 * on the top of the stack. If nextQueue is non-null, requests to post 127 * an event are forwarded to nextQueue. 128 */ 129 private EventQueue nextQueue; 130 131 /* 132 * The previous EventQueue on the stack, or null if this is the 133 * "base" EventQueue. 134 */ 135 private EventQueue previousQueue; 136 137 /* 138 * A single lock to synchronize the push()/pop() and related operations with 139 * all the EventQueues from the AppContext. Synchronization on any particular 140 * event queue(s) is not enough: we should lock the whole stack. 141 */ 142 private final Lock pushPopLock; 143 private final Condition pushPopCond; 144 145 /* 146 * Dummy runnable to wake up EDT from getNextEvent() after 147 push/pop is performed 148 */ 149 private final static Runnable dummyRunnable = new Runnable() { 150 public void run() { 151 } 152 }; 153 154 private EventDispatchThread dispatchThread; 155 156 private final ThreadGroup threadGroup = 157 Thread.currentThread().getThreadGroup(); 158 private final ClassLoader classLoader = 159 Thread.currentThread().getContextClassLoader(); 160 161 /* 162 * The time stamp of the last dispatched InputEvent or ActionEvent. 163 */ 164 private long mostRecentEventTime = System.currentTimeMillis(); 165 166 /** 167 * The modifiers field of the current event, if the current event is an 168 * InputEvent or ActionEvent. 169 */ 170 private WeakReference currentEvent; 171 172 /* 173 * Non-zero if a thread is waiting in getNextEvent(int) for an event of 174 * a particular ID to be posted to the queue. 175 */ 176 private volatile int waitForID; 177 178 private final String name = "AWT-EventQueue-" + threadInitNumber.getAndIncrement(); 179 180 private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventQueue"); 181 182 static { 183 AWTAccessor.setEventQueueAccessor( 184 new AWTAccessor.EventQueueAccessor() { 185 public Thread getDispatchThread(EventQueue eventQueue) { 186 return eventQueue.getDispatchThread(); 187 } 188 public boolean isDispatchThreadImpl(EventQueue eventQueue) { 189 return eventQueue.isDispatchThreadImpl(); 190 } 191 }); 192 } 193 194 public EventQueue() { 195 for (int i = 0; i < NUM_PRIORITIES; i++) { 196 queues[i] = new Queue(); 197 } 198 /* 199 * NOTE: if you ever have to start the associated event dispatch 200 * thread at this point, be aware of the following problem: 201 * If this EventQueue instance is created in 202 * SunToolkit.createNewAppContext() the started dispatch thread 203 * may call AppContext.getAppContext() before createNewAppContext() 204 * completes thus causing mess in thread group to appcontext mapping. 205 */ 206 207 pushPopLock = (Lock)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_LOCK_KEY); 208 pushPopCond = (Condition)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_COND_KEY); 209 } 210 211 /** 212 * Posts a 1.1-style event to the <code>EventQueue</code>. 213 * If there is an existing event on the queue with the same ID 214 * and event source, the source <code>Component</code>'s 215 * <code>coalesceEvents</code> method will be called. 216 * 217 * @param theEvent an instance of <code>java.awt.AWTEvent</code>, 218 * or a subclass of it 219 * @throws NullPointerException if <code>theEvent</code> is <code>null</code> 220 */ 221 public void postEvent(AWTEvent theEvent) { 222 SunToolkit.flushPendingEvents(); 223 postEventPrivate(theEvent); 224 } 225 226 /** 227 * Posts a 1.1-style event to the <code>EventQueue</code>. 228 * If there is an existing event on the queue with the same ID 229 * and event source, the source <code>Component</code>'s 230 * <code>coalesceEvents</code> method will be called. 231 * 232 * @param theEvent an instance of <code>java.awt.AWTEvent</code>, 233 * or a subclass of it 234 */ 235 private final void postEventPrivate(AWTEvent theEvent) { 236 theEvent.isPosted = true; 237 pushPopLock.lock(); 238 try { 239 if (nextQueue != null) { 240 // Forward the event to the top of EventQueue stack 241 nextQueue.postEventPrivate(theEvent); 242 return; 243 } 244 if (dispatchThread == null) { 245 if (theEvent.getSource() == AWTAutoShutdown.getInstance()) { 246 return; 247 } else { 248 initDispatchThread(); 249 } 250 } 251 postEvent(theEvent, getPriority(theEvent)); 252 } finally { 253 pushPopLock.unlock(); 254 } 255 } 256 257 private static int getPriority(AWTEvent theEvent) { 258 if (theEvent instanceof PeerEvent) { 259 PeerEvent peerEvent = (PeerEvent)theEvent; 260 if ((peerEvent.getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) { 261 return ULTIMATE_PRIORITY; 262 } 263 if ((peerEvent.getFlags() & PeerEvent.PRIORITY_EVENT) != 0) { 264 return HIGH_PRIORITY; 265 } 266 if ((peerEvent.getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) { 267 return LOW_PRIORITY; 268 } 269 } 270 int id = theEvent.getID(); 271 if ((id >= PaintEvent.PAINT_FIRST) && (id <= PaintEvent.PAINT_LAST)) { 272 return LOW_PRIORITY; 273 } 274 return NORM_PRIORITY; 275 } 276 277 /** 278 * Posts the event to the internal Queue of specified priority, 279 * coalescing as appropriate. 280 * 281 * @param theEvent an instance of <code>java.awt.AWTEvent</code>, 282 * or a subclass of it 283 * @param priority the desired priority of the event 284 */ 285 private void postEvent(AWTEvent theEvent, int priority) { 286 if (coalesceEvent(theEvent, priority)) { 287 return; 288 } 289 290 EventQueueItem newItem = new EventQueueItem(theEvent); 291 292 cacheEQItem(newItem); 293 294 boolean notifyID = (theEvent.getID() == this.waitForID); 295 296 if (queues[priority].head == null) { 297 boolean shouldNotify = noEvents(); 298 queues[priority].head = queues[priority].tail = newItem; 299 300 if (shouldNotify) { 301 if (theEvent.getSource() != AWTAutoShutdown.getInstance()) { 302 AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); 303 } 304 pushPopCond.signalAll(); 305 } else if (notifyID) { 306 pushPopCond.signalAll(); 307 } 308 } else { 309 // The event was not coalesced or has non-Component source. 310 // Insert it at the end of the appropriate Queue. 311 queues[priority].tail.next = newItem; 312 queues[priority].tail = newItem; 313 if (notifyID) { 314 pushPopCond.signalAll(); 315 } 316 } 317 } 318 319 private boolean coalescePaintEvent(PaintEvent e) { 320 ComponentPeer sourcePeer = ((Component)e.getSource()).peer; 321 if (sourcePeer != null) { 322 sourcePeer.coalescePaintEvent(e); 323 } 324 EventQueueItem[] cache = ((Component)e.getSource()).eventCache; 325 if (cache == null) { 326 return false; 327 } 328 int index = eventToCacheIndex(e); 329 330 if (index != -1 && cache[index] != null) { 331 PaintEvent merged = mergePaintEvents(e, (PaintEvent)cache[index].event); 332 if (merged != null) { 333 cache[index].event = merged; 334 return true; 335 } 336 } 337 return false; 338 } 339 340 private PaintEvent mergePaintEvents(PaintEvent a, PaintEvent b) { 341 Rectangle aRect = a.getUpdateRect(); 342 Rectangle bRect = b.getUpdateRect(); 343 if (bRect.contains(aRect)) { 344 return b; 345 } 346 if (aRect.contains(bRect)) { 347 return a; 348 } 349 return null; 350 } 351 352 private boolean coalesceMouseEvent(MouseEvent e) { 353 EventQueueItem[] cache = ((Component)e.getSource()).eventCache; 354 if (cache == null) { 355 return false; 356 } 357 int index = eventToCacheIndex(e); 358 if (index != -1 && cache[index] != null) { 359 cache[index].event = e; 360 return true; 361 } 362 return false; 363 } 364 365 private boolean coalescePeerEvent(PeerEvent e) { 366 EventQueueItem[] cache = ((Component)e.getSource()).eventCache; 367 if (cache == null) { 368 return false; 369 } 370 int index = eventToCacheIndex(e); 371 if (index != -1 && cache[index] != null) { 372 e = e.coalesceEvents((PeerEvent)cache[index].event); 373 if (e != null) { 374 cache[index].event = e; 375 return true; 376 } else { 377 cache[index] = null; 378 } 379 } 380 return false; 381 } 382 383 /* 384 * Should avoid of calling this method by any means 385 * as it's working time is dependant on EQ length. 386 * In the wors case this method alone can slow down the entire application 387 * 10 times by stalling the Event processing. 388 * Only here by backward compatibility reasons. 389 */ 390 private boolean coalesceOtherEvent(AWTEvent e, int priority) { 391 int id = e.getID(); 392 Component source = (Component)e.getSource(); 393 for (EventQueueItem entry = queues[priority].head; 394 entry != null; entry = entry.next) 395 { 396 // Give Component.coalesceEvents a chance 397 if (entry.event.getSource() == source && entry.event.getID() == id) { 398 AWTEvent coalescedEvent = source.coalesceEvents( 399 entry.event, e); 400 if (coalescedEvent != null) { 401 entry.event = coalescedEvent; 402 return true; 403 } 404 } 405 } 406 return false; 407 } 408 409 private boolean coalesceEvent(AWTEvent e, int priority) { 410 if (!(e.getSource() instanceof Component)) { 411 return false; 412 } 413 if (e instanceof PeerEvent) { 414 return coalescePeerEvent((PeerEvent)e); 415 } 416 // The worst case 417 if (((Component)e.getSource()).isCoalescingEnabled() 418 && coalesceOtherEvent(e, priority)) 419 { 420 return true; 421 } 422 if (e instanceof PaintEvent) { 423 return coalescePaintEvent((PaintEvent)e); 424 } 425 if (e instanceof MouseEvent) { 426 return coalesceMouseEvent((MouseEvent)e); 427 } 428 return false; 429 } 430 431 private void cacheEQItem(EventQueueItem entry) { 432 int index = eventToCacheIndex(entry.event); 433 if (index != -1 && entry.event.getSource() instanceof Component) { 434 Component source = (Component)entry.event.getSource(); 435 if (source.eventCache == null) { 436 source.eventCache = new EventQueueItem[CACHE_LENGTH]; 437 } 438 source.eventCache[index] = entry; 439 } 440 } 441 442 private void uncacheEQItem(EventQueueItem entry) { 443 int index = eventToCacheIndex(entry.event); 444 if (index != -1 && entry.event.getSource() instanceof Component) { 445 Component source = (Component)entry.event.getSource(); 446 if (source.eventCache == null) { 447 return; 448 } 449 source.eventCache[index] = null; 450 } 451 } 452 453 private static final int PAINT = 0; 454 private static final int UPDATE = 1; 455 private static final int MOVE = 2; 456 private static final int DRAG = 3; 457 private static final int PEER = 4; 458 private static final int CACHE_LENGTH = 5; 459 460 private static int eventToCacheIndex(AWTEvent e) { 461 switch(e.getID()) { 462 case PaintEvent.PAINT: 463 return PAINT; 464 case PaintEvent.UPDATE: 465 return UPDATE; 466 case MouseEvent.MOUSE_MOVED: 467 return MOVE; 468 case MouseEvent.MOUSE_DRAGGED: 469 // Return -1 for SunDropTargetEvent since they are usually synchronous 470 // and we don't want to skip them by coalescing with MouseEvent or other drag events 471 return e instanceof SunDropTargetEvent ? -1 : DRAG; 472 default: 473 return e instanceof PeerEvent ? PEER : -1; 474 } 475 } 476 477 /** 478 * Returns whether an event is pending on any of the separate 479 * Queues. 480 * @return whether an event is pending on any of the separate Queues 481 */ 482 private boolean noEvents() { 483 for (int i = 0; i < NUM_PRIORITIES; i++) { 484 if (queues[i].head != null) { 485 return false; 486 } 487 } 488 489 return true; 490 } 491 492 /** 493 * Removes an event from the <code>EventQueue</code> and 494 * returns it. This method will block until an event has 495 * been posted by another thread. 496 * @return the next <code>AWTEvent</code> 497 * @exception InterruptedException 498 * if any thread has interrupted this thread 499 */ 500 public AWTEvent getNextEvent() throws InterruptedException { 501 do { 502 /* 503 * SunToolkit.flushPendingEvents must be called outside 504 * of the synchronized block to avoid deadlock when 505 * event queues are nested with push()/pop(). 506 */ 507 SunToolkit.flushPendingEvents(); 508 pushPopLock.lock(); 509 try { 510 AWTEvent event = getNextEventPrivate(); 511 if (event != null) { 512 return event; 513 } 514 AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread); 515 pushPopCond.await(); 516 } finally { 517 pushPopLock.unlock(); 518 } 519 } while(true); 520 } 521 522 /* 523 * Must be called under the lock. Doesn't call flushPendingEvents() 524 */ 525 AWTEvent getNextEventPrivate() throws InterruptedException { 526 for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { 527 if (queues[i].head != null) { 528 EventQueueItem entry = queues[i].head; 529 queues[i].head = entry.next; 530 if (entry.next == null) { 531 queues[i].tail = null; 532 } 533 uncacheEQItem(entry); 534 return entry.event; 535 } 536 } 537 return null; 538 } 539 540 AWTEvent getNextEvent(int id) throws InterruptedException { 541 do { 542 /* 543 * SunToolkit.flushPendingEvents must be called outside 544 * of the synchronized block to avoid deadlock when 545 * event queues are nested with push()/pop(). 546 */ 547 SunToolkit.flushPendingEvents(); 548 pushPopLock.lock(); 549 try { 550 for (int i = 0; i < NUM_PRIORITIES; i++) { 551 for (EventQueueItem entry = queues[i].head, prev = null; 552 entry != null; prev = entry, entry = entry.next) 553 { 554 if (entry.event.getID() == id) { 555 if (prev == null) { 556 queues[i].head = entry.next; 557 } else { 558 prev.next = entry.next; 559 } 560 if (queues[i].tail == entry) { 561 queues[i].tail = prev; 562 } 563 uncacheEQItem(entry); 564 return entry.event; 565 } 566 } 567 } 568 waitForID = id; 569 pushPopCond.await(); 570 waitForID = 0; 571 } finally { 572 pushPopLock.unlock(); 573 } 574 } while(true); 575 } 576 577 /** 578 * Returns the first event on the <code>EventQueue</code> 579 * without removing it. 580 * @return the first event 581 */ 582 public AWTEvent peekEvent() { 583 pushPopLock.lock(); 584 try { 585 for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { 586 if (queues[i].head != null) { 587 return queues[i].head.event; 588 } 589 } 590 } finally { 591 pushPopLock.unlock(); 592 } 593 594 return null; 595 } 596 597 /** 598 * Returns the first event with the specified id, if any. 599 * @param id the id of the type of event desired 600 * @return the first event of the specified id or <code>null</code> 601 * if there is no such event 602 */ 603 public AWTEvent peekEvent(int id) { 604 pushPopLock.lock(); 605 try { 606 for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { 607 EventQueueItem q = queues[i].head; 608 for (; q != null; q = q.next) { 609 if (q.event.getID() == id) { 610 return q.event; 611 } 612 } 613 } 614 } finally { 615 pushPopLock.unlock(); 616 } 617 618 return null; 619 } 620 621 private static final JavaSecurityAccess javaSecurityAccess = 622 SharedSecrets.getJavaSecurityAccess(); 623 624 /** 625 * Dispatches an event. The manner in which the event is 626 * dispatched depends upon the type of the event and the 627 * type of the event's source object: 628 * <p> </p> 629 * <table border=1 summary="Event types, source types, and dispatch methods"> 630 * <tr> 631 * <th>Event Type</th> 632 * <th>Source Type</th> 633 * <th>Dispatched To</th> 634 * </tr> 635 * <tr> 636 * <td>ActiveEvent</td> 637 * <td>Any</td> 638 * <td>event.dispatch()</td> 639 * </tr> 640 * <tr> 641 * <td>Other</td> 642 * <td>Component</td> 643 * <td>source.dispatchEvent(AWTEvent)</td> 644 * </tr> 645 * <tr> 646 * <td>Other</td> 647 * <td>MenuComponent</td> 648 * <td>source.dispatchEvent(AWTEvent)</td> 649 * </tr> 650 * <tr> 651 * <td>Other</td> 652 * <td>Other</td> 653 * <td>No action (ignored)</td> 654 * </tr> 655 * </table> 656 * <p> </p> 657 * @param event an instance of <code>java.awt.AWTEvent</code>, 658 * or a subclass of it 659 * @throws NullPointerException if <code>event</code> is <code>null</code> 660 * @since 1.2 661 */ 662 protected void dispatchEvent(final AWTEvent event) { 663 final Object src = event.getSource(); 664 final PrivilegedAction<Void> action = new PrivilegedAction<Void>() { 665 public Void run() { 666 dispatchEventImpl(event, src); 667 return null; 668 } 669 }; 670 671 final AccessControlContext stack = AccessController.getContext(); 672 final AccessControlContext srcAcc = getAccessControlContextFrom(src); 673 final AccessControlContext eventAcc = event.getAccessControlContext(); 674 if (srcAcc == null) { 675 javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc); 676 } else { 677 javaSecurityAccess.doIntersectionPrivilege( 678 new PrivilegedAction<Void>() { 679 public Void run() { 680 javaSecurityAccess.doIntersectionPrivilege(action, eventAcc); 681 return null; 682 } 683 }, stack, srcAcc); 684 } 685 } 686 687 private static AccessControlContext getAccessControlContextFrom(Object src) { 688 return src instanceof Component ? 689 ((Component)src).getAccessControlContext() : 690 src instanceof MenuComponent ? 691 ((MenuComponent)src).getAccessControlContext() : 692 src instanceof TrayIcon ? 693 ((TrayIcon)src).getAccessControlContext() : 694 null; 695 } 696 697 /** 698 * Called from dispatchEvent() under a correct AccessControlContext 699 */ 700 private void dispatchEventImpl(final AWTEvent event, final Object src) { 701 event.isPosted = true; 702 if (event instanceof ActiveEvent) { 703 // This could become the sole method of dispatching in time. 704 setCurrentEventAndMostRecentTimeImpl(event); 705 ((ActiveEvent)event).dispatch(); 706 } else if (src instanceof Component) { 707 ((Component)src).dispatchEvent(event); 708 event.dispatched(); 709 } else if (src instanceof MenuComponent) { 710 ((MenuComponent)src).dispatchEvent(event); 711 } else if (src instanceof TrayIcon) { 712 ((TrayIcon)src).dispatchEvent(event); 713 } else if (src instanceof AWTAutoShutdown) { 714 if (noEvents()) { 715 dispatchThread.stopDispatching(); 716 } 717 } else { 718 if (eventLog.isLoggable(PlatformLogger.FINE)) { 719 eventLog.fine("Unable to dispatch event: " + event); 720 } 721 } 722 } 723 724 /** 725 * Returns the timestamp of the most recent event that had a timestamp, and 726 * that was dispatched from the <code>EventQueue</code> associated with the 727 * calling thread. If an event with a timestamp is currently being 728 * dispatched, its timestamp will be returned. If no events have yet 729 * been dispatched, the EventQueue's initialization time will be 730 * returned instead.In the current version of 731 * the JDK, only <code>InputEvent</code>s, 732 * <code>ActionEvent</code>s, and <code>InvocationEvent</code>s have 733 * timestamps; however, future versions of the JDK may add timestamps to 734 * additional event types. Note that this method should only be invoked 735 * from an application's {@link #isDispatchThread event dispatching thread}. 736 * If this method is 737 * invoked from another thread, the current system time (as reported by 738 * <code>System.currentTimeMillis()</code>) will be returned instead. 739 * 740 * @return the timestamp of the last <code>InputEvent</code>, 741 * <code>ActionEvent</code>, or <code>InvocationEvent</code> to be 742 * dispatched, or <code>System.currentTimeMillis()</code> if this 743 * method is invoked on a thread other than an event dispatching 744 * thread 745 * @see java.awt.event.InputEvent#getWhen 746 * @see java.awt.event.ActionEvent#getWhen 747 * @see java.awt.event.InvocationEvent#getWhen 748 * @see #isDispatchThread 749 * 750 * @since 1.4 751 */ 752 public static long getMostRecentEventTime() { 753 return Toolkit.getEventQueue().getMostRecentEventTimeImpl(); 754 } 755 private long getMostRecentEventTimeImpl() { 756 pushPopLock.lock(); 757 try { 758 return (Thread.currentThread() == dispatchThread) 759 ? mostRecentEventTime 760 : System.currentTimeMillis(); 761 } finally { 762 pushPopLock.unlock(); 763 } 764 } 765 766 /** 767 * @return most recent event time on all threads. 768 */ 769 long getMostRecentEventTimeEx() { 770 pushPopLock.lock(); 771 try { 772 return mostRecentEventTime; 773 } finally { 774 pushPopLock.unlock(); 775 } 776 } 777 778 /** 779 * Returns the the event currently being dispatched by the 780 * <code>EventQueue</code> associated with the calling thread. This is 781 * useful if a method needs access to the event, but was not designed to 782 * receive a reference to it as an argument. Note that this method should 783 * only be invoked from an application's event dispatching thread. If this 784 * method is invoked from another thread, null will be returned. 785 * 786 * @return the event currently being dispatched, or null if this method is 787 * invoked on a thread other than an event dispatching thread 788 * @since 1.4 789 */ 790 public static AWTEvent getCurrentEvent() { 791 return Toolkit.getEventQueue().getCurrentEventImpl(); 792 } 793 private AWTEvent getCurrentEventImpl() { 794 pushPopLock.lock(); 795 try { 796 return (Thread.currentThread() == dispatchThread) 797 ? ((AWTEvent)currentEvent.get()) 798 : null; 799 } finally { 800 pushPopLock.unlock(); 801 } 802 } 803 804 /** 805 * Replaces the existing <code>EventQueue</code> with the specified one. 806 * Any pending events are transferred to the new <code>EventQueue</code> 807 * for processing by it. 808 * 809 * @param newEventQueue an <code>EventQueue</code> 810 * (or subclass thereof) instance to be use 811 * @see java.awt.EventQueue#pop 812 * @throws NullPointerException if <code>newEventQueue</code> is <code>null</code> 813 * @since 1.2 814 */ 815 public void push(EventQueue newEventQueue) { 816 if (eventLog.isLoggable(PlatformLogger.FINE)) { 817 eventLog.fine("EventQueue.push(" + newEventQueue + ")"); 818 } 819 820 pushPopLock.lock(); 821 try { 822 EventQueue topQueue = this; 823 while (topQueue.nextQueue != null) { 824 topQueue = topQueue.nextQueue; 825 } 826 827 if ((topQueue.dispatchThread != null) && 828 (topQueue.dispatchThread.getEventQueue() == this)) 829 { 830 newEventQueue.dispatchThread = topQueue.dispatchThread; 831 topQueue.dispatchThread.setEventQueue(newEventQueue); 832 } 833 834 // Transfer all events forward to new EventQueue. 835 while (topQueue.peekEvent() != null) { 836 try { 837 // Use getNextEventPrivate() as it doesn't call flushPendingEvents() 838 newEventQueue.postEventPrivate(topQueue.getNextEventPrivate()); 839 } catch (InterruptedException ie) { 840 if (eventLog.isLoggable(PlatformLogger.FINE)) { 841 eventLog.fine("Interrupted push", ie); 842 } 843 } 844 } 845 846 // Wake up EDT waiting in getNextEvent(), so it can 847 // pick up a new EventQueue. Post the waking event before 848 // topQueue.nextQueue is assigned, otherwise the event would 849 // go newEventQueue 850 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable)); 851 852 newEventQueue.previousQueue = topQueue; 853 topQueue.nextQueue = newEventQueue; 854 855 AppContext appContext = AppContext.getAppContext(); 856 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == topQueue) { 857 appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue); 858 } 859 860 pushPopCond.signalAll(); 861 } finally { 862 pushPopLock.unlock(); 863 } 864 } 865 866 /** 867 * Stops dispatching events using this <code>EventQueue</code>. 868 * Any pending events are transferred to the previous 869 * <code>EventQueue</code> for processing. 870 * <p> 871 * Warning: To avoid deadlock, do not declare this method 872 * synchronized in a subclass. 873 * 874 * @exception EmptyStackException if no previous push was made 875 * on this <code>EventQueue</code> 876 * @see java.awt.EventQueue#push 877 * @since 1.2 878 */ 879 protected void pop() throws EmptyStackException { 880 if (eventLog.isLoggable(PlatformLogger.FINE)) { 881 eventLog.fine("EventQueue.pop(" + this + ")"); 882 } 883 884 pushPopLock.lock(); 885 try { 886 EventQueue topQueue = this; 887 while (topQueue.nextQueue != null) { 888 topQueue = topQueue.nextQueue; 889 } 890 EventQueue prevQueue = topQueue.previousQueue; 891 if (prevQueue == null) { 892 throw new EmptyStackException(); 893 } 894 895 topQueue.previousQueue = null; 896 prevQueue.nextQueue = null; 897 898 // Transfer all events back to previous EventQueue. 899 while (topQueue.peekEvent() != null) { 900 try { 901 prevQueue.postEventPrivate(topQueue.getNextEventPrivate()); 902 } catch (InterruptedException ie) { 903 if (eventLog.isLoggable(PlatformLogger.FINE)) { 904 eventLog.fine("Interrupted pop", ie); 905 } 906 } 907 } 908 909 if ((topQueue.dispatchThread != null) && 910 (topQueue.dispatchThread.getEventQueue() == this)) 911 { 912 prevQueue.dispatchThread = topQueue.dispatchThread; 913 topQueue.dispatchThread.setEventQueue(prevQueue); 914 } 915 916 AppContext appContext = AppContext.getAppContext(); 917 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) { 918 appContext.put(AppContext.EVENT_QUEUE_KEY, prevQueue); 919 } 920 921 // Wake up EDT waiting in getNextEvent(), so it can 922 // pick up a new EventQueue 923 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable)); 924 925 pushPopCond.signalAll(); 926 } finally { 927 pushPopLock.unlock(); 928 } 929 } 930 931 /** 932 * Creates a new {@code secondary loop} associated with this 933 * event queue. Use the {@link SecondaryLoop#enter} and 934 * {@link SecondaryLoop#exit} methods to start and stop the 935 * event loop and dispatch the events from this queue. 936 * 937 * @return secondaryLoop A new secondary loop object, which can 938 * be used to launch a new nested event 939 * loop and dispatch events from this queue 940 * 941 * @see SecondaryLoop#enter 942 * @see SecondaryLoop#exit 943 * 944 * @since 1.7 945 */ 946 public SecondaryLoop createSecondaryLoop() { 947 return createSecondaryLoop(null, null, 0); 948 } 949 950 SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) { 951 pushPopLock.lock(); 952 try { 953 if (nextQueue != null) { 954 // Forward the request to the top of EventQueue stack 955 return nextQueue.createSecondaryLoop(cond, filter, interval); 956 } 957 if (dispatchThread == null) { 958 initDispatchThread(); 959 } 960 return new WaitDispatchSupport(dispatchThread, cond, filter, interval); 961 } finally { 962 pushPopLock.unlock(); 963 } 964 } 965 966 /** 967 * Returns true if the calling thread is 968 * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s 969 * dispatch thread. Use this method to ensure that a particular 970 * task is being executed (or not being) there. 971 * <p> 972 * Note: use the {@link #invokeLater} or {@link #invokeAndWait} 973 * methods to execute a task in 974 * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s 975 * dispatch thread. 976 * <p> 977 * 978 * @return true if running in 979 * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s 980 * dispatch thread 981 * @see #invokeLater 982 * @see #invokeAndWait 983 * @see Toolkit#getSystemEventQueue 984 * @since 1.2 985 */ 986 public static boolean isDispatchThread() { 987 EventQueue eq = Toolkit.getEventQueue(); 988 return eq.isDispatchThreadImpl(); 989 } 990 991 final boolean isDispatchThreadImpl() { 992 EventQueue eq = this; 993 pushPopLock.lock(); 994 try { 995 EventQueue next = eq.nextQueue; 996 while (next != null) { 997 eq = next; 998 next = eq.nextQueue; 999 } 1000 return (Thread.currentThread() == eq.dispatchThread); 1001 } finally { 1002 pushPopLock.unlock(); 1003 } 1004 } 1005 1006 final void initDispatchThread() { 1007 pushPopLock.lock(); 1008 try { 1009 AppContext appContext = AppContext.getAppContext(); 1010 if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) { 1011 dispatchThread = AccessController.doPrivileged( 1012 new PrivilegedAction<EventDispatchThread>() { 1013 public EventDispatchThread run() { 1014 EventDispatchThread t = 1015 new EventDispatchThread(threadGroup, 1016 name, 1017 EventQueue.this); 1018 t.setContextClassLoader(classLoader); 1019 t.setPriority(Thread.NORM_PRIORITY + 1); 1020 t.setDaemon(false); 1021 return t; 1022 } 1023 } 1024 ); 1025 AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); 1026 dispatchThread.start(); 1027 } 1028 } finally { 1029 pushPopLock.unlock(); 1030 } 1031 } 1032 1033 final boolean detachDispatchThread(EventDispatchThread edt, boolean forceDetach) { 1034 /* 1035 * This synchronized block is to secure that the event dispatch 1036 * thread won't die in the middle of posting a new event to the 1037 * associated event queue. It is important because we notify 1038 * that the event dispatch thread is busy after posting a new event 1039 * to its queue, so the EventQueue.dispatchThread reference must 1040 * be valid at that point. 1041 */ 1042 pushPopLock.lock(); 1043 try { 1044 if (edt == dispatchThread) { 1045 /* 1046 * Don't detach the thread if any events are pending. Not 1047 * sure if it's a possible scenario, though. 1048 * 1049 * Fix for 4648733. Check both the associated java event 1050 * queue and the PostEventQueue. 1051 */ 1052 if (!forceDetach && (peekEvent() != null) || !SunToolkit.isPostEventQueueEmpty()) { 1053 return false; 1054 } 1055 dispatchThread = null; 1056 } 1057 AWTAutoShutdown.getInstance().notifyThreadFree(edt); 1058 return true; 1059 } finally { 1060 pushPopLock.unlock(); 1061 } 1062 } 1063 1064 /* 1065 * Gets the <code>EventDispatchThread</code> for this 1066 * <code>EventQueue</code>. 1067 * @return the event dispatch thread associated with this event queue 1068 * or <code>null</code> if this event queue doesn't have a 1069 * working thread associated with it 1070 * @see java.awt.EventQueue#initDispatchThread 1071 * @see java.awt.EventQueue#detachDispatchThread 1072 */ 1073 final EventDispatchThread getDispatchThread() { 1074 pushPopLock.lock(); 1075 try { 1076 return dispatchThread; 1077 } finally { 1078 pushPopLock.unlock(); 1079 } 1080 } 1081 1082 /* 1083 * Removes any pending events for the specified source object. 1084 * If removeAllEvents parameter is <code>true</code> then all 1085 * events for the specified source object are removed, if it 1086 * is <code>false</code> then <code>SequencedEvent</code>, <code>SentEvent</code>, 1087 * <code>FocusEvent</code>, <code>WindowEvent</code>, <code>KeyEvent</code>, 1088 * and <code>InputMethodEvent</code> are kept in the queue, but all other 1089 * events are removed. 1090 * 1091 * This method is normally called by the source's 1092 * <code>removeNotify</code> method. 1093 */ 1094 final void removeSourceEvents(Object source, boolean removeAllEvents) { 1095 SunToolkit.flushPendingEvents(); 1096 pushPopLock.lock(); 1097 try { 1098 for (int i = 0; i < NUM_PRIORITIES; i++) { 1099 EventQueueItem entry = queues[i].head; 1100 EventQueueItem prev = null; 1101 while (entry != null) { 1102 if ((entry.event.getSource() == source) 1103 && (removeAllEvents 1104 || ! (entry.event instanceof SequencedEvent 1105 || entry.event instanceof SentEvent 1106 || entry.event instanceof FocusEvent 1107 || entry.event instanceof WindowEvent 1108 || entry.event instanceof KeyEvent 1109 || entry.event instanceof InputMethodEvent))) 1110 { 1111 if (entry.event instanceof SequencedEvent) { 1112 ((SequencedEvent)entry.event).dispose(); 1113 } 1114 if (entry.event instanceof SentEvent) { 1115 ((SentEvent)entry.event).dispose(); 1116 } 1117 if (prev == null) { 1118 queues[i].head = entry.next; 1119 } else { 1120 prev.next = entry.next; 1121 } 1122 uncacheEQItem(entry); 1123 } else { 1124 prev = entry; 1125 } 1126 entry = entry.next; 1127 } 1128 queues[i].tail = prev; 1129 } 1130 } finally { 1131 pushPopLock.unlock(); 1132 } 1133 } 1134 1135 static void setCurrentEventAndMostRecentTime(AWTEvent e) { 1136 Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e); 1137 } 1138 private void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) { 1139 pushPopLock.lock(); 1140 try { 1141 if (Thread.currentThread() != dispatchThread) { 1142 return; 1143 } 1144 1145 currentEvent = new WeakReference(e); 1146 1147 // This series of 'instanceof' checks should be replaced with a 1148 // polymorphic type (for example, an interface which declares a 1149 // getWhen() method). However, this would require us to make such 1150 // a type public, or to place it in sun.awt. Both of these approaches 1151 // have been frowned upon. So for now, we hack. 1152 // 1153 // In tiger, we will probably give timestamps to all events, so this 1154 // will no longer be an issue. 1155 long mostRecentEventTime2 = Long.MIN_VALUE; 1156 if (e instanceof InputEvent) { 1157 InputEvent ie = (InputEvent)e; 1158 mostRecentEventTime2 = ie.getWhen(); 1159 } else if (e instanceof InputMethodEvent) { 1160 InputMethodEvent ime = (InputMethodEvent)e; 1161 mostRecentEventTime2 = ime.getWhen(); 1162 } else if (e instanceof ActionEvent) { 1163 ActionEvent ae = (ActionEvent)e; 1164 mostRecentEventTime2 = ae.getWhen(); 1165 } else if (e instanceof InvocationEvent) { 1166 InvocationEvent ie = (InvocationEvent)e; 1167 mostRecentEventTime2 = ie.getWhen(); 1168 } 1169 mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2); 1170 } finally { 1171 pushPopLock.unlock(); 1172 } 1173 } 1174 1175 /** 1176 * Causes <code>runnable</code> to have its <code>run</code> 1177 * method called in the {@link #isDispatchThread dispatch thread} of 1178 * {@link Toolkit#getSystemEventQueue the system EventQueue}. 1179 * This will happen after all pending events are processed. 1180 * 1181 * @param runnable the <code>Runnable</code> whose <code>run</code> 1182 * method should be executed 1183 * asynchronously in the 1184 * {@link #isDispatchThread event dispatch thread} 1185 * of {@link Toolkit#getSystemEventQueue the system EventQueue} 1186 * @see #invokeAndWait 1187 * @see Toolkit#getSystemEventQueue 1188 * @see #isDispatchThread 1189 * @since 1.2 1190 */ 1191 public static void invokeLater(Runnable runnable) { 1192 Toolkit.getEventQueue().postEvent( 1193 new InvocationEvent(Toolkit.getDefaultToolkit(), runnable)); 1194 } 1195 1196 /** 1197 * Causes <code>runnable</code> to have its <code>run</code> 1198 * method called in the {@link #isDispatchThread dispatch thread} of 1199 * {@link Toolkit#getSystemEventQueue the system EventQueue}. 1200 * This will happen after all pending events are processed. 1201 * The call blocks until this has happened. This method 1202 * will throw an Error if called from the 1203 * {@link #isDispatchThread event dispatcher thread}. 1204 * 1205 * @param runnable the <code>Runnable</code> whose <code>run</code> 1206 * method should be executed 1207 * synchronously in the 1208 * {@link #isDispatchThread event dispatch thread} 1209 * of {@link Toolkit#getSystemEventQueue the system EventQueue} 1210 * @exception InterruptedException if any thread has 1211 * interrupted this thread 1212 * @exception InvocationTargetException if an throwable is thrown 1213 * when running <code>runnable</code> 1214 * @see #invokeLater 1215 * @see Toolkit#getSystemEventQueue 1216 * @see #isDispatchThread 1217 * @since 1.2 1218 */ 1219 public static void invokeAndWait(Runnable runnable) 1220 throws InterruptedException, InvocationTargetException { 1221 1222 if (EventQueue.isDispatchThread()) { 1223 throw new Error("Cannot call invokeAndWait from the event dispatcher thread"); 1224 } 1225 1226 class AWTInvocationLock {} 1227 Object lock = new AWTInvocationLock(); 1228 1229 InvocationEvent event = 1230 new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock, 1231 true); 1232 1233 synchronized (lock) { 1234 Toolkit.getEventQueue().postEvent(event); 1235 while (!event.isDispatched()) { 1236 lock.wait(); 1237 } 1238 } 1239 1240 Throwable eventThrowable = event.getThrowable(); 1241 if (eventThrowable != null) { 1242 throw new InvocationTargetException(eventThrowable); 1243 } 1244 } 1245 1246 /* 1247 * Called from PostEventQueue.postEvent to notify that a new event 1248 * appeared. First it proceeds to the EventQueue on the top of the 1249 * stack, then notifies the associated dispatch thread if it exists 1250 * or starts a new one otherwise. 1251 */ 1252 private void wakeup(boolean isShutdown) { 1253 pushPopLock.lock(); 1254 try { 1255 if (nextQueue != null) { 1256 // Forward call to the top of EventQueue stack. 1257 nextQueue.wakeup(isShutdown); 1258 } else if (dispatchThread != null) { 1259 pushPopCond.signalAll(); 1260 } else if (!isShutdown) { 1261 initDispatchThread(); 1262 } 1263 } finally { 1264 pushPopLock.unlock(); 1265 } 1266 } 1267 } 1268 1269 /** 1270 * The Queue object holds pointers to the beginning and end of one internal 1271 * queue. An EventQueue object is composed of multiple internal Queues, one 1272 * for each priority supported by the EventQueue. All Events on a particular 1273 * internal Queue have identical priority. 1274 */ 1275 class Queue { 1276 EventQueueItem head; 1277 EventQueueItem tail; 1278 }