1 /* Pidgin is the legal property of its developers, whose names are too numerous
2 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "eventloop.h"
25 #include "gtksession.h"
29 #include <X11/ICE/ICElib.h>
30 #include <X11/SM/SMlib.h>
37 #include "gtk3compat.h"
39 #define ERROR_LENGTH 512
41 static IceIOErrorHandler ice_installed_io_error_handler
;
42 static SmcConn session
= NULL
;
43 static gchar
*myself
= NULL
;
44 static gboolean had_first_save
= FALSE
;
45 static gboolean session_managed
= FALSE
;
47 /* ICE belt'n'braces stuff */
49 struct ice_connection_info
{
54 static void ice_process_messages(gpointer data
, gint fd
,
55 PurpleInputCondition condition
) {
56 struct ice_connection_info
*conninfo
= (struct ice_connection_info
*) data
;
57 IceProcessMessagesStatus status
;
59 /* please don't block... please! */
60 status
= IceProcessMessages(conninfo
->connection
, NULL
, NULL
);
62 if (status
== IceProcessMessagesIOError
) {
63 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
64 "ICE IO error, closing connection... ");
66 /* IO error, please disconnect */
67 IceSetShutdownNegotiation(conninfo
->connection
, False
);
68 IceCloseConnection(conninfo
->connection
);
70 if (purple_debug_is_verbose()) {
71 purple_debug_misc("Session Management",
72 "Connection closed.");
75 /* cancel the handler */
76 purple_input_remove(conninfo
->input_id
);
80 static void ice_connection_watch(IceConn connection
, IcePointer client_data
,
81 Bool opening
, IcePointer
*watch_data
) {
82 struct ice_connection_info
*conninfo
= NULL
;
85 purple_debug_misc("Session Management",
86 "Handling new ICE connection...");
88 /* ensure ICE connection is not passed to child processes */
89 if (fcntl(IceConnectionNumber(connection
), F_SETFD
, FD_CLOEXEC
) != 0)
90 purple_debug_warning("gtksession", "couldn't set FD_CLOEXEC\n");
92 conninfo
= g_new(struct ice_connection_info
, 1);
93 conninfo
->connection
= connection
;
95 /* watch the connection */
96 conninfo
->input_id
= purple_input_add(IceConnectionNumber(connection
), PURPLE_INPUT_READ
,
97 ice_process_messages
, conninfo
);
98 *watch_data
= conninfo
;
100 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
101 "Handling closed ICE connection... \n");
103 /* get the input ID back and stop watching it */
104 conninfo
= (struct ice_connection_info
*) *watch_data
;
105 purple_input_remove(conninfo
->input_id
);
109 if (purple_debug_is_verbose()) {
110 purple_debug_misc("Session Management",
111 "ICE connection handled.");
115 /* We call any handler installed before (or after) ice_init but
116 * avoid calling the default libICE handler which does an exit().
118 * This means we do nothing by default, which is probably correct,
119 * the connection will get closed by libICE
122 static void ice_io_error_handler(IceConn connection
) {
123 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
124 "Handling ICE IO error... ");
126 if (ice_installed_io_error_handler
)
127 (*ice_installed_io_error_handler
)(connection
);
129 if (purple_debug_is_verbose()) {
130 purple_debug_misc("Session Management",
131 "ICE IO error handled.");
135 static void ice_init(void) {
136 IceIOErrorHandler default_handler
;
138 ice_installed_io_error_handler
= IceSetIOErrorHandler(NULL
);
139 default_handler
= IceSetIOErrorHandler(ice_io_error_handler
);
141 if (ice_installed_io_error_handler
== default_handler
)
142 ice_installed_io_error_handler
= NULL
;
144 IceAddConnectionWatch(ice_connection_watch
, NULL
);
146 if (purple_debug_is_verbose()) {
147 purple_debug_misc("Session Management", "ICE initialized.");
151 /* my magic utility function */
153 static gchar
**session_make_command(gchar
*client_id
, gchar
*config_dir
) {
158 if (client_id
) i
+= 2;
159 if (config_dir
) i
+= 2; /* we will specify purple's user dir */
161 ret
= g_new(gchar
*, i
);
162 ret
[j
++] = g_strdup(myself
);
165 ret
[j
++] = g_strdup("--session");
166 ret
[j
++] = g_strdup(client_id
);
170 ret
[j
++] = g_strdup("--config");
171 ret
[j
++] = g_strdup(config_dir
);
174 ret
[j
++] = g_strdup("--display");
175 ret
[j
++] = g_strdup((gchar
*)gdk_display_get_name(gdk_display_get_default()));
182 /* SM callback handlers */
184 static void session_save_yourself(SmcConn conn
, SmPointer data
, int save_type
,
185 Bool shutdown
, int interact_style
, Bool fast
) {
186 if (had_first_save
== FALSE
&& save_type
== SmSaveLocal
&&
187 interact_style
== SmInteractStyleNone
&& !shutdown
&&
189 /* this is just a dry run, spit it back */
190 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
191 "Received first save_yourself\n");
192 SmcSaveYourselfDone(conn
, True
);
193 had_first_save
= TRUE
;
197 /* tum ti tum... don't add anything else here without *
198 * reading SMlib.PS from an X.org ftp server near you */
200 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
201 "Received save_yourself\n");
203 if (save_type
== SmSaveGlobal
|| save_type
== SmSaveBoth
) {
204 /* may as well do something ... */
205 /* or not -- save_prefs(); */
208 SmcSaveYourselfDone(conn
, True
);
211 static void session_die(SmcConn conn
, SmPointer data
) {
212 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
217 static void session_save_complete(SmcConn conn
, SmPointer data
) {
218 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
219 "Received save_complete\n");
222 static void session_shutdown_cancelled(SmcConn conn
, SmPointer data
) {
223 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
224 "Received shutdown_cancelled\n");
227 /* utility functions stolen from Gnome-client */
229 static void session_set_value(SmcConn conn
, gchar
*name
, char *type
,
230 int num_vals
, SmPropValue
*vals
) {
234 g_return_if_fail(conn
);
238 prop
.num_vals
= num_vals
;
242 SmcSetProperties(conn
, 1, proplist
);
245 static void session_set_string(SmcConn conn
, gchar
*name
, gchar
*value
) {
248 g_return_if_fail(name
);
250 val
.length
= strlen (value
)+1;
253 session_set_value(conn
, name
, SmARRAY8
, 1, &val
);
256 static void session_set_gchar(SmcConn conn
, gchar
*name
, gchar value
) {
259 g_return_if_fail(name
);
264 session_set_value(conn
, name
, SmCARD8
, 1, &val
);
267 static void session_set_array(SmcConn conn
, gchar
*name
, gchar
*array
[]) {
274 g_return_if_fail (name
);
276 /* We count the number of elements in our array. */
277 for (ptr
= array
, argc
= 0; *ptr
; ptr
++, argc
++) /* LOOP */;
279 /* Now initialize the 'vals' array. */
280 vals
= g_new (SmPropValue
, argc
);
281 for (ptr
= array
, i
= 0 ; i
< argc
; ptr
++, i
++) {
282 vals
[i
].length
= strlen (*ptr
);
283 vals
[i
].value
= *ptr
;
286 session_set_value(conn
, name
, SmLISTofARRAY8
, argc
, vals
);
293 /* setup functions */
296 pidgin_session_init(gchar
*argv0
, gchar
*previous_id
, gchar
*config_dir
)
299 SmcCallbacks callbacks
;
300 gchar
*client_id
= NULL
;
301 gchar error
[ERROR_LENGTH
] = "";
305 if (session
!= NULL
) {
306 /* session is already established, what the hell is going on? */
307 purple_debug(PURPLE_DEBUG_WARNING
, "Session Management",
308 "Duplicated call to pidgin_session_init!\n");
312 if (g_getenv("SESSION_MANAGER") == NULL
) {
313 purple_debug(PURPLE_DEBUG_ERROR
, "Session Management",
314 "No SESSION_MANAGER found, aborting.\n");
320 callbacks
.save_yourself
.callback
= session_save_yourself
;
321 callbacks
.die
.callback
= session_die
;
322 callbacks
.save_complete
.callback
= session_save_complete
;
323 callbacks
.shutdown_cancelled
.callback
= session_shutdown_cancelled
;
325 callbacks
.save_yourself
.client_data
= NULL
;
326 callbacks
.die
.client_data
= NULL
;
327 callbacks
.save_complete
.client_data
= NULL
;
328 callbacks
.shutdown_cancelled
.client_data
= NULL
;
331 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
332 "Connecting with previous ID %s\n", previous_id
);
334 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
335 "Connecting with no previous ID\n");
338 session
= SmcOpenConnection(NULL
, "session", SmProtoMajor
, SmProtoMinor
, SmcSaveYourselfProcMask
|
339 SmcDieProcMask
| SmcSaveCompleteProcMask
| SmcShutdownCancelledProcMask
,
340 &callbacks
, previous_id
, &client_id
, ERROR_LENGTH
, error
);
342 if (session
== NULL
) {
343 if (error
[0] != '\0') {
344 purple_debug(PURPLE_DEBUG_ERROR
, "Session Management",
345 "Connection failed with error: %s\n", error
);
347 purple_debug(PURPLE_DEBUG_ERROR
, "Session Management",
348 "Connetion failed with unknown error.\n");
353 tmp
= SmcVendor(session
);
354 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
355 "Connected to manager (%s) with client ID %s\n",
359 session_managed
= TRUE
;
360 gdk_x11_set_sm_client_id(client_id
);
362 tmp
= g_get_current_dir();
363 session_set_string(session
, SmCurrentDirectory
, tmp
);
366 tmp
= g_strdup_printf("%d", (int) getpid());
367 session_set_string(session
, SmProcessID
, tmp
);
370 tmp
= g_strdup(g_get_user_name());
371 session_set_string(session
, SmUserID
, tmp
);
374 session_set_gchar(session
, SmRestartStyleHint
, (gchar
) SmRestartIfRunning
);
375 session_set_string(session
, SmProgram
, (gchar
*) g_get_prgname());
377 myself
= g_strdup(argv0
);
378 purple_debug(PURPLE_DEBUG_MISC
, "Session Management",
379 "Using %s as command\n", myself
);
381 cmd
= session_make_command(NULL
, config_dir
);
382 session_set_array(session
, SmCloneCommand
, cmd
);
385 /* this is currently useless, but gnome-session warns 'the following applications will not
386 save their current status' bla bla if we don't have it and the user checks 'Save Session'
388 cmd
= g_new(gchar
*, 2);
389 cmd
[0] = g_strdup("/bin/true");
391 session_set_array(session
, SmDiscardCommand
, cmd
);
394 cmd
= session_make_command(client_id
, config_dir
);
395 session_set_array(session
, SmRestartCommand
, cmd
);
406 if (session
== NULL
) /* no session to close */
409 SmcCloseConnection(session
, 0, NULL
);
411 purple_debug(PURPLE_DEBUG_INFO
, "Session Management",
412 "Connection closed.\n");