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., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 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.LowPriorityEvent; 42 import gnu.java.awt.peer.NativeEventLoopRunningEvent; 43 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; 53 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. 58 */ 59 60 /** 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 63 * events. 64 * 65 * @author Bryce McKinlay 66 * @author Aaron M. Renn (arenn@urbanophile.com) 67 */ 68 public class EventQueue 69 { 70 /** 71 * Indicates events that are processed with normal priority. This is normally 72 * all events except PaintEvents. 73 */ 74 private static final int NORM_PRIORITY = 0; 75 76 /** 77 * Indicates events that are processed with lowes priority. This is normally 78 * all PaintEvents and LowPriorityEvents. 79 */ 80 private static final int LOW_PRIORITY = 1; 81 82 /** 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. 87 * 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. 91 */ 92 private class Queue 93 { 94 /** 95 * The first item in the queue. This is where events are popped from. 96 */ 97 AWTEvent queueHead; 98 99 /** 100 * The last item. This is where events are posted to. 101 */ 102 AWTEvent queueTail; 103 } 104 105 /** 106 * The three internal event queues. 107 * 108 * @see Queue 109 */ 110 private Queue[] queues; 111 112 private EventQueue next; 113 private EventQueue prev; 114 private AWTEvent currentEvent; 115 private long lastWhen = System.currentTimeMillis(); 116 117 private EventDispatchThread dispatchThread = new EventDispatchThread(this); 118 private boolean nativeLoopRunning = false; 119 isShutdown()120 private boolean isShutdown () 121 { 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 124 125 if (nativeLoopRunning) 126 return false; 127 128 if (peekEvent() != null) 129 return false; 130 131 if (Frame.hasDisplayableFrames()) 132 return false; 133 134 return true; 135 } 136 137 /** 138 * Initializes a new instance of <code>EventQueue</code>. 139 */ EventQueue()140 public EventQueue() 141 { 142 queues = new Queue[2]; 143 queues[NORM_PRIORITY] = new Queue(); 144 queues[LOW_PRIORITY] = new Queue(); 145 } 146 147 /** 148 * Returns the next event in the queue. This method will block until 149 * an event is available or until the thread is interrupted. 150 * 151 * @return The next event in the queue. 152 * 153 * @exception InterruptedException If this thread is interrupted while 154 * waiting for an event to be posted to the queue. 155 */ getNextEvent()156 public synchronized AWTEvent getNextEvent() 157 throws InterruptedException 158 { 159 if (next != null) 160 return next.getNextEvent(); 161 162 AWTEvent res = getNextEventImpl(true); 163 164 while (res == null) 165 { 166 if (isShutdown()) 167 { 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; 176 177 // Interrupt the event dispatch thread. 178 throw new InterruptedException(); 179 } 180 181 wait(); 182 res = getNextEventImpl(true); 183 } 184 185 return res; 186 } 187 188 /** 189 * Fetches and possibly removes the next event from the internal queues. 190 * This method returns immediately. When all queues are empty, this returns 191 * <code>null</code>: 192 * 193 * @param remove <true> when the event should be removed from the queue, 194 * <code>false</code> otherwise 195 * 196 * @return the next event or <code>null</code> when all internal queues 197 * are empty 198 */ getNextEventImpl(boolean remove)199 private AWTEvent getNextEventImpl(boolean remove) 200 { 201 AWTEvent next = null; 202 for (int i = 0; i < queues.length && next == null; i++) 203 { 204 Queue q = queues[i]; 205 if (q.queueHead != null) 206 { 207 // Got an event, remove it. 208 next = q.queueHead; 209 if (remove) 210 { 211 // Unlink event from the queue. 212 q.queueHead = next.queueNext; 213 if (q.queueHead == null) 214 q.queueTail = null; 215 next.queueNext = null; 216 } 217 } 218 } 219 return next; 220 } 221 222 /** 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 225 * is interrupted. 226 * 227 * @return The next event in the queue. 228 * @specnote Does not block. Returns null if there are no events on the 229 * queue. 230 */ peekEvent()231 public synchronized AWTEvent peekEvent() 232 { 233 if (next != null) 234 return next.peekEvent(); 235 236 return getNextEventImpl(false); 237 } 238 239 /** 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 243 * is interrupted. 244 * 245 * @param id The event id to return. 246 * 247 * @return The next event in the queue. 248 * 249 * @specnote Does not block. Returns null if there are no matching events 250 * on the queue. 251 */ peekEvent(int id)252 public synchronized AWTEvent peekEvent(int id) 253 { 254 if (next != null) 255 return next.peekEvent(id); 256 257 AWTEvent evt = null; 258 for (int i = 0; i < queues.length && evt == null; i++) 259 { 260 Queue q = queues[i]; 261 evt = q.queueHead; 262 while (evt != null && evt.id != id) 263 evt = evt.queueNext; 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 266 // internal queue). 267 } 268 return evt; 269 } 270 271 /** 272 * Posts a new event to the queue. 273 * 274 * @param evt The event to post to the queue. 275 * 276 * @exception NullPointerException If event is null. 277 */ postEvent(AWTEvent evt)278 public void postEvent(AWTEvent evt) 279 { 280 postEventImpl(evt); 281 } 282 283 /** 284 * Sorts events to their priority and calls 285 * {@link #postEventImpl(AWTEvent, int)}. 286 * 287 * @param evt the event to post 288 */ postEventImpl(AWTEvent evt)289 private synchronized final void postEventImpl(AWTEvent evt) 290 { 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 295 // low priority. 296 if (evt instanceof NativeEventLoopRunningEvent) 297 { 298 nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning(); 299 notify(); 300 return; 301 } 302 postEventImpl(evt, priority); 303 } 304 305 /** 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(). 309 * 310 * @param evt the event to post 311 * @param priority the priority of the event 312 */ postEventImpl(AWTEvent evt, int priority)313 private final void postEventImpl(AWTEvent evt, int priority) 314 { 315 if (evt == null) 316 throw new NullPointerException(); 317 318 if (next != null) 319 { 320 next.postEvent(evt); 321 return; 322 } 323 324 Object source = evt.getSource(); 325 326 Queue q = queues[priority]; 327 if (source instanceof Component) 328 { 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); 336 337 // Check for any events already on the queue with the same source 338 // and ID. 339 AWTEvent previous = null; 340 for (AWTEvent qevt = q.queueHead; qevt != null; qevt = qevt.queueNext) 341 { 342 Object src = qevt.getSource(); 343 if (qevt.id == evt.id && src == comp) 344 { 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) 350 { 351 // Yes. Replace the existing event with the combined event. 352 if (qevt != coalescedEvt) 353 { 354 if (previous != null) 355 { 356 assert previous.queueNext == qevt; 357 previous.queueNext = coalescedEvt; 358 } 359 else 360 { 361 assert q.queueHead == qevt; 362 q.queueHead = coalescedEvt; 363 } 364 coalescedEvt.queueNext = qevt.queueNext; 365 if (q.queueTail == qevt) 366 q.queueTail = coalescedEvt; 367 qevt.queueNext = null; 368 } 369 return; 370 } 371 } 372 previous = qevt; 373 } 374 } 375 376 if (q.queueHead == null) 377 { 378 // We have an empty queue. Set this event both as head and as tail. 379 q.queueHead = evt; 380 q.queueTail = evt; 381 } 382 else 383 { 384 // Note: queueTail should not be null here. 385 q.queueTail.queueNext = evt; 386 q.queueTail = evt; 387 } 388 389 if (dispatchThread == null || !dispatchThread.isAlive()) 390 { 391 dispatchThread = new EventDispatchThread(this); 392 dispatchThread.start(); 393 } 394 395 notify(); 396 } 397 398 /** 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. 403 * 404 * @exception InterruptedException If another thread has interrupted 405 * this thread. 406 * @exception InvocationTargetException If an exception is thrown when running 407 * runnable. 408 * 409 * @since 1.2 410 */ invokeAndWait(Runnable runnable)411 public static void invokeAndWait(Runnable runnable) 412 throws InterruptedException, InvocationTargetException 413 { 414 if (isDispatchThread ()) 415 throw new Error("Can't call invokeAndWait from event dispatch thread"); 416 417 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 418 Object notifyObject = new Object(); 419 420 InvocationEvent ie = 421 new InvocationEvent(eq, runnable, notifyObject, true); 422 423 synchronized (notifyObject) 424 { 425 eq.postEvent(ie); 426 notifyObject.wait(); 427 } 428 429 Exception exception; 430 431 if ((exception = ie.getException()) != null) 432 throw new InvocationTargetException(exception); 433 } 434 435 /** 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. 439 * 440 * @since 1.2 441 */ invokeLater(Runnable runnable)442 public static void invokeLater(Runnable runnable) 443 { 444 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 445 446 InvocationEvent ie = 447 new InvocationEvent(eq, runnable, null, false); 448 449 eq.postEvent(ie); 450 } 451 452 /** 453 * Return true if the current thread is the current AWT event dispatch 454 * thread. 455 */ isDispatchThread()456 public static boolean isDispatchThread() 457 { 458 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 459 460 /* Find last EventQueue in chain */ 461 while (eq.next != null) 462 eq = eq.next; 463 464 return (Thread.currentThread() == eq.dispatchThread); 465 } 466 467 /** 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. 471 * 472 * @since 1.4 473 */ getCurrentEvent()474 public static AWTEvent getCurrentEvent() 475 { 476 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 477 Thread ct = Thread.currentThread(); 478 479 /* Find out if this thread is the dispatch thread for any of the 480 EventQueues in the chain */ 481 while (ct != eq.dispatchThread) 482 { 483 // Try next EventQueue, if any 484 if (eq.next == null) 485 return null; // Not an event dispatch thread 486 eq = eq.next; 487 } 488 489 return eq.currentEvent; 490 } 491 492 /** 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(). 497 * 498 * @exception NullPointerException if newEventQueue is null. 499 */ push(EventQueue newEventQueue)500 public synchronized void push(EventQueue newEventQueue) 501 { 502 if (newEventQueue == null) 503 throw new NullPointerException (); 504 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() */ 508 if (next != null) 509 { 510 next.push (newEventQueue); 511 return; 512 } 513 514 /* Make sure we have a live dispatch thread to drive the queue */ 515 if (dispatchThread == null) 516 dispatchThread = new EventDispatchThread(this); 517 518 synchronized (newEventQueue) 519 { 520 // The RI transfers the events without calling the new eventqueue's 521 // push(), but using getNextEvent(). 522 while (peekEvent() != null) 523 { 524 try 525 { 526 newEventQueue.postEventImpl(getNextEvent()); 527 } 528 catch (InterruptedException ex) 529 { 530 // What should we do with this? 531 ex.printStackTrace(); 532 } 533 } 534 newEventQueue.prev = this; 535 } 536 537 next = newEventQueue; 538 } 539 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. 542 * 543 * @exception EmptyStackException If no previous push was made on this 544 * EventQueue. 545 */ pop()546 protected void pop() throws EmptyStackException 547 { 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 551 to get this lock. */ 552 EventQueue previous = prev; 553 if (previous == null) 554 throw new EmptyStackException(); 555 synchronized (previous) 556 { 557 synchronized (this) 558 { 559 EventQueue nextQueue = next; 560 if (nextQueue != null) 561 { 562 nextQueue.pop(); 563 } 564 else 565 { 566 previous.next = null; 567 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) 571 { 572 try 573 { 574 previous.postEventImpl(getNextEvent()); 575 } 576 catch (InterruptedException ex) 577 { 578 // What should we do with this? 579 ex.printStackTrace(); 580 } 581 } 582 prev = null; 583 // Tell our EventDispatchThread that it can end 584 // execution. 585 if (dispatchThread != null) 586 { 587 dispatchThread.interrupt(); 588 dispatchThread = null; 589 } 590 } 591 } 592 } 593 } 594 595 /** 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. 598 * 599 * @exception NullPointerException If event is null. 600 */ dispatchEvent(AWTEvent evt)601 protected void dispatchEvent(AWTEvent evt) 602 { 603 currentEvent = evt; 604 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(); 611 612 if (evt instanceof ActiveEvent) 613 { 614 ActiveEvent active_evt = (ActiveEvent) evt; 615 active_evt.dispatch(); 616 } 617 else 618 { 619 Object source = evt.getSource(); 620 621 if (source instanceof Component) 622 { 623 Component srccmp = (Component) source; 624 srccmp.dispatchEvent(evt); 625 } 626 else if (source instanceof MenuComponent) 627 { 628 MenuComponent srccmp = (MenuComponent) source; 629 srccmp.dispatchEvent(evt); 630 } 631 } 632 } 633 634 /** 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(). 643 * 644 * @return the most recent timestamp 645 * @see InputEvent#getWhen() 646 * @see ActionEvent#getWhen() 647 * @see InvocationEvent#getWhen() 648 * @see InputMethodEvent#getWhen() 649 * @since 1.4 650 */ getMostRecentEventTime()651 public static long getMostRecentEventTime() 652 { 653 EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 654 if (Thread.currentThread() != eq.dispatchThread) 655 return System.currentTimeMillis(); 656 return eq.lastWhen; 657 } 658 } 659