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