/* * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.glass.ui.gtk; import com.sun.glass.ui.Application; import com.sun.glass.ui.CommonDialogs.ExtensionFilter; import com.sun.glass.ui.CommonDialogs.FileChooserResult; import com.sun.glass.ui.Cursor; import com.sun.glass.ui.InvokeLaterDispatcher; import com.sun.glass.ui.Pixels; import com.sun.glass.ui.Robot; import com.sun.glass.ui.Screen; import com.sun.glass.ui.Size; import com.sun.glass.ui.Timer; import com.sun.glass.ui.View; import com.sun.glass.ui.Window; import java.io.File; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Map; import java.util.concurrent.CountDownLatch; final class GtkApplication extends Application implements InvokeLaterDispatcher.InvokeLaterSubmitter { static { AccessController.doPrivileged((PrivilegedAction) () -> { Application.loadNativeLibrary(); return null; }); } public static int screen = -1; public static long display = 0; public static long visualID = 0; private final InvokeLaterDispatcher invokeLaterDispatcher; GtkApplication() { int gtkVersion = AccessController.doPrivileged((PrivilegedAction) () -> { String v = System.getProperty("jdk.gtk.version","2"); int ret = 0; if ("3".equals(v)) { System.out.println("jdk.gtk.version is 3"); ret = 3; } else if ("2".equals(v) || v.startsWith("2.")) { System.out.println("jdk.gtk.version is 2"); ret = 2; } return ret; }); boolean gtkVersionVerbose = AccessController.doPrivileged((PrivilegedAction) () -> { return Boolean.getBoolean("jdk.gtk.verbose"); }); _setGTKversion(gtkVersion, gtkVersionVerbose); // Check whether the Display is valid and throw an exception if not. // We use UnsupportedOperationException rather than HeadlessException // so as not to introduce a dependency on AWT. if (!isDisplayValid()) { throw new UnsupportedOperationException("Unable to open DISPLAY"); } // Embedded in SWT, with shared event thread boolean isEventThread = AccessController .doPrivileged((PrivilegedAction) () -> Boolean.getBoolean("javafx.embed.isEventThread")); if (!isEventThread) { invokeLaterDispatcher = new InvokeLaterDispatcher(this); invokeLaterDispatcher.start(); } else { invokeLaterDispatcher = null; } } private static native int _setGTKversion(int version, boolean verbose); private static boolean isDisplayValid() { return _isDisplayValid(); } private void initDisplay() { Map ds = getDeviceDetails(); if (ds != null) { Object value; value = ds.get("XDisplay"); if (value != null) { display = (Long)value; } value = ds.get("XVisualID"); if (value != null) { visualID = (Long)value; } value = ds.get("XScreenID"); if (value != null) { screen = (Integer)value; } } } private void init() { initDisplay(); long eventProc = 0; Map map = getDeviceDetails(); if (map != null) { Long result = (Long) map.get("javafx.embed.eventProc"); eventProc = result == null ? 0 : result; } final boolean disableGrab = AccessController.doPrivileged((PrivilegedAction) () -> Boolean.getBoolean("sun.awt.disablegrab") || Boolean.getBoolean("glass.disableGrab")); _init(eventProc, disableGrab); } @Override protected void runLoop(final Runnable launchable) { // Embedded in SWT, with shared event thread final boolean isEventThread = AccessController .doPrivileged((PrivilegedAction) () -> Boolean.getBoolean("javafx.embed.isEventThread")); if (isEventThread) { init(); setEventThread(Thread.currentThread()); launchable.run(); return; } final boolean noErrorTrap = AccessController .doPrivileged((PrivilegedAction) () -> Boolean.getBoolean("glass.noErrorTrap")); final Thread toolkitThread = AccessController.doPrivileged((PrivilegedAction) () -> new Thread(() -> { init(); _runLoop(launchable, noErrorTrap); }, "GtkNativeMainLoopThread")); setEventThread(toolkitThread); toolkitThread.start(); } @Override protected void finishTerminating() { final Thread toolkitThread = getEventThread(); if (toolkitThread != null) { _terminateLoop(); setEventThread(null); } super.finishTerminating(); } @Override public boolean shouldUpdateWindow() { return true; } private static native boolean _isDisplayValid(); private native void _terminateLoop(); private native void _init(long eventProc, boolean disableGrab); private native void _runLoop(Runnable launchable, boolean noErrorTrap); @Override protected void _invokeAndWait(final Runnable runnable) { if (invokeLaterDispatcher != null) { invokeLaterDispatcher.invokeAndWait(runnable); } else { final CountDownLatch latch = new CountDownLatch(1); submitForLaterInvocation(() -> { if (runnable != null) runnable.run(); latch.countDown(); }); try { latch.await(); } catch (InterruptedException e) { //FAIL SILENTLY } } } private native void _submitForLaterInvocation(Runnable r); // InvokeLaterDispatcher.InvokeLaterSubmitter @Override public void submitForLaterInvocation(Runnable r) { _submitForLaterInvocation(r); } @Override protected void _invokeLater(Runnable runnable) { if (invokeLaterDispatcher != null) { invokeLaterDispatcher.invokeLater(runnable); } else { submitForLaterInvocation(runnable); } } private Object eventLoopExitEnterPassValue; private native void enterNestedEventLoopImpl(); private native void leaveNestedEventLoopImpl(); @Override protected Object _enterNestedEventLoop() { if (invokeLaterDispatcher != null) { invokeLaterDispatcher.notifyEnteringNestedEventLoop(); } try { enterNestedEventLoopImpl(); final Object retValue = eventLoopExitEnterPassValue; eventLoopExitEnterPassValue = null; return retValue; } finally { if (invokeLaterDispatcher != null) { invokeLaterDispatcher.notifyLeftNestedEventLoop(); } } } @Override protected void _leaveNestedEventLoop(Object retValue) { if (invokeLaterDispatcher != null) { invokeLaterDispatcher.notifyLeavingNestedEventLoop(); } eventLoopExitEnterPassValue = retValue; leaveNestedEventLoopImpl(); } @Override public Window createWindow(Window owner, Screen screen, int styleMask) { return new GtkWindow(owner, screen, styleMask); } @Override public Window createWindow(long parent) { return new GtkChildWindow(parent); } @Override public View createView() { return new GtkView(); } @Override public Cursor createCursor(int type) { return new GtkCursor(type); } @Override public Cursor createCursor(int x, int y, Pixels pixels) { return new GtkCursor(x, y, pixels); } @Override protected void staticCursor_setVisible(boolean visible) { } @Override protected Size staticCursor_getBestSize(int width, int height) { return GtkCursor._getBestSize(width, height); } @Override public Pixels createPixels(int width, int height, ByteBuffer data) { return new GtkPixels(width, height, data); } @Override public Pixels createPixels(int width, int height, IntBuffer data) { return new GtkPixels(width, height, data); } @Override public Pixels createPixels(int width, int height, IntBuffer data, float scalex, float scaley) { return new GtkPixels(width, height, data, scalex, scaley); } @Override protected int staticPixels_getNativeFormat() { return Pixels.Format.BYTE_BGRA_PRE; // TODO } @Override public Robot createRobot() { return new GtkRobot(); } @Override public Timer createTimer(Runnable runnable) { return new GtkTimer(runnable); } @Override protected native int staticTimer_getMinPeriod(); @Override protected native int staticTimer_getMaxPeriod(); @Override protected double staticScreen_getVideoRefreshPeriod() { return 0.0; // indicate millisecond resolution } @Override native protected Screen[] staticScreen_getScreens(); @Override protected FileChooserResult staticCommonDialogs_showFileChooser( Window owner, String folder, String filename, String title, int type, boolean multipleMode, ExtensionFilter[] extensionFilters, int defaultFilterIndex) { return GtkCommonDialogs.showFileChooser(owner, folder, filename, title, type, multipleMode, extensionFilters, defaultFilterIndex); } @Override protected File staticCommonDialogs_showFolderChooser(Window owner, String folder, String title) { return GtkCommonDialogs.showFolderChooser(owner, folder, title); } @Override protected native long staticView_getMultiClickTime(); @Override protected native int staticView_getMultiClickMaxX(); @Override protected native int staticView_getMultiClickMaxY(); @Override protected boolean _supportsInputMethods() { return true; } @Override protected native boolean _supportsTransparentWindows(); @Override protected boolean _supportsUnifiedWindows() { return false; } @Override protected native int _getKeyCodeForChar(char c); }