1 /* 2 * Copyright (c) 2010, 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.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 350 scene.sceneListener.mouseEvent(mouseEventType(type), x, y, xAbs, yAbs, 351 mouseEventButton(button), isPopupTrigger, isSynthesized, 352 shiftDown, controlDown, altDown, metaDown, 353 primaryButtonDown, middleButtonDown, secondaryButtonDown); 354 } 355 } finally { 356 if (stage != null) { 357 stage.setInEventHandler(false); 358 } 359 if (PULSE_LOGGING_ENABLED) { 360 PulseLogger.newInput(null); 361 } 362 } 363 return null; 364 } 365 } 366 367 @Override 368 public void handleMouseEvent(View view, long time, int type, int button, 369 int x, int y, int xAbs, int yAbs, 370 int modifiers, boolean isPopupTrigger, boolean isSynthesized) 371 { 372 mouseNotification.view = view; 373 mouseNotification.time = time; 374 mouseNotification.type = type; 375 mouseNotification.button = button; 376 mouseNotification.x = x; 377 mouseNotification.y = y; 378 mouseNotification.xAbs = xAbs; 379 mouseNotification.yAbs = yAbs; 380 mouseNotification.modifiers = modifiers; 381 mouseNotification.isPopupTrigger = isPopupTrigger; 382 mouseNotification.isSynthesized = isSynthesized; 383 384 QuantumToolkit.runWithoutRenderLock(() -> { 385 return AccessController.doPrivileged(mouseNotification, scene.getAccessControlContext()); 386 }); 387 } 388 389 @Override public void handleMenuEvent(final View view, 390 final int x, final int y, final int xAbs, final int yAbs, 391 final boolean isKeyboardTrigger) 392 { 393 if (PULSE_LOGGING_ENABLED) { 394 PulseLogger.newInput("MENU_EVENT"); 395 } 396 WindowStage stage = scene.getWindowStage(); 397 try { 398 if (stage != null) { 399 stage.setInEventHandler(true); 400 } 401 QuantumToolkit.runWithoutRenderLock(() -> { 402 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 403 if (scene.sceneListener != null) { 404 scene.sceneListener.menuEvent(x, y, xAbs, yAbs, isKeyboardTrigger); 405 } 406 return null; 407 }, scene.getAccessControlContext()); 408 }); 409 } finally { 410 if (stage != null) { 411 stage.setInEventHandler(false); 412 } 413 if (PULSE_LOGGING_ENABLED) { 414 PulseLogger.newInput(null); 415 } 416 } 417 } 418 419 @Override public void handleScrollEvent(final View view, final long time, 420 final int x, final int y, final int xAbs, final int yAbs, 421 final double deltaX, final double deltaY, final int modifiers, 422 final int lines, final int chars, 423 final int defaultLines, final int defaultChars, 424 final double xMultiplier, final double yMultiplier) 425 { 426 if (PULSE_LOGGING_ENABLED) { 427 PulseLogger.newInput("SCROLL_EVENT"); 428 } 429 WindowStage stage = scene.getWindowStage(); 430 try { 431 if (stage != null) { 432 stage.setInEventHandler(true); 433 } 434 QuantumToolkit.runWithoutRenderLock(() -> { 435 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 436 if (scene.sceneListener != null) { 437 scene.sceneListener.scrollEvent(ScrollEvent.SCROLL, 438 deltaX, deltaY, 0, 0, 439 xMultiplier, yMultiplier, 440 0, // touchCount 441 chars, lines, defaultChars, defaultLines, 442 x, y, xAbs, yAbs, 443 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 444 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 445 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 446 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 447 false, // this is always indirect 448 false); // this has no inertia 449 } 450 return null; 451 }, scene.getAccessControlContext()); 452 }); 453 } finally { 454 if (stage != null) { 455 stage.setInEventHandler(false); 456 } 457 if (PULSE_LOGGING_ENABLED) { 458 PulseLogger.newInput(null); 459 } 460 } 461 } 462 463 private static byte inputMethodEventAttrValue(int pos, int[] attrBoundary, byte[] attrValue) { 464 if (attrBoundary != null) { 465 for (int current = 0; current < attrBoundary.length-1; current++) { 466 if (pos >= attrBoundary[current] && 467 pos < attrBoundary[current+1]) { 468 return attrValue[current]; 469 } 470 } 471 } 472 return View.IME_ATTR_INPUT_ERROR; 473 } 474 475 private static ObservableList<InputMethodTextRun> inputMethodEventComposed( 476 String text, int commitCount, int[] clauseBoundary, int[] attrBoundary, byte[] attrValue) 477 { 478 ObservableList<InputMethodTextRun> composed = new TrackableObservableList<InputMethodTextRun>() { 479 @Override 480 protected void onChanged(ListChangeListener.Change<InputMethodTextRun> c) { 481 } 482 }; 483 484 if (commitCount < text.length()) { 485 if (clauseBoundary == null) { 486 // Create one single segment as UNSELECTED_RAW 487 composed.add(new InputMethodTextRun( 488 text.substring(commitCount), 489 InputMethodHighlight.UNSELECTED_RAW)); 490 } else { 491 for (int current = 0; current < clauseBoundary.length-1; current++) { 492 if (clauseBoundary[current] < commitCount) { 493 continue; 494 } 495 496 InputMethodHighlight highlight; 497 switch (inputMethodEventAttrValue(clauseBoundary[current], attrBoundary, attrValue)) { 498 case View.IME_ATTR_TARGET_CONVERTED: 499 highlight = InputMethodHighlight.SELECTED_CONVERTED; 500 break; 501 case View.IME_ATTR_CONVERTED: 502 highlight = InputMethodHighlight.UNSELECTED_CONVERTED; 503 break; 504 case View.IME_ATTR_TARGET_NOTCONVERTED: 505 highlight = InputMethodHighlight.SELECTED_RAW; 506 break; 507 case View.IME_ATTR_INPUT: 508 case View.IME_ATTR_INPUT_ERROR: 509 default: 510 highlight = InputMethodHighlight.UNSELECTED_RAW; 511 break; 512 } 513 composed.add(new InputMethodTextRun( 514 text.substring(clauseBoundary[current], 515 clauseBoundary[current+1]), 516 highlight)); 517 } 518 } 519 } 520 return composed; 521 } 522 523 @Override public void handleInputMethodEvent(final long time, final String text, 524 final int[] clauseBoundary, 525 final int[] attrBoundary, final byte[] attrValue, 526 final int commitCount, final int cursorPos) 527 { 528 if (PULSE_LOGGING_ENABLED) { 529 PulseLogger.newInput("INPUT_METHOD_EVENT"); 530 } 531 WindowStage stage = scene.getWindowStage(); 532 try { 533 if (stage != null) { 534 stage.setInEventHandler(true); 535 } 536 QuantumToolkit.runWithoutRenderLock(() -> { 537 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 538 if (scene.sceneListener != null) { 539 String t = text != null ? text : ""; 540 EventType<InputMethodEvent> eventType = 541 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED; 542 ObservableList<InputMethodTextRun> composed = inputMethodEventComposed( 543 t, commitCount, clauseBoundary, attrBoundary, attrValue); 544 String committed = t.substring(0, commitCount); 545 scene.sceneListener.inputMethodEvent(eventType, composed, committed, cursorPos); 546 } 547 return null; 548 }, scene.getAccessControlContext()); 549 }); 550 } finally { 551 if (stage != null) { 552 stage.setInEventHandler(false); 553 } 554 if (PULSE_LOGGING_ENABLED) { 555 PulseLogger.newInput(null); 556 } 557 } 558 } 559 560 @Override 561 public double[] getInputMethodCandidatePos(int offset) { 562 Point2D p2d = scene.inputMethodRequests.getTextLocation(offset); 563 double[] ret = new double[2]; 564 ret[0] = p2d.getX(); 565 ret[1] = p2d.getY(); 566 return ret; 567 } 568 569 private static TransferMode actionToTransferMode(int dropActions) { 570 if (dropActions == Clipboard.ACTION_NONE) { 571 return null; 572 } else if (dropActions == Clipboard.ACTION_COPY 573 //IE drop action for URL copy; XXX: should be fixed in Glass 574 || dropActions == (Clipboard.ACTION_COPY | Clipboard.ACTION_REFERENCE) ) 575 { 576 return TransferMode.COPY; 577 } else if (dropActions == Clipboard.ACTION_MOVE 578 //IE drop action for URL move; XXX: should be fixed in Glass 579 || dropActions == (Clipboard.ACTION_MOVE | Clipboard.ACTION_REFERENCE) ) 580 { 581 return TransferMode.MOVE; 582 } else if (dropActions == Clipboard.ACTION_REFERENCE) { 583 return TransferMode.LINK; 584 } else if (dropActions == Clipboard.ACTION_COPY_OR_MOVE) { 585 if (QuantumToolkit.verbose) { 586 System.err.println("Ambiguous drop action: " + Integer.toHexString(dropActions)); 587 } 588 } else { 589 if (QuantumToolkit.verbose) { 590 System.err.println("Unknown drop action: " + Integer.toHexString(dropActions)); 591 } 592 } 593 return null; 594 } 595 596 private static int transferModeToAction(TransferMode tm) { 597 if (tm == null) { 598 return Clipboard.ACTION_NONE; 599 } 600 601 switch (tm) { 602 case COPY: 603 return Clipboard.ACTION_COPY; 604 case MOVE: 605 return Clipboard.ACTION_MOVE; 606 case LINK: 607 return Clipboard.ACTION_REFERENCE; 608 default: 609 return Clipboard.ACTION_NONE; 610 } 611 } 612 613 @Override public int handleDragEnter(View view, 614 final int x, final int y, final int xAbs, final int yAbs, 615 final int recommendedDropAction, 616 final ClipboardAssistance dropTargetAssistant) 617 { 618 if (PULSE_LOGGING_ENABLED) { 619 PulseLogger.newInput("DRAG_ENTER"); 620 } 621 TransferMode action; 622 try { 623 action = QuantumToolkit.runWithoutRenderLock(() -> { 624 return dndHandler.handleDragEnter(x, y, xAbs, yAbs, 625 actionToTransferMode(recommendedDropAction), 626 dropTargetAssistant); 627 }); 628 } finally { 629 if (PULSE_LOGGING_ENABLED) { 630 PulseLogger.newInput(null); 631 } 632 } 633 return transferModeToAction(action); 634 } 635 636 @Override public void handleDragLeave(View view, final ClipboardAssistance dropTargetAssistant) { 637 if (PULSE_LOGGING_ENABLED) { 638 PulseLogger.newInput("DRAG_LEAVE"); 639 } 640 try { 641 QuantumToolkit.runWithoutRenderLock(() -> { 642 dndHandler.handleDragLeave(dropTargetAssistant); 643 return null; 644 }); 645 } finally { 646 if (PULSE_LOGGING_ENABLED) { 647 PulseLogger.newInput(null); 648 } 649 } 650 } 651 652 @Override public int handleDragDrop(View view, 653 final int x, final int y, final int xAbs, final int yAbs, 654 final int recommendedDropAction, 655 final ClipboardAssistance dropTargetAssistant) 656 { 657 if (PULSE_LOGGING_ENABLED) { 658 PulseLogger.newInput("DRAG_DROP"); 659 } 660 TransferMode action; 661 try { 662 action = QuantumToolkit.runWithoutRenderLock(() -> { 663 return dndHandler.handleDragDrop(x, y, xAbs, yAbs, 664 actionToTransferMode(recommendedDropAction), 665 dropTargetAssistant); 666 }); 667 } finally { 668 if (PULSE_LOGGING_ENABLED) { 669 PulseLogger.newInput(null); 670 } 671 } 672 return transferModeToAction(action); 673 } 674 675 @Override public int handleDragOver(View view, 676 final int x, final int y, final int xAbs, final int yAbs, 677 final int recommendedDropAction, 678 final ClipboardAssistance dropTargetAssistant) 679 { 680 if (PULSE_LOGGING_ENABLED) { 681 PulseLogger.newInput("DRAG_OVER"); 682 } 683 TransferMode action; 684 try { 685 action = QuantumToolkit.runWithoutRenderLock(() -> { 686 return dndHandler.handleDragOver(x, y, xAbs, yAbs, 687 actionToTransferMode(recommendedDropAction), 688 dropTargetAssistant); 689 }); 690 } finally { 691 if (PULSE_LOGGING_ENABLED) { 692 PulseLogger.newInput(null); 693 } 694 } 695 return transferModeToAction(action); 696 } 697 698 private ClipboardAssistance dropSourceAssistant; 699 700 @Override public void handleDragStart(View view, final int button, 701 final int x, final int y, final int xAbs, final int yAbs, 702 final ClipboardAssistance assistant) 703 { 704 if (PULSE_LOGGING_ENABLED) { 705 PulseLogger.newInput("DRAG_START"); 706 } 707 dropSourceAssistant = assistant; 708 try { 709 QuantumToolkit.runWithoutRenderLock(() -> { 710 dndHandler.handleDragStart(button, x, y, xAbs, yAbs, assistant); 711 return null; 712 }); 713 } finally { 714 if (PULSE_LOGGING_ENABLED) { 715 PulseLogger.newInput(null); 716 } 717 } 718 } 719 720 @Override public void handleDragEnd(View view, final int performedAction) { 721 if (PULSE_LOGGING_ENABLED) { 722 PulseLogger.newInput("DRAG_END"); 723 } 724 try { 725 QuantumToolkit.runWithoutRenderLock(() -> { 726 dndHandler.handleDragEnd(actionToTransferMode(performedAction), dropSourceAssistant); 727 return null; 728 }); 729 } finally { 730 if (PULSE_LOGGING_ENABLED) { 731 PulseLogger.newInput(null); 732 } 733 } 734 } 735 736 // TODO - dropTargetListener.dropActionChanged 737 // TODO - dragSourceListener.dropActionChanged 738 739 private final ViewEventNotification viewNotification = new ViewEventNotification(); 740 private class ViewEventNotification implements PrivilegedAction<Void> { 741 View view; 742 long time; 743 int type; 744 745 @Override 746 public Void run() { 747 if (scene.sceneListener == null) { 748 return null; 749 } 750 switch (type) { 751 case ViewEvent.REPAINT: { 752 Window w = view.getWindow(); 753 if (w != null && w.getMinimumWidth() == view.getWidth() && !w.isVisible()) { 754 // RT-21057 - ignore initial minimum size setting if not visible 755 break; 756 } 757 if (QuantumToolkit.drawInPaint && w != null && w.isVisible()) { 758 WindowStage stage = scene.getWindowStage(); 759 if (stage != null && !stage.isApplet()) { 760 collector.liveRepaintRenderJob(scene); 761 } 762 } 763 scene.entireSceneNeedsRepaint(); 764 break; 765 } 766 case ViewEvent.RESIZE: { 767 Window w = view.getWindow(); 768 scene.sceneListener.changedSize(view.getWidth(), view.getHeight()); 769 scene.entireSceneNeedsRepaint(); 770 QuantumToolkit.runWithRenderLock(() -> { 771 scene.updateSceneState(); 772 return null; 773 }); 774 if (QuantumToolkit.liveResize && w != null && w.isVisible()) { 775 WindowStage stage = scene.getWindowStage(); 776 if (stage != null && !stage.isApplet()) { 777 collector.liveRepaintRenderJob(scene); 778 } 779 } 780 break; 781 } 782 case ViewEvent.MOVE: 783 scene.sceneListener.changedLocation(view.getX(), view.getY()); 784 break; 785 case ViewEvent.FULLSCREEN_ENTER: 786 case ViewEvent.FULLSCREEN_EXIT: 787 if (scene.getWindowStage() != null) { 788 scene.getWindowStage().fullscreenChanged(type == ViewEvent.FULLSCREEN_ENTER); 789 } 790 break; 791 case ViewEvent.ADD: 792 case ViewEvent.REMOVE: 793 // unhandled 794 break; 795 default: 796 throw new RuntimeException("handleViewEvent: unhandled type: " + type); 797 } 798 return null; 799 } 800 } 801 802 @Override public void handleViewEvent(View view, long time, final int type) { 803 if (PULSE_LOGGING_ENABLED) { 804 PulseLogger.newInput("VIEW_EVENT: "+ViewEvent.getTypeString(type)); 805 } 806 viewNotification.view = view; 807 viewNotification.time = time; 808 viewNotification.type = type; 809 try { 810 QuantumToolkit.runWithoutRenderLock(() -> { 811 return AccessController.doPrivileged(viewNotification, scene.getAccessControlContext()); 812 }); 813 } 814 finally { 815 if (PULSE_LOGGING_ENABLED) { 816 PulseLogger.newInput(null); 817 } 818 } 819 } 820 821 @Override public void handleScrollGestureEvent( 822 View view, final long time, final int type, 823 final int modifiers, final boolean isDirect, final boolean isInertia, final int touchCount, 824 final int x, final int y, final int xAbs, final int yAbs, final double dx, final double dy, 825 final double totaldx, final double totaldy, final double multiplierX, final double multiplierY) 826 { 827 if (PULSE_LOGGING_ENABLED) { 828 PulseLogger.newInput("SCROLL_GESTURE_EVENT"); 829 } 830 WindowStage stage = scene.getWindowStage(); 831 try { 832 if (stage != null) { 833 stage.setInEventHandler(true); 834 } 835 QuantumToolkit.runWithoutRenderLock(() -> { 836 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 837 if (scene.sceneListener != null) { 838 EventType<ScrollEvent> eventType; 839 switch(type) { 840 case GestureEvent.GESTURE_STARTED: 841 eventType = ScrollEvent.SCROLL_STARTED; 842 break; 843 case GestureEvent.GESTURE_PERFORMED: 844 eventType = ScrollEvent.SCROLL; 845 break; 846 case GestureEvent.GESTURE_FINISHED: 847 eventType = ScrollEvent.SCROLL_FINISHED; 848 break; 849 default: 850 throw new RuntimeException("Unknown scroll event type: " + type); 851 } 852 scene.sceneListener.scrollEvent(eventType, dx, dy, totaldx, totaldy, 853 multiplierX, multiplierY, 854 touchCount, 855 0, 0, 0, 0, 856 x == View.GESTURE_NO_VALUE ? Double.NaN : x, 857 y == View.GESTURE_NO_VALUE ? Double.NaN : y, 858 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs, 859 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs, 860 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 861 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 862 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 863 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 864 isDirect, isInertia); 865 } 866 return null; 867 }, scene.getAccessControlContext()); 868 }); 869 } finally { 870 if (stage != null) { 871 stage.setInEventHandler(false); 872 } 873 if (PULSE_LOGGING_ENABLED) { 874 PulseLogger.newInput(null); 875 } 876 } 877 } 878 879 @Override public void handleZoomGestureEvent( 880 View view, final long time, final int type, 881 final int modifiers, final boolean isDirect, final boolean isInertia, 882 final int originx, final int originy, 883 final int originxAbs, final int originyAbs, 884 final double scale, double expansion, 885 final double totalscale, double totalexpansion) 886 { 887 if (PULSE_LOGGING_ENABLED) { 888 PulseLogger.newInput("ZOOM_GESTURE_EVENT"); 889 } 890 WindowStage stage = scene.getWindowStage(); 891 try { 892 if (stage != null) { 893 stage.setInEventHandler(true); 894 } 895 QuantumToolkit.runWithoutRenderLock(() -> { 896 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 897 if (scene.sceneListener != null) { 898 EventType<ZoomEvent> eventType; 899 switch (type) { 900 case GestureEvent.GESTURE_STARTED: 901 eventType = ZoomEvent.ZOOM_STARTED; 902 break; 903 case GestureEvent.GESTURE_PERFORMED: 904 eventType = ZoomEvent.ZOOM; 905 break; 906 case GestureEvent.GESTURE_FINISHED: 907 eventType = ZoomEvent.ZOOM_FINISHED; 908 break; 909 default: 910 throw new RuntimeException("Unknown scroll event type: " + type); 911 } 912 scene.sceneListener.zoomEvent(eventType, scale, totalscale, 913 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx, 914 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy, 915 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs, 916 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs, 917 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 918 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 919 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 920 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 921 isDirect, isInertia); 922 } 923 return null; 924 }, scene.getAccessControlContext()); 925 }); 926 } finally { 927 if (stage != null) { 928 stage.setInEventHandler(false); 929 } 930 if (PULSE_LOGGING_ENABLED) { 931 PulseLogger.newInput(null); 932 } 933 } 934 } 935 936 @Override public void handleRotateGestureEvent( 937 View view, final long time, final int type, 938 final int modifiers, final boolean isDirect, final boolean isInertia, 939 final int originx, final int originy, 940 final int originxAbs, final int originyAbs, 941 final double dangle, final double totalangle) 942 { 943 if (PULSE_LOGGING_ENABLED) { 944 PulseLogger.newInput("ROTATE_GESTURE_EVENT"); 945 } 946 WindowStage stage = scene.getWindowStage(); 947 try { 948 if (stage != null) { 949 stage.setInEventHandler(true); 950 } 951 QuantumToolkit.runWithoutRenderLock(() -> { 952 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 953 if (scene.sceneListener != null) { 954 EventType<RotateEvent> eventType; 955 switch (type) { 956 case GestureEvent.GESTURE_STARTED: 957 eventType = RotateEvent.ROTATION_STARTED; 958 break; 959 case GestureEvent.GESTURE_PERFORMED: 960 eventType = RotateEvent.ROTATE; 961 break; 962 case GestureEvent.GESTURE_FINISHED: 963 eventType = RotateEvent.ROTATION_FINISHED; 964 break; 965 default: 966 throw new RuntimeException("Unknown scroll event type: " + type); 967 } 968 scene.sceneListener.rotateEvent(eventType, dangle, totalangle, 969 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx, 970 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy, 971 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs, 972 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs, 973 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 974 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 975 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 976 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 977 isDirect, isInertia); 978 } 979 return null; 980 }, scene.getAccessControlContext()); 981 }); 982 } finally { 983 if (stage != null) { 984 stage.setInEventHandler(false); 985 } 986 if (PULSE_LOGGING_ENABLED) { 987 PulseLogger.newInput(null); 988 } 989 } 990 } 991 992 @Override public void handleSwipeGestureEvent( 993 View view, final long time, int type, 994 final int modifiers, final boolean isDirect, 995 boolean isInertia, final int touchCount, 996 final int dir, final int x, final int y, final int xAbs, final int yAbs) 997 { 998 if (PULSE_LOGGING_ENABLED) { 999 PulseLogger.newInput("SWIPE_EVENT"); 1000 } 1001 WindowStage stage = scene.getWindowStage(); 1002 try { 1003 if (stage != null) { 1004 stage.setInEventHandler(true); 1005 } 1006 QuantumToolkit.runWithoutRenderLock(() -> { 1007 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1008 if (scene.sceneListener != null) { 1009 EventType<SwipeEvent> eventType; 1010 switch (dir) { 1011 case SwipeGesture.DIR_UP: 1012 eventType = SwipeEvent.SWIPE_UP; 1013 break; 1014 case SwipeGesture.DIR_DOWN: 1015 eventType = SwipeEvent.SWIPE_DOWN; 1016 break; 1017 case SwipeGesture.DIR_LEFT: 1018 eventType = SwipeEvent.SWIPE_LEFT; 1019 break; 1020 case SwipeGesture.DIR_RIGHT: 1021 eventType = SwipeEvent.SWIPE_RIGHT; 1022 break; 1023 default: 1024 throw new RuntimeException("Unknown swipe event direction: " + dir); 1025 } 1026 scene.sceneListener.swipeEvent(eventType, touchCount, 1027 x == View.GESTURE_NO_VALUE ? Double.NaN : x, 1028 y == View.GESTURE_NO_VALUE ? Double.NaN : y, 1029 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs, 1030 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs, 1031 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1032 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1033 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1034 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 1035 isDirect); 1036 } 1037 return null; 1038 }, scene.getAccessControlContext()); 1039 }); 1040 } finally { 1041 if (stage != null) { 1042 stage.setInEventHandler(false); 1043 } 1044 if (PULSE_LOGGING_ENABLED) { 1045 PulseLogger.newInput(null); 1046 } 1047 } 1048 } 1049 1050 @Override public void handleBeginTouchEvent( 1051 View view, final long time, final int modifiers, 1052 final boolean isDirect, final int touchEventCount) 1053 { 1054 if (PULSE_LOGGING_ENABLED) { 1055 PulseLogger.newInput("BEGIN_TOUCH_EVENT"); 1056 } 1057 WindowStage stage = scene.getWindowStage(); 1058 try { 1059 if (stage != null) { 1060 stage.setInEventHandler(true); 1061 } 1062 QuantumToolkit.runWithoutRenderLock(() -> { 1063 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1064 if (scene.sceneListener != null) { 1065 scene.sceneListener.touchEventBegin(time, touchEventCount, 1066 isDirect, 1067 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1068 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1069 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1070 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0); 1071 } 1072 return null; 1073 }, scene.getAccessControlContext()); 1074 }); 1075 } finally { 1076 if (stage != null) { 1077 stage.setInEventHandler(false); 1078 } 1079 if (PULSE_LOGGING_ENABLED) { 1080 PulseLogger.newInput(null); 1081 } 1082 } 1083 1084 gestures.notifyBeginTouchEvent(time, modifiers, isDirect, touchEventCount); 1085 } 1086 1087 @Override public void handleNextTouchEvent( 1088 View view, final long time, final int type, final long touchId, 1089 final int x, final int y, final int xAbs, final int yAbs) 1090 { 1091 if (PULSE_LOGGING_ENABLED) { 1092 PulseLogger.newInput("NEXT_TOUCH_EVENT"); 1093 } 1094 WindowStage stage = scene.getWindowStage(); 1095 try { 1096 if (stage != null) { 1097 stage.setInEventHandler(true); 1098 } 1099 QuantumToolkit.runWithoutRenderLock(() -> { 1100 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1101 if (scene.sceneListener != null) { 1102 TouchPoint.State state; 1103 switch (type) { 1104 case TouchEvent.TOUCH_PRESSED: 1105 state = TouchPoint.State.PRESSED; 1106 break; 1107 case TouchEvent.TOUCH_MOVED: 1108 state = TouchPoint.State.MOVED; 1109 break; 1110 case TouchEvent.TOUCH_STILL: 1111 state = TouchPoint.State.STATIONARY; 1112 break; 1113 case TouchEvent.TOUCH_RELEASED: 1114 state = TouchPoint.State.RELEASED; 1115 break; 1116 default: 1117 throw new RuntimeException("Unknown touch state: " + type); 1118 } 1119 scene.sceneListener.touchEventNext(state, touchId, x, y, xAbs, yAbs); 1120 } 1121 return null; 1122 }, scene.getAccessControlContext()); 1123 }); 1124 } finally { 1125 if (stage != null) { 1126 stage.setInEventHandler(false); 1127 } 1128 if (PULSE_LOGGING_ENABLED) { 1129 PulseLogger.newInput(null); 1130 } 1131 } 1132 1133 gestures.notifyNextTouchEvent(time, type, touchId, x, y, xAbs, yAbs); 1134 } 1135 1136 @Override public void handleEndTouchEvent(View view, long time) { 1137 if (PULSE_LOGGING_ENABLED) { 1138 PulseLogger.newInput("END_TOUCH_EVENT"); 1139 } 1140 WindowStage stage = scene.getWindowStage(); 1141 try { 1142 if (stage != null) { 1143 stage.setInEventHandler(true); 1144 } 1145 QuantumToolkit.runWithoutRenderLock(() -> { 1146 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1147 if (scene.sceneListener != null) { 1148 scene.sceneListener.touchEventEnd(); 1149 } 1150 return null; 1151 }, scene.getAccessControlContext()); 1152 }); 1153 } finally { 1154 if (stage != null) { 1155 stage.setInEventHandler(false); 1156 } 1157 if (PULSE_LOGGING_ENABLED) { 1158 PulseLogger.newInput(null); 1159 } 1160 } 1161 1162 gestures.notifyEndTouchEvent(time); 1163 } 1164 1165 @Override 1166 public Accessible getSceneAccessible() { 1167 if (scene != null && scene.sceneListener != null) { 1168 return scene.sceneListener.getSceneAccessible(); 1169 } 1170 return null; 1171 } 1172 }