thunar-usagebars/thunar/thunar-session-client.c
4aiman e969f94310 thunar 4.20.2 upstream
Tree of tag thunar-4.20.2 (commit 11a519f) from https://gitlab.xfce.org/xfce/thunar.
Upstream history is not included here to keep the repository small.
2026-11-03 22:42:48 +03:00

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