blob: f49bacb670ae6e0d544d0d239425cc1ce3fe4bfc [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt;
27
28import java.awt.event.InputEvent;
29import java.awt.event.MouseEvent;
30import java.awt.event.ActionEvent;
31import java.awt.event.WindowEvent;
32import java.lang.reflect.Method;
33import java.security.AccessController;
34import sun.security.action.GetPropertyAction;
35import sun.awt.AWTAutoShutdown;
36import sun.awt.SunToolkit;
37
38import java.util.Vector;
39import java.util.logging.*;
40
41import sun.awt.dnd.SunDragSourceContextPeer;
42
43/**
44 * EventDispatchThread is a package-private AWT class which takes
45 * events off the EventQueue and dispatches them to the appropriate
46 * AWT components.
47 *
48 * The Thread starts a "permanent" event pump with a call to
49 * pumpEvents(Conditional) in its run() method. Event handlers can choose to
50 * block this event pump at any time, but should start a new pump (<b>not</b>
51 * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
52 * secondary event pump will exit automatically as soon as the Condtional
53 * evaluate()s to false and an additional Event is pumped and dispatched.
54 *
55 * @author Tom Ball
56 * @author Amy Fowler
57 * @author Fred Ecks
58 * @author David Mendenhall
59 *
60 * @since 1.1
61 */
62class EventDispatchThread extends Thread {
63 private static final Logger eventLog = Logger.getLogger("java.awt.event.EventDispatchThread");
64
65 private EventQueue theQueue;
66 private boolean doDispatch = true;
67 private static final int ANY_EVENT = -1;
68
69 private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
70 // used in handleException
71 private int modalFiltersCount = 0;
72
73 EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
74 super(group, name);
75 theQueue = queue;
76 }
77
78 void stopDispatchingImpl(boolean wait) {
79 // Note: We stop dispatching via a flag rather than using
80 // Thread.interrupt() because we can't guarantee that the wait()
81 // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98
82
83 StopDispatchEvent stopEvent = new StopDispatchEvent();
84
85 // wait for the dispatcher to complete
86 if (Thread.currentThread() != this) {
87
88 // fix 4122683, 4128923
89 // Post an empty event to ensure getNextEvent is unblocked
90 //
91 // We have to use postEventPrivate instead of postEvent because
92 // EventQueue.pop calls EventDispatchThread.stopDispatching.
93 // Calling SunToolkit.flushPendingEvents in this case could
94 // lead to deadlock.
95 theQueue.postEventPrivate(stopEvent);
96
97 if (wait) {
98 try {
99 join();
100 } catch(InterruptedException e) {
101 }
102 }
103 } else {
104 stopEvent.dispatch();
105 }
106 synchronized (theQueue) {
107 if (theQueue.getDispatchThread() == this) {
108 theQueue.detachDispatchThread();
109 }
110 }
111 }
112
113 public void stopDispatching() {
114 stopDispatchingImpl(true);
115 }
116
117 public void stopDispatchingLater() {
118 stopDispatchingImpl(false);
119 }
120
121 class StopDispatchEvent extends AWTEvent implements ActiveEvent {
122 /*
123 * serialVersionUID
124 */
125 static final long serialVersionUID = -3692158172100730735L;
126
127 public StopDispatchEvent() {
128 super(EventDispatchThread.this,0);
129 }
130
131 public void dispatch() {
132 doDispatch = false;
133 }
134 }
135
136 public void run() {
137 try {
138 pumpEvents(new Conditional() {
139 public boolean evaluate() {
140 return true;
141 }
142 });
143 } finally {
144 /*
145 * This synchronized block is to secure that the event dispatch
146 * thread won't die in the middle of posting a new event to the
147 * associated event queue. It is important because we notify
148 * that the event dispatch thread is busy after posting a new event
149 * to its queue, so the EventQueue.dispatchThread reference must
150 * be valid at that point.
151 */
152 synchronized (theQueue) {
153 if (theQueue.getDispatchThread() == this) {
154 theQueue.detachDispatchThread();
155 }
156 /*
157 * Event dispatch thread dies in case of an uncaught exception.
158 * A new event dispatch thread for this queue will be started
159 * only if a new event is posted to it. In case if no more
160 * events are posted after this thread died all events that
161 * currently are in the queue will never be dispatched.
162 */
163 /*
164 * Fix for 4648733. Check both the associated java event
165 * queue and the PostEventQueue.
166 */
167 if (theQueue.peekEvent() != null ||
168 !SunToolkit.isPostEventQueueEmpty()) {
169 theQueue.initDispatchThread();
170 }
171 AWTAutoShutdown.getInstance().notifyThreadFree(this);
172 }
173 }
174 }
175
176 void pumpEvents(Conditional cond) {
177 pumpEvents(ANY_EVENT, cond);
178 }
179
180 void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
181 pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
182 }
183
184 void pumpEvents(int id, Conditional cond) {
185 pumpEventsForHierarchy(id, cond, null);
186 }
187
188 void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent)
189 {
190 pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
191 }
192
193 void pumpEventsForFilter(Conditional cond, EventFilter filter) {
194 pumpEventsForFilter(ANY_EVENT, cond, filter);
195 }
196
197 void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
198 addEventFilter(filter);
199 while (doDispatch && cond.evaluate()) {
200 if (isInterrupted() || !pumpOneEventForFilters(id)) {
201 doDispatch = false;
202 }
203 }
204 removeEventFilter(filter);
205 }
206
207 void addEventFilter(EventFilter filter) {
208 synchronized (eventFilters) {
209 if (!eventFilters.contains(filter)) {
210 if (filter instanceof ModalEventFilter) {
211 ModalEventFilter newFilter = (ModalEventFilter)filter;
212 int k = 0;
213 for (k = 0; k < eventFilters.size(); k++) {
214 EventFilter f = eventFilters.get(k);
215 if (f instanceof ModalEventFilter) {
216 ModalEventFilter cf = (ModalEventFilter)f;
217 if (cf.compareTo(newFilter) > 0) {
218 break;
219 }
220 }
221 }
222 eventFilters.add(k, filter);
223 modalFiltersCount++;
224 } else {
225 eventFilters.add(filter);
226 }
227 }
228 }
229 }
230
231 void removeEventFilter(EventFilter filter) {
232 synchronized (eventFilters) {
233 if (eventFilters.contains(filter)) {
234 if (filter instanceof ModalEventFilter) {
235 modalFiltersCount--;
236 }
237 eventFilters.remove(filter);
238 }
239 }
240 }
241
242 boolean pumpOneEventForFilters(int id) {
243 try {
244 AWTEvent event;
245 boolean eventOK;
246 do {
247 event = (id == ANY_EVENT)
248 ? theQueue.getNextEvent()
249 : theQueue.getNextEvent(id);
250
251 eventOK = true;
252 synchronized (eventFilters) {
253 for (int i = eventFilters.size() - 1; i >= 0; i--) {
254 EventFilter f = eventFilters.get(i);
255 EventFilter.FilterAction accept = f.acceptEvent(event);
256 if (accept == EventFilter.FilterAction.REJECT) {
257 eventOK = false;
258 break;
259 } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
260 break;
261 }
262 }
263 }
264 eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
265 if (!eventOK) {
266 event.consume();
267 }
268 }
269 while (eventOK == false);
270
271 if (eventLog.isLoggable(Level.FINEST)) {
272 eventLog.log(Level.FINEST, "Dispatching: " + event);
273 }
274
275 theQueue.dispatchEvent(event);
276 return true;
277 }
278 catch (ThreadDeath death) {
279 return false;
280
281 }
282 catch (InterruptedException interruptedException) {
283 return false; // AppContext.dispose() interrupts all
284 // Threads in the AppContext
285
286 }
287 // Can get and throw only unchecked exceptions
288 catch (RuntimeException e) {
289 processException(e, modalFiltersCount > 0);
290 } catch (Error e) {
291 processException(e, modalFiltersCount > 0);
292 }
293 return true;
294 }
295
296 private void processException(Throwable e, boolean isModal) {
297 if (eventLog.isLoggable(Level.FINE)) {
298 eventLog.log(Level.FINE, "Processing exception: " + e +
299 ", isModal = " + isModal);
300 }
301 if (!handleException(e)) {
302 // See bug ID 4499199.
303 // If we are in a modal dialog, we cannot throw
304 // an exception for the ThreadGroup to handle (as added
305 // in RFE 4063022). If we did, the message pump of
306 // the modal dialog would be interrupted.
307 // We instead choose to handle the exception ourselves.
308 // It may be useful to add either a runtime flag or API
309 // later if someone would like to instead dispose the
310 // dialog and allow the thread group to handle it.
311 if (isModal) {
312 System.err.println(
313 "Exception occurred during event dispatching:");
314 e.printStackTrace();
315 } else if (e instanceof RuntimeException) {
316 throw (RuntimeException)e;
317 } else if (e instanceof Error) {
318 throw (Error)e;
319 }
320 }
321 }
322
323 private static final String handlerPropName = "sun.awt.exception.handler";
324 private static String handlerClassName = null;
325 private static String NO_HANDLER = new String();
326
327 /**
328 * Handles an exception thrown in the event-dispatch thread.
329 *
330 * <p> If the system property "sun.awt.exception.handler" is defined, then
331 * when this method is invoked it will attempt to do the following:
332 *
333 * <ol>
334 * <li> Load the class named by the value of that property, using the
335 * current thread's context class loader,
336 * <li> Instantiate that class using its zero-argument constructor,
337 * <li> Find the resulting handler object's <tt>public void handle</tt>
338 * method, which should take a single argument of type
339 * <tt>Throwable</tt>, and
340 * <li> Invoke the handler's <tt>handle</tt> method, passing it the
341 * <tt>thrown</tt> argument that was passed to this method.
342 * </ol>
343 *
344 * If any of the first three steps fail then this method will return
345 * <tt>false</tt> and all following invocations of this method will return
346 * <tt>false</tt> immediately. An exception thrown by the handler object's
347 * <tt>handle</tt> will be caught, and will cause this method to return
348 * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully
349 * invoked, then this method will return <tt>true</tt>. This method will
350 * never throw any sort of exception.
351 *
352 * <p> <i>Note:</i> This method is a temporary hack to work around the
353 * absence of a real API that provides the ability to replace the
354 * event-dispatch thread. The magic "sun.awt.exception.handler" property
355 * <i>will be removed</i> in a future release.
356 *
357 * @param thrown The Throwable that was thrown in the event-dispatch
358 * thread
359 *
360 * @return <tt>false</tt> if any of the above steps failed, otherwise
361 * <tt>true</tt>
362 */
363 private boolean handleException(Throwable thrown) {
364
365 try {
366
367 if (handlerClassName == NO_HANDLER) {
368 return false; /* Already tried, and failed */
369 }
370
371 /* Look up the class name */
372 if (handlerClassName == null) {
373 handlerClassName = ((String) AccessController.doPrivileged(
374 new GetPropertyAction(handlerPropName)));
375 if (handlerClassName == null) {
376 handlerClassName = NO_HANDLER; /* Do not try this again */
377 return false;
378 }
379 }
380
381 /* Load the class, instantiate it, and find its handle method */
382 Method m;
383 Object h;
384 try {
385 ClassLoader cl = Thread.currentThread().getContextClassLoader();
386 Class c = Class.forName(handlerClassName, true, cl);
387 m = c.getMethod("handle", new Class[] { Throwable.class });
388 h = c.newInstance();
389 } catch (Throwable x) {
390 handlerClassName = NO_HANDLER; /* Do not try this again */
391 return false;
392 }
393
394 /* Finally, invoke the handler */
395 m.invoke(h, new Object[] { thrown });
396
397 } catch (Throwable x) {
398 return false;
399 }
400
401 return true;
402 }
403
404 boolean isDispatching(EventQueue eq) {
405 return theQueue.equals(eq);
406 }
407
408 EventQueue getEventQueue() { return theQueue; }
409
410 private static class HierarchyEventFilter implements EventFilter {
411 private Component modalComponent;
412 public HierarchyEventFilter(Component modalComponent) {
413 this.modalComponent = modalComponent;
414 }
415 public FilterAction acceptEvent(AWTEvent event) {
416 if (modalComponent != null) {
417 int eventID = event.getID();
418 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
419 (eventID <= MouseEvent.MOUSE_LAST);
420 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
421 (eventID <= ActionEvent.ACTION_LAST);
422 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
423 /*
424 * filter out MouseEvent and ActionEvent that's outside
425 * the modalComponent hierarchy.
426 * KeyEvent is handled by using enqueueKeyEvent
427 * in Dialog.show
428 */
429 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
430 /*
431 * Modal internal frames are handled separately. If event is
432 * for some component from another heavyweight than modalComp,
433 * it is accepted. If heavyweight is the same - we still accept
434 * event and perform further filtering in LightweightDispatcher
435 */
436 return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
437 }
438 if (mouseEvent || actionEvent || windowClosingEvent) {
439 Object o = event.getSource();
440 if (o instanceof sun.awt.ModalExclude) {
441 // Exclude this object from modality and
442 // continue to pump it's events.
443 return FilterAction.ACCEPT;
444 } else if (o instanceof Component) {
445 Component c = (Component) o;
446 // 5.0u3 modal exclusion
447 boolean modalExcluded = false;
448 if (modalComponent instanceof Container) {
449 while (c != modalComponent && c != null) {
450 if ((c instanceof Window) &&
451 (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
452 // Exclude this window and all its children from
453 // modality and continue to pump it's events.
454 modalExcluded = true;
455 break;
456 }
457 c = c.getParent();
458 }
459 }
460 if (!modalExcluded && (c != modalComponent)) {
461 return FilterAction.REJECT;
462 }
463 }
464 }
465 }
466 return FilterAction.ACCEPT;
467 }
468 }
469}