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