1 /* 2 * Copyright (c) 2010, 2015, 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.javafx.tk.quantum; 27 28 import com.sun.glass.events.GestureEvent; 29 import com.sun.glass.events.KeyEvent; 30 import com.sun.glass.events.MouseEvent; 31 import com.sun.glass.events.ViewEvent; 32 import com.sun.glass.events.TouchEvent; 33 import com.sun.glass.events.SwipeGesture; 34 import com.sun.glass.ui.Accessible; 35 import com.sun.glass.ui.Clipboard; 36 import com.sun.glass.ui.ClipboardAssistance; 37 import com.sun.glass.ui.View; 38 import com.sun.glass.ui.Window; 39 import com.sun.javafx.PlatformUtil; 40 import com.sun.javafx.collections.TrackableObservableList; 41 import com.sun.javafx.logging.PulseLogger; 42 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED; 43 import com.sun.javafx.scene.input.KeyCodeMap; 44 45 import javafx.collections.ListChangeListener; 46 import javafx.collections.ObservableList; 47 48 import javafx.event.EventType; 49 import javafx.geometry.Point2D; 50 import javafx.scene.input.InputMethodEvent; 51 import javafx.scene.input.InputMethodHighlight; 52 import javafx.scene.input.InputMethodTextRun; 53 import javafx.scene.input.MouseButton; 54 import javafx.scene.input.RotateEvent; 55 import javafx.scene.input.ScrollEvent; 56 import javafx.scene.input.SwipeEvent; 57 import javafx.scene.input.TouchPoint; 58 import javafx.scene.input.TransferMode; 59 import javafx.scene.input.ZoomEvent; 60 61 import java.security.AccessController; 62 import java.security.PrivilegedAction; 63 64 class GlassViewEventHandler extends View.EventHandler { 65 66 static boolean zoomGestureEnabled; 67 static boolean rotateGestureEnabled; 68 static boolean scrollGestureEnabled; 69 static { 70 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 71 zoomGestureEnabled = Boolean.valueOf(System.getProperty("com.sun.javafx.gestures.zoom", "false")); 72 rotateGestureEnabled = Boolean.valueOf(System.getProperty("com.sun.javafx.gestures.rotate", "false")); 73 scrollGestureEnabled = Boolean.valueOf(System.getProperty("com.sun.javafx.gestures.scroll", "false")); 74 return null; 75 }); 76 } 77 78 private ViewScene scene; 79 private final GlassSceneDnDEventHandler dndHandler; 80 private final GestureRecognizers gestures; 81 82 public GlassViewEventHandler(final ViewScene scene) { 83 this.scene = scene; 84 85 dndHandler = new GlassSceneDnDEventHandler(scene); 86 87 gestures = new GestureRecognizers(); 88 if (PlatformUtil.isWindows() || PlatformUtil.isIOS() || PlatformUtil.isEmbedded()) { 89 gestures.add(new SwipeGestureRecognizer(scene)); 90 } 91 if (zoomGestureEnabled) { 92 gestures.add(new ZoomGestureRecognizer(scene)); 93 } 94 if (rotateGestureEnabled) { 95 gestures.add(new RotateGestureRecognizer(scene)); 96 } 97 if (scrollGestureEnabled) { 98 gestures.add(new ScrollGestureRecognizer(scene)); 99 } 100 } 101 102 // Default fullscreen allows limited keyboard input. 103 // It will only receive events from the following keys: 104 // DOWN, UP, LEFT, RIGHT, SPACE, TAB, PAGE_UP, PAGE_DOWN, 105 // HOME, END, ENTER. 106 private static boolean allowableFullScreenKeys(int key) { 107 switch (key) { 108 case KeyEvent.VK_DOWN: 109 case KeyEvent.VK_UP: 110 case KeyEvent.VK_LEFT: 111 case KeyEvent.VK_RIGHT: 112 case KeyEvent.VK_SPACE: 113 case KeyEvent.VK_TAB: 114 case KeyEvent.VK_PAGE_DOWN: 115 case KeyEvent.VK_PAGE_UP: 116 case KeyEvent.VK_HOME: 117 case KeyEvent.VK_END: 118 case KeyEvent.VK_ENTER: 119 return true; 120 } 121 return false; 122 } 123 124 private boolean checkFullScreenKeyEvent(int type, int key, char chars[], int modifiers) { 125 return scene.getWindowStage().isTrustedFullScreen() || allowableFullScreenKeys(key); 126 } 127 128 private final PaintCollector collector = PaintCollector.getInstance(); 129 130 private static EventType<javafx.scene.input.KeyEvent> keyEventType(int glassType) { 131 switch (glassType) { 132 case com.sun.glass.events.KeyEvent.PRESS: 133 return javafx.scene.input.KeyEvent.KEY_PRESSED; 134 case com.sun.glass.events.KeyEvent.RELEASE: 135 return javafx.scene.input.KeyEvent.KEY_RELEASED; 136 case com.sun.glass.events.KeyEvent.TYPED: 137 return javafx.scene.input.KeyEvent.KEY_TYPED; 138 default: 139 if (QuantumToolkit.verbose) { 140 System.err.println("Unknown Glass key event type: " + glassType); 141 } 142 return null; 143 } 144 } 145 146 private final KeyEventNotification keyNotification = new KeyEventNotification(); 147 private class KeyEventNotification implements PrivilegedAction<Void> { 148 View view; 149 long time; 150 int type; 151 int key; 152 char[] chars; 153 int modifiers; 154 155 @Override 156 public Void run() { 157 if (PULSE_LOGGING_ENABLED) { 158 PulseLogger.newInput(keyEventType(type).toString()); 159 } 160 WindowStage stage = scene.getWindowStage(); 161 try { 162 if (stage != null) { 163 stage.setInEventHandler(true); 164 } 165 166 boolean shiftDown = (modifiers & KeyEvent.MODIFIER_SHIFT) != 0; 167 boolean controlDown = (modifiers & KeyEvent.MODIFIER_CONTROL) != 0; 168 boolean altDown = (modifiers & KeyEvent.MODIFIER_ALT) != 0; 169 boolean metaDown = (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0; 170 171 String str = new String(chars); 172 String text = str; // TODO: this must be a text like "HOME", "F1", or "A" 173 174 javafx.scene.input.KeyEvent keyEvent = new javafx.scene.input.KeyEvent( 175 keyEventType(type), 176 str, text, 177 KeyCodeMap.valueOf(key) , 178 shiftDown, controlDown, altDown, metaDown); 179 180 switch (type) { 181 case com.sun.glass.events.KeyEvent.PRESS: 182 if (view.isInFullscreen() && stage != null) { 183 if (stage.getSavedFullScreenExitKey() != null 184 && stage.getSavedFullScreenExitKey().match(keyEvent)) { 185 stage.exitFullScreen(); 186 } 187 } 188 /* NOBREAK */ 189 case com.sun.glass.events.KeyEvent.RELEASE: 190 case com.sun.glass.events.KeyEvent.TYPED: 191 if (view.isInFullscreen()) { 192 if (!checkFullScreenKeyEvent(type, key, chars, modifiers)) { 193 break; 194 } 195 } 196 if (scene.sceneListener != null) { 197 scene.sceneListener.keyEvent(keyEvent); 198 } 199 break; 200 default: 201 if (QuantumToolkit.verbose) { 202 System.out.println("handleKeyEvent: unhandled type: " + type); 203 } 204 } 205 } finally { 206 if (stage != null) { 207 stage.setInEventHandler(false); 208 } 209 if (PULSE_LOGGING_ENABLED) { 210 PulseLogger.newInput(null); 211 } 212 } 213 return null; 214 } 215 } 216 217 @Override public void handleKeyEvent(View view, long time, int type, int key, 218 char[] chars, int modifiers) 219 { 220 keyNotification.view = view; 221 keyNotification.time = time; 222 keyNotification.type = type; 223 keyNotification.key = key; 224 keyNotification.chars = chars; 225 keyNotification.modifiers = modifiers; 226 227 QuantumToolkit.runWithoutRenderLock(() -> { 228 return AccessController.doPrivileged(keyNotification, scene.getAccessControlContext()); 229 }); 230 } 231 232 private static EventType<javafx.scene.input.MouseEvent> mouseEventType(int glassType) { 233 switch (glassType) { 234 case com.sun.glass.events.MouseEvent.DOWN: 235 return javafx.scene.input.MouseEvent.MOUSE_PRESSED; 236 case com.sun.glass.events.MouseEvent.UP: 237 return javafx.scene.input.MouseEvent.MOUSE_RELEASED; 238 case com.sun.glass.events.MouseEvent.ENTER: 239 return javafx.scene.input.MouseEvent.MOUSE_ENTERED; 240 case com.sun.glass.events.MouseEvent.EXIT: 241 return javafx.scene.input.MouseEvent.MOUSE_EXITED; 242 case com.sun.glass.events.MouseEvent.MOVE: 243 return javafx.scene.input.MouseEvent.MOUSE_MOVED; 244 case com.sun.glass.events.MouseEvent.DRAG: 245 return javafx.scene.input.MouseEvent.MOUSE_DRAGGED; 246 case com.sun.glass.events.MouseEvent.WHEEL: 247 throw new IllegalArgumentException("WHEEL event cannot " 248 + "be translated to MouseEvent, must be translated to " 249 + "ScrollEvent"); 250 default: 251 if (QuantumToolkit.verbose) { 252 System.err.println("Unknown Glass mouse event type: " + glassType); 253 } 254 return null; 255 } 256 } 257 258 private static MouseButton mouseEventButton(int glassButton) { 259 switch (glassButton) { 260 case com.sun.glass.events.MouseEvent.BUTTON_LEFT: 261 return MouseButton.PRIMARY; 262 case com.sun.glass.events.MouseEvent.BUTTON_RIGHT: 263 return MouseButton.SECONDARY; 264 case com.sun.glass.events.MouseEvent.BUTTON_OTHER: 265 return MouseButton.MIDDLE; 266 default: 267 return MouseButton.NONE; 268 } 269 } 270 271 // Used to determine whether a DRAG operation has been initiated on this window 272 private int mouseButtonPressedMask = 0; 273 274 private final MouseEventNotification mouseNotification = new MouseEventNotification(); 275 private class MouseEventNotification implements PrivilegedAction<Void> { 276 View view; 277 long time; 278 int type; 279 int button; 280 int x, y, xAbs, yAbs; 281 int modifiers; 282 boolean isPopupTrigger; 283 boolean isSynthesized; 284 285 @Override 286 public Void run() { 287 if (PULSE_LOGGING_ENABLED) { 288 PulseLogger.newInput(mouseEventType(type).toString()); 289 } 290 291 int buttonMask; 292 switch (button) { 293 case MouseEvent.BUTTON_LEFT: 294 buttonMask = KeyEvent.MODIFIER_BUTTON_PRIMARY; 295 break; 296 case MouseEvent.BUTTON_OTHER: 297 buttonMask = KeyEvent.MODIFIER_BUTTON_MIDDLE; 298 break; 299 case MouseEvent.BUTTON_RIGHT: 300 buttonMask = KeyEvent.MODIFIER_BUTTON_SECONDARY; 301 break; 302 default: 303 buttonMask = 0; 304 break; 305 } 306 307 switch (type) { 308 case MouseEvent.MOVE: 309 if (button != MouseEvent.BUTTON_NONE) { 310 //RT-11305: the drag hasn't been started on this window -- ignore the event 311 return null; 312 } 313 break; 314 case MouseEvent.UP: 315 if ((mouseButtonPressedMask & buttonMask) == 0) { 316 //RT-11305: the mouse button hasn't been pressed on this window -- ignore the event 317 return null; 318 } 319 mouseButtonPressedMask &= ~buttonMask; 320 break; 321 case MouseEvent.DOWN: 322 mouseButtonPressedMask |= buttonMask; 323 break; 324 case MouseEvent.ENTER: 325 case MouseEvent.EXIT: 326 break; 327 case MouseEvent.CLICK: 328 // Don't send click events to FX, as they are generated in Scene 329 return null; 330 default: 331 if (QuantumToolkit.verbose) { 332 System.out.println("handleMouseEvent: unhandled type: " + type); 333 } 334 } 335 336 WindowStage stage = scene.getWindowStage(); 337 try { 338 if (stage != null) { 339 stage.setInEventHandler(true); 340 } 341 if (scene.sceneListener != null) { 342 boolean shiftDown = (modifiers & KeyEvent.MODIFIER_SHIFT) != 0; 343 boolean controlDown = (modifiers & KeyEvent.MODIFIER_CONTROL) != 0; 344 boolean altDown = (modifiers & KeyEvent.MODIFIER_ALT) != 0; 345 boolean metaDown = (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0; 346 boolean primaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_PRIMARY) != 0; 347 boolean middleButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_MIDDLE) != 0; 348 boolean secondaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_SECONDARY) != 0; 349 Window w = view.getWindow(); 350 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 351 352 scene.sceneListener.mouseEvent(mouseEventType(type), 353 x / pScale, y / pScale, xAbs / pScale, yAbs / pScale, 354 mouseEventButton(button), isPopupTrigger, isSynthesized, 355 shiftDown, controlDown, altDown, metaDown, 356 primaryButtonDown, middleButtonDown, secondaryButtonDown); 357 } 358 } finally { 359 if (stage != null) { 360 stage.setInEventHandler(false); 361 } 362 if (PULSE_LOGGING_ENABLED) { 363 PulseLogger.newInput(null); 364 } 365 } 366 return null; 367 } 368 } 369 370 @Override 371 public void handleMouseEvent(View view, long time, int type, int button, 372 int x, int y, int xAbs, int yAbs, 373 int modifiers, boolean isPopupTrigger, boolean isSynthesized) 374 { 375 mouseNotification.view = view; 376 mouseNotification.time = time; 377 mouseNotification.type = type; 378 mouseNotification.button = button; 379 mouseNotification.x = x; 380 mouseNotification.y = y; 381 mouseNotification.xAbs = xAbs; 382 mouseNotification.yAbs = yAbs; 383 mouseNotification.modifiers = modifiers; 384 mouseNotification.isPopupTrigger = isPopupTrigger; 385 mouseNotification.isSynthesized = isSynthesized; 386 387 QuantumToolkit.runWithoutRenderLock(() -> { 388 return AccessController.doPrivileged(mouseNotification, scene.getAccessControlContext()); 389 }); 390 } 391 392 @Override public void handleMenuEvent(final View view, 393 final int x, final int y, final int xAbs, final int yAbs, 394 final boolean isKeyboardTrigger) 395 { 396 if (PULSE_LOGGING_ENABLED) { 397 PulseLogger.newInput("MENU_EVENT"); 398 } 399 WindowStage stage = scene.getWindowStage(); 400 try { 401 if (stage != null) { 402 stage.setInEventHandler(true); 403 } 404 QuantumToolkit.runWithoutRenderLock(() -> { 405 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 406 if (scene.sceneListener != null) { 407 Window w = view.getWindow(); 408 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 409 scene.sceneListener.menuEvent(x / pScale, y / pScale, 410 xAbs / pScale, yAbs / pScale, 411 isKeyboardTrigger); 412 } 413 return null; 414 }, scene.getAccessControlContext()); 415 }); 416 } finally { 417 if (stage != null) { 418 stage.setInEventHandler(false); 419 } 420 if (PULSE_LOGGING_ENABLED) { 421 PulseLogger.newInput(null); 422 } 423 } 424 } 425 426 @Override public void handleScrollEvent(final View view, final long time, 427 final int x, final int y, final int xAbs, final int yAbs, 428 final double deltaX, final double deltaY, final int modifiers, 429 final int lines, final int chars, 430 final int defaultLines, final int defaultChars, 431 final double xMultiplier, final double yMultiplier) 432 { 433 if (PULSE_LOGGING_ENABLED) { 434 PulseLogger.newInput("SCROLL_EVENT"); 435 } 436 WindowStage stage = scene.getWindowStage(); 437 try { 438 if (stage != null) { 439 stage.setInEventHandler(true); 440 } 441 QuantumToolkit.runWithoutRenderLock(() -> { 442 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 443 if (scene.sceneListener != null) { 444 Window w = view.getWindow(); 445 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 446 scene.sceneListener.scrollEvent(ScrollEvent.SCROLL, 447 deltaX / pScale, deltaY / pScale, 0, 0, 448 xMultiplier, yMultiplier, 449 0, // touchCount 450 chars, lines, defaultChars, defaultLines, 451 x / pScale, y / pScale, xAbs / pScale, yAbs / pScale, 452 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 453 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 454 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 455 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 456 false, // this is always indirect 457 false); // this has no inertia 458 } 459 return null; 460 }, scene.getAccessControlContext()); 461 }); 462 } finally { 463 if (stage != null) { 464 stage.setInEventHandler(false); 465 } 466 if (PULSE_LOGGING_ENABLED) { 467 PulseLogger.newInput(null); 468 } 469 } 470 } 471 472 private static byte inputMethodEventAttrValue(int pos, int[] attrBoundary, byte[] attrValue) { 473 if (attrBoundary != null) { 474 for (int current = 0; current < attrBoundary.length-1; current++) { 475 if (pos >= attrBoundary[current] && 476 pos < attrBoundary[current+1]) { 477 return attrValue[current]; 478 } 479 } 480 } 481 return View.IME_ATTR_INPUT_ERROR; 482 } 483 484 private static ObservableList<InputMethodTextRun> inputMethodEventComposed( 485 String text, int commitCount, int[] clauseBoundary, int[] attrBoundary, byte[] attrValue) 486 { 487 ObservableList<InputMethodTextRun> composed = new TrackableObservableList<InputMethodTextRun>() { 488 @Override 489 protected void onChanged(ListChangeListener.Change<InputMethodTextRun> c) { 490 } 491 }; 492 493 if (commitCount < text.length()) { 494 if (clauseBoundary == null) { 495 // Create one single segment as UNSELECTED_RAW 496 composed.add(new InputMethodTextRun( 497 text.substring(commitCount), 498 InputMethodHighlight.UNSELECTED_RAW)); 499 } else { 500 for (int current = 0; current < clauseBoundary.length-1; current++) { 501 if (clauseBoundary[current] < commitCount) { 502 continue; 503 } 504 505 InputMethodHighlight highlight; 506 switch (inputMethodEventAttrValue(clauseBoundary[current], attrBoundary, attrValue)) { 507 case View.IME_ATTR_TARGET_CONVERTED: 508 highlight = InputMethodHighlight.SELECTED_CONVERTED; 509 break; 510 case View.IME_ATTR_CONVERTED: 511 highlight = InputMethodHighlight.UNSELECTED_CONVERTED; 512 break; 513 case View.IME_ATTR_TARGET_NOTCONVERTED: 514 highlight = InputMethodHighlight.SELECTED_RAW; 515 break; 516 case View.IME_ATTR_INPUT: 517 case View.IME_ATTR_INPUT_ERROR: 518 default: 519 highlight = InputMethodHighlight.UNSELECTED_RAW; 520 break; 521 } 522 composed.add(new InputMethodTextRun( 523 text.substring(clauseBoundary[current], 524 clauseBoundary[current+1]), 525 highlight)); 526 } 527 } 528 } 529 return composed; 530 } 531 532 @Override public void handleInputMethodEvent(final long time, final String text, 533 final int[] clauseBoundary, 534 final int[] attrBoundary, final byte[] attrValue, 535 final int commitCount, final int cursorPos) 536 { 537 if (PULSE_LOGGING_ENABLED) { 538 PulseLogger.newInput("INPUT_METHOD_EVENT"); 539 } 540 WindowStage stage = scene.getWindowStage(); 541 try { 542 if (stage != null) { 543 stage.setInEventHandler(true); 544 } 545 QuantumToolkit.runWithoutRenderLock(() -> { 546 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 547 if (scene.sceneListener != null) { 548 String t = text != null ? text : ""; 549 EventType<InputMethodEvent> eventType = 550 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED; 551 ObservableList<InputMethodTextRun> composed = inputMethodEventComposed( 552 t, commitCount, clauseBoundary, attrBoundary, attrValue); 553 String committed = t.substring(0, commitCount); 554 scene.sceneListener.inputMethodEvent(eventType, composed, committed, cursorPos); 555 } 556 return null; 557 }, scene.getAccessControlContext()); 558 }); 559 } finally { 560 if (stage != null) { 561 stage.setInEventHandler(false); 562 } 563 if (PULSE_LOGGING_ENABLED) { 564 PulseLogger.newInput(null); 565 } 566 } 567 } 568 569 @Override 570 public double[] getInputMethodCandidatePos(int offset) { 571 Point2D p2d = scene.inputMethodRequests.getTextLocation(offset); 572 double[] ret = new double[2]; 573 ret[0] = p2d.getX(); 574 ret[1] = p2d.getY(); 575 return ret; 576 } 577 578 private static TransferMode actionToTransferMode(int dropActions) { 579 if (dropActions == Clipboard.ACTION_NONE) { 580 return null; 581 } else if (dropActions == Clipboard.ACTION_COPY 582 //IE drop action for URL copy; XXX: should be fixed in Glass 583 || dropActions == (Clipboard.ACTION_COPY | Clipboard.ACTION_REFERENCE) ) 584 { 585 return TransferMode.COPY; 586 } else if (dropActions == Clipboard.ACTION_MOVE 587 //IE drop action for URL move; XXX: should be fixed in Glass 588 || dropActions == (Clipboard.ACTION_MOVE | Clipboard.ACTION_REFERENCE) ) 589 { 590 return TransferMode.MOVE; 591 } else if (dropActions == Clipboard.ACTION_REFERENCE) { 592 return TransferMode.LINK; 593 } else if (dropActions == Clipboard.ACTION_COPY_OR_MOVE) { 594 if (QuantumToolkit.verbose) { 595 System.err.println("Ambiguous drop action: " + Integer.toHexString(dropActions)); 596 } 597 } else { 598 if (QuantumToolkit.verbose) { 599 System.err.println("Unknown drop action: " + Integer.toHexString(dropActions)); 600 } 601 } 602 return null; 603 } 604 605 private static int transferModeToAction(TransferMode tm) { 606 if (tm == null) { 607 return Clipboard.ACTION_NONE; 608 } 609 610 switch (tm) { 611 case COPY: 612 return Clipboard.ACTION_COPY; 613 case MOVE: 614 return Clipboard.ACTION_MOVE; 615 case LINK: 616 return Clipboard.ACTION_REFERENCE; 617 default: 618 return Clipboard.ACTION_NONE; 619 } 620 } 621 622 @Override public int handleDragEnter(View view, 623 final int x, final int y, final int xAbs, final int yAbs, 624 final int recommendedDropAction, 625 final ClipboardAssistance dropTargetAssistant) 626 { 627 if (PULSE_LOGGING_ENABLED) { 628 PulseLogger.newInput("DRAG_ENTER"); 629 } 630 TransferMode action; 631 try { 632 action = QuantumToolkit.runWithoutRenderLock(() -> { 633 return dndHandler.handleDragEnter(x, y, xAbs, yAbs, 634 actionToTransferMode(recommendedDropAction), 635 dropTargetAssistant); 636 }); 637 } finally { 638 if (PULSE_LOGGING_ENABLED) { 639 PulseLogger.newInput(null); 640 } 641 } 642 return transferModeToAction(action); 643 } 644 645 @Override public void handleDragLeave(View view, final ClipboardAssistance dropTargetAssistant) { 646 if (PULSE_LOGGING_ENABLED) { 647 PulseLogger.newInput("DRAG_LEAVE"); 648 } 649 try { 650 QuantumToolkit.runWithoutRenderLock(() -> { 651 dndHandler.handleDragLeave(dropTargetAssistant); 652 return null; 653 }); 654 } finally { 655 if (PULSE_LOGGING_ENABLED) { 656 PulseLogger.newInput(null); 657 } 658 } 659 } 660 661 @Override public int handleDragDrop(View view, 662 final int x, final int y, final int xAbs, final int yAbs, 663 final int recommendedDropAction, 664 final ClipboardAssistance dropTargetAssistant) 665 { 666 if (PULSE_LOGGING_ENABLED) { 667 PulseLogger.newInput("DRAG_DROP"); 668 } 669 TransferMode action; 670 try { 671 action = QuantumToolkit.runWithoutRenderLock(() -> { 672 return dndHandler.handleDragDrop(x, y, xAbs, yAbs, 673 actionToTransferMode(recommendedDropAction), 674 dropTargetAssistant); 675 }); 676 } finally { 677 if (PULSE_LOGGING_ENABLED) { 678 PulseLogger.newInput(null); 679 } 680 } 681 return transferModeToAction(action); 682 } 683 684 @Override public int handleDragOver(View view, 685 final int x, final int y, final int xAbs, final int yAbs, 686 final int recommendedDropAction, 687 final ClipboardAssistance dropTargetAssistant) 688 { 689 if (PULSE_LOGGING_ENABLED) { 690 PulseLogger.newInput("DRAG_OVER"); 691 } 692 TransferMode action; 693 try { 694 action = QuantumToolkit.runWithoutRenderLock(() -> { 695 return dndHandler.handleDragOver(x, y, xAbs, yAbs, 696 actionToTransferMode(recommendedDropAction), 697 dropTargetAssistant); 698 }); 699 } finally { 700 if (PULSE_LOGGING_ENABLED) { 701 PulseLogger.newInput(null); 702 } 703 } 704 return transferModeToAction(action); 705 } 706 707 private ClipboardAssistance dropSourceAssistant; 708 709 @Override public void handleDragStart(View view, final int button, 710 final int x, final int y, final int xAbs, final int yAbs, 711 final ClipboardAssistance assistant) 712 { 713 if (PULSE_LOGGING_ENABLED) { 714 PulseLogger.newInput("DRAG_START"); 715 } 716 dropSourceAssistant = assistant; 717 try { 718 QuantumToolkit.runWithoutRenderLock(() -> { 719 dndHandler.handleDragStart(button, x, y, xAbs, yAbs, assistant); 720 return null; 721 }); 722 } finally { 723 if (PULSE_LOGGING_ENABLED) { 724 PulseLogger.newInput(null); 725 } 726 } 727 } 728 729 @Override public void handleDragEnd(View view, final int performedAction) { 730 if (PULSE_LOGGING_ENABLED) { 731 PulseLogger.newInput("DRAG_END"); 732 } 733 try { 734 QuantumToolkit.runWithoutRenderLock(() -> { 735 dndHandler.handleDragEnd(actionToTransferMode(performedAction), dropSourceAssistant); 736 return null; 737 }); 738 } finally { 739 if (PULSE_LOGGING_ENABLED) { 740 PulseLogger.newInput(null); 741 } 742 } 743 } 744 745 // TODO - dropTargetListener.dropActionChanged 746 // TODO - dragSourceListener.dropActionChanged 747 748 private final ViewEventNotification viewNotification = new ViewEventNotification(); 749 private class ViewEventNotification implements PrivilegedAction<Void> { 750 View view; 751 long time; 752 int type; 753 754 @Override 755 public Void run() { 756 if (scene.sceneListener == null) { 757 return null; 758 } 759 switch (type) { 760 case ViewEvent.REPAINT: { 761 Window w = view.getWindow(); 762 if (w != null && w.getMinimumWidth() == view.getWidth() && !w.isVisible()) { 763 // RT-21057 - ignore initial minimum size setting if not visible 764 break; 765 } 766 if (QuantumToolkit.drawInPaint && w != null && w.isVisible()) { 767 WindowStage stage = scene.getWindowStage(); 768 if (stage != null && !stage.isApplet()) { 769 collector.liveRepaintRenderJob(scene); 770 } 771 } 772 scene.entireSceneNeedsRepaint(); 773 break; 774 } 775 case ViewEvent.RESIZE: { 776 Window w = view.getWindow(); 777 float pScale = (w == null) ? 1.0f : w.getPlatformScale(); 778 scene.sceneListener.changedSize(view.getWidth() / pScale, 779 view.getHeight() / pScale); 780 scene.entireSceneNeedsRepaint(); 781 QuantumToolkit.runWithRenderLock(() -> { 782 scene.updateSceneState(); 783 return null; 784 }); 785 if (QuantumToolkit.liveResize && w != null && w.isVisible()) { 786 WindowStage stage = scene.getWindowStage(); 787 if (stage != null && !stage.isApplet()) { 788 collector.liveRepaintRenderJob(scene); 789 } 790 } 791 break; 792 } 793 case ViewEvent.MOVE: { 794 // MOVE events can be "synthesized" and the window will 795 // be null if this is synthesized during a "REMOVE" event 796 Window w = view.getWindow(); 797 float pScale = (w == null) ? 1.0f : w.getPlatformScale(); 798 scene.sceneListener.changedLocation(view.getX() / pScale, 799 view.getY() / pScale); 800 break; 801 } 802 case ViewEvent.FULLSCREEN_ENTER: 803 case ViewEvent.FULLSCREEN_EXIT: 804 if (scene.getWindowStage() != null) { 805 scene.getWindowStage().fullscreenChanged(type == ViewEvent.FULLSCREEN_ENTER); 806 } 807 break; 808 case ViewEvent.ADD: 809 case ViewEvent.REMOVE: 810 // unhandled 811 break; 812 default: 813 throw new RuntimeException("handleViewEvent: unhandled type: " + type); 814 } 815 return null; 816 } 817 } 818 819 @Override public void handleViewEvent(View view, long time, final int type) { 820 if (PULSE_LOGGING_ENABLED) { 821 PulseLogger.newInput("VIEW_EVENT: "+ViewEvent.getTypeString(type)); 822 } 823 viewNotification.view = view; 824 viewNotification.time = time; 825 viewNotification.type = type; 826 try { 827 QuantumToolkit.runWithoutRenderLock(() -> { 828 return AccessController.doPrivileged(viewNotification, scene.getAccessControlContext()); 829 }); 830 } 831 finally { 832 if (PULSE_LOGGING_ENABLED) { 833 PulseLogger.newInput(null); 834 } 835 } 836 } 837 838 @Override public void handleScrollGestureEvent( 839 View view, final long time, final int type, 840 final int modifiers, final boolean isDirect, final boolean isInertia, final int touchCount, 841 final int x, final int y, final int xAbs, final int yAbs, final double dx, final double dy, 842 final double totaldx, final double totaldy, final double multiplierX, final double multiplierY) 843 { 844 if (PULSE_LOGGING_ENABLED) { 845 PulseLogger.newInput("SCROLL_GESTURE_EVENT"); 846 } 847 WindowStage stage = scene.getWindowStage(); 848 try { 849 if (stage != null) { 850 stage.setInEventHandler(true); 851 } 852 QuantumToolkit.runWithoutRenderLock(() -> { 853 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 854 if (scene.sceneListener != null) { 855 EventType<ScrollEvent> eventType; 856 switch(type) { 857 case GestureEvent.GESTURE_STARTED: 858 eventType = ScrollEvent.SCROLL_STARTED; 859 break; 860 case GestureEvent.GESTURE_PERFORMED: 861 eventType = ScrollEvent.SCROLL; 862 break; 863 case GestureEvent.GESTURE_FINISHED: 864 eventType = ScrollEvent.SCROLL_FINISHED; 865 break; 866 default: 867 throw new RuntimeException("Unknown scroll event type: " + type); 868 } 869 Window w = view.getWindow(); 870 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 871 scene.sceneListener.scrollEvent(eventType, 872 dx / pScale, dy / pScale, totaldx / pScale, totaldy / pScale, 873 multiplierX, multiplierY, 874 touchCount, 875 0, 0, 0, 0, 876 x == View.GESTURE_NO_VALUE ? Double.NaN : x / pScale, 877 y == View.GESTURE_NO_VALUE ? Double.NaN : y / pScale, 878 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs / pScale, 879 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs / pScale, 880 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 881 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 882 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 883 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 884 isDirect, isInertia); 885 } 886 return null; 887 }, scene.getAccessControlContext()); 888 }); 889 } finally { 890 if (stage != null) { 891 stage.setInEventHandler(false); 892 } 893 if (PULSE_LOGGING_ENABLED) { 894 PulseLogger.newInput(null); 895 } 896 } 897 } 898 899 @Override public void handleZoomGestureEvent( 900 View view, final long time, final int type, 901 final int modifiers, final boolean isDirect, final boolean isInertia, 902 final int originx, final int originy, 903 final int originxAbs, final int originyAbs, 904 final double scale, double expansion, 905 final double totalscale, double totalexpansion) 906 { 907 if (PULSE_LOGGING_ENABLED) { 908 PulseLogger.newInput("ZOOM_GESTURE_EVENT"); 909 } 910 WindowStage stage = scene.getWindowStage(); 911 try { 912 if (stage != null) { 913 stage.setInEventHandler(true); 914 } 915 QuantumToolkit.runWithoutRenderLock(() -> { 916 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 917 if (scene.sceneListener != null) { 918 EventType<ZoomEvent> eventType; 919 switch (type) { 920 case GestureEvent.GESTURE_STARTED: 921 eventType = ZoomEvent.ZOOM_STARTED; 922 break; 923 case GestureEvent.GESTURE_PERFORMED: 924 eventType = ZoomEvent.ZOOM; 925 break; 926 case GestureEvent.GESTURE_FINISHED: 927 eventType = ZoomEvent.ZOOM_FINISHED; 928 break; 929 default: 930 throw new RuntimeException("Unknown scroll event type: " + type); 931 } 932 Window w = view.getWindow(); 933 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 934 // REMIND: Scale the [total]scale params too? 935 scene.sceneListener.zoomEvent(eventType, scale, totalscale, 936 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx / pScale, 937 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy / pScale, 938 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs / pScale, 939 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs / pScale, 940 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 941 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 942 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 943 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 944 isDirect, isInertia); 945 } 946 return null; 947 }, scene.getAccessControlContext()); 948 }); 949 } finally { 950 if (stage != null) { 951 stage.setInEventHandler(false); 952 } 953 if (PULSE_LOGGING_ENABLED) { 954 PulseLogger.newInput(null); 955 } 956 } 957 } 958 959 @Override public void handleRotateGestureEvent( 960 View view, final long time, final int type, 961 final int modifiers, final boolean isDirect, final boolean isInertia, 962 final int originx, final int originy, 963 final int originxAbs, final int originyAbs, 964 final double dangle, final double totalangle) 965 { 966 if (PULSE_LOGGING_ENABLED) { 967 PulseLogger.newInput("ROTATE_GESTURE_EVENT"); 968 } 969 WindowStage stage = scene.getWindowStage(); 970 try { 971 if (stage != null) { 972 stage.setInEventHandler(true); 973 } 974 QuantumToolkit.runWithoutRenderLock(() -> { 975 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 976 if (scene.sceneListener != null) { 977 EventType<RotateEvent> eventType; 978 switch (type) { 979 case GestureEvent.GESTURE_STARTED: 980 eventType = RotateEvent.ROTATION_STARTED; 981 break; 982 case GestureEvent.GESTURE_PERFORMED: 983 eventType = RotateEvent.ROTATE; 984 break; 985 case GestureEvent.GESTURE_FINISHED: 986 eventType = RotateEvent.ROTATION_FINISHED; 987 break; 988 default: 989 throw new RuntimeException("Unknown scroll event type: " + type); 990 } 991 Window w = view.getWindow(); 992 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 993 scene.sceneListener.rotateEvent(eventType, dangle, totalangle, 994 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx / pScale, 995 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy / pScale, 996 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs / pScale, 997 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs / pScale, 998 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 999 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1000 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1001 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 1002 isDirect, isInertia); 1003 } 1004 return null; 1005 }, scene.getAccessControlContext()); 1006 }); 1007 } finally { 1008 if (stage != null) { 1009 stage.setInEventHandler(false); 1010 } 1011 if (PULSE_LOGGING_ENABLED) { 1012 PulseLogger.newInput(null); 1013 } 1014 } 1015 } 1016 1017 @Override public void handleSwipeGestureEvent( 1018 View view, final long time, int type, 1019 final int modifiers, final boolean isDirect, 1020 boolean isInertia, final int touchCount, 1021 final int dir, final int x, final int y, final int xAbs, final int yAbs) 1022 { 1023 if (PULSE_LOGGING_ENABLED) { 1024 PulseLogger.newInput("SWIPE_EVENT"); 1025 } 1026 WindowStage stage = scene.getWindowStage(); 1027 try { 1028 if (stage != null) { 1029 stage.setInEventHandler(true); 1030 } 1031 QuantumToolkit.runWithoutRenderLock(() -> { 1032 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1033 if (scene.sceneListener != null) { 1034 EventType<SwipeEvent> eventType; 1035 switch (dir) { 1036 case SwipeGesture.DIR_UP: 1037 eventType = SwipeEvent.SWIPE_UP; 1038 break; 1039 case SwipeGesture.DIR_DOWN: 1040 eventType = SwipeEvent.SWIPE_DOWN; 1041 break; 1042 case SwipeGesture.DIR_LEFT: 1043 eventType = SwipeEvent.SWIPE_LEFT; 1044 break; 1045 case SwipeGesture.DIR_RIGHT: 1046 eventType = SwipeEvent.SWIPE_RIGHT; 1047 break; 1048 default: 1049 throw new RuntimeException("Unknown swipe event direction: " + dir); 1050 } 1051 Window w = view.getWindow(); 1052 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 1053 scene.sceneListener.swipeEvent(eventType, touchCount, 1054 x == View.GESTURE_NO_VALUE ? Double.NaN : x / pScale, 1055 y == View.GESTURE_NO_VALUE ? Double.NaN : y / pScale, 1056 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs / pScale, 1057 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs / pScale, 1058 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1059 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1060 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1061 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 1062 isDirect); 1063 } 1064 return null; 1065 }, scene.getAccessControlContext()); 1066 }); 1067 } finally { 1068 if (stage != null) { 1069 stage.setInEventHandler(false); 1070 } 1071 if (PULSE_LOGGING_ENABLED) { 1072 PulseLogger.newInput(null); 1073 } 1074 } 1075 } 1076 1077 @Override public void handleBeginTouchEvent( 1078 View view, final long time, final int modifiers, 1079 final boolean isDirect, final int touchEventCount) 1080 { 1081 if (PULSE_LOGGING_ENABLED) { 1082 PulseLogger.newInput("BEGIN_TOUCH_EVENT"); 1083 } 1084 WindowStage stage = scene.getWindowStage(); 1085 try { 1086 if (stage != null) { 1087 stage.setInEventHandler(true); 1088 } 1089 QuantumToolkit.runWithoutRenderLock(() -> { 1090 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1091 if (scene.sceneListener != null) { 1092 scene.sceneListener.touchEventBegin(time, touchEventCount, 1093 isDirect, 1094 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1095 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1096 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1097 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0); 1098 } 1099 return null; 1100 }, scene.getAccessControlContext()); 1101 }); 1102 } finally { 1103 if (stage != null) { 1104 stage.setInEventHandler(false); 1105 } 1106 if (PULSE_LOGGING_ENABLED) { 1107 PulseLogger.newInput(null); 1108 } 1109 } 1110 1111 gestures.notifyBeginTouchEvent(time, modifiers, isDirect, touchEventCount); 1112 } 1113 1114 @Override public void handleNextTouchEvent( 1115 View view, final long time, final int type, final long touchId, 1116 final int x, final int y, final int xAbs, final int yAbs) 1117 { 1118 if (PULSE_LOGGING_ENABLED) { 1119 PulseLogger.newInput("NEXT_TOUCH_EVENT"); 1120 } 1121 WindowStage stage = scene.getWindowStage(); 1122 try { 1123 if (stage != null) { 1124 stage.setInEventHandler(true); 1125 } 1126 QuantumToolkit.runWithoutRenderLock(() -> { 1127 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1128 if (scene.sceneListener != null) { 1129 TouchPoint.State state; 1130 switch (type) { 1131 case TouchEvent.TOUCH_PRESSED: 1132 state = TouchPoint.State.PRESSED; 1133 break; 1134 case TouchEvent.TOUCH_MOVED: 1135 state = TouchPoint.State.MOVED; 1136 break; 1137 case TouchEvent.TOUCH_STILL: 1138 state = TouchPoint.State.STATIONARY; 1139 break; 1140 case TouchEvent.TOUCH_RELEASED: 1141 state = TouchPoint.State.RELEASED; 1142 break; 1143 default: 1144 throw new RuntimeException("Unknown touch state: " + type); 1145 } 1146 Window w = view.getWindow(); 1147 double pScale = (w == null) ? 1.0 : w.getPlatformScale(); 1148 scene.sceneListener.touchEventNext(state, touchId, 1149 x / pScale, y / pScale, xAbs / pScale, yAbs / pScale); 1150 } 1151 return null; 1152 }, scene.getAccessControlContext()); 1153 }); 1154 } finally { 1155 if (stage != null) { 1156 stage.setInEventHandler(false); 1157 } 1158 if (PULSE_LOGGING_ENABLED) { 1159 PulseLogger.newInput(null); 1160 } 1161 } 1162 1163 gestures.notifyNextTouchEvent(time, type, touchId, x, y, xAbs, yAbs); 1164 } 1165 1166 @Override public void handleEndTouchEvent(View view, long time) { 1167 if (PULSE_LOGGING_ENABLED) { 1168 PulseLogger.newInput("END_TOUCH_EVENT"); 1169 } 1170 WindowStage stage = scene.getWindowStage(); 1171 try { 1172 if (stage != null) { 1173 stage.setInEventHandler(true); 1174 } 1175 QuantumToolkit.runWithoutRenderLock(() -> { 1176 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1177 if (scene.sceneListener != null) { 1178 scene.sceneListener.touchEventEnd(); 1179 } 1180 return null; 1181 }, scene.getAccessControlContext()); 1182 }); 1183 } finally { 1184 if (stage != null) { 1185 stage.setInEventHandler(false); 1186 } 1187 if (PULSE_LOGGING_ENABLED) { 1188 PulseLogger.newInput(null); 1189 } 1190 } 1191 1192 gestures.notifyEndTouchEvent(time); 1193 } 1194 1195 @Override 1196 public Accessible getSceneAccessible() { 1197 if (scene != null && scene.sceneListener != null) { 1198 return scene.sceneListener.getSceneAccessible(); 1199 } 1200 return null; 1201 } 1202 }