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 }