1. /*
  2. * @(#)EventQueue.java 1.71 01/02/09
  3. *
  4. * Copyright 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt;
  11. import java.awt.event.PaintEvent;
  12. import java.awt.event.InvocationEvent;
  13. import java.awt.event.KeyEvent;
  14. import java.awt.event.MouseEvent;
  15. import java.awt.ActiveEvent;
  16. import java.util.EmptyStackException;
  17. import java.lang.reflect.InvocationTargetException;
  18. import sun.awt.PeerEvent;
  19. import sun.awt.SunToolkit;
  20. import sun.awt.DebugHelper;
  21. /**
  22. * EventQueue is a platform-independent class that queues events, both
  23. * from the underlying peer classes and from trusted application classes.
  24. * <p>
  25. * Some browsers partition applets in different code bases into separate
  26. * contexts, and establish walls between these contexts. In such a scenario,
  27. * there will be one EventQueue per context. Other browsers place all applets
  28. * into the same context, implying that there will be only a single, global
  29. * EventQueue for all applets. This behavior is implementation-dependent.
  30. * Consult your browser's documentation for more information.
  31. *
  32. * @author Thomas Ball
  33. * @author Fred Ecks
  34. * @author David Mendenhall
  35. *
  36. * @version 1.71, 02/09/01
  37. * @since 1.1
  38. */
  39. public class EventQueue {
  40. private static final DebugHelper dbg = DebugHelper.create(EventQueue.class);
  41. // From Thread.java
  42. private static int threadInitNumber;
  43. private static synchronized int nextThreadNum() {
  44. return threadInitNumber++;
  45. }
  46. private static final int LOW_PRIORITY = 0;
  47. private static final int NORM_PRIORITY = 1;
  48. private static final int HIGH_PRIORITY = 2;
  49. private static final int NUM_PRIORITIES = HIGH_PRIORITY + 1;
  50. /*
  51. * We maintain one Queue for each priority that the EventQueue supports.
  52. * That is, the EventQueue object is actually implemented as
  53. * NUM_PRIORITIES queues and all Events on a particular internal Queue
  54. * have identical priority. Events are pulled off the EventQueue starting
  55. * with the Queue of highest priority. We progress in decreasing order
  56. * across all Queues.
  57. */
  58. private Queue[] queues = new Queue[NUM_PRIORITIES];
  59. /*
  60. * The next EventQueue on the stack, or null if this EventQueue is
  61. * on the top of the stack. If nextQueue is non-null, requests to post
  62. * an event are forwarded to nextQueue.
  63. */
  64. private EventQueue nextQueue;
  65. /*
  66. * The previous EventQueue on the stack, or null if this is the
  67. * "base" EventQueue.
  68. */
  69. private EventQueue previousQueue;
  70. private EventDispatchThread dispatchThread;
  71. /*
  72. * Debugging flag -- set true and recompile to enable checking.
  73. */
  74. private final static boolean debug = false;
  75. public EventQueue() {
  76. for (int i = 0; i < NUM_PRIORITIES; i++) {
  77. queues[i] = new Queue();
  78. }
  79. String name = "AWT-EventQueue-" + nextThreadNum();
  80. dispatchThread = new EventDispatchThread(name, this);
  81. dispatchThread.setPriority(Thread.NORM_PRIORITY + 1);
  82. dispatchThread.start();
  83. }
  84. /**
  85. * Post a 1.1-style event to the EventQueue. If there is an
  86. * existing event on the queue with the same ID and event source,
  87. * the source Component's coalesceEvents method will be called.
  88. *
  89. * @param theEvent an instance of java.awt.AWTEvent, or a
  90. * subclass of it.
  91. */
  92. public void postEvent(AWTEvent theEvent) {
  93. Toolkit toolkit = Toolkit.getDefaultToolkit();
  94. if (toolkit instanceof SunToolkit) {
  95. ((SunToolkit)toolkit).flushPendingEvents();
  96. }
  97. postEventPrivate(theEvent);
  98. }
  99. /**
  100. * Post a 1.1-style event to the EventQueue. If there is an
  101. * existing event on the queue with the same ID and event source,
  102. * the source Component's coalesceEvents method will be called.
  103. *
  104. * @param theEvent an instance of java.awt.AWTEvent, or a
  105. * subclass of it.
  106. */
  107. final void postEventPrivate(AWTEvent theEvent) {
  108. synchronized(this) {
  109. int id = theEvent.getID();
  110. if (nextQueue != null) {
  111. // Forward event to top of EventQueue stack.
  112. nextQueue.postEventPrivate(theEvent);
  113. } else if (theEvent instanceof PeerEvent &&
  114. (((PeerEvent)theEvent).getFlags() &
  115. PeerEvent.PRIORITY_EVENT) != 0) {
  116. postEvent(theEvent, HIGH_PRIORITY);
  117. } else if (id == PaintEvent.PAINT ||
  118. id == PaintEvent.UPDATE) {
  119. postEvent(theEvent, LOW_PRIORITY);
  120. } else {
  121. postEvent(theEvent, NORM_PRIORITY);
  122. }
  123. }
  124. }
  125. /**
  126. * Posts the event to the internal Queue of specified priority,
  127. * coalescing as appropriate.
  128. */
  129. private void postEvent(AWTEvent theEvent, int priority) {
  130. EventQueueItem newItem = new EventQueueItem(theEvent);
  131. if (queues[priority].head == null) {
  132. boolean shouldNotify = noEvents();
  133. queues[priority].head = queues[priority].tail = newItem;
  134. // This component doesn't have any events of this type on the
  135. // queue, so we have to initialize the RepaintArea with theEvent
  136. if (theEvent.getID() == PaintEvent.PAINT ||
  137. theEvent.getID() == PaintEvent.UPDATE) {
  138. Object source = theEvent.getSource();
  139. ((Component)source).coalesceEvents(theEvent, theEvent);
  140. }
  141. if (shouldNotify) {
  142. notifyAll();
  143. }
  144. } else {
  145. Object source = theEvent.getSource();
  146. boolean isPeerEvent = theEvent instanceof PeerEvent;
  147. // For Component source events, traverse the entire list,
  148. // trying to coalesce events
  149. if (source instanceof Component) {
  150. EventQueueItem q = queues[priority].head;
  151. // fix bug 4301264, do not coalesce mouse move/drag events
  152. // across other types of mouse events.
  153. if (theEvent.id == Event.MOUSE_MOVE ||
  154. theEvent.id == Event.MOUSE_DRAG) {
  155. EventQueueItem qm;
  156. for(qm = q; qm != null; qm = qm.next) {
  157. if ((qm.event instanceof MouseEvent) &&
  158. qm.id != theEvent.id) {
  159. q = qm;
  160. }
  161. }
  162. }
  163. for (;;) {
  164. if (q.id == newItem.id && q.event.getSource() == source) {
  165. AWTEvent coalescedEvent;
  166. coalescedEvent = ((Component)source).coalesceEvents(q.event, theEvent);
  167. if (isPeerEvent) {
  168. if( coalescedEvent == null && q.event instanceof PeerEvent) {
  169. coalescedEvent = ((PeerEvent)q.event).coalesceEvents((PeerEvent)theEvent);
  170. }
  171. }
  172. if (coalescedEvent != null) {
  173. // Remove debugging statement because
  174. // calling AWTEvent.toString here causes a
  175. // deadlock.
  176. //
  177. // if (dbg.on) {
  178. // dbg.println("EventQueue coalesced event: " +
  179. // coalescedEvent);
  180. // }
  181. q.event = coalescedEvent;
  182. return;
  183. }
  184. }
  185. if (q.next != null) {
  186. q = q.next;
  187. } else {
  188. break;
  189. }
  190. }
  191. }
  192. // The event was not coalesced or has non-Component source.
  193. // Insert it at the end of the appropriate Queue.
  194. if (theEvent.getID() == PaintEvent.PAINT ||
  195. theEvent.getID() == PaintEvent.UPDATE) {
  196. // This component doesn't have any events of this type on the
  197. // queue, so we have to initialize the RepaintArea with theEvent
  198. ((Component)source).coalesceEvents(theEvent, theEvent);
  199. }
  200. queues[priority].tail.next = newItem;
  201. queues[priority].tail = newItem;
  202. }
  203. }
  204. /**
  205. * @return whether an event is pending on any of the separate Queues
  206. */
  207. private boolean noEvents() {
  208. for (int i = 0; i < NUM_PRIORITIES; i++) {
  209. if (queues[i].head != null) {
  210. return false;
  211. }
  212. }
  213. return true;
  214. }
  215. /**
  216. * Remove an event from the EventQueue and return it. This method will
  217. * block until an event has been posted by another thread.
  218. * @return the next AWTEvent
  219. * @exception InterruptedException
  220. * if another thread has interrupted this thread.
  221. */
  222. public synchronized AWTEvent getNextEvent() throws InterruptedException {
  223. do {
  224. for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
  225. if (queues[i].head != null) {
  226. EventQueueItem eqi = queues[i].head;
  227. queues[i].head = eqi.next;
  228. if (eqi.next == null) {
  229. queues[i].tail = null;
  230. }
  231. return eqi.event;
  232. }
  233. }
  234. wait();
  235. } while(true);
  236. }
  237. /**
  238. * Return the first event on the EventQueue without removing it.
  239. * @return the first event
  240. */
  241. public synchronized AWTEvent peekEvent() {
  242. for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
  243. if (queues[i].head != null) {
  244. return queues[i].head.event;
  245. }
  246. }
  247. return null;
  248. }
  249. /**
  250. * Return the first event with the specified id, if any.
  251. * @param id the id of the type of event desired.
  252. * @return the first event of the specified id
  253. */
  254. public synchronized AWTEvent peekEvent(int id) {
  255. for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
  256. EventQueueItem q = queues[i].head;
  257. for (; q != null; q = q.next) {
  258. if (q.id == id) {
  259. return q.event;
  260. }
  261. }
  262. }
  263. return null;
  264. }
  265. /**
  266. * Dispatch an event. The manner in which the event is
  267. * dispatched depends upon the type of the event and the
  268. * type of the event's source
  269. * object:
  270. * <p> </p>
  271. * <table border>
  272. * <tr>
  273. * <th>Event Type</th>
  274. * <th>Source Type</th>
  275. * <th>Dispatched To</th>
  276. * </tr>
  277. * <tr>
  278. * <td>ActiveEvent</td>
  279. * <td>Any</td>
  280. * <td>event.dispatch()</td>
  281. * </tr>
  282. * <tr>
  283. * <td>Other</td>
  284. * <td>Component</td>
  285. * <td>source.dispatchEvent(AWTEvent)</td>
  286. * </tr>
  287. * <tr>
  288. * <td>Other</td>
  289. * <td>MenuComponent</td>
  290. * <td>source.dispatchEvent(AWTEvent)</td>
  291. * </tr>
  292. * <tr>
  293. * <td>Other</td>
  294. * <td>Other</td>
  295. * <td>No action (ignored)</td>
  296. * </tr>
  297. * </table>
  298. * <p> </p>
  299. * @param theEvent an instance of java.awt.AWTEvent, or a
  300. * subclass of it.
  301. */
  302. protected void dispatchEvent(AWTEvent event) {
  303. Object src = event.getSource();
  304. if (event instanceof ActiveEvent) {
  305. // This could become the sole method of dispatching in time.
  306. ((ActiveEvent)event).dispatch();
  307. } else if (src instanceof Component) {
  308. ((Component)src).dispatchEvent(event);
  309. } else if (src instanceof MenuComponent) {
  310. ((MenuComponent)src).dispatchEvent(event);
  311. } else {
  312. System.err.println("unable to dispatch event: " + event);
  313. }
  314. }
  315. /**
  316. * Replace the existing EventQueue with the specified one.
  317. * Any pending events are transferred to the new EventQueue
  318. * for processing by it.
  319. *
  320. * @param an EventQueue (or subclass thereof) instance to be used.
  321. * @see java.awt.EventQueue#pop
  322. */
  323. public synchronized void push(EventQueue newEventQueue) {
  324. if (debug) {
  325. System.out.println("EventQueue.push(" + newEventQueue + ")");
  326. }
  327. if (nextQueue != null) {
  328. nextQueue.push(newEventQueue);
  329. return;
  330. }
  331. synchronized (newEventQueue) {
  332. // Transfer all events forward to new EventQueue.
  333. while (peekEvent() != null) {
  334. try {
  335. newEventQueue.postEventPrivate(getNextEvent());
  336. } catch (InterruptedException ie) {
  337. if (debug) {
  338. System.err.println("interrupted push:");
  339. ie.printStackTrace(System.err);
  340. }
  341. }
  342. }
  343. newEventQueue.previousQueue = this;
  344. }
  345. nextQueue = newEventQueue;
  346. }
  347. /**
  348. * Stop dispatching events using this EventQueue instance.
  349. * Any pending events are transferred to the previous
  350. * EventQueue for processing by it.
  351. *
  352. * @exception if no previous push was made on this EventQueue.
  353. * @see java.awt.EventQueue#push
  354. */
  355. protected void pop() throws EmptyStackException {
  356. if (debug) {
  357. System.out.println("EventQueue.pop(" + this + ")");
  358. }
  359. // To prevent deadlock, we lock on the previous EventQueue before
  360. // this one. This uses the same locking order as everything else
  361. // in EventQueue.java, so deadlock isn't possible.
  362. EventQueue prev = previousQueue;
  363. synchronized ((prev != null) ? prev : this) {
  364. synchronized(this) {
  365. if (nextQueue != null) {
  366. nextQueue.pop();
  367. return;
  368. }
  369. if (previousQueue == null) {
  370. throw new EmptyStackException();
  371. }
  372. // Transfer all events back to previous EventQueue.
  373. previousQueue.nextQueue = null;
  374. while (peekEvent() != null) {
  375. try {
  376. previousQueue.postEventPrivate(getNextEvent());
  377. } catch (InterruptedException ie) {
  378. if (debug) {
  379. System.err.println("interrupted pop:");
  380. ie.printStackTrace(System.err);
  381. }
  382. }
  383. }
  384. previousQueue = null;
  385. }
  386. }
  387. dispatchThread.stopDispatching(); // Must be done outside synchronized
  388. // block to avoid possible deadlock
  389. }
  390. /**
  391. * Returns true if the calling thread is the current AWT EventQueue's
  392. * dispatch thread. Use this call the ensure that a given
  393. * task is being executed (or not being) on the current AWT
  394. * EventDispatchThread.
  395. *
  396. * @return true if running on the current AWT EventQueue's dispatch thread.
  397. */
  398. public static boolean isDispatchThread() {
  399. EventQueue eq = Toolkit.getEventQueue();
  400. EventQueue next = eq.nextQueue;
  401. while (next != null) {
  402. eq = next;
  403. next = eq.nextQueue;
  404. }
  405. return (Thread.currentThread() == eq.dispatchThread);
  406. }
  407. /*
  408. * Get the EventDispatchThread for this EventQueue.
  409. */
  410. final EventDispatchThread getDispatchThread() {
  411. return dispatchThread;
  412. }
  413. /*
  414. * Change the target of any pending KeyEvents because of a focus change.
  415. */
  416. final synchronized void changeKeyEventFocus(Object newSource) {
  417. for (int i = 0; i < NUM_PRIORITIES; i++) {
  418. EventQueueItem q = queues[i].head;
  419. for (; q != null; q = q.next) {
  420. if (q.event instanceof KeyEvent) {
  421. ((KeyEvent)q.event).setSource(newSource);
  422. }
  423. }
  424. }
  425. }
  426. /*
  427. * Remove any pending events for the specified source object.
  428. * This method is normally called by the source's removeNotify method.
  429. */
  430. final void removeSourceEvents(Object source) {
  431. Toolkit toolkit = Toolkit.getDefaultToolkit();
  432. if (toolkit instanceof SunToolkit) {
  433. ((SunToolkit)toolkit).flushPendingEvents();
  434. }
  435. synchronized (this) {
  436. for (int i = 0; i < NUM_PRIORITIES; i++) {
  437. EventQueueItem entry = queues[i].head;
  438. EventQueueItem prev = null;
  439. while (entry != null) {
  440. if (entry.event.getSource() == source) {
  441. if (prev == null) {
  442. queues[i].head = entry.next;
  443. } else {
  444. prev.next = entry.next;
  445. }
  446. } else {
  447. prev = entry;
  448. }
  449. entry = entry.next;
  450. }
  451. queues[i].tail = prev;
  452. }
  453. }
  454. }
  455. /**
  456. * Causes <i>runnable</i> to have its run() method called in the dispatch
  457. * thread of the EventQueue. This will happen after all pending events
  458. * are processed.
  459. *
  460. * @param runnable the Runnable whose run() method should be executed
  461. * synchronously on the EventQueue
  462. * @see #invokeAndWait
  463. * @since 1.2
  464. */
  465. public static void invokeLater(Runnable runnable) {
  466. Toolkit.getEventQueue().postEvent(
  467. new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
  468. }
  469. /**
  470. * Causes <i>runnable</i> to have its run() method called in the dispatch
  471. * thread of the EventQueue. This will happen after all pending events
  472. * are processed. The call blocks until this has happened. This method
  473. * will throw an Error if called from the event dispatcher thread.
  474. *
  475. * @param runnable the Runnable whose run() method should be executed
  476. * synchronously on the EventQueue
  477. * @exception InterruptedException if another thread has
  478. * interrupted this thread
  479. * @exception InvocationTargetException if an exception is thrown
  480. * when running <i>runnable</i>
  481. * @see #invokeLater
  482. * @since 1.2
  483. */
  484. public static void invokeAndWait(Runnable runnable)
  485. throws InterruptedException, InvocationTargetException {
  486. if (EventQueue.isDispatchThread()) {
  487. throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
  488. }
  489. class AWTInvocationLock {}
  490. Object lock = new AWTInvocationLock();
  491. EventQueue queue = Toolkit.getEventQueue();
  492. InvocationEvent event =
  493. new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
  494. true);
  495. synchronized (lock) {
  496. Toolkit.getEventQueue().postEvent(event);
  497. lock.wait();
  498. }
  499. Exception eventException = event.getException();
  500. if (eventException != null) {
  501. throw new InvocationTargetException(eventException);
  502. }
  503. }
  504. }
  505. /**
  506. * The Queue object holds pointers to the beginning and end of one internal
  507. * queue. An EventQueue object is composed of multiple internal Queues, one
  508. * for each priority supported by the EventQueue. All Events on a particular
  509. * internal Queue have identical priority.
  510. */
  511. class Queue {
  512. EventQueueItem head;
  513. EventQueueItem tail;
  514. }
  515. class EventQueueItem {
  516. AWTEvent event;
  517. int id;
  518. EventQueueItem next;
  519. EventQueueItem(AWTEvent evt) {
  520. event = evt;
  521. id = evt.getID();
  522. }
  523. }