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