480 lines
15 KiB
C
480 lines
15 KiB
C
|
|
/* vi:set et ai sw=2 sts=2 ts=2: */
|
||
|
|
/*-
|
||
|
|
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
|
||
|
|
*
|
||
|
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
|
* under the terms of the GNU General Public License as published by the Free
|
||
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||
|
|
* any later version.
|
||
|
|
*
|
||
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||
|
|
* more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU General Public License along with
|
||
|
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||
|
|
* Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifdef HAVE_CONFIG_H
|
||
|
|
#include "config.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef HAVE_MEMORY_H
|
||
|
|
#include <memory.h>
|
||
|
|
#endif
|
||
|
|
#include <stdio.h>
|
||
|
|
#ifdef HAVE_STDLIB_H
|
||
|
|
#include <stdlib.h>
|
||
|
|
#endif
|
||
|
|
#ifdef HAVE_STRING_H
|
||
|
|
#include <string.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef HAVE_LIBSM
|
||
|
|
#include <X11/SM/SMlib.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include "thunar/thunar-application.h"
|
||
|
|
#include "thunar/thunar-file.h"
|
||
|
|
#include "thunar/thunar-ice.h"
|
||
|
|
#include "thunar/thunar-private.h"
|
||
|
|
#include "thunar/thunar-session-client.h"
|
||
|
|
|
||
|
|
#include <gdk/gdkx.h>
|
||
|
|
#include <glib/gstdio.h>
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_finalize (GObject *object);
|
||
|
|
#ifdef HAVE_LIBSM
|
||
|
|
static gboolean
|
||
|
|
thunar_session_client_connect (ThunarSessionClient *session_client,
|
||
|
|
const gchar *previous_id);
|
||
|
|
static void
|
||
|
|
thunar_session_client_restore (ThunarSessionClient *session_client);
|
||
|
|
static void
|
||
|
|
thunar_session_client_die (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client);
|
||
|
|
static void
|
||
|
|
thunar_session_client_save_complete (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client);
|
||
|
|
static void
|
||
|
|
thunar_session_client_save_yourself (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client,
|
||
|
|
gint save_type,
|
||
|
|
Bool shutdown,
|
||
|
|
gint interact_style,
|
||
|
|
Bool fast);
|
||
|
|
static void
|
||
|
|
thunar_session_client_shutdown_cancelled (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client);
|
||
|
|
#endif /* !HAVE_LIBSM */
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
struct _ThunarSessionClientClass
|
||
|
|
{
|
||
|
|
GObjectClass __parent__;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct _ThunarSessionClient
|
||
|
|
{
|
||
|
|
GObject __parent__;
|
||
|
|
|
||
|
|
gchar *path;
|
||
|
|
gchar *id;
|
||
|
|
|
||
|
|
#ifdef HAVE_LIBSM
|
||
|
|
SmcConn connection;
|
||
|
|
#endif
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
G_DEFINE_TYPE (ThunarSessionClient, thunar_session_client, G_TYPE_OBJECT)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_class_init (ThunarSessionClientClass *klass)
|
||
|
|
{
|
||
|
|
GObjectClass *gobject_class;
|
||
|
|
|
||
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
||
|
|
gobject_class->finalize = thunar_session_client_finalize;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_init (ThunarSessionClient *session_client)
|
||
|
|
{
|
||
|
|
/* initialize the ICE layer */
|
||
|
|
thunar_ice_init ();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_finalize (GObject *object)
|
||
|
|
{
|
||
|
|
ThunarSessionClient *session_client = THUNAR_SESSION_CLIENT (object);
|
||
|
|
|
||
|
|
#ifdef HAVE_LIBSM
|
||
|
|
/* disconnect from the session manager */
|
||
|
|
if (G_LIKELY (session_client->connection != NULL))
|
||
|
|
SmcCloseConnection (session_client->connection, 0, NULL);
|
||
|
|
#endif /* !HAVE_LIBSM */
|
||
|
|
|
||
|
|
/* release internal state */
|
||
|
|
g_free (session_client->path);
|
||
|
|
g_free (session_client->id);
|
||
|
|
|
||
|
|
(*G_OBJECT_CLASS (thunar_session_client_parent_class)->finalize) (object);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#ifdef HAVE_LIBSM
|
||
|
|
static gboolean
|
||
|
|
thunar_session_client_connect (ThunarSessionClient *session_client,
|
||
|
|
const gchar *previous_id)
|
||
|
|
{
|
||
|
|
SmcCallbacks session_callbacks;
|
||
|
|
SmPropValue value_discard[3];
|
||
|
|
SmPropValue value_restart[4];
|
||
|
|
SmPropValue value_userid[1];
|
||
|
|
SmPropValue value_priority[1];
|
||
|
|
SmProp *properties[6];
|
||
|
|
SmProp prop_clone;
|
||
|
|
SmProp prop_discard;
|
||
|
|
SmProp prop_program;
|
||
|
|
SmProp prop_restart;
|
||
|
|
SmProp prop_userid;
|
||
|
|
SmProp prop_priority;
|
||
|
|
gchar error_string[1];
|
||
|
|
gchar *spec;
|
||
|
|
gchar *id = NULL;
|
||
|
|
|
||
|
|
/* setup the session client callbacks */
|
||
|
|
session_callbacks.die.callback = (SmcDieProc) thunar_session_client_die;
|
||
|
|
session_callbacks.die.client_data = session_client;
|
||
|
|
session_callbacks.save_complete.callback = (SmcSaveCompleteProc) thunar_session_client_save_complete;
|
||
|
|
session_callbacks.save_complete.client_data = session_client;
|
||
|
|
session_callbacks.save_yourself.callback = (SmcSaveYourselfProc) thunar_session_client_save_yourself;
|
||
|
|
session_callbacks.save_yourself.client_data = session_client;
|
||
|
|
session_callbacks.shutdown_cancelled.callback = (SmcShutdownCancelledProc) thunar_session_client_shutdown_cancelled;
|
||
|
|
session_callbacks.shutdown_cancelled.client_data = session_client;
|
||
|
|
|
||
|
|
/* try to establish a connection to the session manager */
|
||
|
|
session_client->connection = SmcOpenConnection (NULL, NULL, SmProtoMajor, SmProtoMinor, SmcDieProcMask | SmcSaveCompleteProcMask | SmcSaveYourselfProcMask | SmcShutdownCancelledProcMask, &session_callbacks,
|
||
|
|
(gchar *) previous_id, &id, sizeof (error_string), error_string);
|
||
|
|
if (G_UNLIKELY (session_client->connection == NULL))
|
||
|
|
return FALSE;
|
||
|
|
|
||
|
|
/* tell GDK about our new session id */
|
||
|
|
gdk_x11_set_sm_client_id (id);
|
||
|
|
|
||
|
|
/* remember the returned client id */
|
||
|
|
session_client->id = id;
|
||
|
|
|
||
|
|
/* determine the session file path */
|
||
|
|
spec = g_strconcat ("sessions/Thunar-", session_client->id, NULL);
|
||
|
|
session_client->path = xfce_resource_save_location (XFCE_RESOURCE_CACHE, spec, TRUE);
|
||
|
|
g_free (spec);
|
||
|
|
|
||
|
|
/* verify that we were able to create the path to the file */
|
||
|
|
if (G_UNLIKELY (session_client->path == NULL))
|
||
|
|
{
|
||
|
|
/* disconnect from the session manager */
|
||
|
|
SmcCloseConnection (session_client->connection, 0, NULL);
|
||
|
|
session_client->connection = NULL;
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* SmCloneCommand (see SmRestartCommand) */
|
||
|
|
prop_clone.name = SmCloneCommand;
|
||
|
|
prop_clone.type = SmLISTofARRAY8;
|
||
|
|
prop_clone.num_vals = 1;
|
||
|
|
prop_clone.vals = &value_restart[0];
|
||
|
|
|
||
|
|
/* SmDiscardCommand */
|
||
|
|
prop_discard.name = SmDiscardCommand;
|
||
|
|
prop_discard.type = SmLISTofARRAY8;
|
||
|
|
prop_discard.num_vals = G_N_ELEMENTS (value_discard);
|
||
|
|
prop_discard.vals = &value_discard[0];
|
||
|
|
value_discard[0].value = "rm";
|
||
|
|
value_discard[0].length = sizeof ("rm") - 1;
|
||
|
|
value_discard[1].value = "-f";
|
||
|
|
value_discard[1].length = sizeof ("-f") - 1;
|
||
|
|
value_discard[2].value = session_client->path;
|
||
|
|
value_discard[2].length = strlen (session_client->path);
|
||
|
|
|
||
|
|
/* SmProgram (see SmRestartCommand) */
|
||
|
|
prop_program.name = SmProgram;
|
||
|
|
prop_program.type = SmARRAY8;
|
||
|
|
prop_program.num_vals = 1;
|
||
|
|
prop_program.vals = &value_restart[0];
|
||
|
|
|
||
|
|
/* SmRestartCommand */
|
||
|
|
prop_restart.name = SmRestartCommand;
|
||
|
|
prop_restart.type = SmLISTofARRAY8;
|
||
|
|
prop_restart.num_vals = G_N_ELEMENTS (value_restart);
|
||
|
|
prop_restart.vals = &value_restart[0];
|
||
|
|
value_restart[0].value = "Thunar";
|
||
|
|
value_restart[0].length = sizeof ("Thunar") - 1;
|
||
|
|
value_restart[1].value = "--sm-client-id";
|
||
|
|
value_restart[1].length = sizeof ("--sm-client-id") - 1;
|
||
|
|
value_restart[2].value = session_client->id;
|
||
|
|
value_restart[2].length = strlen (session_client->id);
|
||
|
|
value_restart[3].value = "--daemon";
|
||
|
|
value_restart[3].length = sizeof ("--daemon") - 1;
|
||
|
|
|
||
|
|
/* SmUserID */
|
||
|
|
prop_userid.name = SmUserID;
|
||
|
|
prop_userid.type = SmARRAY8;
|
||
|
|
prop_userid.num_vals = G_N_ELEMENTS (value_userid);
|
||
|
|
prop_userid.vals = &value_userid[0];
|
||
|
|
value_userid[0].value = (gchar *) g_get_user_name ();
|
||
|
|
value_userid[0].length = strlen (value_userid[0].value);
|
||
|
|
|
||
|
|
/* _GSM_Priority */
|
||
|
|
prop_priority.name = "_GSM_Priority";
|
||
|
|
prop_priority.type = SmCARD8;
|
||
|
|
prop_priority.num_vals = G_N_ELEMENTS (value_priority);
|
||
|
|
prop_priority.vals = &value_priority[0];
|
||
|
|
value_priority[0].value = "\36"; /* this is octal, 30 in decimal */
|
||
|
|
value_priority[0].length = 1;
|
||
|
|
|
||
|
|
/* setup the properties list */
|
||
|
|
properties[0] = &prop_clone;
|
||
|
|
properties[1] = &prop_discard;
|
||
|
|
properties[2] = &prop_program;
|
||
|
|
properties[3] = &prop_restart;
|
||
|
|
properties[4] = &prop_userid;
|
||
|
|
properties[5] = &prop_priority;
|
||
|
|
|
||
|
|
/* send the properties to the session manager */
|
||
|
|
SmcSetProperties (session_client->connection, G_N_ELEMENTS (properties), properties);
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_restore (ThunarSessionClient *session_client)
|
||
|
|
{
|
||
|
|
ThunarApplication *application;
|
||
|
|
gchar **uris;
|
||
|
|
GtkWidget *window;
|
||
|
|
XfceRc *rc;
|
||
|
|
gchar **roles;
|
||
|
|
guint n;
|
||
|
|
gint active_tab;
|
||
|
|
|
||
|
|
/* try to open the session file */
|
||
|
|
rc = xfce_rc_simple_open (session_client->path, TRUE);
|
||
|
|
if (G_UNLIKELY (rc == NULL))
|
||
|
|
return;
|
||
|
|
|
||
|
|
/* grab a reference on the application */
|
||
|
|
application = thunar_application_get ();
|
||
|
|
|
||
|
|
/* determine all window roles */
|
||
|
|
roles = xfce_rc_get_groups (rc);
|
||
|
|
for (n = 0; roles[n] != NULL; ++n)
|
||
|
|
{
|
||
|
|
/* skip the null group */
|
||
|
|
if (strcmp (roles[n], "[NULL]") == 0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
/* enter the group */
|
||
|
|
xfce_rc_set_group (rc, roles[n]);
|
||
|
|
|
||
|
|
/* determine the URI for the new window */
|
||
|
|
uris = xfce_rc_read_list_entry (rc, "URI", ";");
|
||
|
|
if (G_UNLIKELY (uris == NULL))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
/* active tab */
|
||
|
|
active_tab = xfce_rc_read_int_entry (rc, "PAGE", -1);
|
||
|
|
|
||
|
|
/* open the new window */
|
||
|
|
window = g_object_new (THUNAR_TYPE_WINDOW, "role", roles[n], NULL);
|
||
|
|
thunar_application_take_window (application, GTK_WINDOW (window));
|
||
|
|
gtk_widget_show (window);
|
||
|
|
|
||
|
|
/* open tabs */
|
||
|
|
if (!thunar_window_set_directories (THUNAR_WINDOW (window), uris, active_tab))
|
||
|
|
{
|
||
|
|
/* no tabs were opened */
|
||
|
|
gtk_widget_destroy (window);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* The accel map will be loaded after the first window is created */
|
||
|
|
/* For some reason it is important to do so only AFTER creation of the window! */
|
||
|
|
/* When loaded, the accelerators of the first window need to be reconnected */
|
||
|
|
if (thunar_application_accel_map_init (application))
|
||
|
|
thunar_window_reconnect_accelerators (THUNAR_WINDOW (window));
|
||
|
|
|
||
|
|
/* cleanup */
|
||
|
|
g_strfreev (uris);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* cleanup */
|
||
|
|
g_object_unref (G_OBJECT (application));
|
||
|
|
xfce_rc_close (rc);
|
||
|
|
g_strfreev (roles);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_die (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client)
|
||
|
|
{
|
||
|
|
ThunarApplication *application;
|
||
|
|
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_SESSION_CLIENT (session_client));
|
||
|
|
_thunar_return_if_fail (session_client->connection == connection);
|
||
|
|
|
||
|
|
application = thunar_application_get ();
|
||
|
|
thunar_application_quit (application);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_save_complete (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_SESSION_CLIENT (session_client));
|
||
|
|
_thunar_return_if_fail (session_client->connection == connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_save_yourself (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client,
|
||
|
|
gint save_type,
|
||
|
|
Bool shutdown,
|
||
|
|
gint interact_style,
|
||
|
|
Bool fast)
|
||
|
|
{
|
||
|
|
ThunarApplication *application;
|
||
|
|
const gchar *role;
|
||
|
|
GList *uri, *uris; // list of uri strings
|
||
|
|
GList *windows;
|
||
|
|
GList *lp;
|
||
|
|
FILE *fp;
|
||
|
|
gint active_page;
|
||
|
|
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_SESSION_CLIENT (session_client));
|
||
|
|
_thunar_return_if_fail (session_client->connection == connection);
|
||
|
|
|
||
|
|
/* check if we should save our current state */
|
||
|
|
if (save_type == SmSaveLocal || save_type == SmSaveBoth)
|
||
|
|
{
|
||
|
|
/* save the active windows */
|
||
|
|
application = thunar_application_get ();
|
||
|
|
windows = thunar_application_get_windows (application);
|
||
|
|
|
||
|
|
if (windows != NULL)
|
||
|
|
{
|
||
|
|
/* try to open the session file for writing */
|
||
|
|
fp = fopen (session_client->path, "w");
|
||
|
|
if (G_LIKELY (fp != NULL))
|
||
|
|
{
|
||
|
|
for (lp = windows; lp != NULL; lp = lp->next)
|
||
|
|
{
|
||
|
|
/* determine the role for the window */
|
||
|
|
role = gtk_window_get_role (lp->data);
|
||
|
|
if (G_UNLIKELY (role == NULL))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
/* determine the directories for the window */
|
||
|
|
uris = thunar_window_get_directories (lp->data, &active_page);
|
||
|
|
if (G_UNLIKELY (uris == NULL))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
/* save the window */
|
||
|
|
fprintf (fp, "[%s]\n", role);
|
||
|
|
fprintf (fp, "PAGE=%d\n", active_page);
|
||
|
|
fprintf (fp, "URI=");
|
||
|
|
for (uri = uris; uri != NULL; uri = uri->next)
|
||
|
|
{
|
||
|
|
gchar *uri_string = uri->data;
|
||
|
|
fprintf (fp, "%s", uri_string);
|
||
|
|
if (G_LIKELY (uri->next != NULL))
|
||
|
|
fprintf (fp, ";");
|
||
|
|
}
|
||
|
|
fprintf (fp, "\n\n");
|
||
|
|
|
||
|
|
/* cleanup */
|
||
|
|
g_list_free_full (uris, g_free);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* cleanup */
|
||
|
|
fclose (fp);
|
||
|
|
}
|
||
|
|
|
||
|
|
g_list_free (windows);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* no windows, remove the file */
|
||
|
|
g_unlink (session_client->path);
|
||
|
|
}
|
||
|
|
|
||
|
|
g_object_unref (G_OBJECT (application));
|
||
|
|
}
|
||
|
|
|
||
|
|
/* tell the session manager that we're done */
|
||
|
|
SmcSaveYourselfDone (connection, True);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_session_client_shutdown_cancelled (SmcConn connection,
|
||
|
|
ThunarSessionClient *session_client)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_SESSION_CLIENT (session_client));
|
||
|
|
_thunar_return_if_fail (session_client->connection == connection);
|
||
|
|
}
|
||
|
|
#endif /* !HAVE_LIBSM */
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_session_client_new:
|
||
|
|
* @session_id : the previous session id, or %NULL.
|
||
|
|
*
|
||
|
|
* Allocates a new #ThunarSessionClient with the specified
|
||
|
|
* @session_id. If @session_id is %NULL the session manager
|
||
|
|
* will assign a new session id, otherwise the session for
|
||
|
|
* the specified id will be restored.
|
||
|
|
*
|
||
|
|
* Return value: the newly allocated #ThunarSessionClient.
|
||
|
|
**/
|
||
|
|
ThunarSessionClient *
|
||
|
|
thunar_session_client_new (const gchar *session_id)
|
||
|
|
{
|
||
|
|
ThunarSessionClient *session_client;
|
||
|
|
|
||
|
|
/* allocate a new session client */
|
||
|
|
session_client = g_object_new (THUNAR_TYPE_SESSION_CLIENT, NULL);
|
||
|
|
|
||
|
|
#ifdef HAVE_LIBSM
|
||
|
|
/* try to connect to the session manager and restore the previous session */
|
||
|
|
if (thunar_session_client_connect (session_client, session_id) && session_id != NULL)
|
||
|
|
thunar_session_client_restore (session_client);
|
||
|
|
#endif /* !HAVE_LIBSM */
|
||
|
|
|
||
|
|
return session_client;
|
||
|
|
}
|