| /* 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 java.awt.event.ActionEvent; |
| import java.awt.event.InputEvent; |
| import java.awt.event.InputMethodEvent; |
| import java.awt.event.InvocationEvent; |
| 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 |
| { |
| private static final int INITIAL_QUEUE_DEPTH = 8; |
| private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH]; |
| |
| private int next_in = 0; // Index where next event will be added to queue |
| private int next_out = 0; // Index of next event to be removed from queue |
| |
| private EventQueue next; |
| private EventQueue prev; |
| private AWTEvent currentEvent; |
| private long lastWhen = System.currentTimeMillis(); |
| |
| private EventDispatchThread dispatchThread = new EventDispatchThread(this); |
| private boolean shutdown = false; |
| |
| synchronized private void setShutdown (boolean b) |
| { |
| shutdown = b; |
| } |
| |
| synchronized boolean isShutdown () |
| { |
| if (shutdown) |
| return true; |
| |
| // 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 |
| |
| // FIXME: check somewhere that the native queue is empty |
| if (peekEvent() == null) |
| { |
| Frame[] frames = Frame.getFrames(); |
| for (int i = 0; i < frames.length; ++i) |
| if (frames[i].isDisplayable()) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Initializes a new instance of <code>EventQueue</code>. |
| */ |
| public EventQueue() |
| { |
| } |
| |
| /** |
| * 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(); |
| |
| while (next_in == next_out) |
| { |
| // We are not allowed to return null from this method, yet it |
| // is possible that we actually have run out of native events |
| // in the enclosing while() loop, and none of the native events |
| // happened to cause AWT events. We therefore ought to check |
| // the isShutdown() condition here, before risking a "native |
| // wait". If we check it before entering this function we may |
| // wait forever for events after the shutdown condition has |
| // arisen. |
| |
| if (isShutdown()) |
| throw new InterruptedException(); |
| |
| wait(); |
| } |
| |
| AWTEvent res = queue[next_out]; |
| |
| if (++next_out == queue.length) |
| next_out = 0; |
| return res; |
| } |
| |
| /** |
| * 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(); |
| |
| if (next_in != next_out) |
| return queue[next_out]; |
| else |
| return null; |
| } |
| |
| /** |
| * 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); |
| |
| int i = next_out; |
| while (i != next_in) |
| { |
| AWTEvent qevt = queue[i]; |
| if (qevt.id == id) |
| return qevt; |
| } |
| return null; |
| } |
| |
| /** |
| * Posts a new event to the queue. |
| * |
| * @param evt The event to post to the queue. |
| * |
| * @exception NullPointerException If event is null. |
| */ |
| public synchronized void postEvent(AWTEvent evt) |
| { |
| if (evt == null) |
| throw new NullPointerException(); |
| |
| if (next != null) |
| { |
| next.postEvent(evt); |
| return; |
| } |
| |
| /* Check for any events already on the queue with the same source |
| and ID. */ |
| int i = next_out; |
| while (i != next_in) |
| { |
| AWTEvent qevt = queue[i]; |
| Object src; |
| if (qevt.id == evt.id |
| && (src = qevt.getSource()) == evt.getSource() |
| && src instanceof Component) |
| { |
| /* If there are, call coalesceEvents on the source component |
| to see if they can be combined. */ |
| Component srccmp = (Component) src; |
| AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt); |
| if (coalesced_evt != null) |
| { |
| /* Yes. Replace the existing event with the combined event. */ |
| queue[i] = coalesced_evt; |
| return; |
| } |
| break; |
| } |
| if (++i == queue.length) |
| i = 0; |
| } |
| |
| queue[next_in] = evt; |
| if (++next_in == queue.length) |
| next_in = 0; |
| |
| if (next_in == next_out) |
| { |
| /* Queue is full. Extend it. */ |
| AWTEvent[] oldQueue = queue; |
| queue = new AWTEvent[queue.length * 2]; |
| |
| int len = oldQueue.length - next_out; |
| System.arraycopy(oldQueue, next_out, queue, 0, len); |
| if (next_out != 0) |
| System.arraycopy(oldQueue, 0, queue, len, next_out); |
| |
| next_out = 0; |
| next_in = oldQueue.length; |
| } |
| |
| 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(); |
| Thread current = Thread.currentThread(); |
| |
| InvocationEvent ie = |
| new InvocationEvent(eq, runnable, current, true); |
| |
| synchronized (current) |
| { |
| eq.postEvent(ie); |
| current.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); |
| |
| int i = next_out; |
| while (i != next_in) |
| { |
| newEventQueue.postEvent(queue[i]); |
| next_out = i; |
| if (++i == queue.length) |
| i = 0; |
| } |
| |
| next = newEventQueue; |
| newEventQueue.prev = this; |
| } |
| |
| /** 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 |
| { |
| if (prev == null) |
| throw new 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. */ |
| synchronized (prev) |
| { |
| prev.next = next; |
| if (next != null) |
| next.prev = prev; |
| |
| synchronized (this) |
| { |
| int i = next_out; |
| while (i != next_in) |
| { |
| prev.postEvent(queue[i]); |
| next_out = i; |
| if (++i == queue.length) |
| i = 0; |
| } |
| // Empty the queue so it can be reused |
| next_in = 0; |
| next_out = 0; |
| |
| setShutdown(true); |
| dispatchThread = null; |
| this.notifyAll(); |
| } |
| } |
| } |
| |
| /** |
| * 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; |
| } |
| } |