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 
  26 package sun.lwawt;
  27 
  28 import java.awt.*;
  29 import java.awt.List;
  30 import java.awt.datatransfer.*;
  31 import java.awt.dnd.DropTarget;
  32 import java.awt.image.*;
  33 import java.awt.peer.*;
  34 import java.security.*;
  35 import java.util.*;
  36 
  37 import sun.awt.*;
  38 import sun.print.*;
  39 import sun.awt.util.ThreadGroupUtils;
  40 
  41 import static sun.lwawt.LWWindowPeer.PeerType;
  42 
  43 public 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 ColorModel getColorModel() throws HeadlessException {
 363         return GraphicsEnvironment.getLocalGraphicsEnvironment()
 364                                   .getDefaultScreenDevice()
 365                                   .getDefaultConfiguration().getColorModel();
 366     }
 367 
 368     @Override
 369     public final boolean isDesktopSupported() {
 370         return true;
 371     }
 372 
 373     @Override
 374     public final boolean isTaskbarSupported() {
 375         return true;
 376     }
 377 
 378     @Override
 379     public final KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() {
 380         return LWKeyboardFocusManagerPeer.getInstance();
 381     }
 382 
 383     @Override
 384     public final synchronized MouseInfoPeer getMouseInfoPeer() {
 385         if (mouseInfoPeer == null) {
 386             mouseInfoPeer = createMouseInfoPeerImpl();
 387         }
 388         return mouseInfoPeer;
 389     }
 390 
 391     protected final MouseInfoPeer createMouseInfoPeerImpl() {
 392         return new LWMouseInfoPeer();
 393     }
 394 
 395     protected abstract PlatformWindow getPlatformWindowUnderMouse();
 396 
 397     @Override
 398     public final PrintJob getPrintJob(Frame frame, String doctitle,
 399                                       Properties props) {
 400         return getPrintJob(frame, doctitle, null, null);
 401     }
 402 
 403     @Override
 404     public final PrintJob getPrintJob(Frame frame, String doctitle,
 405                                       JobAttributes jobAttributes,
 406                                       PageAttributes pageAttributes) {
 407         if (GraphicsEnvironment.isHeadless()) {
 408             throw new IllegalArgumentException();
 409         }
 410 
 411         PrintJob2D printJob = new PrintJob2D(frame, doctitle, jobAttributes, pageAttributes);
 412 
 413         if (!printJob.printDialog()) {
 414             printJob = null;
 415         }
 416 
 417         return printJob;
 418     }
 419 
 420     @Override
 421     public final Clipboard getSystemClipboard() {
 422         SecurityManager security = System.getSecurityManager();
 423         if (security != null) {
 424             security.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
 425         }
 426 
 427         synchronized (this) {
 428             if (clipboard == null) {
 429                 clipboard = createPlatformClipboard();
 430             }
 431         }
 432         return clipboard;
 433     }
 434 
 435     protected abstract SecurityWarningWindow createSecurityWarning(
 436             Window ownerWindow, LWWindowPeer ownerPeer);
 437 
 438     // ---- DELEGATES ---- //
 439 
 440     public abstract Clipboard createPlatformClipboard();
 441 
 442     /*
 443      * Creates a delegate for the given peer type (window, frame, dialog, etc.)
 444      */
 445     protected abstract PlatformWindow createPlatformWindow(PeerType peerType);
 446 
 447     protected abstract PlatformComponent createPlatformComponent();
 448 
 449     protected abstract PlatformComponent createLwPlatformComponent();
 450 
 451     protected abstract FileDialogPeer createFileDialogPeer(FileDialog target);
 452 
 453     protected abstract PlatformDropTarget createDropTarget(DropTarget dropTarget,
 454                                                            Component component,
 455                                                            LWComponentPeer<?, ?> peer);
 456 
 457     // ---- UTILITY METHODS ---- //
 458 
 459     /*
 460      * Expose non-public targetToPeer() method.
 461      */
 462     public static final Object targetToPeer(Object target) {
 463         return SunToolkit.targetToPeer(target);
 464     }
 465 
 466     /*
 467      * Expose non-public targetDisposedPeer() method.
 468      */
 469     public static final void targetDisposedPeer(Object target, Object peer) {
 470         SunToolkit.targetDisposedPeer(target, peer);
 471     }
 472 
 473     /*
 474      * Returns the current cursor manager.
 475      */
 476     public abstract LWCursorManager getCursorManager();
 477 
 478     public static void postEvent(AWTEvent event) {
 479         postEvent(targetToAppContext(event.getSource()), event);
 480     }
 481 
 482     @Override
 483     public final void grab(final Window w) {
 484         final Object peer = AWTAccessor.getComponentAccessor().getPeer(w);
 485         if (peer != null) {
 486             ((LWWindowPeer) peer).grab();
 487         }
 488     }
 489 
 490     @Override
 491     public final void ungrab(final Window w) {
 492         final Object peer = AWTAccessor.getComponentAccessor().getPeer(w);
 493         if (peer != null) {
 494             ((LWWindowPeer) peer).ungrab(false);
 495         }
 496     }
 497 
 498     @Override
 499     protected final Object lazilyLoadDesktopProperty(final String name) {
 500         if (name.equals("awt.dynamicLayoutSupported")) {
 501             return isDynamicLayoutSupported();
 502         }
 503         return super.lazilyLoadDesktopProperty(name);
 504     }
 505 
 506     @Override
 507     public final void setDynamicLayout(final boolean dynamic) {
 508         dynamicLayoutSetting = dynamic;
 509     }
 510 
 511     @Override
 512     protected final boolean isDynamicLayoutSet() {
 513         return dynamicLayoutSetting;
 514     }
 515 
 516     @Override
 517     public final boolean isDynamicLayoutActive() {
 518         // "Live resizing" is active by default and user's data is ignored.
 519         return isDynamicLayoutSupported();
 520     }
 521 
 522     /**
 523      * Returns true if dynamic layout of Containers on resize is supported by
 524      * the underlying operating system and/or window manager.
 525      */
 526     protected final boolean isDynamicLayoutSupported() {
 527         // "Live resizing" is supported by default.
 528         return true;
 529     }
 530 }