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