GNU Classpath (0.95) | |
Frames | No Frames |
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: 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: }
GNU Classpath (0.95) |