1/*
2 * Copyright (c) 2011, 2016, 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
26package sun.lwawt;
27
28import java.awt.*;
29import java.awt.List;
30import java.awt.datatransfer.*;
31import java.awt.dnd.DropTarget;
32import java.awt.image.*;
33import java.awt.peer.*;
34import java.security.*;
35import java.util.*;
36
37import sun.awt.*;
38import sun.print.*;
39import sun.awt.util.ThreadGroupUtils;
40
41import static sun.lwawt.LWWindowPeer.PeerType;
42
43public abstract class LWToolkit extends SunToolkit implements Runnable {
44
45    private static final int STATE_NONE = 0;
46    private static final int STATE_INIT = 1;
47    private static final int STATE_MESSAGELOOP = 2;
48    private static final int STATE_SHUTDOWN = 3;
49    private static final int STATE_CLEANUP = 4;
50    private static final int STATE_DONE = 5;
51
52    private int runState = STATE_NONE;
53
54    private Clipboard clipboard;
55    private MouseInfoPeer mouseInfoPeer;
56
57    /**
58     * Dynamic Layout Resize client code setting.
59     */
60    private volatile boolean dynamicLayoutSetting = true;
61
62    protected LWToolkit() {
63    }
64
65    /*
66     * This method is called by subclasses to start this toolkit
67     * by launching the message loop.
68     *
69     * This method waits for the toolkit to be completely initialized
70     * and returns before the message pump is started.
71     */
72    protected final void init() {
73        AWTAutoShutdown.notifyToolkitThreadBusy();
74        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
75            Runnable shutdownRunnable = () -> {
76                shutdown();
77                waitForRunState(STATE_CLEANUP);
78            };
79            Thread shutdown = new Thread(
80                    ThreadGroupUtils.getRootThreadGroup(), shutdownRunnable,
81                    "AWT-Shutdown", 0, false);
82            shutdown.setContextClassLoader(null);
83            Runtime.getRuntime().addShutdownHook(shutdown);
84            String name = "AWT-LW";
85            Thread toolkitThread = new Thread(
86                   ThreadGroupUtils.getRootThreadGroup(), this, name, 0, false);
87            toolkitThread.setDaemon(true);
88            toolkitThread.setPriority(Thread.NORM_PRIORITY + 1);
89            toolkitThread.start();
90            return null;
91        });
92        waitForRunState(STATE_MESSAGELOOP);
93    }
94
95    /*
96     * Implemented in subclasses to initialize platform-dependent
97     * part of the toolkit (open X display connection, create
98     * toolkit HWND, etc.)
99     *
100     * This method is called on the toolkit thread.
101     */
102    protected abstract void platformInit();
103
104    /*
105     * Sends a request to stop the message pump.
106     */
107    public final void shutdown() {
108        setRunState(STATE_SHUTDOWN);
109        platformShutdown();
110    }
111
112    /*
113     * Implemented in subclasses to release all the platform-
114     * dependent resources. Called after the message loop is
115     * terminated.
116     *
117     * Could be called (always called?) on a non-toolkit thread.
118     */
119    protected abstract void platformShutdown();
120
121    /*
122     * Implemented in subclasses to release all the platform
123     * resources before the application is terminated.
124     *
125     * This method is called on the toolkit thread.
126     */
127    protected abstract void platformCleanup();
128
129    private synchronized int getRunState() {
130        return runState;
131    }
132
133    private synchronized void setRunState(int state) {
134        runState = state;
135        notifyAll();
136    }
137
138    public final boolean isTerminating() {
139        return getRunState() >= STATE_SHUTDOWN;
140    }
141
142    private void waitForRunState(int state) {
143        while (getRunState() < state) {
144            try {
145                synchronized (this) {
146                    wait();
147                }
148            } catch (InterruptedException z) {
149                // TODO: log
150                break;
151            }
152        }
153    }
154
155    @Override
156    public final void run() {
157        setRunState(STATE_INIT);
158        platformInit();
159        AWTAutoShutdown.notifyToolkitThreadFree();
160        setRunState(STATE_MESSAGELOOP);
161        while (getRunState() < STATE_SHUTDOWN) {
162            try {
163                platformRunMessage();
164                if (Thread.currentThread().isInterrupted()) {
165                    if (AppContext.getAppContext().isDisposed()) {
166                        break;
167                    }
168                }
169            } catch (ThreadDeath td) {
170                //XXX: if there isn't native code on the stack, the VM just
171                //kills the thread right away. Do we expect to catch it
172                //nevertheless?
173                break;
174            } catch (Throwable t) {
175                // TODO: log
176                System.err.println("Exception on the toolkit thread");
177                t.printStackTrace(System.err);
178            }
179        }
180        //XXX: if that's a secondary loop, jump back to the STATE_MESSAGELOOP
181        setRunState(STATE_CLEANUP);
182        AWTAutoShutdown.notifyToolkitThreadFree();
183        platformCleanup();
184        setRunState(STATE_DONE);
185    }
186
187    /*
188     * Process the next message(s) from the native event queue.
189     *
190     * Initially, all the LWToolkit implementations were supposed
191     * to have the similar message loop sequence: check if any events
192     * available, peek events, wait. However, the later analysis shown
193     * that X11 and Windows implementations are really different, so
194     * let the subclasses do whatever they require.
195     */
196    protected abstract void platformRunMessage();
197
198    public static LWToolkit getLWToolkit() {
199        return (LWToolkit)Toolkit.getDefaultToolkit();
200    }
201
202    // ---- TOPLEVEL PEERS ---- //
203
204    /*
205     * Note that LWWindowPeer implements WindowPeer, FramePeer
206     * and DialogPeer interfaces.
207     */
208    protected LWWindowPeer createDelegatedPeer(Window target,
209                                               PlatformComponent platformComponent,
210                                               PlatformWindow platformWindow,
211                                               PeerType peerType) {
212        LWWindowPeer peer = new LWWindowPeer(target, platformComponent, platformWindow, peerType);
213        targetCreatedPeer(target, peer);
214        peer.initialize();
215        return peer;
216    }
217
218    @Override
219    public final FramePeer createLightweightFrame(LightweightFrame target) {
220        PlatformComponent platformComponent = createLwPlatformComponent();
221        PlatformWindow platformWindow = createPlatformWindow(PeerType.LW_FRAME);
222        LWLightweightFramePeer peer = new LWLightweightFramePeer(target,
223                                                                 platformComponent,
224                                                                 platformWindow);
225        targetCreatedPeer(target, peer);
226        peer.initialize();
227        return peer;
228    }
229
230    @Override
231    public final WindowPeer createWindow(Window target) {
232        PlatformComponent platformComponent = createPlatformComponent();
233        PlatformWindow platformWindow = createPlatformWindow(PeerType.SIMPLEWINDOW);
234        return createDelegatedPeer(target, platformComponent, platformWindow, PeerType.SIMPLEWINDOW);
235    }
236
237    @Override
238    public final FramePeer createFrame(Frame target) {
239        PlatformComponent platformComponent = createPlatformComponent();
240        PlatformWindow platformWindow = createPlatformWindow(PeerType.FRAME);
241        return createDelegatedPeer(target, platformComponent, platformWindow, PeerType.FRAME);
242    }
243
244    @Override
245    public DialogPeer createDialog(Dialog target) {
246        PlatformComponent platformComponent = createPlatformComponent();
247        PlatformWindow platformWindow = createPlatformWindow(PeerType.DIALOG);
248        return createDelegatedPeer(target, platformComponent, platformWindow, PeerType.DIALOG);
249    }
250
251    @Override
252    public final FileDialogPeer createFileDialog(FileDialog target) {
253        FileDialogPeer peer = createFileDialogPeer(target);
254        targetCreatedPeer(target, peer);
255        return peer;
256    }
257
258    // ---- LIGHTWEIGHT COMPONENT PEERS ---- //
259
260    @Override
261    public final ButtonPeer createButton(Button target) {
262        PlatformComponent platformComponent = createPlatformComponent();
263        LWButtonPeer peer = new LWButtonPeer(target, platformComponent);
264        targetCreatedPeer(target, peer);
265        peer.initialize();
266        return peer;
267    }
268
269    @Override
270    public final CheckboxPeer createCheckbox(Checkbox target) {
271        PlatformComponent platformComponent = createPlatformComponent();
272        LWCheckboxPeer peer = new LWCheckboxPeer(target, platformComponent);
273        targetCreatedPeer(target, peer);
274        peer.initialize();
275        return peer;
276    }
277
278    @Override
279    public final ChoicePeer createChoice(Choice target) {
280        PlatformComponent platformComponent = createPlatformComponent();
281        LWChoicePeer peer = new LWChoicePeer(target, platformComponent);
282        targetCreatedPeer(target, peer);
283        peer.initialize();
284        return peer;
285    }
286
287    @Override
288    public final LabelPeer createLabel(Label target) {
289        PlatformComponent platformComponent = createPlatformComponent();
290        LWLabelPeer peer = new LWLabelPeer(target, platformComponent);
291        targetCreatedPeer(target, peer);
292        peer.initialize();
293        return peer;
294    }
295
296    @Override
297    public final CanvasPeer createCanvas(Canvas target) {
298        PlatformComponent platformComponent = createPlatformComponent();
299        LWCanvasPeer<?, ?> peer = new LWCanvasPeer<>(target, platformComponent);
300        targetCreatedPeer(target, peer);
301        peer.initialize();
302        return peer;
303    }
304
305    @Override
306    public final ListPeer createList(List target) {
307        PlatformComponent platformComponent = createPlatformComponent();
308        LWListPeer peer = new LWListPeer(target, platformComponent);
309        targetCreatedPeer(target, peer);
310        peer.initialize();
311        return peer;
312    }
313
314    @Override
315    public final PanelPeer createPanel(Panel target) {
316        PlatformComponent platformComponent = createPlatformComponent();
317        LWPanelPeer peer = new LWPanelPeer(target, platformComponent);
318        targetCreatedPeer(target, peer);
319        peer.initialize();
320        return peer;
321    }
322
323    @Override
324    public final ScrollPanePeer createScrollPane(ScrollPane target) {
325        PlatformComponent platformComponent = createPlatformComponent();
326        LWScrollPanePeer peer = new LWScrollPanePeer(target, platformComponent);
327        targetCreatedPeer(target, peer);
328        peer.initialize();
329        return peer;
330    }
331
332    @Override
333    public final ScrollbarPeer createScrollbar(Scrollbar target) {
334        PlatformComponent platformComponent = createPlatformComponent();
335        LWScrollBarPeer peer = new LWScrollBarPeer(target, platformComponent);
336        targetCreatedPeer(target, peer);
337        peer.initialize();
338        return peer;
339    }
340
341    @Override
342    public final TextAreaPeer createTextArea(TextArea target) {
343        PlatformComponent platformComponent = createPlatformComponent();
344        LWTextAreaPeer peer = new LWTextAreaPeer(target, platformComponent);
345        targetCreatedPeer(target, peer);
346        peer.initialize();
347        return peer;
348    }
349
350    @Override
351    public final TextFieldPeer createTextField(TextField target) {
352        PlatformComponent platformComponent = createPlatformComponent();
353        LWTextFieldPeer peer = new LWTextFieldPeer(target, platformComponent);
354        targetCreatedPeer(target, peer);
355        peer.initialize();
356        return peer;
357    }
358
359    // ---- NON-COMPONENT PEERS ---- //
360
361    @Override
362    public final boolean isDesktopSupported() {
363        return true;
364    }
365
366    @Override
367    public final boolean isTaskbarSupported() {
368        return true;
369    }
370
371    @Override
372    public final KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() {
373        return LWKeyboardFocusManagerPeer.getInstance();
374    }
375
376    @Override
377    public final synchronized MouseInfoPeer getMouseInfoPeer() {
378        if (mouseInfoPeer == null) {
379            mouseInfoPeer = createMouseInfoPeerImpl();
380        }
381        return mouseInfoPeer;
382    }
383
384    protected final MouseInfoPeer createMouseInfoPeerImpl() {
385        return new LWMouseInfoPeer();
386    }
387
388    protected abstract PlatformWindow getPlatformWindowUnderMouse();
389
390    @Override
391    public final PrintJob getPrintJob(Frame frame, String doctitle,
392                                      Properties props) {
393        return getPrintJob(frame, doctitle, null, null);
394    }
395
396    @Override
397    public final PrintJob getPrintJob(Frame frame, String doctitle,
398                                      JobAttributes jobAttributes,
399                                      PageAttributes pageAttributes) {
400        if (frame == null) {
401            throw new NullPointerException("frame must not be null");
402        }
403
404        if (GraphicsEnvironment.isHeadless()) {
405            throw new IllegalArgumentException();
406        }
407
408        PrintJob2D printJob = new PrintJob2D(frame, doctitle, jobAttributes, pageAttributes);
409
410        if (!printJob.printDialog()) {
411            printJob = null;
412        }
413
414        return printJob;
415    }
416
417    @Override
418    public final Clipboard getSystemClipboard() {
419        SecurityManager security = System.getSecurityManager();
420        if (security != null) {
421            security.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
422        }
423
424        synchronized (this) {
425            if (clipboard == null) {
426                clipboard = createPlatformClipboard();
427            }
428        }
429        return clipboard;
430    }
431
432    protected abstract SecurityWarningWindow createSecurityWarning(
433            Window ownerWindow, LWWindowPeer ownerPeer);
434
435    // ---- DELEGATES ---- //
436
437    public abstract Clipboard createPlatformClipboard();
438
439    /*
440     * Creates a delegate for the given peer type (window, frame, dialog, etc.)
441     */
442    protected abstract PlatformWindow createPlatformWindow(PeerType peerType);
443
444    protected abstract PlatformComponent createPlatformComponent();
445
446    protected abstract PlatformComponent createLwPlatformComponent();
447
448    protected abstract FileDialogPeer createFileDialogPeer(FileDialog target);
449
450    protected abstract PlatformDropTarget createDropTarget(DropTarget dropTarget,
451                                                           Component component,
452                                                           LWComponentPeer<?, ?> peer);
453
454    // ---- UTILITY METHODS ---- //
455
456    /*
457     * Expose non-public targetToPeer() method.
458     */
459    public static final Object targetToPeer(Object target) {
460        return SunToolkit.targetToPeer(target);
461    }
462
463    /*
464     * Expose non-public targetDisposedPeer() method.
465     */
466    public static final void targetDisposedPeer(Object target, Object peer) {
467        SunToolkit.targetDisposedPeer(target, peer);
468    }
469
470    /*
471     * Returns the current cursor manager.
472     */
473    public abstract LWCursorManager getCursorManager();
474
475    public static void postEvent(AWTEvent event) {
476        postEvent(targetToAppContext(event.getSource()), event);
477    }
478
479    @Override
480    public final void grab(final Window w) {
481        final Object peer = AWTAccessor.getComponentAccessor().getPeer(w);
482        if (peer != null) {
483            ((LWWindowPeer) peer).grab();
484        }
485    }
486
487    @Override
488    public final void ungrab(final Window w) {
489        final Object peer = AWTAccessor.getComponentAccessor().getPeer(w);
490        if (peer != null) {
491            ((LWWindowPeer) peer).ungrab(false);
492        }
493    }
494
495    @Override
496    protected final Object lazilyLoadDesktopProperty(final String name) {
497        if (name.equals("awt.dynamicLayoutSupported")) {
498            return isDynamicLayoutSupported();
499        }
500        return super.lazilyLoadDesktopProperty(name);
501    }
502
503    @Override
504    public final void setDynamicLayout(final boolean dynamic) {
505        dynamicLayoutSetting = dynamic;
506    }
507
508    @Override
509    protected final boolean isDynamicLayoutSet() {
510        return dynamicLayoutSetting;
511    }
512
513    @Override
514    public final boolean isDynamicLayoutActive() {
515        // "Live resizing" is active by default and user's data is ignored.
516        return isDynamicLayoutSupported();
517    }
518
519    /**
520     * Returns true if dynamic layout of Containers on resize is supported by
521     * the underlying operating system and/or window manager.
522     */
523    protected final boolean isDynamicLayoutSupported() {
524        // "Live resizing" is supported by default.
525        return true;
526    }
527}
528