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