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