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