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