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