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