don't save our state for SmSaveGlobal session saves, just Local ones
[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 #ifdef DEBUG
408     {
409         const gchar *sname =
410             (save_type == SmSaveLocal ? "SmSaveLocal" :
411              (save_type == SmSaveGlobal ? "SmSaveGlobal" :
412               (save_type == SmSaveBoth ? "SmSaveBoth" : "INVALID!!")));
413         ob_debug_type(OB_DEBUG_SM, "Session save requested, type %s\n", sname);
414     }
415 #endif
416
417     if (save_type == SmSaveGlobal) {
418         /* we have no data to save.  we only store state to get back to where
419            we were, we don't keep open writable files or anything */
420         SmcSaveYourselfDone(conn, TRUE);
421         return;
422     }
423
424     vendor = SmcVendor(sm_conn);
425     ob_debug_type(OB_DEBUG_SM, "Session manager's vendor: %s\n", vendor);
426
427     if (!strcmp(vendor, "KDE")) {
428         /* ksmserver guarantees that phase 1 will complete before allowing any
429            clients interaction, so we can save this sanely here before they
430            get messed up from interaction */
431         savedata = sm_save_get_data();
432     }
433     free(vendor);
434
435     if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, savedata)) {
436         ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed\n");
437         g_free(savedata);
438         SmcSaveYourselfDone(conn, FALSE);
439     }
440 }
441
442 static void sm_die(SmcConn conn, SmPointer data)
443 {
444     ob_debug_type(OB_DEBUG_SM, "Die requested\n");
445     ob_exit(0);
446 }
447
448 static void sm_save_complete(SmcConn conn, SmPointer data)
449 {
450     ob_debug_type(OB_DEBUG_SM, "Save complete\n");
451 }
452
453 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
454 {
455     ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled\n");
456 }
457
458 static gboolean session_save_to_file(const ObSMSaveData *savedata)
459 {
460     FILE *f;
461     GList *it;
462     gboolean success = TRUE;
463
464     f = fopen(ob_sm_save_file, "w");
465     if (!f) {
466         success = FALSE;
467         g_message(_("Unable to save the session to \"%s\": %s"),
468                   ob_sm_save_file, g_strerror(errno));
469     } else {
470         fprintf(f, "<?xml version=\"1.0\"?>\n\n");
471         fprintf(f, "<openbox_session>\n\n");
472
473         fprintf(f, "<desktop>%d</desktop>\n", savedata->desktop);
474
475         fprintf(f, "<numdesktops>%d</numdesktops>\n", screen_num_desktops);
476
477         fprintf(f, "<desktoplayout>\n");
478         fprintf(f, "  <orientation>%d</orientation>\n",
479                 screen_desktop_layout.orientation);
480         fprintf(f, "  <startcorner>%d</startcorner>\n",
481                 screen_desktop_layout.start_corner);
482         fprintf(f, "  <columns>%d</columns>\n",
483                 screen_desktop_layout.columns);
484         fprintf(f, "  <rows>%d</rows>\n",
485                 screen_desktop_layout.rows);
486         fprintf(f, "</desktoplayout>\n");
487
488         if (screen_desktop_names) {
489             gint i;
490
491             fprintf(f, "<desktopnames>\n");
492             for (i = 0; screen_desktop_names[i]; ++i)
493                 fprintf(f, "  <name>%s</name>\n", screen_desktop_names[i]);
494             fprintf(f, "</desktopnames>\n");
495         }
496
497         /* they are ordered top to bottom in stacking order */
498         for (it = stacking_list; it; it = g_list_next(it)) {
499             gint prex, prey, prew, preh;
500             ObClient *c;
501             gchar *t;
502
503             if (WINDOW_IS_CLIENT(it->data))
504                 c = WINDOW_AS_CLIENT(it->data);
505             else
506                 continue;
507
508             if (!client_normal(c))
509                 continue;
510
511             if (!c->sm_client_id) {
512                 ob_debug_type(OB_DEBUG_SM, "Client %s does not have a "
513                               "session id set\n",
514                               c->title);
515                 if (!c->wm_command) {
516                     ob_debug_type(OB_DEBUG_SM, "Client %s does not have an "
517                                   "oldskool wm_command set either. We won't "
518                                   "be saving its data\n",
519                                   c->title);
520                     continue;
521                 }
522             }
523
524             ob_debug_type(OB_DEBUG_SM, "Saving state for client %s\n",
525                           c->title);
526
527             prex = c->area.x;
528             prey = c->area.y;
529             prew = c->area.width;
530             preh = c->area.height;
531             if (c->fullscreen) {
532                 prex = c->pre_fullscreen_area.x;
533                 prey = c->pre_fullscreen_area.x;
534                 prew = c->pre_fullscreen_area.width;
535                 preh = c->pre_fullscreen_area.height;
536             }
537             if (c->max_horz) {
538                 prex = c->pre_max_area.x;
539                 prew = c->pre_max_area.width;
540             }
541             if (c->max_vert) {
542                 prey = c->pre_max_area.y;
543                 preh = c->pre_max_area.height;
544             }
545
546             if (c->sm_client_id)
547                 fprintf(f, "<window id=\"%s\">\n", c->sm_client_id);
548             else
549                 fprintf(f, "<window command=\"%s\">\n", c->wm_command);
550
551             t = g_markup_escape_text(c->name, -1);
552             fprintf(f, "\t<name>%s</name>\n", t);
553             g_free(t);
554
555             t = g_markup_escape_text(c->class, -1);
556             fprintf(f, "\t<class>%s</class>\n", t);
557             g_free(t);
558
559             t = g_markup_escape_text(c->role, -1);
560             fprintf(f, "\t<role>%s</role>\n", t);
561             g_free(t);
562
563             fprintf(f, "\t<windowtype>%d</windowtype>\n", c->type);
564
565             fprintf(f, "\t<desktop>%d</desktop>\n", c->desktop);
566             fprintf(f, "\t<x>%d</x>\n", prex);
567             fprintf(f, "\t<y>%d</y>\n", prey);
568             fprintf(f, "\t<width>%d</width>\n", prew);
569             fprintf(f, "\t<height>%d</height>\n", preh);
570             if (c->shaded)
571                 fprintf(f, "\t<shaded />\n");
572             if (c->iconic)
573                 fprintf(f, "\t<iconic />\n");
574             if (c->skip_pager)
575                 fprintf(f, "\t<skip_pager />\n");
576             if (c->skip_taskbar)
577                 fprintf(f, "\t<skip_taskbar />\n");
578             if (c->fullscreen)
579                 fprintf(f, "\t<fullscreen />\n");
580             if (c->above)
581                 fprintf(f, "\t<above />\n");
582             if (c->below)
583                 fprintf(f, "\t<below />\n");
584             if (c->max_horz)
585                 fprintf(f, "\t<max_horz />\n");
586             if (c->max_vert)
587                 fprintf(f, "\t<max_vert />\n");
588             if (c->undecorated)
589                 fprintf(f, "\t<undecorated />\n");
590             if (savedata->focus_client == c)
591                 fprintf(f, "\t<focused />\n");
592             fprintf(f, "</window>\n\n");
593         }
594
595         fprintf(f, "</openbox_session>\n");
596
597         if (fflush(f)) {
598             success = FALSE;
599             g_message(_("Error while saving the session to \"%s\": %s"),
600                       ob_sm_save_file, g_strerror(errno));
601         }
602         fclose(f);
603     }
604
605     return success;
606 }
607
608 static void session_state_free(ObSessionState *state)
609 {
610     if (state) {
611         g_free(state->id);
612         g_free(state->command);
613         g_free(state->name);
614         g_free(state->class);
615         g_free(state->role);
616
617         g_free(state);
618     }
619 }
620
621 static gboolean session_state_cmp(ObSessionState *s, ObClient *c)
622 {
623     ob_debug_type(OB_DEBUG_SM, "Comparing client against saved state: \n");
624     ob_debug_type(OB_DEBUG_SM, "  client id: %s \n", c->sm_client_id);
625     ob_debug_type(OB_DEBUG_SM, "  client name: %s \n", c->name);
626     ob_debug_type(OB_DEBUG_SM, "  client class: %s \n", c->class);
627     ob_debug_type(OB_DEBUG_SM, "  client role: %s \n", c->role);
628     ob_debug_type(OB_DEBUG_SM, "  client type: %d \n", c->type);
629     ob_debug_type(OB_DEBUG_SM, "  client command: %s \n",
630                   c->wm_command ? c->wm_command : "(null)");
631     ob_debug_type(OB_DEBUG_SM, "  state id: %s \n", s->id);
632     ob_debug_type(OB_DEBUG_SM, "  state name: %s \n", s->name);
633     ob_debug_type(OB_DEBUG_SM, "  state class: %s \n", s->class);
634     ob_debug_type(OB_DEBUG_SM, "  state role: %s \n", s->role);
635     ob_debug_type(OB_DEBUG_SM, "  state type: %d \n", s->type);
636     ob_debug_type(OB_DEBUG_SM, "  state command: %s \n",
637                   s->command ? s->command : "(null)");
638
639     if ((c->sm_client_id && s->id && !strcmp(c->sm_client_id, s->id)) ||
640         (c->wm_command && s->command && !strcmp(c->wm_command, s->command)))
641     {
642         return (!strcmp(s->name, c->name) &&
643                 !strcmp(s->class, c->class) &&
644                 !strcmp(s->role, c->role) &&
645                 /* the check for type is to catch broken clients, like
646                    firefox, which open a different window on startup
647                    with the same info as the one we saved. only do this
648                    check for old windows that dont use xsmp, others should
649                    know better ! */
650                 (!s->command || c->type == s->type));
651     }
652     return FALSE;
653 }
654
655 GList* session_state_find(ObClient *c)
656 {
657     GList *it;
658
659     for (it = session_saved_state; it; it = g_list_next(it)) {
660         ObSessionState *s = it->data;
661         if (!s->matched && session_state_cmp(s, c)) {
662             s->matched = TRUE;
663             break;
664         }
665     }
666     return it;
667 }
668
669 static void session_load_file(const gchar *path)
670 {
671     xmlDocPtr doc;
672     xmlNodePtr node, n, m;
673     GList *it, *inext;
674
675     if (!parse_load(path, "openbox_session", &doc, &node))
676         return;
677
678     if ((n = parse_find_node("desktop", node->children)))
679         session_desktop = parse_int(doc, n);
680
681     if ((n = parse_find_node("numdesktops", node->children)))
682         session_num_desktops = parse_int(doc, n);
683
684     if ((n = parse_find_node("desktoplayout", node->children))) {
685         /* make sure they are all there for it to be valid */
686         if ((m = parse_find_node("orientation", n->children)))
687             session_desktop_layout.orientation = parse_int(doc, m);
688         if (m && (m = parse_find_node("startcorner", n->children)))
689             session_desktop_layout.start_corner = parse_int(doc, m);
690         if (m && (m = parse_find_node("columns", n->children)))
691             session_desktop_layout.columns = parse_int(doc, m);
692         if (m && (m = parse_find_node("rows", n->children)))
693             session_desktop_layout.rows = parse_int(doc, m);
694         session_desktop_layout_present = m != NULL;
695     }
696
697     if ((n = parse_find_node("desktopnames", node->children))) {
698         for (m = parse_find_node("name", n->children); m;
699              m = parse_find_node("name", m->next))
700         {
701             session_desktop_names = g_slist_append(session_desktop_names,
702                                                    parse_string(doc, m));
703         }
704     }
705
706     for (node = parse_find_node("window", node->children); node != NULL;
707          node = parse_find_node("window", node->next))
708     {
709         ObSessionState *state;
710
711         state = g_new0(ObSessionState, 1);
712
713         if (!parse_attr_string("id", node, &state->id))
714             if (!parse_attr_string("command", node, &state->command))
715             goto session_load_bail;
716         if (!(n = parse_find_node("name", node->children)))
717             goto session_load_bail;
718         state->name = parse_string(doc, n);
719         if (!(n = parse_find_node("class", node->children)))
720             goto session_load_bail;
721         state->class = parse_string(doc, n);
722         if (!(n = parse_find_node("role", node->children)))
723             goto session_load_bail;
724         state->role = parse_string(doc, n);
725         if (!(n = parse_find_node("windowtype", node->children)))
726             goto session_load_bail;
727         state->type = parse_int(doc, n);
728         if (!(n = parse_find_node("desktop", node->children)))
729             goto session_load_bail;
730         state->desktop = parse_int(doc, n);
731         if (!(n = parse_find_node("x", node->children)))
732             goto session_load_bail;
733         state->x = parse_int(doc, n);
734         if (!(n = parse_find_node("y", node->children)))
735             goto session_load_bail;
736         state->y = parse_int(doc, n);
737         if (!(n = parse_find_node("width", node->children)))
738             goto session_load_bail;
739         state->w = parse_int(doc, n);
740         if (!(n = parse_find_node("height", node->children)))
741             goto session_load_bail;
742         state->h = parse_int(doc, n);
743
744         state->shaded =
745             parse_find_node("shaded", node->children) != NULL;
746         state->iconic =
747             parse_find_node("iconic", node->children) != NULL;
748         state->skip_pager =
749             parse_find_node("skip_pager", node->children) != NULL;
750         state->skip_taskbar =
751             parse_find_node("skip_taskbar", node->children) != NULL;
752         state->fullscreen =
753             parse_find_node("fullscreen", node->children) != NULL;
754         state->above =
755             parse_find_node("above", node->children) != NULL;
756         state->below =
757             parse_find_node("below", node->children) != NULL;
758         state->max_horz =
759             parse_find_node("max_horz", node->children) != NULL;
760         state->max_vert =
761             parse_find_node("max_vert", node->children) != NULL;
762         state->undecorated =
763             parse_find_node("undecorated", node->children) != NULL;
764         state->focused =
765             parse_find_node("focused", node->children) != NULL;
766
767         /* save this. they are in the file in stacking order, so preserve
768            that order here */
769         session_saved_state = g_list_append(session_saved_state, state);
770         continue;
771
772     session_load_bail:
773         session_state_free(state);
774     }
775
776     /* Remove any duplicates.  This means that if two windows (or more) are
777        saved with the same session state, we won't restore a session for any
778        of them because we don't know what window to put what on. AHEM FIREFOX.
779
780        This is going to be an O(2^n) kind of operation unfortunately.
781     */
782     for (it = session_saved_state; it; it = inext) {
783         GList *jt, *jnext;
784         gboolean founddup = FALSE;
785         ObSessionState *s1 = it->data;
786
787         inext = g_list_next(it);
788
789         for (jt = g_list_next(it); jt; jt = jnext) {
790             ObSessionState *s2 = jt->data;
791             gboolean match;
792
793             jnext = g_list_next(jt);
794
795             if (s1->id && s2->id)
796                 match = strcmp(s1->id, s2->id) == 0;
797             else if (s1->command && s2->command)
798                 match = strcmp(s1->command, s2->command) == 0;
799             else
800                 match = FALSE;
801
802             if (match &&
803                 !strcmp(s1->name, s2->name) &&
804                 !strcmp(s1->class, s2->class) &&
805                 !strcmp(s1->role, s2->role))
806             {
807                 session_state_free(s2);
808                 session_saved_state =
809                     g_list_delete_link(session_saved_state, jt);
810                 founddup = TRUE;
811             }
812         }
813
814         if (founddup) {
815             session_state_free(s1);
816             session_saved_state = g_list_delete_link(session_saved_state, it);
817         }
818     }
819
820     xmlFreeDoc(doc);
821 }
822
823 void session_request_logout(gboolean silent)
824 {
825     if (sm_conn) {
826         SmcRequestSaveYourself(sm_conn,
827                                SmSaveGlobal,
828                                TRUE, /* logout */
829                                (silent ?
830                                 SmInteractStyleNone : SmInteractStyleAny),
831                                TRUE,  /* if false, with GSM, it shows the old
832                                          logout prompt */
833                                TRUE); /* global */
834     }
835     else
836         g_message(_("Not connected to a session manager"));
837 }
838
839 #endif