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