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