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