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 }