1 /*
   2  * Copyright (c) 2010, 2018, 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 package com.sun.glass.ui.monocle;
  26 
  27 import com.sun.glass.ui.Application;
  28 import com.sun.glass.ui.CommonDialogs.ExtensionFilter;
  29 import com.sun.glass.ui.CommonDialogs.FileChooserResult;
  30 import com.sun.glass.ui.Cursor;
  31 import com.sun.glass.ui.GlassRobot;
  32 import com.sun.glass.ui.Pixels;
  33 import com.sun.glass.ui.Screen;
  34 import com.sun.glass.ui.Size;
  35 import com.sun.glass.ui.Timer;
  36 import com.sun.glass.ui.View;
  37 import com.sun.glass.ui.Window;
  38 import javafx.collections.SetChangeListener;
  39 
  40 import java.io.File;
  41 import java.nio.ByteBuffer;
  42 import java.nio.IntBuffer;
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 
  46 public final class MonocleApplication extends Application {
  47 
  48     private final NativePlatform platform =
  49             NativePlatformFactory.getNativePlatform();
  50     private final RunnableProcessor runnableProcessor = platform.getRunnableProcessor();
  51 
  52     /** Bit to indicate that a device has touch support */
  53     private static final int DEVICE_TOUCH = 0;
  54     /** Bit to indicate that a device has multitouch support */
  55     private static final int DEVICE_MULTITOUCH = 1;
  56     /** Bit to indicate that a device has relative motion pointer support */
  57     private static final int DEVICE_POINTER = 2;
  58     /** Bit to indicate that a device has arrow keys and a select key */
  59     private static final int DEVICE_5WAY = 3;
  60     /** Bit to indicate that a device has a full PC keyboard */
  61     private static final int DEVICE_PC_KEYBOARD = 4;
  62     /** Largest bit used in device capability bitmasks */
  63     private static final int DEVICE_MAX = 4;
  64     /** A running count of the numbers of devices with each device capability */
  65     private int[] deviceFlags = new int[DEVICE_MAX + 1];
  66     private Thread shutdownHookThread;
  67     private Runnable renderEndNotifier = () -> platform.getScreen().swapBuffers();
  68 
  69     MonocleApplication() {
  70         for (InputDevice device : platform.getInputDeviceRegistry().getInputDevices()) {
  71             updateDeviceFlags(device, true);
  72         }
  73         platform.getInputDeviceRegistry().getInputDevices().addListener(
  74                 (SetChangeListener<InputDevice>) change -> {
  75                     if (change.wasAdded()) {
  76                         InputDevice device = change.getElementAdded();
  77                         updateDeviceFlags(device, true);
  78                     } else if (change.wasRemoved()) {
  79                         InputDevice device = change.getElementRemoved();
  80                         updateDeviceFlags(device, false);
  81                     }
  82                 }
  83         );
  84     }
  85 
  86     private void updateDeviceFlags(InputDevice device, boolean added) {
  87         int modifier = added ? 1 : -1;
  88         if (device.isTouch()) {
  89             deviceFlags[DEVICE_TOUCH] += modifier;
  90         }
  91         if (device.isMultiTouch()) {
  92             deviceFlags[DEVICE_MULTITOUCH] += modifier;
  93         }
  94         if (device.isRelative()) {
  95             deviceFlags[DEVICE_POINTER] += modifier;
  96             if (deviceFlags[DEVICE_POINTER] >= 1  && added) {
  97                 staticCursor_setVisible(true);
  98             } else if (deviceFlags[DEVICE_POINTER] < 1 && !added) {
  99                 staticCursor_setVisible(false);
 100             }
 101         }
 102         if (device.isFullKeyboard()) {
 103             deviceFlags[DEVICE_PC_KEYBOARD] += modifier;
 104         }
 105         if (device.is5Way()) {
 106             deviceFlags[DEVICE_5WAY] += modifier;
 107         }
 108     }
 109 
 110     @Override
 111     protected void runLoop(Runnable launchable) {
 112         runnableProcessor.invokeLater(launchable);
 113         long stackSize = AccessController.doPrivileged(
 114                 (PrivilegedAction<Long>)
 115                         () -> Long.getLong("monocle.stackSize", 0));
 116         Thread t = new Thread(
 117                 new ThreadGroup("Event"),
 118                 runnableProcessor,
 119                 "Event Thread",
 120                 stackSize);
 121         setEventThread(t);
 122         t.start();
 123         shutdownHookThread = new Thread("Monocle shutdown hook") {
 124             @Override public void run() {
 125             platform.shutdown();
 126             }
 127         };
 128         Runtime.getRuntime().addShutdownHook(shutdownHookThread);
 129     }
 130 
 131     @Override
 132     protected void _invokeAndWait(Runnable runnable) {
 133         runnableProcessor.invokeAndWait(runnable);
 134     }
 135 
 136     @Override
 137     protected void _invokeLater(Runnable runnable) {
 138         runnableProcessor.invokeLater(runnable);
 139     }
 140 
 141     @Override
 142     protected Object _enterNestedEventLoop() {
 143         return runnableProcessor.enterNestedEventLoop();
 144     }
 145 
 146     @Override
 147     protected void _leaveNestedEventLoop(Object retValue) {
 148         runnableProcessor.leaveNestedEventLoop(retValue);
 149     }
 150 
 151     @Override
 152     public Window createWindow(Window owner, Screen screen, int styleMask) {
 153         return new MonocleWindow(owner, screen, styleMask);
 154     }
 155 
 156     @Override
 157     public Window createWindow(long parent) {
 158         return new MonocleWindow(parent);
 159     }
 160 
 161     @Override
 162     public View createView() {
 163         return new MonocleView();
 164     }
 165 
 166     @Override
 167     public Cursor createCursor(int type) {
 168         return new MonocleCursor(type);
 169     }
 170 
 171     @Override
 172     public Cursor createCursor(int x, int y, Pixels pixels) {
 173         return new MonocleCursor(x, y, pixels);
 174     }
 175 
 176     @Override
 177     protected void staticCursor_setVisible(boolean visible) {
 178         NativeCursor cursor = NativePlatformFactory.getNativePlatform().getCursor();
 179         cursor.setVisibility(deviceFlags[DEVICE_POINTER] >= 1 ? visible : false);
 180     }
 181 
 182     @Override
 183     protected Size staticCursor_getBestSize(int width, int height) {
 184         NativeCursor cursor = NativePlatformFactory.getNativePlatform().getCursor();
 185         return cursor.getBestSize();
 186     }
 187 
 188     @Override
 189     public Pixels createPixels(int width, int height, ByteBuffer data) {
 190         return new MonoclePixels(width, height, data);
 191     }
 192 
 193     @Override
 194     public Pixels createPixels(int width, int height, IntBuffer data) {
 195         return new MonoclePixels(width, height, data);
 196     }
 197 
 198     @Override
 199     public Pixels createPixels(int width, int height, IntBuffer data,
 200                                float scalex, float scaley)
 201     {
 202         return new MonoclePixels(width, height, data, scalex, scaley);
 203     }
 204 
 205     @Override
 206     protected int staticPixels_getNativeFormat() {
 207         return platform.getScreen().getNativeFormat();
 208     }
 209 
 210     @Override
 211     public GlassRobot createRobot() {
 212         return new MonocleRobot();
 213     }
 214 
 215     @Override
 216     protected double staticScreen_getVideoRefreshPeriod() {
 217         return 0.0;
 218     }
 219 
 220     @Override
 221     protected Screen[] staticScreen_getScreens() {
 222         NativeScreen ns = platform.getScreen();
 223         Screen screen = new Screen(1l, // dummy native pointer;
 224                                    ns.getDepth(),
 225                                    0, 0, ns.getWidth(), ns.getHeight(),
 226                                    0, 0, ns.getWidth(), ns.getHeight(),
 227                                    0, 0, ns.getWidth(), ns.getHeight(),
 228                                    ns.getDPI(), ns.getDPI(),
 229                                    ns.getScale(), ns.getScale(),
 230                                    ns.getScale(), ns.getScale());
 231         // Move the cursor to the middle of the screen
 232         MouseState mouseState = new MouseState();
 233         mouseState.setX(ns.getWidth() / 2);
 234         mouseState.setY(ns.getHeight() / 2);
 235         MouseInput.getInstance().setState(mouseState, false);
 236         return new Screen[] { screen };
 237     }
 238 
 239     @Override
 240     public Timer createTimer(Runnable runnable) {
 241         return new MonocleTimer(runnable);
 242     }
 243 
 244     @Override
 245     protected int staticTimer_getMinPeriod() {
 246         return MonocleTimer.getMinPeriod_impl();
 247     }
 248 
 249     @Override
 250     protected int staticTimer_getMaxPeriod() {
 251         return MonocleTimer.getMaxPeriod_impl();
 252     }
 253 
 254     public boolean hasWindowManager() {
 255         return false;
 256     }
 257 
 258     @Override
 259     protected FileChooserResult staticCommonDialogs_showFileChooser(
 260             Window owner, String folder, String filename, String title,
 261             int type, boolean multipleMode,
 262             ExtensionFilter[] extensionFilters,
 263             int defaultFilterIndex) {
 264         throw new UnsupportedOperationException();
 265     }
 266 
 267     @Override
 268     protected File staticCommonDialogs_showFolderChooser(Window owner,
 269                                                          String folder,
 270                                                          String title) {
 271         Thread.dumpStack();
 272         throw new UnsupportedOperationException();
 273     }
 274 
 275     @Override
 276     protected long staticView_getMultiClickTime() {
 277         return MonocleView._getMultiClickTime();
 278     }
 279 
 280     @Override
 281     protected int staticView_getMultiClickMaxX() {
 282         return MonocleView._getMultiClickMaxX();
 283     }
 284 
 285     @Override
 286     protected int staticView_getMultiClickMaxY() {
 287         return MonocleView._getMultiClickMaxY();
 288     }
 289 
 290     @Override
 291     protected boolean _supportsTransparentWindows() {
 292         return true;
 293     }
 294 
 295     @Override
 296     protected boolean _supportsUnifiedWindows() {
 297         return false;
 298     }
 299 
 300     @Override
 301     public boolean hasTwoLevelFocus() {
 302         return deviceFlags[DEVICE_PC_KEYBOARD] == 0 && deviceFlags[DEVICE_5WAY] > 0;
 303     }
 304 
 305     @Override
 306     public boolean hasVirtualKeyboard() {
 307         return deviceFlags[DEVICE_PC_KEYBOARD] == 0 && deviceFlags[DEVICE_TOUCH] > 0;
 308     }
 309 
 310     @Override
 311     public boolean hasTouch() {
 312         return deviceFlags[DEVICE_TOUCH] > 0;
 313     }
 314 
 315     @Override
 316     public boolean hasMultiTouch() {
 317         return deviceFlags[DEVICE_MULTITOUCH] > 0;
 318     }
 319 
 320     @Override
 321     public boolean hasPointer() {
 322         return deviceFlags[DEVICE_POINTER] > 0;
 323     }
 324 
 325     @Override
 326     public void notifyRenderingFinished() {
 327         invokeLater(renderEndNotifier);
 328     }
 329 
 330     @Override
 331     protected void finishTerminating() {
 332         //if this method is getting called, we don't need the shutdown hook
 333         Runtime.getRuntime().removeShutdownHook(shutdownHookThread);
 334         setEventThread(null);
 335         platform.shutdown();
 336         super.finishTerminating();
 337     }
 338 
 339     void enterDnDEventLoop() {
 340         _enterNestedEventLoop();
 341     }
 342 
 343     void leaveDndEventLoop() {
 344         _leaveNestedEventLoop(null);
 345     }
 346 
 347     @Override
 348     protected int _getKeyCodeForChar(char c) {
 349         return KeyInput.getInstance().getKeyCodeForChar(c);
 350     }
 351 
 352 }