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