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