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 gnu.java.awt.LowPriorityEvent;
42 import gnu.java.awt.peer.NativeEventLoopRunningEvent;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.InputEvent;
46 import java.awt.event.InputMethodEvent;
47 import java.awt.event.InvocationEvent;
48 import java.awt.event.PaintEvent;
49 import java.awt.peer.ComponentPeer;
50 import java.awt.peer.LightweightPeer;
51 import java.lang.reflect.InvocationTargetException;
52 import java.util.EmptyStackException;
54 /* Written using on-line Java 2 Platform Standard Edition v1.3 API
55 * Specification, as well as "The Java Class Libraries", 2nd edition
56 * (Addison-Wesley, 1998).
57 * Status: Believed complete, but untested.
61 * This class manages a queue of <code>AWTEvent</code> objects that
62 * are posted to it. The AWT system uses only one event queue for all
65 * @author Bryce McKinlay
66 * @author Aaron M. Renn (arenn@urbanophile.com)
68 public class EventQueue
71 * Indicates events that are processed with normal priority. This is normally
72 * all events except PaintEvents.
74 private static final int NORM_PRIORITY = 0;
77 * Indicates events that are processed with lowes priority. This is normally
78 * all PaintEvents and LowPriorityEvents.
80 private static final int LOW_PRIORITY = 1;
83 * Implements the actual queue. EventQueue has 2 internal queues for
84 * different priorities:
85 * 1 PaintEvents are always dispatched with low priority.
86 * 2. All other events are dispatched with normal priority.
88 * This makes sure that the actual painting (output) is performed _after_ all
89 * available input has been processed and that the paint regions are
90 * coalesced as much as possible.
95 * The first item in the queue. This is where events are popped from.
100 * The last item. This is where events are posted to.
106 * The three internal event queues.
110 private Queue[] queues;
112 private EventQueue next;
113 private EventQueue prev;
114 private AWTEvent currentEvent;
115 private long lastWhen = System.currentTimeMillis();
117 private EventDispatchThread dispatchThread = new EventDispatchThread(this);
118 private boolean nativeLoopRunning = false;
120 private boolean isShutdown ()
122 // This is the exact self-shutdown condition specified in J2SE:
123 // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html
125 if (nativeLoopRunning)
128 if (peekEvent() != null)
131 Frame[] frames = Frame.getFrames();
132 for (int i = 0; i < frames.length; ++i)
133 if (frames[i].isDisplayable())
140 * Initializes a new instance of <code>EventQueue</code>.
144 queues = new Queue[2];
145 queues[NORM_PRIORITY] = new Queue();
146 queues[LOW_PRIORITY] = new Queue();
150 * Returns the next event in the queue. This method will block until
151 * an event is available or until the thread is interrupted.
153 * @return The next event in the queue.
155 * @exception InterruptedException If this thread is interrupted while
156 * waiting for an event to be posted to the queue.
158 public synchronized AWTEvent getNextEvent()
159 throws InterruptedException
162 return next.getNextEvent();
164 AWTEvent res = getNextEventImpl(true);
170 // Explicitly set dispathThread to null. If we don't do
171 // this, there is a race condition where dispatchThread
172 // can be != null even after the event dispatch thread has
173 // stopped running. If that happens, then the
174 // dispatchThread == null check in postEventImpl will
175 // fail, and a new event dispatch thread will not be
176 // created, leaving invokeAndWaits waiting indefinitely.
177 dispatchThread = null;
179 // Interrupt the event dispatch thread.
180 throw new InterruptedException();
184 res = getNextEventImpl(true);
191 * Fetches and possibly removes the next event from the internal queues.
192 * This method returns immediately. When all queues are empty, this returns
195 * @param remove <true> when the event should be removed from the queue,
196 * <code>false</code> otherwise
198 * @return the next event or <code>null</code> when all internal queues
201 private AWTEvent getNextEventImpl(boolean remove)
203 AWTEvent next = null;
204 for (int i = 0; i < queues.length && next == null; i++)
207 if (q.queueHead != null)
209 // Got an event, remove it.
213 // Unlink event from the queue.
214 q.queueHead = next.queueNext;
215 if (q.queueHead == null)
217 next.queueNext = null;
225 * Returns the next event in the queue without removing it from the queue.
226 * This method will block until an event is available or until the thread
229 * @return The next event in the queue.
230 * @specnote Does not block. Returns null if there are no events on the
233 public synchronized AWTEvent peekEvent()
236 return next.peekEvent();
238 return getNextEventImpl(false);
242 * Returns the next event in the queue that has the specified id
243 * without removing it from the queue.
244 * This method will block until an event is available or until the thread
247 * @param id The event id to return.
249 * @return The next event in the queue.
251 * @specnote Does not block. Returns null if there are no matching events
254 public synchronized AWTEvent peekEvent(int id)
257 return next.peekEvent(id);
260 for (int i = 0; i < queues.length && evt == null; i++)
264 while (evt != null && evt.id != id)
266 // At this point we either have found an event (evt != null -> exit
267 // for loop), or we have found no event (evt == null -> search next
274 * Posts a new event to the queue.
276 * @param evt The event to post to the queue.
278 * @exception NullPointerException If event is null.
280 public void postEvent(AWTEvent evt)
286 * Sorts events to their priority and calls
287 * {@link #postEventImpl(AWTEvent, int)}.
289 * @param evt the event to post
291 private synchronized final void postEventImpl(AWTEvent evt)
293 int priority = NORM_PRIORITY;
294 if (evt instanceof PaintEvent || evt instanceof LowPriorityEvent)
295 priority = LOW_PRIORITY;
296 // TODO: Maybe let Swing RepaintManager events also be processed with
298 if (evt instanceof NativeEventLoopRunningEvent)
300 nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning();
304 postEventImpl(evt, priority);
308 * Actually performs the event posting. This is needed because the
309 * RI doesn't use the public postEvent() method when transferring events
310 * between event queues in push() and pop().
312 * @param evt the event to post
313 * @param priority the priority of the event
315 private final void postEventImpl(AWTEvent evt, int priority)
318 throw new NullPointerException();
326 Object source = evt.getSource();
328 Queue q = queues[priority];
329 if (source instanceof Component)
331 // For PaintEvents, ask the ComponentPeer to coalesce the event
332 // when the component is heavyweight.
333 Component comp = (Component) source;
334 ComponentPeer peer = comp.peer;
335 if (peer != null && evt instanceof PaintEvent
336 && ! (peer instanceof LightweightPeer))
337 peer.coalescePaintEvent((PaintEvent) evt);
339 // Check for any events already on the queue with the same source
341 AWTEvent previous = null;
342 for (AWTEvent qevt = q.queueHead; qevt != null; qevt = qevt.queueNext)
344 Object src = qevt.getSource();
345 if (qevt.id == evt.id && src == comp)
347 // If there are, call coalesceEvents on the source component
348 // to see if they can be combined.
349 Component srccmp = (Component) src;
350 AWTEvent coalescedEvt = srccmp.coalesceEvents(qevt, evt);
351 if (coalescedEvt != null)
353 // Yes. Replace the existing event with the combined event.
354 if (qevt != coalescedEvt)
356 if (previous != null)
358 assert previous.queueNext == qevt;
359 previous.queueNext = coalescedEvt;
363 assert q.queueHead == qevt;
364 q.queueHead = coalescedEvt;
366 coalescedEvt.queueNext = qevt.queueNext;
367 if (q.queueTail == qevt)
368 q.queueTail = coalescedEvt;
369 qevt.queueNext = null;
378 if (q.queueHead == null)
380 // We have an empty queue. Set this event both as head and as tail.
386 // Note: queueTail should not be null here.
387 q.queueTail.queueNext = evt;
391 if (dispatchThread == null || !dispatchThread.isAlive())
393 dispatchThread = new EventDispatchThread(this);
394 dispatchThread.start();
401 * Causes runnable to have its run method called in the dispatch thread of the
402 * EventQueue. This will happen after all pending events are processed. The
403 * call blocks until this has happened. This method will throw an Error if
404 * called from the event dispatcher thread.
406 * @exception InterruptedException If another thread has interrupted
408 * @exception InvocationTargetException If an exception is thrown when running
413 public static void invokeAndWait(Runnable runnable)
414 throws InterruptedException, InvocationTargetException
416 if (isDispatchThread ())
417 throw new Error("Can't call invokeAndWait from event dispatch thread");
419 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
420 Object notifyObject = new Object();
423 new InvocationEvent(eq, runnable, notifyObject, true);
425 synchronized (notifyObject)
433 if ((exception = ie.getException()) != null)
434 throw new InvocationTargetException(exception);
438 * This arranges for runnable to have its run method called in the
439 * dispatch thread of the EventQueue. This will happen after all
440 * pending events are processed.
444 public static void invokeLater(Runnable runnable)
446 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
449 new InvocationEvent(eq, runnable, null, false);
455 * Return true if the current thread is the current AWT event dispatch
458 public static boolean isDispatchThread()
460 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
462 /* Find last EventQueue in chain */
463 while (eq.next != null)
466 return (Thread.currentThread() == eq.dispatchThread);
470 * Return the event currently being dispatched by the event
471 * dispatch thread. If the current thread is not the event
472 * dispatch thread, this method returns null.
476 public static AWTEvent getCurrentEvent()
478 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
479 Thread ct = Thread.currentThread();
481 /* Find out if this thread is the dispatch thread for any of the
482 EventQueues in the chain */
483 while (ct != eq.dispatchThread)
485 // Try next EventQueue, if any
487 return null; // Not an event dispatch thread
491 return eq.currentEvent;
495 * Allows a custom EventQueue implementation to replace this one.
496 * All pending events are transferred to the new queue. Calls to postEvent,
497 * getNextEvent, and peekEvent and others are forwarded to the pushed queue
498 * until it is removed with a pop().
500 * @exception NullPointerException if newEventQueue is null.
502 public synchronized void push(EventQueue newEventQueue)
504 if (newEventQueue == null)
505 throw new NullPointerException ();
507 /* Make sure we are at the top of the stack because callers can
508 only get a reference to the one at the bottom using
509 Toolkit.getDefaultToolkit().getSystemEventQueue() */
512 next.push (newEventQueue);
516 /* Make sure we have a live dispatch thread to drive the queue */
517 if (dispatchThread == null)
518 dispatchThread = new EventDispatchThread(this);
520 synchronized (newEventQueue)
522 // The RI transfers the events without calling the new eventqueue's
523 // push(), but using getNextEvent().
524 while (peekEvent() != null)
528 newEventQueue.postEventImpl(getNextEvent());
530 catch (InterruptedException ex)
532 // What should we do with this?
533 ex.printStackTrace();
536 newEventQueue.prev = this;
539 next = newEventQueue;
542 /** Transfer any pending events from this queue back to the parent queue that
543 * was previously push()ed. Event dispatch from this queue is suspended.
545 * @exception EmptyStackException If no previous push was made on this
548 protected void pop() throws EmptyStackException
550 /* The order is important here, we must get the prev lock first,
551 or deadlock could occur as callers usually get here following
552 prev's next pointer, and thus obtain prev's lock before trying
554 EventQueue previous = prev;
555 if (previous == null)
556 throw new EmptyStackException();
557 synchronized (previous)
561 EventQueue nextQueue = next;
562 if (nextQueue != null)
568 previous.next = null;
570 // The RI transfers the events without calling the new eventqueue's
571 // push(), so this should be OK and most effective.
572 while (peekEvent() != null)
576 previous.postEventImpl(getNextEvent());
578 catch (InterruptedException ex)
580 // What should we do with this?
581 ex.printStackTrace();
585 // Tell our EventDispatchThread that it can end
587 if (dispatchThread != null)
589 dispatchThread.interrupt();
590 dispatchThread = null;
598 * Dispatches an event. The manner in which the event is dispatched depends
599 * upon the type of the event and the type of the event's source object.
601 * @exception NullPointerException If event is null.
603 protected void dispatchEvent(AWTEvent evt)
607 if (evt instanceof InputEvent)
608 lastWhen = ((InputEvent) evt).getWhen();
609 else if (evt instanceof ActionEvent)
610 lastWhen = ((ActionEvent) evt).getWhen();
611 else if (evt instanceof InvocationEvent)
612 lastWhen = ((InvocationEvent) evt).getWhen();
614 if (evt instanceof ActiveEvent)
616 ActiveEvent active_evt = (ActiveEvent) evt;
617 active_evt.dispatch();
621 Object source = evt.getSource();
623 if (source instanceof Component)
625 Component srccmp = (Component) source;
626 srccmp.dispatchEvent(evt);
628 else if (source instanceof MenuComponent)
630 MenuComponent srccmp = (MenuComponent) source;
631 srccmp.dispatchEvent(evt);
637 * Returns the timestamp of the most recent event that had a timestamp, or
638 * the initialization time of the event queue if no events have been fired.
639 * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s,
640 * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have
641 * timestamps, but this may be added to other events in future versions.
642 * If this is called by the event dispatching thread, it can be any
643 * (sequential) value, but to other threads, the safest bet is to return
644 * System.currentTimeMillis().
646 * @return the most recent timestamp
647 * @see InputEvent#getWhen()
648 * @see ActionEvent#getWhen()
649 * @see InvocationEvent#getWhen()
650 * @see InputMethodEvent#getWhen()
653 public static long getMostRecentEventTime()
655 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
656 if (Thread.currentThread() != eq.dispatchThread)
657 return System.currentTimeMillis();