/*
* 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;
}