You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1395 lines
49 KiB
1395 lines
49 KiB
/*
|
|
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package java.awt;
|
|
|
|
import java.awt.event.*;
|
|
|
|
import java.awt.peer.ComponentPeer;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.security.AccessController;
|
|
import java.security.PrivilegedAction;
|
|
|
|
import java.util.EmptyStackException;
|
|
|
|
import sun.awt.*;
|
|
import sun.awt.dnd.SunDropTargetEvent;
|
|
import sun.util.logging.PlatformLogger;
|
|
|
|
import java.util.concurrent.locks.Condition;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import java.security.AccessControlContext;
|
|
|
|
import sun.misc.SharedSecrets;
|
|
import sun.misc.JavaSecurityAccess;
|
|
|
|
/**
|
|
* <code>EventQueue</code> is a platform-independent class
|
|
* that queues events, both from the underlying peer classes
|
|
* and from trusted application classes.
|
|
* <p>
|
|
* It encapsulates asynchronous event dispatch machinery which
|
|
* extracts events from the queue and dispatches them by calling
|
|
* {@link #dispatchEvent(AWTEvent) dispatchEvent(AWTEvent)} method
|
|
* on this <code>EventQueue</code> with the event to be dispatched
|
|
* as an argument. The particular behavior of this machinery is
|
|
* implementation-dependent. The only requirements are that events
|
|
* which were actually enqueued to this queue (note that events
|
|
* being posted to the <code>EventQueue</code> can be coalesced)
|
|
* are dispatched:
|
|
* <dl>
|
|
* <dt> Sequentially.
|
|
* <dd> That is, it is not permitted that several events from
|
|
* this queue are dispatched simultaneously.
|
|
* <dt> In the same order as they are enqueued.
|
|
* <dd> That is, if <code>AWTEvent</code> A is enqueued
|
|
* to the <code>EventQueue</code> before
|
|
* <code>AWTEvent</code> B then event B will not be
|
|
* dispatched before event A.
|
|
* </dl>
|
|
* <p>
|
|
* Some browsers partition applets in different code bases into
|
|
* separate contexts, and establish walls between these contexts.
|
|
* In such a scenario, there will be one <code>EventQueue</code>
|
|
* per context. Other browsers place all applets into the same
|
|
* context, implying that there will be only a single, global
|
|
* <code>EventQueue</code> for all applets. This behavior is
|
|
* implementation-dependent. Consult your browser's documentation
|
|
* for more information.
|
|
* <p>
|
|
* For information on the threading issues of the event dispatch
|
|
* machinery, see <a href="doc-files/AWTThreadIssues.html#Autoshutdown"
|
|
* >AWT Threading Issues</a>.
|
|
*
|
|
* @author Thomas Ball
|
|
* @author Fred Ecks
|
|
* @author David Mendenhall
|
|
*
|
|
* @since 1.1
|
|
*/
|
|
public class EventQueue {
|
|
private static final AtomicInteger threadInitNumber = new AtomicInteger(0);
|
|
|
|
private static final int LOW_PRIORITY = 0;
|
|
private static final int NORM_PRIORITY = 1;
|
|
private static final int HIGH_PRIORITY = 2;
|
|
private static final int ULTIMATE_PRIORITY = 3;
|
|
|
|
private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1;
|
|
|
|
/*
|
|
* We maintain one Queue for each priority that the EventQueue supports.
|
|
* That is, the EventQueue object is actually implemented as
|
|
* NUM_PRIORITIES queues and all Events on a particular internal Queue
|
|
* have identical priority. Events are pulled off the EventQueue starting
|
|
* with the Queue of highest priority. We progress in decreasing order
|
|
* across all Queues.
|
|
*/
|
|
private Queue[] queues = new Queue[NUM_PRIORITIES];
|
|
|
|
/*
|
|
* The next EventQueue on the stack, or null if this EventQueue is
|
|
* on the top of the stack. If nextQueue is non-null, requests to post
|
|
* an event are forwarded to nextQueue.
|
|
*/
|
|
private EventQueue nextQueue;
|
|
|
|
/*
|
|
* The previous EventQueue on the stack, or null if this is the
|
|
* "base" EventQueue.
|
|
*/
|
|
private EventQueue previousQueue;
|
|
|
|
/*
|
|
* A single lock to synchronize the push()/pop() and related operations with
|
|
* all the EventQueues from the AppContext. Synchronization on any particular
|
|
* event queue(s) is not enough: we should lock the whole stack.
|
|
*/
|
|
private final Lock pushPopLock;
|
|
private final Condition pushPopCond;
|
|
|
|
/*
|
|
* Dummy runnable to wake up EDT from getNextEvent() after
|
|
push/pop is performed
|
|
*/
|
|
private final static Runnable dummyRunnable = new Runnable() {
|
|
public void run() {
|
|
}
|
|
};
|
|
|
|
private EventDispatchThread dispatchThread;
|
|
|
|
private final ThreadGroup threadGroup =
|
|
Thread.currentThread().getThreadGroup();
|
|
private final ClassLoader classLoader =
|
|
Thread.currentThread().getContextClassLoader();
|
|
|
|
/*
|
|
* The time stamp of the last dispatched InputEvent or ActionEvent.
|
|
*/
|
|
private long mostRecentEventTime = System.currentTimeMillis();
|
|
|
|
/*
|
|
* The time stamp of the last KeyEvent .
|
|
*/
|
|
private long mostRecentKeyEventTime = System.currentTimeMillis();
|
|
|
|
/**
|
|
* The modifiers field of the current event, if the current event is an
|
|
* InputEvent or ActionEvent.
|
|
*/
|
|
private WeakReference<AWTEvent> currentEvent;
|
|
|
|
/*
|
|
* Non-zero if a thread is waiting in getNextEvent(int) for an event of
|
|
* a particular ID to be posted to the queue.
|
|
*/
|
|
private volatile int waitForID;
|
|
|
|
/*
|
|
* AppContext corresponding to the queue.
|
|
*/
|
|
private final AppContext appContext;
|
|
|
|
private final String name = "AWT-EventQueue-" + threadInitNumber.getAndIncrement();
|
|
|
|
private FwDispatcher fwDispatcher;
|
|
|
|
private static volatile PlatformLogger eventLog;
|
|
|
|
private static final PlatformLogger getEventLog() {
|
|
if(eventLog == null) {
|
|
eventLog = PlatformLogger.getLogger("java.awt.event.EventQueue");
|
|
}
|
|
return eventLog;
|
|
}
|
|
|
|
static {
|
|
AWTAccessor.setEventQueueAccessor(
|
|
new AWTAccessor.EventQueueAccessor() {
|
|
public Thread getDispatchThread(EventQueue eventQueue) {
|
|
return eventQueue.getDispatchThread();
|
|
}
|
|
public boolean isDispatchThreadImpl(EventQueue eventQueue) {
|
|
return eventQueue.isDispatchThreadImpl();
|
|
}
|
|
public void removeSourceEvents(EventQueue eventQueue,
|
|
Object source,
|
|
boolean removeAllEvents)
|
|
{
|
|
eventQueue.removeSourceEvents(source, removeAllEvents);
|
|
}
|
|
public boolean noEvents(EventQueue eventQueue) {
|
|
return eventQueue.noEvents();
|
|
}
|
|
public void wakeup(EventQueue eventQueue, boolean isShutdown) {
|
|
eventQueue.wakeup(isShutdown);
|
|
}
|
|
public void invokeAndWait(Object source, Runnable r)
|
|
throws InterruptedException, InvocationTargetException
|
|
{
|
|
EventQueue.invokeAndWait(source, r);
|
|
}
|
|
public void setFwDispatcher(EventQueue eventQueue,
|
|
FwDispatcher dispatcher) {
|
|
eventQueue.setFwDispatcher(dispatcher);
|
|
}
|
|
|
|
@Override
|
|
public long getMostRecentEventTime(EventQueue eventQueue) {
|
|
return eventQueue.getMostRecentEventTimeImpl();
|
|
}
|
|
});
|
|
}
|
|
|
|
public EventQueue() {
|
|
for (int i = 0; i < NUM_PRIORITIES; i++) {
|
|
queues[i] = new Queue();
|
|
}
|
|
/*
|
|
* NOTE: if you ever have to start the associated event dispatch
|
|
* thread at this point, be aware of the following problem:
|
|
* If this EventQueue instance is created in
|
|
* SunToolkit.createNewAppContext() the started dispatch thread
|
|
* may call AppContext.getAppContext() before createNewAppContext()
|
|
* completes thus causing mess in thread group to appcontext mapping.
|
|
*/
|
|
|
|
appContext = AppContext.getAppContext();
|
|
pushPopLock = (Lock)appContext.get(AppContext.EVENT_QUEUE_LOCK_KEY);
|
|
pushPopCond = (Condition)appContext.get(AppContext.EVENT_QUEUE_COND_KEY);
|
|
}
|
|
|
|
/**
|
|
* Posts a 1.1-style event to the <code>EventQueue</code>.
|
|
* If there is an existing event on the queue with the same ID
|
|
* and event source, the source <code>Component</code>'s
|
|
* <code>coalesceEvents</code> method will be called.
|
|
*
|
|
* @param theEvent an instance of <code>java.awt.AWTEvent</code>,
|
|
* or a subclass of it
|
|
* @throws NullPointerException if <code>theEvent</code> is <code>null</code>
|
|
*/
|
|
public void postEvent(AWTEvent theEvent) {
|
|
SunToolkit.flushPendingEvents(appContext);
|
|
postEventPrivate(theEvent);
|
|
}
|
|
|
|
/**
|
|
* Posts a 1.1-style event to the <code>EventQueue</code>.
|
|
* If there is an existing event on the queue with the same ID
|
|
* and event source, the source <code>Component</code>'s
|
|
* <code>coalesceEvents</code> method will be called.
|
|
*
|
|
* @param theEvent an instance of <code>java.awt.AWTEvent</code>,
|
|
* or a subclass of it
|
|
*/
|
|
private final void postEventPrivate(AWTEvent theEvent) {
|
|
theEvent.isPosted = true;
|
|
pushPopLock.lock();
|
|
try {
|
|
if (nextQueue != null) {
|
|
// Forward the event to the top of EventQueue stack
|
|
nextQueue.postEventPrivate(theEvent);
|
|
return;
|
|
}
|
|
if (dispatchThread == null) {
|
|
if (theEvent.getSource() == AWTAutoShutdown.getInstance()) {
|
|
return;
|
|
} else {
|
|
initDispatchThread();
|
|
}
|
|
}
|
|
postEvent(theEvent, getPriority(theEvent));
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
private static int getPriority(AWTEvent theEvent) {
|
|
if (theEvent instanceof PeerEvent) {
|
|
PeerEvent peerEvent = (PeerEvent)theEvent;
|
|
if ((peerEvent.getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) {
|
|
return ULTIMATE_PRIORITY;
|
|
}
|
|
if ((peerEvent.getFlags() & PeerEvent.PRIORITY_EVENT) != 0) {
|
|
return HIGH_PRIORITY;
|
|
}
|
|
if ((peerEvent.getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) {
|
|
return LOW_PRIORITY;
|
|
}
|
|
}
|
|
int id = theEvent.getID();
|
|
if ((id >= PaintEvent.PAINT_FIRST) && (id <= PaintEvent.PAINT_LAST)) {
|
|
return LOW_PRIORITY;
|
|
}
|
|
return NORM_PRIORITY;
|
|
}
|
|
|
|
/**
|
|
* Posts the event to the internal Queue of specified priority,
|
|
* coalescing as appropriate.
|
|
*
|
|
* @param theEvent an instance of <code>java.awt.AWTEvent</code>,
|
|
* or a subclass of it
|
|
* @param priority the desired priority of the event
|
|
*/
|
|
private void postEvent(AWTEvent theEvent, int priority) {
|
|
if (coalesceEvent(theEvent, priority)) {
|
|
return;
|
|
}
|
|
|
|
EventQueueItem newItem = new EventQueueItem(theEvent);
|
|
|
|
cacheEQItem(newItem);
|
|
|
|
boolean notifyID = (theEvent.getID() == this.waitForID);
|
|
|
|
if (queues[priority].head == null) {
|
|
boolean shouldNotify = noEvents();
|
|
queues[priority].head = queues[priority].tail = newItem;
|
|
|
|
if (shouldNotify) {
|
|
if (theEvent.getSource() != AWTAutoShutdown.getInstance()) {
|
|
AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
|
|
}
|
|
pushPopCond.signalAll();
|
|
} else if (notifyID) {
|
|
pushPopCond.signalAll();
|
|
}
|
|
} else {
|
|
// The event was not coalesced or has non-Component source.
|
|
// Insert it at the end of the appropriate Queue.
|
|
queues[priority].tail.next = newItem;
|
|
queues[priority].tail = newItem;
|
|
if (notifyID) {
|
|
pushPopCond.signalAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean coalescePaintEvent(PaintEvent e) {
|
|
ComponentPeer sourcePeer = ((Component)e.getSource()).peer;
|
|
if (sourcePeer != null) {
|
|
sourcePeer.coalescePaintEvent(e);
|
|
}
|
|
EventQueueItem[] cache = ((Component)e.getSource()).eventCache;
|
|
if (cache == null) {
|
|
return false;
|
|
}
|
|
int index = eventToCacheIndex(e);
|
|
|
|
if (index != -1 && cache[index] != null) {
|
|
PaintEvent merged = mergePaintEvents(e, (PaintEvent)cache[index].event);
|
|
if (merged != null) {
|
|
cache[index].event = merged;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private PaintEvent mergePaintEvents(PaintEvent a, PaintEvent b) {
|
|
Rectangle aRect = a.getUpdateRect();
|
|
Rectangle bRect = b.getUpdateRect();
|
|
if (bRect.contains(aRect)) {
|
|
return b;
|
|
}
|
|
if (aRect.contains(bRect)) {
|
|
return a;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean coalesceMouseEvent(MouseEvent e) {
|
|
EventQueueItem[] cache = ((Component)e.getSource()).eventCache;
|
|
if (cache == null) {
|
|
return false;
|
|
}
|
|
int index = eventToCacheIndex(e);
|
|
if (index != -1 && cache[index] != null) {
|
|
cache[index].event = e;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean coalescePeerEvent(PeerEvent e) {
|
|
EventQueueItem[] cache = ((Component)e.getSource()).eventCache;
|
|
if (cache == null) {
|
|
return false;
|
|
}
|
|
int index = eventToCacheIndex(e);
|
|
if (index != -1 && cache[index] != null) {
|
|
e = e.coalesceEvents((PeerEvent)cache[index].event);
|
|
if (e != null) {
|
|
cache[index].event = e;
|
|
return true;
|
|
} else {
|
|
cache[index] = null;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Should avoid of calling this method by any means
|
|
* as it's working time is dependant on EQ length.
|
|
* In the wors case this method alone can slow down the entire application
|
|
* 10 times by stalling the Event processing.
|
|
* Only here by backward compatibility reasons.
|
|
*/
|
|
private boolean coalesceOtherEvent(AWTEvent e, int priority) {
|
|
int id = e.getID();
|
|
Component source = (Component)e.getSource();
|
|
for (EventQueueItem entry = queues[priority].head;
|
|
entry != null; entry = entry.next)
|
|
{
|
|
// Give Component.coalesceEvents a chance
|
|
if (entry.event.getSource() == source && entry.event.getID() == id) {
|
|
AWTEvent coalescedEvent = source.coalesceEvents(
|
|
entry.event, e);
|
|
if (coalescedEvent != null) {
|
|
entry.event = coalescedEvent;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean coalesceEvent(AWTEvent e, int priority) {
|
|
if (!(e.getSource() instanceof Component)) {
|
|
return false;
|
|
}
|
|
if (e instanceof PeerEvent) {
|
|
return coalescePeerEvent((PeerEvent)e);
|
|
}
|
|
// The worst case
|
|
if (((Component)e.getSource()).isCoalescingEnabled()
|
|
&& coalesceOtherEvent(e, priority))
|
|
{
|
|
return true;
|
|
}
|
|
if (e instanceof PaintEvent) {
|
|
return coalescePaintEvent((PaintEvent)e);
|
|
}
|
|
if (e instanceof MouseEvent) {
|
|
return coalesceMouseEvent((MouseEvent)e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void cacheEQItem(EventQueueItem entry) {
|
|
int index = eventToCacheIndex(entry.event);
|
|
if (index != -1 && entry.event.getSource() instanceof Component) {
|
|
Component source = (Component)entry.event.getSource();
|
|
if (source.eventCache == null) {
|
|
source.eventCache = new EventQueueItem[CACHE_LENGTH];
|
|
}
|
|
source.eventCache[index] = entry;
|
|
}
|
|
}
|
|
|
|
private void uncacheEQItem(EventQueueItem entry) {
|
|
int index = eventToCacheIndex(entry.event);
|
|
if (index != -1 && entry.event.getSource() instanceof Component) {
|
|
Component source = (Component)entry.event.getSource();
|
|
if (source.eventCache == null) {
|
|
return;
|
|
}
|
|
source.eventCache[index] = null;
|
|
}
|
|
}
|
|
|
|
private static final int PAINT = 0;
|
|
private static final int UPDATE = 1;
|
|
private static final int MOVE = 2;
|
|
private static final int DRAG = 3;
|
|
private static final int PEER = 4;
|
|
private static final int CACHE_LENGTH = 5;
|
|
|
|
private static int eventToCacheIndex(AWTEvent e) {
|
|
switch(e.getID()) {
|
|
case PaintEvent.PAINT:
|
|
return PAINT;
|
|
case PaintEvent.UPDATE:
|
|
return UPDATE;
|
|
case MouseEvent.MOUSE_MOVED:
|
|
return MOVE;
|
|
case MouseEvent.MOUSE_DRAGGED:
|
|
// Return -1 for SunDropTargetEvent since they are usually synchronous
|
|
// and we don't want to skip them by coalescing with MouseEvent or other drag events
|
|
return e instanceof SunDropTargetEvent ? -1 : DRAG;
|
|
default:
|
|
return e instanceof PeerEvent ? PEER : -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether an event is pending on any of the separate
|
|
* Queues.
|
|
* @return whether an event is pending on any of the separate Queues
|
|
*/
|
|
private boolean noEvents() {
|
|
for (int i = 0; i < NUM_PRIORITIES; i++) {
|
|
if (queues[i].head != null) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes an event from the <code>EventQueue</code> and
|
|
* returns it. This method will block until an event has
|
|
* been posted by another thread.
|
|
* @return the next <code>AWTEvent</code>
|
|
* @exception InterruptedException
|
|
* if any thread has interrupted this thread
|
|
*/
|
|
public AWTEvent getNextEvent() throws InterruptedException {
|
|
do {
|
|
/*
|
|
* SunToolkit.flushPendingEvents must be called outside
|
|
* of the synchronized block to avoid deadlock when
|
|
* event queues are nested with push()/pop().
|
|
*/
|
|
SunToolkit.flushPendingEvents(appContext);
|
|
pushPopLock.lock();
|
|
try {
|
|
AWTEvent event = getNextEventPrivate();
|
|
if (event != null) {
|
|
return event;
|
|
}
|
|
AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread);
|
|
pushPopCond.await();
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
} while(true);
|
|
}
|
|
|
|
/*
|
|
* Must be called under the lock. Doesn't call flushPendingEvents()
|
|
*/
|
|
AWTEvent getNextEventPrivate() throws InterruptedException {
|
|
for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
|
|
if (queues[i].head != null) {
|
|
EventQueueItem entry = queues[i].head;
|
|
queues[i].head = entry.next;
|
|
if (entry.next == null) {
|
|
queues[i].tail = null;
|
|
}
|
|
uncacheEQItem(entry);
|
|
return entry.event;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
AWTEvent getNextEvent(int id) throws InterruptedException {
|
|
do {
|
|
/*
|
|
* SunToolkit.flushPendingEvents must be called outside
|
|
* of the synchronized block to avoid deadlock when
|
|
* event queues are nested with push()/pop().
|
|
*/
|
|
SunToolkit.flushPendingEvents(appContext);
|
|
pushPopLock.lock();
|
|
try {
|
|
for (int i = 0; i < NUM_PRIORITIES; i++) {
|
|
for (EventQueueItem entry = queues[i].head, prev = null;
|
|
entry != null; prev = entry, entry = entry.next)
|
|
{
|
|
if (entry.event.getID() == id) {
|
|
if (prev == null) {
|
|
queues[i].head = entry.next;
|
|
} else {
|
|
prev.next = entry.next;
|
|
}
|
|
if (queues[i].tail == entry) {
|
|
queues[i].tail = prev;
|
|
}
|
|
uncacheEQItem(entry);
|
|
return entry.event;
|
|
}
|
|
}
|
|
}
|
|
waitForID = id;
|
|
pushPopCond.await();
|
|
waitForID = 0;
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
} while(true);
|
|
}
|
|
|
|
/**
|
|
* Returns the first event on the <code>EventQueue</code>
|
|
* without removing it.
|
|
* @return the first event
|
|
*/
|
|
public AWTEvent peekEvent() {
|
|
pushPopLock.lock();
|
|
try {
|
|
for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
|
|
if (queues[i].head != null) {
|
|
return queues[i].head.event;
|
|
}
|
|
}
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the first event with the specified id, if any.
|
|
* @param id the id of the type of event desired
|
|
* @return the first event of the specified id or <code>null</code>
|
|
* if there is no such event
|
|
*/
|
|
public AWTEvent peekEvent(int id) {
|
|
pushPopLock.lock();
|
|
try {
|
|
for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
|
|
EventQueueItem q = queues[i].head;
|
|
for (; q != null; q = q.next) {
|
|
if (q.event.getID() == id) {
|
|
return q.event;
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static final JavaSecurityAccess javaSecurityAccess =
|
|
SharedSecrets.getJavaSecurityAccess();
|
|
|
|
/**
|
|
* Dispatches an event. The manner in which the event is
|
|
* dispatched depends upon the type of the event and the
|
|
* type of the event's source object:
|
|
*
|
|
* <table border=1 summary="Event types, source types, and dispatch methods">
|
|
* <tr>
|
|
* <th>Event Type</th>
|
|
* <th>Source Type</th>
|
|
* <th>Dispatched To</th>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>ActiveEvent</td>
|
|
* <td>Any</td>
|
|
* <td>event.dispatch()</td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Other</td>
|
|
* <td>Component</td>
|
|
* <td>source.dispatchEvent(AWTEvent)</td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Other</td>
|
|
* <td>MenuComponent</td>
|
|
* <td>source.dispatchEvent(AWTEvent)</td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Other</td>
|
|
* <td>Other</td>
|
|
* <td>No action (ignored)</td>
|
|
* </tr>
|
|
* </table>
|
|
* <p>
|
|
* @param event an instance of <code>java.awt.AWTEvent</code>,
|
|
* or a subclass of it
|
|
* @throws NullPointerException if <code>event</code> is <code>null</code>
|
|
* @since 1.2
|
|
*/
|
|
protected void dispatchEvent(final AWTEvent event) {
|
|
final Object src = event.getSource();
|
|
final PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
|
|
public Void run() {
|
|
// In case fwDispatcher is installed and we're already on the
|
|
// dispatch thread (e.g. performing DefaultKeyboardFocusManager.sendMessage),
|
|
// dispatch the event straight away.
|
|
if (fwDispatcher == null || isDispatchThreadImpl()) {
|
|
dispatchEventImpl(event, src);
|
|
} else {
|
|
fwDispatcher.scheduleDispatch(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (dispatchThread.filterAndCheckEvent(event)) {
|
|
dispatchEventImpl(event, src);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
final AccessControlContext stack = AccessController.getContext();
|
|
final AccessControlContext srcAcc = getAccessControlContextFrom(src);
|
|
final AccessControlContext eventAcc = event.getAccessControlContext();
|
|
if (srcAcc == null) {
|
|
javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc);
|
|
} else {
|
|
javaSecurityAccess.doIntersectionPrivilege(
|
|
new PrivilegedAction<Void>() {
|
|
public Void run() {
|
|
javaSecurityAccess.doIntersectionPrivilege(action, eventAcc);
|
|
return null;
|
|
}
|
|
}, stack, srcAcc);
|
|
}
|
|
}
|
|
|
|
private static AccessControlContext getAccessControlContextFrom(Object src) {
|
|
return src instanceof Component ?
|
|
((Component)src).getAccessControlContext() :
|
|
src instanceof MenuComponent ?
|
|
((MenuComponent)src).getAccessControlContext() :
|
|
src instanceof TrayIcon ?
|
|
((TrayIcon)src).getAccessControlContext() :
|
|
null;
|
|
}
|
|
|
|
/**
|
|
* Called from dispatchEvent() under a correct AccessControlContext
|
|
*/
|
|
private void dispatchEventImpl(final AWTEvent event, final Object src) {
|
|
event.isPosted = true;
|
|
if (event instanceof ActiveEvent) {
|
|
// This could become the sole method of dispatching in time.
|
|
setCurrentEventAndMostRecentTimeImpl(event);
|
|
((ActiveEvent)event).dispatch();
|
|
} else if (src instanceof Component) {
|
|
((Component)src).dispatchEvent(event);
|
|
event.dispatched();
|
|
} else if (src instanceof MenuComponent) {
|
|
((MenuComponent)src).dispatchEvent(event);
|
|
} else if (src instanceof TrayIcon) {
|
|
((TrayIcon)src).dispatchEvent(event);
|
|
} else if (src instanceof AWTAutoShutdown) {
|
|
if (noEvents()) {
|
|
dispatchThread.stopDispatching();
|
|
}
|
|
} else {
|
|
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
|
|
getEventLog().fine("Unable to dispatch event: " + event);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the timestamp of the most recent event that had a timestamp, and
|
|
* that was dispatched from the <code>EventQueue</code> associated with the
|
|
* calling thread. If an event with a timestamp is currently being
|
|
* dispatched, its timestamp will be returned. If no events have yet
|
|
* been dispatched, the EventQueue's initialization time will be
|
|
* returned instead.In the current version of
|
|
* the JDK, only <code>InputEvent</code>s,
|
|
* <code>ActionEvent</code>s, and <code>InvocationEvent</code>s have
|
|
* timestamps; however, future versions of the JDK may add timestamps to
|
|
* additional event types. Note that this method should only be invoked
|
|
* from an application's {@link #isDispatchThread event dispatching thread}.
|
|
* If this method is
|
|
* invoked from another thread, the current system time (as reported by
|
|
* <code>System.currentTimeMillis()</code>) will be returned instead.
|
|
*
|
|
* @return the timestamp of the last <code>InputEvent</code>,
|
|
* <code>ActionEvent</code>, or <code>InvocationEvent</code> to be
|
|
* dispatched, or <code>System.currentTimeMillis()</code> if this
|
|
* method is invoked on a thread other than an event dispatching
|
|
* thread
|
|
* @see java.awt.event.InputEvent#getWhen
|
|
* @see java.awt.event.ActionEvent#getWhen
|
|
* @see java.awt.event.InvocationEvent#getWhen
|
|
* @see #isDispatchThread
|
|
*
|
|
* @since 1.4
|
|
*/
|
|
public static long getMostRecentEventTime() {
|
|
return Toolkit.getEventQueue().getMostRecentEventTimeImpl();
|
|
}
|
|
private long getMostRecentEventTimeImpl() {
|
|
pushPopLock.lock();
|
|
try {
|
|
return (Thread.currentThread() == dispatchThread)
|
|
? mostRecentEventTime
|
|
: System.currentTimeMillis();
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return most recent event time on all threads.
|
|
*/
|
|
long getMostRecentEventTimeEx() {
|
|
pushPopLock.lock();
|
|
try {
|
|
return mostRecentEventTime;
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the the event currently being dispatched by the
|
|
* <code>EventQueue</code> associated with the calling thread. This is
|
|
* useful if a method needs access to the event, but was not designed to
|
|
* receive a reference to it as an argument. Note that this method should
|
|
* only be invoked from an application's event dispatching thread. If this
|
|
* method is invoked from another thread, null will be returned.
|
|
*
|
|
* @return the event currently being dispatched, or null if this method is
|
|
* invoked on a thread other than an event dispatching thread
|
|
* @since 1.4
|
|
*/
|
|
public static AWTEvent getCurrentEvent() {
|
|
return Toolkit.getEventQueue().getCurrentEventImpl();
|
|
}
|
|
private AWTEvent getCurrentEventImpl() {
|
|
pushPopLock.lock();
|
|
try {
|
|
return (Thread.currentThread() == dispatchThread)
|
|
? currentEvent.get()
|
|
: null;
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replaces the existing <code>EventQueue</code> with the specified one.
|
|
* Any pending events are transferred to the new <code>EventQueue</code>
|
|
* for processing by it.
|
|
*
|
|
* @param newEventQueue an <code>EventQueue</code>
|
|
* (or subclass thereof) instance to be use
|
|
* @see java.awt.EventQueue#pop
|
|
* @throws NullPointerException if <code>newEventQueue</code> is <code>null</code>
|
|
* @since 1.2
|
|
*/
|
|
public void push(EventQueue newEventQueue) {
|
|
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
|
|
getEventLog().fine("EventQueue.push(" + newEventQueue + ")");
|
|
}
|
|
|
|
pushPopLock.lock();
|
|
try {
|
|
EventQueue topQueue = this;
|
|
while (topQueue.nextQueue != null) {
|
|
topQueue = topQueue.nextQueue;
|
|
}
|
|
if (topQueue.fwDispatcher != null) {
|
|
throw new RuntimeException("push() to queue with fwDispatcher");
|
|
}
|
|
if ((topQueue.dispatchThread != null) &&
|
|
(topQueue.dispatchThread.getEventQueue() == this))
|
|
{
|
|
newEventQueue.dispatchThread = topQueue.dispatchThread;
|
|
topQueue.dispatchThread.setEventQueue(newEventQueue);
|
|
}
|
|
|
|
// Transfer all events forward to new EventQueue.
|
|
while (topQueue.peekEvent() != null) {
|
|
try {
|
|
// Use getNextEventPrivate() as it doesn't call flushPendingEvents()
|
|
newEventQueue.postEventPrivate(topQueue.getNextEventPrivate());
|
|
} catch (InterruptedException ie) {
|
|
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
|
|
getEventLog().fine("Interrupted push", ie);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (topQueue.dispatchThread != null) {
|
|
// Wake up EDT waiting in getNextEvent(), so it can
|
|
// pick up a new EventQueue. Post the waking event before
|
|
// topQueue.nextQueue is assigned, otherwise the event would
|
|
// go newEventQueue
|
|
topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
|
|
}
|
|
|
|
newEventQueue.previousQueue = topQueue;
|
|
topQueue.nextQueue = newEventQueue;
|
|
|
|
if (appContext.get(AppContext.EVENT_QUEUE_KEY) == topQueue) {
|
|
appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue);
|
|
}
|
|
|
|
pushPopCond.signalAll();
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops dispatching events using this <code>EventQueue</code>.
|
|
* Any pending events are transferred to the previous
|
|
* <code>EventQueue</code> for processing.
|
|
* <p>
|
|
* Warning: To avoid deadlock, do not declare this method
|
|
* synchronized in a subclass.
|
|
*
|
|
* @exception EmptyStackException if no previous push was made
|
|
* on this <code>EventQueue</code>
|
|
* @see java.awt.EventQueue#push
|
|
* @since 1.2
|
|
*/
|
|
protected void pop() throws EmptyStackException {
|
|
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
|
|
getEventLog().fine("EventQueue.pop(" + this + ")");
|
|
}
|
|
|
|
pushPopLock.lock();
|
|
try {
|
|
EventQueue topQueue = this;
|
|
while (topQueue.nextQueue != null) {
|
|
topQueue = topQueue.nextQueue;
|
|
}
|
|
EventQueue prevQueue = topQueue.previousQueue;
|
|
if (prevQueue == null) {
|
|
throw new EmptyStackException();
|
|
}
|
|
|
|
topQueue.previousQueue = null;
|
|
prevQueue.nextQueue = null;
|
|
|
|
// Transfer all events back to previous EventQueue.
|
|
while (topQueue.peekEvent() != null) {
|
|
try {
|
|
prevQueue.postEventPrivate(topQueue.getNextEventPrivate());
|
|
} catch (InterruptedException ie) {
|
|
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
|
|
getEventLog().fine("Interrupted pop", ie);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((topQueue.dispatchThread != null) &&
|
|
(topQueue.dispatchThread.getEventQueue() == this))
|
|
{
|
|
prevQueue.dispatchThread = topQueue.dispatchThread;
|
|
topQueue.dispatchThread.setEventQueue(prevQueue);
|
|
}
|
|
|
|
if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
|
|
appContext.put(AppContext.EVENT_QUEUE_KEY, prevQueue);
|
|
}
|
|
|
|
// Wake up EDT waiting in getNextEvent(), so it can
|
|
// pick up a new EventQueue
|
|
topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
|
|
|
|
pushPopCond.signalAll();
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new {@code secondary loop} associated with this
|
|
* event queue. Use the {@link SecondaryLoop#enter} and
|
|
* {@link SecondaryLoop#exit} methods to start and stop the
|
|
* event loop and dispatch the events from this queue.
|
|
*
|
|
* @return secondaryLoop A new secondary loop object, which can
|
|
* be used to launch a new nested event
|
|
* loop and dispatch events from this queue
|
|
*
|
|
* @see SecondaryLoop#enter
|
|
* @see SecondaryLoop#exit
|
|
*
|
|
* @since 1.7
|
|
*/
|
|
public SecondaryLoop createSecondaryLoop() {
|
|
return createSecondaryLoop(null, null, 0);
|
|
}
|
|
|
|
private class FwSecondaryLoopWrapper implements SecondaryLoop {
|
|
final private SecondaryLoop loop;
|
|
final private EventFilter filter;
|
|
|
|
public FwSecondaryLoopWrapper(SecondaryLoop loop, EventFilter filter) {
|
|
this.loop = loop;
|
|
this.filter = filter;
|
|
}
|
|
|
|
@Override
|
|
public boolean enter() {
|
|
if (filter != null) {
|
|
dispatchThread.addEventFilter(filter);
|
|
}
|
|
return loop.enter();
|
|
}
|
|
|
|
@Override
|
|
public boolean exit() {
|
|
if (filter != null) {
|
|
dispatchThread.removeEventFilter(filter);
|
|
}
|
|
return loop.exit();
|
|
}
|
|
}
|
|
|
|
SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) {
|
|
pushPopLock.lock();
|
|
try {
|
|
if (nextQueue != null) {
|
|
// Forward the request to the top of EventQueue stack
|
|
return nextQueue.createSecondaryLoop(cond, filter, interval);
|
|
}
|
|
if (fwDispatcher != null) {
|
|
return new FwSecondaryLoopWrapper(fwDispatcher.createSecondaryLoop(), filter);
|
|
}
|
|
if (dispatchThread == null) {
|
|
initDispatchThread();
|
|
}
|
|
return new WaitDispatchSupport(dispatchThread, cond, filter, interval);
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the calling thread is
|
|
* {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
|
|
* dispatch thread. Use this method to ensure that a particular
|
|
* task is being executed (or not being) there.
|
|
* <p>
|
|
* Note: use the {@link #invokeLater} or {@link #invokeAndWait}
|
|
* methods to execute a task in
|
|
* {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
|
|
* dispatch thread.
|
|
* <p>
|
|
*
|
|
* @return true if running in
|
|
* {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
|
|
* dispatch thread
|
|
* @see #invokeLater
|
|
* @see #invokeAndWait
|
|
* @see Toolkit#getSystemEventQueue
|
|
* @since 1.2
|
|
*/
|
|
public static boolean isDispatchThread() {
|
|
EventQueue eq = Toolkit.getEventQueue();
|
|
return eq.isDispatchThreadImpl();
|
|
}
|
|
|
|
final boolean isDispatchThreadImpl() {
|
|
EventQueue eq = this;
|
|
pushPopLock.lock();
|
|
try {
|
|
EventQueue next = eq.nextQueue;
|
|
while (next != null) {
|
|
eq = next;
|
|
next = eq.nextQueue;
|
|
}
|
|
if (eq.fwDispatcher != null) {
|
|
return eq.fwDispatcher.isDispatchThread();
|
|
}
|
|
return (Thread.currentThread() == eq.dispatchThread);
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
final void initDispatchThread() {
|
|
pushPopLock.lock();
|
|
try {
|
|
if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
|
|
dispatchThread = AccessController.doPrivileged(
|
|
new PrivilegedAction<EventDispatchThread>() {
|
|
public EventDispatchThread run() {
|
|
EventDispatchThread t =
|
|
new EventDispatchThread(threadGroup,
|
|
name,
|
|
EventQueue.this);
|
|
t.setContextClassLoader(classLoader);
|
|
t.setPriority(Thread.NORM_PRIORITY + 1);
|
|
t.setDaemon(false);
|
|
AWTAutoShutdown.getInstance().notifyThreadBusy(t);
|
|
return t;
|
|
}
|
|
}
|
|
);
|
|
dispatchThread.start();
|
|
}
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
final void detachDispatchThread(EventDispatchThread edt) {
|
|
/*
|
|
* Minimize discard possibility for non-posted events
|
|
*/
|
|
SunToolkit.flushPendingEvents(appContext);
|
|
/*
|
|
* This synchronized block is to secure that the event dispatch
|
|
* thread won't die in the middle of posting a new event to the
|
|
* associated event queue. It is important because we notify
|
|
* that the event dispatch thread is busy after posting a new event
|
|
* to its queue, so the EventQueue.dispatchThread reference must
|
|
* be valid at that point.
|
|
*/
|
|
pushPopLock.lock();
|
|
try {
|
|
if (edt == dispatchThread) {
|
|
dispatchThread = null;
|
|
}
|
|
AWTAutoShutdown.getInstance().notifyThreadFree(edt);
|
|
/*
|
|
* Event was posted after EDT events pumping had stopped, so start
|
|
* another EDT to handle this event
|
|
*/
|
|
if (peekEvent() != null) {
|
|
initDispatchThread();
|
|
}
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Gets the <code>EventDispatchThread</code> for this
|
|
* <code>EventQueue</code>.
|
|
* @return the event dispatch thread associated with this event queue
|
|
* or <code>null</code> if this event queue doesn't have a
|
|
* working thread associated with it
|
|
* @see java.awt.EventQueue#initDispatchThread
|
|
* @see java.awt.EventQueue#detachDispatchThread
|
|
*/
|
|
final EventDispatchThread getDispatchThread() {
|
|
pushPopLock.lock();
|
|
try {
|
|
return dispatchThread;
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Removes any pending events for the specified source object.
|
|
* If removeAllEvents parameter is <code>true</code> then all
|
|
* events for the specified source object are removed, if it
|
|
* is <code>false</code> then <code>SequencedEvent</code>, <code>SentEvent</code>,
|
|
* <code>FocusEvent</code>, <code>WindowEvent</code>, <code>KeyEvent</code>,
|
|
* and <code>InputMethodEvent</code> are kept in the queue, but all other
|
|
* events are removed.
|
|
*
|
|
* This method is normally called by the source's
|
|
* <code>removeNotify</code> method.
|
|
*/
|
|
final void removeSourceEvents(Object source, boolean removeAllEvents) {
|
|
SunToolkit.flushPendingEvents(appContext);
|
|
pushPopLock.lock();
|
|
try {
|
|
for (int i = 0; i < NUM_PRIORITIES; i++) {
|
|
EventQueueItem entry = queues[i].head;
|
|
EventQueueItem prev = null;
|
|
while (entry != null) {
|
|
if ((entry.event.getSource() == source)
|
|
&& (removeAllEvents
|
|
|| ! (entry.event instanceof SequencedEvent
|
|
|| entry.event instanceof SentEvent
|
|
|| entry.event instanceof FocusEvent
|
|
|| entry.event instanceof WindowEvent
|
|
|| entry.event instanceof KeyEvent
|
|
|| entry.event instanceof InputMethodEvent)))
|
|
{
|
|
if (entry.event instanceof SequencedEvent) {
|
|
((SequencedEvent)entry.event).dispose();
|
|
}
|
|
if (entry.event instanceof SentEvent) {
|
|
((SentEvent)entry.event).dispose();
|
|
}
|
|
if (entry.event instanceof InvocationEvent) {
|
|
AWTAccessor.getInvocationEventAccessor()
|
|
.dispose((InvocationEvent)entry.event);
|
|
}
|
|
if (prev == null) {
|
|
queues[i].head = entry.next;
|
|
} else {
|
|
prev.next = entry.next;
|
|
}
|
|
uncacheEQItem(entry);
|
|
} else {
|
|
prev = entry;
|
|
}
|
|
entry = entry.next;
|
|
}
|
|
queues[i].tail = prev;
|
|
}
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
synchronized long getMostRecentKeyEventTime() {
|
|
pushPopLock.lock();
|
|
try {
|
|
return mostRecentKeyEventTime;
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
static void setCurrentEventAndMostRecentTime(AWTEvent e) {
|
|
Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e);
|
|
}
|
|
private void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) {
|
|
pushPopLock.lock();
|
|
try {
|
|
if (Thread.currentThread() != dispatchThread) {
|
|
return;
|
|
}
|
|
|
|
currentEvent = new WeakReference<>(e);
|
|
|
|
// This series of 'instanceof' checks should be replaced with a
|
|
// polymorphic type (for example, an interface which declares a
|
|
// getWhen() method). However, this would require us to make such
|
|
// a type public, or to place it in sun.awt. Both of these approaches
|
|
// have been frowned upon. So for now, we hack.
|
|
//
|
|
// In tiger, we will probably give timestamps to all events, so this
|
|
// will no longer be an issue.
|
|
long mostRecentEventTime2 = Long.MIN_VALUE;
|
|
if (e instanceof InputEvent) {
|
|
InputEvent ie = (InputEvent)e;
|
|
mostRecentEventTime2 = ie.getWhen();
|
|
if (e instanceof KeyEvent) {
|
|
mostRecentKeyEventTime = ie.getWhen();
|
|
}
|
|
} else if (e instanceof InputMethodEvent) {
|
|
InputMethodEvent ime = (InputMethodEvent)e;
|
|
mostRecentEventTime2 = ime.getWhen();
|
|
} else if (e instanceof ActionEvent) {
|
|
ActionEvent ae = (ActionEvent)e;
|
|
mostRecentEventTime2 = ae.getWhen();
|
|
} else if (e instanceof InvocationEvent) {
|
|
InvocationEvent ie = (InvocationEvent)e;
|
|
mostRecentEventTime2 = ie.getWhen();
|
|
}
|
|
mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Causes <code>runnable</code> to have its <code>run</code>
|
|
* method called in the {@link #isDispatchThread dispatch thread} of
|
|
* {@link Toolkit#getSystemEventQueue the system EventQueue}.
|
|
* This will happen after all pending events are processed.
|
|
*
|
|
* @param runnable the <code>Runnable</code> whose <code>run</code>
|
|
* method should be executed
|
|
* asynchronously in the
|
|
* {@link #isDispatchThread event dispatch thread}
|
|
* of {@link Toolkit#getSystemEventQueue the system EventQueue}
|
|
* @see #invokeAndWait
|
|
* @see Toolkit#getSystemEventQueue
|
|
* @see #isDispatchThread
|
|
* @since 1.2
|
|
*/
|
|
public static void invokeLater(Runnable runnable) {
|
|
Toolkit.getEventQueue().postEvent(
|
|
new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
|
|
}
|
|
|
|
/**
|
|
* Causes <code>runnable</code> to have its <code>run</code>
|
|
* method called in the {@link #isDispatchThread dispatch thread} of
|
|
* {@link Toolkit#getSystemEventQueue the system EventQueue}.
|
|
* This will happen after all pending events are processed.
|
|
* The call blocks until this has happened. This method
|
|
* will throw an Error if called from the
|
|
* {@link #isDispatchThread event dispatcher thread}.
|
|
*
|
|
* @param runnable the <code>Runnable</code> whose <code>run</code>
|
|
* method should be executed
|
|
* synchronously in the
|
|
* {@link #isDispatchThread event dispatch thread}
|
|
* of {@link Toolkit#getSystemEventQueue the system EventQueue}
|
|
* @exception InterruptedException if any thread has
|
|
* interrupted this thread
|
|
* @exception InvocationTargetException if an throwable is thrown
|
|
* when running <code>runnable</code>
|
|
* @see #invokeLater
|
|
* @see Toolkit#getSystemEventQueue
|
|
* @see #isDispatchThread
|
|
* @since 1.2
|
|
*/
|
|
public static void invokeAndWait(Runnable runnable)
|
|
throws InterruptedException, InvocationTargetException
|
|
{
|
|
invokeAndWait(Toolkit.getDefaultToolkit(), runnable);
|
|
}
|
|
|
|
static void invokeAndWait(Object source, Runnable runnable)
|
|
throws InterruptedException, InvocationTargetException
|
|
{
|
|
if (EventQueue.isDispatchThread()) {
|
|
throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
|
|
}
|
|
|
|
class AWTInvocationLock {}
|
|
Object lock = new AWTInvocationLock();
|
|
|
|
InvocationEvent event =
|
|
new InvocationEvent(source, runnable, lock, true);
|
|
|
|
synchronized (lock) {
|
|
Toolkit.getEventQueue().postEvent(event);
|
|
while (!event.isDispatched()) {
|
|
lock.wait();
|
|
}
|
|
}
|
|
|
|
Throwable eventThrowable = event.getThrowable();
|
|
if (eventThrowable != null) {
|
|
throw new InvocationTargetException(eventThrowable);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called from PostEventQueue.postEvent to notify that a new event
|
|
* appeared. First it proceeds to the EventQueue on the top of the
|
|
* stack, then notifies the associated dispatch thread if it exists
|
|
* or starts a new one otherwise.
|
|
*/
|
|
private void wakeup(boolean isShutdown) {
|
|
pushPopLock.lock();
|
|
try {
|
|
if (nextQueue != null) {
|
|
// Forward call to the top of EventQueue stack.
|
|
nextQueue.wakeup(isShutdown);
|
|
} else if (dispatchThread != null) {
|
|
pushPopCond.signalAll();
|
|
} else if (!isShutdown) {
|
|
initDispatchThread();
|
|
}
|
|
} finally {
|
|
pushPopLock.unlock();
|
|
}
|
|
}
|
|
|
|
// The method is used by AWTAccessor for javafx/AWT single threaded mode.
|
|
private void setFwDispatcher(FwDispatcher dispatcher) {
|
|
if (nextQueue != null) {
|
|
nextQueue.setFwDispatcher(dispatcher);
|
|
} else {
|
|
fwDispatcher = dispatcher;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The Queue object holds pointers to the beginning and end of one internal
|
|
* queue. An EventQueue object is composed of multiple internal Queues, one
|
|
* for each priority supported by the EventQueue. All Events on a particular
|
|
* internal Queue have identical priority.
|
|
*/
|
|
class Queue {
|
|
EventQueueItem head;
|
|
EventQueueItem tail;
|
|
}
|