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 }