1 /* 2 * Copyright (c) 2010, 2018, 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.javafx.util.Logging; 40 import com.sun.glass.utils.NativeLibLoader; 41 import sun.util.logging.PlatformLogger; 42 43 import java.io.File; 44 import java.lang.reflect.Method; 45 import java.nio.ByteBuffer; 46 import java.nio.IntBuffer; 47 import java.security.AccessController; 48 import java.security.PrivilegedAction; 49 import java.util.Map; 50 import java.util.concurrent.CountDownLatch; 51 import java.lang.annotation.Native; 52 53 final class GtkApplication extends Application implements 54 InvokeLaterDispatcher.InvokeLaterSubmitter { 55 private static final String SWT_INTERNAL_CLASS = 56 "org.eclipse.swt.internal.gtk.OS"; 57 private static final int forcedGtkVersion; 58 59 static { 60 //check for SWT-GTK lib presence 61 Class<?> OS = AccessController. 62 doPrivileged((PrivilegedAction<Class<?>>) () -> { 63 try { 64 return Class.forName(SWT_INTERNAL_CLASS, true, 65 ClassLoader.getSystemClassLoader()); 66 } catch (Exception e) {} 67 try { 68 return Class.forName(SWT_INTERNAL_CLASS, true, 69 Thread.currentThread().getContextClassLoader()); 70 } catch (Exception e) {} 71 return null; 72 }); 73 if (OS != null) { 74 PlatformLogger logger = Logging.getJavaFXLogger(); 75 logger.fine("SWT-GTK library found. Try to obtain GTK version."); 76 Method method = AccessController. 77 doPrivileged((PrivilegedAction<Method>) () -> { 78 try { 79 return OS.getMethod("gtk_major_version"); 80 } catch (Exception e) { 81 return null; 82 } 83 }); 84 int ver = 0; 85 if (method != null) { 86 try { 87 ver = ((Number)method.invoke(OS)).intValue(); 88 } catch (Exception e) { 89 logger.warning("Method gtk_major_version() of " + 90 "the org.eclipse.swt.internal.gtk.OS class " + 91 "returns error. SWT GTK version cannot be detected. " + 92 "GTK3 will be used as default."); 93 ver = 3; 94 } 95 } 96 if (ver < 2 || ver > 3) { 97 logger.warning("SWT-GTK uses unsupported major GTK version " 98 + ver + ". GTK3 will be used as default."); 99 ver = 3; 100 } 101 forcedGtkVersion = ver; 102 } else { 103 forcedGtkVersion = 0; 104 } 105 106 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 107 Application.loadNativeLibrary(); 108 return null; 109 }); 110 } 111 112 public static int screen = -1; 113 public static long display = 0; 114 public static long visualID = 0; 115 116 private final InvokeLaterDispatcher invokeLaterDispatcher; 117 118 GtkApplication() { 119 120 final int gtkVersion = forcedGtkVersion == 0 ? 121 AccessController.doPrivileged((PrivilegedAction<Integer>) () -> { 122 String v = System.getProperty("jdk.gtk.version","2"); 123 int ret = 0; 124 if ("3".equals(v) || v.startsWith("3.")) { 125 ret = 3; 126 } else if ("2".equals(v) || v.startsWith("2.")) { 127 ret = 2; 128 } 129 return ret; 130 }) : forcedGtkVersion; 131 132 boolean gtkVersionVerbose = 133 AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> { 134 return Boolean.getBoolean("jdk.gtk.verbose"); 135 136 }); 137 138 int libraryToLoad = _queryLibrary(gtkVersion, gtkVersionVerbose); 139 140 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 141 if (libraryToLoad == QUERY_NO_DISPLAY) { 142 throw new UnsupportedOperationException("Unable to open DISPLAY"); 143 } else if (libraryToLoad == QUERY_USE_CURRENT) { 144 if (gtkVersionVerbose) { 145 System.out.println("Glass GTK library to load is already loaded"); 146 } 147 } else if (libraryToLoad == QUERY_LOAD_GTK2) { 148 if (gtkVersionVerbose) { 149 System.out.println("Glass GTK library to load is glassgtk2"); 150 } 151 NativeLibLoader.loadLibrary("glassgtk2"); 152 } else if (libraryToLoad == QUERY_LOAD_GTK3) { 153 if (gtkVersionVerbose) { 154 System.out.println("Glass GTK library to load is glassgtk3"); 155 } 156 NativeLibLoader.loadLibrary("glassgtk3"); 157 } else { 158 throw new UnsupportedOperationException("Internal Error"); 159 } 160 return null; 161 }); 162 163 int version = _initGTK(gtkVersion, gtkVersionVerbose); 164 if (version == -1) { 165 throw new RuntimeException("Error loading GTK libraries"); 166 } 167 168 // Embedded in SWT, with shared event thread 169 boolean isEventThread = AccessController 170 .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.embed.isEventThread")); 171 if (!isEventThread) { 172 invokeLaterDispatcher = new InvokeLaterDispatcher(this); 173 invokeLaterDispatcher.start(); 174 } else { 175 invokeLaterDispatcher = null; 176 } 177 } 178 179 @Native private static final int QUERY_ERROR = -2; 180 @Native private static final int QUERY_NO_DISPLAY = -1; 181 @Native private static final int QUERY_USE_CURRENT = 1; 182 @Native private static final int QUERY_LOAD_GTK2 = 2; 183 @Native private static final int QUERY_LOAD_GTK3 = 3; 184 /* 185 * check the system and return an indication of which library to load 186 * return values are the QUERY_ constants 187 */ 188 private static native int _queryLibrary(int version, boolean verbose); 189 190 private static native int _initGTK(int version, boolean verbose); 191 192 private void initDisplay() { 193 Map ds = getDeviceDetails(); 194 if (ds != null) { 195 Object value; 196 value = ds.get("XDisplay"); 197 if (value != null) { 198 display = (Long)value; 199 } 200 value = ds.get("XVisualID"); 201 if (value != null) { 202 visualID = (Long)value; 203 } 204 value = ds.get("XScreenID"); 205 if (value != null) { 206 screen = (Integer)value; 207 } 208 } 209 } 210 211 private void init() { 212 initDisplay(); 213 long eventProc = 0; 214 Map map = getDeviceDetails(); 215 if (map != null) { 216 Long result = (Long) map.get("javafx.embed.eventProc"); 217 eventProc = result == null ? 0 : result; 218 } 219 220 final boolean disableGrab = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("sun.awt.disablegrab") || 221 Boolean.getBoolean("glass.disableGrab")); 222 223 _init(eventProc, disableGrab); 224 } 225 226 @Override 227 protected void runLoop(final Runnable launchable) { 228 // Embedded in SWT, with shared event thread 229 final boolean isEventThread = AccessController 230 .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.embed.isEventThread")); 231 232 if (isEventThread) { 233 init(); 234 setEventThread(Thread.currentThread()); 235 launchable.run(); 236 return; 237 } 238 239 final boolean noErrorTrap = AccessController 240 .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("glass.noErrorTrap")); 241 242 final Thread toolkitThread = 243 AccessController.doPrivileged((PrivilegedAction<Thread>) () -> new Thread(() -> { 244 init(); 245 _runLoop(launchable, noErrorTrap); 246 }, "GtkNativeMainLoopThread")); 247 setEventThread(toolkitThread); 248 toolkitThread.start(); 249 } 250 251 @Override 252 protected void finishTerminating() { 253 final Thread toolkitThread = getEventThread(); 254 if (toolkitThread != null) { 255 _terminateLoop(); 256 setEventThread(null); 257 } 258 super.finishTerminating(); 259 } 260 261 @Override public boolean shouldUpdateWindow() { 262 return true; 263 } 264 265 private native void _terminateLoop(); 266 267 private native void _init(long eventProc, boolean disableGrab); 268 269 private native void _runLoop(Runnable launchable, boolean noErrorTrap); 270 271 @Override 272 protected void _invokeAndWait(final Runnable runnable) { 273 if (invokeLaterDispatcher != null) { 274 invokeLaterDispatcher.invokeAndWait(runnable); 275 } else { 276 final CountDownLatch latch = new CountDownLatch(1); 277 submitForLaterInvocation(() -> { 278 if (runnable != null) runnable.run(); 279 latch.countDown(); 280 }); 281 try { 282 latch.await(); 283 } catch (InterruptedException e) { 284 //FAIL SILENTLY 285 } 286 } 287 } 288 289 private native void _submitForLaterInvocation(Runnable r); 290 // InvokeLaterDispatcher.InvokeLaterSubmitter 291 @Override public void submitForLaterInvocation(Runnable r) { 292 _submitForLaterInvocation(r); 293 } 294 295 @Override protected void _invokeLater(Runnable runnable) { 296 if (invokeLaterDispatcher != null) { 297 invokeLaterDispatcher.invokeLater(runnable); 298 } else { 299 submitForLaterInvocation(runnable); 300 } 301 } 302 303 private Object eventLoopExitEnterPassValue; 304 305 private native void enterNestedEventLoopImpl(); 306 307 private native void leaveNestedEventLoopImpl(); 308 309 @Override 310 protected Object _enterNestedEventLoop() { 311 if (invokeLaterDispatcher != null) { 312 invokeLaterDispatcher.notifyEnteringNestedEventLoop(); 313 } 314 try { 315 enterNestedEventLoopImpl(); 316 final Object retValue = eventLoopExitEnterPassValue; 317 eventLoopExitEnterPassValue = null; 318 return retValue; 319 } finally { 320 if (invokeLaterDispatcher != null) { 321 invokeLaterDispatcher.notifyLeftNestedEventLoop(); 322 } 323 } 324 } 325 326 @Override 327 protected void _leaveNestedEventLoop(Object retValue) { 328 if (invokeLaterDispatcher != null) { 329 invokeLaterDispatcher.notifyLeavingNestedEventLoop(); 330 } 331 eventLoopExitEnterPassValue = retValue; 332 leaveNestedEventLoopImpl(); 333 } 334 335 @Override 336 public Window createWindow(Window owner, Screen screen, int styleMask) { 337 return new GtkWindow(owner, screen, styleMask); 338 } 339 340 @Override 341 public Window createWindow(long parent) { 342 return new GtkChildWindow(parent); 343 } 344 345 @Override 346 public View createView() { 347 return new GtkView(); 348 } 349 350 @Override 351 public Cursor createCursor(int type) { 352 return new GtkCursor(type); 353 } 354 355 @Override 356 public Cursor createCursor(int x, int y, Pixels pixels) { 357 return new GtkCursor(x, y, pixels); 358 } 359 360 @Override 361 protected void staticCursor_setVisible(boolean visible) { 362 } 363 364 @Override 365 protected Size staticCursor_getBestSize(int width, int height) { 366 return GtkCursor._getBestSize(width, height); 367 } 368 369 @Override 370 public Pixels createPixels(int width, int height, ByteBuffer data) { 371 return new GtkPixels(width, height, data); 372 } 373 374 @Override 375 public Pixels createPixels(int width, int height, IntBuffer data) { 376 return new GtkPixels(width, height, data); 377 } 378 379 @Override 380 public Pixels createPixels(int width, int height, IntBuffer data, float scale) { 381 return new GtkPixels(width, height, data, scale); 382 } 383 384 @Override 385 protected int staticPixels_getNativeFormat() { 386 return Pixels.Format.BYTE_BGRA_PRE; // TODO 387 } 388 389 @Override 390 public Robot createRobot() { 391 return new GtkRobot(); 392 } 393 394 @Override 395 public Timer createTimer(Runnable runnable) { 396 return new GtkTimer(runnable); 397 } 398 399 @Override 400 protected native int staticTimer_getMinPeriod(); 401 402 @Override 403 protected native int staticTimer_getMaxPeriod(); 404 405 @Override protected double staticScreen_getVideoRefreshPeriod() { 406 return 0.0; // indicate millisecond resolution 407 } 408 409 @Override native protected Screen[] staticScreen_getScreens(); 410 411 @Override 412 protected FileChooserResult staticCommonDialogs_showFileChooser( 413 Window owner, String folder, String filename, String title, 414 int type, boolean multipleMode, ExtensionFilter[] extensionFilters, int defaultFilterIndex) { 415 416 return GtkCommonDialogs.showFileChooser(owner, folder, filename, title, 417 type, multipleMode, extensionFilters, defaultFilterIndex); 418 } 419 420 @Override 421 protected File staticCommonDialogs_showFolderChooser(Window owner, String folder, String title) { 422 return GtkCommonDialogs.showFolderChooser(owner, folder, title); 423 } 424 425 @Override 426 protected native long staticView_getMultiClickTime(); 427 428 @Override 429 protected native int staticView_getMultiClickMaxX(); 430 431 @Override 432 protected native int staticView_getMultiClickMaxY(); 433 434 @Override 435 protected boolean _supportsInputMethods() { 436 return true; 437 } 438 439 @Override 440 protected native boolean _supportsTransparentWindows(); 441 442 @Override protected boolean _supportsUnifiedWindows() { 443 return false; 444 } 445 446 @Override 447 protected native int _getKeyCodeForChar(char c); 448 449 }