diff options
Diffstat (limited to 'bundles/org.eclipse.swt')
7 files changed, 764 insertions, 1 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c index b1acaff1d0..16ae5c75c4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c @@ -14766,6 +14766,18 @@ fail: } #endif +#ifndef NO__1g_1dbus_1proxy_1get_1name_1owner +JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1dbus_1proxy_1get_1name_1owner) + (JNIEnv *env, jclass that, jlong arg0) +{ + jlong rc = 0; + OS_NATIVE_ENTER(env, that, _1g_1dbus_1proxy_1get_1name_1owner_FUNC); + rc = (jlong)g_dbus_proxy_get_name_owner((GDBusProxy *)arg0); + OS_NATIVE_EXIT(env, that, _1g_1dbus_1proxy_1get_1name_1owner_FUNC); + return rc; +} +#endif + #ifndef NO__1g_1dbus_1proxy_1new_1for_1bus_1sync JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1dbus_1proxy_1new_1for_1bus_1sync) (JNIEnv *env, jclass that, jint arg0, jint arg1, jlong arg2, jbyteArray arg3, jbyteArray arg4, jbyteArray arg5, jlong arg6, jlongArray arg7) @@ -15028,6 +15040,22 @@ JNIEXPORT void JNICALL OS_NATIVE(_1g_1get_1current_1time) } #endif +#ifndef NO__1g_1getenv +JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1getenv) + (JNIEnv *env, jclass that, jbyteArray arg0) +{ + jbyte *lparg0=NULL; + jlong rc = 0; + OS_NATIVE_ENTER(env, that, _1g_1getenv_FUNC); + if (arg0) if ((lparg0 = (*env)->GetByteArrayElements(env, arg0, NULL)) == NULL) goto fail; + rc = (jlong)g_getenv((const gchar *)lparg0); +fail: + if (arg0 && lparg0) (*env)->ReleaseByteArrayElements(env, arg0, lparg0, JNI_ABORT); + OS_NATIVE_EXIT(env, that, _1g_1getenv_FUNC); + return rc; +} +#endif + #ifndef NO__1g_1hash_1table_1get_1values JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1hash_1table_1get_1values) (JNIEnv *env, jclass that, jlong arg0) @@ -16127,6 +16155,20 @@ fail: } #endif +#ifndef NO__1g_1unsetenv +JNIEXPORT void JNICALL OS_NATIVE(_1g_1unsetenv) + (JNIEnv *env, jclass that, jbyteArray arg0) +{ + jbyte *lparg0=NULL; + OS_NATIVE_ENTER(env, that, _1g_1unsetenv_FUNC); + if (arg0) if ((lparg0 = (*env)->GetByteArrayElements(env, arg0, NULL)) == NULL) goto fail; + g_unsetenv((const gchar *)lparg0); +fail: + if (arg0 && lparg0) (*env)->ReleaseByteArrayElements(env, arg0, lparg0, JNI_ABORT); + OS_NATIVE_EXIT(env, that, _1g_1unsetenv_FUNC); +} +#endif + #ifndef NO__1g_1utf16_1offset_1to_1pointer JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1utf16_1offset_1to_1pointer) (JNIEnv *env, jclass that, jlong arg0, jlong arg1) @@ -16479,6 +16521,66 @@ JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1n_1children) } #endif +#ifndef NO__1g_1variant_1new___3BZ_3B +JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1new___3BZ_3B) + (JNIEnv *env, jclass that, jbyteArray arg0, jboolean arg1, jbyteArray arg2) +{ + jbyte *lparg0=NULL; + jbyte *lparg2=NULL; + jlong rc = 0; + OS_NATIVE_ENTER(env, that, _1g_1variant_1new___3BZ_3B_FUNC); + if (arg0) if ((lparg0 = (*env)->GetByteArrayElements(env, arg0, NULL)) == NULL) goto fail; + if (arg2) if ((lparg2 = (*env)->GetByteArrayElements(env, arg2, NULL)) == NULL) goto fail; + rc = (jlong)g_variant_new((const gchar *)lparg0, (gboolean)arg1, (const gchar *)lparg2); +fail: + if (arg2 && lparg2) (*env)->ReleaseByteArrayElements(env, arg2, lparg2, JNI_ABORT); + if (arg0 && lparg0) (*env)->ReleaseByteArrayElements(env, arg0, lparg0, JNI_ABORT); + OS_NATIVE_EXIT(env, that, _1g_1variant_1new___3BZ_3B_FUNC); + return rc; +} +#endif + +#ifndef NO__1g_1variant_1new___3B_3B +JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1new___3B_3B) + (JNIEnv *env, jclass that, jbyteArray arg0, jbyteArray arg1) +{ + jbyte *lparg0=NULL; + jbyte *lparg1=NULL; + jlong rc = 0; + OS_NATIVE_ENTER(env, that, _1g_1variant_1new___3B_3B_FUNC); + if (arg0) if ((lparg0 = (*env)->GetByteArrayElements(env, arg0, NULL)) == NULL) goto fail; + if (arg1) if ((lparg1 = (*env)->GetByteArrayElements(env, arg1, NULL)) == NULL) goto fail; + rc = (jlong)g_variant_new((const gchar *)lparg0, (const gchar *)lparg1); +fail: + if (arg1 && lparg1) (*env)->ReleaseByteArrayElements(env, arg1, lparg1, JNI_ABORT); + if (arg0 && lparg0) (*env)->ReleaseByteArrayElements(env, arg0, lparg0, JNI_ABORT); + OS_NATIVE_EXIT(env, that, _1g_1variant_1new___3B_3B_FUNC); + return rc; +} +#endif + +#ifndef NO__1g_1variant_1new___3B_3B_3B +JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1new___3B_3B_3B) + (JNIEnv *env, jclass that, jbyteArray arg0, jbyteArray arg1, jbyteArray arg2) +{ + jbyte *lparg0=NULL; + jbyte *lparg1=NULL; + jbyte *lparg2=NULL; + jlong rc = 0; + OS_NATIVE_ENTER(env, that, _1g_1variant_1new___3B_3B_3B_FUNC); + if (arg0) if ((lparg0 = (*env)->GetByteArrayElements(env, arg0, NULL)) == NULL) goto fail; + if (arg1) if ((lparg1 = (*env)->GetByteArrayElements(env, arg1, NULL)) == NULL) goto fail; + if (arg2) if ((lparg2 = (*env)->GetByteArrayElements(env, arg2, NULL)) == NULL) goto fail; + rc = (jlong)g_variant_new((const gchar *)lparg0, (const gchar *)lparg1, (const gchar *)lparg2); +fail: + if (arg2 && lparg2) (*env)->ReleaseByteArrayElements(env, arg2, lparg2, JNI_ABORT); + if (arg1 && lparg1) (*env)->ReleaseByteArrayElements(env, arg1, lparg1, JNI_ABORT); + if (arg0 && lparg0) (*env)->ReleaseByteArrayElements(env, arg0, lparg0, JNI_ABORT); + OS_NATIVE_EXIT(env, that, _1g_1variant_1new___3B_3B_3B_FUNC); + return rc; +} +#endif + #ifndef NO__1g_1variant_1new_1boolean JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1new_1boolean) (JNIEnv *env, jclass that, jboolean arg0) @@ -16571,6 +16673,16 @@ JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1new_1uint64) } #endif +#ifndef NO__1g_1variant_1type_1free +JNIEXPORT void JNICALL OS_NATIVE(_1g_1variant_1type_1free) + (JNIEnv *env, jclass that, jlong arg0) +{ + OS_NATIVE_ENTER(env, that, _1g_1variant_1type_1free_FUNC); + g_variant_type_free((GVariantType *)arg0); + OS_NATIVE_EXIT(env, that, _1g_1variant_1type_1free_FUNC); +} +#endif + #ifndef NO__1g_1variant_1type_1new JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1variant_1type_1new) (JNIEnv *env, jclass that, jbyteArray arg0) @@ -16587,6 +16699,16 @@ fail: } #endif +#ifndef NO__1g_1variant_1unref +JNIEXPORT void JNICALL OS_NATIVE(_1g_1variant_1unref) + (JNIEnv *env, jclass that, jlong arg0) +{ + OS_NATIVE_ENTER(env, that, _1g_1variant_1unref_FUNC); + g_variant_unref((GVariant *)arg0); + OS_NATIVE_EXIT(env, that, _1g_1variant_1unref_FUNC); +} +#endif + #ifndef NO__1getpid JNIEXPORT jint JNICALL OS_NATIVE(_1getpid) (JNIEnv *env, jclass that) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c index ef46e89be3..40806b67d5 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c @@ -1223,6 +1223,7 @@ char * OS_nativeFunctionNames[] = { "_1g_1dbus_1proxy_1call", "_1g_1dbus_1proxy_1call_1finish", "_1g_1dbus_1proxy_1call_1sync", + "_1g_1dbus_1proxy_1get_1name_1owner", "_1g_1dbus_1proxy_1new_1for_1bus_1sync", "_1g_1error_1free", "_1g_1error_1get_1message", @@ -1240,6 +1241,7 @@ char * OS_nativeFunctionNames[] = { "_1g_1filename_1to_1utf8", "_1g_1free", "_1g_1get_1current_1time", + "_1g_1getenv", "_1g_1hash_1table_1get_1values", "_1g_1icon_1new_1for_1string", "_1g_1icon_1to_1string", @@ -1326,6 +1328,7 @@ char * OS_nativeFunctionNames[] = { "_1g_1type_1parent", "_1g_1type_1query", "_1g_1type_1register_1static", + "_1g_1unsetenv", "_1g_1utf16_1offset_1to_1pointer", "_1g_1utf16_1offset_1to_1utf8_1offset", "_1g_1utf16_1pointer_1to_1offset", @@ -1352,6 +1355,9 @@ char * OS_nativeFunctionNames[] = { "_1g_1variant_1get_1uint64", "_1g_1variant_1is_1of_1type", "_1g_1variant_1n_1children", + "_1g_1variant_1new___3BZ_3B", + "_1g_1variant_1new___3B_3B", + "_1g_1variant_1new___3B_3B_3B", "_1g_1variant_1new_1boolean", "_1g_1variant_1new_1byte", "_1g_1variant_1new_1double", @@ -1359,7 +1365,9 @@ char * OS_nativeFunctionNames[] = { "_1g_1variant_1new_1string", "_1g_1variant_1new_1tuple", "_1g_1variant_1new_1uint64", + "_1g_1variant_1type_1free", "_1g_1variant_1type_1new", + "_1g_1variant_1unref", "_1getpid", "_1glib_1major_1version", "_1glib_1micro_1version", diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h index 7e6d144d0b..935e7d2522 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h @@ -1197,6 +1197,7 @@ typedef enum { _1g_1dbus_1proxy_1call_FUNC, _1g_1dbus_1proxy_1call_1finish_FUNC, _1g_1dbus_1proxy_1call_1sync_FUNC, + _1g_1dbus_1proxy_1get_1name_1owner_FUNC, _1g_1dbus_1proxy_1new_1for_1bus_1sync_FUNC, _1g_1error_1free_FUNC, _1g_1error_1get_1message_FUNC, @@ -1214,6 +1215,7 @@ typedef enum { _1g_1filename_1to_1utf8_FUNC, _1g_1free_FUNC, _1g_1get_1current_1time_FUNC, + _1g_1getenv_FUNC, _1g_1hash_1table_1get_1values_FUNC, _1g_1icon_1new_1for_1string_FUNC, _1g_1icon_1to_1string_FUNC, @@ -1300,6 +1302,7 @@ typedef enum { _1g_1type_1parent_FUNC, _1g_1type_1query_FUNC, _1g_1type_1register_1static_FUNC, + _1g_1unsetenv_FUNC, _1g_1utf16_1offset_1to_1pointer_FUNC, _1g_1utf16_1offset_1to_1utf8_1offset_FUNC, _1g_1utf16_1pointer_1to_1offset_FUNC, @@ -1326,6 +1329,9 @@ typedef enum { _1g_1variant_1get_1uint64_FUNC, _1g_1variant_1is_1of_1type_FUNC, _1g_1variant_1n_1children_FUNC, + _1g_1variant_1new___3BZ_3B_FUNC, + _1g_1variant_1new___3B_3B_FUNC, + _1g_1variant_1new___3B_3B_3B_FUNC, _1g_1variant_1new_1boolean_FUNC, _1g_1variant_1new_1byte_FUNC, _1g_1variant_1new_1double_FUNC, @@ -1333,7 +1339,9 @@ typedef enum { _1g_1variant_1new_1string_FUNC, _1g_1variant_1new_1tuple_FUNC, _1g_1variant_1new_1uint64_FUNC, + _1g_1variant_1type_1free_FUNC, _1g_1variant_1type_1new_FUNC, + _1g_1variant_1unref_FUNC, _1getpid_FUNC, _1glib_1major_1version_FUNC, _1glib_1micro_1version_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java index 6e605618d6..213b7d8e70 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java @@ -1741,6 +1741,18 @@ public static final void g_free(long mem) { } } /** + * @param variable cast=(const gchar *),flags=no_out + */ +public static final native long _g_getenv(byte [] variable); +public static final long g_getenv(byte [] variable) { + lock.lock(); + try { + return _g_getenv(variable); + } finally { + lock.unlock(); + } +} +/** * @param table cast=(GHashTable *) */ public static final native long _g_hash_table_get_values(long table); @@ -2791,7 +2803,18 @@ public static final long g_value_peek_pointer (long value) { lock.unlock(); } } - +/** + * @param variable cast=(const gchar *),flags=no_out + */ +public static final native void _g_unsetenv(byte [] variable); +public static final void g_unsetenv(byte [] variable) { + lock.lock(); + try { + _g_unsetenv(variable); + } finally { + lock.unlock(); + } +} /** @method flags=const */ public static final native int _glib_major_version(); public static final int glib_major_version() { @@ -4237,6 +4260,20 @@ public static final void g_dbus_proxy_call (long proxy, byte[] method_name, long /** * @param proxy cast=(GDBusProxy *) + * @category gdbus + */ +public static final native long _g_dbus_proxy_get_name_owner (long proxy); +public static final long g_dbus_proxy_get_name_owner (long proxy) { + lock.lock(); + try { + return _g_dbus_proxy_get_name_owner (proxy); + } finally { + lock.unlock(); + } +} + +/** + * @param proxy cast=(GDBusProxy *) * @param res cast=(GAsyncResult *) * @param error cast=(GError **) * @category gdbus @@ -4374,6 +4411,21 @@ public static final void g_variant_builder_add_value (long builder, long value) } /** + * @param type cast=(GVariantType *) + * @category gdbus + */ +public static final native void _g_variant_type_free (long type); +/** @category gdbus */ +public static final void g_variant_type_free (long type) { + lock.lock(); + try { + _g_variant_type_free(type); + } finally { + lock.unlock(); + } +} + +/** * @param type cast=(const gchar *) * @category gdbus */ @@ -4419,6 +4471,56 @@ public static final void g_variant_builder_unref (long builder) { } /** + * @param format_string cast=(const gchar *),flags=no_out + * @param arg0 cast=(const gchar *),flags=no_out + * @category gdbus + */ +public static final native long _g_variant_new (byte[] format_string, byte[] arg0); +/** @category gdbus */ +public static final long g_variant_new (byte[] format_string, byte[] arg0) { + lock.lock(); + try { + return _g_variant_new(format_string, arg0); + } finally { + lock.unlock(); + } +} + +/** + * @param format_string cast=(const gchar *),flags=no_out + * @param arg0 cast=(gboolean) + * @param arg1 cast=(const gchar *),flags=no_out + * @category gdbus + */ +public static final native long _g_variant_new (byte[] format_string, boolean arg0, byte[] arg1); +/** @category gdbus */ +public static final long g_variant_new (byte[] format_string, boolean arg0, byte[] arg1) { + lock.lock(); + try { + return _g_variant_new(format_string, arg0, arg1); + } finally { + lock.unlock(); + } +} + +/** + * @param format_string cast=(const gchar *),flags=no_out + * @param arg0 cast=(const gchar *),flags=no_out + * @param arg1 cast=(const gchar *),flags=no_out + * @category gdbus + */ +public static final native long _g_variant_new (byte[] format_string, byte[] arg0, byte[] arg1); +/** @category gdbus */ +public static final long g_variant_new (byte[] format_string, byte[] arg0, byte[] arg1) { + lock.lock(); + try { + return _g_variant_new(format_string, arg0, arg1); + } finally { + lock.unlock(); + } +} + +/** * @param intval cast=(gint32) * @category gdbus */ @@ -4692,6 +4794,21 @@ public static final long g_variant_new_string (byte[] string) { } /** + * @param value cast=(GVariant *) + * @category gdbus + */ +public static final native void _g_variant_unref (long value); +/** @category gdbus */ +public static final void g_variant_unref (long value) { + lock.lock(); + try { + _g_variant_unref (value); + } finally { + lock.unlock(); + } +} + +/** * @param object cast=(GObject *) */ public static final native long _g_object_ref_sink(long object); diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java index 1d2d9edae9..64aa8a5bcc 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java +++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java @@ -380,6 +380,7 @@ class WebkitGDBus { long finalGVariant = OS.g_variant_new_tuple(variants, 1); OS.g_dbus_method_invocation_return_value(invocation, finalGVariant); OS.g_variant_builder_unref(builder); + OS.g_variant_type_free(type); return; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SessionManagerDBus.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SessionManagerDBus.java new file mode 100644 index 0000000000..02b67d4fd0 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SessionManagerDBus.java @@ -0,0 +1,480 @@ +/******************************************************************************* + * Copyright (c) 2019 Syntevo and others. All rights reserved. + * The contents of this file are made available under the terms + * of the GNU Lesser General Public License (LGPL) Version 2.1 that + * accompanies this distribution (lgpl-v21.txt). The LGPL is also + * available at http://www.gnu.org/licenses/lgpl.html. If the version + * of the LGPL at http://www.gnu.org is different to the version of + * the LGPL accompanying this distribution and there is any conflict + * between the two license versions, the terms of the LGPL accompanying + * this distribution shall govern. + * + * Contributors: + * Syntevo - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.internal; + +import org.eclipse.swt.internal.gtk.OS; + +import java.util.ArrayList; + +/** + * Communicates with session manager to receive logoff/shutdown events. + * + * GTK also has an implementation (see gtk_application_impl_dbus_startup) + * However, it requires GtkApplication, and SWT doesn't use that. + * + * Current session manager clients can be seen in: + * dbus-send --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.GetClients + * + * If you know clientObjectPath, you can send Stop signal with: + * dbus-send --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager/ClientXX org.gnome.SessionManager.Client.Stop + */ +public class SessionManagerDBus { + public interface IListener { + /** + * Are you ready to exit? + * + * Time limit imposed by session manager is 1 second. + * Final cleanup should happen in stop(). + * @return false to hint that you're not ready. Session manager can ignore the hint. + */ + boolean isReadyToExit(); + + /** + * Perform final cleanup here. + * + * Please note that time limit imposed by session manager is 10 seconds. + */ + void stop(); + } + + private static class ShutdownHook extends Thread { + private SessionManagerDBus parent; + + public ShutdownHook(SessionManagerDBus parent) { + this.parent = parent; + } + + public void run() { + parent.stop(); + } + + public void install() { + try { + Runtime.getRuntime().addShutdownHook(this); + } catch (IllegalArgumentException ex) { + // Shouldn't happen + ex.printStackTrace(); + } catch (IllegalStateException ex) { + // Shouldn't happen + ex.printStackTrace(); + } catch (SecurityException ex) { + // That's pity, but not too much of a problem. + // Client will stay registered, contributing to clutter a little bit. + } + } + + public void remove() { + try { + Runtime.getRuntime().removeShutdownHook(this); + } catch (IllegalStateException ex) { + // The virtual machine is already in the process of shutting down. + // That's expected. + } catch (SecurityException ex) { + // Shouldn't happen if 'addShutdownHook' worked. + ex.printStackTrace(); + } + } + } + + private ArrayList<IListener> listeners = new ArrayList<IListener>(); + private Callback g_signal_callback; + private ShutdownHook shutdownHook = new ShutdownHook(this); + private long sessionManagerProxy; + private long clientProxy; + private String clientObjectPath; + private boolean isGnome; + + private static int dbusTimeoutMsec = 10000; + + public SessionManagerDBus() { + // Allow to disable session manager, for example in case it conflicts with + // session manager connection implemented in application itself. + boolean isDisabled = System.getProperty("org.eclipse.swt.internal.SessionManagerDBus.disable") != null; + if (isDisabled) return; + + start(); + } + + /** + * Subscribes display for session manager events. + * + * Display will receive SWT.Close and will be able to hint that the session should not end. + * Please note that time limit imposed by session manager is 1 second. + * Final cleanup should happen at Display.dispose(). + * + * Display will be disposed before session ends, allowing final cleanup to happen. + * Please note that time limit imposed by session manager is 10 seconds. + */ + public void addListener(IListener listener) { + listeners.add(listener); + } + + public void removeListener(IListener listener) { + listeners.remove(listener); + } + + private boolean start() { + if (!connectSessionManager() || !registerClient() || !connectClientSignal()) { + stop(); + return false; + } + + // Both XFCE and Gnome will automatically unregister client on exit. + // However, to be on the correct side, we should also do it. + // Shutdown hook is used, because there's no other exit callback in SWT. + // Display.dispose() isn't good because there could be many displays. + // Also, in theory Displays can be created and disposed multiple times. + shutdownHook.install(); + + return true; + } + + private void stop() { + if ((sessionManagerProxy != 0) && (clientObjectPath != null)) { + long args = OS.g_variant_new( + Converter.javaStringToCString("(o)"), //$NON-NLS-1$ + Converter.javaStringToCString(clientObjectPath)); + + long [] error = new long [1]; + OS.g_dbus_proxy_call_sync( + sessionManagerProxy, + Converter.javaStringToCString("UnregisterClient"), //$NON-NLS-1$ + args, + OS.G_DBUS_CALL_FLAGS_NONE, + dbusTimeoutMsec, + 0, + error); + + if (error[0] != 0) { + System.err.format( + "SWT SessionManagerDBus: Failed to UnregisterClient: %s%n", + extractFreeGError(error[0])); + } + + clientObjectPath = null; + } + + if (clientProxy != 0) { + OS.g_object_unref(clientProxy); + clientProxy = 0; + } + + if (sessionManagerProxy != 0) { + OS.g_object_unref(sessionManagerProxy); + sessionManagerProxy = 0; + } + + if (g_signal_callback != null) { + g_signal_callback.dispose(); + g_signal_callback = null; + } + + shutdownHook.remove(); + } + + private void sendEndSessionResponse(boolean is_ok, String reason) { + long args = OS.g_variant_new( + Converter.javaStringToCString("(bs)"), //$NON-NLS-1$ + is_ok, + Converter.javaStringToCString(reason)); + + long [] error = new long [1]; + OS.g_dbus_proxy_call( + clientProxy, + Converter.javaStringToCString("EndSessionResponse"), //$NON-NLS-1$ + args, + OS.G_DBUS_CALL_FLAGS_NONE, + dbusTimeoutMsec, + 0, + 0, + error); + + if (error[0] != 0) { + System.err.format( + "SWT SessionManagerDBus: Failed to EndSessionResponse: %s%n", + extractFreeGError(error[0])); + } + } + + private boolean isReadyToExit() { + boolean isReady = true; + + // Inform everyone even if someone is not ready. + for (int i = 0; i < listeners.size(); i++) { + IListener listener = listeners.get(i); + isReady = isReady && listener.isReadyToExit(); + } + + return isReady; + } + + private void handleStop() { + for (int i = 0; i < listeners.size(); i++) { + IListener listener = listeners.get(i); + listener.stop(); + } + } + + /** + * Receives events from session manager. + * + * Docs: https://developer.gnome.org/gio/stable/GDBusProxy.html#GDBusProxy-g-signal + * NOTE: Will be called through native callback. + * @see this.g_signal_callback + * @return Error string in case of error, null if successful. + */ + @SuppressWarnings("unused") + private long g_signal_handler(long proxy, long sender_name, long signal_name, long parameters, long user_data) { + String signalName = Converter.cCharPtrToJavaString(signal_name, false); + + switch (signalName) { + case "QueryEndSession": //$NON-NLS-1$ + sendEndSessionResponse(isReadyToExit(), ""); + break; + case "EndSession": //$NON-NLS-1$ + handleStop(); + // Only respond after we've done, or session can end while we're still working. + // Even if we don't want the session to end, I don't think sending 'false' here can be of any help. + sendEndSessionResponse(true, ""); + break; + case "Stop": + handleStop(); + break; + } + + // DBus expects 'void', but to make it easier to use with 'Callback' I return 'long'. + return 0; + } + + private static String extractVariantTupleS(long variant) { + long childVariant = OS.g_variant_get_child_value(variant, 0); + long childString = OS.g_variant_get_string(childVariant, null); + + String result = Converter.cCharPtrToJavaString(childString, false); + + OS.g_variant_unref(childVariant); + return result; + } + + private static String extractFreeGError(long errorPtr) { + long errorMessageC = OS.g_error_get_message(errorPtr); + String errorMessageStr = Converter.cCharPtrToJavaString(errorMessageC, false); + OS.g_error_free(errorPtr); + return errorMessageStr; + } + + /** + * Creates a connection to the session manager. + * + * Saves result to member variable when successful. + * @return Error string in case of error, null if successful. + */ + private String connectSessionManager(String dbusName, String objectPath, String interfaceName) { + int sessionManagerFlags = + OS.G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + OS.G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + OS.G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS; + + long [] error = new long [1]; + long proxy = OS.g_dbus_proxy_new_for_bus_sync( + OS.G_BUS_TYPE_SESSION, + sessionManagerFlags, + 0, + Converter.javaStringToCString(dbusName), + Converter.javaStringToCString(objectPath), + Converter.javaStringToCString(interfaceName), + 0, + error); + + // Proxy is usually created even for non-existent service names. + if (proxy == 0) return extractFreeGError(error[0]); + + // Is the service actually present? + long owner = OS.g_dbus_proxy_get_name_owner(proxy); + if (owner == 0) { + OS.g_object_unref(proxy); + return "Service not present"; + } + OS.g_free(owner); + + // Success + sessionManagerProxy = proxy; + return null; + } + + private boolean connectSessionManager() { + String errorGnome = connectSessionManager( + "org.gnome.SessionManager", //$NON-NLS-1$ + "/org/gnome/SessionManager", //$NON-NLS-1$ + "org.gnome.SessionManager"); //$NON-NLS-1$ + + if (errorGnome == null) { + isGnome = true; + return true; + } + + String errorXCFE = connectSessionManager( + "org.xfce.SessionManager", //$NON-NLS-1$ + "/org/xfce/SessionManager", //$NON-NLS-1$ + "org.xfce.Session.Manager"); //$NON-NLS-1$ + + if (errorXCFE == null) { + isGnome = false; + return true; + } + + System.err.format( + "SWT SessionManagerDBus: Failed to connect to SessionManager (gnome: %s, xcfe: %s)%n", + errorGnome, + errorXCFE); + + return false; + } + + /** + * Gets the value of 'DESKTOP_AUTOSTART_ID'. + * + * This environment variable is set by session manager if the + * application was auto started (because it is configured to run + * automatically for every session). The variable helps session + * manager to match autostart settings with actual applications. + * + * For applications that were not started automatically, the + * variable is expected to be absent. + * + * Once used, 'DESKTOP_AUTOSTART_ID' must not leak into child + * processes, or they will fail to 'RegisterClient'. + */ + private String claimDesktopAutostartID() { + byte[] DESKTOP_AUTOSTART_ID = Converter.javaStringToCString("DESKTOP_AUTOSTART_ID"); //$NON-NLS-1$ + + // NOTE: the returned pointer is not valid after g_unsetenv() + long valueC = OS.g_getenv(DESKTOP_AUTOSTART_ID); + if (valueC == 0) return null; + String result = Converter.cCharPtrToJavaString(valueC, false); + + // Unset value, so it doesn't leak into child processes + OS.g_unsetenv(DESKTOP_AUTOSTART_ID); + + return result; + } + + /** + * Issues 'RegisterClient' dbus request to register with session manager. + * + * Saves result to member variable when successful. + * @return Error string in case of error, null if successful. + */ + private String registerClient(String appID, String clientStartupID) { + long args = OS.g_variant_new( + Converter.javaStringToCString("(ss)"), //$NON-NLS-1$ + Converter.javaStringToCString(appID), + Converter.javaStringToCString(clientStartupID)); + + long [] error = new long [1]; + long clientInfo = OS.g_dbus_proxy_call_sync( + sessionManagerProxy, + Converter.javaStringToCString("RegisterClient"), //$NON-NLS-1$ + args, + OS.G_DBUS_CALL_FLAGS_NONE, + dbusTimeoutMsec, + 0, + error); + + if (clientInfo == 0) return extractFreeGError(error[0]); + + // Success + clientObjectPath = extractVariantTupleS(clientInfo); + OS.g_variant_unref(clientInfo); + return null; + } + + private boolean registerClient() { + // This ID doesn't matter much, at least according to what I know. + // Still, I decided to make it customizable for those who love identity. + String appID = System.getProperty("org.eclipse.swt.internal.SessionManagerDBus.appID"); //$NON-NLS-1$ + if (appID == null) appID = "org.eclipse.swt.Application"; //$NON-NLS-1$ + + // Applications are expected to register using value of + // 'DESKTOP_AUTOSTART_ID' environment if it's present. + String desktopAutostartID = claimDesktopAutostartID(); + if (desktopAutostartID != null) { + String errorText = registerClient(appID, desktopAutostartID); + if (errorText == null) return true; + + // Bugged launchers use their 'DESKTOP_AUTOSTART_ID', but forget to unset it. + // This leaks a value that can't be used. + // The workaround is to retry with empty ID below. + // This pretends that parent's bug is already fixed. + boolean parentLeakedID = errorText.startsWith("GDBus.Error:org.gnome.SessionManager.AlreadyRegistered:"); //$NON-NLS-1$ + if (!parentLeakedID) return false; + } + + // In absence of 'DESKTOP_AUTOSTART_ID' just use empty ID. + String errorText = registerClient(appID, ""); + if (errorText == null) return true; + + // On XFCE 'RegisterClient' is only available since 4.13.0. + // Don't print this error since it's expected. + if (!isGnome && errorText.startsWith("GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: ")) //$NON-NLS-1$ + return false; + + System.err.format( + "SWT SessionManagerDBus: Failed to RegisterClient: %s%n", + errorText); + + return false; + } + + private boolean connectClientSignal() { + String dbusName; + String interfaceName; + if (isGnome) { + dbusName = "org.gnome.SessionManager"; //$NON-NLS-1$ + interfaceName = "org.gnome.SessionManager.ClientPrivate"; //$NON-NLS-1$ + } else { + dbusName = "org.xfce.SessionManager"; //$NON-NLS-1$ + interfaceName = "org.xfce.Session.Client"; //$NON-NLS-1$ + } + + long [] error = new long [1]; + clientProxy = OS.g_dbus_proxy_new_for_bus_sync( + OS.G_BUS_TYPE_SESSION, + 0, + 0, + Converter.javaStringToCString(dbusName), + Converter.javaStringToCString(clientObjectPath), + Converter.javaStringToCString(interfaceName), + 0, + error); + + if (clientProxy == 0) { + System.err.format( + "SWT SessionManagerDBus: Failed to connect to Client: %s%n", + extractFreeGError(error[0])); + return false; + } + + // The rest of the code makes this key call possible + g_signal_callback = new Callback(this, "g_signal_handler", 5); //$NON-NLS-1$ + OS.g_signal_connect( + clientProxy, + Converter.javaStringToCString("g-signal"), //$NON-NLS-1$ + g_signal_callback.getAddress(), + 0); + + return true; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java index 506106abeb..5c7fd36499 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java @@ -192,6 +192,26 @@ public class Display extends Device { Thread thread; /* Display Shutdown */ + private class SessionManagerListener implements SessionManagerDBus.IListener { + Display parent; + + public SessionManagerListener(Display parent) { + this.parent = parent; + } + + public boolean isReadyToExit() { + Event event = new Event (); + parent.sendEvent(SWT.Close, event); + return event.doit; + } + + public void stop() { + parent.dispose(); + } + } + + static SessionManagerDBus sessionManagerDBus = new SessionManagerDBus(); + SessionManagerListener sessionManagerListener; Runnable [] disposeList; /* Deferred Layout list */ @@ -3567,6 +3587,7 @@ protected void init () { initializeSystemSettings (); initializeWidgetTable (); initializeWindowManager (); + initializeSessionManager (); } void initializeCallbacks () { @@ -3960,6 +3981,11 @@ void initializeWindowManager () { } } +void initializeSessionManager() { + sessionManagerListener = new SessionManagerListener(this); + sessionManagerDBus.addListener(sessionManagerListener); +} + /** * Invokes platform specific functionality to dispose a GC handle. * <p> @@ -4692,6 +4718,7 @@ protected void release () { disposeList = null; synchronizer.releaseSynchronizer (); synchronizer = null; + sessionManagerDBus.removeListener(sessionManagerListener); releaseDisplay (); super.release (); } |