1 /*
   2  * Copyright (c) 2011, 2015, 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.misc.ManagedLocalsThread;
  39 import sun.print.*;
  40 import sun.awt.util.ThreadGroupUtils;
  41 
  42 import static sun.lwawt.LWWindowPeer.PeerType;
  43 
  44 public abstract class LWToolkit extends SunToolkit implements Runnable {
  45 
  46     private static final int STATE_NONE = 0;
  47     private static final int STATE_INIT = 1;
  48     private static final int STATE_MESSAGELOOP = 2;
  49     private static final int STATE_SHUTDOWN = 3;
  50     private static final int STATE_CLEANUP = 4;
  51     private static final int STATE_DONE = 5;
  52 
  53     private int runState = STATE_NONE;
  54 
  55     private Clipboard clipboard;
  56     private MouseInfoPeer mouseInfoPeer;
  57 
  58     /**
  59      * Dynamic Layout Resize client code setting.
  60      */
  61     private volatile boolean dynamicLayoutSetting = true;
  62 
  63     protected LWToolkit() {
  64     }
  65 
  66     /*
  67      * This method is called by subclasses to start this toolkit
  68      * by launching the message loop.
  69      *
  70      * This method waits for the toolkit to be completely initialized
  71      * and returns before the message pump is started.
  72      */
  73     protected final void init() {
  74         AWTAutoShutdown.notifyToolkitThreadBusy();
  75         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
  76             Runnable shutdownRunnable = () -> {
  77                 shutdown();
  78                 waitForRunState(STATE_CLEANUP);
  79             };
  80             Thread shutdown = new ManagedLocalsThread(
  81                     ThreadGroupUtils.getRootThreadGroup(), shutdownRunnable);
  82             shutdown.setContextClassLoader(null);
  83             Runtime.getRuntime().addShutdownHook(shutdown);
  84             String name = "AWT-LW";
  85             Thread toolkitThread = new ManagedLocalsThread(
  86                     ThreadGroupUtils.getRootThreadGroup(), this, name);
  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 KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() {
 375         return LWKeyboardFocusManagerPeer.getInstance();
 376     }
 377 
 378     @Override
 379     public final synchronized MouseInfoPeer getMouseInfoPeer() {
 380         if (mouseInfoPeer == null) {
 381             mouseInfoPeer = createMouseInfoPeerImpl();
 382         }
 383         return mouseInfoPeer;
 384     }
 385 
 386     protected final MouseInfoPeer createMouseInfoPeerImpl() {
 387         return new LWMouseInfoPeer();
 388     }
 389 
 390     protected abstract PlatformWindow getPlatformWindowUnderMouse();
 391 
 392     @Override
 393     public final PrintJob getPrintJob(Frame frame, String doctitle,
 394                                       Properties props) {
 395         return getPrintJob(frame, doctitle, null, null);
 396     }
 397 
 398     @Override
 399     public final PrintJob getPrintJob(Frame frame, String doctitle,
 400                                       JobAttributes jobAttributes,
 401                                       PageAttributes pageAttributes) {
 402         if (GraphicsEnvironment.isHeadless()) {
 403             throw new IllegalArgumentException();
 404         }
 405 
 406         PrintJob2D printJob = new PrintJob2D(frame, doctitle, jobAttributes, pageAttributes);
 407 
 408         if (!printJob.printDialog()) {
 409             printJob = null;
 410         }
 411 
 412         return printJob;
 413     }
 414 
 415     @Override
 416     public final Clipboard getSystemClipboard() {
 417         SecurityManager security = System.getSecurityManager();
 418         if (security != null) {
 419             security.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
 420         }
 421 
 422         synchronized (this) {
 423             if (clipboard == null) {
 424                 clipboard = createPlatformClipboard();
 425             }
 426         }
 427         return clipboard;
 428     }
 429 
 430     protected abstract SecurityWarningWindow createSecurityWarning(
 431             Window ownerWindow, LWWindowPeer ownerPeer);
 432 
 433     // ---- DELEGATES ---- //
 434 
 435     public abstract Clipboard createPlatformClipboard();
 436 
 437     /*
 438      * Creates a delegate for the given peer type (window, frame, dialog, etc.)
 439      */
 440     protected abstract PlatformWindow createPlatformWindow(PeerType peerType);
 441 
 442     protected abstract PlatformComponent createPlatformComponent();
 443 
 444     protected abstract PlatformComponent createLwPlatformComponent();
 445 
 446     protected abstract FileDialogPeer createFileDialogPeer(FileDialog target);
 447 
 448     protected abstract PlatformDropTarget createDropTarget(DropTarget dropTarget,
 449                                                            Component component,
 450                                                            LWComponentPeer<?, ?> peer);
 451 
 452     // ---- UTILITY METHODS ---- //
 453 
 454     /*
 455      * Expose non-public targetToPeer() method.
 456      */
 457     public static final Object targetToPeer(Object target) {
 458         return SunToolkit.targetToPeer(target);
 459     }
 460 
 461     /*
 462      * Expose non-public targetDisposedPeer() method.
 463      */
 464     public static final void targetDisposedPeer(Object target, Object peer) {
 465         SunToolkit.targetDisposedPeer(target, peer);
 466     }
 467 
 468     /*
 469      * Returns the current cursor manager.
 470      */
 471     public abstract LWCursorManager getCursorManager();
 472 
 473     public static void postEvent(AWTEvent event) {
 474         postEvent(targetToAppContext(event.getSource()), event);
 475     }
 476 
 477     @Override
 478     public final void grab(final Window w) {
 479         final Object peer = AWTAccessor.getComponentAccessor().getPeer(w);
 480         if (peer != null) {
 481             ((LWWindowPeer) peer).grab();
 482         }
 483     }
 484 
 485     @Override
 486     public final void ungrab(final Window w) {
 487         final Object peer = AWTAccessor.getComponentAccessor().getPeer(w);
 488         if (peer != null) {
 489             ((LWWindowPeer) peer).ungrab(false);
 490         }
 491     }
 492 
 493     @Override
 494     protected final Object lazilyLoadDesktopProperty(final String name) {
 495         if (name.equals("awt.dynamicLayoutSupported")) {
 496             return isDynamicLayoutSupported();
 497         }
 498         return super.lazilyLoadDesktopProperty(name);
 499     }
 500 
 501     @Override
 502     public final void setDynamicLayout(final boolean dynamic) {
 503         dynamicLayoutSetting = dynamic;
 504     }
 505 
 506     @Override
 507     protected final boolean isDynamicLayoutSet() {
 508         return dynamicLayoutSetting;
 509     }
 510 
 511     @Override
 512     public final boolean isDynamicLayoutActive() {
 513         // "Live resizing" is active by default and user's data is ignored.
 514         return isDynamicLayoutSupported();
 515     }
 516 
 517     /**
 518      * Returns true if dynamic layout of Containers on resize is supported by
 519      * the underlying operating system and/or window manager.
 520      */
 521     protected final boolean isDynamicLayoutSupported() {
 522         // "Live resizing" is supported by default.
 523         return true;
 524     }
 525 }