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