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