Added EggSMClient, to properly close when user logs off
parent
1c134aa9c6
commit
409c11184a
|
@ -58,3 +58,4 @@ moo/mooterm/termhelper_res\.rc$
|
|||
gmon\.out
|
||||
py-compile
|
||||
^moo/mooedit/language-specs/testdir/.*$
|
||||
^moo/mooapp/smclient/(README|egg-session-end.c|eggdesktopfile.[ch]|eggsmclient-libgnomeui.[ch]|eggsmclient-osx.c|gedit.diff|smclient.patch)
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
# moo/configure.ac
|
||||
#
|
||||
|
||||
AC_INIT([medit],[0.8.1],[muntyan@tamu.edu],[medit])
|
||||
AC_INIT([medit],[0.8.2],[muntyan@tamu.edu],[medit])
|
||||
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
MOO_DEFINE_VERSIONS(moo,0,8,1)
|
||||
MOO_DEFINE_VERSIONS(moo,0,8,2)
|
||||
MOO_DEFINE_MODULE_VERSIONS(1,0)
|
||||
|
||||
MOO_AC_I18N(moo)
|
||||
|
@ -49,6 +49,7 @@ moo/mooedit/plugins/Makefile
|
|||
moo/mooedit/plugins/activestrings/Makefile
|
||||
moo/mooedit/plugins/fileselector/Makefile
|
||||
moo/mooapp/Makefile
|
||||
moo/mooapp/smclient/Makefile
|
||||
moo/moopython/Makefile
|
||||
moo/moopython/plugins/Makefile
|
||||
moo/moopython/plugins/pyproject/Makefile
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
SUBDIRS = smclient
|
||||
|
||||
BUILT_SOURCES = \
|
||||
mooappabout-glade.h
|
||||
|
||||
|
@ -41,6 +43,8 @@ mooappabout-glade.h: $(srcdir)/glade/mooappabout.glade $(XML2H)
|
|||
|
||||
noinst_LTLIBRARIES = libmooapp.la
|
||||
libmooapp_la_SOURCES = $(mooapp_sources)
|
||||
libmooapp_la_LIBADD = smclient/libsmclient.la
|
||||
|
||||
AM_CFLAGS = \
|
||||
-I.. \
|
||||
-I$(top_builddir) \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define WANT_MOO_APP_CMD_CHARS
|
||||
#include "mooapp/mooappinput.h"
|
||||
#include "mooapp/mooapp-private.h"
|
||||
#include "mooapp/smclient/eggsmclient.h"
|
||||
#include "mooterm/mootermwindow.h"
|
||||
#include "mooedit/mooeditprefs.h"
|
||||
#include "mooedit/mooeditor.h"
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "mooutils/moostock.h"
|
||||
#include "mooutils/mooutils-fs.h"
|
||||
#include "mooutils/mooutils-misc.h"
|
||||
#include "mooutils/mooutils-win32.h"
|
||||
#include "mooutils/mooi18n.h"
|
||||
#include "mooutils/xdgmime/xdgmime.h"
|
||||
#include <string.h>
|
||||
|
@ -60,6 +62,8 @@
|
|||
static MooApp *moo_app_instance = NULL;
|
||||
static MooAppInput *moo_app_input = NULL;
|
||||
|
||||
static volatile int signal_received;
|
||||
|
||||
|
||||
struct _MooAppPrivate {
|
||||
char **argv;
|
||||
|
@ -76,6 +80,8 @@ struct _MooAppPrivate {
|
|||
GSList *terminals;
|
||||
MooTermWindow *term_window;
|
||||
|
||||
EggSMClient *sm_client;
|
||||
|
||||
MooUIXML *ui_xml;
|
||||
char *default_ui;
|
||||
guint quit_handler_id;
|
||||
|
@ -83,7 +89,6 @@ struct _MooAppPrivate {
|
|||
gboolean use_terminal;
|
||||
|
||||
char *tmpdir;
|
||||
guint sigintr : 1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -386,12 +391,21 @@ moo_app_instance_init (MooApp *app)
|
|||
|
||||
|
||||
#if defined(HAVE_SIGNAL)
|
||||
static RETSIGTYPE
|
||||
sigint_handler (G_GNUC_UNUSED int sig)
|
||||
static void
|
||||
setup_signals (void(*handler)(int))
|
||||
{
|
||||
if (moo_app_instance && moo_app_instance->priv)
|
||||
moo_app_instance->priv->sigintr = TRUE;
|
||||
signal (SIGINT, SIG_DFL);
|
||||
signal (SIGINT, handler);
|
||||
#ifdef SIGHUP
|
||||
/* TODO: maybe detach from terminal in this case? */
|
||||
signal (SIGHUP, handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler (int sig)
|
||||
{
|
||||
signal_received = sig;
|
||||
setup_signals (SIG_DFL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -419,7 +433,7 @@ moo_app_constructor (GType type,
|
|||
app->priv->info->full_name = g_strdup (app->priv->info->short_name);
|
||||
|
||||
#if defined(HAVE_SIGNAL) && defined(SIGINT)
|
||||
signal (SIGINT, sigint_handler);
|
||||
setup_signals (sigint_handler);
|
||||
#endif
|
||||
|
||||
install_editor_actions ();
|
||||
|
@ -839,7 +853,7 @@ moo_app_init_real (MooApp *app)
|
|||
#if defined(__WIN32__) && defined(MOO_BUILD_TERM)
|
||||
if (app->priv->use_terminal)
|
||||
{
|
||||
char *dir = moo_get_app_dir ();
|
||||
char *dir = moo_win32_get_app_dir ();
|
||||
moo_term_set_helper_directory (dir);
|
||||
g_free (dir);
|
||||
}
|
||||
|
@ -953,16 +967,55 @@ on_gtk_main_quit (MooApp *app)
|
|||
static gboolean
|
||||
check_signal (void)
|
||||
{
|
||||
if (moo_app_instance && moo_app_instance->priv->sigintr)
|
||||
if (signal_received)
|
||||
{
|
||||
moo_app_instance->priv->sigintr = FALSE;
|
||||
printf ("%s\n", g_strsignal (signal_received));
|
||||
MOO_APP_GET_CLASS(moo_app_instance)->quit (moo_app_instance);
|
||||
gtk_main_quit ();
|
||||
exit (0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
moo_app_try_quit (MooApp *app)
|
||||
{
|
||||
gboolean stopped = FALSE;
|
||||
|
||||
g_return_val_if_fail (MOO_IS_APP (app), FALSE);
|
||||
|
||||
if (!app->priv->running)
|
||||
return TRUE;
|
||||
|
||||
app->priv->in_try_quit = TRUE;
|
||||
g_signal_emit (app, signals[TRY_QUIT], 0, &stopped);
|
||||
app->priv->in_try_quit = FALSE;
|
||||
|
||||
return !stopped;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sm_quit_requested (MooApp *app)
|
||||
{
|
||||
EggSMClient *sm_client;
|
||||
|
||||
sm_client = app->priv->sm_client;
|
||||
g_return_if_fail (sm_client != NULL);
|
||||
|
||||
g_object_ref (sm_client);
|
||||
egg_sm_client_will_quit (sm_client, moo_app_try_quit (app));
|
||||
g_object_unref (sm_client);
|
||||
}
|
||||
|
||||
static void
|
||||
sm_quit (MooApp *app)
|
||||
{
|
||||
if (!moo_app_quit (app))
|
||||
MOO_APP_GET_CLASS(app)->quit (app);
|
||||
}
|
||||
|
||||
static int
|
||||
moo_app_run_real (MooApp *app)
|
||||
{
|
||||
|
@ -974,6 +1027,16 @@ moo_app_run_real (MooApp *app)
|
|||
|
||||
_moo_timeout_add (100, (GSourceFunc) check_signal, NULL);
|
||||
|
||||
app->priv->sm_client = egg_sm_client_get ();
|
||||
/* make it install log handler */
|
||||
g_option_group_free (egg_sm_client_get_option_group ());
|
||||
g_signal_connect_swapped (app->priv->sm_client, "quit-requested",
|
||||
G_CALLBACK (sm_quit_requested), app);
|
||||
g_signal_connect_swapped (app->priv->sm_client, "quit",
|
||||
G_CALLBACK (sm_quit), app);
|
||||
if (EGG_SM_CLIENT_GET_CLASS (app->priv->sm_client)->startup)
|
||||
EGG_SM_CLIENT_GET_CLASS (app->priv->sm_client)->startup (app->priv->sm_client, NULL);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return app->priv->exit_code;
|
||||
|
@ -989,7 +1052,7 @@ moo_app_try_quit_real (MooApp *app)
|
|||
return FALSE;
|
||||
|
||||
#ifdef MOO_BUILD_EDIT
|
||||
if (!moo_editor_close_all (app->priv->editor, TRUE))
|
||||
if (!moo_editor_close_all (app->priv->editor, TRUE, TRUE))
|
||||
return TRUE;
|
||||
#endif /* MOO_BUILD_EDIT */
|
||||
|
||||
|
@ -1039,6 +1102,9 @@ moo_app_quit_real (MooApp *app)
|
|||
else
|
||||
app->priv->running = FALSE;
|
||||
|
||||
g_object_unref (app->priv->sm_client);
|
||||
app->priv->sm_client = NULL;
|
||||
|
||||
if (moo_app_input)
|
||||
{
|
||||
_moo_app_input_shutdown (moo_app_input);
|
||||
|
@ -1055,7 +1121,7 @@ moo_app_quit_real (MooApp *app)
|
|||
app->priv->term_window = NULL;
|
||||
|
||||
#ifdef MOO_BUILD_EDIT
|
||||
moo_editor_close_all (app->priv->editor, FALSE);
|
||||
moo_editor_close_all (app->priv->editor, FALSE, FALSE);
|
||||
|
||||
moo_plugin_shutdown ();
|
||||
|
||||
|
@ -1119,18 +1185,12 @@ moo_app_run (MooApp *app)
|
|||
gboolean
|
||||
moo_app_quit (MooApp *app)
|
||||
{
|
||||
gboolean stopped = FALSE;
|
||||
|
||||
g_return_val_if_fail (MOO_IS_APP (app), FALSE);
|
||||
|
||||
if (app->priv->in_try_quit || !app->priv->running)
|
||||
return TRUE;
|
||||
|
||||
app->priv->in_try_quit = TRUE;
|
||||
g_signal_emit (app, signals[TRY_QUIT], 0, &stopped);
|
||||
app->priv->in_try_quit = FALSE;
|
||||
|
||||
if (!stopped)
|
||||
if (moo_app_try_quit (app))
|
||||
{
|
||||
MOO_APP_GET_CLASS(app)->quit (app);
|
||||
return TRUE;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
AM_CFLAGS = \
|
||||
-I$(top_builddir) \
|
||||
$(MOO_DEBUG_CFLAGS) \
|
||||
$(MOO_CFLAGS) \
|
||||
$(MOO_DEBUG_CFLAGS) \
|
||||
$(MOO_W_NO_MISSING_FIELD_INITIALIZERS) \
|
||||
$(MOO_W_NO_UNUSED)
|
||||
|
||||
noinst_LTLIBRARIES = \
|
||||
libsmclient.la
|
||||
|
||||
libsmclient_la_SOURCES = \
|
||||
eggsmclient.c \
|
||||
eggsmclient.h \
|
||||
eggsmclient-private.h
|
||||
|
||||
libsmclient_la_LIBADD =
|
||||
|
||||
if GDK_X11
|
||||
AM_CFLAGS += -DEGG_SM_CLIENT_BACKEND_XSMP
|
||||
libsmclient_la_LIBADD += -lSM -lICE
|
||||
libsmclient_la_SOURCES += eggsmclient-xsmp.c
|
||||
endif GDK_X11
|
||||
|
||||
if GDK_WIN32
|
||||
AM_CFLAGS += -DEGG_SM_CLIENT_BACKEND_WIN32
|
||||
libsmclient_la_SOURCES += eggsmclient-win32.c
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
eggsmclient-xsmp.c \
|
||||
eggsmclient-win32.c
|
|
@ -0,0 +1,49 @@
|
|||
/* eggsmclient-private.h
|
||||
* Copyright (C) 2007 Novell, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser 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.
|
||||
*/
|
||||
|
||||
#ifndef __EGG_SM_CLIENT_PRIVATE_H__
|
||||
#define __EGG_SM_CLIENT_PRIVATE_H__
|
||||
|
||||
#include <gdkconfig.h>
|
||||
#include "eggsmclient.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
char *egg_sm_client_save_state (EggSMClient *client);
|
||||
void egg_sm_client_quit_requested (EggSMClient *client);
|
||||
void egg_sm_client_quit_cancelled (EggSMClient *client);
|
||||
void egg_sm_client_quit (EggSMClient *client);
|
||||
|
||||
#if defined (GDK_WINDOWING_X11)
|
||||
# ifdef EGG_SM_CLIENT_BACKEND_XSMP
|
||||
EggSMClient *egg_sm_client_xsmp_new (void);
|
||||
# endif
|
||||
# ifdef EGG_SM_CLIENT_BACKEND_DBUS
|
||||
EggSMClient *egg_sm_client_dbus_new (void);
|
||||
# endif
|
||||
#elif defined (EGG_SM_CLIENT_BACKEND_WIN32)
|
||||
EggSMClient *egg_sm_client_win32_new (void);
|
||||
#elif defined (EGG_SM_CLIENT_BACKEND_OSX)
|
||||
EggSMClient *egg_sm_client_osx_new (void);
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Novell, 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-private.h"
|
||||
#include <gtk/gtk.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#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_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class))
|
||||
#define EGG_IS_SM_CLIENT_WIN32(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_WIN32))
|
||||
#define EGG_IS_SM_CLIENT_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_WIN32))
|
||||
#define EGG_SM_CLIENT_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class))
|
||||
|
||||
typedef struct _EggSMClientWin32 EggSMClientWin32;
|
||||
typedef struct _EggSMClientWin32Class EggSMClientWin32Class;
|
||||
|
||||
struct _EggSMClientWin32 {
|
||||
EggSMClient parent;
|
||||
|
||||
#ifdef VISTA
|
||||
gboolean registered;
|
||||
char *restart_args;
|
||||
char *state_dir;
|
||||
#endif
|
||||
|
||||
gboolean in_filter;
|
||||
gboolean will_quit;
|
||||
gboolean will_quit_set;
|
||||
};
|
||||
|
||||
struct _EggSMClientWin32Class
|
||||
{
|
||||
EggSMClientClass parent_class;
|
||||
|
||||
};
|
||||
|
||||
static void sm_client_win32_startup (EggSMClient *client,
|
||||
const char *client_id);
|
||||
#ifdef VISTA
|
||||
static void sm_client_win32_register_client (EggSMClient *client,
|
||||
const char *desktop_path);
|
||||
static void sm_client_win32_set_restart_command (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv);
|
||||
#endif
|
||||
static void sm_client_win32_will_quit (EggSMClient *client,
|
||||
gboolean will_quit);
|
||||
static gboolean sm_client_win32_end_session (EggSMClient *client,
|
||||
EggSMClientEndStyle style,
|
||||
gboolean request_confirmation);
|
||||
|
||||
static GdkFilterReturn egg_sm_client_win32_filter (GdkXEvent *xevent,
|
||||
GdkEvent *event,
|
||||
gpointer data);
|
||||
|
||||
G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT)
|
||||
|
||||
static void
|
||||
egg_sm_client_win32_init (EggSMClientWin32 *win32)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
egg_sm_client_win32_class_init (EggSMClientWin32Class *klass)
|
||||
{
|
||||
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
|
||||
|
||||
sm_client_class->startup = sm_client_win32_startup;
|
||||
#ifdef VISTA
|
||||
sm_client_class->register_client = sm_client_win32_register_client;
|
||||
sm_client_class->set_restart_command = sm_client_win32_set_restart_command;
|
||||
#endif
|
||||
sm_client_class->will_quit = sm_client_win32_will_quit;
|
||||
sm_client_class->end_session = sm_client_win32_end_session;
|
||||
}
|
||||
|
||||
EggSMClient *
|
||||
egg_sm_client_win32_new (void)
|
||||
{
|
||||
return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sm_client_win32_startup (EggSMClient *client,
|
||||
const char *client_id)
|
||||
{
|
||||
gdk_window_add_filter (NULL, egg_sm_client_win32_filter, client);
|
||||
}
|
||||
|
||||
#ifdef VISTA
|
||||
static void
|
||||
sm_client_win32_register_client (EggSMClient *client,
|
||||
const char *desktop_path)
|
||||
{
|
||||
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
||||
|
||||
win32->registered = TRUE;
|
||||
set_restart_info (win32);
|
||||
}
|
||||
|
||||
static void
|
||||
sm_client_win32_set_restart_command (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv)
|
||||
{
|
||||
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
||||
GString *cmdline = g_string_new (NULL);
|
||||
|
||||
g_return_if_fail (win32->registered == TRUE);
|
||||
|
||||
g_free (win32->restart_args);
|
||||
|
||||
/* RegisterApplicationRestart only cares about the part of the
|
||||
* command line after the executable name.
|
||||
*/
|
||||
if (argc > 1)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* FIXME: what is the right way to quote the arguments? */
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (i > 1)
|
||||
g_string_append_c (cmdline, ' ');
|
||||
g_string_append (cmdline, argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
win32->restart_args = g_string_free (cmdline, FALSE);
|
||||
set_restart_info (win32);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
sm_client_win32_will_quit (EggSMClient *client,
|
||||
gboolean will_quit)
|
||||
{
|
||||
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
||||
|
||||
win32->will_quit = will_quit;
|
||||
win32->will_quit_set = TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sm_client_win32_end_session (EggSMClient *client,
|
||||
EggSMClientEndStyle style,
|
||||
gboolean request_confirmation)
|
||||
{
|
||||
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
|
||||
UINT uFlags = EWX_LOGOFF;
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case EGG_SM_CLIENT_END_SESSION_DEFAULT:
|
||||
case EGG_SM_CLIENT_LOGOUT:
|
||||
uFlags = EWX_LOGOFF;
|
||||
break;
|
||||
case EGG_SM_CLIENT_REBOOT:
|
||||
uFlags = EWX_REBOOT;
|
||||
break;
|
||||
case EGG_SM_CLIENT_SHUTDOWN:
|
||||
uFlags = EWX_POWEROFF;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!request_confirmation)
|
||||
uFlags |= EWX_FORCE;
|
||||
|
||||
#ifdef SHTDN_REASON_FLAG_PLANNED
|
||||
ExitWindowsEx (uFlags, SHTDN_REASON_FLAG_PLANNED);
|
||||
#else
|
||||
ExitWindowsEx (uFlags, 0);
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
will_quit (EggSMClientWin32 *win32)
|
||||
{
|
||||
/* Will this really work? Or do we need to do something kinky like
|
||||
* arrange to have at least one WM_QUERYENDSESSION message delivered
|
||||
* to another thread and then have that thread wait for the main
|
||||
* thread to process the quit_requested signal asynchronously before
|
||||
* returning? FIXME
|
||||
*/
|
||||
win32->will_quit_set = FALSE;
|
||||
|
||||
egg_sm_client_quit_requested ((EggSMClient *)win32);
|
||||
while (!win32->will_quit_set)
|
||||
gtk_main_iteration ();
|
||||
|
||||
return win32->will_quit;
|
||||
}
|
||||
|
||||
static GdkFilterReturn
|
||||
egg_sm_client_win32_filter (GdkXEvent *xevent,
|
||||
GdkEvent *event,
|
||||
gpointer data)
|
||||
{
|
||||
EggSMClientWin32 *win32 = data;
|
||||
EggSMClient *client = data;
|
||||
MSG *msg = (MSG *)xevent;
|
||||
GdkFilterReturn retval;
|
||||
|
||||
if (win32->in_filter)
|
||||
{
|
||||
g_message ("win32->in_filter");
|
||||
return GDK_FILTER_CONTINUE;
|
||||
}
|
||||
|
||||
/* FIXME: I think these messages are delivered per-window, not
|
||||
* per-app, so we need to make sure we only act on them once. (Does
|
||||
* the win32 backend have client leader windows like x11?)
|
||||
*/
|
||||
|
||||
win32->in_filter = TRUE;
|
||||
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_QUERYENDSESSION:
|
||||
/* If we return GDK_FILTER_CONTINUE, the event will eventually
|
||||
* get passed on to DefWindowProc, which will return TRUE,
|
||||
* allowing the logout to continue. If we return
|
||||
* GDK_FILTER_REMOVE, then inner_window_procedure will return
|
||||
* its default value, 0, aka FALSE, causing the logout to be
|
||||
* aborted.
|
||||
*/
|
||||
retval = will_quit (win32) ? GDK_FILTER_CONTINUE : GDK_FILTER_REMOVE;
|
||||
break;
|
||||
|
||||
case WM_ENDSESSION:
|
||||
if (msg->wParam)
|
||||
{
|
||||
/* The session is ending */
|
||||
#ifdef VISTA
|
||||
if ((msg->lParam & ENDSESSION_CLOSEAPP) && win32->registered)
|
||||
{
|
||||
g_free (win32->state_dir);
|
||||
win32->state_dir = egg_sm_client_save_state (client);
|
||||
set_restart_info (win32);
|
||||
}
|
||||
#endif
|
||||
egg_sm_client_quit (client);
|
||||
}
|
||||
#ifdef VISTA
|
||||
else
|
||||
{
|
||||
/* The session isn't ending */
|
||||
egg_sm_client_quit_cancelled (client);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This will cause 0 to be returned, which is what we're supposed
|
||||
* to return, although the docs don't say what happens if you return
|
||||
* any other value.
|
||||
*/
|
||||
retval = GDK_FILTER_REMOVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = GDK_FILTER_CONTINUE;
|
||||
break;
|
||||
}
|
||||
|
||||
win32->in_filter = FALSE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef VISTA
|
||||
static void
|
||||
set_restart_info (EggSMClientWin32 *win32)
|
||||
{
|
||||
PCWSTR cmdline;
|
||||
|
||||
if (win32->state_dir)
|
||||
{
|
||||
char *restart_args =
|
||||
g_strdup_printf ("--sm-client-state-dir \"%s\"%s%s",
|
||||
win32->state_dir,
|
||||
*win32->restart_args ? " " : "",
|
||||
win32->restart_args);
|
||||
|
||||
/* FIXME: is this right? */
|
||||
cmdline = g_utf8_to_utf16 (restart_command, -1, NULL, NULL, NULL);
|
||||
}
|
||||
else if (*win32->restart_args)
|
||||
cmdline = g_utf8_to_utf16 (win32->restart_args, -1, NULL, NULL, NULL);
|
||||
else
|
||||
cmdline = NULL;
|
||||
|
||||
RegisterApplicationRestart (cmdline, RESTART_NO_CRASH | RESTART_NO_HANG);
|
||||
g_free (cmdline);
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Novell, 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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "eggsmclient.h"
|
||||
#include "eggsmclient-private.h"
|
||||
|
||||
static void egg_sm_client_debug_handler (const char *log_domain,
|
||||
GLogLevelFlags log_level,
|
||||
const char *message,
|
||||
gpointer user_data);
|
||||
|
||||
enum {
|
||||
SAVE_STATE,
|
||||
QUIT_REQUESTED,
|
||||
QUIT_CANCELLED,
|
||||
QUIT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
|
||||
|
||||
static EggSMClient *global_client;
|
||||
|
||||
static void
|
||||
egg_sm_client_init (EggSMClient *client)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
egg_sm_client_class_init (EggSMClientClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
/**
|
||||
* EggSMClient::save_state:
|
||||
* @client: the client
|
||||
* @state_dir: directory to save state into
|
||||
*
|
||||
* Emitted when the session manager has requested that the
|
||||
* application save information about its current state. The
|
||||
* application should create whatever sort of files it needs in
|
||||
* @state_dir in order to remember its state; the session manager
|
||||
* may then restart the application in a future session and tell it
|
||||
* to initialize itself from that state.
|
||||
*
|
||||
* The @state_dir will be different every time this signal is
|
||||
* emitted, and there is no reason for the application to keep track
|
||||
* of it after it is done saving its state; #EggSMClient will
|
||||
* arrange for the directory to be deleted when it is no longer
|
||||
* needed.
|
||||
*
|
||||
* Alternatively, rather than (or in addition to) using @state_dir,
|
||||
* the application can save its state by calling
|
||||
* egg_sm_client_set_restart_command() during the processing of this
|
||||
* signal (eg, to include a list of files to open).
|
||||
**/
|
||||
signals[SAVE_STATE] =
|
||||
g_signal_new ("save_state",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, save_state),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__STRING,
|
||||
G_TYPE_NONE,
|
||||
1, G_TYPE_STRING);
|
||||
|
||||
/**
|
||||
* EggSMClient::quit_requested:
|
||||
* @client: the client
|
||||
*
|
||||
* Emitted when the session manager requests that the application
|
||||
* exit (generally because the user is logging out). The application
|
||||
* should decide whether or not it is willing to quit (perhaps after
|
||||
* asking the user what to do with documents that have unsaved
|
||||
* changes) and then call egg_sm_client_will_quit(), passing %TRUE
|
||||
* or %FALSE to give its answer to the session manager. (It does not
|
||||
* need to give an answer before returning from the signal handler;
|
||||
* it can interact with the user asynchronously and then give its
|
||||
* answer later on.) If the application does not connect to this
|
||||
* signal, then #EggSMClient will automatically return %TRUE on its
|
||||
* behalf.
|
||||
*
|
||||
* The application should not save its session state as part of
|
||||
* handling this signal; if the user has requested that the session
|
||||
* be saved when logging out, then ::save_state will be emitted
|
||||
* separately.
|
||||
*
|
||||
* If the application agrees to quit, it should then wait for either
|
||||
* the ::quit_cancelled or ::quit signals to be emitted.
|
||||
**/
|
||||
signals[QUIT_REQUESTED] =
|
||||
g_signal_new ("quit_requested",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
/**
|
||||
* EggSMClient::quit_cancelled:
|
||||
* @client: the client
|
||||
*
|
||||
* Emitted when the session manager decides to cancel a logout after
|
||||
* the application has already agreed to quit. After receiving this
|
||||
* signal, the application can go back to what it was doing before
|
||||
* receiving the ::quit_requested signal.
|
||||
**/
|
||||
signals[QUIT_CANCELLED] =
|
||||
g_signal_new ("quit_cancelled",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
/**
|
||||
* EggSMClient::quit:
|
||||
* @client: the client
|
||||
*
|
||||
* Emitted when the session manager wants the application to quit
|
||||
* (generally because the user is logging out). The application
|
||||
* should exit as soon as possible after receiving this signal; if
|
||||
* it does not, the session manager may choose to forcibly kill it.
|
||||
*
|
||||
* Normally a GUI application would only be sent a ::quit if it
|
||||
* agreed to quit in response to a ::quit_requested signal. However,
|
||||
* this is not guaranteed; in some situations the session manager
|
||||
* may decide to end the session without giving applications a
|
||||
* chance to object.
|
||||
**/
|
||||
signals[QUIT] =
|
||||
g_signal_new ("quit",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, quit),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
||||
static gboolean sm_client_disable = FALSE;
|
||||
static char *sm_client_state_dir = NULL;
|
||||
static char *sm_client_config_prefix = NULL;
|
||||
static char *sm_client_id = 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-dir", 0, 0,
|
||||
G_OPTION_ARG_STRING, &sm_client_state_dir,
|
||||
N_("Specify directory containing saved configuration"), N_("DIR") },
|
||||
{ "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
|
||||
G_OPTION_ARG_STRING, &sm_client_config_prefix,
|
||||
NULL, NULL },
|
||||
{ "sm-client-id", 0, 0,
|
||||
G_OPTION_ARG_STRING, &sm_client_id,
|
||||
N_("Specify session management ID"), N_("ID") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gboolean
|
||||
sm_client_post_parse_func (GOptionContext *context,
|
||||
GOptionGroup *group,
|
||||
gpointer data,
|
||||
GError **error)
|
||||
{
|
||||
EggSMClient *client = egg_sm_client_get ();
|
||||
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_get_option_group:
|
||||
*
|
||||
* Creates a %GOptionGroup containing the session-management-related
|
||||
* options. You should add this group to the application's
|
||||
* %GOptionContext if you want to use #EggSMClient.
|
||||
*
|
||||
* Return value: the %GOptionGroup
|
||||
**/
|
||||
GOptionGroup *
|
||||
egg_sm_client_get_option_group (void)
|
||||
{
|
||||
GOptionGroup *group;
|
||||
|
||||
/* Use our own debug handler for the "EggSMClient" domain. */
|
||||
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
|
||||
egg_sm_client_debug_handler, NULL);
|
||||
|
||||
group = g_option_group_new ("sm-client",
|
||||
_("Session Management Options"),
|
||||
_("Show Session Management options"),
|
||||
NULL, NULL);
|
||||
g_option_group_add_entries (group, entries);
|
||||
g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_register:
|
||||
* @desktop_path: Path to the application's .desktop file (or %NULL)
|
||||
*
|
||||
* Registers the application with the session manager. This MAY
|
||||
* (depending on session manager policies and user preferences) mean
|
||||
* that the application will be automatically restarted on future
|
||||
* logins. (See also egg_sm_client_get().)
|
||||
*
|
||||
* @desktop_path is used to find various information about the
|
||||
* application, including its (localized) name and description, its
|
||||
* icon, the command used to invoke it, and whether or not it wants to
|
||||
* be restarted by the session manager if it crashes. (On Windows and
|
||||
* OS X, the name and icon are determined automatically, and you can
|
||||
* simply pass %NULL for @desktop_path. Under X11, if you pass %NULL
|
||||
* for @desktop_path, the restart command defaults to the return value
|
||||
* of g_get_prgname(). You can use egg_sm_client_set_restart_command()
|
||||
* to set an alternate restart command if necessary.)
|
||||
*
|
||||
* Return value: the #EggSMClient instance for this application
|
||||
**/
|
||||
EggSMClient *
|
||||
egg_sm_client_register (const char *desktop_path)
|
||||
{
|
||||
EggSMClient *client = egg_sm_client_get ();
|
||||
|
||||
if (!sm_client_disable &&
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->register_client)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->register_client (client, desktop_path);
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_get:
|
||||
*
|
||||
* Returns the master #EggSMClient.
|
||||
*
|
||||
* This method (as opposed to egg_sm_client_register()) can be used by
|
||||
* an application that wants to listen to the logout-related signals,
|
||||
* or that wants to call egg_sm_client_end_session(), but that does
|
||||
* not ever want to be restarted automatically in future sessions.
|
||||
*
|
||||
* This method can also be used by libraries that want to connect to
|
||||
* the ::save_state signal (although that signal will only be emitted
|
||||
* if the application calls egg_sm_client_register().)
|
||||
*
|
||||
* Return value: the master #EggSMClient.
|
||||
**/
|
||||
EggSMClient *
|
||||
egg_sm_client_get (void)
|
||||
{
|
||||
if (!global_client)
|
||||
{
|
||||
#if defined (EGG_SM_CLIENT_BACKEND_WIN32)
|
||||
global_client = egg_sm_client_win32_new ();
|
||||
# elif defined (EGG_SM_CLIENT_BACKEND_OSX)
|
||||
global_client = egg_sm_client_osx_new ();
|
||||
#else
|
||||
/* If both D-Bus and XSMP are compiled in, try D-Bus first and fall
|
||||
* back to XSMP if D-Bus session management isn't available.
|
||||
*/
|
||||
# ifdef EGG_SM_CLIENT_BACKEND_DBUS
|
||||
global_client = egg_sm_client_dbus_new ();
|
||||
# endif
|
||||
# ifdef EGG_SM_CLIENT_BACKEND_XSMP
|
||||
if (!global_client)
|
||||
global_client = egg_sm_client_xsmp_new ();
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Fallback: create a dummy client, so that callers don't have
|
||||
* to worry about a %NULL return value.
|
||||
*/
|
||||
if (!global_client)
|
||||
global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL);
|
||||
}
|
||||
|
||||
return global_client;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_is_resumed:
|
||||
* @client: the client
|
||||
*
|
||||
* Checks whether or not the current session has been resumed from
|
||||
* a previous saved session. If so, the application should call
|
||||
* egg_sm_client_get_state_dir() and restore its state from the files
|
||||
* contained there.
|
||||
*
|
||||
* Return value: %TRUE if the session has been resumed
|
||||
**/
|
||||
gboolean
|
||||
egg_sm_client_is_resumed (EggSMClient *client)
|
||||
{
|
||||
g_return_val_if_fail (client == global_client, FALSE);
|
||||
|
||||
/* FIXME: should this actually check both state_dir and config_prefix?
|
||||
* Or just state_dir? Or is there really not any need for this method
|
||||
* at all?
|
||||
*/
|
||||
|
||||
return sm_client_state_dir != NULL || sm_client_config_prefix != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_get_state_dir:
|
||||
* @client: the client
|
||||
*
|
||||
* If the application was resumed by the session manager, this will
|
||||
* return the path to the directory containing its state from the
|
||||
* previous session.
|
||||
*
|
||||
* Return value: the directory containing the application's earlier
|
||||
* state, or %NULL
|
||||
**/
|
||||
const char *
|
||||
egg_sm_client_get_state_dir (EggSMClient *client)
|
||||
{
|
||||
g_return_val_if_fail (client == global_client, NULL);
|
||||
|
||||
return sm_client_state_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_get_config_prefix:
|
||||
* @client: the client
|
||||
*
|
||||
* If the application was resumed by the session manager with the
|
||||
* command-line argument "--sm-config-prefix ...", this will return
|
||||
* the prefix specified (and egg_sm_client_get_state_dir() will return
|
||||
* %NULL). This can be used by applications that used to use
|
||||
* #GnomeClient, and that want to support upgrading old saved states.
|
||||
*
|
||||
* Return value: the specified "config prefix", or %NULL
|
||||
**/
|
||||
const char *
|
||||
egg_sm_client_get_config_prefix (EggSMClient *client)
|
||||
{
|
||||
g_return_val_if_fail (client == global_client, NULL);
|
||||
|
||||
return sm_client_config_prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_set_restart_command:
|
||||
* @client: the client
|
||||
* @argc: the length of @argv
|
||||
* @argv: argument vector
|
||||
*
|
||||
* Sets the command used to restart @client if it does not have a
|
||||
* .desktop file that can be used to find its restart command.
|
||||
*
|
||||
* This can also be used when handling the ::save_state signal, to
|
||||
* save the current state via an updated command line. (Eg, providing
|
||||
* a list of filenames to open when the application is resumed.)
|
||||
**/
|
||||
void
|
||||
egg_sm_client_set_restart_command (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv)
|
||||
{
|
||||
g_return_if_fail (EGG_IS_SM_CLIENT (client));
|
||||
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_will_quit:
|
||||
* @client: the client
|
||||
* @will_quit: whether or not the application is willing to quit
|
||||
*
|
||||
* This MUST be called in response to the ::quit_requested signal, to
|
||||
* indicate whether or not the application is willing to quit. The
|
||||
* application may call it either directly from the signal handler, or
|
||||
* at some later point (eg, after asynchronously interacting with the
|
||||
* user).
|
||||
*
|
||||
* If the application does not connect to ::quit_requested,
|
||||
* #EggSMClient will call this method on its behalf (passing %TRUE
|
||||
* for @will_quit).
|
||||
*
|
||||
* After calling this method, the application should wait to receive
|
||||
* either ::quit_cancelled or ::quit.
|
||||
**/
|
||||
void
|
||||
egg_sm_client_will_quit (EggSMClient *client,
|
||||
gboolean will_quit)
|
||||
{
|
||||
g_return_if_fail (EGG_IS_SM_CLIENT (client));
|
||||
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit);
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_end_session:
|
||||
* @client: the client
|
||||
* @style: a hint at how to end the session
|
||||
* @request_confirmation: whether or not the user should get a chance
|
||||
* to confirm the action
|
||||
*
|
||||
* Requests that the session manager end the current session. @style
|
||||
* indicates how the session should be ended, and
|
||||
* @request_confirmation indicates whether or not the user should be
|
||||
* given a chance to confirm the logout/reboot/shutdown. Both of these
|
||||
* flags are merely hints though; the session manager may choose to
|
||||
* ignore them.
|
||||
*
|
||||
* Return value: %TRUE if the request was sent; %FALSE if it could not
|
||||
* be (eg, because it could not connect to the session manager).
|
||||
**/
|
||||
gboolean
|
||||
egg_sm_client_end_session (EggSMClient *client,
|
||||
EggSMClientEndStyle style,
|
||||
gboolean request_confirmation)
|
||||
{
|
||||
g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE);
|
||||
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->end_session)
|
||||
{
|
||||
return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style,
|
||||
request_confirmation);
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Signal-emitting callbacks from platform-specific code */
|
||||
|
||||
#ifdef EGG_SM_CLIENT_BACKEND_XSMP
|
||||
char *
|
||||
egg_sm_client_save_state (EggSMClient *client)
|
||||
{
|
||||
char *state_dir;
|
||||
gboolean success;
|
||||
|
||||
g_return_val_if_fail (client == global_client, NULL);
|
||||
|
||||
if (!g_signal_has_handler_pending (client, signals[SAVE_STATE], 0, FALSE))
|
||||
return NULL;
|
||||
|
||||
state_dir = g_strdup_printf ("%s%csession-state%c%s-XXXXXX",
|
||||
g_get_user_config_dir (),
|
||||
G_DIR_SEPARATOR, G_DIR_SEPARATOR,
|
||||
g_get_prgname ());
|
||||
|
||||
success = mkdtemp (state_dir) != NULL;
|
||||
if (!success && errno == ENOENT)
|
||||
{
|
||||
char *sep = strrchr (state_dir, G_DIR_SEPARATOR);
|
||||
|
||||
*sep = '\0';
|
||||
if (g_mkdir_with_parents (state_dir, 0755) == 0)
|
||||
{
|
||||
sprintf (sep, "%c%s-XXXXXX", G_DIR_SEPARATOR, g_get_prgname ());
|
||||
success = mkdtemp (state_dir) != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
g_warning ("Could not create session state directory '%s': %s",
|
||||
state_dir, g_strerror (errno));
|
||||
g_free (state_dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_debug ("Emitting save_state");
|
||||
g_signal_emit (client, signals[SAVE_STATE], 0, state_dir);
|
||||
g_debug ("Done emitting save_state");
|
||||
|
||||
if (rmdir (state_dir) == 0)
|
||||
{
|
||||
g_free (state_dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return state_dir;
|
||||
}
|
||||
#endif /* EGG_SM_CLIENT_BACKEND_XSMP */
|
||||
|
||||
void
|
||||
egg_sm_client_quit_requested (EggSMClient *client)
|
||||
{
|
||||
g_return_if_fail (client == global_client);
|
||||
|
||||
if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE))
|
||||
{
|
||||
g_debug ("Not emitting quit_requested because no one is listening");
|
||||
egg_sm_client_will_quit (client, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Emitting quit_requested");
|
||||
g_signal_emit (client, signals[QUIT_REQUESTED], 0);
|
||||
g_debug ("Done emitting quit_requested");
|
||||
}
|
||||
|
||||
void
|
||||
egg_sm_client_quit_cancelled (EggSMClient *client)
|
||||
{
|
||||
g_return_if_fail (client == global_client);
|
||||
|
||||
g_debug ("Emitting quit_cancelled");
|
||||
g_signal_emit (client, signals[QUIT_CANCELLED], 0);
|
||||
g_debug ("Done emitting quit_cancelled");
|
||||
}
|
||||
|
||||
void
|
||||
egg_sm_client_quit (EggSMClient *client)
|
||||
{
|
||||
g_return_if_fail (client == global_client);
|
||||
|
||||
g_debug ("Emitting quit");
|
||||
g_signal_emit (client, signals[QUIT], 0);
|
||||
g_debug ("Done emitting quit");
|
||||
|
||||
/* FIXME: should we just call gtk_main_quit() here? */
|
||||
/* Of course not! */
|
||||
}
|
||||
|
||||
static void
|
||||
egg_sm_client_debug_handler (const char *log_domain,
|
||||
GLogLevelFlags log_level,
|
||||
const char *message,
|
||||
gpointer user_data)
|
||||
{
|
||||
static int debug = -1;
|
||||
|
||||
if (debug < 0)
|
||||
debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
|
||||
|
||||
if (debug)
|
||||
g_log_default_handler (log_domain, log_level, message, NULL);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* eggsmclient.h
|
||||
* Copyright (C) 2007 Novell, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser 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.
|
||||
*/
|
||||
|
||||
#ifndef __EGG_SM_CLIENT_H__
|
||||
#define __EGG_SM_CLIENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define EGG_TYPE_SM_CLIENT (egg_sm_client_get_type ())
|
||||
#define EGG_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient))
|
||||
#define EGG_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass))
|
||||
#define EGG_IS_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT))
|
||||
#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT))
|
||||
#define EGG_SM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass))
|
||||
|
||||
typedef struct _EggSMClient EggSMClient;
|
||||
typedef struct _EggSMClientClass EggSMClientClass;
|
||||
typedef struct _EggSMClientPrivate EggSMClientPrivate;
|
||||
|
||||
typedef enum {
|
||||
EGG_SM_CLIENT_END_SESSION_DEFAULT,
|
||||
EGG_SM_CLIENT_LOGOUT,
|
||||
EGG_SM_CLIENT_REBOOT,
|
||||
EGG_SM_CLIENT_SHUTDOWN
|
||||
} EggSMClientEndStyle;
|
||||
|
||||
struct _EggSMClient
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
};
|
||||
|
||||
struct _EggSMClientClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*save_state) (EggSMClient *client,
|
||||
const char *state_dir);
|
||||
|
||||
void (*quit_requested) (EggSMClient *client);
|
||||
void (*quit_cancelled) (EggSMClient *client);
|
||||
void (*quit) (EggSMClient *client);
|
||||
|
||||
/* virtual methods */
|
||||
void (*startup) (EggSMClient *client,
|
||||
const char *client_id);
|
||||
void (*register_client) (EggSMClient *client,
|
||||
const char *desktop_path);
|
||||
void (*set_restart_command) (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv);
|
||||
void (*will_quit) (EggSMClient *client,
|
||||
gboolean will_quit);
|
||||
gboolean (*end_session) (EggSMClient *client,
|
||||
EggSMClientEndStyle style,
|
||||
gboolean request_confirmation);
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_egg_reserved1) (void);
|
||||
void (*_egg_reserved2) (void);
|
||||
void (*_egg_reserved3) (void);
|
||||
void (*_egg_reserved4) (void);
|
||||
};
|
||||
|
||||
GType egg_sm_client_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GOptionGroup *egg_sm_client_get_option_group (void);
|
||||
|
||||
/* Initialization */
|
||||
EggSMClient *egg_sm_client_register (const char *desktop_file);
|
||||
EggSMClient *egg_sm_client_get (void);
|
||||
|
||||
/* Resuming a saved session */
|
||||
gboolean egg_sm_client_is_resumed (EggSMClient *client);
|
||||
const char *egg_sm_client_get_state_dir (EggSMClient *client);
|
||||
const char *egg_sm_client_get_config_prefix (EggSMClient *client);
|
||||
|
||||
/* Alternate means of saving state */
|
||||
void egg_sm_client_set_restart_command (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv);
|
||||
|
||||
/* Handling "quit_requested" signal */
|
||||
void egg_sm_client_will_quit (EggSMClient *client,
|
||||
gboolean will_quit);
|
||||
|
||||
/* Initiate a logout/reboot/shutdown */
|
||||
gboolean egg_sm_client_end_session (EggSMClient *client,
|
||||
EggSMClientEndStyle style,
|
||||
gboolean request_confirmation);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __EGG_SM_CLIENT_H__ */
|
Loading…
Reference in New Issue