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 showScene(Rectangle2D bounds) throws Exception {
 120         TestApplication.getStage();
 121         frameWait(2);
 122         new TestRunnable() {
 123             @Override
 124             public void test() throws Exception {
 125                 Rectangle2D stageBounds = bounds;
 126                 if (stageBounds == null) {
 127                     Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
 128                     stageBounds = new Rectangle2D(0, 0, screenBounds.getWidth(), screenBounds.getHeight());
 129                 }
 130                 stage.setX(stageBounds.getMinX());
 131                 stage.setY(stageBounds.getMinY());
 132                 stage.setWidth(stageBounds.getWidth());
 133                 stage.setHeight(stageBounds.getHeight());
 134                 Rectangle r = new Rectangle(stageBounds.getWidth(), stageBounds.getHeight());
 135                 r.setFill(Color.BLUE);
 136                 root = new Group();
 137                 root.getChildren().add(r);
 138                 Scene scene = new Scene(root, stageBounds.getWidth(), stageBounds.getHeight());
 139                 stage.setScene(scene);
 140                 stage.show();
 141                 stage.requestFocus();
 142             }
 143         }.invokeAndWait();
 144         frameWait(2);
 145     }
 146 
 147     public static void showFullScreenScene() throws Exception {
 148         showScene(null);
 149     }
 150 
 151     public static void showInMiddleOfScreen() throws Exception {
 152         TestApplication.getStage();
 153         // wait for events to finish being delivered to the previous scene
 154         frameWait(2);
 155         new TestRunnable() {
 156             @Override
 157             public void test() throws Exception {
 158                 Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
 159                 stage.setX(bounds.getWidth() / 4);
 160                 stage.setY(bounds.getHeight() / 4);
 161                 stage.setWidth(bounds.getWidth() / 2);
 162                 stage.setHeight(bounds.getHeight() / 2);
 163                 Rectangle r = new Rectangle(bounds.getWidth() / 2,
 164                                             bounds.getHeight() / 2);
 165                 r.setFill(Color.BLUE);
 166                 root = new Group();
 167                 root.getChildren().add(r);
 168                 Scene scene = new Scene(root, bounds.getWidth() / 2,
 169                                         bounds.getHeight() / 2);
 170                 stage.setScene(scene);
 171 
 172                 stage.show();
 173                 stage.requestFocus();
 174             }
 175         }.invokeAndWait();
 176         frameWait(2);
 177     }
 178 
 179     public static void waitForNextPulse() throws InterruptedException {
 180         frameWait(1);
 181     }
 182 
 183     public static void waitForLayout() throws InterruptedException {
 184         frameWait(5);
 185     }
 186 
 187     private static void frameWait(int n) {
 188         final CountDownLatch frameCounter = new CountDownLatch(n);
 189         Platform.runLater(() -> new AnimationTimer() {
 190             @Override
 191             public void handle(long now) {
 192                 frameCounter.countDown();
 193                 if (frameCounter.getCount() == 0l) {
 194                     stop();
 195                 }
 196             }
 197         }.start());
 198         try {
 199             frameCounter.await();
 200         } catch (InterruptedException ex) {
 201             Assert.fail("Unexpected exception: " + ex);
 202         }
 203     }
 204 
 205     public static void addKeyListeners() throws Exception {
 206         getStage().getScene().setOnKeyTyped((e) -> TestLog.log(
 207                 "Key typed: " + e.getCharacter()));
 208         getStage().getScene().setOnKeyPressed((e) -> TestLog.log("Key pressed: " + e.getCode()));
 209         getStage().getScene().setOnKeyReleased((e) -> TestLog.log("Key released: " + e.getCode()));
 210     }
 211 
 212     public static void addMouseListeners() throws Exception {
 213         getStage().getScene().setOnMousePressed((e) -> TestLog.log("Mouse pressed: "
 214                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 215         getStage().getScene().setOnMouseMoved((e) -> TestLog.log("Mouse moved: "
 216                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 217         getStage().getScene().setOnMouseDragged((e) -> TestLog.log("Mouse dragged: "
 218                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 219         getStage().getScene().setOnMouseReleased((e) -> TestLog.log("Mouse released: "
 220                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 221         getStage().getScene().setOnMouseClicked((e) -> TestLog.log("Mouse clicked: "
 222                 + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 223         getStage().getScene().setOnMouseEntered((e) -> TestLog.log("Mouse entered: "
 224                             + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 225         getStage().getScene().setOnMouseExited((e) -> TestLog.log("Mouse exited: "
 226                             + (int) e.getScreenX() + ", " + (int) e.getScreenY()));
 227     }
 228 
 229     public static void addTouchListeners() throws Exception {
 230         Consumer<List<TouchPoint>> logTouchPoints = (tps) -> {
 231             TestLog.log("Touch points count: [" + tps.size() +"]"); 
 232             for (TouchPoint tp : tps) {
 233                 TestLog.log("TouchPoint: " + tp.getState() + " "
 234                         + (int) tp.getScreenX() + ", " + (int) tp.getScreenY()
 235                         + " id=" + tp.getId());
 236             }
 237         };
 238         getStage().getScene().setOnTouchPressed((e) -> {
 239             TestLog.log("Touch pressed: "
 240                     + (int) e.getTouchPoint().getScreenX()
 241                     + ", "
 242                     + (int) e.getTouchPoint().getScreenY());
 243             logTouchPoints.accept(e.getTouchPoints());
 244         });
 245         getStage().getScene().setOnTouchReleased((e) -> {
 246             TestLog.log("Touch released: "
 247                     + (int) e.getTouchPoint().getScreenX()
 248                     + ", "
 249                     + (int) e.getTouchPoint().getScreenY());
 250             logTouchPoints.accept(e.getTouchPoints());
 251         });
 252         getStage().getScene().setOnTouchMoved((e) -> {
 253             TestLog.log("Touch moved: "
 254                     + (int) e.getTouchPoint().getScreenX()
 255                     + ", "
 256                     + (int) e.getTouchPoint().getScreenY());
 257             logTouchPoints.accept(e.getTouchPoints());
 258         });
 259         getStage().getScene().setOnTouchStationary((e) -> {
 260             TestLog.log("Touch stationary: "
 261                     + (int) e.getTouchPoint().getScreenX()
 262                     + ", "
 263                     + (int) e.getTouchPoint().getScreenY());
 264             logTouchPoints.accept(e.getTouchPoints());
 265         });
 266     }
 267 
 268     public static void addGestureListeners() throws Exception {
 269         //Zoom
 270         getStage().getScene().setOnZoom((e) -> TestLog.log("Zoom, factor: " + e.getZoomFactor()
 271                 + ", total factor: " + e.getTotalZoomFactor()
 272                 + ", inertia value: " + e.isInertia()));
 273 
 274         getStage().getScene().setOnZoomStarted((e) -> TestLog.log("Zoom started, factor: " + e.getZoomFactor()
 275                 + ", total factor: " + e.getTotalZoomFactor()
 276                 + ", inertia value: " + e.isInertia()));
 277 
 278         getStage().getScene().setOnZoomFinished((e) -> TestLog.log("Zoom finished, factor: " + e.getZoomFactor()
 279                 + ", total factor: " + e.getTotalZoomFactor()
 280                 + ", inertia value: " + e.isInertia()));
 281 
 282         //Rotate
 283         getStage().getScene().setOnRotate((e) -> TestLog.log("Rotation, angle: " + Math.round(e.getAngle())
 284                 + ", total angle: " + Math.round(e.getTotalAngle())
 285                 + ", inertia value: " + e.isInertia()));
 286 
 287         getStage().getScene().setOnRotationStarted((e) -> TestLog.log("Rotation started, angle: " + Math.round(e.getAngle())
 288                 + ", total angle: " + Math.round(e.getTotalAngle())
 289                 + ", inertia value: " + e.isInertia()));
 290 
 291         getStage().getScene().setOnRotationFinished((e) -> TestLog.log("Rotation finished, angle: " + Math.round(e.getAngle())
 292                 + ", total angle: " + Math.round(e.getTotalAngle())
 293                 + ", inertia value: " + e.isInertia()));
 294 
 295         //Scroll
 296         getStage().getScene().setOnScroll((e) -> TestLog.log("Scroll, DeltaX: " + Math.round(e.getDeltaX())
 297                 + ", DeltaY: " + Math.round(e.getDeltaY())
 298                 + ", totalDeltaX: " + Math.round(e.getTotalDeltaX())
 299                 + ", totalDeltaY: " + Math.round(e.getTotalDeltaY())
 300                 + ", touch points: " + e.getTouchCount()
 301                 + ", inertia value: " + e.isInertia()));
 302 
 303         getStage().getScene().setOnScrollStarted((e) -> TestLog.log("Scroll started, DeltaX: " + Math.round(e.getDeltaX())
 304                 + ", DeltaY: " + Math.round(e.getDeltaY())
 305                 + ", totalDeltaX: " + Math.round(e.getTotalDeltaX())
 306                 + ", totalDeltaY: " + Math.round(e.getTotalDeltaY())
 307                 + ", touch points: " + e.getTouchCount()
 308                 + ", inertia value: " + e.isInertia()));
 309 
 310         getStage().getScene().setOnScrollFinished((e) -> TestLog.log("Scroll finished, DeltaX: " + Math.round(e.getDeltaX())
 311                 + ", DeltaY: " + Math.round(e.getDeltaY())
 312                 + ", totalDeltaX: " + Math.round(e.getTotalDeltaX())
 313                 + ", totalDeltaY: " + Math.round(e.getTotalDeltaY())
 314                 + ", touch points: " + e.getTouchCount()
 315                 + ", inertia value: " + e.isInertia()));
 316     }
 317 
 318     public static void movePointerTo(final int targetX, final int targetY) throws Exception {
 319         final Semaphore released = new Semaphore(0);
 320         EventHandler<TouchEvent> touchHandler = (e) -> released.release();
 321         getStage().addEventHandler(TouchEvent.TOUCH_RELEASED, touchHandler);
 322         final UInput ui = new UInput();
 323         ui.processLine("OPEN");
 324         ui.processLine("VENDOR 0x596");
 325         ui.processLine("PRODUCT 0x502");
 326         ui.processLine("VERSION 1");
 327         ui.processLine("EVBIT EV_SYN");
 328         ui.processLine("EVBIT EV_KEY");
 329         ui.processLine("KEYBIT BTN_TOUCH");
 330         ui.processLine("EVBIT EV_ABS");
 331         ui.processLine("ABSBIT ABS_PRESSURE");
 332         ui.processLine("ABSBIT ABS_X");
 333         ui.processLine("ABSBIT ABS_Y");
 334         ui.processLine("ABSMIN ABS_X 0");
 335         ui.processLine("ABSMAX ABS_X 4095");
 336         ui.processLine("ABSMIN ABS_Y 0");
 337         ui.processLine("ABSMAX ABS_Y 4095");
 338         ui.processLine("ABSMIN ABS_PRESSURE 0");
 339         ui.processLine("ABSMAX ABS_PRESSURE 1");
 340         ui.processLine("PROPBIT INPUT_PROP_POINTER");
 341         ui.processLine("PROPBIT INPUT_PROP_DIRECT");
 342         ui.processLine("PROPERTY ID_INPUT_TOUCHSCREEN 1");
 343         ui.processLine("CREATE");
 344         Rectangle2D r = Screen.getPrimary().getBounds();
 345         int x = (int) ((targetX * 4096.0) / r.getWidth());
 346         int y = (int) ((targetY * 4096.0) / r.getHeight());
 347         ui.processLine("EV_ABS ABS_X " + x);
 348         ui.processLine("EV_ABS ABS_Y " + y);
 349         ui.processLine("EV_ABS ABS_PRESSURE 1");
 350         ui.processLine("EV_KEY BTN_TOUCH 1");
 351         ui.processLine("EV_SYN");
 352         ui.processLine("EV_ABS ABS_X " + x);
 353         ui.processLine("EV_ABS ABS_Y " + y);
 354         ui.processLine("EV_ABS ABS_PRESSURE 0");
 355         ui.processLine("EV_KEY BTN_TOUCH 0");
 356         ui.processLine("EV_SYN");
 357         try {
 358             Assert.assertTrue(released.tryAcquire(3, TimeUnit.SECONDS));
 359             TestRunnable.invokeAndWait(() -> {
 360                 Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot();
 361                 try {
 362                     TestLog.log("x = " + robot.getMouseX());
 363                     TestLog.log("y = " + robot.getMouseY());
 364                     TestLog.log("targetX = " + targetX);
 365                     TestLog.log("targetY = " + targetY);
 366                     Assert.assertEquals(targetX, robot.getMouseX());
 367                     Assert.assertEquals(targetY, robot.getMouseY());
 368                 } finally {
 369                     robot.destroy();
 370                 }
 371             });
 372             frameWait(1);
 373         } finally {
 374             getStage().removeEventHandler(TouchEvent.TOUCH_RELEASED, touchHandler);
 375             ui.processLine("DESTROY");
 376             ui.processLine("CLOSE");
 377             ui.dispose();
 378         }
 379         frameWait(1);
 380     }
 381 
 382     public static int getTapRadius() {
 383         return tapRadius;
 384     }
 385 
 386 
 387     public static boolean isVerbose() {
 388         return verbose;
 389     }
 390 
 391     public static double getTimeScale() {
 392         return timeScale;
 393     }
 394 
 395 
 396     private static void fetchScreenBounds() {
 397         if (Platform.isFxApplicationThread()) {
 398             screen.set(Screen.getPrimary().getBounds());
 399         } else {
 400             CountDownLatch latch = new CountDownLatch(1);
 401             Platform.runLater(() -> {
 402                 screen.set(Screen.getPrimary().getBounds());
 403                 latch.countDown();
 404             });
 405             try {
 406                 latch.await();
 407             } catch (InterruptedException e) {
 408                 e.printStackTrace();
 409             }
 410         }
 411     }
 412 
 413     public static Rectangle2D getScreenBounds() {
 414         Rectangle2D r = screen.get();
 415         if (r == null) {
 416             fetchScreenBounds();
 417             r = screen.get();
 418         }
 419         return r;
 420     }
 421 
 422 }