add a title to the session logout confirm prompt
[mikachu/openbox.git] / openbox / session.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    session.c for the Openbox window manager
4    Copyright (c) 2003-2007   Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 /* This session code is largely inspired by metacity code. */
20
21 #include "session.h"
22
23 struct _ObClient;
24
25 GList *session_saved_state = NULL;
26 gint session_desktop = -1;
27 gint session_num_desktops = 0;
28 gboolean session_desktop_layout_present = FALSE;
29 ObDesktopLayout session_desktop_layout;
30 GSList *session_desktop_names = NULL;
31
32 #ifndef USE_SM
33 void session_startup(gint argc, gchar **argv) {}
34 void session_shutdown(gboolean permanent) {}
35 GList* session_state_find(struct _ObClient *c) { return NULL; }
36 void session_request_logout(gboolean silent) {}
37 #else
38
39 #include "debug.h"
40 #include "openbox.h"
41 #include "client.h"
42 #include "prop.h"
43 #include "focus.h"
44 #include "gettext.h"
45 #include "parser/parse.h"
46
47 #include <time.h>
48 #include <errno.h>
49 #include <stdio.h>
50
51 #ifdef HAVE_UNISTD_H
52 #  include <sys/types.h>
53 #  include <unistd.h>
54 #endif
55
56 #include <X11/SM/SMlib.h>
57
58 #define SM_ERR_LEN 1024
59
60 static SmcConn  sm_conn;
61 static gint     sm_argc;
62 static gchar  **sm_argv;
63
64 /* Data saved from the first level save yourself */
65 typedef struct {
66     ObClient *focus_client;
67     gint      desktop;
68 } ObSMSaveData;
69
70 static gboolean session_connect();
71
72 static void session_load_file(const gchar *path);
73 static gboolean session_save_to_file(const ObSMSaveData *savedata);
74
75 static void session_setup_program();
76 static void session_setup_user();
77 static void session_setup_restart_style(gboolean restart);
78 static void session_setup_pid();
79 static void session_setup_priority();
80 static void session_setup_clone_command();
81 static void session_setup_restart_command();
82
83 static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type,
84                              Bool shutdown, gint interact_style, Bool fast);
85 static void sm_die(SmcConn conn, SmPointer data);
86 static void sm_save_complete(SmcConn conn, SmPointer data);
87 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data);
88
89 static gboolean session_state_cmp(ObSessionState *s, ObClient *c);
90 static void session_state_free(ObSessionState *state);
91
92 void session_startup(gint argc, gchar **argv)
93 {
94     gchar *dir;
95
96     if (!ob_sm_use) return;
97
98     sm_argc = argc;
99     sm_argv = argv;
100
101     dir = g_build_filename(parse_xdg_data_home_path(),
102                            "openbox", "sessions", NULL);
103     if (!parse_mkdir_path(dir, 0700)) {
104         g_message(_("Unable to make directory \"%s\": %s"),
105                   dir, g_strerror(errno));
106     }
107
108     if (ob_sm_save_file != NULL) {
109         if (ob_sm_restore) {
110             ob_debug_type(OB_DEBUG_SM, "Loading from session file %s\n",
111                           ob_sm_save_file);
112             session_load_file(ob_sm_save_file);
113         }
114     } else {
115         gchar *filename;
116
117         /* this algo is from metacity */
118         filename = g_strdup_printf("%u-%u-%u.obs",
119                                    (guint)time(NULL),
120                                    (guint)getpid(),
121                                    g_random_int());
122         ob_sm_save_file = g_build_filename(dir, filename, NULL);
123         g_free(filename);
124     }
125
126     if (session_connect()) {
127         session_setup_program();
128         session_setup_user();
129         session_setup_restart_style(TRUE);
130         session_setup_pid();
131         session_setup_priority();
132         session_setup_clone_command();
133     }
134
135     g_free(dir);
136 }
137
138 void session_shutdown(gboolean permanent)
139 {
140     if (!ob_sm_use) return;
141
142     if (sm_conn) {
143         /* if permanent is true then we will change our session state so that
144            the SM won't run us again */
145         if (permanent)
146             session_setup_restart_style(FALSE);
147
148         SmcCloseConnection(sm_conn, 0, NULL);
149
150         while (session_saved_state) {
151             session_state_free(session_saved_state->data);
152             session_saved_state = g_list_delete_link(session_saved_state,
153                                                      session_saved_state);
154         }
155     }
156 }
157
158 /*! Connect to the session manager and set up our callback functions */
159 static gboolean session_connect()
160 {
161     SmcCallbacks cb;
162     gchar *oldid;
163     gchar sm_err[SM_ERR_LEN];
164
165     /* set up our callback functions */
166     cb.save_yourself.callback = sm_save_yourself;
167     cb.save_yourself.client_data = NULL;
168     cb.die.callback = sm_die;
169     cb.die.client_data = NULL;
170     cb.save_complete.callback = sm_save_complete;
171     cb.save_complete.client_data = NULL;
172     cb.shutdown_cancelled.callback = sm_shutdown_cancelled;
173     cb.shutdown_cancelled.client_data = NULL;
174
175     /* connect to the server */
176     oldid = ob_sm_id;
177     ob_debug_type(OB_DEBUG_SM, "Connecting to SM with id: %s\n",
178                   oldid ? oldid : "(null)");
179     sm_conn = SmcOpenConnection(NULL, NULL, 1, 0,
180                                 SmcSaveYourselfProcMask |
181                                 SmcDieProcMask |
182                                 SmcSaveCompleteProcMask |
183                                 SmcShutdownCancelledProcMask,
184                                 &cb, oldid, &ob_sm_id,
185                                 SM_ERR_LEN-1, sm_err);
186     g_free(oldid);
187     ob_debug_type(OB_DEBUG_SM, "Connected to SM with id: %s\n", ob_sm_id);
188     if (sm_conn == NULL)
189         ob_debug("Failed to connect to session manager: %s\n", sm_err);
190     return sm_conn != NULL;
191 }
192
193 static void session_setup_program()
194 {
195     SmPropValue vals = {
196         .value = sm_argv[0],
197         .length = strlen(sm_argv[0]) + 1
198     };
199     SmProp prop = {
200         .name = g_strdup(SmProgram),
201         .type = g_strdup(SmARRAY8),
202         .num_vals = 1,
203         .vals = &vals
204     };
205     SmProp *list = &prop;
206     ob_debug_type(OB_DEBUG_SM, "Setting program: %s\n", sm_argv[0]);
207     SmcSetProperties(sm_conn, 1, &list);
208     g_free(prop.name);
209     g_free(prop.type);
210 }
211
212 static void session_setup_user()
213 {
214     char *user = g_strdup(g_get_user_name());
215
216     SmPropValue vals = {
217         .value = user,
218         .length = strlen(user) + 1
219     };
220     SmProp prop = {
221         .name = g_strdup(SmUserID),
222         .type = g_strdup(SmARRAY8),
223         .num_vals = 1,
224         .vals = &vals
225     };
226     SmProp *list = &prop;
227     ob_debug_type(OB_DEBUG_SM, "Setting user: %s\n", user);
228     SmcSetProperties(sm_conn, 1, &list);
229     g_free(prop.name);
230     g_free(prop.type);
231     g_free(user);
232 }
233
234 static void session_setup_restart_style(gboolean restart)
235 {
236     gchar restart_hint = restart ? SmRestartImmediately : SmRestartIfRunning;
237
238     SmPropValue vals = {
239         .value = &restart_hint,
240         .length = 1
241     };
242     SmProp prop = {
243         .name = g_strdup(SmRestartStyleHint),
244         .type = g_strdup(SmCARD8),
245         .num_vals = 1,
246         .vals = &vals
247     };
248     SmProp *list = &prop;
249     ob_debug_type(OB_DEBUG_SM, "Setting restart: %d\n", restart);
250     SmcSetProperties(sm_conn, 1, &list);
251     g_free(prop.name);
252     g_free(prop.type);
253 }
254
255 static void session_setup_pid()
256 {
257     gchar *pid = g_strdup_printf("%ld", (glong) getpid());
258
259     SmPropValue vals = {
260         .value = pid,
261         .length = strlen(pid) + 1
262     };
263     SmProp prop = {
264         .name = g_strdup(SmProcessID),
265         .type = g_strdup(SmARRAY8),
266         .num_vals = 1,
267         .vals = &vals
268     };
269     SmProp *list = &prop;
270     ob_debug_type(OB_DEBUG_SM, "Setting pid: %s\n", pid);
271     SmcSetProperties(sm_conn, 1, &list);
272     g_free(prop.name);
273     g_free(prop.type);
274     g_free(pid);
275 }
276
277 /*! This is a gnome-session-manager extension */
278 static void session_setup_priority()
279 {
280     gchar priority = 20; /* 20 is a lower prioity to run before other apps */
281
282     SmPropValue vals = {
283         .value = &priority,
284         .length = 1
285     };
286     SmProp prop = {
287         .name = g_strdup("_GSM_Priority"),
288         .type = g_strdup(SmCARD8),
289         .num_vals = 1,
290         .vals = &vals
291     };
292     SmProp *list = &prop;
293     ob_debug_type(OB_DEBUG_SM, "Setting priority: %d\n", priority);
294     SmcSetProperties(sm_conn, 1, &list);
295     g_free(prop.name);
296     g_free(prop.type);
297 }
298
299 static void session_setup_clone_command()
300 {
301     gint i;
302
303     SmPropValue *vals = g_new(SmPropValue, sm_argc);
304     SmProp prop = {
305         .name = g_strdup(SmCloneCommand),
306         .type = g_strdup(SmLISTofARRAY8),
307         .num_vals = sm_argc,
308         .vals = vals
309     };
310     SmProp *list = &prop;
311
312     ob_debug_type(OB_DEBUG_SM, "Setting clone command: (%d)\n", sm_argc);
313     for (i = 0; i < sm_argc; ++i) {
314         vals[i].value = sm_argv[i];
315         vals[i].length = strlen(sm_argv[i]) + 1;
316         ob_debug_type(OB_DEBUG_SM, "    %s\n", vals[i].value);
317     }
318
319     SmcSetProperties(sm_conn, 1, &list);
320     g_free(prop.name);
321     g_free(prop.type);
322     g_free(vals);
323 }
324
325 static void session_setup_restart_command()
326 {
327     gint i;
328
329     SmPropValue *vals = g_new(SmPropValue, sm_argc + 4);
330     SmProp prop = {
331         .name = g_strdup(SmRestartCommand),
332         .type = g_strdup(SmLISTofARRAY8),
333         .num_vals = sm_argc + 4,
334         .vals = vals
335     };
336     SmProp *list = &prop;
337
338     ob_debug_type(OB_DEBUG_SM, "Setting restart command: (%d)\n", sm_argc+4);
339     for (i = 0; i < sm_argc; ++i) {
340         vals[i].value = sm_argv[i];
341         vals[i].length = strlen(sm_argv[i]) + 1;
342         ob_debug_type(OB_DEBUG_SM, "    %s\n", vals[i].value);
343     }
344
345     vals[i].value = g_strdup("--sm-client-id");
346     vals[i].length = strlen("--sm-client-id") + 1;
347     vals[i+1].value = ob_sm_id;
348     vals[i+1].length = strlen(ob_sm_id) + 1;
349     ob_debug_type(OB_DEBUG_SM, "    %s\n", vals[i].value);
350     ob_debug_type(OB_DEBUG_SM, "    %s\n", vals[i+1].value);
351
352     vals[i+2].value = g_strdup("--sm-save-file");
353     vals[i+2].length = strlen("--sm-save-file") + 1;
354     vals[i+3].value = ob_sm_save_file;
355     vals[i+3].length = strlen(ob_sm_save_file) + 1;
356     ob_debug_type(OB_DEBUG_SM, "    %s\n", vals[i+2].value);
357     ob_debug_type(OB_DEBUG_SM, "    %s\n", vals[i+3].value);
358
359     SmcSetProperties(sm_conn, 1, &list);
360     g_free(prop.name);
361     g_free(prop.type);
362     g_free(vals[i].value);
363     g_free(vals[i+2].value);
364     g_free(vals);
365 }
366
367 static ObSMSaveData *sm_save_get_data()
368 {
369     ObSMSaveData *savedata = g_new0(ObSMSaveData, 1);
370     /* save the active desktop and client.
371        we don't bother to preemptively save the other desktop state like
372        number and names of desktops, cuz those shouldn't be changing during
373        the save.. */
374     savedata->focus_client = focus_client;
375     savedata->desktop = screen_desktop;
376     return savedata;
377 }
378
379 static void sm_save_yourself_2(SmcConn conn, SmPointer data)
380 {
381     gboolean success;
382     ObSMSaveData *savedata = data;
383
384     /* save the current state */
385     ob_debug_type(OB_DEBUG_SM, "Session save phase 2 requested\n");
386     ob_debug_type(OB_DEBUG_SM,
387                   "  Saving session to file '%s'\n", ob_sm_save_file);
388     if (savedata == NULL)
389         savedata = sm_save_get_data();
390     success = session_save_to_file(savedata);
391     g_free(savedata);
392
393     /* tell the session manager how to restore this state */
394     if (success) session_setup_restart_command();
395
396     ob_debug_type(OB_DEBUG_SM, "Saving is done (success = %d)\n", success);
397     SmcSaveYourselfDone(conn, success);
398 }
399
400
401 static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type,
402                              Bool shutdown, gint interact_style, Bool fast)
403 {
404     ObSMSaveData *savedata = NULL;
405     gchar *vendor;
406
407     ob_debug_type(OB_DEBUG_SM, "Session save requested\n");
408
409     vendor = SmcVendor(sm_conn);
410     ob_debug_type(OB_DEBUG_SM, "Session manager's vendor: %s\n", vendor);
411
412     if (!strcmp(vendor, "KDE")) {
413         /* ksmserver guarantees that phase 1 will complete before allowing any
414            clients interaction, so we can save this sanely here before they
415            get messed up from interaction */
416         savedata = sm_save_get_data();
417     }
418     free(vendor);
419
420     if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, savedata)) {
421         ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed\n");
422         g_free(savedata);
423         SmcSaveYourselfDone(conn, FALSE);
424     }
425 }
426
427 static void sm_die(SmcConn conn, SmPointer data)
428 {
429     ob_debug_type(OB_DEBUG_SM, "Die requested\n");
430     ob_exit(0);
431 }
432
433 static void sm_save_complete(SmcConn conn, SmPointer data)
434 {
435     ob_debug_type(OB_DEBUG_SM, "Save complete\n");
436 }
437
438 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
439 {
440     ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled\n");
441 }
442
443 static gboolean session_save_to_file(const ObSMSaveData *savedata)
444 {
445     FILE *f;
446     GList *it;
447     gboolean success = TRUE;
448
449     f = fopen(ob_sm_save_file, "w");
450     if (!f) {
451         success = FALSE;
452         g_message(_("Unable to save the session to \"%s\": %s"),
453                   ob_sm_save_file, g_strerror(errno));
454     } else {
455         fprintf(f, "<?xml version=\"1.0\"?>\n\n");
456         fprintf(f, "<openbox_session>\n\n");
457
458         fprintf(f, "<desktop>%d</desktop>\n", savedata->desktop);
459
460         fprintf(f, "<numdesktops>%d</numdesktops>\n", screen_num_desktops);
461
462         fprintf(f, "<desktoplayout>\n");
463         fprintf(f, "  <orientation>%d</orientation>\n",
464                 screen_desktop_layout.orientation);
465         fprintf(f, "  <startcorner>%d</startcorner>\n",
466                 screen_desktop_layout.start_corner);
467         fprintf(f, "  <columns>%d</columns>\n",
468                 screen_desktop_layout.columns);
469         fprintf(f, "  <rows>%d</rows>\n",
470                 screen_desktop_layout.rows);
471         fprintf(f, "</desktoplayout>\n");
472
473         if (screen_desktop_names) {
474             gint i;
475
476             fprintf(f, "<desktopnames>\n");
477             for (i = 0; screen_desktop_names[i]; ++i)
478                 fprintf(f, "  <name>%s</name>\n", screen_desktop_names[i]);
479             fprintf(f, "</desktopnames>\n");
480         }
481
482         /* they are ordered top to bottom in stacking order */
483         for (it = stacking_list; it; it = g_list_next(it)) {
484             gint prex, prey, prew, preh;
485             ObClient *c;
486             gchar *t;
487
488             if (WINDOW_IS_CLIENT(it->data))
489                 c = WINDOW_AS_CLIENT(it->data);
490             else
491                 continue;
492
493             if (!client_normal(c))
494                 continue;
495
496             if (!c->sm_client_id) {
497                 ob_debug_type(OB_DEBUG_SM, "Client %s does not have a "
498                               "session id set\n",
499                               c->title);
500                 if (!c->wm_command) {
501                     ob_debug_type(OB_DEBUG_SM, "Client %s does not have an "
502                                   "oldskool wm_command set either. We won't "
503                                   "be saving its data\n",
504                                   c->title);
505                     continue;
506                 }
507             }
508
509             ob_debug_type(OB_DEBUG_SM, "Saving state for client %s\n",
510                           c->title);
511
512             prex = c->area.x;
513             prey = c->area.y;
514             prew = c->area.width;
515             preh = c->area.height;
516             if (c->fullscreen) {
517                 prex = c->pre_fullscreen_area.x;
518                 prey = c->pre_fullscreen_area.x;
519                 prew = c->pre_fullscreen_area.width;
520                 preh = c->pre_fullscreen_area.height;
521             }
522             if (c->max_horz) {
523                 prex = c->pre_max_area.x;
524                 prew = c->pre_max_area.width;
525             }
526             if (c->max_vert) {
527                 prey = c->pre_max_area.y;
528                 preh = c->pre_max_area.height;
529             }
530
531             if (c->sm_client_id)
532                 fprintf(f, "<window id=\"%s\">\n", c->sm_client_id);
533             else
534                 fprintf(f, "<window command=\"%s\">\n", c->wm_command);
535
536             t = g_markup_escape_text(c->name, -1);
537             fprintf(f, "\t<name>%s</name>\n", t);
538             g_free(t);
539
540             t = g_markup_escape_text(c->class, -1);
541             fprintf(f, "\t<class>%s</class>\n", t);
542             g_free(t);
543
544             t = g_markup_escape_text(c->role, -1);
545             fprintf(f, "\t<role>%s</role>\n", t);
546             g_free(t);
547
548             fprintf(f, "\t<windowtype>%d</windowtype>\n", c->type);
549
550             fprintf(f, "\t<desktop>%d</desktop>\n", c->desktop);
551             fprintf(f, "\t<x>%d</x>\n", prex);
552             fprintf(f, "\t<y>%d</y>\n", prey);
553             fprintf(f, "\t<width>%d</width>\n", prew);
554             fprintf(f, "\t<height>%d</height>\n", preh);
555             if (c->shaded)
556                 fprintf(f, "\t<shaded />\n");
557             if (c->iconic)
558                 fprintf(f, "\t<iconic />\n");
559             if (c->skip_pager)
560                 fprintf(f, "\t<skip_pager />\n");
561             if (c->skip_taskbar)
562                 fprintf(f, "\t<skip_taskbar />\n");
563             if (c->fullscreen)
564                 fprintf(f, "\t<fullscreen />\n");
565             if (c->above)
566                 fprintf(f, "\t<above />\n");
567             if (c->below)
568                 fprintf(f, "\t<below />\n");
569             if (c->max_horz)
570                 fprintf(f, "\t<max_horz />\n");
571             if (c->max_vert)
572                 fprintf(f, "\t<max_vert />\n");
573             if (c->undecorated)
574                 fprintf(f, "\t<undecorated />\n");
575             if (savedata->focus_client == c)
576                 fprintf(f, "\t<focused />\n");
577             fprintf(f, "</window>\n\n");
578         }
579
580         fprintf(f, "</openbox_session>\n");
581
582         if (fflush(f)) {
583             success = FALSE;
584             g_message(_("Error while saving the session to \"%s\": %s"),
585                       ob_sm_save_file, g_strerror(errno));
586         }
587         fclose(f);
588     }
589
590     return success;
591 }
592
593 static void session_state_free(ObSessionState *state)
594 {
595     if (state) {
596         g_free(state->id);
597         g_free(state->command);
598         g_free(state->name);
599         g_free(state->class);
600         g_free(state->role);
601
602         g_free(state);
603     }
604 }
605
606 static gboolean session_state_cmp(ObSessionState *s, ObClient *c)
607 {
608     ob_debug_type(OB_DEBUG_SM, "Comparing client against saved state: \n");
609     ob_debug_type(OB_DEBUG_SM, "  client id: %s \n", c->sm_client_id);
610     ob_debug_type(OB_DEBUG_SM, "  client name: %s \n", c->name);
611     ob_debug_type(OB_DEBUG_SM, "  client class: %s \n", c->class);
612     ob_debug_type(OB_DEBUG_SM, "  client role: %s \n", c->role);
613     ob_debug_type(OB_DEBUG_SM, "  client type: %d \n", c->type);
614     ob_debug_type(OB_DEBUG_SM, "  client command: %s \n",
615                   c->wm_command ? c->wm_command : "(null)");
616     ob_debug_type(OB_DEBUG_SM, "  state id: %s \n", s->id);
617     ob_debug_type(OB_DEBUG_SM, "  state name: %s \n", s->name);
618     ob_debug_type(OB_DEBUG_SM, "  state class: %s \n", s->class);
619     ob_debug_type(OB_DEBUG_SM, "  state role: %s \n", s->role);
620     ob_debug_type(OB_DEBUG_SM, "  state type: %d \n", s->type);
621     ob_debug_type(OB_DEBUG_SM, "  state command: %s \n",
622                   s->command ? s->command : "(null)");
623
624     if ((c->sm_client_id && s->id && !strcmp(c->sm_client_id, s->id)) ||
625         (c->wm_command && s->command && !strcmp(c->wm_command, s->command)))
626     {
627         return (!strcmp(s->name, c->name) &&
628                 !strcmp(s->class, c->class) &&
629                 !strcmp(s->role, c->role) &&
630                 /* the check for type is to catch broken clients, like
631                    firefox, which open a different window on startup
632                    with the same info as the one we saved. only do this
633                    check for old windows that dont use xsmp, others should
634                    know better ! */
635                 (!s->command || c->type == s->type));
636     }
637     return FALSE;
638 }
639
640 GList* session_state_find(ObClient *c)
641 {
642     GList *it;
643
644     for (it = session_saved_state; it; it = g_list_next(it)) {
645         ObSessionState *s = it->data;
646         if (!s->matched && session_state_cmp(s, c)) {
647             s->matched = TRUE;
648             break;
649         }
650     }
651     return it;
652 }
653
654 static void session_load_file(const gchar *path)
655 {
656     xmlDocPtr doc;
657     xmlNodePtr node, n, m;
658     GList *it, *inext;
659
660     if (!parse_load(path, "openbox_session", &doc, &node))
661         return;
662
663     if ((n = parse_find_node("desktop", node->children)))
664         session_desktop = parse_int(doc, n);
665
666     if ((n = parse_find_node("numdesktops", node->children)))
667         session_num_desktops = parse_int(doc, n);
668
669     if ((n = parse_find_node("desktoplayout", node->children))) {
670         /* make sure they are all there for it to be valid */
671         if ((m = parse_find_node("orientation", n->children)))
672             session_desktop_layout.orientation = parse_int(doc, m);
673         if (m && (m = parse_find_node("startcorner", n->children)))
674             session_desktop_layout.start_corner = parse_int(doc, m);
675         if (m && (m = parse_find_node("columns", n->children)))
676             session_desktop_layout.columns = parse_int(doc, m);
677         if (m && (m = parse_find_node("rows", n->children)))
678             session_desktop_layout.rows = parse_int(doc, m);
679         session_desktop_layout_present = m != NULL;
680     }
681
682     if ((n = parse_find_node("desktopnames", node->children))) {
683         for (m = parse_find_node("name", n->children); m;
684              m = parse_find_node("name", m->next))
685         {
686             session_desktop_names = g_slist_append(session_desktop_names,
687                                                    parse_string(doc, m));
688         }
689     }
690
691     for (node = parse_find_node("window", node->children); node != NULL;
692          node = parse_find_node("window", node->next))
693     {
694         ObSessionState *state;
695
696         state = g_new0(ObSessionState, 1);
697
698         if (!parse_attr_string("id", node, &state->id))
699             if (!parse_attr_string("command", node, &state->command))
700             goto session_load_bail;
701         if (!(n = parse_find_node("name", node->children)))
702             goto session_load_bail;
703         state->name = parse_string(doc, n);
704         if (!(n = parse_find_node("class", node->children)))
705             goto session_load_bail;
706         state->class = parse_string(doc, n);
707         if (!(n = parse_find_node("role", node->children)))
708             goto session_load_bail;
709         state->role = parse_string(doc, n);
710         if (!(n = parse_find_node("windowtype", node->children)))
711             goto session_load_bail;
712         state->type = parse_int(doc, n);
713         if (!(n = parse_find_node("desktop", node->children)))
714             goto session_load_bail;
715         state->desktop = parse_int(doc, n);
716         if (!(n = parse_find_node("x", node->children)))
717             goto session_load_bail;
718         state->x = parse_int(doc, n);
719         if (!(n = parse_find_node("y", node->children)))
720             goto session_load_bail;
721         state->y = parse_int(doc, n);
722         if (!(n = parse_find_node("width", node->children)))
723             goto session_load_bail;
724         state->w = parse_int(doc, n);
725         if (!(n = parse_find_node("height", node->children)))
726             goto session_load_bail;
727         state->h = parse_int(doc, n);
728
729         state->shaded =
730             parse_find_node("shaded", node->children) != NULL;
731         state->iconic =
732             parse_find_node("iconic", node->children) != NULL;
733         state->skip_pager =
734             parse_find_node("skip_pager", node->children) != NULL;
735         state->skip_taskbar =
736             parse_find_node("skip_taskbar", node->children) != NULL;
737         state->fullscreen =
738             parse_find_node("fullscreen", node->children) != NULL;
739         state->above =
740             parse_find_node("above", node->children) != NULL;
741         state->below =
742             parse_find_node("below", node->children) != NULL;
743         state->max_horz =
744             parse_find_node("max_horz", node->children) != NULL;
745         state->max_vert =
746             parse_find_node("max_vert", node->children) != NULL;
747         state->undecorated =
748             parse_find_node("undecorated", node->children) != NULL;
749         state->focused =
750             parse_find_node("focused", node->children) != NULL;
751
752         /* save this. they are in the file in stacking order, so preserve
753            that order here */
754         session_saved_state = g_list_append(session_saved_state, state);
755         continue;
756
757     session_load_bail:
758         session_state_free(state);
759     }
760
761     /* Remove any duplicates.  This means that if two windows (or more) are
762        saved with the same session state, we won't restore a session for any
763        of them because we don't know what window to put what on. AHEM FIREFOX.
764
765        This is going to be an O(2^n) kind of operation unfortunately.
766     */
767     for (it = session_saved_state; it; it = inext) {
768         GList *jt, *jnext;
769         gboolean founddup = FALSE;
770         ObSessionState *s1 = it->data;
771
772         inext = g_list_next(it);
773
774         for (jt = g_list_next(it); jt; jt = jnext) {
775             ObSessionState *s2 = jt->data;
776             gboolean match;
777
778             jnext = g_list_next(jt);
779
780             if (s1->id && s2->id)
781                 match = strcmp(s1->id, s2->id) == 0;
782             else if (s1->command && s2->command)
783                 match = strcmp(s1->command, s2->command) == 0;
784             else
785                 match = FALSE;
786
787             if (match &&
788                 !strcmp(s1->name, s2->name) &&
789                 !strcmp(s1->class, s2->class) &&
790                 !strcmp(s1->role, s2->role))
791             {
792                 session_state_free(s2);
793                 session_saved_state =
794                     g_list_delete_link(session_saved_state, jt);
795                 founddup = TRUE;
796             }
797         }
798
799         if (founddup) {
800             session_state_free(s1);
801             session_saved_state = g_list_delete_link(session_saved_state, it);
802         }
803     }
804
805     xmlFreeDoc(doc);
806 }
807
808 void session_request_logout(gboolean silent)
809 {
810     if (sm_conn) {
811         SmcRequestSaveYourself(sm_conn,
812                                SmSaveBoth,
813                                TRUE, /* logout */
814                                (silent ?
815                                 SmInteractStyleNone : SmInteractStyleAny),
816                                TRUE,  /* if false, with GSM, it shows the old
817                                          logout prompt */
818                                TRUE); /* global */
819     }
820     else
821         g_message(_("Not connected to a session manager"));
822 }
823
824 #endif