Latest eggsmclient
This commit is contained in:
parent
c3daead67e
commit
9ef26ef91a
@ -27,15 +27,12 @@
|
|||||||
|
|
||||||
#include "eggdesktopfile.h"
|
#include "eggdesktopfile.h"
|
||||||
|
|
||||||
#include <mooutils/moo-environ.h>
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#include <gtk/gtkwindow.h>
|
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
struct EggDesktopFile {
|
struct EggDesktopFile {
|
||||||
GKeyFile *key_file;
|
GKeyFile *key_file;
|
||||||
@ -58,7 +55,6 @@ struct EggDesktopFile {
|
|||||||
EggDesktopFile *
|
EggDesktopFile *
|
||||||
egg_desktop_file_new (const char *desktop_file_path, GError **error)
|
egg_desktop_file_new (const char *desktop_file_path, GError **error)
|
||||||
{
|
{
|
||||||
EggDesktopFile *desktop_file;
|
|
||||||
GKeyFile *key_file;
|
GKeyFile *key_file;
|
||||||
|
|
||||||
key_file = g_key_file_new ();
|
key_file = g_key_file_new ();
|
||||||
@ -68,13 +64,8 @@ egg_desktop_file_new (const char *desktop_file_path, GError **error)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_file = egg_desktop_file_new_from_key_file (key_file,
|
return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
|
||||||
desktop_file_path,
|
error);
|
||||||
error);
|
|
||||||
if (!desktop_file)
|
|
||||||
g_key_file_free (key_file);
|
|
||||||
|
|
||||||
return desktop_file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,9 +99,42 @@ egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
|
|||||||
full_path,
|
full_path,
|
||||||
error);
|
error);
|
||||||
g_free (full_path);
|
g_free (full_path);
|
||||||
if (!desktop_file)
|
return desktop_file;
|
||||||
g_key_file_free (key_file);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* egg_desktop_file_new_from_dirs:
|
||||||
|
* @desktop_file_path: relative path to a Freedesktop-style Desktop file
|
||||||
|
* @search_dirs: NULL-terminated array of directories to search
|
||||||
|
* @error: error pointer
|
||||||
|
*
|
||||||
|
* Looks for @desktop_file_path in the paths returned from
|
||||||
|
* g_get_user_data_dir() and g_get_system_data_dirs(), and creates
|
||||||
|
* a new #EggDesktopFile from it.
|
||||||
|
*
|
||||||
|
* Return value: the new #EggDesktopFile, or %NULL on error.
|
||||||
|
**/
|
||||||
|
EggDesktopFile *
|
||||||
|
egg_desktop_file_new_from_dirs (const char *desktop_file_path,
|
||||||
|
const char **search_dirs,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
EggDesktopFile *desktop_file;
|
||||||
|
GKeyFile *key_file;
|
||||||
|
char *full_path;
|
||||||
|
|
||||||
|
key_file = g_key_file_new ();
|
||||||
|
if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs,
|
||||||
|
&full_path, 0, error))
|
||||||
|
{
|
||||||
|
g_key_file_free (key_file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
desktop_file = egg_desktop_file_new_from_key_file (key_file,
|
||||||
|
full_path,
|
||||||
|
error);
|
||||||
|
g_free (full_path);
|
||||||
return desktop_file;
|
return desktop_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +145,8 @@ egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
|
|||||||
* @error: error pointer
|
* @error: error pointer
|
||||||
*
|
*
|
||||||
* Creates a new #EggDesktopFile for @key_file. Assumes ownership of
|
* Creates a new #EggDesktopFile for @key_file. Assumes ownership of
|
||||||
* @key_file on success (meaning it will be freed when the desktop_file
|
* @key_file (on success or failure); you should consider @key_file to
|
||||||
* is freed).
|
* be freed after calling this function.
|
||||||
*
|
*
|
||||||
* Return value: the new #EggDesktopFile, or %NULL on error.
|
* Return value: the new #EggDesktopFile, or %NULL on error.
|
||||||
**/
|
**/
|
||||||
@ -139,6 +163,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
|||||||
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
|
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
|
||||||
EGG_DESKTOP_FILE_ERROR_INVALID,
|
EGG_DESKTOP_FILE_ERROR_INVALID,
|
||||||
_("File is not a valid .desktop file"));
|
_("File is not a valid .desktop file"));
|
||||||
|
g_key_file_free (key_file);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,13 +187,14 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
|||||||
EGG_DESKTOP_FILE_ERROR_INVALID,
|
EGG_DESKTOP_FILE_ERROR_INVALID,
|
||||||
_("Unrecognized desktop file Version '%s'"), version);
|
_("Unrecognized desktop file Version '%s'"), version);
|
||||||
g_free (version);
|
g_free (version);
|
||||||
|
g_key_file_free (key_file);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
g_free (version);
|
g_free (version);
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_file = g_new0 (EggDesktopFile, 1);
|
desktop_file = g_new0 (EggDesktopFile, 1);
|
||||||
|
desktop_file->key_file = key_file;
|
||||||
|
|
||||||
if (g_path_is_absolute (source))
|
if (g_path_is_absolute (source))
|
||||||
desktop_file->source = g_filename_to_uri (source, NULL, NULL);
|
desktop_file->source = g_filename_to_uri (source, NULL, NULL);
|
||||||
@ -204,6 +230,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
|||||||
if (!exec)
|
if (!exec)
|
||||||
{
|
{
|
||||||
egg_desktop_file_free (desktop_file);
|
egg_desktop_file_free (desktop_file);
|
||||||
|
g_free (type);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +263,7 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
|||||||
if (!url)
|
if (!url)
|
||||||
{
|
{
|
||||||
egg_desktop_file_free (desktop_file);
|
egg_desktop_file_free (desktop_file);
|
||||||
|
g_free (type);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
g_free (url);
|
g_free (url);
|
||||||
@ -245,6 +273,8 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
|||||||
else
|
else
|
||||||
desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
|
desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
|
||||||
|
|
||||||
|
g_free (type);
|
||||||
|
|
||||||
/* Check the Icon key */
|
/* Check the Icon key */
|
||||||
desktop_file->icon = g_key_file_get_string (key_file,
|
desktop_file->icon = g_key_file_get_string (key_file,
|
||||||
EGG_DESKTOP_FILE_GROUP,
|
EGG_DESKTOP_FILE_GROUP,
|
||||||
@ -268,7 +298,6 @@ egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_file->key_file = key_file;
|
|
||||||
return desktop_file;
|
return desktop_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,22 +317,6 @@ egg_desktop_file_free (EggDesktopFile *desktop_file)
|
|||||||
g_free (desktop_file);
|
g_free (desktop_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* egg_desktop_file_get_key_file:
|
|
||||||
* @desktop_file: an #EggDesktopFile
|
|
||||||
*
|
|
||||||
* Gets the #GKeyFile associated with @desktop_file. You must not free
|
|
||||||
* this value, and changes made to it will not be reflected by
|
|
||||||
* @desktop_file.
|
|
||||||
*
|
|
||||||
* Return value: the #GKeyFile associated with @desktop_file.
|
|
||||||
**/
|
|
||||||
GKeyFile *
|
|
||||||
egg_desktop_file_get_key_file (EggDesktopFile *desktop_file)
|
|
||||||
{
|
|
||||||
return desktop_file->key_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* egg_desktop_file_get_source:
|
* egg_desktop_file_get_source:
|
||||||
* @desktop_file: an #EggDesktopFile
|
* @desktop_file: an #EggDesktopFile
|
||||||
@ -367,6 +380,91 @@ egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
|
|||||||
return desktop_file->icon;
|
return desktop_file->icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
egg_desktop_file_has_key (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_has_key (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
egg_desktop_file_get_string (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_string (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
const char *locale,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_locale_string (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key, locale,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
egg_desktop_file_get_boolean (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_boolean (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
egg_desktop_file_get_numeric (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_double (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
egg_desktop_file_get_integer (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_integer (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
char **
|
||||||
|
egg_desktop_file_get_string_list (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
gsize *length,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_string_list (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key, length,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
char **
|
||||||
|
egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
const char *locale,
|
||||||
|
gsize *length,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_key_file_get_locale_string_list (desktop_file->key_file,
|
||||||
|
EGG_DESKTOP_FILE_GROUP, key,
|
||||||
|
locale, length,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* egg_desktop_file_can_launch:
|
* egg_desktop_file_can_launch:
|
||||||
* @desktop_file: an #EggDesktopFile
|
* @desktop_file: an #EggDesktopFile
|
||||||
@ -821,7 +919,7 @@ parse_link (EggDesktopFile *desktop_file,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
|
#if GTK_CHECK_VERSION (2, 12, 0)
|
||||||
static char *
|
static char *
|
||||||
start_startup_notification (GdkDisplay *display,
|
start_startup_notification (GdkDisplay *display,
|
||||||
EggDesktopFile *desktop_file,
|
EggDesktopFile *desktop_file,
|
||||||
@ -899,7 +997,7 @@ end_startup_notification (GdkDisplay *display,
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000)
|
#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GdkDisplay *display;
|
GdkDisplay *display;
|
||||||
@ -929,10 +1027,10 @@ set_startup_notification_timeout (GdkDisplay *display,
|
|||||||
sn_data->display = g_object_ref (display);
|
sn_data->display = g_object_ref (display);
|
||||||
sn_data->startup_id = g_strdup (startup_id);
|
sn_data->startup_id = g_strdup (startup_id);
|
||||||
|
|
||||||
g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
|
g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
|
||||||
startup_notification_timeout, sn_data);
|
startup_notification_timeout, sn_data);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
|
#endif /* GTK 2.12 */
|
||||||
|
|
||||||
static GPtrArray *
|
static GPtrArray *
|
||||||
array_putenv (GPtrArray *env, char *variable)
|
array_putenv (GPtrArray *env, char *variable)
|
||||||
@ -941,12 +1039,20 @@ array_putenv (GPtrArray *env, char *variable)
|
|||||||
|
|
||||||
if (!env)
|
if (!env)
|
||||||
{
|
{
|
||||||
char **environ_ptr = environ;
|
char **envp;
|
||||||
|
|
||||||
env = g_ptr_array_new ();
|
env = g_ptr_array_new ();
|
||||||
|
|
||||||
for (i = 0; environ_ptr[i]; i++)
|
envp = g_listenv ();
|
||||||
g_ptr_array_add (env, g_strdup (environ_ptr[i]));
|
for (i = 0; envp[i]; i++)
|
||||||
|
{
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
value = g_getenv (envp[i]);
|
||||||
|
g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
|
||||||
|
value ? value : ""));
|
||||||
|
}
|
||||||
|
g_strfreev (envp);
|
||||||
}
|
}
|
||||||
|
|
||||||
keylen = strcspn (variable, "=");
|
keylen = strcspn (variable, "=");
|
||||||
@ -976,7 +1082,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
EggDesktopFileLaunchOption option;
|
EggDesktopFileLaunchOption option;
|
||||||
GSList *translated_documents = NULL, *docs;
|
GSList *translated_documents = NULL, *docs = NULL;
|
||||||
char *command, **argv;
|
char *command, **argv;
|
||||||
int argc, i, screen_num;
|
int argc, i, screen_num;
|
||||||
gboolean success, current_success;
|
gboolean success, current_success;
|
||||||
@ -1111,7 +1217,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file,
|
|||||||
}
|
}
|
||||||
g_free (command);
|
g_free (command);
|
||||||
|
|
||||||
#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
|
#if GTK_CHECK_VERSION (2, 12, 0)
|
||||||
startup_id = start_startup_notification (display, desktop_file,
|
startup_id = start_startup_notification (display, desktop_file,
|
||||||
argv[0], screen_num,
|
argv[0], screen_num,
|
||||||
workspace, launch_time);
|
workspace, launch_time);
|
||||||
@ -1124,7 +1230,10 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file,
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
startup_id = NULL;
|
startup_id = NULL;
|
||||||
#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
|
#endif /* GTK 2.12 */
|
||||||
|
|
||||||
|
if (env != NULL)
|
||||||
|
g_ptr_array_add (env, NULL);
|
||||||
|
|
||||||
current_success =
|
current_success =
|
||||||
g_spawn_async_with_pipes (directory,
|
g_spawn_async_with_pipes (directory,
|
||||||
@ -1139,7 +1248,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file,
|
|||||||
|
|
||||||
if (startup_id)
|
if (startup_id)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
|
#if GTK_CHECK_VERSION (2, 12, 0)
|
||||||
if (current_success)
|
if (current_success)
|
||||||
{
|
{
|
||||||
set_startup_notification_timeout (display, startup_id);
|
set_startup_notification_timeout (display, startup_id);
|
||||||
@ -1150,7 +1259,7 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file,
|
|||||||
g_free (startup_id);
|
g_free (startup_id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
|
#endif /* GTK 2.12 */
|
||||||
g_free (startup_id);
|
g_free (startup_id);
|
||||||
}
|
}
|
||||||
else if (ret_startup_id)
|
else if (ret_startup_id)
|
||||||
@ -1175,8 +1284,8 @@ egg_desktop_file_launchv (EggDesktopFile *desktop_file,
|
|||||||
out:
|
out:
|
||||||
if (env)
|
if (env)
|
||||||
{
|
{
|
||||||
g_strfreev ((char **)env->pdata);
|
g_ptr_array_foreach (env, (GFunc)g_free, NULL);
|
||||||
g_ptr_array_free (env, FALSE);
|
g_ptr_array_free (env, TRUE);
|
||||||
}
|
}
|
||||||
free_document_list (translated_documents);
|
free_document_list (translated_documents);
|
||||||
|
|
||||||
@ -1287,6 +1396,8 @@ egg_desktop_file_launch (EggDesktopFile *desktop_file,
|
|||||||
free_document_list (documents);
|
free_document_list (documents);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED:
|
||||||
|
case EGG_DESKTOP_FILE_TYPE_DIRECTORY:
|
||||||
default:
|
default:
|
||||||
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
|
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
|
||||||
EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
|
EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
|
||||||
@ -1309,23 +1420,9 @@ egg_desktop_file_error_quark (void)
|
|||||||
G_LOCK_DEFINE_STATIC (egg_desktop_file);
|
G_LOCK_DEFINE_STATIC (egg_desktop_file);
|
||||||
static EggDesktopFile *egg_desktop_file;
|
static EggDesktopFile *egg_desktop_file;
|
||||||
|
|
||||||
/**
|
static void
|
||||||
* egg_set_desktop_file:
|
egg_set_desktop_file_internal (const char *desktop_file_path,
|
||||||
* @desktop_file_path: path to the application's desktop file
|
gboolean set_defaults)
|
||||||
*
|
|
||||||
* Creates an #EggDesktopFile for the application from the data at
|
|
||||||
* @desktop_file_path. This will also call g_set_application_name()
|
|
||||||
* with the localized application name from the desktop file, and
|
|
||||||
* gtk_window_set_default_icon_name() or
|
|
||||||
* gtk_window_set_default_icon_from_file() with the application's
|
|
||||||
* icon. Other code may use additional information from the desktop
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* Note that for thread safety reasons, this function can only
|
|
||||||
* be called once.
|
|
||||||
**/
|
|
||||||
void
|
|
||||||
egg_set_desktop_file (const char *desktop_file_path)
|
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
@ -1341,20 +1438,67 @@ egg_set_desktop_file (const char *desktop_file_path)
|
|||||||
g_error_free (error);
|
g_error_free (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set localized application name and default window icon */
|
if (set_defaults && egg_desktop_file != NULL) {
|
||||||
if (egg_desktop_file->name)
|
/* Set localized application name and default window icon */
|
||||||
g_set_application_name (egg_desktop_file->name);
|
if (egg_desktop_file->name)
|
||||||
if (egg_desktop_file->icon)
|
g_set_application_name (egg_desktop_file->name);
|
||||||
{
|
if (egg_desktop_file->icon)
|
||||||
if (g_path_is_absolute (egg_desktop_file->icon))
|
{
|
||||||
gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
|
if (g_path_is_absolute (egg_desktop_file->icon))
|
||||||
else
|
gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
|
||||||
gtk_window_set_default_icon_name (egg_desktop_file->icon);
|
else
|
||||||
}
|
gtk_window_set_default_icon_name (egg_desktop_file->icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
G_UNLOCK (egg_desktop_file);
|
G_UNLOCK (egg_desktop_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* egg_set_desktop_file:
|
||||||
|
* @desktop_file_path: path to the application's desktop file
|
||||||
|
*
|
||||||
|
* Creates an #EggDesktopFile for the application from the data at
|
||||||
|
* @desktop_file_path. This will also call g_set_application_name()
|
||||||
|
* with the localized application name from the desktop file, and
|
||||||
|
* gtk_window_set_default_icon_name() or
|
||||||
|
* gtk_window_set_default_icon_from_file() with the application's
|
||||||
|
* icon. Other code may use additional information from the desktop
|
||||||
|
* file.
|
||||||
|
* See egg_set_desktop_file_without_defaults() for a variant of this
|
||||||
|
* function that does not set the application name and default window
|
||||||
|
* icon.
|
||||||
|
*
|
||||||
|
* Note that for thread safety reasons, this function can only
|
||||||
|
* be called once, and is mutually exclusive with calling
|
||||||
|
* egg_set_desktop_file_without_defaults().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
egg_set_desktop_file (const char *desktop_file_path)
|
||||||
|
{
|
||||||
|
egg_set_desktop_file_internal (desktop_file_path, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* egg_set_desktop_file_without_defaults:
|
||||||
|
* @desktop_file_path: path to the application's desktop file
|
||||||
|
*
|
||||||
|
* Creates an #EggDesktopFile for the application from the data at
|
||||||
|
* @desktop_file_path.
|
||||||
|
* See egg_set_desktop_file() for a variant of this function that
|
||||||
|
* sets the application name and default window icon from the information
|
||||||
|
* in the desktop file.
|
||||||
|
*
|
||||||
|
* Note that for thread safety reasons, this function can only
|
||||||
|
* be called once, and is mutually exclusive with calling
|
||||||
|
* egg_set_desktop_file().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
egg_set_desktop_file_without_defaults (const char *desktop_file_path)
|
||||||
|
{
|
||||||
|
egg_set_desktop_file_internal (desktop_file_path, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* egg_get_desktop_file:
|
* egg_get_desktop_file:
|
||||||
*
|
*
|
||||||
|
@ -37,16 +37,17 @@ typedef enum {
|
|||||||
EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path,
|
EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
|
EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
|
||||||
GError **error);
|
GError **error);
|
||||||
EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *desktop,
|
EggDesktopFile *egg_desktop_file_new_from_dirs (const char *desktop_file_path,
|
||||||
|
const char **search_dirs,
|
||||||
|
GError **error);
|
||||||
|
EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
||||||
const char *source,
|
const char *source,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
void egg_desktop_file_free (EggDesktopFile *desktop_file);
|
void egg_desktop_file_free (EggDesktopFile *desktop_file);
|
||||||
|
|
||||||
GKeyFile *egg_desktop_file_get_key_file (EggDesktopFile *desktop_file);
|
|
||||||
|
|
||||||
const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file);
|
const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file);
|
||||||
|
|
||||||
EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file);
|
EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file);
|
||||||
@ -109,6 +110,37 @@ typedef enum {
|
|||||||
#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass"
|
#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass"
|
||||||
#define EGG_DESKTOP_FILE_KEY_URL "URL"
|
#define EGG_DESKTOP_FILE_KEY_URL "URL"
|
||||||
|
|
||||||
|
/* Accessors */
|
||||||
|
gboolean egg_desktop_file_has_key (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error);
|
||||||
|
char *egg_desktop_file_get_string (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error) G_GNUC_MALLOC;
|
||||||
|
char *egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
const char *locale,
|
||||||
|
GError **error) G_GNUC_MALLOC;
|
||||||
|
gboolean egg_desktop_file_get_boolean (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error);
|
||||||
|
double egg_desktop_file_get_numeric (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error);
|
||||||
|
int egg_desktop_file_get_integer (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
GError **error);
|
||||||
|
char **egg_desktop_file_get_string_list (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
gsize *length,
|
||||||
|
GError **error) G_GNUC_MALLOC;
|
||||||
|
char **egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file,
|
||||||
|
const char *key,
|
||||||
|
const char *locale,
|
||||||
|
gsize *length,
|
||||||
|
GError **error) G_GNUC_MALLOC;
|
||||||
|
|
||||||
|
|
||||||
/* Errors */
|
/* Errors */
|
||||||
#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark()
|
#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark()
|
||||||
|
|
||||||
@ -121,8 +153,9 @@ typedef enum {
|
|||||||
} EggDesktopFileError;
|
} EggDesktopFileError;
|
||||||
|
|
||||||
/* Global application desktop file */
|
/* Global application desktop file */
|
||||||
void egg_set_desktop_file (const char *desktop_file_path);
|
void egg_set_desktop_file (const char *desktop_file_path);
|
||||||
EggDesktopFile *egg_get_desktop_file (void);
|
void egg_set_desktop_file_without_defaults (const char *desktop_file_path);
|
||||||
|
EggDesktopFile *egg_get_desktop_file (void);
|
||||||
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
294
moo/eggsmclient/eggsmclient-dbus.c
Normal file
294
moo/eggsmclient/eggsmclient-dbus.c
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2008 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "eggsmclient.h"
|
||||||
|
#include "eggsmclient-private.h"
|
||||||
|
|
||||||
|
#include "eggdesktopfile.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#include <dbus/dbus-glib.h>
|
||||||
|
|
||||||
|
#define GSM_DBUS_NAME "org.gnome.SessionManager"
|
||||||
|
#define GSM_DBUS_PATH "/org/gnome/SessionManager"
|
||||||
|
#define GSM_DBUS_INTERFACE "org.gnome.SessionManager"
|
||||||
|
|
||||||
|
#define GSM_CLIENT_PRIVATE_DBUS_INTERFACE "org.gnome.SessionManager.ClientPrivate"
|
||||||
|
#define GSM_CLIENT_DBUS_INTERFACE "org.gnome.SessionManager.Client"
|
||||||
|
|
||||||
|
#define EGG_TYPE_SM_CLIENT_DBUS (egg_sm_client_dbus_get_type ())
|
||||||
|
#define EGG_SM_CLIENT_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_DBUS, EggSMClientDBus))
|
||||||
|
#define EGG_SM_CLIENT_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_DBUS, EggSMClientDBusClass))
|
||||||
|
#define EGG_IS_SM_CLIENT_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_DBUS))
|
||||||
|
#define EGG_IS_SM_CLIENT_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_DBUS))
|
||||||
|
#define EGG_SM_CLIENT_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_DBUS, EggSMClientDBusClass))
|
||||||
|
|
||||||
|
typedef struct _EggSMClientDBus EggSMClientDBus;
|
||||||
|
typedef struct _EggSMClientDBusClass EggSMClientDBusClass;
|
||||||
|
|
||||||
|
struct _EggSMClientDBus
|
||||||
|
{
|
||||||
|
EggSMClient parent;
|
||||||
|
|
||||||
|
DBusGConnection *conn;
|
||||||
|
DBusGProxy *sm_proxy, *client_proxy;
|
||||||
|
char *client_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _EggSMClientDBusClass
|
||||||
|
{
|
||||||
|
EggSMClientClass parent_class;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sm_client_dbus_startup (EggSMClient *client,
|
||||||
|
const char *client_id);
|
||||||
|
static void sm_client_dbus_will_quit (EggSMClient *client,
|
||||||
|
gboolean will_quit);
|
||||||
|
static gboolean sm_client_dbus_end_session (EggSMClient *client,
|
||||||
|
EggSMClientEndStyle style,
|
||||||
|
gboolean request_confirmation);
|
||||||
|
|
||||||
|
static void dbus_client_query_end_session (DBusGProxy *proxy,
|
||||||
|
guint flags,
|
||||||
|
gpointer smclient);
|
||||||
|
static void dbus_client_end_session (DBusGProxy *proxy,
|
||||||
|
guint flags,
|
||||||
|
gpointer smclient);
|
||||||
|
static void dbus_client_cancel_end_session (DBusGProxy *proxy,
|
||||||
|
gpointer smclient);
|
||||||
|
static void dbus_client_stop (DBusGProxy *proxy,
|
||||||
|
gpointer smclient);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (EggSMClientDBus, egg_sm_client_dbus, EGG_TYPE_SM_CLIENT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
egg_sm_client_dbus_init (EggSMClientDBus *dbus)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
egg_sm_client_dbus_class_init (EggSMClientDBusClass *klass)
|
||||||
|
{
|
||||||
|
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
|
||||||
|
|
||||||
|
sm_client_class->startup = sm_client_dbus_startup;
|
||||||
|
sm_client_class->will_quit = sm_client_dbus_will_quit;
|
||||||
|
sm_client_class->end_session = sm_client_dbus_end_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
EggSMClient *
|
||||||
|
egg_sm_client_dbus_new (void)
|
||||||
|
{
|
||||||
|
DBusGConnection *conn;
|
||||||
|
DBusGProxy *proxy;
|
||||||
|
EggSMClientDBus *dbus;
|
||||||
|
|
||||||
|
conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
proxy = dbus_g_proxy_new_for_name (conn, GSM_DBUS_NAME, GSM_DBUS_PATH,
|
||||||
|
GSM_DBUS_INTERFACE);
|
||||||
|
if (!proxy)
|
||||||
|
{
|
||||||
|
g_object_unref (conn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus = g_object_new (EGG_TYPE_SM_CLIENT_DBUS, NULL);
|
||||||
|
dbus->conn = conn;
|
||||||
|
dbus->sm_proxy = proxy;
|
||||||
|
return (EggSMClient *)dbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sm_client_dbus_startup (EggSMClient *client,
|
||||||
|
const char *client_id)
|
||||||
|
{
|
||||||
|
EggSMClientDBus *dbus = (EggSMClientDBus *)client;
|
||||||
|
GError *error = NULL;
|
||||||
|
char *client_path, *ret_client_id;
|
||||||
|
DBusGProxy *client_public;
|
||||||
|
|
||||||
|
if (!dbus_g_proxy_call (dbus->sm_proxy, "RegisterClient", &error,
|
||||||
|
G_TYPE_STRING, g_get_prgname (),
|
||||||
|
G_TYPE_STRING, client_id,
|
||||||
|
G_TYPE_INVALID,
|
||||||
|
DBUS_TYPE_G_OBJECT_PATH, &client_path,
|
||||||
|
G_TYPE_INVALID))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to register client: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_debug ("Client registered with session manager: %s", client_path);
|
||||||
|
dbus->client_proxy = dbus_g_proxy_new_for_name (dbus->conn, GSM_DBUS_NAME,
|
||||||
|
client_path,
|
||||||
|
GSM_CLIENT_PRIVATE_DBUS_INTERFACE);
|
||||||
|
dbus_g_proxy_add_signal (dbus->client_proxy, "QueryEndSession",
|
||||||
|
G_TYPE_UINT,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
dbus_g_proxy_connect_signal (dbus->client_proxy, "QueryEndSession",
|
||||||
|
G_CALLBACK (dbus_client_query_end_session),
|
||||||
|
dbus, NULL);
|
||||||
|
dbus_g_proxy_add_signal (dbus->client_proxy, "EndSession",
|
||||||
|
G_TYPE_UINT,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
dbus_g_proxy_connect_signal (dbus->client_proxy, "EndSession",
|
||||||
|
G_CALLBACK (dbus_client_end_session),
|
||||||
|
dbus, NULL);
|
||||||
|
dbus_g_proxy_add_signal (dbus->client_proxy, "CancelEndSession",
|
||||||
|
G_TYPE_UINT,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
dbus_g_proxy_connect_signal (dbus->client_proxy, "CancelEndSession",
|
||||||
|
G_CALLBACK (dbus_client_cancel_end_session),
|
||||||
|
dbus, NULL);
|
||||||
|
dbus_g_proxy_add_signal (dbus->client_proxy, "Stop",
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
dbus_g_proxy_connect_signal (dbus->client_proxy, "Stop",
|
||||||
|
G_CALLBACK (dbus_client_stop),
|
||||||
|
dbus, NULL);
|
||||||
|
|
||||||
|
client_public = dbus_g_proxy_new_for_name (dbus->conn, GSM_DBUS_NAME,
|
||||||
|
client_path,
|
||||||
|
GSM_CLIENT_DBUS_INTERFACE);
|
||||||
|
if (dbus_g_proxy_call (client_public, "GetStartupId", &error,
|
||||||
|
G_TYPE_INVALID,
|
||||||
|
G_TYPE_STRING, &ret_client_id,
|
||||||
|
G_TYPE_INVALID))
|
||||||
|
{
|
||||||
|
gdk_threads_enter ();
|
||||||
|
gdk_set_sm_client_id (ret_client_id);
|
||||||
|
gdk_threads_leave ();
|
||||||
|
|
||||||
|
g_debug ("Got client ID \"%s\"", ret_client_id);
|
||||||
|
g_free (ret_client_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_warning ("Could not get client id: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
g_object_unref (client_public);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sm_client_dbus_shutdown (EggSMClient *client)
|
||||||
|
{
|
||||||
|
EggSMClientDBus *dbus = EGG_SM_CLIENT_DBUS (client);
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!dbus_g_proxy_call (dbus->sm_proxy, "UnregisterClient", &error,
|
||||||
|
DBUS_TYPE_G_OBJECT_PATH, dbus->client_path,
|
||||||
|
G_TYPE_INVALID,
|
||||||
|
G_TYPE_INVALID))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to unregister client: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (dbus->client_path);
|
||||||
|
dbus->client_path = NULL;
|
||||||
|
|
||||||
|
g_object_unref (dbus->client_proxy);
|
||||||
|
dbus->client_proxy = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbus_client_query_end_session (DBusGProxy *proxy,
|
||||||
|
guint flags,
|
||||||
|
gpointer smclient)
|
||||||
|
{
|
||||||
|
egg_sm_client_quit_requested (smclient);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sm_client_dbus_will_quit (EggSMClient *client,
|
||||||
|
gboolean will_quit)
|
||||||
|
{
|
||||||
|
EggSMClientDBus *dbus = (EggSMClientDBus *)client;
|
||||||
|
|
||||||
|
g_return_if_fail (dbus->client_proxy != NULL);
|
||||||
|
|
||||||
|
dbus_g_proxy_call (dbus->client_proxy, "EndSessionResponse", NULL,
|
||||||
|
G_TYPE_BOOLEAN, will_quit,
|
||||||
|
G_TYPE_STRING, NULL,
|
||||||
|
G_TYPE_INVALID,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbus_client_end_session (DBusGProxy *proxy,
|
||||||
|
guint flags,
|
||||||
|
gpointer smclient)
|
||||||
|
{
|
||||||
|
sm_client_dbus_will_quit (smclient, TRUE);
|
||||||
|
sm_client_dbus_shutdown (smclient);
|
||||||
|
egg_sm_client_quit (smclient);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbus_client_cancel_end_session (DBusGProxy *proxy,
|
||||||
|
gpointer smclient)
|
||||||
|
{
|
||||||
|
egg_sm_client_quit_cancelled (smclient);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbus_client_stop (DBusGProxy *proxy,
|
||||||
|
gpointer smclient)
|
||||||
|
{
|
||||||
|
sm_client_dbus_shutdown (smclient);
|
||||||
|
egg_sm_client_quit (smclient);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sm_client_dbus_end_session (EggSMClient *client,
|
||||||
|
EggSMClientEndStyle style,
|
||||||
|
gboolean request_confirmation)
|
||||||
|
{
|
||||||
|
EggSMClientDBus *dbus = (EggSMClientDBus *)client;
|
||||||
|
|
||||||
|
if (style == EGG_SM_CLIENT_END_SESSION_DEFAULT ||
|
||||||
|
style == EGG_SM_CLIENT_LOGOUT)
|
||||||
|
{
|
||||||
|
return dbus_g_proxy_call (dbus->sm_proxy, "Logout", NULL,
|
||||||
|
G_TYPE_UINT, request_confirmation ? 0 : 1,
|
||||||
|
G_TYPE_INVALID,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return dbus_g_proxy_call (dbus->sm_proxy, "Shutdown", NULL,
|
||||||
|
G_TYPE_INVALID,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
}
|
||||||
|
}
|
235
moo/eggsmclient/eggsmclient-osx.c
Normal file
235
moo/eggsmclient/eggsmclient-osx.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 Novell, Inc.
|
||||||
|
* Copyright (C) 2008 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* EggSMClientOSX
|
||||||
|
*
|
||||||
|
* For details on the OS X logout process, see:
|
||||||
|
* http://developer.apple.com/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/BootProcess.html#//apple_ref/doc/uid/20002130-114618
|
||||||
|
*
|
||||||
|
* EggSMClientOSX registers for the kAEQuitApplication AppleEvent; the
|
||||||
|
* handler we register (quit_requested()) will be invoked from inside
|
||||||
|
* the quartz event-handling code (specifically, from inside
|
||||||
|
* [NSApplication nextEventMatchingMask]) when an AppleEvent arrives.
|
||||||
|
* We use AESuspendTheCurrentEvent() and AEResumeTheCurrentEvent() to
|
||||||
|
* allow asynchronous / non-main-loop-reentering processing of the
|
||||||
|
* quit request. (These are part of the Carbon framework; it doesn't
|
||||||
|
* seem to be possible to handle AppleEvents asynchronously from
|
||||||
|
* Cocoa.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "eggsmclient-private.h"
|
||||||
|
#include <glib.h>
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
|
||||||
|
#define EGG_TYPE_SM_CLIENT_OSX (egg_sm_client_osx_get_type ())
|
||||||
|
#define EGG_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSX))
|
||||||
|
#define EGG_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass))
|
||||||
|
#define EGG_IS_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_OSX))
|
||||||
|
#define EGG_IS_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_OSX))
|
||||||
|
#define EGG_SM_CLIENT_OSX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass))
|
||||||
|
|
||||||
|
typedef struct _EggSMClientOSX EggSMClientOSX;
|
||||||
|
typedef struct _EggSMClientOSXClass EggSMClientOSXClass;
|
||||||
|
|
||||||
|
struct _EggSMClientOSX {
|
||||||
|
EggSMClient parent;
|
||||||
|
|
||||||
|
AppleEvent quit_event, quit_reply;
|
||||||
|
gboolean quit_requested, quitting;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _EggSMClientOSXClass
|
||||||
|
{
|
||||||
|
EggSMClientClass parent_class;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sm_client_osx_startup (EggSMClient *client,
|
||||||
|
const char *client_id);
|
||||||
|
static void sm_client_osx_will_quit (EggSMClient *client,
|
||||||
|
gboolean will_quit);
|
||||||
|
static gboolean sm_client_osx_end_session (EggSMClient *client,
|
||||||
|
EggSMClientEndStyle style,
|
||||||
|
gboolean request_confirmation);
|
||||||
|
|
||||||
|
static pascal OSErr quit_requested (const AppleEvent *, AppleEvent *, long);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
egg_sm_client_osx_init (EggSMClientOSX *osx)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
egg_sm_client_osx_class_init (EggSMClientOSXClass *klass)
|
||||||
|
{
|
||||||
|
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
|
||||||
|
|
||||||
|
sm_client_class->startup = sm_client_osx_startup;
|
||||||
|
sm_client_class->will_quit = sm_client_osx_will_quit;
|
||||||
|
sm_client_class->end_session = sm_client_osx_end_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
EggSMClient *
|
||||||
|
egg_sm_client_osx_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sm_client_osx_startup (EggSMClient *client,
|
||||||
|
const char *client_id)
|
||||||
|
{
|
||||||
|
AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
|
||||||
|
NewAEEventHandlerUPP (quit_requested),
|
||||||
|
(long)GPOINTER_TO_SIZE (client), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
idle_quit_requested (gpointer client)
|
||||||
|
{
|
||||||
|
egg_sm_client_quit_requested (client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pascal OSErr
|
||||||
|
quit_requested (const AppleEvent *aevt, AppleEvent *reply, long refcon)
|
||||||
|
{
|
||||||
|
EggSMClient *client = GSIZE_TO_POINTER ((gsize)refcon);
|
||||||
|
EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
|
||||||
|
|
||||||
|
g_return_val_if_fail (!osx->quit_requested, userCanceledErr);
|
||||||
|
|
||||||
|
/* FIXME AEInteractWithUser? */
|
||||||
|
|
||||||
|
osx->quit_requested = TRUE;
|
||||||
|
AEDuplicateDesc (aevt, &osx->quit_event);
|
||||||
|
AEDuplicateDesc (reply, &osx->quit_reply);
|
||||||
|
AESuspendTheCurrentEvent (aevt);
|
||||||
|
|
||||||
|
/* Don't emit the "quit_requested" signal immediately, since we're
|
||||||
|
* called from a weird point in the guts of gdkeventloop-quartz.c
|
||||||
|
*/
|
||||||
|
g_idle_add (idle_quit_requested, client);
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pascal OSErr
|
||||||
|
quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, long refcon)
|
||||||
|
{
|
||||||
|
EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
|
||||||
|
|
||||||
|
osx->quit_requested = FALSE;
|
||||||
|
return osx->quitting ? noErr : userCanceledErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
idle_will_quit (gpointer client)
|
||||||
|
{
|
||||||
|
EggSMClientOSX *osx = (EggSMClientOSX *)client;
|
||||||
|
|
||||||
|
/* Resume the event with a new handler that will return a value to
|
||||||
|
* the system.
|
||||||
|
*/
|
||||||
|
AEResumeTheCurrentEvent (&osx->quit_event, &osx->quit_reply,
|
||||||
|
NewAEEventHandlerUPP (quit_requested_resumed),
|
||||||
|
(long)GPOINTER_TO_SIZE (client));
|
||||||
|
AEDisposeDesc (&osx->quit_event);
|
||||||
|
AEDisposeDesc (&osx->quit_reply);
|
||||||
|
|
||||||
|
if (osx->quitting)
|
||||||
|
egg_sm_client_quit (client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sm_client_osx_will_quit (EggSMClient *client,
|
||||||
|
gboolean will_quit)
|
||||||
|
{
|
||||||
|
EggSMClientOSX *osx = (EggSMClientOSX *)client;
|
||||||
|
|
||||||
|
g_return_if_fail (osx->quit_requested);
|
||||||
|
|
||||||
|
osx->quitting = will_quit;
|
||||||
|
|
||||||
|
/* Finish in an idle handler since the caller might have called
|
||||||
|
* egg_sm_client_will_quit() from inside the "quit_requested" signal
|
||||||
|
* handler, but may not expect the "quit" signal to arrive during
|
||||||
|
* the _will_quit() call.
|
||||||
|
*/
|
||||||
|
g_idle_add (idle_will_quit, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sm_client_osx_end_session (EggSMClient *client,
|
||||||
|
EggSMClientEndStyle style,
|
||||||
|
gboolean request_confirmation)
|
||||||
|
{
|
||||||
|
static const ProcessSerialNumber loginwindow_psn = { 0, kSystemProcess };
|
||||||
|
AppleEvent event = { typeNull, NULL }, reply = { typeNull, NULL };
|
||||||
|
AEAddressDesc target;
|
||||||
|
AEEventID id;
|
||||||
|
OSErr err;
|
||||||
|
|
||||||
|
switch (style)
|
||||||
|
{
|
||||||
|
case EGG_SM_CLIENT_END_SESSION_DEFAULT:
|
||||||
|
case EGG_SM_CLIENT_LOGOUT:
|
||||||
|
id = request_confirmation ? kAELogOut : kAEReallyLogOut;
|
||||||
|
break;
|
||||||
|
case EGG_SM_CLIENT_REBOOT:
|
||||||
|
id = request_confirmation ? kAEShowRestartDialog : kAERestart;
|
||||||
|
break;
|
||||||
|
case EGG_SM_CLIENT_SHUTDOWN:
|
||||||
|
id = request_confirmation ? kAEShowShutdownDialog : kAEShutDown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn,
|
||||||
|
sizeof (loginwindow_psn), &target);
|
||||||
|
if (err != noErr)
|
||||||
|
{
|
||||||
|
g_warning ("Could not create descriptor for loginwindow: %d", err);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = AECreateAppleEvent (kCoreEventClass, id, &target,
|
||||||
|
kAutoGenerateReturnID, kAnyTransactionID,
|
||||||
|
&event);
|
||||||
|
AEDisposeDesc (&target);
|
||||||
|
if (err != noErr)
|
||||||
|
{
|
||||||
|
g_warning ("Could not create logout AppleEvent: %d", err);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = AESend (&event, &reply, kAENoReply, kAENormalPriority,
|
||||||
|
kAEDefaultTimeout, NULL, NULL);
|
||||||
|
AEDisposeDesc (&event);
|
||||||
|
if (err == noErr)
|
||||||
|
AEDisposeDesc (&reply);
|
||||||
|
|
||||||
|
return err == noErr;
|
||||||
|
}
|
@ -20,13 +20,18 @@
|
|||||||
#ifndef __EGG_SM_CLIENT_PRIVATE_H__
|
#ifndef __EGG_SM_CLIENT_PRIVATE_H__
|
||||||
#define __EGG_SM_CLIENT_PRIVATE_H__
|
#define __EGG_SM_CLIENT_PRIVATE_H__
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#include <gtk/gtk.h>
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
||||||
|
#if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0)
|
||||||
|
/* GTK+ 3 includes this automatically */
|
||||||
|
#include <gdkconfig.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gdkconfig.h>
|
|
||||||
#include "eggsmclient.h"
|
#include "eggsmclient.h"
|
||||||
|
|
||||||
|
#undef G_LOG_DOMAIN
|
||||||
|
#define G_LOG_DOMAIN "EggSMClient"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
GKeyFile *egg_sm_client_save_state (EggSMClient *client);
|
GKeyFile *egg_sm_client_save_state (EggSMClient *client);
|
||||||
@ -46,12 +51,9 @@ EggSMClient *egg_sm_client_dbus_new (void);
|
|||||||
#elif defined (GDK_WINDOWING_WIN32)
|
#elif defined (GDK_WINDOWING_WIN32)
|
||||||
GType egg_sm_client_win32_get_type (void);
|
GType egg_sm_client_win32_get_type (void);
|
||||||
EggSMClient *egg_sm_client_win32_new (void);
|
EggSMClient *egg_sm_client_win32_new (void);
|
||||||
#elif defined (GDK_WINDOWING_QUARTZ) && 0
|
#elif defined (GDK_WINDOWING_QUARTZ)
|
||||||
GType egg_sm_client_osx_get_type (void);
|
GType egg_sm_client_osx_get_type (void);
|
||||||
EggSMClient *egg_sm_client_osx_new (void);
|
EggSMClient *egg_sm_client_osx_new (void);
|
||||||
#else
|
|
||||||
GType egg_sm_client_dummy_get_type (void);
|
|
||||||
EggSMClient *egg_sm_client_dummy_new (void);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
@ -17,12 +17,42 @@
|
|||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* EggSMClientWin32
|
||||||
|
*
|
||||||
|
* For details on the Windows XP logout process, see:
|
||||||
|
* http://msdn.microsoft.com/en-us/library/aa376876.aspx.
|
||||||
|
*
|
||||||
|
* Vista adds some new APIs which EggSMClient does not make use of; see
|
||||||
|
* http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx
|
||||||
|
*
|
||||||
|
* When shutting down, Windows sends every top-level window a
|
||||||
|
* WM_QUERYENDSESSION event, which the application must respond to
|
||||||
|
* synchronously, saying whether or not it will quit. To avoid main
|
||||||
|
* loop re-entrancy problems (and to avoid having to muck about too
|
||||||
|
* much with the guts of the gdk-win32 main loop), we watch for this
|
||||||
|
* event in a separate thread, which then signals the main thread and
|
||||||
|
* waits for the main thread to handle the event. Since we don't want
|
||||||
|
* to require g_thread_init() to be called, we do this all using
|
||||||
|
* Windows-specific thread methods.
|
||||||
|
*
|
||||||
|
* After the application handles the WM_QUERYENDSESSION event,
|
||||||
|
* Windows then sends it a WM_ENDSESSION event with a TRUE or FALSE
|
||||||
|
* parameter indicating whether the session is or is not actually
|
||||||
|
* going to end now. We handle this from the other thread as well.
|
||||||
|
*
|
||||||
|
* As mentioned above, Vista introduces several additional new APIs
|
||||||
|
* that don't fit into the (current) EggSMClient API. Windows also has
|
||||||
|
* an entirely separate shutdown-notification scheme for non-GUI apps,
|
||||||
|
* which we also don't handle here.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "eggsmclient-private.h"
|
#include "eggsmclient-private.h"
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <process.h>
|
||||||
|
|
||||||
#define EGG_TYPE_SM_CLIENT_WIN32 (egg_sm_client_win32_get_type ())
|
#define EGG_TYPE_SM_CLIENT_WIN32 (egg_sm_client_win32_get_type ())
|
||||||
#define EGG_SM_CLIENT_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32))
|
#define EGG_SM_CLIENT_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32))
|
||||||
@ -37,7 +67,10 @@ typedef struct _EggSMClientWin32Class EggSMClientWin32Class;
|
|||||||
struct _EggSMClientWin32 {
|
struct _EggSMClientWin32 {
|
||||||
EggSMClient parent;
|
EggSMClient parent;
|
||||||
|
|
||||||
GAsyncQueue *msg_queue;
|
HANDLE message_event, response_event;
|
||||||
|
|
||||||
|
volatile GSourceFunc event;
|
||||||
|
volatile gboolean will_quit;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _EggSMClientWin32Class
|
struct _EggSMClientWin32Class
|
||||||
@ -54,13 +87,17 @@ static gboolean sm_client_win32_end_session (EggSMClient *client,
|
|||||||
EggSMClientEndStyle style,
|
EggSMClientEndStyle style,
|
||||||
gboolean request_confirmation);
|
gboolean request_confirmation);
|
||||||
|
|
||||||
static gpointer sm_client_thread (gpointer data);
|
static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback,
|
||||||
|
gpointer user_data);
|
||||||
|
static gboolean got_message (gpointer user_data);
|
||||||
|
static void sm_client_thread (gpointer data);
|
||||||
|
|
||||||
G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT)
|
G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
egg_sm_client_win32_init (EggSMClientWin32 *win32)
|
egg_sm_client_win32_init (G_GNUC_UNUSED EggSMClientWin32 *win32)
|
||||||
{
|
{
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -81,13 +118,14 @@ egg_sm_client_win32_new (void)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
sm_client_win32_startup (EggSMClient *client,
|
sm_client_win32_startup (EggSMClient *client,
|
||||||
const char *client_id)
|
G_GNUC_UNUSED const char *client_id)
|
||||||
{
|
{
|
||||||
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
||||||
|
|
||||||
/* spawn another thread to listen for logout signals on */
|
win32->message_event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||||
win32->msg_queue = g_async_queue_new ();
|
win32->response_event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||||
g_thread_create (sm_client_thread, client, FALSE, NULL);
|
g_win32_handle_source_add (win32->message_event, got_message, win32);
|
||||||
|
_beginthread (sm_client_thread, 0, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -96,14 +134,14 @@ sm_client_win32_will_quit (EggSMClient *client,
|
|||||||
{
|
{
|
||||||
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
||||||
|
|
||||||
/* Can't push NULL onto a GAsyncQueue, so we add 1 to the value... */
|
win32->will_quit = will_quit;
|
||||||
g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (will_quit + 1));
|
SetEvent (win32->response_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
sm_client_win32_end_session (EggSMClient *client,
|
sm_client_win32_end_session (G_GNUC_UNUSED EggSMClient *client,
|
||||||
EggSMClientEndStyle style,
|
EggSMClientEndStyle style,
|
||||||
gboolean request_confirmation)
|
G_GNUC_UNUSED gboolean request_confirmation)
|
||||||
{
|
{
|
||||||
UINT uFlags = EWX_LOGOFF;
|
UINT uFlags = EWX_LOGOFF;
|
||||||
|
|
||||||
@ -156,7 +194,7 @@ emit_quit (gpointer smclient)
|
|||||||
egg_sm_client_quit (smclient);
|
egg_sm_client_quit (smclient);
|
||||||
gdk_threads_leave ();
|
gdk_threads_leave ();
|
||||||
|
|
||||||
g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (1));
|
SetEvent (win32->response_event);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,25 +207,80 @@ emit_quit_cancelled (gpointer smclient)
|
|||||||
egg_sm_client_quit_cancelled (smclient);
|
egg_sm_client_quit_cancelled (smclient);
|
||||||
gdk_threads_leave ();
|
gdk_threads_leave ();
|
||||||
|
|
||||||
g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (1));
|
SetEvent (win32->response_event);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
got_message (gpointer smclient)
|
||||||
|
{
|
||||||
|
EggSMClientWin32 *win32 = smclient;
|
||||||
|
|
||||||
|
win32->event (win32);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Windows HANDLE GSource */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GSource source;
|
||||||
|
GPollFD pollfd;
|
||||||
|
} GWin32HandleSource;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_win32_handle_source_prepare (G_GNUC_UNUSED GSource *source, gint *timeout)
|
||||||
|
{
|
||||||
|
*timeout = -1;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_win32_handle_source_check (GSource *source)
|
||||||
|
{
|
||||||
|
GWin32HandleSource *hsource = (GWin32HandleSource *)source;
|
||||||
|
|
||||||
|
return hsource->pollfd.revents;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_win32_handle_source_dispatch (G_GNUC_UNUSED GSource *source, GSourceFunc callback, gpointer user_data)
|
||||||
|
{
|
||||||
|
return (*callback) (user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_win32_handle_source_finalize (G_GNUC_UNUSED GSource *source)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSourceFuncs g_win32_handle_source_funcs = {
|
||||||
|
g_win32_handle_source_prepare,
|
||||||
|
g_win32_handle_source_check,
|
||||||
|
g_win32_handle_source_dispatch,
|
||||||
|
g_win32_handle_source_finalize
|
||||||
|
};
|
||||||
|
|
||||||
|
static GSource *
|
||||||
|
g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data)
|
||||||
|
{
|
||||||
|
GWin32HandleSource *hsource;
|
||||||
|
GSource *source;
|
||||||
|
|
||||||
|
source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
|
||||||
|
hsource = (GWin32HandleSource *)source;
|
||||||
|
hsource->pollfd.fd = (int)handle;
|
||||||
|
hsource->pollfd.events = G_IO_IN;
|
||||||
|
hsource->pollfd.revents = 0;
|
||||||
|
g_source_add_poll (source, &hsource->pollfd);
|
||||||
|
|
||||||
|
g_source_set_callback (source, callback, user_data, NULL);
|
||||||
|
g_source_attach (source, NULL);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
/* logout-listener thread */
|
/* logout-listener thread */
|
||||||
|
|
||||||
static int
|
|
||||||
async_emit (EggSMClientWin32 *win32, GSourceFunc emitter)
|
|
||||||
{
|
|
||||||
/* ensure message queue is empty */
|
|
||||||
while (g_async_queue_try_pop (win32->msg_queue))
|
|
||||||
;
|
|
||||||
|
|
||||||
/* Emit signal in the main thread and wait for a response */
|
|
||||||
g_idle_add (emitter, win32);
|
|
||||||
return GPOINTER_TO_INT (g_async_queue_pop (win32->msg_queue)) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static LRESULT CALLBACK
|
static LRESULT CALLBACK
|
||||||
sm_client_win32_window_procedure (HWND hwnd,
|
sm_client_win32_window_procedure (HWND hwnd,
|
||||||
UINT message,
|
UINT message,
|
||||||
@ -200,19 +293,27 @@ sm_client_win32_window_procedure (HWND hwnd,
|
|||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
case WM_QUERYENDSESSION:
|
case WM_QUERYENDSESSION:
|
||||||
return async_emit (win32, emit_quit_requested);
|
win32->event = emit_quit_requested;
|
||||||
|
SetEvent (win32->message_event);
|
||||||
|
|
||||||
|
WaitForSingleObject (win32->response_event, INFINITE);
|
||||||
|
return win32->will_quit;
|
||||||
|
|
||||||
case WM_ENDSESSION:
|
case WM_ENDSESSION:
|
||||||
if (wParam)
|
if (wParam)
|
||||||
{
|
{
|
||||||
/* The session is ending */
|
/* The session is ending */
|
||||||
async_emit (win32, emit_quit);
|
win32->event = emit_quit;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Nope, the session *isn't* ending */
|
/* Nope, the session *isn't* ending */
|
||||||
async_emit (win32, emit_quit_cancelled);
|
win32->event = emit_quit_cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetEvent (win32->message_event);
|
||||||
|
WaitForSingleObject (win32->response_event, INFINITE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -220,11 +321,11 @@ sm_client_win32_window_procedure (HWND hwnd,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer
|
static void
|
||||||
sm_client_thread (gpointer smclient)
|
sm_client_thread (gpointer smclient)
|
||||||
{
|
{
|
||||||
HINSTANCE instance;
|
HINSTANCE instance;
|
||||||
WNDCLASSEXW wcl;
|
WNDCLASSEXW wcl;
|
||||||
ATOM klass;
|
ATOM klass;
|
||||||
HWND window;
|
HWND window;
|
||||||
MSG msg;
|
MSG msg;
|
||||||
@ -247,6 +348,4 @@ sm_client_thread (gpointer smclient)
|
|||||||
/* main loop */
|
/* main loop */
|
||||||
while (GetMessage (&msg, NULL, 0, 0))
|
while (GetMessage (&msg, NULL, 0, 0))
|
||||||
DispatchMessage (&msg);
|
DispatchMessage (&msg);
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,10 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
|
||||||
#include <X11/SM/SMlib.h>
|
#include <X11/SM/SMlib.h>
|
||||||
|
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
|
||||||
#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
|
#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
|
||||||
#define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
|
#define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
|
||||||
@ -57,7 +57,6 @@ typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass;
|
|||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
XSMP_STATE_START,
|
|
||||||
XSMP_STATE_IDLE,
|
XSMP_STATE_IDLE,
|
||||||
XSMP_STATE_SAVE_YOURSELF,
|
XSMP_STATE_SAVE_YOURSELF,
|
||||||
XSMP_STATE_INTERACT_REQUEST,
|
XSMP_STATE_INTERACT_REQUEST,
|
||||||
@ -68,7 +67,6 @@ typedef enum
|
|||||||
} EggSMClientXSMPState;
|
} EggSMClientXSMPState;
|
||||||
|
|
||||||
static const char *state_names[] = {
|
static const char *state_names[] = {
|
||||||
"start",
|
|
||||||
"idle",
|
"idle",
|
||||||
"save-yourself",
|
"save-yourself",
|
||||||
"interact-request",
|
"interact-request",
|
||||||
@ -102,6 +100,7 @@ struct _EggSMClientXSMP
|
|||||||
guint shutting_down : 1;
|
guint shutting_down : 1;
|
||||||
|
|
||||||
/* Todo list */
|
/* Todo list */
|
||||||
|
guint waiting_to_set_initial_properties : 1;
|
||||||
guint waiting_to_emit_quit : 1;
|
guint waiting_to_emit_quit : 1;
|
||||||
guint waiting_to_emit_quit_cancelled : 1;
|
guint waiting_to_emit_quit_cancelled : 1;
|
||||||
guint waiting_to_save_myself : 1;
|
guint waiting_to_save_myself : 1;
|
||||||
@ -180,22 +179,11 @@ egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
|
|||||||
xsmp->restart_style = SmRestartIfRunning;
|
xsmp->restart_style = SmRestartIfRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
sm_client_xsmp_finalize (GObject *object)
|
|
||||||
{
|
|
||||||
EggSMClientXSMP *xsmp = EGG_SM_CLIENT_XSMP (object);
|
|
||||||
g_free (xsmp->client_id);
|
|
||||||
g_strfreev (xsmp->restart_command);
|
|
||||||
G_OBJECT_CLASS (egg_sm_client_xsmp_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
|
egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
|
||||||
{
|
{
|
||||||
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
|
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
|
||||||
|
|
||||||
G_OBJECT_CLASS (sm_client_class)->finalize = sm_client_xsmp_finalize;
|
|
||||||
|
|
||||||
sm_client_class->startup = sm_client_xsmp_startup;
|
sm_client_class->startup = sm_client_xsmp_startup;
|
||||||
sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
|
sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
|
||||||
sm_client_class->will_quit = sm_client_xsmp_will_quit;
|
sm_client_class->will_quit = sm_client_xsmp_will_quit;
|
||||||
@ -212,74 +200,19 @@ egg_sm_client_xsmp_new (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
sm_client_xsmp_connect (gpointer user_data)
|
sm_client_xsmp_set_initial_properties (gpointer user_data)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = user_data;
|
EggSMClientXSMP *xsmp = user_data;
|
||||||
SmcCallbacks callbacks;
|
|
||||||
char *client_id;
|
|
||||||
char error_string_ret[256];
|
|
||||||
char pid_str[64];
|
|
||||||
EggDesktopFile *desktop_file;
|
EggDesktopFile *desktop_file;
|
||||||
GPtrArray *clone, *restart;
|
GPtrArray *clone, *restart;
|
||||||
|
char pid_str[64];
|
||||||
|
|
||||||
g_source_remove (xsmp->idle);
|
if (xsmp->idle)
|
||||||
xsmp->idle = 0;
|
|
||||||
|
|
||||||
ice_init ();
|
|
||||||
SmcSetErrorHandler (smc_error_handler);
|
|
||||||
|
|
||||||
callbacks.save_yourself.callback = xsmp_save_yourself;
|
|
||||||
callbacks.die.callback = xsmp_die;
|
|
||||||
callbacks.save_complete.callback = xsmp_save_complete;
|
|
||||||
callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
|
|
||||||
|
|
||||||
callbacks.save_yourself.client_data = xsmp;
|
|
||||||
callbacks.die.client_data = xsmp;
|
|
||||||
callbacks.save_complete.client_data = xsmp;
|
|
||||||
callbacks.shutdown_cancelled.client_data = xsmp;
|
|
||||||
|
|
||||||
client_id = NULL;
|
|
||||||
error_string_ret[0] = '\0';
|
|
||||||
xsmp->connection =
|
|
||||||
SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
|
|
||||||
SmcSaveYourselfProcMask | SmcDieProcMask |
|
|
||||||
SmcSaveCompleteProcMask |
|
|
||||||
SmcShutdownCancelledProcMask,
|
|
||||||
&callbacks,
|
|
||||||
xsmp->client_id, &client_id,
|
|
||||||
sizeof (error_string_ret), error_string_ret);
|
|
||||||
|
|
||||||
if (!xsmp->connection)
|
|
||||||
{
|
{
|
||||||
g_warning ("Failed to connect to the session manager: %s\n",
|
g_source_remove (xsmp->idle);
|
||||||
error_string_ret[0] ?
|
xsmp->idle = 0;
|
||||||
error_string_ret : "no error message given");
|
|
||||||
xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We expect a pointless initial SaveYourself if either (a) we
|
|
||||||
* didn't have an initial client ID, or (b) we DID have an initial
|
|
||||||
* client ID, but the server rejected it and gave us a new one.
|
|
||||||
*/
|
|
||||||
if (!xsmp->client_id ||
|
|
||||||
(client_id && strcmp (xsmp->client_id, client_id) != 0))
|
|
||||||
xsmp->expecting_initial_save_yourself = TRUE;
|
|
||||||
|
|
||||||
if (client_id)
|
|
||||||
{
|
|
||||||
g_free (xsmp->client_id);
|
|
||||||
xsmp->client_id = g_strdup (client_id);
|
|
||||||
free (client_id);
|
|
||||||
|
|
||||||
gdk_threads_enter ();
|
|
||||||
gdk_set_sm_client_id (xsmp->client_id);
|
|
||||||
gdk_threads_leave ();
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Got client ID \"%s\"", xsmp->client_id);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
xsmp->waiting_to_set_initial_properties = FALSE;
|
||||||
|
|
||||||
if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
|
if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
|
||||||
xsmp->restart_style = SmRestartNever;
|
xsmp->restart_style = SmRestartNever;
|
||||||
@ -288,19 +221,14 @@ sm_client_xsmp_connect (gpointer user_data)
|
|||||||
desktop_file = egg_get_desktop_file ();
|
desktop_file = egg_get_desktop_file ();
|
||||||
if (desktop_file)
|
if (desktop_file)
|
||||||
{
|
{
|
||||||
GKeyFile *key_file;
|
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
char *cmdline, **argv;
|
char *cmdline, **argv;
|
||||||
int argc;
|
int argc;
|
||||||
|
|
||||||
key_file = egg_desktop_file_get_key_file (desktop_file);
|
|
||||||
|
|
||||||
if (xsmp->restart_style == SmRestartIfRunning)
|
if (xsmp->restart_style == SmRestartIfRunning)
|
||||||
{
|
{
|
||||||
if (g_key_file_has_key (key_file, EGG_DESKTOP_FILE_GROUP,
|
if (egg_desktop_file_get_boolean (desktop_file,
|
||||||
"X-GNOME-AutoRestart", NULL) &&
|
"X-GNOME-AutoRestart", NULL))
|
||||||
g_key_file_get_boolean (key_file, EGG_DESKTOP_FILE_GROUP,
|
|
||||||
"X-GNOME-AutoRestart", NULL))
|
|
||||||
xsmp->restart_style = SmRestartImmediately;
|
xsmp->restart_style = SmRestartImmediately;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +247,7 @@ sm_client_xsmp_connect (gpointer user_data)
|
|||||||
err->message);
|
err->message);
|
||||||
g_error_free (err);
|
g_error_free (err);
|
||||||
}
|
}
|
||||||
|
g_free (cmdline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,9 +257,7 @@ sm_client_xsmp_connect (gpointer user_data)
|
|||||||
clone = generate_command (xsmp->restart_command, NULL, NULL);
|
clone = generate_command (xsmp->restart_command, NULL, NULL);
|
||||||
restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
|
restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Setting initial properties");
|
g_debug ("Setting initial properties");
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Program, CloneCommand, RestartCommand, and UserID are required.
|
/* Program, CloneCommand, RestartCommand, and UserID are required.
|
||||||
* ProcessID isn't required, but the SM may be able to do something
|
* ProcessID isn't required, but the SM may be able to do something
|
||||||
@ -355,7 +282,7 @@ sm_client_xsmp_connect (gpointer user_data)
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
xsmp->state = XSMP_STATE_IDLE;
|
update_pending_events (xsmp);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,9 +298,7 @@ sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
|
|||||||
if (!xsmp->connection)
|
if (!xsmp->connection)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Disconnecting");
|
g_debug ("Disconnecting");
|
||||||
#endif
|
|
||||||
|
|
||||||
connection = xsmp->connection;
|
connection = xsmp->connection;
|
||||||
xsmp->connection = NULL;
|
xsmp->connection = NULL;
|
||||||
@ -389,19 +314,78 @@ sm_client_xsmp_startup (EggSMClient *client,
|
|||||||
const char *client_id)
|
const char *client_id)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
|
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
|
||||||
|
SmcCallbacks callbacks;
|
||||||
|
char *ret_client_id;
|
||||||
|
char error_string_ret[256];
|
||||||
|
|
||||||
xsmp->state = XSMP_STATE_START;
|
|
||||||
if (xsmp->client_id)
|
|
||||||
g_free (xsmp->client_id);
|
|
||||||
xsmp->client_id = g_strdup (client_id);
|
xsmp->client_id = g_strdup (client_id);
|
||||||
|
|
||||||
/* Don't connect to the session manager until we reach the main
|
ice_init ();
|
||||||
* loop, since the session manager may assume we're fully up and
|
SmcSetErrorHandler (smc_error_handler);
|
||||||
* running once we connect. (This also gives the application a
|
|
||||||
* chance to call egg_set_desktop_file() before we set the initial
|
callbacks.save_yourself.callback = xsmp_save_yourself;
|
||||||
* properties.)
|
callbacks.die.callback = xsmp_die;
|
||||||
|
callbacks.save_complete.callback = xsmp_save_complete;
|
||||||
|
callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
|
||||||
|
|
||||||
|
callbacks.save_yourself.client_data = xsmp;
|
||||||
|
callbacks.die.client_data = xsmp;
|
||||||
|
callbacks.save_complete.client_data = xsmp;
|
||||||
|
callbacks.shutdown_cancelled.client_data = xsmp;
|
||||||
|
|
||||||
|
client_id = NULL;
|
||||||
|
error_string_ret[0] = '\0';
|
||||||
|
xsmp->connection =
|
||||||
|
SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
|
||||||
|
SmcSaveYourselfProcMask | SmcDieProcMask |
|
||||||
|
SmcSaveCompleteProcMask |
|
||||||
|
SmcShutdownCancelledProcMask,
|
||||||
|
&callbacks,
|
||||||
|
xsmp->client_id, &ret_client_id,
|
||||||
|
sizeof (error_string_ret), error_string_ret);
|
||||||
|
|
||||||
|
if (!xsmp->connection)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to connect to the session manager: %s\n",
|
||||||
|
error_string_ret[0] ?
|
||||||
|
error_string_ret : "no error message given");
|
||||||
|
xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We expect a pointless initial SaveYourself if either (a) we
|
||||||
|
* didn't have an initial client ID, or (b) we DID have an initial
|
||||||
|
* client ID, but the server rejected it and gave us a new one.
|
||||||
*/
|
*/
|
||||||
xsmp->idle = g_idle_add (sm_client_xsmp_connect, client);
|
if (!xsmp->client_id ||
|
||||||
|
(ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
|
||||||
|
xsmp->expecting_initial_save_yourself = TRUE;
|
||||||
|
|
||||||
|
if (ret_client_id)
|
||||||
|
{
|
||||||
|
g_free (xsmp->client_id);
|
||||||
|
xsmp->client_id = g_strdup (ret_client_id);
|
||||||
|
free (ret_client_id);
|
||||||
|
|
||||||
|
#if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0)
|
||||||
|
gdk_set_sm_client_id (xsmp->client_id);
|
||||||
|
#else
|
||||||
|
gdk_x11_set_sm_client_id (xsmp->client_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_debug ("Got client ID \"%s\"", xsmp->client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
xsmp->state = XSMP_STATE_IDLE;
|
||||||
|
|
||||||
|
/* Do not set the initial properties until we reach the main loop,
|
||||||
|
* so that the application has a chance to call
|
||||||
|
* egg_set_desktop_file(). (This may also help the session manager
|
||||||
|
* have a better idea of when the application is fully up and
|
||||||
|
* running.)
|
||||||
|
*/
|
||||||
|
xsmp->waiting_to_set_initial_properties = TRUE;
|
||||||
|
xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -449,24 +433,20 @@ sm_client_xsmp_will_quit (EggSMClient *client,
|
|||||||
|
|
||||||
g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
|
g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
|
g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
|
||||||
#endif
|
|
||||||
SmcInteractDone (xsmp->connection, !will_quit);
|
SmcInteractDone (xsmp->connection, !will_quit);
|
||||||
|
|
||||||
if (will_quit && xsmp->need_save_state)
|
if (will_quit && xsmp->need_save_state)
|
||||||
save_state (xsmp);
|
save_state (xsmp);
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
|
g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
|
||||||
#endif
|
|
||||||
SmcSaveYourselfDone (xsmp->connection, will_quit);
|
SmcSaveYourselfDone (xsmp->connection, will_quit);
|
||||||
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
|
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
sm_client_xsmp_end_session (EggSMClient *client,
|
sm_client_xsmp_end_session (EggSMClient *client,
|
||||||
EggSMClientEndStyle style,
|
G_GNUC_UNUSED EggSMClientEndStyle style,
|
||||||
gboolean request_confirmation)
|
gboolean request_confirmation)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
|
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
|
||||||
@ -495,11 +475,6 @@ sm_client_xsmp_end_session (EggSMClient *client,
|
|||||||
|
|
||||||
switch (xsmp->state)
|
switch (xsmp->state)
|
||||||
{
|
{
|
||||||
case XSMP_STATE_START:
|
|
||||||
/* Force the connection to complete (or fail) now. */
|
|
||||||
sm_client_xsmp_connect (xsmp);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XSMP_STATE_CONNECTION_CLOSED:
|
case XSMP_STATE_CONNECTION_CLOSED:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -520,6 +495,9 @@ sm_client_xsmp_end_session (EggSMClient *client,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case XSMP_STATE_IDLE:
|
case XSMP_STATE_IDLE:
|
||||||
|
if (xsmp->waiting_to_set_initial_properties)
|
||||||
|
sm_client_xsmp_set_initial_properties (xsmp);
|
||||||
|
|
||||||
if (!xsmp->expecting_initial_save_yourself)
|
if (!xsmp->expecting_initial_save_yourself)
|
||||||
break;
|
break;
|
||||||
/* else fall through */
|
/* else fall through */
|
||||||
@ -545,9 +523,7 @@ sm_client_xsmp_end_session (EggSMClient *client,
|
|||||||
else
|
else
|
||||||
save_type = SmSaveGlobal;
|
save_type = SmSaveGlobal;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
|
g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
|
||||||
#endif
|
|
||||||
SmcRequestSaveYourself (xsmp->connection,
|
SmcRequestSaveYourself (xsmp->connection,
|
||||||
save_type,
|
save_type,
|
||||||
True, /* shutdown */
|
True, /* shutdown */
|
||||||
@ -636,7 +612,7 @@ fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
|
|||||||
/* SM callbacks */
|
/* SM callbacks */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xsmp_save_yourself (SmcConn smc_conn,
|
xsmp_save_yourself (G_GNUC_UNUSED SmcConn smc_conn,
|
||||||
SmPointer client_data,
|
SmPointer client_data,
|
||||||
int save_type,
|
int save_type,
|
||||||
Bool shutdown,
|
Bool shutdown,
|
||||||
@ -646,7 +622,6 @@ xsmp_save_yourself (SmcConn smc_conn,
|
|||||||
EggSMClientXSMP *xsmp = client_data;
|
EggSMClientXSMP *xsmp = client_data;
|
||||||
gboolean wants_quit_requested;
|
gboolean wants_quit_requested;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
|
g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
|
||||||
save_type == SmSaveLocal ? "SmSaveLocal" :
|
save_type == SmSaveLocal ? "SmSaveLocal" :
|
||||||
save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
|
save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
|
||||||
@ -655,7 +630,6 @@ xsmp_save_yourself (SmcConn smc_conn,
|
|||||||
interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
|
interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
|
||||||
"SmInteractStyleNone", fast ? "Fast" : "!Fast",
|
"SmInteractStyleNone", fast ? "Fast" : "!Fast",
|
||||||
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
||||||
#endif
|
|
||||||
|
|
||||||
if (xsmp->state != XSMP_STATE_IDLE &&
|
if (xsmp->state != XSMP_STATE_IDLE &&
|
||||||
xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
|
xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
|
||||||
@ -664,6 +638,9 @@ xsmp_save_yourself (SmcConn smc_conn,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xsmp->waiting_to_set_initial_properties)
|
||||||
|
sm_client_xsmp_set_initial_properties (xsmp);
|
||||||
|
|
||||||
/* If this is the initial SaveYourself, ignore it; we've already set
|
/* If this is the initial SaveYourself, ignore it; we've already set
|
||||||
* properties and there's no reason to actually save state too.
|
* properties and there's no reason to actually save state too.
|
||||||
*/
|
*/
|
||||||
@ -675,9 +652,7 @@ xsmp_save_yourself (SmcConn smc_conn,
|
|||||||
interact_style == SmInteractStyleNone &&
|
interact_style == SmInteractStyleNone &&
|
||||||
!shutdown && !fast)
|
!shutdown && !fast)
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
|
g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
|
||||||
#endif
|
|
||||||
SmcSaveYourselfDone (xsmp->connection, True);
|
SmcSaveYourselfDone (xsmp->connection, True);
|
||||||
/* As explained in the comment at the end of
|
/* As explained in the comment at the end of
|
||||||
* do_save_yourself(), SAVE_YOURSELF_DONE is the correct
|
* do_save_yourself(), SAVE_YOURSELF_DONE is the correct
|
||||||
@ -762,10 +737,8 @@ do_save_yourself (EggSMClientXSMP *xsmp)
|
|||||||
{
|
{
|
||||||
xsmp->state = XSMP_STATE_INTERACT_REQUEST;
|
xsmp->state = XSMP_STATE_INTERACT_REQUEST;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Sending InteractRequest(%s)",
|
g_debug ("Sending InteractRequest(%s)",
|
||||||
xsmp->interact_errors ? "Error" : "Normal");
|
xsmp->interact_errors ? "Error" : "Normal");
|
||||||
#endif
|
|
||||||
SmcInteractRequest (xsmp->connection,
|
SmcInteractRequest (xsmp->connection,
|
||||||
xsmp->interact_errors ? SmDialogError : SmDialogNormal,
|
xsmp->interact_errors ? SmDialogError : SmDialogNormal,
|
||||||
xsmp_interact,
|
xsmp_interact,
|
||||||
@ -784,9 +757,7 @@ do_save_yourself (EggSMClientXSMP *xsmp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Sending SaveYourselfDone(True)");
|
g_debug ("Sending SaveYourselfDone(True)");
|
||||||
#endif
|
|
||||||
SmcSaveYourselfDone (xsmp->connection, True);
|
SmcSaveYourselfDone (xsmp->connection, True);
|
||||||
|
|
||||||
/* The client state diagram in the XSMP spec says that after a
|
/* The client state diagram in the XSMP spec says that after a
|
||||||
@ -797,30 +768,6 @@ do_save_yourself (EggSMClientXSMP *xsmp)
|
|||||||
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
|
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
merge_keyfiles (GKeyFile *dest, GKeyFile *source)
|
|
||||||
{
|
|
||||||
int g, k;
|
|
||||||
char **groups, **keys, *value;
|
|
||||||
|
|
||||||
groups = g_key_file_get_groups (source, NULL);
|
|
||||||
for (g = 0; groups[g]; g++)
|
|
||||||
{
|
|
||||||
keys = g_key_file_get_keys (source, groups[g], NULL, NULL);
|
|
||||||
for (k = 0; keys[k]; k++)
|
|
||||||
{
|
|
||||||
value = g_key_file_get_value (source, groups[g], keys[k], NULL);
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
g_key_file_set_value (dest, groups[g], keys[k], value);
|
|
||||||
g_free (value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_strfreev (keys);
|
|
||||||
}
|
|
||||||
g_strfreev (groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
save_state (EggSMClientXSMP *xsmp)
|
save_state (EggSMClientXSMP *xsmp)
|
||||||
{
|
{
|
||||||
@ -851,30 +798,61 @@ save_state (EggSMClientXSMP *xsmp)
|
|||||||
if (desktop_file)
|
if (desktop_file)
|
||||||
{
|
{
|
||||||
GKeyFile *merged_file;
|
GKeyFile *merged_file;
|
||||||
char *exec;
|
char *desktop_file_path;
|
||||||
guint i;
|
|
||||||
|
|
||||||
merged_file = g_key_file_new ();
|
merged_file = g_key_file_new ();
|
||||||
merge_keyfiles (merged_file, egg_desktop_file_get_key_file (desktop_file));
|
desktop_file_path =
|
||||||
merge_keyfiles (merged_file, state_file);
|
g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
|
||||||
|
NULL, NULL);
|
||||||
|
if (desktop_file_path &&
|
||||||
|
g_key_file_load_from_file (merged_file, desktop_file_path,
|
||||||
|
G_KEY_FILE_KEEP_COMMENTS |
|
||||||
|
G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
|
||||||
|
{
|
||||||
|
guint g, k, i;
|
||||||
|
char **groups, **keys, *value, *exec;
|
||||||
|
|
||||||
g_key_file_free (state_file);
|
groups = g_key_file_get_groups (state_file, NULL);
|
||||||
state_file = merged_file;
|
for (g = 0; groups[g]; g++)
|
||||||
|
{
|
||||||
|
keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
|
||||||
|
for (k = 0; keys[k]; k++)
|
||||||
|
{
|
||||||
|
value = g_key_file_get_value (state_file, groups[g],
|
||||||
|
keys[k], NULL);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
g_key_file_set_value (merged_file, groups[g],
|
||||||
|
keys[k], value);
|
||||||
|
g_free (value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_strfreev (keys);
|
||||||
|
}
|
||||||
|
g_strfreev (groups);
|
||||||
|
|
||||||
/* Update Exec key using "--sm-client-state-file %k" */
|
g_key_file_free (state_file);
|
||||||
restart = generate_command (xsmp->restart_command,
|
state_file = merged_file;
|
||||||
NULL, "%k");
|
|
||||||
for (i = 0; i < restart->len; i++)
|
|
||||||
restart->pdata[i] = g_shell_quote (restart->pdata[i]);
|
|
||||||
g_ptr_array_add (restart, NULL);
|
|
||||||
exec = g_strjoinv (" ", (char **)restart->pdata);
|
|
||||||
g_strfreev ((char **)restart->pdata);
|
|
||||||
g_ptr_array_free (restart, FALSE);
|
|
||||||
|
|
||||||
g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
|
/* Update Exec key using "--sm-client-state-file %k" */
|
||||||
EGG_DESKTOP_FILE_KEY_EXEC,
|
restart = generate_command (xsmp->restart_command,
|
||||||
exec);
|
NULL, "%k");
|
||||||
g_free (exec);
|
for (i = 0; i < restart->len; i++)
|
||||||
|
restart->pdata[i] = g_shell_quote (restart->pdata[i]);
|
||||||
|
g_ptr_array_add (restart, NULL);
|
||||||
|
exec = g_strjoinv (" ", (char **)restart->pdata);
|
||||||
|
g_strfreev ((char **)restart->pdata);
|
||||||
|
g_ptr_array_free (restart, FALSE);
|
||||||
|
|
||||||
|
g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
|
||||||
|
EGG_DESKTOP_FILE_KEY_EXEC,
|
||||||
|
exec);
|
||||||
|
g_free (exec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
desktop_file = NULL;
|
||||||
|
|
||||||
|
g_free (desktop_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now write state_file to disk. (We can't use mktemp(), because
|
/* Now write state_file to disk. (We can't use mktemp(), because
|
||||||
@ -953,16 +931,14 @@ save_state (EggSMClientXSMP *xsmp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xsmp_interact (SmcConn smc_conn,
|
xsmp_interact (G_GNUC_UNUSED SmcConn smc_conn,
|
||||||
SmPointer client_data)
|
SmPointer client_data)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = client_data;
|
EggSMClientXSMP *xsmp = client_data;
|
||||||
EggSMClient *client = client_data;
|
EggSMClient *client = client_data;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Received Interact message in state %s",
|
g_debug ("Received Interact message in state %s",
|
||||||
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
||||||
#endif
|
|
||||||
|
|
||||||
if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
|
if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
|
||||||
{
|
{
|
||||||
@ -975,31 +951,27 @@ xsmp_interact (SmcConn smc_conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xsmp_die (SmcConn smc_conn,
|
xsmp_die (G_GNUC_UNUSED SmcConn smc_conn,
|
||||||
SmPointer client_data)
|
SmPointer client_data)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = client_data;
|
EggSMClientXSMP *xsmp = client_data;
|
||||||
EggSMClient *client = client_data;
|
EggSMClient *client = client_data;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Received Die message in state %s",
|
g_debug ("Received Die message in state %s",
|
||||||
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
||||||
#endif
|
|
||||||
|
|
||||||
sm_client_xsmp_disconnect (xsmp);
|
sm_client_xsmp_disconnect (xsmp);
|
||||||
egg_sm_client_quit (client);
|
egg_sm_client_quit (client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xsmp_save_complete (SmcConn smc_conn,
|
xsmp_save_complete (G_GNUC_UNUSED SmcConn smc_conn,
|
||||||
SmPointer client_data)
|
SmPointer client_data)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = client_data;
|
EggSMClientXSMP *xsmp = client_data;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Received SaveComplete message in state %s",
|
g_debug ("Received SaveComplete message in state %s",
|
||||||
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
||||||
#endif
|
|
||||||
|
|
||||||
if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
|
if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
|
||||||
xsmp->state = XSMP_STATE_IDLE;
|
xsmp->state = XSMP_STATE_IDLE;
|
||||||
@ -1008,16 +980,14 @@ xsmp_save_complete (SmcConn smc_conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xsmp_shutdown_cancelled (SmcConn smc_conn,
|
xsmp_shutdown_cancelled (G_GNUC_UNUSED SmcConn smc_conn,
|
||||||
SmPointer client_data)
|
SmPointer client_data)
|
||||||
{
|
{
|
||||||
EggSMClientXSMP *xsmp = client_data;
|
EggSMClientXSMP *xsmp = client_data;
|
||||||
EggSMClient *client = client_data;
|
EggSMClient *client = client_data;
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_debug ("Received ShutdownCancelled message in state %s",
|
g_debug ("Received ShutdownCancelled message in state %s",
|
||||||
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
EGG_SM_CLIENT_XSMP_STATE (xsmp));
|
||||||
#endif
|
|
||||||
|
|
||||||
xsmp->shutting_down = FALSE;
|
xsmp->shutting_down = FALSE;
|
||||||
|
|
||||||
@ -1042,9 +1012,7 @@ xsmp_shutdown_cancelled (SmcConn smc_conn,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
g_debug ("Sending SaveYourselfDone(False)");
|
g_debug ("Sending SaveYourselfDone(False)");
|
||||||
#endif
|
|
||||||
SmcSaveYourselfDone (xsmp->connection, False);
|
SmcSaveYourselfDone (xsmp->connection, False);
|
||||||
|
|
||||||
if (xsmp->state == XSMP_STATE_INTERACT)
|
if (xsmp->state == XSMP_STATE_INTERACT)
|
||||||
@ -1332,14 +1300,15 @@ process_ice_messages (IceConn ice_conn)
|
|||||||
|
|
||||||
case IceProcessMessagesConnectionClosed:
|
case IceProcessMessagesConnectionClosed:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
g_return_val_if_reached (FALSE);
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
ice_iochannel_watch (GIOChannel *channel,
|
ice_iochannel_watch (G_GNUC_UNUSED GIOChannel *channel,
|
||||||
GIOCondition condition,
|
G_GNUC_UNUSED GIOCondition condition,
|
||||||
gpointer client_data)
|
gpointer client_data)
|
||||||
{
|
{
|
||||||
return process_ice_messages (client_data);
|
return process_ice_messages (client_data);
|
||||||
@ -1347,7 +1316,7 @@ ice_iochannel_watch (GIOChannel *channel,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
ice_connection_watch (IceConn ice_conn,
|
ice_connection_watch (IceConn ice_conn,
|
||||||
IcePointer client_data,
|
G_GNUC_UNUSED IcePointer client_data,
|
||||||
Bool opening,
|
Bool opening,
|
||||||
IcePointer *watch_data)
|
IcePointer *watch_data)
|
||||||
{
|
{
|
||||||
@ -1374,31 +1343,31 @@ ice_connection_watch (IceConn ice_conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ice_error_handler (IceConn ice_conn,
|
ice_error_handler (G_GNUC_UNUSED IceConn ice_conn,
|
||||||
Bool swap,
|
G_GNUC_UNUSED Bool swap,
|
||||||
int offending_minor_opcode,
|
G_GNUC_UNUSED int offending_minor_opcode,
|
||||||
unsigned long offending_sequence,
|
G_GNUC_UNUSED unsigned long offending_sequence,
|
||||||
int error_class,
|
G_GNUC_UNUSED int error_class,
|
||||||
int severity,
|
G_GNUC_UNUSED int severity,
|
||||||
IcePointer values)
|
G_GNUC_UNUSED IcePointer values)
|
||||||
{
|
{
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ice_io_error_handler (IceConn ice_conn)
|
ice_io_error_handler (G_GNUC_UNUSED IceConn ice_conn)
|
||||||
{
|
{
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
smc_error_handler (SmcConn smc_conn,
|
smc_error_handler (G_GNUC_UNUSED SmcConn smc_conn,
|
||||||
Bool swap,
|
G_GNUC_UNUSED Bool swap,
|
||||||
int offending_minor_opcode,
|
G_GNUC_UNUSED int offending_minor_opcode,
|
||||||
unsigned long offending_sequence,
|
G_GNUC_UNUSED unsigned long offending_sequence,
|
||||||
int error_class,
|
G_GNUC_UNUSED int error_class,
|
||||||
int severity,
|
G_GNUC_UNUSED int severity,
|
||||||
SmPointer values)
|
G_GNUC_UNUSED SmPointer values)
|
||||||
{
|
{
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <mooutils/mooi18n.h>
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
#include "eggsmclient.h"
|
#include "eggsmclient.h"
|
||||||
#include "eggsmclient-private.h"
|
#include "eggsmclient-private.h"
|
||||||
@ -38,7 +38,7 @@ enum {
|
|||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
static guint signals[LAST_SIGNAL] = { 0 };
|
static guint signals[LAST_SIGNAL];
|
||||||
|
|
||||||
struct _EggSMClientPrivate {
|
struct _EggSMClientPrivate {
|
||||||
GKeyFile *state_file;
|
GKeyFile *state_file;
|
||||||
@ -52,7 +52,7 @@ static EggSMClient *global_client;
|
|||||||
static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
|
static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
egg_sm_client_init (EggSMClient *client)
|
egg_sm_client_init (G_GNUC_UNUSED EggSMClient *client)
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ egg_sm_client_class_init (EggSMClientClass *klass)
|
|||||||
* handling this signal; if the user has requested that the session
|
* handling this signal; if the user has requested that the session
|
||||||
* be saved when logging out, then ::save_state will be emitted
|
* be saved when logging out, then ::save_state will be emitted
|
||||||
* separately.
|
* separately.
|
||||||
*
|
*
|
||||||
* If the application agrees to quit, it should then wait for either
|
* If the application agrees to quit, it should then wait for either
|
||||||
* the ::quit_cancelled or ::quit signals to be emitted.
|
* the ::quit_cancelled or ::quit signals to be emitted.
|
||||||
**/
|
**/
|
||||||
@ -178,29 +178,32 @@ egg_sm_client_class_init (EggSMClientClass *klass)
|
|||||||
static gboolean sm_client_disable = FALSE;
|
static gboolean sm_client_disable = FALSE;
|
||||||
static char *sm_client_state_file = NULL;
|
static char *sm_client_state_file = NULL;
|
||||||
static char *sm_client_id = NULL;
|
static char *sm_client_id = NULL;
|
||||||
|
static char *sm_config_prefix = NULL;
|
||||||
static GOptionEntry entries[] = {
|
|
||||||
{ "sm-client-disable", 0, 0,
|
|
||||||
G_OPTION_ARG_NONE, &sm_client_disable,
|
|
||||||
N_("Disable connection to session manager"), NULL },
|
|
||||||
{ "sm-client-state-file", 0, 0,
|
|
||||||
G_OPTION_ARG_STRING, &sm_client_state_file,
|
|
||||||
N_("Specify file containing saved configuration"), N_("FILE") },
|
|
||||||
{ "sm-client-id", 0, 0,
|
|
||||||
G_OPTION_ARG_STRING, &sm_client_id,
|
|
||||||
N_("Specify session management ID"), N_("ID") },
|
|
||||||
{ NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
sm_client_post_parse_func (GOptionContext *context,
|
sm_client_post_parse_func (G_GNUC_UNUSED GOptionContext *context,
|
||||||
GOptionGroup *group,
|
G_GNUC_UNUSED GOptionGroup *group,
|
||||||
gpointer data,
|
G_GNUC_UNUSED gpointer data,
|
||||||
GError **error)
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
EggSMClient *client = egg_sm_client_get ();
|
EggSMClient *client = egg_sm_client_get ();
|
||||||
|
|
||||||
if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
|
if (sm_client_id == NULL)
|
||||||
|
{
|
||||||
|
const gchar *desktop_autostart_id;
|
||||||
|
|
||||||
|
desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
|
||||||
|
|
||||||
|
if (desktop_autostart_id != NULL)
|
||||||
|
sm_client_id = g_strdup (desktop_autostart_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
|
||||||
|
* use the same client id. */
|
||||||
|
g_unsetenv ("DESKTOP_AUTOSTART_ID");
|
||||||
|
|
||||||
|
if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
|
||||||
|
EGG_SM_CLIENT_GET_CLASS (client)->startup)
|
||||||
EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
|
EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -217,6 +220,29 @@ sm_client_post_parse_func (GOptionContext *context,
|
|||||||
GOptionGroup *
|
GOptionGroup *
|
||||||
egg_sm_client_get_option_group (void)
|
egg_sm_client_get_option_group (void)
|
||||||
{
|
{
|
||||||
|
const GOptionEntry entries[] = {
|
||||||
|
{ "sm-client-disable", 0, 0,
|
||||||
|
G_OPTION_ARG_NONE, &sm_client_disable,
|
||||||
|
N_("Disable connection to session manager"), NULL },
|
||||||
|
{ "sm-client-state-file", 0, 0,
|
||||||
|
G_OPTION_ARG_FILENAME, &sm_client_state_file,
|
||||||
|
N_("Specify file containing saved configuration"), N_("FILE") },
|
||||||
|
{ "sm-client-id", 0, 0,
|
||||||
|
G_OPTION_ARG_STRING, &sm_client_id,
|
||||||
|
N_("Specify session management ID"), N_("ID") },
|
||||||
|
/* GnomeClient compatibility option */
|
||||||
|
{ "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
|
||||||
|
G_OPTION_ARG_NONE, &sm_client_disable,
|
||||||
|
NULL, NULL },
|
||||||
|
/* GnomeClient compatibility option. This is a dummy option that only
|
||||||
|
* exists so that sessions saved by apps with GnomeClient can be restored
|
||||||
|
* later when they've switched to EggSMClient. See bug #575308.
|
||||||
|
*/
|
||||||
|
{ "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
|
||||||
|
G_OPTION_ARG_STRING, &sm_config_prefix,
|
||||||
|
NULL, NULL },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
GOptionGroup *group;
|
GOptionGroup *group;
|
||||||
|
|
||||||
/* Use our own debug handler for the "EggSMClient" domain. */
|
/* Use our own debug handler for the "EggSMClient" domain. */
|
||||||
@ -224,8 +250,8 @@ egg_sm_client_get_option_group (void)
|
|||||||
egg_sm_client_debug_handler, NULL);
|
egg_sm_client_debug_handler, NULL);
|
||||||
|
|
||||||
group = g_option_group_new ("sm-client",
|
group = g_option_group_new ("sm-client",
|
||||||
_("Session Management Options"),
|
_("Session management options:"),
|
||||||
_("Show Session Management options"),
|
_("Show session management options"),
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
g_option_group_add_entries (group, entries);
|
g_option_group_add_entries (group, entries);
|
||||||
g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func);
|
g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func);
|
||||||
@ -240,9 +266,9 @@ egg_sm_client_get_option_group (void)
|
|||||||
* Sets the "mode" of #EggSMClient as follows:
|
* Sets the "mode" of #EggSMClient as follows:
|
||||||
*
|
*
|
||||||
* %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
|
* %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
|
||||||
* disabled. The application will not even connect to the session
|
* disabled, until the mode is changed again. The application will
|
||||||
* manager. (egg_sm_client_get() will still return an #EggSMClient,
|
* not even connect to the session manager. (egg_sm_client_get()
|
||||||
* but it will just be a dummy object.)
|
* will still return an #EggSMClient object.)
|
||||||
*
|
*
|
||||||
* %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
|
* %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
|
||||||
* the session manager (and thus will receive notification when the
|
* the session manager (and thus will receive notification when the
|
||||||
@ -252,12 +278,27 @@ egg_sm_client_get_option_group (void)
|
|||||||
* %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
|
* %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
|
||||||
* function normally.
|
* function normally.
|
||||||
*
|
*
|
||||||
* This must be called before the application's main loop begins.
|
* This must be called before the application's main loop begins and
|
||||||
|
* before any call to egg_sm_client_get(), unless the mode was set
|
||||||
|
* earlier to %EGG_SM_CLIENT_MODE_DISABLED and this call enables
|
||||||
|
* session management. Note that option parsing will call
|
||||||
|
* egg_sm_client_get().
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
egg_sm_client_set_mode (EggSMClientMode mode)
|
egg_sm_client_set_mode (EggSMClientMode mode)
|
||||||
{
|
{
|
||||||
|
EggSMClientMode old_mode = global_client_mode;
|
||||||
|
|
||||||
|
g_return_if_fail (global_client == NULL || global_client_mode == EGG_SM_CLIENT_MODE_DISABLED);
|
||||||
|
g_return_if_fail (!(global_client != NULL && mode == EGG_SM_CLIENT_MODE_DISABLED));
|
||||||
|
|
||||||
global_client_mode = mode;
|
global_client_mode = mode;
|
||||||
|
|
||||||
|
if (global_client != NULL && old_mode == EGG_SM_CLIENT_MODE_DISABLED)
|
||||||
|
{
|
||||||
|
if (EGG_SM_CLIENT_GET_CLASS (global_client)->startup)
|
||||||
|
EGG_SM_CLIENT_GET_CLASS (global_client)->startup (global_client, sm_client_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,24 +333,23 @@ egg_sm_client_get (void)
|
|||||||
{
|
{
|
||||||
if (!global_client)
|
if (!global_client)
|
||||||
{
|
{
|
||||||
if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
|
if (!sm_client_disable)
|
||||||
!sm_client_disable)
|
|
||||||
{
|
{
|
||||||
#if defined (GDK_WINDOWING_WIN32)
|
#if defined (GDK_WINDOWING_WIN32)
|
||||||
global_client = egg_sm_client_win32_new ();
|
global_client = egg_sm_client_win32_new ();
|
||||||
#elif defined (GDK_WINDOWING_QUARTZ)
|
#elif defined (GDK_WINDOWING_QUARTZ)
|
||||||
global_client = egg_sm_client_dummy_new ();
|
global_client = egg_sm_client_osx_new ();
|
||||||
#else
|
#else
|
||||||
/* If both D-Bus and XSMP are compiled in, try D-Bus first
|
/* If both D-Bus and XSMP are compiled in, try XSMP first
|
||||||
* and fall back to XSMP if D-Bus session management isn't
|
* (since it supports state saving) and fall back to D-Bus
|
||||||
* available.
|
* if XSMP isn't available.
|
||||||
*/
|
*/
|
||||||
# ifdef EGG_SM_CLIENT_BACKEND_DBUS
|
|
||||||
global_client = egg_sm_client_dbus_new ();
|
|
||||||
# endif
|
|
||||||
# ifdef EGG_SM_CLIENT_BACKEND_XSMP
|
# ifdef EGG_SM_CLIENT_BACKEND_XSMP
|
||||||
|
global_client = egg_sm_client_xsmp_new ();
|
||||||
|
# endif
|
||||||
|
# ifdef EGG_SM_CLIENT_BACKEND_DBUS
|
||||||
if (!global_client)
|
if (!global_client)
|
||||||
global_client = egg_sm_client_xsmp_new ();
|
global_client = egg_sm_client_dbus_new ();
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -552,7 +592,7 @@ static void
|
|||||||
egg_sm_client_debug_handler (const char *log_domain,
|
egg_sm_client_debug_handler (const char *log_domain,
|
||||||
GLogLevelFlags log_level,
|
GLogLevelFlags log_level,
|
||||||
const char *message,
|
const char *message,
|
||||||
gpointer user_data)
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
{
|
{
|
||||||
static int debug = -1;
|
static int debug = -1;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user