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