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