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