summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/java/awt/EventQueue.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/awt/EventQueue.java')
-rw-r--r--libjava/classpath/java/awt/EventQueue.java552
1 files changed, 552 insertions, 0 deletions
diff --git a/libjava/classpath/java/awt/EventQueue.java b/libjava/classpath/java/awt/EventQueue.java
new file mode 100644
index 00000000000..a8b00781321
--- /dev/null
+++ b/libjava/classpath/java/awt/EventQueue.java
@@ -0,0 +1,552 @@
+/* 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.ClasspathToolkit;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.InputMethodEvent;
+import java.awt.event.InvocationEvent;
+import java.awt.event.WindowEvent;
+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;
+
+ private long lastNativeQueueAccess = 0;
+ private long humanLatencyThreshold = 100;
+
+ synchronized 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
+
+ if (peekEvent() == null
+ && ((ClasspathToolkit) Toolkit.getDefaultToolkit()).nativeQueueEmpty())
+ {
+ 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();
+
+ ClasspathToolkit tk = ((ClasspathToolkit) Toolkit.getDefaultToolkit());
+ long curr = System.currentTimeMillis();
+
+ if (! tk.nativeQueueEmpty() &&
+ (curr - lastNativeQueueAccess > humanLatencyThreshold))
+ {
+ tk.iterateNativeQueue(this, false);
+ lastNativeQueueAccess = curr;
+ }
+
+ while (next_in == next_out)
+ {
+ // Only the EventDispatchThread associated with the top of the stack is
+ // allowed to get events from the native source; everyone else just
+ // waits on the head of the queue.
+
+ if (isDispatchThread())
+ {
+ // 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();
+
+ tk.iterateNativeQueue(this, true);
+ lastNativeQueueAccess = System.currentTimeMillis();
+ }
+ else
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException ie)
+ {
+ }
+ }
+ }
+
+ 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();
+ }
+
+ // Window events might represent the closing of a window, which
+ // might cause the end of the dispatch thread's life, so we'll wake
+ // it up here to give it a chance to check for shutdown.
+
+ if (!isDispatchThread()
+ || (evt.getID() == WindowEvent.WINDOW_CLOSED)
+ || (evt.getID() == WindowEvent.WINDOW_CLOSING))
+ ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue();
+
+ 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;
+
+ ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue();
+ 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;
+ }
+}
OpenPOWER on IntegriCloud