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 }