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 }