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