<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">/*
 * @(#)EventQueue.java	1.71 01/02/09
 *
 * Copyright 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the proprietary information of Sun Microsystems, Inc.  
 * Use is subject to license terms.
 * 
 */

package java.awt;

import java.awt.event.PaintEvent;
import java.awt.event.InvocationEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.ActiveEvent;
import java.util.EmptyStackException;
import java.lang.reflect.InvocationTargetException;
import sun.awt.PeerEvent;
import sun.awt.SunToolkit;
import sun.awt.DebugHelper;

/**
 * EventQueue is a platform-independent class that queues events, both
 * from the underlying peer classes and from trusted application classes.
 * &lt;p&gt;
 * Some browsers partition applets in different code bases into separate
 * contexts, and establish walls between these contexts. In such a scenario,
 * there will be one EventQueue per context. Other browsers place all applets
 * into the same context, implying that there will be only a single, global
 * EventQueue for all applets. This behavior is implementation-dependent.
 * Consult your browser's documentation for more information.
 *
 * @author Thomas Ball
 * @author Fred Ecks
 * @author David Mendenhall
 *
 * @version 	1.71, 02/09/01
 * @since 	1.1
 */
public class EventQueue {
    private static final DebugHelper dbg = DebugHelper.create(EventQueue.class);

    // From Thread.java
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
	return threadInitNumber++;
    }

    private static final int LOW_PRIORITY = 0;
    private static final int NORM_PRIORITY = 1;
    private static final int HIGH_PRIORITY = 2;

    private static final int NUM_PRIORITIES = HIGH_PRIORITY + 1;

    /*
     * We maintain one Queue for each priority that the EventQueue supports.
     * That is, the EventQueue object is actually implemented as
     * NUM_PRIORITIES queues and all Events on a particular internal Queue
     * have identical priority. Events are pulled off the EventQueue starting
     * with the Queue of highest priority. We progress in decreasing order
     * across all Queues.
     */
    private Queue[] queues = new Queue[NUM_PRIORITIES];
    
    /*
     * The next EventQueue on the stack, or null if this EventQueue is
     * on the top of the stack.  If nextQueue is non-null, requests to post
     * an event are forwarded to nextQueue.
     */
    private EventQueue nextQueue;

    /*
     * The previous EventQueue on the stack, or null if this is the
     * "base" EventQueue.
     */
    private EventQueue previousQueue;

    private EventDispatchThread dispatchThread;

    /*
     * Debugging flag -- set true and recompile to enable checking.
     */
    private final static boolean debug = false;

    public EventQueue() {
        for (int i = 0; i &lt; NUM_PRIORITIES; i++) {
	    queues[i] = new Queue();
	}
        String name = "AWT-EventQueue-" + nextThreadNum();
        dispatchThread = new EventDispatchThread(name, this);
        dispatchThread.setPriority(Thread.NORM_PRIORITY + 1);
        dispatchThread.start();
    }

    /**
     * Post a 1.1-style event to the EventQueue.  If there is an
     * existing event on the queue with the same ID and event source,
     * the source Component's coalesceEvents method will be called.
     *
     * @param theEvent an instance of java.awt.AWTEvent, or a
     * subclass of it.
     */
    public void postEvent(AWTEvent theEvent) {
	Toolkit toolkit = Toolkit.getDefaultToolkit();
        if (toolkit instanceof SunToolkit) {
	    ((SunToolkit)toolkit).flushPendingEvents();
	}
        postEventPrivate(theEvent);
    }

    /**
     * Post a 1.1-style event to the EventQueue.  If there is an
     * existing event on the queue with the same ID and event source,
     * the source Component's coalesceEvents method will be called.
     *
     * @param theEvent an instance of java.awt.AWTEvent, or a
     * subclass of it.
     */
    final void postEventPrivate(AWTEvent theEvent) {
        synchronized(this) {
            int id = theEvent.getID();
            if (nextQueue != null) {
                // Forward event to top of EventQueue stack.
                nextQueue.postEventPrivate(theEvent);
            } else if (theEvent instanceof PeerEvent &amp;&amp;
                       (((PeerEvent)theEvent).getFlags() &amp; 
                                       PeerEvent.PRIORITY_EVENT) != 0) {
                postEvent(theEvent, HIGH_PRIORITY);
            } else if (id == PaintEvent.PAINT ||
                       id == PaintEvent.UPDATE) {
                postEvent(theEvent, LOW_PRIORITY);
            } else {
                postEvent(theEvent, NORM_PRIORITY);
            }
        }
    }

    /**
     * Posts the event to the internal Queue of specified priority,
     * coalescing as appropriate.
     */
    private void postEvent(AWTEvent theEvent, int priority) {
        EventQueueItem newItem = new EventQueueItem(theEvent);
	if (queues[priority].head == null) {
	    boolean shouldNotify = noEvents();

	    queues[priority].head = queues[priority].tail = newItem;

            // This component doesn't have any events of this type on the 
            // queue, so we have to initialize the RepaintArea with theEvent
	    if (theEvent.getID() == PaintEvent.PAINT ||
                theEvent.getID() == PaintEvent.UPDATE) {
                Object source = theEvent.getSource();
                ((Component)source).coalesceEvents(theEvent, theEvent);
	    }

	    if (shouldNotify) {
	        notifyAll();
	    }
	} else {
	    Object source = theEvent.getSource();
	    boolean isPeerEvent = theEvent instanceof PeerEvent;

	    // For Component source events, traverse the entire list,
	    // trying to coalesce events
	    if (source instanceof Component) {
	        EventQueueItem q = queues[priority].head;

		// fix bug 4301264, do not coalesce mouse move/drag events
		// across other types of mouse events.
		if (theEvent.id == Event.MOUSE_MOVE ||
		    theEvent.id == Event.MOUSE_DRAG) {
		    EventQueueItem qm;
		    for(qm = q; qm != null; qm = qm.next) {
			if ((qm.event instanceof MouseEvent) &amp;&amp;
			    qm.id != theEvent.id) {
				q = qm;
			}
		    }
		}

		for (;;) {
		    if (q.id == newItem.id &amp;&amp; q.event.getSource() == source) {
		        AWTEvent coalescedEvent;
			coalescedEvent = ((Component)source).coalesceEvents(q.event, theEvent);
			if (isPeerEvent) {
			    if( coalescedEvent == null &amp;&amp; q.event instanceof PeerEvent) {
				coalescedEvent = ((PeerEvent)q.event).coalesceEvents((PeerEvent)theEvent);
			    }
			}
			if (coalescedEvent != null) {
			    // Remove debugging statement because
			    // calling AWTEvent.toString here causes a
			    // deadlock.
			    //
			    // if (dbg.on) {
			    //     dbg.println("EventQueue coalesced event: " +
			    //                 coalescedEvent);
			    // }
			    q.event = coalescedEvent;
			    return;
			}
		    }
		    if (q.next != null) {
		        q = q.next;
		    } else {
		        break;
		    }
		}
	    }

            // The event was not coalesced or has non-Component source.
            // Insert it at the end of the appropriate Queue.
	    if (theEvent.getID() == PaintEvent.PAINT ||
                theEvent.getID() == PaintEvent.UPDATE) {
		// This component doesn't have any events of this type on the 
                // queue, so we have to initialize the RepaintArea with theEvent
	        ((Component)source).coalesceEvents(theEvent, theEvent);
	    }

	    queues[priority].tail.next = newItem;
	    queues[priority].tail = newItem;
	}
    }

    /**
     * @return whether an event is pending on any of the separate Queues
     */
    private boolean noEvents() {
        for (int i = 0; i &lt; NUM_PRIORITIES; i++) {
	    if (queues[i].head != null) {
	        return false;
	    }
	}

	return true;
    }

    /**
     * Remove an event from the EventQueue and return it.  This method will
     * block until an event has been posted by another thread.
     * @return the next AWTEvent
     * @exception InterruptedException 
     *            if another thread has interrupted this thread.
     */
    public synchronized AWTEvent getNextEvent() throws InterruptedException {
        do {
	    for (int i = NUM_PRIORITIES - 1; i &gt;= 0; i--) {
		if (queues[i].head != null) {
		    EventQueueItem eqi = queues[i].head;
		    queues[i].head = eqi.next;
		    if (eqi.next == null) {
			queues[i].tail = null;
		    }
		    return eqi.event;
		}
	    }
            wait();
        } while(true);
    }

    /**
     * Return the first event on the EventQueue without removing it.
     * @return the first event
     */
    public synchronized AWTEvent peekEvent() {
        for (int i = NUM_PRIORITIES - 1; i &gt;= 0; i--) {
	    if (queues[i].head != null) {
	        return queues[i].head.event;
	    }
	}

	return null;
    }

    /**
     * Return the first event with the specified id, if any.
     * @param id the id of the type of event desired.
     * @return the first event of the specified id
     */
    public synchronized AWTEvent peekEvent(int id) {
        for (int i = NUM_PRIORITIES - 1; i &gt;= 0; i--) {
	    EventQueueItem q = queues[i].head;
	    for (; q != null; q = q.next) {
	        if (q.id == id) {
		    return q.event;
		}
	    }
	}

        return null;
    }

    /**
     * Dispatch an event. The manner in which the event is
     * dispatched depends upon the type of the event and the
     * type of the event's source
     * object:
     * &lt;p&gt; &lt;/p&gt;
     * &lt;table border&gt;
     * &lt;tr&gt;
     *     &lt;th&gt;Event Type&lt;/th&gt;
     *     &lt;th&gt;Source Type&lt;/th&gt; 
     *     &lt;th&gt;Dispatched To&lt;/th&gt;
     * &lt;/tr&gt;
     * &lt;tr&gt;
     *     &lt;td&gt;ActiveEvent&lt;/td&gt;
     *     &lt;td&gt;Any&lt;/td&gt;
     *     &lt;td&gt;event.dispatch()&lt;/td&gt;
     * &lt;/tr&gt;
     * &lt;tr&gt;
     *     &lt;td&gt;Other&lt;/td&gt;
     *     &lt;td&gt;Component&lt;/td&gt;
     *     &lt;td&gt;source.dispatchEvent(AWTEvent)&lt;/td&gt;
     * &lt;/tr&gt;
     * &lt;tr&gt;
     *     &lt;td&gt;Other&lt;/td&gt;
     *     &lt;td&gt;MenuComponent&lt;/td&gt;
     *     &lt;td&gt;source.dispatchEvent(AWTEvent)&lt;/td&gt;
     * &lt;/tr&gt;
     * &lt;tr&gt;
     *     &lt;td&gt;Other&lt;/td&gt;
     *     &lt;td&gt;Other&lt;/td&gt;
     *     &lt;td&gt;No action (ignored)&lt;/td&gt;
     * &lt;/tr&gt;
     * &lt;/table&gt;
     * &lt;p&gt; &lt;/p&gt;
     * @param theEvent an instance of java.awt.AWTEvent, or a
     * subclass of it.
     */
    protected void dispatchEvent(AWTEvent event) {
        Object src = event.getSource();
        if (event instanceof ActiveEvent) {
            // This could become the sole method of dispatching in time.
            ((ActiveEvent)event).dispatch();
        } else if (src instanceof Component) {
            ((Component)src).dispatchEvent(event);
        } else if (src instanceof MenuComponent) {
            ((MenuComponent)src).dispatchEvent(event);
        } else {
            System.err.println("unable to dispatch event: " + event);
        }
    }

    /**
     * Replace the existing EventQueue with the specified one.
     * Any pending events are transferred to the new EventQueue
     * for processing by it.
     *
     * @param an EventQueue (or subclass thereof) instance to be used.
     * @see      java.awt.EventQueue#pop
     */
    public synchronized void push(EventQueue newEventQueue) {
	if (debug) {
	    System.out.println("EventQueue.push(" + newEventQueue + ")");
	}

        if (nextQueue != null) {
            nextQueue.push(newEventQueue);
            return;
        }

        synchronized (newEventQueue) {
	    // Transfer all events forward to new EventQueue.
	    while (peekEvent() != null) {
		try {
		    newEventQueue.postEventPrivate(getNextEvent());
		} catch (InterruptedException ie) {
		    if (debug) {
			System.err.println("interrupted push:");
			ie.printStackTrace(System.err);
		    }
		}
	    }

	    newEventQueue.previousQueue = this;
        }
	nextQueue = newEventQueue;
    }

    /**
     * Stop dispatching events using this EventQueue instance.
     * Any pending events are transferred to the previous
     * EventQueue for processing by it.  
     *
     * @exception if no previous push was made on this EventQueue.
     * @see      java.awt.EventQueue#push
     */
    protected void pop() throws EmptyStackException {
	if (debug) {
	    System.out.println("EventQueue.pop(" + this + ")");
	}

	// To prevent deadlock, we lock on the previous EventQueue before
	// this one.  This uses the same locking order as everything else
	// in EventQueue.java, so deadlock isn't possible.
	EventQueue prev = previousQueue;
	synchronized ((prev != null) ? prev : this) {
	  synchronized(this) {
            if (nextQueue != null) {
                nextQueue.pop();
                return;
            }
            if (previousQueue == null) {
                throw new EmptyStackException();
            }

	    // Transfer all events back to previous EventQueue.
	    previousQueue.nextQueue = null;
	    while (peekEvent() != null) {
		try {
		    previousQueue.postEventPrivate(getNextEvent());
		} catch (InterruptedException ie) {
		    if (debug) {
			System.err.println("interrupted pop:");
			ie.printStackTrace(System.err);
		    }
		}
	    }
	    previousQueue = null;
          }
        }

	dispatchThread.stopDispatching(); // Must be done outside synchronized
					  // block to avoid possible deadlock
    }

    /**
     * Returns true if the calling thread is the current AWT EventQueue's
     * dispatch thread.  Use this call the ensure that a given
     * task is being executed (or not being) on the current AWT
     * EventDispatchThread.
     *
     * @return true if running on the current AWT EventQueue's dispatch thread.
     */
    public static boolean isDispatchThread() {
	EventQueue eq = Toolkit.getEventQueue();
	EventQueue next = eq.nextQueue;
	while (next != null) {
	    eq = next;
	    next = eq.nextQueue;
	}
	return (Thread.currentThread() == eq.dispatchThread);
    }

    /*
     * Get the EventDispatchThread for this EventQueue.
     */
    final EventDispatchThread getDispatchThread() {
	return dispatchThread;
    }

    /*
     * Change the target of any pending KeyEvents because of a focus change.
     */
    final synchronized void changeKeyEventFocus(Object newSource) {
        for (int i = 0; i &lt; NUM_PRIORITIES; i++) {
	    EventQueueItem q = queues[i].head;
	    for (; q != null; q = q.next) {
	        if (q.event instanceof KeyEvent) {
		    ((KeyEvent)q.event).setSource(newSource);
		}
	    }
	}
    }

    /*
     * Remove any pending events for the specified source object.
     * This method is normally called by the source's removeNotify method.
     */
    final void removeSourceEvents(Object source) {
	Toolkit toolkit = Toolkit.getDefaultToolkit();
        if (toolkit instanceof SunToolkit) {
	    ((SunToolkit)toolkit).flushPendingEvents();
	}

        synchronized (this) {
	    for (int i = 0; i &lt; NUM_PRIORITIES; i++) {
	        EventQueueItem entry = queues[i].head;
		EventQueueItem prev = null;
		while (entry != null) {
		    if (entry.event.getSource() == source) {
		        if (prev == null) {
			    queues[i].head = entry.next;
			} else {
			    prev.next = entry.next;
			}
		    } else {
		        prev = entry;
		    }
		    entry = entry.next;
		}
		queues[i].tail = prev;
	    }
	}
    }

    /**
     * Causes &lt;i&gt;runnable&lt;/i&gt; to have its run() method called in the dispatch
     * thread of the EventQueue.  This will happen after all pending events
     * are processed.
     *
     * @param runnable  the Runnable whose run() method should be executed
     *                  synchronously on the EventQueue
     * @see             #invokeAndWait
     * @since           1.2
     */
    public static void invokeLater(Runnable runnable) {
        Toolkit.getEventQueue().postEvent(
            new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
    }

    /**
     * Causes &lt;i&gt;runnable&lt;/i&gt; to have its run() method called in the dispatch
     * thread of the EventQueue.  This will happen after all pending events
     * are processed.  The call blocks until this has happened.  This method
     * will throw an Error if called from the event dispatcher thread.
     *
     * @param runnable  the Runnable whose run() method should be executed
     *                  synchronously on the EventQueue
     * @exception       InterruptedException  if another thread has
     *                  interrupted this thread
     * @exception       InvocationTargetException  if an exception is thrown
     *                  when running &lt;i&gt;runnable&lt;/i&gt;
     * @see             #invokeLater
     * @since           1.2
     */
    public static void invokeAndWait(Runnable runnable)
             throws InterruptedException, InvocationTargetException {

        if (EventQueue.isDispatchThread()) {
            throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
        }

	class AWTInvocationLock {}
        Object lock = new AWTInvocationLock();

        EventQueue queue = Toolkit.getEventQueue();
        InvocationEvent event = 
            new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
				true);

        synchronized (lock) {
            Toolkit.getEventQueue().postEvent(event);
            lock.wait();
        }

        Exception eventException = event.getException();
        if (eventException != null) {
            throw new InvocationTargetException(eventException);
        }
    }
}

/**
 * The Queue object holds pointers to the beginning and end of one internal
 * queue. An EventQueue object is composed of multiple internal Queues, one
 * for each priority supported by the EventQueue. All Events on a particular
 * internal Queue have identical priority.
 */
class Queue {
    EventQueueItem head;
    EventQueueItem tail;
}

class EventQueueItem {
    AWTEvent event;
    int      id;
    EventQueueItem next;

    EventQueueItem(AWTEvent evt) {
        event = evt;
        id = evt.getID();
    }
}
</pre></body></html>