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