1 /*
2 * Copyright (c) 1996, 2017, 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.misc.SharedSecrets;
51 import jdk.internal.misc.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} A is enqueued
73 * to the {@code EventQueue} before
74 * {@code AWTEvent} 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(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 static final 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 volatile PlatformLogger eventLog;
186
187 private static final PlatformLogger getEventLog() {
188 if(eventLog == null) {
189 eventLog = PlatformLogger.getLogger("java.awt.event.EventQueue");
190 }
191 return eventLog;
192 }
193
194 private static boolean fxAppThreadIsDispatchThread;
195
196 static {
197 AWTAccessor.setEventQueueAccessor(
198 new AWTAccessor.EventQueueAccessor() {
199 public Thread getDispatchThread(EventQueue eventQueue) {
200 return eventQueue.getDispatchThread();
201 }
202 public boolean isDispatchThreadImpl(EventQueue eventQueue) {
203 return eventQueue.isDispatchThreadImpl();
204 }
205 public void removeSourceEvents(EventQueue eventQueue,
206 Object source,
207 boolean removeAllEvents)
208 {
209 eventQueue.removeSourceEvents(source, removeAllEvents);
210 }
211 public boolean noEvents(EventQueue eventQueue) {
212 return eventQueue.noEvents();
213 }
214 public void wakeup(EventQueue eventQueue, boolean isShutdown) {
215 eventQueue.wakeup(isShutdown);
216 }
217 public void invokeAndWait(Object source, Runnable r)
218 throws InterruptedException, InvocationTargetException
219 {
220 EventQueue.invokeAndWait(source, r);
221 }
222 public void setFwDispatcher(EventQueue eventQueue,
223 FwDispatcher dispatcher) {
224 eventQueue.setFwDispatcher(dispatcher);
225 }
226
227 @Override
228 public long getMostRecentEventTime(EventQueue eventQueue) {
229 return eventQueue.getMostRecentEventTimeImpl();
230 }
231 });
232 AccessController.doPrivileged(new PrivilegedAction<Object>() {
233 public Object run() {
234 fxAppThreadIsDispatchThread =
235 "true".equals(System.getProperty("javafx.embed.singleThread"));
236 return null;
237 }
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 final AccessControlContext stack = AccessController.getContext();
737 final AccessControlContext srcAcc = getAccessControlContextFrom(src);
738 final AccessControlContext eventAcc = event.getAccessControlContext();
739 if (srcAcc == null) {
740 javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc);
741 } else {
742 javaSecurityAccess.doIntersectionPrivilege(
743 new PrivilegedAction<Void>() {
744 public Void run() {
745 javaSecurityAccess.doIntersectionPrivilege(action, eventAcc);
746 return null;
747 }
748 }, stack, srcAcc);
749 }
750 }
751
752 private static AccessControlContext getAccessControlContextFrom(Object src) {
753 return src instanceof Component ?
754 ((Component)src).getAccessControlContext() :
755 src instanceof MenuComponent ?
756 ((MenuComponent)src).getAccessControlContext() :
757 src instanceof TrayIcon ?
758 ((TrayIcon)src).getAccessControlContext() :
759 null;
760 }
761
762 /**
763 * Called from dispatchEvent() under a correct AccessControlContext
764 */
765 private void dispatchEventImpl(final AWTEvent event, final Object src) {
766 event.isPosted = true;
767 if (event instanceof ActiveEvent) {
768 // This could become the sole method of dispatching in time.
769 setCurrentEventAndMostRecentTimeImpl(event);
770 ((ActiveEvent)event).dispatch();
771 } else if (src instanceof Component) {
772 ((Component)src).dispatchEvent(event);
773 event.dispatched();
774 } else if (src instanceof MenuComponent) {
775 ((MenuComponent)src).dispatchEvent(event);
776 } else if (src instanceof TrayIcon) {
777 ((TrayIcon)src).dispatchEvent(event);
778 } else if (src instanceof AWTAutoShutdown) {
779 if (noEvents()) {
780 dispatchThread.stopDispatching();
781 }
782 } else {
783 if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
784 getEventLog().fine("Unable to dispatch event: " + event);
785 }
786 }
787 }
788
789 /**
790 * Returns the timestamp of the most recent event that had a timestamp, and
791 * that was dispatched from the {@code EventQueue} associated with the
792 * calling thread. If an event with a timestamp is currently being
793 * dispatched, its timestamp will be returned. If no events have yet
794 * been dispatched, the EventQueue's initialization time will be
795 * returned instead.In the current version of
796 * the JDK, only {@code InputEvent}s,
797 * {@code ActionEvent}s, and {@code InvocationEvent}s have
798 * timestamps; however, future versions of the JDK may add timestamps to
799 * additional event types. Note that this method should only be invoked
800 * from an application's {@link #isDispatchThread event dispatching thread}.
801 * If this method is
802 * invoked from another thread, the current system time (as reported by
803 * {@code System.currentTimeMillis()}) will be returned instead.
804 *
805 * @return the timestamp of the last {@code InputEvent},
806 * {@code ActionEvent}, or {@code InvocationEvent} to be
807 * dispatched, or {@code System.currentTimeMillis()} if this
808 * method is invoked on a thread other than an event dispatching
809 * thread
810 * @see java.awt.event.InputEvent#getWhen
811 * @see java.awt.event.ActionEvent#getWhen
812 * @see java.awt.event.InvocationEvent#getWhen
813 * @see #isDispatchThread
814 *
815 * @since 1.4
816 */
817 public static long getMostRecentEventTime() {
818 return Toolkit.getEventQueue().getMostRecentEventTimeImpl();
819 }
820 private long getMostRecentEventTimeImpl() {
821 pushPopLock.lock();
822 try {
823 return (Thread.currentThread() == dispatchThread)
824 ? mostRecentEventTime
825 : System.currentTimeMillis();
826 } finally {
827 pushPopLock.unlock();
828 }
829 }
830
831 /**
832 * @return most recent event time on all threads.
833 */
834 long getMostRecentEventTimeEx() {
835 pushPopLock.lock();
836 try {
837 return mostRecentEventTime;
838 } finally {
839 pushPopLock.unlock();
840 }
841 }
842
843 /**
844 * Returns the event currently being dispatched by the
845 * {@code EventQueue} associated with the calling thread. This is
846 * useful if a method needs access to the event, but was not designed to
847 * receive a reference to it as an argument. Note that this method should
848 * only be invoked from an application's event dispatching thread. If this
849 * method is invoked from another thread, null will be returned.
850 *
851 * @return the event currently being dispatched, or null if this method is
852 * invoked on a thread other than an event dispatching thread
853 * @since 1.4
854 */
855 public static AWTEvent getCurrentEvent() {
856 return Toolkit.getEventQueue().getCurrentEventImpl();
857 }
858 private AWTEvent getCurrentEventImpl() {
859 pushPopLock.lock();
860 try {
861 if (Thread.currentThread() == dispatchThread
862 || fxAppThreadIsDispatchThread) {
863 return (currentEvent != null)
864 ? currentEvent.get()
865 : null;
866 }
867 return null;
868 } finally {
869 pushPopLock.unlock();
870 }
871 }
872
873 /**
874 * Replaces the existing {@code EventQueue} with the specified one.
875 * Any pending events are transferred to the new {@code EventQueue}
876 * for processing by it.
877 *
878 * @param newEventQueue an {@code EventQueue}
879 * (or subclass thereof) instance to be use
880 * @see java.awt.EventQueue#pop
881 * @throws NullPointerException if {@code newEventQueue} is {@code null}
882 * @since 1.2
883 */
884 public void push(EventQueue newEventQueue) {
885 if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
886 getEventLog().fine("EventQueue.push(" + newEventQueue + ")");
887 }
888
889 pushPopLock.lock();
890 try {
891 EventQueue topQueue = this;
892 while (topQueue.nextQueue != null) {
893 topQueue = topQueue.nextQueue;
894 }
895 if (topQueue.fwDispatcher != null) {
896 throw new RuntimeException("push() to queue with fwDispatcher");
897 }
898 if ((topQueue.dispatchThread != null) &&
899 (topQueue.dispatchThread.getEventQueue() == this))
900 {
901 newEventQueue.dispatchThread = topQueue.dispatchThread;
902 topQueue.dispatchThread.setEventQueue(newEventQueue);
903 }
904
905 // Transfer all events forward to new EventQueue.
906 while (topQueue.peekEvent() != null) {
907 try {
908 // Use getNextEventPrivate() as it doesn't call flushPendingEvents()
909 newEventQueue.postEventPrivate(topQueue.getNextEventPrivate());
910 } catch (InterruptedException ie) {
911 if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
912 getEventLog().fine("Interrupted push", ie);
913 }
914 }
915 }
916
917 if (topQueue.dispatchThread != null) {
918 // Wake up EDT waiting in getNextEvent(), so it can
919 // pick up a new EventQueue. Post the waking event before
920 // topQueue.nextQueue is assigned, otherwise the event would
921 // go newEventQueue
922 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
923 }
924
925 newEventQueue.previousQueue = topQueue;
926 topQueue.nextQueue = newEventQueue;
927
928 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == topQueue) {
929 appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue);
930 }
931
932 pushPopCond.signalAll();
933 } finally {
934 pushPopLock.unlock();
935 }
936 }
937
938 /**
939 * Stops dispatching events using this {@code EventQueue}.
940 * Any pending events are transferred to the previous
941 * {@code EventQueue} for processing.
942 * <p>
943 * Warning: To avoid deadlock, do not declare this method
944 * synchronized in a subclass.
945 *
946 * @exception EmptyStackException if no previous push was made
947 * on this {@code EventQueue}
948 * @see java.awt.EventQueue#push
949 * @since 1.2
950 */
951 protected void pop() throws EmptyStackException {
952 if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
953 getEventLog().fine("EventQueue.pop(" + this + ")");
954 }
955
956 pushPopLock.lock();
957 try {
958 EventQueue topQueue = this;
959 while (topQueue.nextQueue != null) {
960 topQueue = topQueue.nextQueue;
961 }
962 EventQueue prevQueue = topQueue.previousQueue;
963 if (prevQueue == null) {
964 throw new EmptyStackException();
965 }
966
967 topQueue.previousQueue = null;
968 prevQueue.nextQueue = null;
969
970 // Transfer all events back to previous EventQueue.
971 while (topQueue.peekEvent() != null) {
972 try {
973 prevQueue.postEventPrivate(topQueue.getNextEventPrivate());
974 } catch (InterruptedException ie) {
975 if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
976 getEventLog().fine("Interrupted pop", ie);
977 }
978 }
979 }
980
981 if ((topQueue.dispatchThread != null) &&
982 (topQueue.dispatchThread.getEventQueue() == this))
983 {
984 prevQueue.dispatchThread = topQueue.dispatchThread;
985 topQueue.dispatchThread.setEventQueue(prevQueue);
986 }
987
988 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
989 appContext.put(AppContext.EVENT_QUEUE_KEY, prevQueue);
990 }
991
992 // Wake up EDT waiting in getNextEvent(), so it can
993 // pick up a new EventQueue
994 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
995
996 pushPopCond.signalAll();
997 } finally {
998 pushPopLock.unlock();
999 }
1000 }
1001
1002 /**
1003 * Creates a new {@code secondary loop} associated with this
1004 * event queue. Use the {@link SecondaryLoop#enter} and
1005 * {@link SecondaryLoop#exit} methods to start and stop the
1006 * event loop and dispatch the events from this queue.
1007 *
1008 * @return secondaryLoop A new secondary loop object, which can
1009 * be used to launch a new nested event
1010 * loop and dispatch events from this queue
1011 *
1012 * @see SecondaryLoop#enter
1013 * @see SecondaryLoop#exit
1014 *
1015 * @since 1.7
1016 */
1017 public SecondaryLoop createSecondaryLoop() {
1018 return createSecondaryLoop(null, null, 0);
1019 }
1020
1021 private class FwSecondaryLoopWrapper implements SecondaryLoop {
1022 final private SecondaryLoop loop;
1023 final private EventFilter filter;
1024
1025 public FwSecondaryLoopWrapper(SecondaryLoop loop, EventFilter filter) {
1026 this.loop = loop;
1027 this.filter = filter;
1028 }
1029
1030 @Override
1031 public boolean enter() {
1032 if (filter != null) {
1033 dispatchThread.addEventFilter(filter);
1034 }
1035 return loop.enter();
1036 }
1037
1038 @Override
1039 public boolean exit() {
1040 if (filter != null) {
1041 dispatchThread.removeEventFilter(filter);
1042 }
1043 return loop.exit();
1044 }
1045 }
1046
1047 SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) {
1048 pushPopLock.lock();
1049 try {
1050 if (nextQueue != null) {
1051 // Forward the request to the top of EventQueue stack
1052 return nextQueue.createSecondaryLoop(cond, filter, interval);
1053 }
1054 if (fwDispatcher != null) {
1055 return new FwSecondaryLoopWrapper(fwDispatcher.createSecondaryLoop(), filter);
1056 }
1057 if (dispatchThread == null) {
1058 initDispatchThread();
1059 }
1060 return new WaitDispatchSupport(dispatchThread, cond, filter, interval);
1061 } finally {
1062 pushPopLock.unlock();
1063 }
1064 }
1065
1066 /**
1067 * Returns true if the calling thread is
1068 * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1069 * dispatch thread. Use this method to ensure that a particular
1070 * task is being executed (or not being) there.
1071 * <p>
1072 * Note: use the {@link #invokeLater} or {@link #invokeAndWait}
1073 * methods to execute a task in
1074 * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1075 * dispatch thread.
1076 *
1077 * @return true if running in
1078 * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1079 * dispatch thread
1080 * @see #invokeLater
1081 * @see #invokeAndWait
1082 * @see Toolkit#getSystemEventQueue
1083 * @since 1.2
1084 */
1085 public static boolean isDispatchThread() {
1086 EventQueue eq = Toolkit.getEventQueue();
1087 return eq.isDispatchThreadImpl();
1088 }
1089
1090 final boolean isDispatchThreadImpl() {
1091 EventQueue eq = this;
1092 pushPopLock.lock();
1093 try {
1094 EventQueue next = eq.nextQueue;
1095 while (next != null) {
1096 eq = next;
1097 next = eq.nextQueue;
1098 }
1099 if (eq.fwDispatcher != null) {
1100 return eq.fwDispatcher.isDispatchThread();
1101 }
1102 return (Thread.currentThread() == eq.dispatchThread);
1103 } finally {
1104 pushPopLock.unlock();
1105 }
1106 }
1107
1108 final void initDispatchThread() {
1109 pushPopLock.lock();
1110 try {
1111 if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
1112 dispatchThread = AccessController.doPrivileged(
1113 new PrivilegedAction<EventDispatchThread>() {
1114 public EventDispatchThread run() {
1115 EventDispatchThread t =
1116 new EventDispatchThread(threadGroup,
1117 name,
1118 EventQueue.this);
1119 t.setContextClassLoader(classLoader);
1120 t.setPriority(Thread.NORM_PRIORITY + 1);
1121 t.setDaemon(false);
1122 AWTAutoShutdown.getInstance().notifyThreadBusy(t);
1123 return t;
1124 }
1125 }
1126 );
1127 dispatchThread.start();
1128 }
1129 } finally {
1130 pushPopLock.unlock();
1131 }
1132 }
1133
1134 final void detachDispatchThread(EventDispatchThread edt) {
1135 /*
1136 * Minimize discard possibility for non-posted events
1137 */
1138 SunToolkit.flushPendingEvents(appContext);
1139 /*
1140 * This synchronized block is to secure that the event dispatch
1141 * thread won't die in the middle of posting a new event to the
1142 * associated event queue. It is important because we notify
1143 * that the event dispatch thread is busy after posting a new event
1144 * to its queue, so the EventQueue.dispatchThread reference must
1145 * be valid at that point.
1146 */
1147 pushPopLock.lock();
1148 try {
1149 if (edt == dispatchThread) {
1150 dispatchThread = null;
1151 }
1152 AWTAutoShutdown.getInstance().notifyThreadFree(edt);
1153 /*
1154 * Event was posted after EDT events pumping had stopped, so start
1155 * another EDT to handle this event
1156 */
1157 if (peekEvent() != null) {
1158 initDispatchThread();
1159 }
1160 } finally {
1161 pushPopLock.unlock();
1162 }
1163 }
1164
1165 /*
1166 * Gets the {@code EventDispatchThread} for this
1167 * {@code EventQueue}.
1168 * @return the event dispatch thread associated with this event queue
1169 * or {@code null} if this event queue doesn't have a
1170 * working thread associated with it
1171 * @see java.awt.EventQueue#initDispatchThread
1172 * @see java.awt.EventQueue#detachDispatchThread
1173 */
1174 final EventDispatchThread getDispatchThread() {
1175 pushPopLock.lock();
1176 try {
1177 return dispatchThread;
1178 } finally {
1179 pushPopLock.unlock();
1180 }
1181 }
1182
1183 /*
1184 * Removes any pending events for the specified source object.
1185 * If removeAllEvents parameter is {@code true} then all
1186 * events for the specified source object are removed, if it
1187 * is {@code false} then {@code SequencedEvent}, {@code SentEvent},
1188 * {@code FocusEvent}, {@code WindowEvent}, {@code KeyEvent},
1189 * and {@code InputMethodEvent} are kept in the queue, but all other
1190 * events are removed.
1191 *
1192 * This method is normally called by the source's
1193 * {@code removeNotify} method.
1194 */
1195 final void removeSourceEvents(Object source, boolean removeAllEvents) {
1196 SunToolkit.flushPendingEvents(appContext);
1197 pushPopLock.lock();
1198 try {
1199 for (int i = 0; i < NUM_PRIORITIES; i++) {
1200 EventQueueItem entry = queues[i].head;
1201 EventQueueItem prev = null;
1202 while (entry != null) {
1203 if ((entry.event.getSource() == source)
1204 && (removeAllEvents
1205 || ! (entry.event instanceof SequencedEvent
1206 || entry.event instanceof SentEvent
1207 || entry.event instanceof FocusEvent
1208 || entry.event instanceof WindowEvent
1209 || entry.event instanceof KeyEvent
1210 || entry.event instanceof InputMethodEvent)))
1211 {
1212 if (entry.event instanceof SequencedEvent) {
1213 ((SequencedEvent)entry.event).dispose();
1214 }
1215 if (entry.event instanceof SentEvent) {
1216 ((SentEvent)entry.event).dispose();
1217 }
1218 if (entry.event instanceof InvocationEvent) {
1219 AWTAccessor.getInvocationEventAccessor()
1220 .dispose((InvocationEvent)entry.event);
1221 }
1222 if (entry.event instanceof SunDropTargetEvent) {
1223 ((SunDropTargetEvent)entry.event).dispose();
1224 }
1225 if (prev == null) {
1226 queues[i].head = entry.next;
1227 } else {
1228 prev.next = entry.next;
1229 }
1230 uncacheEQItem(entry);
1231 } else {
1232 prev = entry;
1233 }
1234 entry = entry.next;
1235 }
1236 queues[i].tail = prev;
1237 }
1238 } finally {
1239 pushPopLock.unlock();
1240 }
1241 }
1242
1243 synchronized long getMostRecentKeyEventTime() {
1244 pushPopLock.lock();
1245 try {
1246 return mostRecentKeyEventTime;
1247 } finally {
1248 pushPopLock.unlock();
1249 }
1250 }
1251
1252 static void setCurrentEventAndMostRecentTime(AWTEvent e) {
1253 Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e);
1254 }
1255 private void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) {
1256 pushPopLock.lock();
1257 try {
1258 if (!fxAppThreadIsDispatchThread && Thread.currentThread() != dispatchThread) {
1259 return;
1260 }
1261
1262 currentEvent = new WeakReference<>(e);
1263
1264 // This series of 'instanceof' checks should be replaced with a
1265 // polymorphic type (for example, an interface which declares a
1266 // getWhen() method). However, this would require us to make such
1267 // a type public, or to place it in sun.awt. Both of these approaches
1268 // have been frowned upon. So for now, we hack.
1269 //
1270 // In tiger, we will probably give timestamps to all events, so this
1271 // will no longer be an issue.
1272 long mostRecentEventTime2 = Long.MIN_VALUE;
1273 if (e instanceof InputEvent) {
1274 InputEvent ie = (InputEvent)e;
1275 mostRecentEventTime2 = ie.getWhen();
1276 if (e instanceof KeyEvent) {
1277 mostRecentKeyEventTime = ie.getWhen();
1278 }
1279 } else if (e instanceof InputMethodEvent) {
1280 InputMethodEvent ime = (InputMethodEvent)e;
1281 mostRecentEventTime2 = ime.getWhen();
1282 } else if (e instanceof ActionEvent) {
1283 ActionEvent ae = (ActionEvent)e;
1284 mostRecentEventTime2 = ae.getWhen();
1285 } else if (e instanceof InvocationEvent) {
1286 InvocationEvent ie = (InvocationEvent)e;
1287 mostRecentEventTime2 = ie.getWhen();
1288 }
1289 mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
1290 } finally {
1291 pushPopLock.unlock();
1292 }
1293 }
1294
1295 /**
1296 * Causes {@code runnable} to have its {@code run}
1297 * method called in the {@link #isDispatchThread dispatch thread} of
1298 * {@link Toolkit#getSystemEventQueue the system EventQueue}.
1299 * This will happen after all pending events are processed.
1300 *
1301 * @param runnable the {@code Runnable} whose {@code run}
1302 * method should be executed
1303 * asynchronously in the
1304 * {@link #isDispatchThread event dispatch thread}
1305 * of {@link Toolkit#getSystemEventQueue the system EventQueue}
1306 * @see #invokeAndWait
1307 * @see Toolkit#getSystemEventQueue
1308 * @see #isDispatchThread
1309 * @since 1.2
1310 */
1311 public static void invokeLater(Runnable runnable) {
1312 Toolkit.getEventQueue().postEvent(
1313 new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
1314 }
1315
1316 /**
1317 * Causes {@code runnable} to have its {@code run}
1318 * method called in the {@link #isDispatchThread dispatch thread} of
1319 * {@link Toolkit#getSystemEventQueue the system EventQueue}.
1320 * This will happen after all pending events are processed.
1321 * The call blocks until this has happened. This method
1322 * will throw an Error if called from the
1323 * {@link #isDispatchThread event dispatcher thread}.
1324 *
1325 * @param runnable the {@code Runnable} whose {@code run}
1326 * method should be executed
1327 * synchronously in the
1328 * {@link #isDispatchThread event dispatch thread}
1329 * of {@link Toolkit#getSystemEventQueue the system EventQueue}
1330 * @exception InterruptedException if any thread has
1331 * interrupted this thread
1332 * @exception InvocationTargetException if an throwable is thrown
1333 * when running {@code runnable}
1334 * @see #invokeLater
1335 * @see Toolkit#getSystemEventQueue
1336 * @see #isDispatchThread
1337 * @since 1.2
1338 */
1339 public static void invokeAndWait(Runnable runnable)
1340 throws InterruptedException, InvocationTargetException
1341 {
1342 invokeAndWait(Toolkit.getDefaultToolkit(), runnable);
1343 }
1344
1345 static void invokeAndWait(Object source, Runnable runnable)
1346 throws InterruptedException, InvocationTargetException
1347 {
1348 if (EventQueue.isDispatchThread()) {
1349 throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
1350 }
1351
1352 class AWTInvocationLock {}
1353 Object lock = new AWTInvocationLock();
1354
1355 InvocationEvent event =
1356 new InvocationEvent(source, runnable, lock, true);
1357
1358 synchronized (lock) {
1359 Toolkit.getEventQueue().postEvent(event);
1360 while (!event.isDispatched()) {
1361 lock.wait();
1362 }
1363 }
1364
1365 Throwable eventThrowable = event.getThrowable();
1366 if (eventThrowable != null) {
1367 throw new InvocationTargetException(eventThrowable);
1368 }
1369 }
1370
1371 /*
1372 * Called from PostEventQueue.postEvent to notify that a new event
1373 * appeared. First it proceeds to the EventQueue on the top of the
1374 * stack, then notifies the associated dispatch thread if it exists
1375 * or starts a new one otherwise.
1376 */
1377 private void wakeup(boolean isShutdown) {
1378 pushPopLock.lock();
1379 try {
1380 if (nextQueue != null) {
1381 // Forward call to the top of EventQueue stack.
1382 nextQueue.wakeup(isShutdown);
1383 } else if (dispatchThread != null) {
1384 pushPopCond.signalAll();
1385 } else if (!isShutdown) {
1386 initDispatchThread();
1387 }
1388 } finally {
1389 pushPopLock.unlock();
1390 }
1391 }
1392
1393 // The method is used by AWTAccessor for javafx/AWT single threaded mode.
1394 private void setFwDispatcher(FwDispatcher dispatcher) {
1395 if (nextQueue != null) {
1396 nextQueue.setFwDispatcher(dispatcher);
1397 } else {
1398 fwDispatcher = dispatcher;
1399 }
1400 }
1401 }
1402
1403 /**
1404 * The Queue object holds pointers to the beginning and end of one internal
1405 * queue. An EventQueue object is composed of multiple internal Queues, one
1406 * for each priority supported by the EventQueue. All Events on a particular
1407 * internal Queue have identical priority.
1408 */
1409 class Queue {
1410 EventQueueItem head;
1411 EventQueueItem tail;
1412 }
1413