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