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