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