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