Allow escaping _ in menu labels by putting __
[openbox.git] / openbox / session.c
blob811592ec90cd5fbbfb68531a36ebc5a88e4bf18b
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 session.c for the Openbox window manager
4 Copyright (c) 2003-2007 Dana Jansens
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.
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.
16 See the COPYING file for a copy of the GNU General Public License.
19 /* This session code is largely inspired by metacity code. */
21 #include "session.h"
23 struct _ObClient;
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;
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
39 #include "debug.h"
40 #include "openbox.h"
41 #include "client.h"
42 #include "prop.h"
43 #include "focus.h"
44 #include "gettext.h"
45 #include "parser/parse.h"
47 #include <time.h>
48 #include <errno.h>
49 #include <stdio.h>
51 #ifdef HAVE_UNISTD_H
52 # include <sys/types.h>
53 # include <unistd.h>
54 #endif
56 #include <X11/SM/SMlib.h>
58 #define SM_ERR_LEN 1024
60 static SmcConn sm_conn;
61 static gint sm_argc;
62 static gchar **sm_argv;
64 /* Data saved from the first level save yourself */
65 typedef struct {
66 ObClient *focus_client;
67 gint desktop;
68 } ObSMSaveData;
70 static gboolean session_connect();
72 static void session_load_file(const gchar *path);
73 static gboolean session_save_to_file(const ObSMSaveData *savedata);
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();
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);
89 static gboolean session_state_cmp(ObSessionState *s, ObClient *c);
90 static void session_state_free(ObSessionState *state);
92 void session_startup(gint argc, gchar **argv)
94 gchar *dir;
96 if (!ob_sm_use) return;
98 sm_argc = argc;
99 sm_argv = argv;
101 dir = g_build_filename(parse_xdg_data_home_path(),
102 "openbox", "sessions", NULL);
103 if (!parse_mkdir_path(dir, 0700)) {
104 g_message(_("Unable to make directory \"%s\": %s"),
105 dir, g_strerror(errno));
108 if (ob_sm_save_file != NULL) {
109 if (ob_sm_restore) {
110 ob_debug_type(OB_DEBUG_SM, "Loading from session file %s\n",
111 ob_sm_save_file);
112 session_load_file(ob_sm_save_file);
114 } else {
115 gchar *filename;
117 /* this algo is from metacity */
118 filename = g_strdup_printf("%u-%u-%u.obs",
119 (guint)time(NULL),
120 (guint)getpid(),
121 g_random_int());
122 ob_sm_save_file = g_build_filename(dir, filename, NULL);
123 g_free(filename);
126 if (session_connect()) {
127 session_setup_program();
128 session_setup_user();
129 session_setup_restart_style(TRUE);
130 session_setup_pid();
131 session_setup_priority();
132 session_setup_clone_command();
135 g_free(dir);
138 void session_shutdown(gboolean permanent)
140 if (!ob_sm_use) return;
142 if (sm_conn) {
143 /* if permanent is true then we will change our session state so that
144 the SM won't run us again */
145 if (permanent)
146 session_setup_restart_style(FALSE);
148 SmcCloseConnection(sm_conn, 0, NULL);
150 while (session_saved_state) {
151 session_state_free(session_saved_state->data);
152 session_saved_state = g_list_delete_link(session_saved_state,
153 session_saved_state);
158 /*! Connect to the session manager and set up our callback functions */
159 static gboolean session_connect()
161 SmcCallbacks cb;
162 gchar *oldid;
163 gchar sm_err[SM_ERR_LEN];
165 /* set up our callback functions */
166 cb.save_yourself.callback = sm_save_yourself;
167 cb.save_yourself.client_data = NULL;
168 cb.die.callback = sm_die;
169 cb.die.client_data = NULL;
170 cb.save_complete.callback = sm_save_complete;
171 cb.save_complete.client_data = NULL;
172 cb.shutdown_cancelled.callback = sm_shutdown_cancelled;
173 cb.shutdown_cancelled.client_data = NULL;
175 /* connect to the server */
176 oldid = ob_sm_id;
177 ob_debug_type(OB_DEBUG_SM, "Connecting to SM with id: %s\n",
178 oldid ? oldid : "(null)");
179 sm_conn = SmcOpenConnection(NULL, NULL, 1, 0,
180 SmcSaveYourselfProcMask |
181 SmcDieProcMask |
182 SmcSaveCompleteProcMask |
183 SmcShutdownCancelledProcMask,
184 &cb, oldid, &ob_sm_id,
185 SM_ERR_LEN-1, sm_err);
186 g_free(oldid);
187 ob_debug_type(OB_DEBUG_SM, "Connected to SM with id: %s\n", ob_sm_id);
188 if (sm_conn == NULL)
189 ob_debug("Failed to connect to session manager: %s\n", sm_err);
190 return sm_conn != NULL;
193 static void session_setup_program()
195 SmPropValue vals = {
196 .value = sm_argv[0],
197 .length = strlen(sm_argv[0]) + 1
199 SmProp prop = {
200 .name = g_strdup(SmProgram),
201 .type = g_strdup(SmARRAY8),
202 .num_vals = 1,
203 .vals = &vals
205 SmProp *list = &prop;
206 ob_debug_type(OB_DEBUG_SM, "Setting program: %s\n", sm_argv[0]);
207 SmcSetProperties(sm_conn, 1, &list);
208 g_free(prop.name);
209 g_free(prop.type);
212 static void session_setup_user()
214 char *user = g_strdup(g_get_user_name());
216 SmPropValue vals = {
217 .value = user,
218 .length = strlen(user) + 1
220 SmProp prop = {
221 .name = g_strdup(SmUserID),
222 .type = g_strdup(SmARRAY8),
223 .num_vals = 1,
224 .vals = &vals
226 SmProp *list = &prop;
227 ob_debug_type(OB_DEBUG_SM, "Setting user: %s\n", user);
228 SmcSetProperties(sm_conn, 1, &list);
229 g_free(prop.name);
230 g_free(prop.type);
231 g_free(user);
234 static void session_setup_restart_style(gboolean restart)
236 gchar restart_hint = restart ? SmRestartImmediately : SmRestartIfRunning;
238 SmPropValue vals = {
239 .value = &restart_hint,
240 .length = 1
242 SmProp prop = {
243 .name = g_strdup(SmRestartStyleHint),
244 .type = g_strdup(SmCARD8),
245 .num_vals = 1,
246 .vals = &vals
248 SmProp *list = &prop;
249 ob_debug_type(OB_DEBUG_SM, "Setting restart: %d\n", restart);
250 SmcSetProperties(sm_conn, 1, &list);
251 g_free(prop.name);
252 g_free(prop.type);
255 static void session_setup_pid()
257 gchar *pid = g_strdup_printf("%ld", (glong) getpid());
259 SmPropValue vals = {
260 .value = pid,
261 .length = strlen(pid) + 1
263 SmProp prop = {
264 .name = g_strdup(SmProcessID),
265 .type = g_strdup(SmARRAY8),
266 .num_vals = 1,
267 .vals = &vals
269 SmProp *list = &prop;
270 ob_debug_type(OB_DEBUG_SM, "Setting pid: %s\n", pid);
271 SmcSetProperties(sm_conn, 1, &list);
272 g_free(prop.name);
273 g_free(prop.type);
274 g_free(pid);
277 /*! This is a gnome-session-manager extension */
278 static void session_setup_priority()
280 gchar priority = 20; /* 20 is a lower prioity to run before other apps */
282 SmPropValue vals = {
283 .value = &priority,
284 .length = 1
286 SmProp prop = {
287 .name = g_strdup("_GSM_Priority"),
288 .type = g_strdup(SmCARD8),
289 .num_vals = 1,
290 .vals = &vals
292 SmProp *list = &prop;
293 ob_debug_type(OB_DEBUG_SM, "Setting priority: %d\n", priority);
294 SmcSetProperties(sm_conn, 1, &list);
295 g_free(prop.name);
296 g_free(prop.type);
299 static void session_setup_clone_command()
301 gint i;
303 SmPropValue *vals = g_new(SmPropValue, sm_argc);
304 SmProp prop = {
305 .name = g_strdup(SmCloneCommand),
306 .type = g_strdup(SmLISTofARRAY8),
307 .num_vals = sm_argc,
308 .vals = vals
310 SmProp *list = &prop;
312 ob_debug_type(OB_DEBUG_SM, "Setting clone command: (%d)\n", sm_argc);
313 for (i = 0; i < sm_argc; ++i) {
314 vals[i].value = sm_argv[i];
315 vals[i].length = strlen(sm_argv[i]) + 1;
316 ob_debug_type(OB_DEBUG_SM, " %s\n", vals[i].value);
319 SmcSetProperties(sm_conn, 1, &list);
320 g_free(prop.name);
321 g_free(prop.type);
322 g_free(vals);
325 static void session_setup_restart_command()
327 gint i;
329 SmPropValue *vals = g_new(SmPropValue, sm_argc + 4);
330 SmProp prop = {
331 .name = g_strdup(SmRestartCommand),
332 .type = g_strdup(SmLISTofARRAY8),
333 .num_vals = sm_argc + 4,
334 .vals = vals
336 SmProp *list = &prop;
338 ob_debug_type(OB_DEBUG_SM, "Setting restart command: (%d)\n", sm_argc+4);
339 for (i = 0; i < sm_argc; ++i) {
340 vals[i].value = sm_argv[i];
341 vals[i].length = strlen(sm_argv[i]) + 1;
342 ob_debug_type(OB_DEBUG_SM, " %s\n", vals[i].value);
345 vals[i].value = g_strdup("--sm-client-id");
346 vals[i].length = strlen("--sm-client-id") + 1;
347 vals[i+1].value = ob_sm_id;
348 vals[i+1].length = strlen(ob_sm_id) + 1;
349 ob_debug_type(OB_DEBUG_SM, " %s\n", vals[i].value);
350 ob_debug_type(OB_DEBUG_SM, " %s\n", vals[i+1].value);
352 vals[i+2].value = g_strdup("--sm-save-file");
353 vals[i+2].length = strlen("--sm-save-file") + 1;
354 vals[i+3].value = ob_sm_save_file;
355 vals[i+3].length = strlen(ob_sm_save_file) + 1;
356 ob_debug_type(OB_DEBUG_SM, " %s\n", vals[i+2].value);
357 ob_debug_type(OB_DEBUG_SM, " %s\n", vals[i+3].value);
359 SmcSetProperties(sm_conn, 1, &list);
360 g_free(prop.name);
361 g_free(prop.type);
362 g_free(vals[i].value);
363 g_free(vals[i+2].value);
364 g_free(vals);
367 static ObSMSaveData *sm_save_get_data()
369 ObSMSaveData *savedata = g_new0(ObSMSaveData, 1);
370 /* save the active desktop and client.
371 we don't bother to preemptively save the other desktop state like
372 number and names of desktops, cuz those shouldn't be changing during
373 the save.. */
374 savedata->focus_client = focus_client;
375 savedata->desktop = screen_desktop;
376 return savedata;
379 static void sm_save_yourself_2(SmcConn conn, SmPointer data)
381 gboolean success;
382 ObSMSaveData *savedata = data;
384 /* save the current state */
385 ob_debug_type(OB_DEBUG_SM, "Session save phase 2 requested\n");
386 ob_debug_type(OB_DEBUG_SM,
387 " Saving session to file '%s'\n", ob_sm_save_file);
388 if (savedata == NULL)
389 savedata = sm_save_get_data();
390 success = session_save_to_file(savedata);
391 g_free(savedata);
393 /* tell the session manager how to restore this state */
394 if (success) session_setup_restart_command();
396 ob_debug_type(OB_DEBUG_SM, "Saving is done (success = %d)\n", success);
397 SmcSaveYourselfDone(conn, success);
400 static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type,
401 Bool shutdown, gint interact_style, Bool fast)
403 ObSMSaveData *savedata = NULL;
404 gchar *vendor;
406 #ifdef DEBUG
408 const gchar *sname =
409 (save_type == SmSaveLocal ? "SmSaveLocal" :
410 (save_type == SmSaveGlobal ? "SmSaveGlobal" :
411 (save_type == SmSaveBoth ? "SmSaveBoth" : "INVALID!!")));
412 ob_debug_type(OB_DEBUG_SM, "Session save requested, type %s\n", sname);
414 #endif
416 if (save_type == SmSaveGlobal) {
417 /* we have no data to save. we only store state to get back to where
418 we were, we don't keep open writable files or anything */
419 SmcSaveYourselfDone(conn, TRUE);
420 return;
423 vendor = SmcVendor(sm_conn);
424 ob_debug_type(OB_DEBUG_SM, "Session manager's vendor: %s\n", vendor);
426 if (!strcmp(vendor, "KDE")) {
427 /* ksmserver guarantees that phase 1 will complete before allowing any
428 clients interaction, so we can save this sanely here before they
429 get messed up from interaction */
430 savedata = sm_save_get_data();
432 free(vendor);
434 if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, savedata)) {
435 ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed\n");
436 g_free(savedata);
437 SmcSaveYourselfDone(conn, FALSE);
441 static void sm_die(SmcConn conn, SmPointer data)
443 ob_debug_type(OB_DEBUG_SM, "Die requested\n");
444 ob_exit(0);
447 static void sm_save_complete(SmcConn conn, SmPointer data)
449 ob_debug_type(OB_DEBUG_SM, "Save complete\n");
452 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
454 ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled\n");
457 static gboolean session_save_to_file(const ObSMSaveData *savedata)
459 FILE *f;
460 GList *it;
461 gboolean success = TRUE;
463 f = fopen(ob_sm_save_file, "w");
464 if (!f) {
465 success = FALSE;
466 g_message(_("Unable to save the session to \"%s\": %s"),
467 ob_sm_save_file, g_strerror(errno));
468 } else {
469 fprintf(f, "<?xml version=\"1.0\"?>\n\n");
470 fprintf(f, "<openbox_session>\n\n");
472 fprintf(f, "<desktop>%d</desktop>\n", savedata->desktop);
474 fprintf(f, "<numdesktops>%d</numdesktops>\n", screen_num_desktops);
476 fprintf(f, "<desktoplayout>\n");
477 fprintf(f, " <orientation>%d</orientation>\n",
478 screen_desktop_layout.orientation);
479 fprintf(f, " <startcorner>%d</startcorner>\n",
480 screen_desktop_layout.start_corner);
481 fprintf(f, " <columns>%d</columns>\n",
482 screen_desktop_layout.columns);
483 fprintf(f, " <rows>%d</rows>\n",
484 screen_desktop_layout.rows);
485 fprintf(f, "</desktoplayout>\n");
487 if (screen_desktop_names) {
488 gint i;
489 gchar *t;
491 fprintf(f, "<desktopnames>\n");
492 for (i = 0; screen_desktop_names[i]; ++i){
493 t = g_markup_escape_text(screen_desktop_names[i], -1);
494 fprintf(f, " <name>%s</name>\n", t);
495 g_free(t);
497 fprintf(f, "</desktopnames>\n");
500 /* they are ordered top to bottom in stacking order */
501 for (it = stacking_list; it; it = g_list_next(it)) {
502 gint prex, prey, prew, preh;
503 ObClient *c;
504 gchar *t;
506 if (WINDOW_IS_CLIENT(it->data))
507 c = WINDOW_AS_CLIENT(it->data);
508 else
509 continue;
511 if (!client_normal(c))
512 continue;
514 if (!c->sm_client_id) {
515 ob_debug_type(OB_DEBUG_SM, "Client %s does not have a "
516 "session id set\n",
517 c->title);
518 if (!c->wm_command) {
519 ob_debug_type(OB_DEBUG_SM, "Client %s does not have an "
520 "oldskool wm_command set either. We won't "
521 "be saving its data\n",
522 c->title);
523 continue;
527 ob_debug_type(OB_DEBUG_SM, "Saving state for client %s\n",
528 c->title);
530 prex = c->area.x;
531 prey = c->area.y;
532 prew = c->area.width;
533 preh = c->area.height;
534 if (c->fullscreen) {
535 prex = c->pre_fullscreen_area.x;
536 prey = c->pre_fullscreen_area.x;
537 prew = c->pre_fullscreen_area.width;
538 preh = c->pre_fullscreen_area.height;
540 if (c->max_horz) {
541 prex = c->pre_max_area.x;
542 prew = c->pre_max_area.width;
544 if (c->max_vert) {
545 prey = c->pre_max_area.y;
546 preh = c->pre_max_area.height;
549 if (c->sm_client_id)
550 fprintf(f, "<window id=\"%s\">\n", c->sm_client_id);
551 else {
552 t = g_markup_escape_text(c->wm_command, -1);
553 fprintf(f, "<window command=\"%s\">\n", t);
554 g_free(t);
557 t = g_markup_escape_text(c->name, -1);
558 fprintf(f, "\t<name>%s</name>\n", t);
559 g_free(t);
561 t = g_markup_escape_text(c->class, -1);
562 fprintf(f, "\t<class>%s</class>\n", t);
563 g_free(t);
565 t = g_markup_escape_text(c->role, -1);
566 fprintf(f, "\t<role>%s</role>\n", t);
567 g_free(t);
569 fprintf(f, "\t<windowtype>%d</windowtype>\n", c->type);
571 fprintf(f, "\t<desktop>%d</desktop>\n", c->desktop);
572 fprintf(f, "\t<x>%d</x>\n", prex);
573 fprintf(f, "\t<y>%d</y>\n", prey);
574 fprintf(f, "\t<width>%d</width>\n", prew);
575 fprintf(f, "\t<height>%d</height>\n", preh);
576 if (c->shaded)
577 fprintf(f, "\t<shaded />\n");
578 if (c->iconic)
579 fprintf(f, "\t<iconic />\n");
580 if (c->skip_pager)
581 fprintf(f, "\t<skip_pager />\n");
582 if (c->skip_taskbar)
583 fprintf(f, "\t<skip_taskbar />\n");
584 if (c->fullscreen)
585 fprintf(f, "\t<fullscreen />\n");
586 if (c->above)
587 fprintf(f, "\t<above />\n");
588 if (c->below)
589 fprintf(f, "\t<below />\n");
590 if (c->max_horz)
591 fprintf(f, "\t<max_horz />\n");
592 if (c->max_vert)
593 fprintf(f, "\t<max_vert />\n");
594 if (c->undecorated)
595 fprintf(f, "\t<undecorated />\n");
596 if (savedata->focus_client == c)
597 fprintf(f, "\t<focused />\n");
598 fprintf(f, "</window>\n\n");
601 fprintf(f, "</openbox_session>\n");
603 if (fflush(f)) {
604 success = FALSE;
605 g_message(_("Error while saving the session to \"%s\": %s"),
606 ob_sm_save_file, g_strerror(errno));
608 fclose(f);
611 return success;
614 static void session_state_free(ObSessionState *state)
616 if (state) {
617 g_free(state->id);
618 g_free(state->command);
619 g_free(state->name);
620 g_free(state->class);
621 g_free(state->role);
623 g_free(state);
627 static gboolean session_state_cmp(ObSessionState *s, ObClient *c)
629 ob_debug_type(OB_DEBUG_SM, "Comparing client against saved state: \n");
630 ob_debug_type(OB_DEBUG_SM, " client id: %s \n", c->sm_client_id);
631 ob_debug_type(OB_DEBUG_SM, " client name: %s \n", c->name);
632 ob_debug_type(OB_DEBUG_SM, " client class: %s \n", c->class);
633 ob_debug_type(OB_DEBUG_SM, " client role: %s \n", c->role);
634 ob_debug_type(OB_DEBUG_SM, " client type: %d \n", c->type);
635 ob_debug_type(OB_DEBUG_SM, " client command: %s \n",
636 c->wm_command ? c->wm_command : "(null)");
637 ob_debug_type(OB_DEBUG_SM, " state id: %s \n", s->id);
638 ob_debug_type(OB_DEBUG_SM, " state name: %s \n", s->name);
639 ob_debug_type(OB_DEBUG_SM, " state class: %s \n", s->class);
640 ob_debug_type(OB_DEBUG_SM, " state role: %s \n", s->role);
641 ob_debug_type(OB_DEBUG_SM, " state type: %d \n", s->type);
642 ob_debug_type(OB_DEBUG_SM, " state command: %s \n",
643 s->command ? s->command : "(null)");
645 if ((c->sm_client_id && s->id && !strcmp(c->sm_client_id, s->id)) ||
646 (c->wm_command && s->command && !strcmp(c->wm_command, s->command)))
648 return (!strcmp(s->name, c->name) &&
649 !strcmp(s->class, c->class) &&
650 !strcmp(s->role, c->role) &&
651 /* the check for type is to catch broken clients, like
652 firefox, which open a different window on startup
653 with the same info as the one we saved. only do this
654 check for old windows that dont use xsmp, others should
655 know better ! */
656 (!s->command || c->type == s->type));
658 return FALSE;
661 GList* session_state_find(ObClient *c)
663 GList *it;
665 for (it = session_saved_state; it; it = g_list_next(it)) {
666 ObSessionState *s = it->data;
667 if (!s->matched && session_state_cmp(s, c)) {
668 s->matched = TRUE;
669 break;
672 return it;
675 static void session_load_file(const gchar *path)
677 xmlDocPtr doc;
678 xmlNodePtr node, n, m;
679 GList *it, *inext;
681 if (!parse_load(path, "openbox_session", &doc, &node))
682 return;
684 if ((n = parse_find_node("desktop", node->children)))
685 session_desktop = parse_int(doc, n);
687 if ((n = parse_find_node("numdesktops", node->children)))
688 session_num_desktops = parse_int(doc, n);
690 if ((n = parse_find_node("desktoplayout", node->children))) {
691 /* make sure they are all there for it to be valid */
692 if ((m = parse_find_node("orientation", n->children)))
693 session_desktop_layout.orientation = parse_int(doc, m);
694 if (m && (m = parse_find_node("startcorner", n->children)))
695 session_desktop_layout.start_corner = parse_int(doc, m);
696 if (m && (m = parse_find_node("columns", n->children)))
697 session_desktop_layout.columns = parse_int(doc, m);
698 if (m && (m = parse_find_node("rows", n->children)))
699 session_desktop_layout.rows = parse_int(doc, m);
700 session_desktop_layout_present = m != NULL;
703 if ((n = parse_find_node("desktopnames", node->children))) {
704 for (m = parse_find_node("name", n->children); m;
705 m = parse_find_node("name", m->next))
707 session_desktop_names = g_slist_append(session_desktop_names,
708 parse_string(doc, m));
712 for (node = parse_find_node("window", node->children); node != NULL;
713 node = parse_find_node("window", node->next))
715 ObSessionState *state;
717 state = g_new0(ObSessionState, 1);
719 if (!parse_attr_string("id", node, &state->id))
720 if (!parse_attr_string("command", node, &state->command))
721 goto session_load_bail;
722 if (!(n = parse_find_node("name", node->children)))
723 goto session_load_bail;
724 state->name = parse_string(doc, n);
725 if (!(n = parse_find_node("class", node->children)))
726 goto session_load_bail;
727 state->class = parse_string(doc, n);
728 if (!(n = parse_find_node("role", node->children)))
729 goto session_load_bail;
730 state->role = parse_string(doc, n);
731 if (!(n = parse_find_node("windowtype", node->children)))
732 goto session_load_bail;
733 state->type = parse_int(doc, n);
734 if (!(n = parse_find_node("desktop", node->children)))
735 goto session_load_bail;
736 state->desktop = parse_int(doc, n);
737 if (!(n = parse_find_node("x", node->children)))
738 goto session_load_bail;
739 state->x = parse_int(doc, n);
740 if (!(n = parse_find_node("y", node->children)))
741 goto session_load_bail;
742 state->y = parse_int(doc, n);
743 if (!(n = parse_find_node("width", node->children)))
744 goto session_load_bail;
745 state->w = parse_int(doc, n);
746 if (!(n = parse_find_node("height", node->children)))
747 goto session_load_bail;
748 state->h = parse_int(doc, n);
750 state->shaded =
751 parse_find_node("shaded", node->children) != NULL;
752 state->iconic =
753 parse_find_node("iconic", node->children) != NULL;
754 state->skip_pager =
755 parse_find_node("skip_pager", node->children) != NULL;
756 state->skip_taskbar =
757 parse_find_node("skip_taskbar", node->children) != NULL;
758 state->fullscreen =
759 parse_find_node("fullscreen", node->children) != NULL;
760 state->above =
761 parse_find_node("above", node->children) != NULL;
762 state->below =
763 parse_find_node("below", node->children) != NULL;
764 state->max_horz =
765 parse_find_node("max_horz", node->children) != NULL;
766 state->max_vert =
767 parse_find_node("max_vert", node->children) != NULL;
768 state->undecorated =
769 parse_find_node("undecorated", node->children) != NULL;
770 state->focused =
771 parse_find_node("focused", node->children) != NULL;
773 /* save this. they are in the file in stacking order, so preserve
774 that order here */
775 session_saved_state = g_list_append(session_saved_state, state);
776 continue;
778 session_load_bail:
779 session_state_free(state);
782 /* Remove any duplicates. This means that if two windows (or more) are
783 saved with the same session state, we won't restore a session for any
784 of them because we don't know what window to put what on. AHEM FIREFOX.
786 This is going to be an O(2^n) kind of operation unfortunately.
788 for (it = session_saved_state; it; it = inext) {
789 GList *jt, *jnext;
790 gboolean founddup = FALSE;
791 ObSessionState *s1 = it->data;
793 inext = g_list_next(it);
795 for (jt = g_list_next(it); jt; jt = jnext) {
796 ObSessionState *s2 = jt->data;
797 gboolean match;
799 jnext = g_list_next(jt);
801 if (s1->id && s2->id)
802 match = strcmp(s1->id, s2->id) == 0;
803 else if (s1->command && s2->command)
804 match = strcmp(s1->command, s2->command) == 0;
805 else
806 match = FALSE;
808 if (match &&
809 !strcmp(s1->name, s2->name) &&
810 !strcmp(s1->class, s2->class) &&
811 !strcmp(s1->role, s2->role))
813 session_state_free(s2);
814 session_saved_state =
815 g_list_delete_link(session_saved_state, jt);
816 founddup = TRUE;
820 if (founddup) {
821 session_state_free(s1);
822 session_saved_state = g_list_delete_link(session_saved_state, it);
826 xmlFreeDoc(doc);
829 void session_request_logout(gboolean silent)
831 if (sm_conn) {
832 SmcRequestSaveYourself(sm_conn,
833 SmSaveGlobal,
834 TRUE, /* logout */
835 (silent ?
836 SmInteractStyleNone : SmInteractStyleAny),
837 TRUE, /* if false, with GSM, it shows the old
838 logout prompt */
839 TRUE); /* global */
841 else
842 g_message(_("Not connected to a session manager"));
845 #endif