2 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.awt.event.ActionEvent;
42 import java.awt.event.InputEvent;
43 import java.awt.event.InputMethodEvent;
44 import java.awt.event.InvocationEvent;
45 import java.awt.event.WindowEvent;
46 import java.lang.reflect.InvocationTargetException;
47 import java.util.EmptyStackException;
49 /* Written using on-line Java 2 Platform Standard Edition v1.3 API
50 * Specification, as well as "The Java Class Libraries", 2nd edition
51 * (Addison-Wesley, 1998).
52 * Status: Believed complete, but untested.
56 * This class manages a queue of <code>AWTEvent</code> objects that
57 * are posted to it. The AWT system uses only one event queue for all
60 * @author Bryce McKinlay
61 * @author Aaron M. Renn (arenn@urbanophile.com)
63 public class EventQueue
65 private static final int INITIAL_QUEUE_DEPTH = 8;
66 private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH];
68 private int next_in = 0; // Index where next event will be added to queue
69 private int next_out = 0; // Index of next event to be removed from queue
71 private EventQueue next;
72 private EventQueue prev;
73 private AWTEvent currentEvent;
74 private long lastWhen = System.currentTimeMillis();
76 private EventDispatchThread dispatchThread = new EventDispatchThread(this);
77 private boolean shutdown = false;
79 synchronized private void setShutdown (boolean b)
84 synchronized boolean isShutdown ()
89 // This is the exact self-shutdown condition specified in J2SE:
90 // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html
92 // FIXME: check somewhere that the native queue is empty
93 if (peekEvent() == null)
95 Frame[] frames = Frame.getFrames();
96 for (int i = 0; i < frames.length; ++i)
97 if (frames[i].isDisplayable())
105 * Initializes a new instance of <code>EventQueue</code>.
112 * Returns the next event in the queue. This method will block until
113 * an event is available or until the thread is interrupted.
115 * @return The next event in the queue.
117 * @exception InterruptedException If this thread is interrupted while
118 * waiting for an event to be posted to the queue.
120 public synchronized AWTEvent getNextEvent()
121 throws InterruptedException
124 return next.getNextEvent();
126 while (next_in == next_out)
128 // We are not allowed to return null from this method, yet it
129 // is possible that we actually have run out of native events
130 // in the enclosing while() loop, and none of the native events
131 // happened to cause AWT events. We therefore ought to check
132 // the isShutdown() condition here, before risking a "native
133 // wait". If we check it before entering this function we may
134 // wait forever for events after the shutdown condition has
138 throw new InterruptedException();
143 AWTEvent res = queue[next_out];
145 if (++next_out == queue.length)
151 * Returns the next event in the queue without removing it from the queue.
152 * This method will block until an event is available or until the thread
155 * @return The next event in the queue.
156 * @specnote Does not block. Returns null if there are no events on the
159 public synchronized AWTEvent peekEvent()
162 return next.peekEvent();
164 if (next_in != next_out)
165 return queue[next_out];
171 * Returns the next event in the queue that has the specified id
172 * without removing it from the queue.
173 * This method will block until an event is available or until the thread
176 * @param id The event id to return.
178 * @return The next event in the queue.
180 * @specnote Does not block. Returns null if there are no matching events
183 public synchronized AWTEvent peekEvent(int id)
186 return next.peekEvent(id);
191 AWTEvent qevt = queue[i];
199 * Posts a new event to the queue.
201 * @param evt The event to post to the queue.
203 * @exception NullPointerException If event is null.
205 public synchronized void postEvent(AWTEvent evt)
208 throw new NullPointerException();
216 /* Check for any events already on the queue with the same source
221 AWTEvent qevt = queue[i];
223 if (qevt.id == evt.id
224 && (src = qevt.getSource()) == evt.getSource()
225 && src instanceof Component)
227 /* If there are, call coalesceEvents on the source component
228 to see if they can be combined. */
229 Component srccmp = (Component) src;
230 AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt);
231 if (coalesced_evt != null)
233 /* Yes. Replace the existing event with the combined event. */
234 queue[i] = coalesced_evt;
239 if (++i == queue.length)
243 queue[next_in] = evt;
244 if (++next_in == queue.length)
247 if (next_in == next_out)
249 /* Queue is full. Extend it. */
250 AWTEvent[] oldQueue = queue;
251 queue = new AWTEvent[queue.length * 2];
253 int len = oldQueue.length - next_out;
254 System.arraycopy(oldQueue, next_out, queue, 0, len);
256 System.arraycopy(oldQueue, 0, queue, len, next_out);
259 next_in = oldQueue.length;
262 if (dispatchThread == null || !dispatchThread.isAlive())
264 dispatchThread = new EventDispatchThread(this);
265 dispatchThread.start();
272 * Causes runnable to have its run method called in the dispatch thread of the
273 * EventQueue. This will happen after all pending events are processed. The
274 * call blocks until this has happened. This method will throw an Error if
275 * called from the event dispatcher thread.
277 * @exception InterruptedException If another thread has interrupted
279 * @exception InvocationTargetException If an exception is thrown when running
284 public static void invokeAndWait(Runnable runnable)
285 throws InterruptedException, InvocationTargetException
287 if (isDispatchThread ())
288 throw new Error("Can't call invokeAndWait from event dispatch thread");
290 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
291 Thread current = Thread.currentThread();
294 new InvocationEvent(eq, runnable, current, true);
296 synchronized (current)
304 if ((exception = ie.getException()) != null)
305 throw new InvocationTargetException(exception);
309 * This arranges for runnable to have its run method called in the
310 * dispatch thread of the EventQueue. This will happen after all
311 * pending events are processed.
315 public static void invokeLater(Runnable runnable)
317 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
320 new InvocationEvent(eq, runnable, null, false);
326 * Return true if the current thread is the current AWT event dispatch
329 public static boolean isDispatchThread()
331 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
333 /* Find last EventQueue in chain */
334 while (eq.next != null)
337 return (Thread.currentThread() == eq.dispatchThread);
341 * Return the event currently being dispatched by the event
342 * dispatch thread. If the current thread is not the event
343 * dispatch thread, this method returns null.
347 public static AWTEvent getCurrentEvent()
349 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
350 Thread ct = Thread.currentThread();
352 /* Find out if this thread is the dispatch thread for any of the
353 EventQueues in the chain */
354 while (ct != eq.dispatchThread)
356 // Try next EventQueue, if any
358 return null; // Not an event dispatch thread
362 return eq.currentEvent;
366 * Allows a custom EventQueue implementation to replace this one.
367 * All pending events are transferred to the new queue. Calls to postEvent,
368 * getNextEvent, and peekEvent and others are forwarded to the pushed queue
369 * until it is removed with a pop().
371 * @exception NullPointerException if newEventQueue is null.
373 public synchronized void push(EventQueue newEventQueue)
375 if (newEventQueue == null)
376 throw new NullPointerException ();
378 /* Make sure we are at the top of the stack because callers can
379 only get a reference to the one at the bottom using
380 Toolkit.getDefaultToolkit().getSystemEventQueue() */
383 next.push (newEventQueue);
387 /* Make sure we have a live dispatch thread to drive the queue */
388 if (dispatchThread == null)
389 dispatchThread = new EventDispatchThread(this);
394 newEventQueue.postEvent(queue[i]);
396 if (++i == queue.length)
400 next = newEventQueue;
401 newEventQueue.prev = this;
404 /** Transfer any pending events from this queue back to the parent queue that
405 * was previously push()ed. Event dispatch from this queue is suspended.
407 * @exception EmptyStackException If no previous push was made on this
410 protected void pop() throws EmptyStackException
413 throw new EmptyStackException();
415 /* The order is important here, we must get the prev lock first,
416 or deadlock could occur as callers usually get here following
417 prev's next pointer, and thus obtain prev's lock before trying
430 prev.postEvent(queue[i]);
432 if (++i == queue.length)
435 // Empty the queue so it can be reused
440 dispatchThread = null;
447 * Dispatches an event. The manner in which the event is dispatched depends
448 * upon the type of the event and the type of the event's source object.
450 * @exception NullPointerException If event is null.
452 protected void dispatchEvent(AWTEvent evt)
456 if (evt instanceof InputEvent)
457 lastWhen = ((InputEvent) evt).getWhen();
458 else if (evt instanceof ActionEvent)
459 lastWhen = ((ActionEvent) evt).getWhen();
460 else if (evt instanceof InvocationEvent)
461 lastWhen = ((InvocationEvent) evt).getWhen();
463 if (evt instanceof ActiveEvent)
465 ActiveEvent active_evt = (ActiveEvent) evt;
466 active_evt.dispatch();
470 Object source = evt.getSource();
472 if (source instanceof Component)
474 Component srccmp = (Component) source;
475 srccmp.dispatchEvent(evt);
477 else if (source instanceof MenuComponent)
479 MenuComponent srccmp = (MenuComponent) source;
480 srccmp.dispatchEvent(evt);
486 * Returns the timestamp of the most recent event that had a timestamp, or
487 * the initialization time of the event queue if no events have been fired.
488 * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s,
489 * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have
490 * timestamps, but this may be added to other events in future versions.
491 * If this is called by the event dispatching thread, it can be any
492 * (sequential) value, but to other threads, the safest bet is to return
493 * System.currentTimeMillis().
495 * @return the most recent timestamp
496 * @see InputEvent#getWhen()
497 * @see ActionEvent#getWhen()
498 * @see InvocationEvent#getWhen()
499 * @see InputMethodEvent#getWhen()
502 public static long getMostRecentEventTime()
504 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
505 if (Thread.currentThread() != eq.dispatchThread)
506 return System.currentTimeMillis();