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