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