Latest eggsmclient

This commit is contained in:
Yevgen Muntyan 2011-03-05 15:19:56 -08:00
parent c3daead67e
commit 9ef26ef91a
8 changed files with 1171 additions and 355 deletions

View File

@ -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:
* *

View 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

View 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);
}
}

View 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;
}

View File

@ -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

View File

@ -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;
} }

View File

@ -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 */
} }

View File

@ -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;