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