/* * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package test.robot.com.sun.glass.ui.monocle; import com.sun.glass.ui.monocle.TestLogShim; import com.sun.glass.ui.monocle.TouchInputShim; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.application.Platform; import javafx.event.EventHandler; import javafx.geometry.Rectangle2D; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.input.TouchEvent; import javafx.scene.input.TouchPoint; import javafx.scene.paint.Color; import javafx.scene.robot.Robot; import javafx.scene.shape.Rectangle; import javafx.stage.Screen; import javafx.stage.Stage; import javafx.stage.StageStyle; import org.junit.Assert; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import test.com.sun.glass.ui.monocle.TestRunnable; public class TestApplication extends Application { private static final boolean verbose = Boolean.getBoolean("verbose"); private static final double timeScale = Double.parseDouble( System.getProperty("timeScale", "1")); private static Stage stage; static final Semaphore ready = new Semaphore(1); private static int tapRadius; private static Group root; private static String glassPlatform; private static boolean isMonocle; private static boolean isLens; private static AtomicReference screen = new AtomicReference<>(); private static void initGlassPlatform() { if (glassPlatform == null) { try { TestRunnable.invokeAndWait( () -> glassPlatform = System.getProperty( "glass.platform")); } catch (Exception e) { e.printStackTrace(); } } isMonocle = "Monocle".equals(glassPlatform); isLens = "Lens".equals(glassPlatform); } public static boolean isMonocle() { initGlassPlatform(); return isMonocle; } public static boolean isLens() { initGlassPlatform(); return isLens; } public void start(Stage stage) throws Exception { TestApplication.stage = stage; stage.initStyle(StageStyle.UNDECORATED); ready.release(); } public static Stage getStage() throws InterruptedException { if (stage == null) { ready.acquire(); UInput.setup(); new Thread(() -> Application.launch(TestApplication.class)).start(); ready.acquire(); Platform.runLater(() -> { if (isMonocle()) { tapRadius = TouchInputShim.getTouchRadius(); } else { tapRadius = Integer.getInteger("lens.input.touch.TapRadius", 20); } ready.release(); }); ready.acquire(); } return stage; } public static Group getRootGroup() { return root; } public static void showScene(Rectangle2D bounds) throws Exception { TestApplication.getStage(); frameWait(2); new TestRunnable() { @Override public void test() throws Exception { Rectangle2D stageBounds = bounds; if (stageBounds == null) { Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds(); stageBounds = new Rectangle2D(0, 0, screenBounds.getWidth(), screenBounds.getHeight()); } stage.setX(stageBounds.getMinX()); stage.setY(stageBounds.getMinY()); stage.setWidth(stageBounds.getWidth()); stage.setHeight(stageBounds.getHeight()); Rectangle r = new Rectangle(stageBounds.getWidth(), stageBounds.getHeight()); r.setFill(Color.BLUE); root = new Group(); root.getChildren().add(r); Scene scene = new Scene(root, stageBounds.getWidth(), stageBounds.getHeight()); stage.setScene(scene); stage.show(); stage.requestFocus(); } }.invokeAndWait(); frameWait(2); } public static void showFullScreenScene() throws Exception { showScene(null); } public static void showInMiddleOfScreen() throws Exception { TestApplication.getStage(); // wait for events to finish being delivered to the previous scene frameWait(2); new TestRunnable() { @Override public void test() throws Exception { Rectangle2D bounds = Screen.getPrimary().getVisualBounds(); stage.setX(bounds.getWidth() / 4); stage.setY(bounds.getHeight() / 4); stage.setWidth(bounds.getWidth() / 2); stage.setHeight(bounds.getHeight() / 2); Rectangle r = new Rectangle(bounds.getWidth() / 2, bounds.getHeight() / 2); r.setFill(Color.BLUE); root = new Group(); root.getChildren().add(r); Scene scene = new Scene(root, bounds.getWidth() / 2, bounds.getHeight() / 2); stage.setScene(scene); stage.show(); stage.requestFocus(); } }.invokeAndWait(); frameWait(2); } public static void waitForNextPulse() throws InterruptedException { frameWait(1); } public static void waitForLayout() throws InterruptedException { frameWait(5); } private static void frameWait(int n) { final CountDownLatch frameCounter = new CountDownLatch(n); Platform.runLater(() -> new AnimationTimer() { @Override public void handle(long now) { frameCounter.countDown(); if (frameCounter.getCount() == 0l) { stop(); } } }.start()); try { frameCounter.await(); } catch (InterruptedException ex) { Assert.fail("Unexpected exception: " + ex); } } public static void addKeyListeners() throws Exception { getStage().getScene().setOnKeyTyped((e) -> TestLogShim.log( "Key typed: " + e.getCharacter())); getStage().getScene().setOnKeyPressed((e) -> TestLogShim.log("Key pressed: " + e.getCode())); getStage().getScene().setOnKeyReleased((e) -> TestLogShim.log("Key released: " + e.getCode())); } public static void addMouseListeners() throws Exception { getStage().getScene().setOnMousePressed((e) -> TestLogShim.log("Mouse pressed: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); getStage().getScene().setOnMouseMoved((e) -> TestLogShim.log("Mouse moved: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); getStage().getScene().setOnMouseDragged((e) -> TestLogShim.log("Mouse dragged: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); getStage().getScene().setOnMouseReleased((e) -> TestLogShim.log("Mouse released: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); getStage().getScene().setOnMouseClicked((e) -> TestLogShim.log("Mouse clicked: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); getStage().getScene().setOnMouseEntered((e) -> TestLogShim.log("Mouse entered: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); getStage().getScene().setOnMouseExited((e) -> TestLogShim.log("Mouse exited: " + (int) e.getScreenX() + ", " + (int) e.getScreenY())); } public static void addTouchListeners() throws Exception { Consumer> logTouchPoints = (tps) -> { TestLogShim.log("Touch points count: [" + tps.size() +"]"); for (TouchPoint tp : tps) { TestLogShim.log("TouchPoint: " + tp.getState() + " " + (int) tp.getScreenX() + ", " + (int) tp.getScreenY() + " id=" + tp.getId()); } }; getStage().getScene().setOnTouchPressed((e) -> { TestLogShim.log("Touch pressed: " + (int) e.getTouchPoint().getScreenX() + ", " + (int) e.getTouchPoint().getScreenY()); logTouchPoints.accept(e.getTouchPoints()); }); getStage().getScene().setOnTouchReleased((e) -> { TestLogShim.log("Touch released: " + (int) e.getTouchPoint().getScreenX() + ", " + (int) e.getTouchPoint().getScreenY()); logTouchPoints.accept(e.getTouchPoints()); }); getStage().getScene().setOnTouchMoved((e) -> { TestLogShim.log("Touch moved: " + (int) e.getTouchPoint().getScreenX() + ", " + (int) e.getTouchPoint().getScreenY()); logTouchPoints.accept(e.getTouchPoints()); }); getStage().getScene().setOnTouchStationary((e) -> { TestLogShim.log("Touch stationary: " + (int) e.getTouchPoint().getScreenX() + ", " + (int) e.getTouchPoint().getScreenY()); logTouchPoints.accept(e.getTouchPoints()); }); } public static void addGestureListeners() throws Exception { //Zoom getStage().getScene().setOnZoom((e) -> TestLogShim.log("Zoom, factor: " + e.getZoomFactor() + ", total factor: " + e.getTotalZoomFactor() + ", inertia value: " + e.isInertia())); getStage().getScene().setOnZoomStarted((e) -> TestLogShim.log("Zoom started, factor: " + e.getZoomFactor() + ", total factor: " + e.getTotalZoomFactor() + ", inertia value: " + e.isInertia())); getStage().getScene().setOnZoomFinished((e) -> TestLogShim.log("Zoom finished, factor: " + e.getZoomFactor() + ", total factor: " + e.getTotalZoomFactor() + ", inertia value: " + e.isInertia())); //Rotate getStage().getScene().setOnRotate((e) -> TestLogShim.log("Rotation, angle: " + Math.round(e.getAngle()) + ", total angle: " + Math.round(e.getTotalAngle()) + ", inertia value: " + e.isInertia())); getStage().getScene().setOnRotationStarted((e) -> TestLogShim.log("Rotation started, angle: " + Math.round(e.getAngle()) + ", total angle: " + Math.round(e.getTotalAngle()) + ", inertia value: " + e.isInertia())); getStage().getScene().setOnRotationFinished((e) -> TestLogShim.log("Rotation finished, angle: " + Math.round(e.getAngle()) + ", total angle: " + Math.round(e.getTotalAngle()) + ", inertia value: " + e.isInertia())); //Scroll getStage().getScene().setOnScroll((e) -> TestLogShim.log("Scroll, DeltaX: " + Math.round(e.getDeltaX()) + ", DeltaY: " + Math.round(e.getDeltaY()) + ", totalDeltaX: " + Math.round(e.getTotalDeltaX()) + ", totalDeltaY: " + Math.round(e.getTotalDeltaY()) + ", touch points: " + e.getTouchCount() + ", inertia value: " + e.isInertia())); getStage().getScene().setOnScrollStarted((e) -> TestLogShim.log("Scroll started, DeltaX: " + Math.round(e.getDeltaX()) + ", DeltaY: " + Math.round(e.getDeltaY()) + ", totalDeltaX: " + Math.round(e.getTotalDeltaX()) + ", totalDeltaY: " + Math.round(e.getTotalDeltaY()) + ", touch points: " + e.getTouchCount() + ", inertia value: " + e.isInertia())); getStage().getScene().setOnScrollFinished((e) -> TestLogShim.log("Scroll finished, DeltaX: " + Math.round(e.getDeltaX()) + ", DeltaY: " + Math.round(e.getDeltaY()) + ", totalDeltaX: " + Math.round(e.getTotalDeltaX()) + ", totalDeltaY: " + Math.round(e.getTotalDeltaY()) + ", touch points: " + e.getTouchCount() + ", inertia value: " + e.isInertia())); } public static void movePointerTo(final int targetX, final int targetY) throws Exception { final Semaphore released = new Semaphore(0); EventHandler touchHandler = (e) -> released.release(); getStage().addEventHandler(TouchEvent.TOUCH_RELEASED, touchHandler); final UInput ui = new UInput(); ui.processLine("OPEN"); ui.processLine("VENDOR 0x596"); ui.processLine("PRODUCT 0x502"); ui.processLine("VERSION 1"); ui.processLine("EVBIT EV_SYN"); ui.processLine("EVBIT EV_KEY"); ui.processLine("KEYBIT BTN_TOUCH"); ui.processLine("EVBIT EV_ABS"); ui.processLine("ABSBIT ABS_PRESSURE"); ui.processLine("ABSBIT ABS_X"); ui.processLine("ABSBIT ABS_Y"); ui.processLine("ABSMIN ABS_X 0"); ui.processLine("ABSMAX ABS_X 4095"); ui.processLine("ABSMIN ABS_Y 0"); ui.processLine("ABSMAX ABS_Y 4095"); ui.processLine("ABSMIN ABS_PRESSURE 0"); ui.processLine("ABSMAX ABS_PRESSURE 1"); ui.processLine("PROPBIT INPUT_PROP_POINTER"); ui.processLine("PROPBIT INPUT_PROP_DIRECT"); ui.processLine("PROPERTY ID_INPUT_TOUCHSCREEN 1"); ui.processLine("CREATE"); Rectangle2D r = Screen.getPrimary().getBounds(); int x = (int) ((targetX * 4096.0) / r.getWidth()); int y = (int) ((targetY * 4096.0) / r.getHeight()); ui.processLine("EV_ABS ABS_X " + x); ui.processLine("EV_ABS ABS_Y " + y); ui.processLine("EV_ABS ABS_PRESSURE 1"); ui.processLine("EV_KEY BTN_TOUCH 1"); ui.processLine("EV_SYN"); ui.processLine("EV_ABS ABS_X " + x); ui.processLine("EV_ABS ABS_Y " + y); ui.processLine("EV_ABS ABS_PRESSURE 0"); ui.processLine("EV_KEY BTN_TOUCH 0"); ui.processLine("EV_SYN"); try { Assert.assertTrue(released.tryAcquire(3, TimeUnit.SECONDS)); TestRunnable.invokeAndWait(() -> { Robot robot = new Robot(); TestLogShim.log("x = " + robot.getMouseX()); TestLogShim.log("y = " + robot.getMouseY()); TestLogShim.log("targetX = " + targetX); TestLogShim.log("targetY = " + targetY); Assert.assertEquals(targetX, (int) robot.getMouseX()); Assert.assertEquals(targetY, (int) robot.getMouseY()); }); frameWait(1); } finally { getStage().removeEventHandler(TouchEvent.TOUCH_RELEASED, touchHandler); ui.processLine("DESTROY"); ui.processLine("CLOSE"); ui.dispose(); } frameWait(1); } public static int getTapRadius() { return tapRadius; } public static boolean isVerbose() { return verbose; } public static double getTimeScale() { return timeScale; } private static void fetchScreenBounds() { if (Platform.isFxApplicationThread()) { screen.set(Screen.getPrimary().getBounds()); } else { CountDownLatch latch = new CountDownLatch(1); Platform.runLater(() -> { screen.set(Screen.getPrimary().getBounds()); latch.countDown(); }); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static Rectangle2D getScreenBounds() { Rectangle2D r = screen.get(); if (r == null) { fetchScreenBounds(); r = screen.get(); } return r; } }