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 if (Frame.hasDisplayableFrames())
138 * Initializes a new instance of <code>EventQueue</code>.
142 queues = new Queue[2];
143 queues[NORM_PRIORITY] = new Queue();
144 queues[LOW_PRIORITY] = new Queue();
148 * Returns the next event in the queue. This method will block until
149 * an event is available or until the thread is interrupted.
151 * @return The next event in the queue.
153 * @exception InterruptedException If this thread is interrupted while
154 * waiting for an event to be posted to the queue.
156 public synchronized AWTEvent getNextEvent()
157 throws InterruptedException
160 return next.getNextEvent();
162 AWTEvent res = getNextEventImpl(true);
168 // Explicitly set dispathThread to null. If we don't do
169 // this, there is a race condition where dispatchThread
170 // can be != null even after the event dispatch thread has
171 // stopped running. If that happens, then the
172 // dispatchThread == null check in postEventImpl will
173 // fail, and a new event dispatch thread will not be
174 // created, leaving invokeAndWaits waiting indefinitely.
175 dispatchThread = null;
177 // Interrupt the event dispatch thread.
178 throw new InterruptedException();
182 res = getNextEventImpl(true);
189 * Fetches and possibly removes the next event from the internal queues.
190 * This method returns immediately. When all queues are empty, this returns
193 * @param remove <true> when the event should be removed from the queue,
194 * <code>false</code> otherwise
196 * @return the next event or <code>null</code> when all internal queues
199 private AWTEvent getNextEventImpl(boolean remove)
201 AWTEvent next = null;
202 for (int i = 0; i < queues.length && next == null; i++)
205 if (q.queueHead != null)
207 // Got an event, remove it.
211 // Unlink event from the queue.
212 q.queueHead = next.queueNext;
213 if (q.queueHead == null)
215 next.queueNext = null;
223 * Returns the next event in the queue without removing it from the queue.
224 * This method will block until an event is available or until the thread
227 * @return The next event in the queue.
228 * @specnote Does not block. Returns null if there are no events on the
231 public synchronized AWTEvent peekEvent()
234 return next.peekEvent();
236 return getNextEventImpl(false);
240 * Returns the next event in the queue that has the specified id
241 * without removing it from the queue.
242 * This method will block until an event is available or until the thread
245 * @param id The event id to return.
247 * @return The next event in the queue.
249 * @specnote Does not block. Returns null if there are no matching events
252 public synchronized AWTEvent peekEvent(int id)
255 return next.peekEvent(id);
258 for (int i = 0; i < queues.length && evt == null; i++)
262 while (evt != null && evt.id != id)
264 // At this point we either have found an event (evt != null -> exit
265 // for loop), or we have found no event (evt == null -> search next
272 * Posts a new event to the queue.
274 * @param evt The event to post to the queue.
276 * @exception NullPointerException If event is null.
278 public void postEvent(AWTEvent evt)
284 * Sorts events to their priority and calls
285 * {@link #postEventImpl(AWTEvent, int)}.
287 * @param evt the event to post
289 private synchronized final void postEventImpl(AWTEvent evt)
291 int priority = NORM_PRIORITY;
292 if (evt instanceof PaintEvent || evt instanceof LowPriorityEvent)
293 priority = LOW_PRIORITY;
294 // TODO: Maybe let Swing RepaintManager events also be processed with
296 if (evt instanceof NativeEventLoopRunningEvent)
298 nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning();
302 postEventImpl(evt, priority);
306 * Actually performs the event posting. This is needed because the
307 * RI doesn't use the public postEvent() method when transferring events
308 * between event queues in push() and pop().
310 * @param evt the event to post
311 * @param priority the priority of the event
313 private final void postEventImpl(AWTEvent evt, int priority)
316 throw new NullPointerException();
324 Object source = evt.getSource();
326 Queue q = queues[priority];
327 if (source instanceof Component)
329 // For PaintEvents, ask the ComponentPeer to coalesce the event
330 // when the component is heavyweight.
331 Component comp = (Component) source;
332 ComponentPeer peer = comp.peer;
333 if (peer != null && evt instanceof PaintEvent
334 && ! (peer instanceof LightweightPeer))
335 peer.coalescePaintEvent((PaintEvent) evt);
337 // Check for any events already on the queue with the same source
339 AWTEvent previous = null;
340 for (AWTEvent qevt = q.queueHead; qevt != null; qevt = qevt.queueNext)
342 Object src = qevt.getSource();
343 if (qevt.id == evt.id && src == comp)
345 // If there are, call coalesceEvents on the source component
346 // to see if they can be combined.
347 Component srccmp = (Component) src;
348 AWTEvent coalescedEvt = srccmp.coalesceEvents(qevt, evt);
349 if (coalescedEvt != null)
351 // Yes. Replace the existing event with the combined event.
352 if (qevt != coalescedEvt)
354 if (previous != null)
356 assert previous.queueNext == qevt;
357 previous.queueNext = coalescedEvt;
361 assert q.queueHead == qevt;
362 q.queueHead = coalescedEvt;
364 coalescedEvt.queueNext = qevt.queueNext;
365 if (q.queueTail == qevt)
366 q.queueTail = coalescedEvt;
367 qevt.queueNext = null;
376 if (q.queueHead == null)
378 // We have an empty queue. Set this event both as head and as tail.
384 // Note: queueTail should not be null here.
385 q.queueTail.queueNext = evt;
389 if (dispatchThread == null || !dispatchThread.isAlive())
391 dispatchThread = new EventDispatchThread(this);
392 dispatchThread.start();
399 * Causes runnable to have its run method called in the dispatch thread of the
400 * EventQueue. This will happen after all pending events are processed. The
401 * call blocks until this has happened. This method will throw an Error if
402 * called from the event dispatcher thread.
404 * @exception InterruptedException If another thread has interrupted
406 * @exception InvocationTargetException If an exception is thrown when running
411 public static void invokeAndWait(Runnable runnable)
412 throws InterruptedException, InvocationTargetException
414 if (isDispatchThread ())
415 throw new Error("Can't call invokeAndWait from event dispatch thread");
417 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
418 Object notifyObject = new Object();
421 new InvocationEvent(eq, runnable, notifyObject, true);
423 synchronized (notifyObject)
431 if ((exception = ie.getException()) != null)
432 throw new InvocationTargetException(exception);
436 * This arranges for runnable to have its run method called in the
437 * dispatch thread of the EventQueue. This will happen after all
438 * pending events are processed.
442 public static void invokeLater(Runnable runnable)
444 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
447 new InvocationEvent(eq, runnable, null, false);
453 * Return true if the current thread is the current AWT event dispatch
456 public static boolean isDispatchThread()
458 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
460 /* Find last EventQueue in chain */
461 while (eq.next != null)
464 return (Thread.currentThread() == eq.dispatchThread);
468 * Return the event currently being dispatched by the event
469 * dispatch thread. If the current thread is not the event
470 * dispatch thread, this method returns null.
474 public static AWTEvent getCurrentEvent()
476 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
477 Thread ct = Thread.currentThread();
479 /* Find out if this thread is the dispatch thread for any of the
480 EventQueues in the chain */
481 while (ct != eq.dispatchThread)
483 // Try next EventQueue, if any
485 return null; // Not an event dispatch thread
489 return eq.currentEvent;
493 * Allows a custom EventQueue implementation to replace this one.
494 * All pending events are transferred to the new queue. Calls to postEvent,
495 * getNextEvent, and peekEvent and others are forwarded to the pushed queue
496 * until it is removed with a pop().
498 * @exception NullPointerException if newEventQueue is null.
500 public synchronized void push(EventQueue newEventQueue)
502 if (newEventQueue == null)
503 throw new NullPointerException ();
505 /* Make sure we are at the top of the stack because callers can
506 only get a reference to the one at the bottom using
507 Toolkit.getDefaultToolkit().getSystemEventQueue() */
510 next.push (newEventQueue);
514 /* Make sure we have a live dispatch thread to drive the queue */
515 if (dispatchThread == null)
516 dispatchThread = new EventDispatchThread(this);
518 synchronized (newEventQueue)
520 // The RI transfers the events without calling the new eventqueue's
521 // push(), but using getNextEvent().
522 while (peekEvent() != null)
526 newEventQueue.postEventImpl(getNextEvent());
528 catch (InterruptedException ex)
530 // What should we do with this?
531 ex.printStackTrace();
534 newEventQueue.prev = this;
537 next = newEventQueue;
540 /** Transfer any pending events from this queue back to the parent queue that
541 * was previously push()ed. Event dispatch from this queue is suspended.
543 * @exception EmptyStackException If no previous push was made on this
546 protected void pop() throws EmptyStackException
548 /* The order is important here, we must get the prev lock first,
549 or deadlock could occur as callers usually get here following
550 prev's next pointer, and thus obtain prev's lock before trying
552 EventQueue previous = prev;
553 if (previous == null)
554 throw new EmptyStackException();
555 synchronized (previous)
559 EventQueue nextQueue = next;
560 if (nextQueue != null)
566 previous.next = null;
568 // The RI transfers the events without calling the new eventqueue's
569 // push(), so this should be OK and most effective.
570 while (peekEvent() != null)
574 previous.postEventImpl(getNextEvent());
576 catch (InterruptedException ex)
578 // What should we do with this?
579 ex.printStackTrace();
583 // Tell our EventDispatchThread that it can end
585 if (dispatchThread != null)
587 dispatchThread.interrupt();
588 dispatchThread = null;
596 * Dispatches an event. The manner in which the event is dispatched depends
597 * upon the type of the event and the type of the event's source object.
599 * @exception NullPointerException If event is null.
601 protected void dispatchEvent(AWTEvent evt)
605 if (evt instanceof InputEvent)
606 lastWhen = ((InputEvent) evt).getWhen();
607 else if (evt instanceof ActionEvent)
608 lastWhen = ((ActionEvent) evt).getWhen();
609 else if (evt instanceof InvocationEvent)
610 lastWhen = ((InvocationEvent) evt).getWhen();
612 if (evt instanceof ActiveEvent)
614 ActiveEvent active_evt = (ActiveEvent) evt;
615 active_evt.dispatch();
619 Object source = evt.getSource();
621 if (source instanceof Component)
623 Component srccmp = (Component) source;
624 srccmp.dispatchEvent(evt);
626 else if (source instanceof MenuComponent)
628 MenuComponent srccmp = (MenuComponent) source;
629 srccmp.dispatchEvent(evt);
635 * Returns the timestamp of the most recent event that had a timestamp, or
636 * the initialization time of the event queue if no events have been fired.
637 * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s,
638 * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have
639 * timestamps, but this may be added to other events in future versions.
640 * If this is called by the event dispatching thread, it can be any
641 * (sequential) value, but to other threads, the safest bet is to return
642 * System.currentTimeMillis().
644 * @return the most recent timestamp
645 * @see InputEvent#getWhen()
646 * @see ActionEvent#getWhen()
647 * @see InvocationEvent#getWhen()
648 * @see InputMethodEvent#getWhen()
651 public static long getMostRecentEventTime()
653 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
654 if (Thread.currentThread() != eq.dispatchThread)
655 return System.currentTimeMillis();