src/share/classes/java/awt/EventDispatchThread.java
author Jaroslav Tulach <jtulach@netbeans.org>
Wed, 23 May 2012 11:51:09 +0200
branchjavafx
changeset 5229 4ed8674de305
parent 5155 d45bc4307996
permissions -rw-r--r--
Allowing JavaFX to dispatchEvent in its own, dedicated FX dispatch thread
     1 /*
     2  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 
    26 package java.awt;
    27 
    28 import java.awt.event.MouseEvent;
    29 import java.awt.event.ActionEvent;
    30 import java.awt.event.WindowEvent;
    31 import java.lang.reflect.Method;
    32 
    33 import java.util.ArrayList;
    34 import java.util.concurrent.BlockingQueue;
    35 import java.util.concurrent.Executor;
    36 import java.util.concurrent.LinkedBlockingQueue;
    37 import sun.util.logging.PlatformLogger;
    38 
    39 import sun.awt.dnd.SunDragSourceContextPeer;
    40 import sun.awt.EventQueueDelegate;
    41 
    42 /**
    43  * EventDispatchThread is a package-private AWT class which takes
    44  * events off the EventQueue and dispatches them to the appropriate
    45  * AWT components.
    46  *
    47  * The Thread starts a "permanent" event pump with a call to
    48  * pumpEvents(Conditional) in its run() method. Event handlers can choose to
    49  * block this event pump at any time, but should start a new pump (<b>not</b>
    50  * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
    51  * secondary event pump will exit automatically as soon as the Condtional
    52  * evaluate()s to false and an additional Event is pumped and dispatched.
    53  *
    54  * @author Tom Ball
    55  * @author Amy Fowler
    56  * @author Fred Ecks
    57  * @author David Mendenhall
    58  *
    59  * @since 1.1
    60  */
    61 final class EventDispatchThread {
    62 
    63     private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventDispatchThread");
    64 
    65     private EventQueue theQueue;
    66     private boolean doDispatch = true;
    67     private volatile boolean shutdown = false;
    68 
    69     static final int ANY_EVENT = -1;
    70 
    71     private final Executor executor;
    72     private ArrayList<EventFilter> eventFilters = new ArrayList<EventFilter>();
    73 
    74     EventDispatchThread(ThreadGroup group, String name, EventQueue queue, ClassLoader l) {
    75         // right now using simple executor that sort of mimics the old
    76         // behavior of the run() method of EventDispatchThread. But when
    77         // running together with FX, the executor should dispatch the runnable
    78         // using Platform.invokeLater to share the same processing thread
    79         executor = defaultThread(group, name, l);
    80         setEventQueue(queue);
    81     }
    82     
    83     private static Executor defaultThread(ThreadGroup group, String name, ClassLoader classLoader) {
    84         class ExecRuns implements Executor, Runnable {
    85             final BlockingQueue<Runnable> toRun = new LinkedBlockingQueue<>();
    86             @Override
    87             public void execute(Runnable command) {
    88                 toRun.add(command);
    89             }
    90 
    91             @Override
    92             public void run() {
    93                 while (true) {
    94                     Runnable r;
    95                     try {
    96                         r = toRun.take();
    97                     } catch (InterruptedException ex) {
    98                         eventLog.severe(ex.getMessage(), ex);
    99                         continue;
   100                     }
   101                     r.run();
   102                 }
   103             }
   104         }
   105         ExecRuns er = new ExecRuns();
   106         
   107         Thread t = new Thread(group, name);
   108         t.setContextClassLoader(classLoader);
   109         t.setPriority(Thread.NORM_PRIORITY + 1);
   110         t.setDaemon(false);
   111         t.start();
   112         return er;
   113     }
   114 
   115     /*
   116      * Must be called on EDT only, that's why no synchronization
   117      */
   118     public void stopDispatching() {
   119         doDispatch = false;
   120     }
   121 
   122     public void interrupt() {
   123         shutdown = true;
   124 //XXX        super.interrupt();
   125     }
   126 
   127     public void processEvents() {
   128         executor.execute(new Runnable() {
   129             @Override
   130             public void run() {
   131                 try {
   132                     // XXX: the meaning now should be: the pumpEvents processes
   133                     // currently pending events, and then it returns without
   134                     // any blocking
   135                     pumpEvents(new Conditional() {
   136                         public boolean evaluate() {
   137                             return true;
   138                         }
   139                     });
   140                 } finally {
   141                     if(getEventQueue().detachDispatchThread(this, shutdown)) {
   142                         return;
   143                     }
   144                 }
   145             }
   146         });
   147     }
   148 
   149     // MacOSX change:
   150     //  This was added because this class (and java.awt.Conditional) are package private.
   151     //  There are certain instances where classes in other packages need to block the
   152     //  AWTEventQueue while still allowing it to process events. This uses reflection
   153     //  to call back into the caller in order to remove dependencies.
   154     //
   155     // NOTE: This uses reflection in its implementation, so it is not for performance critical code.
   156     //
   157     //  cond is an instance of sun.lwawt.macosx.EventDispatchAccess
   158     //
   159     private Conditional _macosxGetConditional(final Object cond) {
   160         try {
   161             return new Conditional() {
   162                 final Method evaluateMethod = Class.forName("sun.lwawt.macosx.EventDispatchAccess").getMethod("evaluate", null);
   163                 public boolean evaluate() {
   164                     try {
   165                         return ((Boolean)evaluateMethod.invoke(cond, null)).booleanValue();
   166                     } catch (Exception e) {
   167                         return false;
   168                     }
   169                 }
   170             };
   171         } catch (Exception e) {
   172             return new Conditional() { public boolean evaluate() { return false; } };
   173         }
   174     }
   175 
   176 
   177     void pumpEvents(Conditional cond) {
   178         pumpEvents(ANY_EVENT, cond);
   179     }
   180 
   181     void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
   182         pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
   183     }
   184 
   185     void pumpEvents(int id, Conditional cond) {
   186         pumpEventsForHierarchy(id, cond, null);
   187     }
   188 
   189     void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
   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     //XXX
   198     boolean isInterrupted() {
   199         return false;
   200     }
   201     
   202     void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
   203         addEventFilter(filter);
   204         doDispatch = true;
   205         shutdown |= isInterrupted();
   206         while (doDispatch && !shutdown && cond.evaluate()) {
   207             pumpOneEventForFilters(id);
   208         }
   209         removeEventFilter(filter);
   210     }
   211 
   212     void addEventFilter(EventFilter filter) {
   213         eventLog.finest("adding the event filter: " + filter);
   214         synchronized (eventFilters) {
   215             if (!eventFilters.contains(filter)) {
   216                 if (filter instanceof ModalEventFilter) {
   217                     ModalEventFilter newFilter = (ModalEventFilter)filter;
   218                     int k = 0;
   219                     for (k = 0; k < eventFilters.size(); k++) {
   220                         EventFilter f = eventFilters.get(k);
   221                         if (f instanceof ModalEventFilter) {
   222                             ModalEventFilter cf = (ModalEventFilter)f;
   223                             if (cf.compareTo(newFilter) > 0) {
   224                                 break;
   225                             }
   226                         }
   227                     }
   228                     eventFilters.add(k, filter);
   229                 } else {
   230                     eventFilters.add(filter);
   231                 }
   232             }
   233         }
   234     }
   235 
   236     void removeEventFilter(EventFilter filter) {
   237         eventLog.finest("removing the event filter: " + filter);
   238         synchronized (eventFilters) {
   239             eventFilters.remove(filter);
   240         }
   241     }
   242     
   243     boolean blockWhenNoEvent() {
   244         return true;
   245     }
   246 
   247     void pumpOneEventForFilters(int id) {
   248         AWTEvent event = null;
   249         boolean eventOK = false;
   250         try {
   251             EventQueue eq = null;
   252             EventQueueDelegate.Delegate delegate = null;
   253             do {
   254                 // EventQueue may change during the dispatching
   255                 eq = getEventQueue();
   256                 delegate = EventQueueDelegate.getDelegate();
   257 
   258                 if (delegate != null && id == ANY_EVENT) {
   259                     event = delegate.getNextEvent(eq);
   260                 } else {
   261                     event = eq.getNextEvent(id, blockWhenNoEvent());
   262                 }
   263                 if (event == null) {
   264                     return;
   265                 }
   266 
   267                 eventOK = true;
   268                 synchronized (eventFilters) {
   269                     for (int i = eventFilters.size() - 1; i >= 0; i--) {
   270                         EventFilter f = eventFilters.get(i);
   271                         EventFilter.FilterAction accept = f.acceptEvent(event);
   272                         if (accept == EventFilter.FilterAction.REJECT) {
   273                             eventOK = false;
   274                             break;
   275                         } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
   276                             break;
   277                         }
   278                     }
   279                 }
   280                 eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
   281                 if (!eventOK) {
   282                     event.consume();
   283                 }
   284             }
   285             while (eventOK == false);
   286 
   287             if (eventLog.isLoggable(PlatformLogger.FINEST)) {
   288                 eventLog.finest("Dispatching: " + event);
   289             }
   290 
   291             Object handle = null;
   292             if (delegate != null) {
   293                 handle = delegate.beforeDispatch(event);
   294             }
   295             eq.dispatchEvent(event);
   296             if (delegate != null) {
   297                 delegate.afterDispatch(event, handle);
   298             }
   299         }
   300         catch (ThreadDeath death) {
   301             shutdown = true;
   302             throw death;
   303         }
   304         catch (InterruptedException interruptedException) {
   305             shutdown = true; // AppContext.dispose() interrupts all
   306                              // Threads in the AppContext
   307         }
   308         catch (Throwable e) {
   309             processException(e);
   310         }
   311     }
   312 
   313     private void processException(Throwable e) {
   314         if (eventLog.isLoggable(PlatformLogger.FINE)) {
   315             eventLog.fine("Processing exception: " + e);
   316         }
   317 //XXX        getUncaughtExceptionHandler().uncaughtException(this, e);
   318     }
   319     
   320     public static EventDispatchThread findCurrent() {
   321 //XXX        return (EventDispatchThread)Thread.currentThread();
   322         return null;
   323     }
   324 
   325     public synchronized EventQueue getEventQueue() {
   326         return theQueue;
   327     }
   328     public synchronized void setEventQueue(EventQueue eq) {
   329         theQueue = eq;
   330     }
   331 
   332     private static class HierarchyEventFilter implements EventFilter {
   333         private Component modalComponent;
   334         public HierarchyEventFilter(Component modalComponent) {
   335             this.modalComponent = modalComponent;
   336         }
   337         public FilterAction acceptEvent(AWTEvent event) {
   338             if (modalComponent != null) {
   339                 int eventID = event.getID();
   340                 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
   341                                      (eventID <= MouseEvent.MOUSE_LAST);
   342                 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
   343                                       (eventID <= ActionEvent.ACTION_LAST);
   344                 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
   345                 /*
   346                  * filter out MouseEvent and ActionEvent that's outside
   347                  * the modalComponent hierarchy.
   348                  * KeyEvent is handled by using enqueueKeyEvent
   349                  * in Dialog.show
   350                  */
   351                 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
   352                     /*
   353                      * Modal internal frames are handled separately. If event is
   354                      * for some component from another heavyweight than modalComp,
   355                      * it is accepted. If heavyweight is the same - we still accept
   356                      * event and perform further filtering in LightweightDispatcher
   357                      */
   358                     return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
   359                 }
   360                 if (mouseEvent || actionEvent || windowClosingEvent) {
   361                     Object o = event.getSource();
   362                     if (o instanceof sun.awt.ModalExclude) {
   363                         // Exclude this object from modality and
   364                         // continue to pump it's events.
   365                         return FilterAction.ACCEPT;
   366                     } else if (o instanceof Component) {
   367                         Component c = (Component) o;
   368                         // 5.0u3 modal exclusion
   369                         boolean modalExcluded = false;
   370                         if (modalComponent instanceof Container) {
   371                             while (c != modalComponent && c != null) {
   372                                 if ((c instanceof Window) &&
   373                                     (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
   374                                     // Exclude this window and all its children from
   375                                     //  modality and continue to pump it's events.
   376                                     modalExcluded = true;
   377                                     break;
   378                                 }
   379                                 c = c.getParent();
   380                             }
   381                         }
   382                         if (!modalExcluded && (c != modalComponent)) {
   383                             return FilterAction.REJECT;
   384                         }
   385                     }
   386                 }
   387             }
   388             return FilterAction.ACCEPT;
   389         }
   390     }
   391 }