1 /*
   2  * Copyright (c) 2013, 2014, 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 
  26 package com.sun.glass.ui.monocle;
  27 
  28 import com.sun.glass.ui.Robot;
  29 import javafx.animation.AnimationTimer;
  30 import javafx.application.Application;
  31 import javafx.application.Platform;
  32 import javafx.event.EventHandler;
  33 import javafx.geometry.Rectangle2D;
  34 import javafx.scene.Group;
  35 import javafx.scene.Scene;
  36 import javafx.scene.input.TouchEvent;
  37 import javafx.scene.input.TouchPoint;
  38 import javafx.scene.paint.Color;
  39 import javafx.scene.shape.Rectangle;
  40 import javafx.stage.Screen;
  41 import javafx.stage.Stage;
  42 import javafx.stage.StageStyle;
  43 import org.junit.Assert;
  44 
  45 import java.util.List;
  46 import java.util.concurrent.CountDownLatch;
  47 import java.util.concurrent.Semaphore;
  48 import java.util.concurrent.TimeUnit;
  49 import java.util.concurrent.atomic.AtomicReference;
  50 import java.util.function.Consumer;
  51 
  52 public class TestApplication extends Application {
  53 
  54     private static final boolean verbose = Boolean.getBoolean("verbose");
  55     private static final double timeScale = Double.parseDouble(
  56             System.getProperty("timeScale", "1"));
  57     private static Stage stage;
  58     static final Semaphore ready = new Semaphore(1);
  59     private static int tapRadius;
  60     private static Group root;
  61     private static String glassPlatform;
  62     private static boolean isMonocle;
  63     private static boolean isLens;
  64     private static AtomicReference<Rectangle2D> screen = new AtomicReference<>();
  65 
  66     private static void initGlassPlatform() {
  67         if (glassPlatform == null) {
  68             try {
  69                 TestRunnable.invokeAndWait(
  70                         () -> glassPlatform = System.getProperty(
  71                                 "glass.platform"));
  72             } catch (Exception e) {
  73                 e.printStackTrace();
  74             }
  75         }
  76         isMonocle = "Monocle".equals(glassPlatform);
  77         isLens = "Lens".equals(glassPlatform);
  78     }
  79 
  80     public static boolean isMonocle() {
  81         initGlassPlatform();
  82         return isMonocle;
  83     }
  84 
  85     public static boolean isLens() {
  86         initGlassPlatform();
  87         return isLens;
  88     }
  89 
  90     public void start(Stage stage) throws Exception {
  91         TestApplication.stage = stage;
  92         stage.initStyle(StageStyle.UNDECORATED);
  93         ready.release();
  94     }
  95 
  96     public static Stage getStage() throws InterruptedException {
  97         if (stage == null) {
  98             ready.acquire();
  99             UInput.setup();
 100             new Thread(() -> Application.launch(TestApplication.class)).start();
 101             ready.acquire();
 102             Platform.runLater(() -> {
 103                 if (isMonocle()) {
 104                     tapRadius = TouchInput.getInstance().getTouchRadius();
 105                 } else {
 106                     tapRadius = Integer.getInteger("lens.input.touch.TapRadius", 20);
 107                 }
 108                 ready.release();
 109             });
 110             ready.acquire();          
 111         }
 112         return stage;
 113     }
 114 
 115     public static Group getRootGroup() {
 116         return root;
 117     }
 118 
 119     public static void showFullScreenScene() throws Exception {
 120         TestApplication.getStage();
 121         frameWait(2);
 122         new TestRunnable() {
 123             @Override
 124             public void test() throws Exception {
 125                 Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
 126                 stage.setX(0.0);
 127                 stage.setY(0.0);
 128                 stage.setWidth(bounds.getWidth());
 129                 stage.setHeight(bounds.getHeight());
 130                 Rectangle r = new Rectangle(bounds.getWidth(), bounds.getHeight());
 131                 r.setFill(Color.BLUE);
 132                 root = new Group();
 133                 root.getChildren().add(r);
 134                 Scene scene = new Scene(root, bounds.getWidth(), bounds.getHeight());
 135                 stage.setScene(scene);
 136                 stage.show();
 137                 stage.requestFocus();
 138             }
 139         }.invokeAndWait();
 140         frameWait(2);
 141     }
 142 
 143     public static void showInMiddleOfScreen() throws Exception {
 144         TestApplication.getStage();
 145         // wait for events to finish being delivered to the previous scene
 146         frameWait(2);
 147         new TestRunnable() {
 148             @Override
 149             public void test() throws Exception {
 150                 Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
 151                 stage.setX(bounds.getWidth() / 4);
 152                 stage.setY(bounds.getHeight() / 4);
 153                 stage.setWidth(bounds.getWidth() / 2);
 154                 stage.setHeight(bounds.getHeight() / 2);
 155                 Rectangle r = new Rectangle(bounds.getWidth() / 2,
 156                                             bounds.getHeight() / 2);
 157                 r.setFill(Color.BLUE);
 158                 root = new Group();
 159                 root.getChildren().add(r);
 160                 Scene scene = new Scene(root, bounds.getWidth() / 2,
 161                                         bounds.getHeight() / 2);
 162                 stage.setScene(scene);
 163 
 164                 stage.show();
 165                 stage.requestFocus();
 166             }
 167         }.invokeAndWait();
 168         frameWait(2);
 169     }
 170 
 171     public static void waitForNextPulse() throws InterruptedException {
 172         frameWait(1);
 173     }
 174 
 175     public static void waitForLayout() throws InterruptedException {
 176         frameWait(5);
 177     }
 178 
 179     private static void frameWait(int n) {
 180         final CountDownLatch frameCounter = new CountDownLatch(n);
 181         Platform.runLater(() -> new AnimationTimer() {
 182             @Override
 183             public void handle(long now) {
 184                 frameCounter.countDown();
 185                 if (frameCounter.getCount() == 0l) {
 186                     stop();
 187                 }
 188             }
 189         }.start());
 190         try {
 191             frameCounter.await();
 192         } catch (InterruptedException ex) {
 193             Assert.fail("Unexpected exception: " + ex);
 194         }
 195     }
 196 
 197     public static void addKeyListeners() throws Exception {
 198         getStage().getScene().setOnKeyTyped((e) -> TestLog.log(
 199                 "Key typed: " + e.getCharacter()));
 200         getStage().getScene().setOnKeyPressed((e) -> TestLog.log("Key pressed: " + e.getCode()));
 201         getStage().getScene().setOnKeyReleased((e) -> TestLog.log("Key released: " + e.getCode()));
 202     }
 203 
 204     public static void addMouseListeners() throws Exception {
 205         getStage().getScene().setOnMousePressed((e) -> TestLog.log("Mouse pressed: "
 206                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 207         getStage().getScene().setOnMouseMoved((e) -> TestLog.log("Mouse moved: "
 208                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 209         getStage().getScene().setOnMouseDragged((e) -> TestLog.log("Mouse dragged: "
 210                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 211         getStage().getScene().setOnMouseReleased((e) -> TestLog.log("Mouse released: "
 212                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 213         getStage().getScene().setOnMouseClicked((e) -> TestLog.log("Mouse clicked: "
 214                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 215         getStage().getScene().setOnMouseEntered((e) -> TestLog.log("Mouse entered: "
 216                             + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 217         getStage().getScene().setOnMouseExited((e) -> TestLog.log("Mouse exited: "
 218                             + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 219     }
 220 
 221     public static void addTouchListeners() throws Exception {
 222         Consumer<List<TouchPoint>> logTouchPoints = (tps) -> {
 223             TestLog.log("Touch points count: [" + tps.size() +"]"); 
 224             for (TouchPoint tp : tps) {
 225                 TestLog.log("TouchPoint: " + tp.getState() + " "
 226                         + (int) tp.getScreenX() + ", " + (int) tp.getScreenY()
 227                         + " id=" + tp.getId());
 228             }
 229         };
 230         getStage().getScene().setOnTouchPressed((e) -> {
 231             TestLog.log("Touch pressed: "
 232                     + (int) e.getTouchPoint().getScreenX()
 233                     + ", "
 234                     + (int) e.getTouchPoint().getScreenY());
 235             logTouchPoints.accept(e.getTouchPoints());
 236         });
 237         getStage().getScene().setOnTouchReleased((e) -> {
 238             TestLog.log("Touch released: "
 239                     + (int) e.getTouchPoint().getScreenX()
 240                     + ", "
 241                     + (int) e.getTouchPoint().getScreenY());
 242             logTouchPoints.accept(e.getTouchPoints());
 243         });
 244         getStage().getScene().setOnTouchMoved((e) -> {
 245             TestLog.log("Touch moved: "
 246                     + (int) e.getTouchPoint().getScreenX()
 247                     + ", "
 248                     + (int) e.getTouchPoint().getScreenY());
 249             logTouchPoints.accept(e.getTouchPoints());
 250         });
 251         getStage().getScene().setOnTouchStationary((e) -> {
 252             TestLog.log("Touch stationary: "
 253                     + (int) e.getTouchPoint().getScreenX()
 254                     + ", "
 255                     + (int) e.getTouchPoint().getScreenY());
 256             logTouchPoints.accept(e.getTouchPoints());
 257         });
 258     }
 259 
 260     public static void addGestureListeners() throws Exception {
 261         //Zoom
 262         getStage().getScene().setOnZoom((e) -> TestLog.log("Zoom, factor: " + e.getZoomFactor()
 263                 + ", total factor: " + e.getTotalZoomFactor()
 264                 + ", inertia value: " + e.isInertia()));
 265 
 266         getStage().getScene().setOnZoomStarted((e) -> TestLog.log("Zoom started, factor: " + e.getZoomFactor()
 267                 + ", total factor: " + e.getTotalZoomFactor()
 268                 + ", inertia value: " + e.isInertia()));
 269 
 270         getStage().getScene().setOnZoomFinished((e) -> TestLog.log("Zoom finished, factor: " + e.getZoomFactor()
 271                 + ", total factor: " + e.getTotalZoomFactor()
 272                 + ", inertia value: " + e.isInertia()));
 273 
 274         //Rotate
 275         getStage().getScene().setOnRotate((e) -> TestLog.log("Rotation, angle: " + Math.round(e.getAngle())
 276                 + ", total angle: " + Math.round(e.getTotalAngle())
 277                 + ", inertia value: " + e.isInertia()));
 278 
 279         getStage().getScene().setOnRotationStarted((e) -> TestLog.log("Rotation started, angle: " + Math.round(e.getAngle())
 280                 + ", total angle: " + Math.round(e.getTotalAngle())
 281                 + ", inertia value: " + e.isInertia()));
 282 
 283         getStage().getScene().setOnRotationFinished((e) -> TestLog.log("Rotation finished, angle: " + Math.round(e.getAngle())
 284                 + ", total angle: " + Math.round(e.getTotalAngle())
 285                 + ", inertia value: " + e.isInertia()));
 286 
 287         //Scroll
 288         getStage().getScene().setOnScroll((e) -> TestLog.log("Scroll, DeltaX: " + Math.round(e.getDeltaX())
 289                 + ", DeltaY: " + Math.round(e.getDeltaY())
 290                 + ", totalDeltaX: " + Math.round(e.getTotalDeltaX())
 291                 + ", totalDeltaY: " + Math.round(e.getTotalDeltaY())
 292                 + ", touch points: " + e.getTouchCount()
 293                 + ", inertia value: " + e.isInertia()));
 294 
 295         getStage().getScene().setOnScrollStarted((e) -> TestLog.log("Scroll started, DeltaX: " + Math.round(e.getDeltaX())
 296                 + ", DeltaY: " + Math.round(e.getDeltaY())
 297                 + ", totalDeltaX: " + Math.round(e.getTotalDeltaX())
 298                 + ", totalDeltaY: " + Math.round(e.getTotalDeltaY())
 299                 + ", touch points: " + e.getTouchCount()
 300                 + ", inertia value: " + e.isInertia()));
 301 
 302         getStage().getScene().setOnScrollFinished((e) -> TestLog.log("Scroll finished, DeltaX: " + Math.round(e.getDeltaX())
 303                 + ", DeltaY: " + Math.round(e.getDeltaY())
 304                 + ", totalDeltaX: " + Math.round(e.getTotalDeltaX())
 305                 + ", totalDeltaY: " + Math.round(e.getTotalDeltaY())
 306                 + ", touch points: " + e.getTouchCount()
 307                 + ", inertia value: " + e.isInertia()));
 308     }
 309 
 310     public static void movePointerTo(final int targetX, final int targetY) throws Exception {
 311         final Semaphore released = new Semaphore(0);
 312         EventHandler<TouchEvent> touchHandler = (e) -> released.release();
 313         getStage().addEventHandler(TouchEvent.TOUCH_RELEASED, touchHandler);
 314         final UInput ui = new UInput();
 315         ui.processLine("OPEN");
 316         ui.processLine("VENDOR 0x596");
 317         ui.processLine("PRODUCT 0x502");
 318         ui.processLine("VERSION 1");
 319         ui.processLine("EVBIT EV_SYN");
 320         ui.processLine("EVBIT EV_KEY");
 321         ui.processLine("KEYBIT BTN_TOUCH");
 322         ui.processLine("EVBIT EV_ABS");
 323         ui.processLine("ABSBIT ABS_PRESSURE");
 324         ui.processLine("ABSBIT ABS_X");
 325         ui.processLine("ABSBIT ABS_Y");
 326         ui.processLine("ABSMIN ABS_X 0");
 327         ui.processLine("ABSMAX ABS_X 4095");
 328         ui.processLine("ABSMIN ABS_Y 0");
 329         ui.processLine("ABSMAX ABS_Y 4095");
 330         ui.processLine("ABSMIN ABS_PRESSURE 0");
 331         ui.processLine("ABSMAX ABS_PRESSURE 1");
 332         ui.processLine("PROPBIT INPUT_PROP_POINTER");
 333         ui.processLine("PROPBIT INPUT_PROP_DIRECT");
 334         ui.processLine("PROPERTY ID_INPUT_TOUCHSCREEN 1");
 335         ui.processLine("CREATE");
 336         Rectangle2D r = Screen.getPrimary().getBounds();
 337         int x = (int) ((targetX * 4096.0) / r.getWidth());
 338         int y = (int) ((targetY * 4096.0) / r.getHeight());
 339         ui.processLine("EV_ABS ABS_X " + x);
 340         ui.processLine("EV_ABS ABS_Y " + y);
 341         ui.processLine("EV_ABS ABS_PRESSURE 1");
 342         ui.processLine("EV_KEY BTN_TOUCH 1");
 343         ui.processLine("EV_SYN");
 344         ui.processLine("EV_ABS ABS_X " + x);
 345         ui.processLine("EV_ABS ABS_Y " + y);
 346         ui.processLine("EV_ABS ABS_PRESSURE 0");
 347         ui.processLine("EV_KEY BTN_TOUCH 0");
 348         ui.processLine("EV_SYN");
 349         try {
 350             Assert.assertTrue(released.tryAcquire(3, TimeUnit.SECONDS));
 351             TestRunnable.invokeAndWait(() -> {
 352                 Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot();
 353                 try {
 354                     TestLog.log("x = " + robot.getMouseX());
 355                     TestLog.log("y = " + robot.getMouseY());
 356                     TestLog.log("targetX = " + targetX);
 357                     TestLog.log("targetY = " + targetY);
 358                     Assert.assertEquals(targetX, robot.getMouseX());
 359                     Assert.assertEquals(targetY, robot.getMouseY());
 360                 } finally {
 361                     robot.destroy();
 362                 }
 363             });
 364             frameWait(1);
 365         } finally {
 366             getStage().removeEventHandler(TouchEvent.TOUCH_RELEASED, touchHandler);
 367             ui.processLine("DESTROY");
 368             ui.processLine("CLOSE");
 369             ui.dispose();
 370         }
 371         frameWait(1);
 372     }
 373 
 374     public static int getTapRadius() {
 375         return tapRadius;
 376     }
 377 
 378 
 379     public static boolean isVerbose() {
 380         return verbose;
 381     }
 382 
 383     public static double getTimeScale() {
 384         return timeScale;
 385     }
 386 
 387 
 388     private static void fetchScreenBounds() {
 389         if (Platform.isFxApplicationThread()) {
 390             screen.set(Screen.getPrimary().getBounds());
 391         } else {
 392             CountDownLatch latch = new CountDownLatch(1);
 393             Platform.runLater(() -> {
 394                 screen.set(Screen.getPrimary().getBounds());
 395                 latch.countDown();
 396             });
 397             try {
 398                 latch.await();
 399             } catch (InterruptedException e) {
 400                 e.printStackTrace();
 401             }
 402         }
 403     }
 404 
 405     public static Rectangle2D getScreenBounds() {
 406         Rectangle2D r = screen.get();
 407         if (r == null) {
 408             fetchScreenBounds();
 409             r = screen.get();
 410         }
 411         return r;
 412     }
 413 
 414 }