Mercurial > pidgin
annotate src/gtksession.c @ 12553:9d7fb0b21d9f
[gaim-migrate @ 14871]
one reactionary pref down, eleventy billion to go
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Mon, 19 Dec 2005 05:55:30 +0000 |
parents | bc249de5ea02 |
children |
rev | line source |
---|---|
12246 | 1 /* |
2 * @file gtksession.c X Windows session management API | |
3 * @ingroup gtkui | |
4 * | |
5 * Gaim is the legal property of its developers, whose names are too numerous | |
6 * to list here. Please refer to the COPYRIGHT file distributed with this | |
7 * source distribution. | |
8 * | |
9 * This program is free software; you can redistribute it and/or modify | |
10 * it under the terms of the GNU General Public License as published by | |
11 * the Free Software Foundation; either version 2 of the License, or | |
12 * (at your option) any later version. | |
13 * | |
14 * This program is distributed in the hope that it will be useful, | |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 * GNU General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU General Public License | |
20 * along with this program; if not, write to the Free Software | |
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 * | |
23 */ | |
24 #include "internal.h" | |
25 | |
26 #include "core.h" | |
27 #include "debug.h" | |
28 #include "eventloop.h" | |
12410
bc249de5ea02
[gaim-migrate @ 14717]
Richard Laager <rlaager@wiktel.com>
parents:
12246
diff
changeset
|
29 #include "gtksession.h" |
12246 | 30 |
31 #ifdef USE_SM | |
32 | |
33 #include <X11/ICE/ICElib.h> | |
34 #include <X11/SM/SMlib.h> | |
35 #include <gdk/gdkx.h> | |
36 #include <unistd.h> | |
37 #include <fcntl.h> | |
38 | |
39 #define ERROR_LENGTH 512 | |
40 | |
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; | |
46 | |
47 /* ICE belt'n'braces stuff */ | |
48 | |
49 struct ice_connection_info { | |
50 IceConn connection; | |
51 guint input_id; | |
52 }; | |
53 | |
54 static void ice_process_messages(gpointer data, gint fd, | |
55 GaimInputCondition condition) { | |
56 struct ice_connection_info *conninfo = (struct ice_connection_info*) data; | |
57 IceProcessMessagesStatus status; | |
58 | |
59 /* please don't block... please! */ | |
60 status = IceProcessMessages(conninfo->connection, NULL, NULL); | |
61 | |
62 if (status == IceProcessMessagesIOError) { | |
63 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
64 "ICE IO error, closing connection... "); | |
65 | |
66 /* IO error, please disconnect */ | |
67 IceSetShutdownNegotiation(conninfo->connection, False); | |
68 IceCloseConnection(conninfo->connection); | |
69 | |
70 gaim_debug(GAIM_DEBUG_INFO, NULL, "done.\n"); | |
71 | |
72 /* cancel the handler */ | |
73 gaim_input_remove(conninfo->input_id); | |
74 } | |
75 } | |
76 | |
77 static void ice_connection_watch(IceConn connection, IcePointer client_data, | |
78 Bool opening, IcePointer *watch_data) { | |
79 struct ice_connection_info *conninfo = NULL; | |
80 | |
81 if (opening) { | |
82 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
83 "Handling new ICE connection... "); | |
84 | |
85 /* ensure ICE connection is not passed to child processes */ | |
86 fcntl(IceConnectionNumber(connection), F_SETFD, FD_CLOEXEC); | |
87 | |
88 conninfo = g_new(struct ice_connection_info, 1); | |
89 conninfo->connection = connection; | |
90 | |
91 /* watch the connection */ | |
92 conninfo->input_id = gaim_input_add(IceConnectionNumber(connection), GAIM_INPUT_READ, | |
93 ice_process_messages, conninfo); | |
94 *watch_data = conninfo; | |
95 } else { | |
96 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
97 "Handling closed ICE connection... "); | |
98 | |
99 /* get the input ID back and stop watching it */ | |
100 conninfo = (struct ice_connection_info*) *watch_data; | |
101 gaim_input_remove(conninfo->input_id); | |
102 g_free(conninfo); | |
103 } | |
104 | |
105 gaim_debug(GAIM_DEBUG_INFO, NULL, "done.\n"); | |
106 } | |
107 | |
108 /* We call any handler installed before (or after) ice_init but | |
109 * avoid calling the default libICE handler which does an exit(). | |
110 * | |
111 * This means we do nothing by default, which is probably correct, | |
112 * the connection will get closed by libICE | |
113 */ | |
114 | |
115 static void ice_io_error_handler(IceConn connection) { | |
116 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
117 "Handling ICE IO error... "); | |
118 | |
119 if (ice_installed_io_error_handler) | |
120 (*ice_installed_io_error_handler)(connection); | |
121 | |
122 gaim_debug(GAIM_DEBUG_INFO, NULL, "done.\n"); | |
123 } | |
124 | |
125 static void ice_init() { | |
126 IceIOErrorHandler default_handler; | |
127 | |
128 ice_installed_io_error_handler = IceSetIOErrorHandler(NULL); | |
129 default_handler = IceSetIOErrorHandler(ice_io_error_handler); | |
130 | |
131 if (ice_installed_io_error_handler == default_handler) | |
132 ice_installed_io_error_handler = NULL; | |
133 | |
134 IceAddConnectionWatch(ice_connection_watch, NULL); | |
135 | |
136 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
137 "ICE initialized.\n"); | |
138 } | |
139 | |
140 /* my magic utility function */ | |
141 | |
142 static gchar **session_make_command(gchar *client_id, gchar *config_dir) { | |
143 gint i = 2; | |
144 gint j = 0; | |
145 gchar **ret; | |
146 | |
147 if (client_id) i += 2; | |
148 if (config_dir) i += 2; /* we will specify gaim's user dir */ | |
149 | |
150 ret = g_new(gchar *, i); | |
151 ret[j++] = g_strdup(myself); | |
152 | |
153 if (client_id) { | |
154 ret[j++] = g_strdup("--session"); | |
155 ret[j++] = g_strdup(client_id); | |
156 } | |
157 | |
158 if (config_dir) { | |
159 ret[j++] = g_strdup("--config"); | |
160 ret[j++] = g_strdup(config_dir); | |
161 } | |
162 | |
163 ret[j++] = NULL; | |
164 | |
165 return ret; | |
166 } | |
167 | |
168 /* SM callback handlers */ | |
169 | |
170 static void session_save_yourself(SmcConn conn, SmPointer data, int save_type, | |
171 Bool shutdown, int interact_style, Bool fast) { | |
172 if (had_first_save == FALSE && save_type == SmSaveLocal && | |
173 interact_style == SmInteractStyleNone && !shutdown && | |
174 !fast) { | |
175 /* this is just a dry run, spit it back */ | |
176 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
177 "Received first save_yourself\n"); | |
178 SmcSaveYourselfDone(conn, True); | |
179 had_first_save = TRUE; | |
180 return; | |
181 } | |
182 | |
183 /* tum ti tum... don't add anything else here without * | |
184 * reading SMlib.PS from an X.org ftp server near you */ | |
185 | |
186 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
187 "Received save_yourself\n"); | |
188 | |
189 if (save_type == SmSaveGlobal || save_type == SmSaveBoth) { | |
190 /* may as well do something ... */ | |
191 /* or not -- save_prefs(); */ | |
192 } | |
193 | |
194 SmcSaveYourselfDone(conn, True); | |
195 } | |
196 | |
197 static void session_die(SmcConn conn, SmPointer data) { | |
198 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
199 "Received die\n"); | |
200 gaim_core_quit(); | |
201 } | |
202 | |
203 static void session_save_complete(SmcConn conn, SmPointer data) { | |
204 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
205 "Received save_complete\n"); | |
206 } | |
207 | |
208 static void session_shutdown_cancelled(SmcConn conn, SmPointer data) { | |
209 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
210 "Received shutdown_cancelled\n"); | |
211 } | |
212 | |
213 /* utility functions stolen from Gnome-client */ | |
214 | |
215 static void session_set_value(SmcConn conn, gchar *name, char *type, | |
216 int num_vals, SmPropValue *vals) { | |
217 SmProp *proplist[1]; | |
218 SmProp prop; | |
219 | |
220 g_return_if_fail(conn); | |
221 | |
222 prop.name = name; | |
223 prop.type = type; | |
224 prop.num_vals = num_vals; | |
225 prop.vals = vals; | |
226 | |
227 proplist[0] = ∝ | |
228 SmcSetProperties(conn, 1, proplist); | |
229 } | |
230 | |
231 static void session_set_string(SmcConn conn, gchar *name, gchar *value) { | |
232 SmPropValue val; | |
233 | |
234 g_return_if_fail(name); | |
235 | |
236 val.length = strlen (value)+1; | |
237 val.value = value; | |
238 | |
239 session_set_value(conn, name, SmARRAY8, 1, &val); | |
240 } | |
241 | |
242 static void session_set_gchar(SmcConn conn, gchar *name, gchar value) { | |
243 SmPropValue val; | |
244 | |
245 g_return_if_fail(name); | |
246 | |
247 val.length = 1; | |
248 val.value = &value; | |
249 | |
250 session_set_value(conn, name, SmCARD8, 1, &val); | |
251 } | |
252 | |
253 static void session_set_array(SmcConn conn, gchar *name, gchar *array[]) { | |
254 gint argc; | |
255 gchar **ptr; | |
256 gint i; | |
257 | |
258 SmPropValue *vals; | |
259 | |
260 g_return_if_fail (name); | |
261 | |
262 /* We count the number of elements in our array. */ | |
263 for (ptr = array, argc = 0; *ptr ; ptr++, argc++) /* LOOP */; | |
264 | |
265 /* Now initialize the 'vals' array. */ | |
266 vals = g_new (SmPropValue, argc); | |
267 for (ptr = array, i = 0 ; i < argc ; ptr++, i++) { | |
268 vals[i].length = strlen (*ptr); | |
269 vals[i].value = *ptr; | |
270 } | |
271 | |
272 session_set_value(conn, name, SmLISTofARRAY8, argc, vals); | |
273 | |
274 g_free (vals); | |
275 } | |
276 | |
277 #endif /* USE_SM */ | |
278 | |
279 /* setup functions */ | |
280 | |
281 void | |
282 gaim_gtk_session_init(gchar *argv0, gchar *previous_id, gchar *config_dir) | |
283 { | |
284 #ifdef USE_SM | |
285 SmcCallbacks callbacks; | |
286 gchar *client_id = NULL; | |
287 gchar error[ERROR_LENGTH] = ""; | |
288 gchar *tmp = NULL; | |
289 gchar **cmd = NULL; | |
290 | |
291 if (session != NULL) { | |
292 /* session is already established, what the hell is going on? */ | |
293 gaim_debug(GAIM_DEBUG_WARNING, "Session Management", | |
294 "Duplicated call to gaim_gtk_session_init!\n"); | |
295 return; | |
296 } | |
297 | |
298 if (g_getenv("SESSION_MANAGER") == NULL) { | |
299 gaim_debug(GAIM_DEBUG_ERROR, "Session Management", | |
300 "No SESSION_MANAGER found, aborting.\n"); | |
301 return; | |
302 } | |
303 | |
304 ice_init(); | |
305 | |
306 callbacks.save_yourself.callback = session_save_yourself; | |
307 callbacks.die.callback = session_die; | |
308 callbacks.save_complete.callback = session_save_complete; | |
309 callbacks.shutdown_cancelled.callback = session_shutdown_cancelled; | |
310 | |
311 callbacks.save_yourself.client_data = NULL; | |
312 callbacks.die.client_data = NULL; | |
313 callbacks.save_complete.client_data = NULL; | |
314 callbacks.shutdown_cancelled.client_data = NULL; | |
315 | |
316 if (previous_id) { | |
317 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
318 "Connecting with previous ID %s\n", previous_id); | |
319 } else { | |
320 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
321 "Connecting with no previous ID\n"); | |
322 } | |
323 | |
324 session = SmcOpenConnection(NULL, "session", SmProtoMajor, SmProtoMinor, SmcSaveYourselfProcMask | | |
325 SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, | |
326 &callbacks, previous_id, &client_id, ERROR_LENGTH, error); | |
327 | |
328 if (session == NULL) { | |
329 if (error[0] != '\0') { | |
330 gaim_debug(GAIM_DEBUG_ERROR, "Session Management", | |
331 "Connection failed with error: %s\n", error); | |
332 } else { | |
333 gaim_debug(GAIM_DEBUG_ERROR, "Session Management", | |
334 "Connetion failed with unknown error.\n"); | |
335 } | |
336 return; | |
337 } | |
338 | |
339 tmp = SmcVendor(session); | |
340 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
341 "Connected to manager (%s) with client ID %s\n", | |
342 tmp, client_id); | |
343 g_free(tmp); | |
344 | |
345 session_managed = TRUE; | |
346 gdk_set_sm_client_id(client_id); | |
347 | |
348 tmp = g_get_current_dir(); | |
349 session_set_string(session, SmCurrentDirectory, tmp); | |
350 g_free(tmp); | |
351 | |
352 tmp = g_strdup_printf("%d", (int) getpid()); | |
353 session_set_string(session, SmProcessID, tmp); | |
354 g_free(tmp); | |
355 | |
356 tmp = g_strdup(g_get_user_name()); | |
357 session_set_string(session, SmUserID, tmp); | |
358 g_free(tmp); | |
359 | |
360 session_set_gchar(session, SmRestartStyleHint, (gchar) SmRestartIfRunning); | |
361 session_set_string(session, SmProgram, g_get_prgname()); | |
362 | |
363 myself = g_strdup(argv0); | |
364 gaim_debug(GAIM_DEBUG_MISC, "Session Management", | |
365 "Using %s as command\n", myself); | |
366 | |
367 cmd = session_make_command(NULL, config_dir); | |
368 session_set_array(session, SmCloneCommand, cmd); | |
369 g_strfreev(cmd); | |
370 | |
371 /* this is currently useless, but gnome-session warns 'the following applications will not | |
372 save their current status' bla bla if we don't have it and the user checks 'Save Session' | |
373 when they log out */ | |
374 cmd = g_new(gchar *, 2); | |
375 cmd[0] = g_strdup("/bin/true"); | |
376 cmd[1] = NULL; | |
377 session_set_array(session, SmDiscardCommand, cmd); | |
378 g_strfreev(cmd); | |
379 | |
380 cmd = session_make_command(client_id, config_dir); | |
381 session_set_array(session, SmRestartCommand, cmd); | |
382 g_strfreev(cmd); | |
383 | |
384 g_free(client_id); | |
385 #endif /* USE_SM */ | |
386 } | |
387 | |
388 void | |
389 gaim_gtk_session_end() | |
390 { | |
391 #ifdef USE_SM | |
392 if (session == NULL) /* no session to close */ | |
393 return; | |
394 | |
395 SmcCloseConnection(session, 0, NULL); | |
396 | |
397 gaim_debug(GAIM_DEBUG_INFO, "Session Management", | |
398 "Connection closed.\n"); | |
399 #endif /* USE_SM */ | |
400 } |