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