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 package com.sun.glass.ui.lens; 26 27 import java.io.File; 28 import java.nio.ByteBuffer; 29 import java.nio.IntBuffer; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.LinkedList; 33 34 import com.sun.glass.events.KeyEvent; 35 import com.sun.glass.events.MouseEvent; 36 import com.sun.glass.events.TouchEvent; 37 import com.sun.glass.events.ViewEvent; 38 import com.sun.glass.events.WindowEvent; 39 import com.sun.glass.ui.Application; 40 import com.sun.glass.ui.Clipboard; 41 import com.sun.glass.ui.CommonDialogs.ExtensionFilter; 42 import com.sun.glass.ui.CommonDialogs.FileChooserResult; 43 import com.sun.glass.ui.Cursor; 44 import com.sun.glass.ui.EventLoop; 45 import com.sun.glass.ui.Menu; 46 import com.sun.glass.ui.MenuBar; 47 import com.sun.glass.ui.Pixels; 48 import com.sun.glass.ui.Robot; 49 import com.sun.glass.ui.Screen; 50 import com.sun.glass.ui.Size; 51 import com.sun.glass.ui.Timer; 52 import com.sun.glass.ui.View; 53 import com.sun.glass.ui.Window; 54 import sun.util.logging.PlatformLogger.Level; 55 import java.util.Arrays; 56 import java.util.Iterator; 57 58 final class LensApplication extends Application { 59 60 /** Bit to indicate that a device has touch support */ 61 private static final int DEVICE_TOUCH = 0; 62 /** Bit to indicate that a device has multitouch support */ 63 private static final int DEVICE_MULTITOUCH = 1; 64 /** Bit to indicate that a device has relative motion pointer support */ 65 private static final int DEVICE_POINTER = 2; 66 /** Bit to indicate that a device has arrow keys and a select key */ 67 private static final int DEVICE_5WAY = 3; 68 /** Bit to indicate that a device has a full PC keyboard */ 69 private static final int DEVICE_PC_KEYBOARD = 4; 70 /** Largest bit used in device capability bitmasks */ 71 private static final int DEVICE_MAX = 4; 72 /** A running count of the numbers of devices with each device capability */ 73 private int[] deviceFlags = new int[DEVICE_MAX + 1]; 74 75 /** 76 * Values of -1 mean that these variables are not set. We will reset them to -1 whenever we 77 * get a non-touch move event. 78 */ 79 private int previousTouchMoveX = -1; 80 private int previousTouchMoveY = -1; 81 private int previousTouchMoveScreenX = -1; 82 private int previousTouchMoveScreenY = -1; 83 84 /** True if the application wishes to show the cursor */ 85 private boolean cursorVisible = true; 86 87 Menu windowMenu; 88 Menu editMenu; 89 Menu fileMenu; 90 91 private static final Object invokeAndWaitLock = new Object(); 92 private static Runnable waitingFor; 93 94 private static int activeEventLoopThreads = 0; 95 private static final Object activeEventLoopLock = new Object(); 96 97 private static boolean doComposite = true; 98 99 static private boolean isInitialized = false; 100 101 // setup for JNI 102 private static native void _initIDs(); 103 104 // initialize any native display connection/state 105 private static native boolean _initialize(); 106 107 private static native void _notifyRenderingEnd(); 108 109 private EventLoop dndEventLoop; 110 111 private static void initLibrary() { 112 final String lensProperty = "glass.lens"; 113 final String platform = AccessController.doPrivileged( 114 (PrivilegedAction<String>) () -> System.getProperty(lensProperty, "")); 115 116 doComposite = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("doNativeComposite")); 117 118 if (isInitialized) { 119 //make sure we make this only once 120 return; 121 } 122 123 LensLogger.getLogger().info("LensApplication initialization"); 124 125 if (platform.equals("")) { 126 LensLogger.getLogger().severe( 127 "System property " + lensProperty + " not defined"); 128 } 129 130 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 131 Application.loadNativeLibrary("glass_lens_" + platform); 132 return null; 133 }); 134 _initIDs(); 135 isInitialized = true; 136 LensLogger.getLogger().info("LensApplication initialization done"); 137 } 138 139 static { 140 LensApplication.initLibrary(); 141 } 142 143 //cache the singleton object for native layer usage 144 native void registerApplication(); 145 146 //package protected to be used only by LensPlatformFactory 147 LensApplication() { 148 super(); 149 LensLogger.getLogger().fine("LensApplication ctor called, registering in" 150 + " native layer"); 151 registerApplication(); 152 } 153 154 private static abstract class Event { 155 abstract void dispatch(); 156 } 157 158 private static class RunnableEvent extends Event { 159 private boolean wait; 160 private Runnable runnable; 161 162 RunnableEvent(boolean wait, Runnable runnable) { 163 this.wait = wait; 164 this.runnable = runnable; 165 } 166 167 @Override 168 void dispatch() { 169 runnable.run(); 170 if (wait) { 171 synchronized (invokeAndWaitLock) { 172 waitingFor = null; 173 invokeAndWaitLock.notify(); 174 } 175 } 176 } 177 178 @Override 179 public String toString() { 180 return "RunnableEvent[runnable=" + runnable + ",wait=" + wait + "]"; 181 } 182 } 183 184 @Override 185 public boolean hasWindowManager() { 186 return false; 187 } 188 189 /** 190 * This class is used to handle key events 191 * 192 */ 193 private static class LensKeyEvent extends Event { 194 195 //the view object to notify 196 private LensView view; 197 198 //type of the event (pressed, released...) 199 // as defined in KeyEvent.java 200 private int type; 201 202 //key code 203 private int keyCode; 204 205 //bit mask of modifiers (shift, ctrl...) 206 private int modifiers; 207 208 // A buffer holding the char key sequence, can be 0 length 209 private char[] chars; 210 211 LensKeyEvent(LensView view, int type, int keyCode, int modifiers, 212 char[] chars) { 213 this.view = view; 214 this.type = type; 215 this.keyCode = keyCode; 216 this.modifiers = modifiers; 217 this.chars = chars; 218 } 219 220 @Override 221 void dispatch() { 222 view._notifyKey(type, keyCode, chars, modifiers); 223 } 224 225 @Override 226 public String toString() { 227 return "LensKeyEvent[view=" + view 228 + ",type=" + type 229 + ",keyCode=" + keyCode 230 + ",modifiers=" + modifiers 231 + ",chars=" + String.valueOf(chars) 232 + "]"; 233 } 234 } 235 236 /** 237 * This class is used to handle window related events 238 * 239 */ 240 private static class LensWindowEvent extends Event { 241 242 static enum EType { 243 CLOSE, DESTROY, EXPOSE, FOCUS, MOVE, RESIZE, UNGRAB, FOCUS_DISABLED 244 }; 245 246 private EType type; 247 248 // The window object to notify 249 private LensWindow window; 250 //The event that invoked this notification, as described 251 // in WindowEvent class 252 private int windowEvent; 253 //Window parameters for update 254 private int x; 255 private int y; 256 private int width; 257 private int height; 258 259 /** 260 * Generic constructor, used when no parameters need to be 261 * updated 262 * 263 * @param type LensApplication event 264 * @param window the window to notify 265 * @param windowEvent one of the events described in WindowEvent 266 * class 267 */ 268 LensWindowEvent(EType type, LensWindow window, int windowEvent) { 269 this.type = type; 270 this.window = window; 271 this.windowEvent = windowEvent; 272 } 273 274 /** 275 * Use this constructor when window parameters have been changed 276 * 277 * 278 * @param type LensApplication event 279 * @param window the window to notify 280 * @param windowEvent one of the events described in WindowEvent 281 * class 282 * @param x 283 * @param y 284 * @param width 285 * @param height 286 */ 287 LensWindowEvent(EType type, LensWindow window, int windowEvent, 288 int x, int y, 289 int width, int height) { 290 this.type = type; 291 this.window = window; 292 this.windowEvent = windowEvent; 293 this.x = x; 294 this.y = y; 295 this.width = width; 296 this.height = height; 297 } 298 299 @Override 300 void dispatch() { 301 switch (type) { 302 case FOCUS: 303 window._notifyFocus(windowEvent); 304 break; 305 case MOVE: 306 window._notifyMove(x, y); 307 break; 308 case RESIZE: 309 window._notifyResize(windowEvent, width, height); 310 break; 311 case UNGRAB: 312 window._notifyFocusUngrab(); 313 break; 314 case DESTROY: 315 window._notifyDestroy(); 316 break; 317 case CLOSE: 318 window._notifyClose(); 319 break; 320 case EXPOSE: 321 window._notifyExpose(x, y, width, height); 322 break; 323 case FOCUS_DISABLED: 324 window._notifyFocusDisabled(); 325 break; 326 327 default: 328 LensLogger.getLogger().severe( 329 "Unrecognized window event type"); 330 } 331 } 332 333 @Override 334 public String toString() { 335 return super.toString() + "[window=" + window 336 + ",type=" + type 337 + ",windowEvent=" + windowEvent 338 + ",x=" + x 339 + ",y=" + y 340 + ",width=" + width 341 + ",height=" + height 342 + "]"; 343 } 344 345 } 346 private static class LensMouseEvent extends Event { 347 348 private LensView target; 349 private int action; 350 private int x, y, absx, absy; 351 private int button; 352 private int modifiers; 353 private boolean isPopupTrigger; 354 private boolean isSynthesized; 355 356 LensMouseEvent(LensView target, 357 int action, 358 int x, int y, 359 int absx, int absy, 360 int button, 361 int modifiers, 362 boolean isPopupTrigger, 363 boolean isSynthesized) { 364 this.target = target; 365 this.action = action; 366 this.x = x; 367 this.y = y; 368 this.absx = absx; 369 this.absy = absy; 370 this.button = button; 371 this.modifiers = modifiers; 372 this.isPopupTrigger = isPopupTrigger; 373 this.isSynthesized = isSynthesized; 374 } 375 376 @Override 377 void dispatch() { 378 target._notifyMouse( 379 action, button, 380 x, y, 381 absx, absy, 382 modifiers, 383 isPopupTrigger, isSynthesized); 384 } 385 386 @Override 387 public String toString() { 388 return "LensMouseEvent[target=" + target 389 + ",action=" + action 390 + ",x=" + x 391 + ",y=" + y 392 + ",absx=" + absx 393 + ",absy=" + absy 394 + ",button=" + button 395 + ",modifiers=" + modifiers 396 + ",isPopupTrigger=" + isPopupTrigger 397 + ",isSynthesized=" + isSynthesized 398 + "]"; 399 } 400 } 401 402 private static class LensScrollEvent extends Event { 403 private LensView target; 404 private int x, y; 405 private int absx, absy; 406 private double deltaX, deltaY; 407 private int modifiers; 408 private int lines; 409 private int chars; 410 private int defaultLines; 411 private int defaultChars; 412 private double xMultiplier, yMultiplier; 413 414 LensScrollEvent(LensView target, 415 int x, int y, int absx, int absy, 416 double deltaX, double deltaY, int modifiers, int lines, int chars, 417 int defaultLines, int defaultChars, 418 double xMultiplier, double yMultiplier) { 419 420 this.target = target; 421 this.x = x; 422 this.y = y; 423 this.absx = absx; 424 this.absy = absy; 425 this.deltaX = deltaX; 426 this.deltaY = deltaY; 427 this.modifiers = modifiers; 428 this.lines = lines; 429 this.chars = chars; 430 this.defaultLines = defaultLines; 431 this.defaultChars = defaultChars; 432 this.xMultiplier = xMultiplier; 433 this.yMultiplier = yMultiplier; 434 } 435 436 @Override 437 void dispatch() { 438 target._notifyScroll( 439 x, y, 440 absx, absy, 441 deltaX, deltaY, 442 modifiers, 443 lines, chars, 444 defaultLines, defaultChars, 445 xMultiplier, yMultiplier); 446 } 447 448 @Override 449 public String toString() { 450 return "LensScrollEvent[target=" + target 451 + ",x=" + x 452 + ",y=" + y 453 + ",absx=" + absx 454 + ",absy=" + absy 455 + ",deltaX=" + deltaX 456 + ",deltaY=" + deltaY 457 + ",modifiers=" + modifiers 458 + ",lines=" + lines 459 + ",chars=" + chars 460 + ",defaultLines=" + defaultLines 461 + ",defaultChars=" + defaultChars 462 + ",xMultiplier=" + xMultiplier 463 + ",yMultiplier=" + yMultiplier 464 + "]"; 465 } 466 } 467 468 private static class LensTouchEvent extends Event { 469 470 private LensView view; 471 private int state; 472 private long id; 473 private int x; 474 private int y; 475 private int absX; 476 private int absY; 477 478 LensTouchEvent(LensView view, int state, long id, 479 int x, int y, int absX, int absY) { 480 this.view = view; 481 this.state = state; 482 this.id = id; 483 this.x = x; 484 this.y = y; 485 this.absX = absX; 486 this.absY = absY; 487 } 488 489 @Override 490 void dispatch() { 491 LensTouchInputSupport.postTouchEvent(view, state, id, x, y, absX, absY); 492 } 493 494 @Override 495 public String toString() { 496 return "LensTouchEvent[view=" + view 497 + ",state=" + state 498 + ",id=" + id 499 + ",x=" + x 500 + ",y=" + y 501 + ",absX=" + absX 502 + ",absY=" + absY 503 + "]"; 504 } 505 } 506 507 private static class LensMultiTouchEvent extends Event { 508 509 private LensView view; 510 private int[] states; 511 private long[] ids; 512 private int[] xs; 513 private int[] ys; 514 private int dx; 515 private int dy; 516 517 LensMultiTouchEvent(LensView view, int[] states, long[] ids, 518 int[] xs, int[] ys, int dx, int dy) { 519 this.view = view; 520 this.states = states; 521 this.ids = ids; 522 this.xs = xs; 523 this.ys = ys; 524 this.dx = dx; 525 this.dy = dy; 526 } 527 528 @Override 529 void dispatch() { 530 LensTouchInputSupport.postMultiTouchEvent( 531 view, states, ids, xs, ys, dx, dy); 532 } 533 534 @Override 535 public String toString() { 536 return "LensMultiTouchEvent[view=" + view 537 + ", ids " + Arrays.toString(ids) 538 + ", states " + Arrays.toString(states) 539 + ", xs " + Arrays.toString(xs) 540 + ", ys " + Arrays.toString(ys) 541 + "]"; 542 } 543 } 544 545 private static class LensViewEvent extends Event { 546 private LensView target; 547 private int x, y, width, height; 548 private int viewEventType; 549 550 LensViewEvent(LensView view, int viewEventType, 551 int x, int y, int width, int height) { 552 this.target = view; 553 this.x = x; 554 this.y = y; 555 this.width = width; 556 this.height = height; 557 this.viewEventType = viewEventType; 558 } 559 560 @Override 561 void dispatch() { 562 target._notifyViewEvent(viewEventType); 563 } 564 565 @Override 566 public String toString() { 567 return "LensViewEvent[target=" + target 568 + ", x=" + x 569 + ", y=" + y 570 + ", width=" + width 571 + ", height=" + height 572 + ", event type code " + viewEventType 573 + ", event type name " 574 + ViewEvent.getTypeString(viewEventType); 575 } 576 } 577 578 private class LensDragEvent extends Event { 579 int x, y, absx, absy; 580 DragActions action; 581 LensView view; 582 583 //This variable are used to overcome an enum limitation (you can't 584 //use switch clause on custom values enum) 585 final int ENTER = DragActions.ENTER.getValue(); 586 final int LEAVE = DragActions.LEAVE.getValue(); 587 final int OVER = DragActions.OVER.getValue(); 588 final int DROP = DragActions.DROP.getValue(); 589 590 LensDragEvent(LensView view, int x, int y, int absx, int absy, DragActions action) { 591 this.absx = absx; 592 this.absy = absy; 593 this.x = x; 594 this.y = y; 595 this.action = action; 596 this.view = view; 597 } 598 @Override 599 void dispatch() { 600 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 601 LensLogger.getLogger().finest("processing drag " + action); 602 } 603 switch (action) { 604 case ENTER: 605 view._notifyDragEnter(x, y, absx, absy, Clipboard.ACTION_COPY_OR_MOVE); 606 break; 607 case LEAVE: 608 view._notifyDragLeave(); 609 break; 610 case OVER: 611 view._notifyDragOver(x, y, absx, absy, Clipboard.ACTION_COPY_OR_MOVE); 612 break; 613 case DROP: 614 view._notifyDragDrop(x, y, absx, absy, Clipboard.ACTION_COPY_OR_MOVE); 615 leaveDndEventLoop(null); 616 break; 617 default: 618 return; 619 } 620 } 621 622 @Override 623 public String toString() { 624 return "LensDragEvent[x=" + x 625 + ", y=" + y 626 + ", absx=" + absx 627 + ", absy=" + absy 628 + ", action " + action 629 + ", view " 630 + view; 631 } 632 } 633 634 635 private class LensMenuEvent extends Event { 636 LensView view; 637 int x; 638 int y; 639 int xAbs; 640 int yAbs; 641 boolean isKeyboardTrigger; 642 643 LensMenuEvent(LensView view, int x, int y, int xAbs, 644 int yAbs, boolean isKeyboardTrigger) { 645 this.view = view; 646 this.x = x; 647 this.y = y; 648 this.xAbs = xAbs; 649 this.yAbs = yAbs; 650 this.isKeyboardTrigger = isKeyboardTrigger; 651 } 652 653 @Override 654 void dispatch() { 655 view._notifyMenu(x, y, xAbs, yAbs, isKeyboardTrigger); 656 } 657 658 @Override 659 public String toString() { 660 return "LensMenuEvent[view=" + view 661 + ", x=" + x 662 + ", y=" + y 663 + ", absx=" + xAbs 664 + ", absy=" + yAbs 665 + ", isKeyboardTrigger=" + isKeyboardTrigger + "]"; 666 } 667 } 668 669 private class LensDeviceEvent extends Event { 670 private int flags; 671 private boolean attach; 672 LensDeviceEvent(int flags, boolean attach) { 673 this.flags = flags; 674 this.attach = attach; 675 } 676 @Override 677 void dispatch() { 678 for (int i = 0; i <= DEVICE_MAX; i++) { 679 if ((flags & (1 << i)) != 0) { 680 if (attach) { 681 deviceFlags[i] ++; 682 } else { 683 deviceFlags[i] --; 684 } 685 } 686 } 687 688 if ((flags & (1 << DEVICE_POINTER)) != 0) { 689 // Turn on/off cursor image 690 if (attach && (deviceFlags[DEVICE_POINTER] == 1) && cursorVisible) { 691 LensCursor.setVisible_impl(true); 692 } else if (!attach && (deviceFlags[DEVICE_POINTER] == 0)) { 693 LensCursor.setVisible_impl(false); 694 } 695 } 696 } 697 } 698 699 private class LensScreenEvent extends Event { 700 @Override 701 void dispatch() { 702 Screen.notifySettingsChanged(); 703 } 704 } 705 706 private final LinkedList<Event> eventList = new LinkedList<Event>(); 707 708 private void postEvent(Event e) { 709 if (Thread.currentThread() == getEventThread()) { 710 try { 711 e.dispatch(); 712 } catch (Exception ex) { 713 reportException(ex); 714 } 715 } else { 716 synchronized (eventList) { 717 eventList.addLast(e); 718 eventList.notify(); 719 } 720 } 721 } 722 723 /** Posts a mouse event, filtering out sequences of similar motion or drag 724 * events. */ 725 private void postMouseEvent(LensView view, int eventType, 726 int x, int y, int absx, int absy, 727 int button, int modifiers, 728 boolean isPopupTrigger, boolean isSynthesized) { 729 synchronized (eventList) { 730 if (!eventList.isEmpty() && (eventType == MouseEvent.DRAG 731 || eventType == MouseEvent.MOVE)) { 732 Iterator <Event>events = eventList.descendingIterator(); 733 while (events.hasNext()) { 734 Event lastEvent = events.next(); 735 if (lastEvent instanceof LensMouseEvent) { 736 LensMouseEvent e = (LensMouseEvent) lastEvent; 737 if (e.target == view 738 && e.action == eventType 739 && e.button == button 740 && e.modifiers == modifiers 741 && e.isPopupTrigger == isPopupTrigger 742 && e.isSynthesized == isSynthesized) { 743 // rewrite the coordinates of the scheduled event with 744 // the coordinates of this event. 745 e.x = x; 746 e.y = y; 747 e.absx = absx; 748 e.absy = absy; 749 return; 750 } 751 } 752 } 753 } 754 postEvent(new LensMouseEvent(view, eventType, x, y, absx, absy, 755 button, modifiers, isPopupTrigger, 756 isSynthesized)); 757 } 758 } 759 760 private static class RunLoopControl { 761 boolean active; // thread should continue to process events. 762 Object release; // object to return with on leave nested 763 } 764 765 // our stack of nested run loops - note using LinkedList because we 766 // are already using that class for events. 767 LinkedList<RunLoopControl> activeRunLoops = new LinkedList<RunLoopControl>(); 768 769 @Override 770 protected Object _enterNestedEventLoop() { 771 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 772 LensLogger.getLogger().fine("_enterNestedEventLoop"); 773 } 774 775 // we are being called on the current active event thread 776 // via dispatch, so it is stalled until we return. 777 778 // start our nested loop, which will block until that exits 779 Object ret = _runLoop(); 780 781 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 782 LensLogger.getLogger().fine("Resuming event loop"); 783 } 784 785 // and return the value that was passed into leaveNested 786 return ret; 787 } 788 789 790 @Override 791 protected void _leaveNestedEventLoop(Object retValue) { 792 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 793 LensLogger.getLogger().fine("_leaveNestedEventLoop"); 794 } 795 796 // we are being called from dispatch of the current running 797 // event thread. We want to cause this thread to exit, and 798 // restart the nested on. 799 800 RunLoopControl current = activeRunLoops.pop(); 801 assert current != null; 802 803 // let the current run loop die when we return to dispatch. 804 current.active = false; 805 // and give it the ret object so it will return it to the 806 // blocked nesting call. 807 current.release = retValue; 808 809 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 810 LensLogger.getLogger().fine("_leaveNestedEventLoop"); 811 } 812 813 // when we return from this dispatched event, we will exit 814 // because we are no longer active, and then the nested 815 // call can return the release value we just provided. 816 817 } 818 819 private Object _runLoop() { 820 // Note: this method can be recursively called 821 // so do not add any platform initialization code here ! 822 823 final RunLoopControl control = new RunLoopControl(); 824 825 //push this new instance on the stack 826 activeRunLoops.push(control); 827 828 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 829 LensLogger.getLogger().fine("Starting event loop"); 830 } 831 832 control.active = true; 833 while (control.active) { 834 Event event; 835 synchronized (eventList) { 836 if (eventList.isEmpty()) { 837 try { 838 eventList.wait(); 839 } catch (InterruptedException e) { 840 continue; 841 } 842 } 843 if (eventList.isEmpty()) { 844 continue; 845 } 846 event = eventList.removeFirst(); 847 } 848 849 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 850 LensLogger.getLogger().fine("Processing " + event); 851 } 852 853 try { 854 event.dispatch(); 855 } catch (Exception e) { 856 reportException(e); 857 } 858 } 859 860 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 861 LensLogger.getLogger().fine("Leaving event loop"); 862 } 863 864 return control.release; 865 } 866 867 private static void registerEventLoop() { 868 synchronized (activeEventLoopLock) { 869 activeEventLoopThreads ++; 870 LensLogger.getLogger().info( 871 "activeEventLoopThreads := " + activeEventLoopThreads); 872 activeEventLoopLock.notifyAll(); 873 } 874 } 875 876 private static void unregisterEventLoop() { 877 synchronized (activeEventLoopLock) { 878 activeEventLoopThreads --; 879 LensLogger.getLogger().info( 880 "activeEventLoopThreads := " + activeEventLoopThreads); 881 activeEventLoopLock.notifyAll(); 882 } 883 } 884 885 private static void waitEventLoopsToFinish() { 886 synchronized (activeEventLoopLock) { 887 try { 888 LensLogger.getLogger().info("Waiting for all event loops to finish"); 889 while (activeEventLoopThreads > 0) { 890 LensLogger.getLogger().info( 891 "activeEventLoopThreads = " + activeEventLoopThreads); 892 activeEventLoopLock.wait(); 893 } 894 } catch (InterruptedException e) { 895 LensLogger.getLogger().severe("interrupted"); 896 } 897 } 898 } 899 900 @Override 901 protected void runLoop(Runnable launchable) { 902 _invokeLater(launchable); 903 long stackSize = AccessController.doPrivileged( 904 (PrivilegedAction<Long>) () -> Long.getLong("glass.lens.stackSize", 0)); 905 Thread toolkitThread = new Thread(new ThreadGroup("Events"), 906 () -> { 907 if (!_initialize()) { 908 LensLogger.getLogger().severe("Display failed initialization"); 909 throw new RuntimeException("Display failed initialization"); 910 } 911 _runLoop(); 912 }, "Lens Event Thread", stackSize); 913 setEventThread(toolkitThread); 914 toolkitThread.start(); 915 Runtime.getRuntime().addShutdownHook(new Thread() { 916 @Override public void run() { 917 shutdown(); 918 } 919 }); 920 } 921 922 private static int nativeThreadCounter = 0; 923 924 // Call into the native impl to start up any native device input queues 925 // needed. Native will upcall to createNativeEventThread to start any threads. 926 private native void nativeEventLoop(final LensApplication lensApp, 927 long nativeEventHandler, 928 long nativeWindow); 929 930 // Note: this Native event thread is designed to listen for events 931 // from native sources. For example, it may poll some native input devices. 932 // It is *not* the same as the FX event thred. 933 private static void createNativeEventThread(final long nativeEventHandler, 934 final long data) { 935 936 final LensApplication lensApplication = 937 (LensApplication)Application.GetApplication(); 938 939 Thread eventThread = new Thread(() -> { 940 registerEventLoop(); 941 lensApplication.nativeEventLoop(lensApplication, 942 nativeEventHandler, data); 943 944 //when the native function return 945 //event loop has exited 946 unregisterEventLoop(); 947 }, ("Lens Native Event Thread " + (nativeThreadCounter++))); 948 949 LensLogger.getLogger().info("Starting native event thread"); 950 951 eventThread.setDaemon(true); 952 eventThread.start(); 953 } 954 955 Object enterDnDEventLoop() { 956 dndEventLoop = createEventLoop(); 957 return dndEventLoop.enter(); 958 } 959 960 void leaveDndEventLoop(Object value) { 961 dndEventLoop.leave(value); 962 } 963 964 native void shutdown(); 965 966 @Override 967 protected void finishTerminating() { 968 LensLogger.getLogger().info("Finishing terminating"); 969 970 shutdown(); 971 synchronized (eventList) { 972 eventList.clear(); 973 while (!activeRunLoops.isEmpty()) { 974 RunLoopControl control = activeRunLoops.pop(); 975 control.active = false; 976 } 977 eventList.notify(); 978 } 979 980 super.finishTerminating(); 981 } 982 983 @Override 984 protected boolean _supportsTransparentWindows() { 985 return true; 986 } 987 988 @Override protected boolean _supportsUnifiedWindows() { 989 return false; 990 } 991 992 //******************************************************************* 993 // Runloop/Event queue support additions 994 995 //Window events 996 997 /** 998 * handles the following window events WindowEvent.MINIMIZE / 999 * MAXIMIZE /RESTORE / RESIZE 1000 * See Window.java::notifyResize() for more information 1001 * 1002 * @param window the window object which this event belongs to 1003 * @param eventType WindowEvent.MINIMIZE / MAXIMIZE /RESTORE / 1004 * RESIZE 1005 * @param width new width 1006 * @param height new height 1007 */ 1008 protected void notifyWindowResize(LensWindow window, 1009 int eventType, 1010 int width, int height) { 1011 if (LensLogger.getLogger().isLoggable(Level.INFO)) { 1012 LensLogger.getLogger().info( 1013 "notifyResize with "+WindowEvent.getEventName(eventType)+ 1014 " event "+ window + " to " + width + "x" + height); 1015 } 1016 if (window != null) { 1017 postEvent(new LensWindowEvent(LensWindowEvent.EType.RESIZE, 1018 window, 1019 eventType, 0, 0, 1020 width, height)); 1021 } 1022 } 1023 1024 /** 1025 * Notify JFX that the window have been moved and provide the 1026 * new coordinates. 1027 * 1028 * This notification can be either response for JFX request to 1029 * change the window location, or user have changed it. The 1030 * later case will happen when window a manager is used. 1031 * 1032 * @param window The window which this event belongs to 1033 * @param x new X coordinate of the window 1034 * @param y new Y Coordinate of the window 1035 */ 1036 protected void notifyWindowMove(LensWindow window, int x, int y) { 1037 if (LensLogger.getLogger().isLoggable(Level.INFO)) { 1038 LensLogger.getLogger().info( 1039 "Move " + window + " to " + x + "," + y); 1040 } 1041 postEvent(new LensWindowEvent(LensWindowEvent.EType.MOVE, 1042 window, 1043 WindowEvent.MOVE, 1044 x, y, 1045 0, 0)); 1046 } 1047 1048 /** 1049 * This notification informs JFX on window events that doesn't 1050 * requires additional information for handling the 1051 * notification. For example window have gained/lost focus. 1052 * 1053 * @param window The window which this event belongs to 1054 * @param windowEvent the event type as defined in WindowEvent 1055 * class. 1056 */ 1057 protected void notifyWindowEvent(LensWindow window, int windowEvent) { 1058 1059 1060 LensWindowEvent.EType etype = null; 1061 switch (windowEvent) { 1062 case WindowEvent.FOCUS_GAINED: 1063 etype = LensWindowEvent.EType.FOCUS; 1064 break; 1065 case WindowEvent.FOCUS_LOST: 1066 etype = LensWindowEvent.EType.FOCUS; 1067 break; 1068 case WindowEvent.DESTROY: 1069 etype = LensWindowEvent.EType.DESTROY; 1070 break; 1071 case WindowEvent.CLOSE: 1072 etype = LensWindowEvent.EType.CLOSE; 1073 break; 1074 case WindowEvent.FOCUS_UNGRAB: 1075 etype = LensWindowEvent.EType.UNGRAB; 1076 break; 1077 case WindowEvent.FOCUS_DISABLED: 1078 etype = LensWindowEvent.EType.FOCUS_DISABLED; 1079 break; 1080 default: 1081 LensLogger.getLogger().warning("Unsupported event type ("+ 1082 WindowEvent.getEventName(windowEvent)+") skipping event"); 1083 return; 1084 } 1085 1086 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1087 LensLogger.getLogger().fine( 1088 "notifyWindowEvent eventType = " + 1089 WindowEvent.getEventName(windowEvent)); 1090 } 1091 1092 if (etype != null) { 1093 postEvent(new LensWindowEvent(etype, window, windowEvent)); 1094 } 1095 } 1096 1097 protected void windowExpose(LensWindow window, int x, int y, int width, 1098 int height) { 1099 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1100 LensLogger.getLogger().fine( 1101 "Expose " + window + " " 1102 + x + "," + y + "+" + width + "x" + height); 1103 } 1104 postEvent(new LensWindowEvent(LensWindowEvent.EType.EXPOSE, 1105 window, WindowEvent.RESIZE, 1106 x, y, width, height)); 1107 } 1108 1109 1110 //Input events 1111 1112 1113 /** 1114 * Notify key event from native layer 1115 * 1116 * @param view the window which the view is related to 1117 * @param type event type (KeyEvent.PRESS ...) 1118 * @param keyCode key code for the event (KeyEvent.VK_*) 1119 * @param modifiers bit mask of key modifiers 1120 * (KeyEvent.MODIFIER_*) 1121 * @param chars char sequence buffer. can be 0 length, must not 1122 * be null 1123 */ 1124 private void notifyKeyEvent(LensView view, int type , int keyCode, 1125 int modifiers, char[] chars) { 1126 try { 1127 if (LensLogger.getLogger().isLoggable(Level.FINER)) { 1128 LensLogger.getLogger().finer("Key event on " + view); 1129 } 1130 postEvent(new LensKeyEvent(view, type, keyCode, 1131 modifiers , chars)); 1132 } catch (Exception e) { 1133 reportException(e); 1134 } 1135 } 1136 1137 /** 1138 * Notify mouse event from native layer 1139 * 1140 * @param view the window which the view is related to 1141 * @param eventType one of MouseEvent constants 1142 * @param x location of event inside the view 1143 * @param y location of event inside the view 1144 * @param absx location of event on the screen 1145 * @param absy location of event on the screen 1146 * @param button currently pressed button, required only in applicable events 1147 * such as MouseEvent.DOWN 1148 * @param modifiers mask of currently pressed special keys and mouse 1149 * buttons. always required 1150 * @param isPopupTrigger true when event is context menu hint (usually right 1151 * button release) 1152 * @param isSynthesized used when event is logical such MouseEvent.CLICK 1153 */ 1154 1155 void notifyMouseEvent(LensView view, int eventType, 1156 int x, int y, int absx, int absy, 1157 int button, int modifiers, 1158 boolean isPopupTrigger, boolean isSynthesized) { 1159 1160 try { 1161 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1162 LensLogger.getLogger().finest("Mouse event on " + view); 1163 } 1164 1165 //continue process events only if not already consumed 1166 if (!handleDragEvents(view, eventType, x, y, absx, absy, button, modifiers)) { 1167 postMouseEvent(view, eventType, x, y, absx, absy, 1168 button, modifiers, 1169 isPopupTrigger, isSynthesized); 1170 } 1171 } catch (Exception e) { 1172 reportException(e); 1173 } 1174 } 1175 1176 ////// Drag And Drop Support 1177 1178 //used to mark the button which triggered the drag - when its release 1179 //the drag ends 1180 private int cachedButtonPressed = MouseEvent.BUTTON_NONE; 1181 //The View that the drag is currently hoover above 1182 private LensView dragView = null; 1183 //mask of operations done on current drag event 1184 //mask values are ORed from DragActions enum 1185 private int dragActionsPreformed = DragActions.NONE.getValue(); 1186 1187 //Mark if drag processing have started by upper levels, 1188 //value changes when notifyDragStart is called 1189 private boolean dragStarted = false; 1190 1191 1192 //Possible actioins for drag events 1193 private enum DragActions { 1194 NONE(0, "NONE"), 1195 ENTER(1 << 1, "ENTER"), 1196 LEAVE(1 << 2, "LEAVE"), 1197 OVER(1 << 3, "OVER"), 1198 DROP(1 << 4, "DROP"); 1199 public int value; 1200 private String name; 1201 DragActions(int value, String name) { 1202 this.value = value; 1203 this.name = name; 1204 } 1205 1206 public int getValue() { 1207 return value; 1208 } 1209 1210 @Override 1211 public String toString() { 1212 return name; 1213 } 1214 1215 }; 1216 1217 private native void _notfyPlatformDnDStarted(); 1218 private native void _notfyPlatformDnDEnded(); 1219 /** 1220 * This function should be only called from LensDnDClipboard after it has 1221 * been initialized. 1222 * After this method have called it meand that we are inside nested event 1223 * loop, which is responsible to handle all drag events, all other mouse 1224 * events are discarded until Drag Drop is detected. 1225 */ 1226 void notifyDragStart() { 1227 _notfyPlatformDnDStarted(); 1228 dragStarted = true; 1229 } 1230 /** 1231 * Transforms mouse events into drag events when drag detected. When this 1232 * functions returns true, it means that the event is been consumed by this 1233 * method and no further processing is required 1234 * 1235 * @param view the view which owns the event 1236 * @param eventType type of event, one of MouseEvent constants 1237 * @param x location of event inside the view 1238 * @param y location of event inside the view 1239 * @param absx location of event on the screen 1240 * @param absy location of event on the screen 1241 * @param button currently pressed button, required only in applicable 1242 * events such as MouseEvent.DOWN 1243 * @param modifiers mask of currently pressed special keys and mouse 1244 * buttons. always required 1245 * @return boolean true if event has been consumed and no further processing 1246 * required 1247 */ 1248 private boolean handleDragEvents(LensView view, int eventType, 1249 int x, int y, int absx, int absy, 1250 int button, int modifiers) { 1251 boolean eventConsumed = false; 1252 1253 if (eventType == MouseEvent.DOWN && cachedButtonPressed == MouseEvent.BUTTON_NONE) { 1254 //save the button that might have started the drag event 1255 cachedButtonPressed = button; 1256 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1257 LensLogger.getLogger().finest("Caching mouse button - " + button); 1258 } 1259 } else if (eventType == MouseEvent.UP && button == cachedButtonPressed) { 1260 //reset cached button on mouse up 1261 cachedButtonPressed = MouseEvent.BUTTON_NONE; 1262 1263 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1264 LensLogger.getLogger().finest("reset mouse button cache " + button); 1265 } 1266 if (dragStarted) { 1267 //drag button has been released while drag is active = drop 1268 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1269 LensLogger.getLogger().finest("notifying drag DROP"); 1270 } 1271 postEvent(new LensDragEvent(view, x, y, absx, absy, DragActions.DROP)); 1272 1273 //notify platform DnD ended 1274 _notfyPlatformDnDEnded(); 1275 1276 //reset internal state machine 1277 dragActionsPreformed = DragActions.NONE.getValue(); 1278 dragView = null; 1279 dragStarted = false; 1280 } 1281 } else if (eventType == MouseEvent.MOVE && 1282 cachedButtonPressed != MouseEvent.BUTTON_NONE && 1283 ((modifiers & KeyEvent.MODIFIER_BUTTON_PRIMARY) == KeyEvent.MODIFIER_BUTTON_PRIMARY || 1284 (modifiers & KeyEvent.MODIFIER_BUTTON_MIDDLE) == KeyEvent.MODIFIER_BUTTON_MIDDLE || 1285 (modifiers & KeyEvent.MODIFIER_BUTTON_SECONDARY) == KeyEvent.MODIFIER_BUTTON_SECONDARY)) { 1286 //move + mouse button pressed = drag 1287 1288 if (dragStarted) { 1289 //consume all event after drag have started 1290 eventConsumed = true; 1291 1292 //drag has been initiated - handle drag drop/over/enter/leave events 1293 if (dragView == view && 1294 dragActionsPreformed == DragActions.NONE.getValue()) { 1295 //first notification 1296 postEvent(new LensDragEvent(view, x, y, absx, absy, DragActions.ENTER)); 1297 dragActionsPreformed |= DragActions.ENTER.getValue(); 1298 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1299 LensLogger.getLogger().finest("Notifying DragEnter"); 1300 } 1301 } else if (dragView == view && 1302 (dragActionsPreformed & DragActions.ENTER.getValue()) == DragActions.ENTER.getValue()) { 1303 //view was notified that drag has entered to it 1304 //now we need to send DragOver notification 1305 postEvent(new LensDragEvent(view, x, y, absx, absy, DragActions.OVER)); 1306 dragActionsPreformed |= DragActions.OVER.getValue(); 1307 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1308 LensLogger.getLogger().finest("Notifying DragOver"); 1309 } 1310 } else if (dragView != view) { 1311 //drag was moved to another view, leave the old one and 1312 //reset the actions flags and dragView, 1313 //also notify the new view for dragEnter 1314 1315 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1316 LensLogger.getLogger().finest("Notifying DragLeave old view"); 1317 } 1318 postEvent(new LensDragEvent(dragView, x, y, absx, absy, DragActions.LEAVE)); 1319 1320 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1321 LensLogger.getLogger().finest("Notifying DragEnter new view"); 1322 } 1323 postEvent(new LensDragEvent(view, x, y, absx, absy, DragActions.ENTER)); 1324 1325 dragActionsPreformed = DragActions.ENTER.getValue(); 1326 dragView = view; 1327 } 1328 1329 } else { 1330 eventType = MouseEvent.DRAG; 1331 if (dragView == null) { 1332 //cache the view that the drag started on 1333 dragView = view; 1334 } 1335 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1336 LensLogger.getLogger().finest("Drag detected - sending DRAG event"); 1337 } 1338 postMouseEvent(view, eventType, x, y, absx, absy, 1339 button, modifiers, 1340 false /*isPopupTrigger*/, 1341 false /*isSynthesized*/); 1342 eventConsumed = true; 1343 1344 } 1345 } 1346 1347 return eventConsumed; 1348 } 1349 1350 1351 /** 1352 * Notify scroll event from native layer 1353 * 1354 * @param view the window which the view is related to 1355 * @param x 1356 * @param y 1357 * @param absx 1358 * @param absy 1359 * @param deltaX 1360 * @param deltaY 1361 * @param modifiers 1362 * @param lines 1363 * @param chars 1364 * @param defaultLines 1365 * @param defaultChars 1366 * @param xMultiplier 1367 * @param yMultiplier 1368 * @param modifiers 1369 */ 1370 private void notifyScrollEvent(LensView view, 1371 int x, int y, int absx, int absy, 1372 double deltaX, double deltaY, int modifiers, 1373 int lines, int chars, int defaultLines, 1374 int defaultChars, double xMultiplier, 1375 double yMultiplier) { 1376 1377 try { 1378 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1379 LensLogger.getLogger().fine("Scroll event on " + view); 1380 } 1381 1382 postEvent(new LensScrollEvent(view, x, y, absx, absy, 1383 deltaX, deltaY, modifiers, 1384 lines, chars, defaultLines, 1385 defaultChars, xMultiplier, 1386 yMultiplier)); 1387 } catch (Exception e) { 1388 reportException(e); 1389 } 1390 } 1391 1392 /** 1393 * Posts the touch move event. This method will inspect to see if the previous event on the queue is another 1394 * TouchMoveEvent with the same view and id. If so, it will update that event with the new x, y, absX and absY 1395 * so as to reduce the number of events on the queue. 1396 * 1397 * @param view the view which owns the event 1398 * @param id the id of the finger slot (usually 1) 1399 * @param x the x coordinate of the event 1400 * @param y the y coordinate of the event 1401 * @param absX not sure 1402 * @param absY not sure 1403 */ 1404 private void postTouchMoveEvent(LensView view, long id, int x, int y, int absX, int absY) { 1405 synchronized (eventList) { 1406 boolean isEventCompacted = false; 1407 if (!eventList.isEmpty()) { 1408 Iterator <Event>events = eventList.descendingIterator(); 1409 while (events.hasNext()) { 1410 Event lastEvent = events.next(); 1411 if (lastEvent instanceof LensTouchEvent) { 1412 LensTouchEvent e = (LensTouchEvent) lastEvent; 1413 if (e.view == view && 1414 e.id == id && 1415 (e.state == TouchEvent.TOUCH_MOVED || e.state == TouchEvent.TOUCH_STILL)) { 1416 // Rewrite the coordinates of the scheduled event 1417 // with the coordinates of this event. 1418 e.x = x; 1419 e.y = y; 1420 e.absX = absX; 1421 e.absY = absY; 1422 isEventCompacted = true; 1423 } 1424 //we have found the last touch event on the queue, break 1425 break; 1426 } else if (lastEvent instanceof LensMultiTouchEvent) { 1427 //another touch event type was posted, can't update event 1428 break; 1429 } 1430 } 1431 } 1432 1433 if (!isEventCompacted) { 1434 postEvent(new LensTouchEvent(view, TouchEvent.TOUCH_MOVED, id, x, y, absX, absY)); 1435 } 1436 1437 } 1438 } 1439 1440 /** 1441 * Notify touch event from native layer 1442 * 1443 * @param view the window which the view is related to 1444 * @param state the finger state (e.g. TouchEvent.TOUCH_PRESSED) 1445 * @param id the id of the finger slot 1446 * @param x the x coordinate relative to the view origin 1447 * @param y the y coordinate relative to the view origin 1448 * @param absX the x coordinate relative to the screen origin 1449 * @param absY the y coordinate relative to the screen origin 1450 */ 1451 private void notifyTouchEvent(LensView view, int state, long id, 1452 int x, int y, int absX, int absY) { 1453 try { 1454 final boolean hadPreviousTouchMove = previousTouchMoveScreenX >= 0; 1455 if (state == TouchEvent.TOUCH_MOVED) { 1456 postTouchMoveEvent(view, id, x, y, absX, absY); 1457 } else { 1458 postEvent(new LensTouchEvent(view, state, id, x, y, absX, absY)); 1459 } 1460 1461 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1462 LensLogger.getLogger().fine("Touch event " 1463 + state + " at " 1464 + x + "," + y 1465 + " on " + view); 1466 } 1467 } catch (Exception e) { 1468 reportException(e); 1469 } 1470 } 1471 1472 /** 1473 * Notify multitouch event from native layer 1474 * 1475 * @param view the window which the view is related to 1476 * @param states list of the finger states of each touch point 1477 * @param ids list of the IDs of each touch point 1478 * @param xs list of of the absolute X coordinates of each touch point 1479 * @param ys list of of the absolute Y coordinates of each touch point 1480 * @param dx value to be added to X coordinates to convert them to relative 1481 * @param dy value to be added to Y coordinates to convert them to relative 1482 */ 1483 private void notifyMultiTouchEvent(LensView view, int[] states, long[] ids, 1484 int[] xs, int[] ys, int dx, int dy) { 1485 try { 1486 1487 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1488 LensLogger.getLogger().fine("MultiTouch event with " 1489 + states.length + " points " 1490 + " on " + view); 1491 } 1492 synchronized (eventList) { 1493 // Try to match this multitouch event against a the last multi touch 1494 // pending event on the queue, if they match and event type is move, 1495 // update the existing event with the new values instead of posting a new event 1496 // This code assume that the order of touch points doesn't 1497 //change between events 1498 boolean match = false; 1499 if (!eventList.isEmpty()) { 1500 Iterator <Event>events = eventList.descendingIterator(); 1501 while (events.hasNext()) { 1502 Event lastEvent = events.next(); 1503 if (lastEvent instanceof LensMultiTouchEvent) { 1504 LensMultiTouchEvent e = (LensMultiTouchEvent) lastEvent; 1505 if (e.view == view 1506 && e.states.length == states.length) { 1507 assert(states.length == ids.length); 1508 assert(e.states.length == e.ids.length); 1509 match = true; 1510 for (int i = 0; i < states.length && match; i++) { 1511 //check if event is motion related 1512 if ((e.states[i] != TouchEvent.TOUCH_MOVED && 1513 e.states[i] != TouchEvent.TOUCH_STILL) || 1514 (states[i] != TouchEvent.TOUCH_MOVED && 1515 states[i] != TouchEvent.TOUCH_STILL)) { 1516 match = false; 1517 } 1518 if (e.ids[i] != ids[i]) { 1519 match = false; 1520 } 1521 } 1522 if (match) { 1523 // rewrite the coordinates of the scheduled event 1524 // with the coordinates of this event. 1525 e.xs = xs; 1526 e.ys = ys; 1527 e.states = states; 1528 e.dx = dx; 1529 e.dy = dy; 1530 } 1531 } 1532 //we have found the last touch event on the queue, break 1533 break; 1534 } else if (lastEvent instanceof LensTouchEvent) { 1535 //another touch event type was posted, can't update event 1536 break; 1537 } 1538 } 1539 } 1540 if (!match) { 1541 postEvent(new LensMultiTouchEvent(view, states, ids, 1542 xs, ys, dx, dy)); 1543 } 1544 } 1545 1546 } catch (Exception e) { 1547 reportException(e); 1548 } 1549 } 1550 1551 1552 1553 /** 1554 * Notify view event from native 1555 * View events are one of the events listed in ViewEvent.java 1556 * 1557 * @param view the event occurred in 1558 * @param viewEventType the type of event as listed in 1559 * ViewEvent.java 1560 */ 1561 private void notifyViewEvent(LensView view, int viewEventType, 1562 int x, int y, int width, int height) { 1563 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1564 LensLogger.getLogger().fine( 1565 "Notify event type " 1566 + ViewEvent.getTypeString(viewEventType) 1567 + " on " + view); 1568 } 1569 1570 postEvent(new LensViewEvent(view, viewEventType, 1571 x, y, width, height)); 1572 } 1573 1574 private void notifyMenuEvent(LensView view, int x, int y, int xAbs, 1575 int yAbs, boolean isKeyboardTrigger) { 1576 if (LensLogger.getLogger().isLoggable(Level.FINER)) { 1577 LensLogger.getLogger().finer( 1578 "Notify menu event " + 1579 "x=" + x + ", y=" + y + ", xAbs=" + xAbs + ", yAbs=" + yAbs + 1580 ", isKeyboardTrigger " + isKeyboardTrigger + 1581 ", on " + view); 1582 } 1583 if (view != null) { 1584 postEvent(new LensMenuEvent(view, x, y, xAbs, yAbs, isKeyboardTrigger)); 1585 } else { 1586 if (LensLogger.getLogger().isLoggable(Level.FINER)) { 1587 LensLogger.getLogger().finer("view is null, skipping event"); 1588 } 1589 } 1590 } 1591 1592 /** 1593 * Notify device event from native 1594 * A device event is sent when an input device is attached or detached. 1595 * 1596 * @param flags the device type flags (a bitmask containing values up to 2^DEVICE_MAX) 1597 * @param attach true is the device was attached, false if it was detached. 1598 */ 1599 private void notifyDeviceEvent(int flags, boolean attach) { 1600 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1601 LensLogger.getLogger().fine( 1602 "Notify device event attach=" + attach 1603 + ", flags=0x" + Integer.toHexString(flags)); 1604 } 1605 1606 postEvent(new LensDeviceEvent(flags, attach)); 1607 } 1608 1609 /** 1610 * Notify changes in screen settings from native layer 1611 * This method is triggered when native code detects a change in e.g. screen orientation 1612 */ 1613 private void notifyScreenSettingsChanged() { 1614 if (LensLogger.getLogger().isLoggable(Level.FINE)) { 1615 LensLogger.getLogger().fine("Notify screen settings changed"); 1616 } 1617 postEvent(new LensScreenEvent()); 1618 } 1619 1620 //******************************************************************* 1621 1622 public void installWindowMenu(MenuBar menubar) { 1623 // not sure what menubars look like on an embedded device yet. 1624 this.windowMenu = createMenu("Window"); 1625 } 1626 1627 public Menu getWindowMenu() { 1628 return this.windowMenu; 1629 } 1630 1631 @Override 1632 public void installDefaultMenus(MenuBar menubar) { 1633 } 1634 1635 // FACTORY METHODS 1636 @Override 1637 public Window createWindow(Window owner, Screen screen, int styleMask) { 1638 return new LensWindow(owner, screen, styleMask); 1639 } 1640 1641 @Override 1642 public Window createWindow(long parent) { 1643 return new LensWindow(parent); 1644 } 1645 1646 @Override 1647 public View createView() { 1648 return new LensView(); 1649 } 1650 1651 @Override 1652 public Cursor createCursor(int type) { 1653 return new LensCursor(type); 1654 } 1655 1656 @Override 1657 public Cursor createCursor(int x, int y, Pixels pixels) { 1658 return new LensCursor(x, y, pixels); 1659 } 1660 1661 @Override 1662 protected void staticCursor_setVisible(boolean visible) { 1663 cursorVisible = visible; 1664 if (deviceFlags[DEVICE_POINTER] >= 1) { 1665 LensCursor.setVisible_impl(visible); 1666 } 1667 } 1668 1669 @Override 1670 protected Size staticCursor_getBestSize(int width, int height) { 1671 return LensCursor.getBestSize_impl(width, height); 1672 } 1673 1674 @Override 1675 public Pixels createPixels(int width, int height, ByteBuffer data) { 1676 return new LensPixels(width, height, data); 1677 } 1678 1679 @Override 1680 public Pixels createPixels(int width, int height, IntBuffer data) { 1681 return new LensPixels(width, height, data); 1682 } 1683 1684 @Override 1685 public Pixels createPixels(int width, int height, IntBuffer data, float scalex, float scaley) { 1686 return new LensPixels(width, height, data, scalex, scaley); 1687 } 1688 1689 @Override 1690 protected int staticPixels_getNativeFormat() { 1691 return LensPixels.getNativeFormat_impl(); 1692 } 1693 1694 @Override 1695 public Robot createRobot() { 1696 return new LensRobot(); 1697 } 1698 1699 @Override protected double staticScreen_getVideoRefreshPeriod() { 1700 return 0.0; // indicate millisecond resolution 1701 } 1702 1703 @Override native protected Screen[] staticScreen_getScreens(); 1704 1705 @Override 1706 public Timer createTimer(Runnable runnable) { 1707 return new LensTimer(runnable); 1708 } 1709 1710 @Override 1711 protected int staticTimer_getMinPeriod() { 1712 return LensTimer.getMinPeriod_impl(); 1713 } 1714 1715 @Override 1716 protected int staticTimer_getMaxPeriod() { 1717 return LensTimer.getMaxPeriod_impl(); 1718 } 1719 1720 @Override protected FileChooserResult 1721 staticCommonDialogs_showFileChooser(Window owner, String folder, 1722 String filename, 1723 String title, int type, 1724 boolean multipleMode, 1725 ExtensionFilter[] extensionFilters, int defaultFilterIndex) { 1726 // FileChooser APIs should return null when they cannot show a dialog 1727 return null; 1728 } 1729 1730 @Override 1731 protected File staticCommonDialogs_showFolderChooser(Window owner, 1732 String folder, 1733 String title) { 1734 // FileChooser APIs should return null when they cannot show a dialog 1735 return null; 1736 } 1737 1738 @Override protected long staticView_getMultiClickTime() { 1739 return LensView._getMultiClickTime(); 1740 } 1741 1742 @Override protected int staticView_getMultiClickMaxX() { 1743 return LensView._getMultiClickMaxX(); 1744 } 1745 1746 @Override protected int staticView_getMultiClickMaxY() { 1747 return LensView._getMultiClickMaxY(); 1748 } 1749 1750 @Override 1751 protected void _invokeAndWait(Runnable runnable) { 1752 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1753 LensLogger.getLogger().fine("invokeAndWait " + runnable); 1754 } 1755 synchronized (invokeAndWaitLock) { 1756 waitingFor = runnable; 1757 } 1758 synchronized (eventList) { 1759 eventList.addLast(new RunnableEvent(true, runnable)); 1760 eventList.notify(); 1761 } 1762 synchronized (invokeAndWaitLock) { 1763 while (waitingFor == runnable) { 1764 try { 1765 invokeAndWaitLock.wait(); 1766 } catch (InterruptedException ex) { 1767 } 1768 } 1769 } 1770 } 1771 1772 @Override 1773 protected void _invokeLater(Runnable runnable) { 1774 if (LensLogger.getLogger().isLoggable(Level.FINEST)) { 1775 LensLogger.getLogger().fine("invokeLater " + runnable); 1776 } 1777 synchronized (eventList) { 1778 eventList.addLast(new RunnableEvent(false, runnable)); 1779 eventList.notify(); 1780 } 1781 } 1782 1783 public boolean hasTwoLevelFocus() { 1784 return deviceFlags[DEVICE_PC_KEYBOARD] == 0 && deviceFlags[DEVICE_5WAY] > 0; 1785 } 1786 1787 public boolean hasVirtualKeyboard() { 1788 return deviceFlags[DEVICE_PC_KEYBOARD] == 0 && deviceFlags[DEVICE_TOUCH] > 0; 1789 } 1790 1791 public boolean hasTouch() { 1792 return deviceFlags[DEVICE_TOUCH] > 0; 1793 } 1794 1795 public boolean hasMultiTouch() { 1796 return deviceFlags[DEVICE_MULTITOUCH] > 0; 1797 } 1798 1799 public boolean hasPointer() { 1800 return deviceFlags[DEVICE_POINTER] > 0; 1801 } 1802 1803 @Override 1804 protected native int _getKeyCodeForChar(char c); 1805 1806 }