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