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.gtk;
  26 
  27 import com.sun.glass.ui.Application;
  28 import com.sun.glass.ui.CommonDialogs.ExtensionFilter;
  29 import com.sun.glass.ui.CommonDialogs.FileChooserResult;
  30 import com.sun.glass.ui.Cursor;
  31 import com.sun.glass.ui.InvokeLaterDispatcher;
  32 import com.sun.glass.ui.Pixels;
  33 import com.sun.glass.ui.Robot;
  34 import com.sun.glass.ui.Screen;
  35 import com.sun.glass.ui.Size;
  36 import com.sun.glass.ui.Timer;
  37 import com.sun.glass.ui.View;
  38 import com.sun.glass.ui.Window;
  39 
  40 import java.io.File;
  41 import java.nio.ByteBuffer;
  42 import java.nio.IntBuffer;
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 import java.util.Map;
  46 import java.util.concurrent.CountDownLatch;
  47 
  48 final class GtkApplication extends Application implements InvokeLaterDispatcher.InvokeLaterSubmitter {
  49 
  50     static {
  51         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
  52             Application.loadNativeLibrary();
  53             return null;
  54         });
  55     }
  56 
  57     public static  int screen = -1;
  58     public static  long display = 0;
  59     public static  long visualID = 0;
  60 
  61     private final InvokeLaterDispatcher invokeLaterDispatcher;
  62 
  63     GtkApplication() {
  64         // Check whether the Display is valid and throw an exception if not.
  65         // We use UnsupportedOperationException rather than HeadlessException
  66         // so as not to introduce a dependency on AWT.
  67         if (!isDisplayValid()) {
  68             throw new UnsupportedOperationException("Unable to open DISPLAY");
  69         }
  70 
  71         // Embedded in SWT, with shared event thread
  72         boolean isEventThread = AccessController
  73                 .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.embed.isEventThread"));
  74         if (!isEventThread) {
  75             invokeLaterDispatcher = new InvokeLaterDispatcher(this);
  76             invokeLaterDispatcher.start();
  77         } else {
  78             invokeLaterDispatcher = null;
  79         }
  80     }
  81 
  82     private static boolean isDisplayValid() {
  83         return _isDisplayValid();
  84     }
  85 
  86     private void initDisplay() {
  87         Map ds = getDeviceDetails();
  88         if (ds != null) {
  89             Object value;
  90             value = ds.get("XDisplay");
  91             if (value != null) {
  92                 display = (Long)value;
  93             }
  94             value = ds.get("XVisualID");
  95             if (value != null) {
  96                 visualID = (Long)value;
  97             }
  98             value = ds.get("XScreenID");
  99             if (value != null) {
 100                 screen = (Integer)value;
 101             }
 102         }
 103     }
 104 
 105     private void init() {
 106         initDisplay();
 107         long eventProc = 0;
 108         Map map = getDeviceDetails();
 109         if (map != null) {
 110             Long result = (Long) map.get("javafx.embed.eventProc");
 111             eventProc = result == null ? 0 : result;
 112         }
 113 
 114         final boolean disableGrab = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("sun.awt.disablegrab") ||
 115                Boolean.getBoolean("glass.disableGrab"));
 116 
 117         _init(eventProc, disableGrab);
 118     }
 119 
 120     @Override
 121     protected void runLoop(final Runnable launchable) {
 122         // Embedded in SWT, with shared event thread
 123         final boolean isEventThread = AccessController
 124             .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.embed.isEventThread"));
 125 
 126         if (isEventThread) {
 127             init();
 128             setEventThread(Thread.currentThread());
 129             launchable.run();
 130             return;
 131         }
 132 
 133         final boolean noErrorTrap = AccessController
 134             .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("glass.noErrorTrap"));
 135 
 136         final Thread toolkitThread =
 137             AccessController.doPrivileged((PrivilegedAction<Thread>) () -> new Thread(() -> {
 138                 init();
 139                 _runLoop(launchable, noErrorTrap);
 140             }, "GtkNativeMainLoopThread"));
 141         setEventThread(toolkitThread);
 142         toolkitThread.start();
 143     }
 144 
 145     @Override
 146     protected void finishTerminating() {
 147         final Thread toolkitThread = getEventThread();
 148         if (toolkitThread != null) {
 149             _terminateLoop();
 150             setEventThread(null);
 151         }
 152         super.finishTerminating();
 153     }
 154 
 155     @Override public boolean shouldUpdateWindow() {
 156         return true;
 157     }
 158 
 159     private static native boolean _isDisplayValid();
 160 
 161     private native void _terminateLoop();
 162 
 163     private native void _init(long eventProc, boolean disableGrab);
 164 
 165     private native void _runLoop(Runnable launchable, boolean noErrorTrap);
 166 
 167     @Override
 168     protected void _invokeAndWait(final Runnable runnable) {
 169         if (invokeLaterDispatcher != null) {
 170             invokeLaterDispatcher.invokeAndWait(runnable);
 171         } else {
 172             final CountDownLatch latch = new CountDownLatch(1);
 173             submitForLaterInvocation(() -> {
 174                 if (runnable != null) runnable.run();
 175                 latch.countDown();
 176             });
 177             try {
 178                 latch.await();
 179             } catch (InterruptedException e) {
 180                 //FAIL SILENTLY
 181             }
 182         }
 183     }
 184 
 185     private native void _submitForLaterInvocation(Runnable r);
 186     // InvokeLaterDispatcher.InvokeLaterSubmitter
 187     @Override public void submitForLaterInvocation(Runnable r) {
 188         _submitForLaterInvocation(r);
 189     }
 190 
 191     @Override protected void _invokeLater(Runnable runnable) {
 192         if (invokeLaterDispatcher != null) {
 193             invokeLaterDispatcher.invokeLater(runnable);
 194         } else {
 195             submitForLaterInvocation(runnable);
 196         }
 197     }
 198 
 199     private Object eventLoopExitEnterPassValue;
 200 
 201     private native void enterNestedEventLoopImpl();
 202 
 203     private native void leaveNestedEventLoopImpl();
 204 
 205     @Override
 206     protected Object _enterNestedEventLoop() {
 207         if (invokeLaterDispatcher != null) {
 208             invokeLaterDispatcher.notifyEnteringNestedEventLoop();
 209         }
 210         try {
 211             enterNestedEventLoopImpl();
 212             final Object retValue = eventLoopExitEnterPassValue;
 213             eventLoopExitEnterPassValue = null;
 214             return retValue;
 215         } finally {
 216             if (invokeLaterDispatcher != null) {
 217                 invokeLaterDispatcher.notifyLeftNestedEventLoop();
 218             }
 219         }
 220     }
 221 
 222     @Override
 223     protected void _leaveNestedEventLoop(Object retValue) {
 224         if (invokeLaterDispatcher != null) {
 225             invokeLaterDispatcher.notifyLeavingNestedEventLoop();
 226         }
 227         eventLoopExitEnterPassValue = retValue;
 228         leaveNestedEventLoopImpl();
 229     }
 230 
 231     @Override
 232     public Window createWindow(Window owner, Screen screen, int styleMask) {
 233         return new GtkWindow(owner, screen, styleMask);
 234     }
 235 
 236     @Override
 237     public Window createWindow(long parent) {
 238         return new GtkChildWindow(parent);
 239     }
 240 
 241     @Override
 242     public View createView() {
 243         return new GtkView();
 244     }
 245 
 246     @Override
 247     public Cursor createCursor(int type) {
 248         return new GtkCursor(type);
 249     }
 250 
 251     @Override
 252     public Cursor createCursor(int x, int y, Pixels pixels) {
 253         return new GtkCursor(x, y, pixels);
 254     }
 255 
 256     @Override
 257     protected void staticCursor_setVisible(boolean visible) {
 258     }
 259 
 260     @Override
 261     protected Size staticCursor_getBestSize(int width, int height) {
 262         return GtkCursor._getBestSize(width, height);
 263     }
 264 
 265     @Override
 266     public Pixels createPixels(int width, int height, ByteBuffer data) {
 267         return new GtkPixels(width, height, data);
 268     }
 269 
 270     @Override
 271     public Pixels createPixels(int width, int height, IntBuffer data) {
 272         return new GtkPixels(width, height, data);
 273     }
 274 
 275     @Override
 276     public Pixels createPixels(int width, int height, IntBuffer data, float scale) {
 277         return new GtkPixels(width, height, data, scale);
 278     }
 279 
 280     @Override
 281     protected int staticPixels_getNativeFormat() {
 282         return Pixels.Format.BYTE_BGRA_PRE; // TODO
 283     }
 284 
 285     @Override
 286     public Robot createRobot() {
 287         return new GtkRobot();
 288     }
 289 
 290     @Override
 291     public Timer createTimer(Runnable runnable) {
 292         return new GtkTimer(runnable);
 293     }
 294 
 295     @Override
 296     protected native int staticTimer_getMinPeriod();
 297 
 298     @Override
 299     protected native int staticTimer_getMaxPeriod();
 300 
 301     @Override protected double staticScreen_getVideoRefreshPeriod() {
 302         return 0.0;     // indicate millisecond resolution
 303     }
 304 
 305     @Override native protected Screen[] staticScreen_getScreens();
 306 
 307     @Override
 308     protected FileChooserResult staticCommonDialogs_showFileChooser(
 309             Window owner, String folder, String filename, String title,
 310             int type, boolean multipleMode, ExtensionFilter[] extensionFilters, int defaultFilterIndex) {
 311 
 312         return GtkCommonDialogs.showFileChooser(owner, folder, filename, title,
 313                 type, multipleMode, extensionFilters, defaultFilterIndex);
 314     }
 315 
 316     @Override
 317     protected File staticCommonDialogs_showFolderChooser(Window owner, String folder, String title) {
 318         return GtkCommonDialogs.showFolderChooser(owner, folder, title);
 319     }
 320 
 321     @Override
 322     protected native long staticView_getMultiClickTime();
 323 
 324     @Override
 325     protected native int staticView_getMultiClickMaxX();
 326 
 327     @Override
 328     protected native int staticView_getMultiClickMaxY();
 329 
 330     @Override
 331     protected boolean _supportsInputMethods() {
 332         return true;
 333     }
 334 
 335     @Override
 336     protected native boolean _supportsTransparentWindows();
 337 
 338     @Override protected boolean _supportsUnifiedWindows() {
 339         return false;
 340     }
 341 
 342     @Override
 343     protected native int _getKeyCodeForChar(char c);
 344 
 345 }