OSDN Git Service

Fix "make dvi"
[pf3gnuchains/gcc-fork.git] / libjava / java / awt / EventQueue.java
1 /* EventQueue.java --
2    Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005  Free Software Foundation
3
4 This file is part of GNU Classpath.
5
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)
9 any later version.
10
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.
15
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
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
24 combination.
25
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. */
37
38
39 package java.awt;
40
41 import gnu.java.awt.ClasspathToolkit;
42
43 import java.awt.event.ActionEvent;
44 import java.awt.event.InputEvent;
45 import java.awt.event.InvocationEvent;
46 import java.awt.event.WindowEvent;
47 import java.lang.reflect.InvocationTargetException;
48 import java.util.EmptyStackException;
49
50 /* Written using on-line Java 2 Platform Standard Edition v1.3 API 
51  * Specification, as well as "The Java Class Libraries", 2nd edition 
52  * (Addison-Wesley, 1998).
53  * Status:  Believed complete, but untested.
54  */
55
56 /**
57  * This class manages a queue of <code>AWTEvent</code> objects that
58  * are posted to it.  The AWT system uses only one event queue for all
59  * events.
60  *
61  * @author Bryce McKinlay
62  * @author Aaron M. Renn (arenn@urbanophile.com)
63  */
64 public class EventQueue
65 {
66   private static final int INITIAL_QUEUE_DEPTH = 8;
67   private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH];
68
69   private int next_in = 0; // Index where next event will be added to queue
70   private int next_out = 0; // Index of next event to be removed from queue
71
72   private EventQueue next;
73   private EventQueue prev;
74   private AWTEvent currentEvent;
75   private long lastWhen = System.currentTimeMillis();
76
77   private EventDispatchThread dispatchThread = new EventDispatchThread(this);
78   private boolean shutdown = false;
79
80   private long lastNativeQueueAccess = 0;
81   private long humanLatencyThreshold = 100;
82
83   synchronized void setShutdown (boolean b) 
84   {
85     shutdown = b;
86   }
87
88   synchronized boolean isShutdown ()
89   {
90     if (shutdown)
91       return true;
92
93     // This is the exact self-shutdown condition specified in J2SE:
94     // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html
95     
96     if (peekEvent() == null
97         && ((ClasspathToolkit) Toolkit.getDefaultToolkit()).nativeQueueEmpty())
98       {
99         Frame[] frames = Frame.getFrames();
100         for (int i = 0; i < frames.length; ++i)
101           if (frames[i].isDisplayable())
102             return false;
103         return true;
104       }
105     return false;
106   }
107
108   /**
109    * Initializes a new instance of <code>EventQueue</code>.
110    */
111   public EventQueue()
112   {
113   }
114
115   /**
116    * Returns the next event in the queue.  This method will block until
117    * an event is available or until the thread is interrupted.
118    *
119    * @return The next event in the queue.
120    *
121    * @exception InterruptedException If this thread is interrupted while
122    * waiting for an event to be posted to the queue.
123    */
124   public synchronized AWTEvent getNextEvent()
125     throws InterruptedException
126   {
127     if (next != null)
128       return next.getNextEvent();
129     
130     ClasspathToolkit tk = ((ClasspathToolkit) Toolkit.getDefaultToolkit());
131     long curr = System.currentTimeMillis();
132
133     if (! tk.nativeQueueEmpty() &&
134         (curr - lastNativeQueueAccess > humanLatencyThreshold))
135       {
136         tk.iterateNativeQueue(this, false);
137         lastNativeQueueAccess = curr;
138       }
139
140     while (next_in == next_out)
141       {
142         // Only the EventDispatchThread associated with the top of the stack is
143         // allowed to get events from the native source; everyone else just
144         // waits on the head of the queue.
145
146         if (isDispatchThread())
147           {
148             // We are not allowed to return null from this method, yet it
149             // is possible that we actually have run out of native events
150             // in the enclosing while() loop, and none of the native events
151             // happened to cause AWT events. We therefore ought to check
152             // the isShutdown() condition here, before risking a "native
153             // wait". If we check it before entering this function we may
154             // wait forever for events after the shutdown condition has
155             // arisen.
156
157             if (isShutdown())
158               throw new InterruptedException();
159
160             tk.iterateNativeQueue(this, true);
161             lastNativeQueueAccess = System.currentTimeMillis();
162           }
163         else
164           {
165             try
166               {
167                 wait();
168               }
169             catch (InterruptedException ie)
170               {
171               }
172           }
173       }
174
175     AWTEvent res = queue[next_out];
176
177     if (++next_out == queue.length)
178       next_out = 0;
179     return res;
180   }
181
182   /**
183    * Returns the next event in the queue without removing it from the queue.
184    * This method will block until an event is available or until the thread
185    * is interrupted.
186    *
187    * @return The next event in the queue.
188    * @specnote Does not block. Returns null if there are no events on the 
189    *            queue. 
190    */ 
191   public synchronized AWTEvent peekEvent()
192   {
193     if (next != null)
194       return next.peekEvent();
195
196     if (next_in != next_out)
197       return queue[next_out];
198     else
199       return null;
200   }
201
202   /**
203    * Returns the next event in the queue that has the specified id
204    * without removing it from the queue.
205    * This method will block until an event is available or until the thread
206    * is interrupted.
207    *
208    * @param id The event id to return.
209    *
210    * @return The next event in the queue.
211    *
212    * @specnote Does not block. Returns null if there are no matching events 
213    *            on the queue. 
214    */ 
215   public synchronized AWTEvent peekEvent(int id)
216   {
217     if (next != null)
218       return next.peekEvent(id);
219
220     int i = next_out;
221     while (i != next_in)
222       {
223         AWTEvent qevt = queue[i];
224         if (qevt.id == id)
225           return qevt;
226       }
227     return null;
228   }
229
230   /**
231    * Posts a new event to the queue.
232    *
233    * @param evt The event to post to the queue.
234    *
235    * @exception NullPointerException If event is null.
236    */
237   public synchronized void postEvent(AWTEvent evt)
238   {
239     if (evt == null)
240       throw new NullPointerException();
241
242     if (next != null)
243       {
244         next.postEvent(evt);
245         return;
246       }
247
248     /* Check for any events already on the queue with the same source 
249        and ID. */       
250     int i = next_out;
251     while (i != next_in)
252       {
253         AWTEvent qevt = queue[i];
254         Object src;
255         if (qevt.id == evt.id
256             && (src = qevt.getSource()) == evt.getSource()
257             && src instanceof Component)
258           {
259             /* If there are, call coalesceEvents on the source component 
260                to see if they can be combined. */
261             Component srccmp = (Component) src;
262             AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt);
263             if (coalesced_evt != null)
264               {
265                 /* Yes. Replace the existing event with the combined event. */
266                 queue[i] = coalesced_evt;
267                 return;
268               }
269             break;
270           }
271         if (++i == queue.length)
272           i = 0;
273       }
274
275     queue[next_in] = evt;    
276     if (++next_in == queue.length)
277       next_in = 0;
278
279     if (next_in == next_out)
280       {
281         /* Queue is full. Extend it. */
282         AWTEvent[] oldQueue = queue;
283         queue = new AWTEvent[queue.length * 2];
284
285         int len = oldQueue.length - next_out;
286         System.arraycopy(oldQueue, next_out, queue, 0, len);
287         if (next_out != 0)
288           System.arraycopy(oldQueue, 0, queue, len, next_out);
289
290         next_out = 0;
291         next_in = oldQueue.length;
292       }
293     
294     if (dispatchThread == null || !dispatchThread.isAlive())
295       {
296         dispatchThread = new EventDispatchThread(this);
297         dispatchThread.start();
298       }
299
300     // Window events might represent the closing of a window, which
301     // might cause the end of the dispatch thread's life, so we'll wake
302     // it up here to give it a chance to check for shutdown.
303
304     if (!isDispatchThread() 
305         || (evt.getID() == WindowEvent.WINDOW_CLOSED)
306         || (evt.getID() == WindowEvent.WINDOW_CLOSING))
307       ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue();
308
309     notify();
310   }
311
312   /**
313    * Causes runnable to have its run method called in the dispatch thread of the
314    * EventQueue. This will happen after all pending events are processed. The
315    * call blocks until this has happened. This method will throw an Error if
316    * called from the event dispatcher thread.
317    *
318    * @exception InterruptedException If another thread has interrupted
319    * this thread.
320    * @exception InvocationTargetException If an exception is thrown when running
321    * runnable.
322    *
323    * @since 1.2
324    */
325   public static void invokeAndWait(Runnable runnable)
326     throws InterruptedException, InvocationTargetException
327   {
328     if (isDispatchThread ())
329       throw new Error("Can't call invokeAndWait from event dispatch thread");
330
331     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
332     Thread current = Thread.currentThread();
333
334     InvocationEvent ie = 
335       new InvocationEvent(eq, runnable, current, true);
336
337     synchronized (current)
338       {
339         eq.postEvent(ie);
340         current.wait();
341       }
342
343     Exception exception;
344
345     if ((exception = ie.getException()) != null)
346       throw new InvocationTargetException(exception);
347   }
348
349   /**
350    * This arranges for runnable to have its run method called in the
351    * dispatch thread of the EventQueue.  This will happen after all
352    * pending events are processed.
353    *
354    * @since 1.2
355    */
356   public static void invokeLater(Runnable runnable)
357   {
358     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
359
360     InvocationEvent ie = 
361       new InvocationEvent(eq, runnable, null, false);
362
363     eq.postEvent(ie);
364   }
365
366   /**
367    * Return true if the current thread is the current AWT event dispatch
368    * thread.
369    */
370   public static boolean isDispatchThread()
371   {
372     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
373     
374     /* Find last EventQueue in chain */ 
375     while (eq.next != null)
376       eq = eq.next;
377
378     return (Thread.currentThread() == eq.dispatchThread);
379   }
380
381   /**
382    * Return the event currently being dispatched by the event
383    * dispatch thread.  If the current thread is not the event
384    * dispatch thread, this method returns null.
385    *
386    * @since 1.4
387    */
388   public static AWTEvent getCurrentEvent()
389   {
390     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
391     Thread ct = Thread.currentThread();
392     
393     /* Find out if this thread is the dispatch thread for any of the
394        EventQueues in the chain */ 
395     while (ct != eq.dispatchThread)
396       {
397         // Try next EventQueue, if any
398         if (eq.next == null)
399            return null;  // Not an event dispatch thread
400         eq = eq.next;
401       }
402
403     return eq.currentEvent;
404   }
405
406   /**
407    * Allows a custom EventQueue implementation to replace this one. 
408    * All pending events are transferred to the new queue. Calls to postEvent,
409    * getNextEvent, and peekEvent and others are forwarded to the pushed queue
410    * until it is removed with a pop().
411    *
412    * @exception NullPointerException if newEventQueue is null.
413    */
414   public synchronized void push(EventQueue newEventQueue)
415   {
416     if (newEventQueue == null)
417       throw new NullPointerException ();
418
419     /* Make sure we are at the top of the stack because callers can
420        only get a reference to the one at the bottom using
421        Toolkit.getDefaultToolkit().getSystemEventQueue() */
422     if (next != null)
423       {
424         next.push (newEventQueue);
425         return;
426       }
427
428     /* Make sure we have a live dispatch thread to drive the queue */
429     if (dispatchThread == null)
430       dispatchThread = new EventDispatchThread(this);
431
432     int i = next_out;
433     while (i != next_in)
434       {
435         newEventQueue.postEvent(queue[i]);
436         next_out = i;
437         if (++i == queue.length)
438           i = 0;
439       }
440
441     next = newEventQueue;
442     newEventQueue.prev = this;    
443   }
444
445   /** Transfer any pending events from this queue back to the parent queue that
446     * was previously push()ed. Event dispatch from this queue is suspended.
447     *
448     * @exception EmptyStackException If no previous push was made on this
449     * EventQueue.
450     */
451   protected void pop() throws EmptyStackException
452   {
453     if (prev == null)
454       throw new EmptyStackException();
455
456     /* The order is important here, we must get the prev lock first,
457        or deadlock could occur as callers usually get here following
458        prev's next pointer, and thus obtain prev's lock before trying
459        to get this lock. */
460     synchronized (prev)
461       {
462         prev.next = next;
463         if (next != null)
464           next.prev = prev;
465
466         synchronized (this)
467           {
468             int i = next_out;
469             while (i != next_in)
470               {
471                 prev.postEvent(queue[i]);
472                 next_out = i;
473                 if (++i == queue.length)
474                   i = 0;
475               }
476             // Empty the queue so it can be reused
477             next_in = 0;
478             next_out = 0;
479
480             ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue();
481             setShutdown(true);
482             dispatchThread = null;
483             this.notifyAll();
484           }
485       }
486   }
487
488   /**
489    * Dispatches an event. The manner in which the event is dispatched depends
490    * upon the type of the event and the type of the event's source object.
491    *
492    * @exception NullPointerException If event is null.
493    */
494   protected void dispatchEvent(AWTEvent evt)
495   {
496     currentEvent = evt;
497
498     if (evt instanceof InputEvent)
499       lastWhen = ((InputEvent) evt).getWhen();
500     else if (evt instanceof ActionEvent)
501       lastWhen = ((ActionEvent) evt).getWhen();
502     else if (evt instanceof InvocationEvent)
503       lastWhen = ((InvocationEvent) evt).getWhen();
504
505     if (evt instanceof ActiveEvent)
506       {
507         ActiveEvent active_evt = (ActiveEvent) evt;
508         active_evt.dispatch();
509       }
510     else
511       {
512         Object source = evt.getSource();
513
514         if (source instanceof Component)
515           {
516             Component srccmp = (Component) source;
517             srccmp.dispatchEvent(evt);
518           }
519         else if (source instanceof MenuComponent)
520           {
521             MenuComponent srccmp = (MenuComponent) source;
522             srccmp.dispatchEvent(evt);
523           }
524       }
525   }
526
527   /**
528    * Returns the timestamp of the most recent event that had a timestamp, or
529    * the initialization time of the event queue if no events have been fired.
530    * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s,
531    * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have
532    * timestamps, but this may be added to other events in future versions.
533    * If this is called by the event dispatching thread, it can be any
534    * (sequential) value, but to other threads, the safest bet is to return
535    * System.currentTimeMillis().
536    *
537    * @return the most recent timestamp
538    * @see InputEvent#getWhen()
539    * @see ActionEvent#getWhen()
540    * @see InvocationEvent#getWhen()
541    * @see InputMethodEvent#getWhen()
542    * @since 1.4
543    */
544   public static long getMostRecentEventTime()
545   {
546     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
547     if (Thread.currentThread() != eq.dispatchThread)
548       return System.currentTimeMillis();
549     return eq.lastWhen;
550   }
551 }