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