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 }