| /* EventQueue.java -- | |
| Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.awt; | |
| import gnu.java.awt.LowPriorityEvent; | |
| import gnu.java.awt.peer.NativeEventLoopRunningEvent; | |
| import java.awt.event.ActionEvent; | |
| import java.awt.event.InputEvent; | |
| import java.awt.event.InputMethodEvent; | |
| import java.awt.event.InvocationEvent; | |
| import java.awt.event.PaintEvent; | |
| import java.awt.peer.ComponentPeer; | |
| import java.awt.peer.LightweightPeer; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.util.EmptyStackException; | |
| /* Written using on-line Java 2 Platform Standard Edition v1.3 API | |
| * Specification, as well as "The Java Class Libraries", 2nd edition | |
| * (Addison-Wesley, 1998). | |
| * Status: Believed complete, but untested. | |
| */ | |
| /** | |
| * This class manages a queue of <code>AWTEvent</code> objects that | |
| * are posted to it. The AWT system uses only one event queue for all | |
| * events. | |
| * | |
| * @author Bryce McKinlay | |
| * @author Aaron M. Renn (arenn@urbanophile.com) | |
| */ | |
| public class EventQueue | |
| { | |
| /** | |
| * Indicates events that are processed with normal priority. This is normally | |
| * all events except PaintEvents. | |
| */ | |
| private static final int NORM_PRIORITY = 0; | |
| /** | |
| * Indicates events that are processed with lowes priority. This is normally | |
| * all PaintEvents and LowPriorityEvents. | |
| */ | |
| private static final int LOW_PRIORITY = 1; | |
| /** | |
| * Implements the actual queue. EventQueue has 2 internal queues for | |
| * different priorities: | |
| * 1 PaintEvents are always dispatched with low priority. | |
| * 2. All other events are dispatched with normal priority. | |
| * | |
| * This makes sure that the actual painting (output) is performed _after_ all | |
| * available input has been processed and that the paint regions are | |
| * coalesced as much as possible. | |
| */ | |
| private class Queue | |
| { | |
| /** | |
| * The first item in the queue. This is where events are popped from. | |
| */ | |
| AWTEvent queueHead; | |
| /** | |
| * The last item. This is where events are posted to. | |
| */ | |
| AWTEvent queueTail; | |
| } | |
| /** | |
| * The three internal event queues. | |
| * | |
| * @see Queue | |
| */ | |
| private Queue[] queues; | |
| private EventQueue next; | |
| private EventQueue prev; | |
| private AWTEvent currentEvent; | |
| private long lastWhen = System.currentTimeMillis(); | |
| private EventDispatchThread dispatchThread = new EventDispatchThread(this); | |
| private boolean nativeLoopRunning = false; | |
| private boolean isShutdown () | |
| { | |
| // This is the exact self-shutdown condition specified in J2SE: | |
| // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html | |
| if (nativeLoopRunning) | |
| return false; | |
| if (peekEvent() != null) | |
| return false; | |
| if (Frame.hasDisplayableFrames()) | |
| return false; | |
| return true; | |
| } | |
| /** | |
| * Initializes a new instance of <code>EventQueue</code>. | |
| */ | |
| public EventQueue() | |
| { | |
| queues = new Queue[2]; | |
| queues[NORM_PRIORITY] = new Queue(); | |
| queues[LOW_PRIORITY] = new Queue(); | |
| } | |
| /** | |
| * Returns the next event in the queue. This method will block until | |
| * an event is available or until the thread is interrupted. | |
| * | |
| * @return The next event in the queue. | |
| * | |
| * @exception InterruptedException If this thread is interrupted while | |
| * waiting for an event to be posted to the queue. | |
| */ | |
| public synchronized AWTEvent getNextEvent() | |
| throws InterruptedException | |
| { | |
| if (next != null) | |
| return next.getNextEvent(); | |
| AWTEvent res = getNextEventImpl(true); | |
| while (res == null) | |
| { | |
| if (isShutdown()) | |
| { | |
| // Explicitly set dispathThread to null. If we don't do | |
| // this, there is a race condition where dispatchThread | |
| // can be != null even after the event dispatch thread has | |
| // stopped running. If that happens, then the | |
| // dispatchThread == null check in postEventImpl will | |
| // fail, and a new event dispatch thread will not be | |
| // created, leaving invokeAndWaits waiting indefinitely. | |
| dispatchThread = null; | |
| // Interrupt the event dispatch thread. | |
| throw new InterruptedException(); | |
| } | |
| wait(); | |
| res = getNextEventImpl(true); | |
| } | |
| return res; | |
| } | |
| /** | |
| * Fetches and possibly removes the next event from the internal queues. | |
| * This method returns immediately. When all queues are empty, this returns | |
| * <code>null</code>: | |
| * | |
| * @param remove <true> when the event should be removed from the queue, | |
| * <code>false</code> otherwise | |
| * | |
| * @return the next event or <code>null</code> when all internal queues | |
| * are empty | |
| */ | |
| private AWTEvent getNextEventImpl(boolean remove) | |
| { | |
| AWTEvent next = null; | |
| for (int i = 0; i < queues.length && next == null; i++) | |
| { | |
| Queue q = queues[i]; | |
| if (q.queueHead != null) | |
| { | |
| // Got an event, remove it. | |
| next = q.queueHead; | |
| if (remove) | |
| { | |
| // Unlink event from the queue. | |
| q.queueHead = next.queueNext; | |
| if (q.queueHead == null) | |
| q.queueTail = null; | |
| next.queueNext = null; | |
| } | |
| } | |
| } | |
| return next; | |
| } | |
| /** | |
| * Returns the next event in the queue without removing it from the queue. | |
| * This method will block until an event is available or until the thread | |
| * is interrupted. | |
| * | |
| * @return The next event in the queue. | |
| * @specnote Does not block. Returns null if there are no events on the | |
| * queue. | |
| */ | |
| public synchronized AWTEvent peekEvent() | |
| { | |
| if (next != null) | |
| return next.peekEvent(); | |
| return getNextEventImpl(false); | |
| } | |
| /** | |
| * Returns the next event in the queue that has the specified id | |
| * without removing it from the queue. | |
| * This method will block until an event is available or until the thread | |
| * is interrupted. | |
| * | |
| * @param id The event id to return. | |
| * | |
| * @return The next event in the queue. | |
| * | |
| * @specnote Does not block. Returns null if there are no matching events | |
| * on the queue. | |
| */ | |
| public synchronized AWTEvent peekEvent(int id) | |
| { | |
| if (next != null) | |
| return next.peekEvent(id); | |
| AWTEvent evt = null; | |
| for (int i = 0; i < queues.length && evt == null; i++) | |
| { | |
| Queue q = queues[i]; | |
| evt = q.queueHead; | |
| while (evt != null && evt.id != id) | |
| evt = evt.queueNext; | |
| // At this point we either have found an event (evt != null -> exit | |
| // for loop), or we have found no event (evt == null -> search next | |
| // internal queue). | |
| } | |
| return evt; | |
| } | |
| /** | |
| * Posts a new event to the queue. | |
| * | |
| * @param evt The event to post to the queue. | |
| * | |
| * @exception NullPointerException If event is null. | |
| */ | |
| public void postEvent(AWTEvent evt) | |
| { | |
| postEventImpl(evt); | |
| } | |
| /** | |
| * Sorts events to their priority and calls | |
| * {@link #postEventImpl(AWTEvent, int)}. | |
| * | |
| * @param evt the event to post | |
| */ | |
| private synchronized final void postEventImpl(AWTEvent evt) | |
| { | |
| int priority = NORM_PRIORITY; | |
| if (evt instanceof PaintEvent || evt instanceof LowPriorityEvent) | |
| priority = LOW_PRIORITY; | |
| // TODO: Maybe let Swing RepaintManager events also be processed with | |
| // low priority. | |
| if (evt instanceof NativeEventLoopRunningEvent) | |
| { | |
| nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning(); | |
| notify(); | |
| return; | |
| } | |
| postEventImpl(evt, priority); | |
| } | |
| /** | |
| * Actually performs the event posting. This is needed because the | |
| * RI doesn't use the public postEvent() method when transferring events | |
| * between event queues in push() and pop(). | |
| * | |
| * @param evt the event to post | |
| * @param priority the priority of the event | |
| */ | |
| private final void postEventImpl(AWTEvent evt, int priority) | |
| { | |
| if (evt == null) | |
| throw new NullPointerException(); | |
| if (next != null) | |
| { | |
| next.postEvent(evt); | |
| return; | |
| } | |
| Object source = evt.getSource(); | |
| Queue q = queues[priority]; | |
| if (source instanceof Component) | |
| { | |
| // For PaintEvents, ask the ComponentPeer to coalesce the event | |
| // when the component is heavyweight. | |
| Component comp = (Component) source; | |
| ComponentPeer peer = comp.peer; | |
| if (peer != null && evt instanceof PaintEvent | |
| && ! (peer instanceof LightweightPeer)) | |
| peer.coalescePaintEvent((PaintEvent) evt); | |
| // Check for any events already on the queue with the same source | |
| // and ID. | |
| AWTEvent previous = null; | |
| for (AWTEvent qevt = q.queueHead; qevt != null; qevt = qevt.queueNext) | |
| { | |
| Object src = qevt.getSource(); | |
| if (qevt.id == evt.id && src == comp) | |
| { | |
| // If there are, call coalesceEvents on the source component | |
| // to see if they can be combined. | |
| Component srccmp = (Component) src; | |
| AWTEvent coalescedEvt = srccmp.coalesceEvents(qevt, evt); | |
| if (coalescedEvt != null) | |
| { | |
| // Yes. Replace the existing event with the combined event. | |
| if (qevt != coalescedEvt) | |
| { | |
| if (previous != null) | |
| { | |
| assert previous.queueNext == qevt; | |
| previous.queueNext = coalescedEvt; | |
| } | |
| else | |
| { | |
| assert q.queueHead == qevt; | |
| q.queueHead = coalescedEvt; | |
| } | |
| coalescedEvt.queueNext = qevt.queueNext; | |
| if (q.queueTail == qevt) | |
| q.queueTail = coalescedEvt; | |
| qevt.queueNext = null; | |
| } | |
| return; | |
| } | |
| } | |
| previous = qevt; | |
| } | |
| } | |
| if (q.queueHead == null) | |
| { | |
| // We have an empty queue. Set this event both as head and as tail. | |
| q.queueHead = evt; | |
| q.queueTail = evt; | |
| } | |
| else | |
| { | |
| // Note: queueTail should not be null here. | |
| q.queueTail.queueNext = evt; | |
| q.queueTail = evt; | |
| } | |
| if (dispatchThread == null || !dispatchThread.isAlive()) | |
| { | |
| dispatchThread = new EventDispatchThread(this); | |
| dispatchThread.start(); | |
| } | |
| notify(); | |
| } | |
| /** | |
| * Causes runnable to have its run method called in the dispatch thread of the | |
| * 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 event dispatcher thread. | |
| * | |
| * @exception InterruptedException If another thread has interrupted | |
| * this thread. | |
| * @exception InvocationTargetException If an exception is thrown when running | |
| * runnable. | |
| * | |
| * @since 1.2 | |
| */ | |
| public static void invokeAndWait(Runnable runnable) | |
| throws InterruptedException, InvocationTargetException | |
| { | |
| if (isDispatchThread ()) | |
| throw new Error("Can't call invokeAndWait from event dispatch thread"); | |
| EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); | |
| Object notifyObject = new Object(); | |
| InvocationEvent ie = | |
| new InvocationEvent(eq, runnable, notifyObject, true); | |
| synchronized (notifyObject) | |
| { | |
| eq.postEvent(ie); | |
| notifyObject.wait(); | |
| } | |
| Exception exception; | |
| if ((exception = ie.getException()) != null) | |
| throw new InvocationTargetException(exception); | |
| } | |
| /** | |
| * This arranges for runnable to have its run method called in the | |
| * dispatch thread of the EventQueue. This will happen after all | |
| * pending events are processed. | |
| * | |
| * @since 1.2 | |
| */ | |
| public static void invokeLater(Runnable runnable) | |
| { | |
| EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); | |
| InvocationEvent ie = | |
| new InvocationEvent(eq, runnable, null, false); | |
| eq.postEvent(ie); | |
| } | |
| /** | |
| * Return true if the current thread is the current AWT event dispatch | |
| * thread. | |
| */ | |
| public static boolean isDispatchThread() | |
| { | |
| EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); | |
| /* Find last EventQueue in chain */ | |
| while (eq.next != null) | |
| eq = eq.next; | |
| return (Thread.currentThread() == eq.dispatchThread); | |
| } | |
| /** | |
| * Return the event currently being dispatched by the event | |
| * dispatch thread. If the current thread is not the event | |
| * dispatch thread, this method returns null. | |
| * | |
| * @since 1.4 | |
| */ | |
| public static AWTEvent getCurrentEvent() | |
| { | |
| EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); | |
| Thread ct = Thread.currentThread(); | |
| /* Find out if this thread is the dispatch thread for any of the | |
| EventQueues in the chain */ | |
| while (ct != eq.dispatchThread) | |
| { | |
| // Try next EventQueue, if any | |
| if (eq.next == null) | |
| return null; // Not an event dispatch thread | |
| eq = eq.next; | |
| } | |
| return eq.currentEvent; | |
| } | |
| /** | |
| * Allows a custom EventQueue implementation to replace this one. | |
| * All pending events are transferred to the new queue. Calls to postEvent, | |
| * getNextEvent, and peekEvent and others are forwarded to the pushed queue | |
| * until it is removed with a pop(). | |
| * | |
| * @exception NullPointerException if newEventQueue is null. | |
| */ | |
| public synchronized void push(EventQueue newEventQueue) | |
| { | |
| if (newEventQueue == null) | |
| throw new NullPointerException (); | |
| /* Make sure we are at the top of the stack because callers can | |
| only get a reference to the one at the bottom using | |
| Toolkit.getDefaultToolkit().getSystemEventQueue() */ | |
| if (next != null) | |
| { | |
| next.push (newEventQueue); | |
| return; | |
| } | |
| /* Make sure we have a live dispatch thread to drive the queue */ | |
| if (dispatchThread == null) | |
| dispatchThread = new EventDispatchThread(this); | |
| synchronized (newEventQueue) | |
| { | |
| // The RI transfers the events without calling the new eventqueue's | |
| // push(), but using getNextEvent(). | |
| while (peekEvent() != null) | |
| { | |
| try | |
| { | |
| newEventQueue.postEventImpl(getNextEvent()); | |
| } | |
| catch (InterruptedException ex) | |
| { | |
| // What should we do with this? | |
| ex.printStackTrace(); | |
| } | |
| } | |
| newEventQueue.prev = this; | |
| } | |
| next = newEventQueue; | |
| } | |
| /** Transfer any pending events from this queue back to the parent queue that | |
| * was previously push()ed. Event dispatch from this queue is suspended. | |
| * | |
| * @exception EmptyStackException If no previous push was made on this | |
| * EventQueue. | |
| */ | |
| protected void pop() throws EmptyStackException | |
| { | |
| /* The order is important here, we must get the prev lock first, | |
| or deadlock could occur as callers usually get here following | |
| prev's next pointer, and thus obtain prev's lock before trying | |
| to get this lock. */ | |
| EventQueue previous = prev; | |
| if (previous == null) | |
| throw new EmptyStackException(); | |
| synchronized (previous) | |
| { | |
| synchronized (this) | |
| { | |
| EventQueue nextQueue = next; | |
| if (nextQueue != null) | |
| { | |
| nextQueue.pop(); | |
| } | |
| else | |
| { | |
| previous.next = null; | |
| // The RI transfers the events without calling the new eventqueue's | |
| // push(), so this should be OK and most effective. | |
| while (peekEvent() != null) | |
| { | |
| try | |
| { | |
| previous.postEventImpl(getNextEvent()); | |
| } | |
| catch (InterruptedException ex) | |
| { | |
| // What should we do with this? | |
| ex.printStackTrace(); | |
| } | |
| } | |
| prev = null; | |
| // Tell our EventDispatchThread that it can end | |
| // execution. | |
| if (dispatchThread != null) | |
| { | |
| dispatchThread.interrupt(); | |
| dispatchThread = null; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * 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. | |
| * | |
| * @exception NullPointerException If event is null. | |
| */ | |
| protected void dispatchEvent(AWTEvent evt) | |
| { | |
| currentEvent = evt; | |
| if (evt instanceof InputEvent) | |
| lastWhen = ((InputEvent) evt).getWhen(); | |
| else if (evt instanceof ActionEvent) | |
| lastWhen = ((ActionEvent) evt).getWhen(); | |
| else if (evt instanceof InvocationEvent) | |
| lastWhen = ((InvocationEvent) evt).getWhen(); | |
| if (evt instanceof ActiveEvent) | |
| { | |
| ActiveEvent active_evt = (ActiveEvent) evt; | |
| active_evt.dispatch(); | |
| } | |
| else | |
| { | |
| Object source = evt.getSource(); | |
| if (source instanceof Component) | |
| { | |
| Component srccmp = (Component) source; | |
| srccmp.dispatchEvent(evt); | |
| } | |
| else if (source instanceof MenuComponent) | |
| { | |
| MenuComponent srccmp = (MenuComponent) source; | |
| srccmp.dispatchEvent(evt); | |
| } | |
| } | |
| } | |
| /** | |
| * Returns the timestamp of the most recent event that had a timestamp, or | |
| * the initialization time of the event queue if no events have been fired. | |
| * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s, | |
| * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have | |
| * timestamps, but this may be added to other events in future versions. | |
| * If this is called by the event dispatching thread, it can be any | |
| * (sequential) value, but to other threads, the safest bet is to return | |
| * System.currentTimeMillis(). | |
| * | |
| * @return the most recent timestamp | |
| * @see InputEvent#getWhen() | |
| * @see ActionEvent#getWhen() | |
| * @see InvocationEvent#getWhen() | |
| * @see InputMethodEvent#getWhen() | |
| * @since 1.4 | |
| */ | |
| public static long getMostRecentEventTime() | |
| { | |
| EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); | |
| if (Thread.currentThread() != eq.dispatchThread) | |
| return System.currentTimeMillis(); | |
| return eq.lastWhen; | |
| } | |
| } |