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. */
25 GList
*session_saved_state
= NULL
;
26 gint session_desktop
= -1;
29 void session_startup(gint argc
, gchar
**argv
) {}
30 void session_shutdown(gboolean permanent
) {}
31 GList
* session_state_find(struct _ObClient
*c
) { return NULL
; }
41 #include "parser/parse.h"
48 # include <sys/types.h>
52 #include <X11/SM/SMlib.h>
54 #define SM_ERR_LEN 1024
56 static SmcConn sm_conn
;
58 static gchar
**sm_argv
;
60 /* Data saved from the first level save yourself */
62 ObClient
*focus_client
;
66 static gboolean
session_connect();
68 static void session_load_file(const gchar
*path
);
69 static gboolean
session_save_to_file(const ObSMSaveData
*savedata
);
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();
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
);
85 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
);
86 static void session_state_free(ObSessionState
*state
);
88 void session_startup(gint argc
, gchar
**argv
)
92 if (!ob_sm_use
) return;
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
));
104 if (ob_sm_save_file
!= NULL
) {
105 ob_debug_type(OB_DEBUG_SM
, "Loading from session file %s\n",
107 session_load_file(ob_sm_save_file
);
111 /* this algo is from metacity */
112 filename
= g_strdup_printf("%u-%u-%u.obs",
116 ob_sm_save_file
= g_build_filename(dir
, filename
, NULL
);
120 if (session_connect()) {
121 session_setup_program();
122 session_setup_user();
123 session_setup_restart_style(TRUE
);
125 session_setup_priority();
126 session_setup_clone_command();
132 void session_shutdown(gboolean permanent
)
134 if (!ob_sm_use
) return;
137 /* if permanent is true then we will change our session state so that
138 the SM won't run us again */
140 session_setup_restart_style(FALSE
);
142 SmcCloseConnection(sm_conn
, 0, NULL
);
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
);
152 /*! Connect to the session manager and set up our callback functions */
153 static gboolean
session_connect()
157 gchar sm_err
[SM_ERR_LEN
];
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
;
169 /* connect to the server */
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
|
176 SmcSaveCompleteProcMask
|
177 SmcShutdownCancelledProcMask
,
178 &cb
, oldid
, &ob_sm_id
,
179 SM_ERR_LEN
-1, sm_err
);
181 ob_debug_type(OB_DEBUG_SM
, "Connected to SM with id: %s\n", ob_sm_id
);
183 ob_debug("Failed to connect to session manager: %s\n", sm_err
);
184 return sm_conn
!= NULL
;
187 static void session_setup_program()
191 .length
= strlen(sm_argv
[0]) + 1
194 .name
= g_strdup(SmProgram
),
195 .type
= g_strdup(SmARRAY8
),
199 SmProp
*list
= &prop
;
200 ob_debug_type(OB_DEBUG_SM
, "Setting program: %s\n", sm_argv
[0]);
201 SmcSetProperties(sm_conn
, 1, &list
);
206 static void session_setup_user()
208 char *user
= g_strdup(g_get_user_name());
212 .length
= strlen(user
) + 1
215 .name
= g_strdup(SmUserID
),
216 .type
= g_strdup(SmARRAY8
),
220 SmProp
*list
= &prop
;
221 ob_debug_type(OB_DEBUG_SM
, "Setting user: %s\n", user
);
222 SmcSetProperties(sm_conn
, 1, &list
);
228 static void session_setup_restart_style(gboolean restart
)
230 gchar restart_hint
= restart
? SmRestartImmediately
: SmRestartIfRunning
;
233 .value
= &restart_hint
,
237 .name
= g_strdup(SmRestartStyleHint
),
238 .type
= g_strdup(SmCARD8
),
242 SmProp
*list
= &prop
;
243 ob_debug_type(OB_DEBUG_SM
, "Setting restart: %d\n", restart
);
244 SmcSetProperties(sm_conn
, 1, &list
);
249 static void session_setup_pid()
251 gchar
*pid
= g_strdup_printf("%ld", (glong
) getpid());
255 .length
= strlen(pid
) + 1
258 .name
= g_strdup(SmProcessID
),
259 .type
= g_strdup(SmARRAY8
),
263 SmProp
*list
= &prop
;
264 ob_debug_type(OB_DEBUG_SM
, "Setting pid: %s\n", pid
);
265 SmcSetProperties(sm_conn
, 1, &list
);
271 /*! This is a gnome-session-manager extension */
272 static void session_setup_priority()
274 gchar priority
= 20; /* 20 is a lower prioity to run before other apps */
281 .name
= g_strdup("_GSM_Priority"),
282 .type
= g_strdup(SmCARD8
),
286 SmProp
*list
= &prop
;
287 ob_debug_type(OB_DEBUG_SM
, "Setting priority: %d\n", priority
);
288 SmcSetProperties(sm_conn
, 1, &list
);
293 static void session_setup_clone_command()
297 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
);
299 .name
= g_strdup(SmCloneCommand
),
300 .type
= g_strdup(SmLISTofARRAY8
),
304 SmProp
*list
= &prop
;
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
);
313 SmcSetProperties(sm_conn
, 1, &list
);
319 static void session_setup_restart_command()
323 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
+ 4);
325 .name
= g_strdup(SmRestartCommand
),
326 .type
= g_strdup(SmLISTofARRAY8
),
327 .num_vals
= sm_argc
+ 4,
330 SmProp
*list
= &prop
;
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
);
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
);
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
);
353 SmcSetProperties(sm_conn
, 1, &list
);
356 g_free(vals
[i
].value
);
357 g_free(vals
[i
+2].value
);
361 static ObSMSaveData
*sm_save_get_data()
363 ObSMSaveData
*savedata
= g_new0(ObSMSaveData
, 1);
364 savedata
->focus_client
= focus_client
;
365 savedata
->desktop
= screen_desktop
;
369 static void sm_save_yourself_2(SmcConn conn
, SmPointer data
)
372 ObSMSaveData
*savedata
= data
;
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
);
383 /* tell the session manager how to restore this state */
384 if (success
) session_setup_restart_command();
386 ob_debug_type(OB_DEBUG_SM
, "Saving is done (success = %d)\n", success
);
387 SmcSaveYourselfDone(conn
, success
);
391 static void sm_save_yourself(SmcConn conn
, SmPointer data
, gint save_type
,
392 Bool shutdown
, gint interact_style
, Bool fast
)
394 ObSMSaveData
*savedata
= NULL
;
397 ob_debug_type(OB_DEBUG_SM
, "Session save requested\n");
399 vendor
= SmcVendor(sm_conn
);
400 ob_debug_type(OB_DEBUG_SM
, "Session manager's vendor: %s\n", vendor
);
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();
410 if (!SmcRequestSaveYourselfPhase2(conn
, sm_save_yourself_2
, savedata
)) {
411 ob_debug_type(OB_DEBUG_SM
, "Requst for phase 2 failed\n");
413 SmcSaveYourselfDone(conn
, FALSE
);
417 static void sm_die(SmcConn conn
, SmPointer data
)
419 ob_debug_type(OB_DEBUG_SM
, "Die requested\n");
423 static void sm_save_complete(SmcConn conn
, SmPointer data
)
425 ob_debug_type(OB_DEBUG_SM
, "Save complete\n");
428 static void sm_shutdown_cancelled(SmcConn conn
, SmPointer data
)
430 ob_debug_type(OB_DEBUG_SM
, "Shutdown cancelled\n");
433 static gboolean
session_save_to_file(const ObSMSaveData
*savedata
)
437 gboolean success
= TRUE
;
439 f
= fopen(ob_sm_save_file
, "w");
442 g_message(_("Unable to save the session to '%s': %s"),
443 ob_sm_save_file
, g_strerror(errno
));
445 fprintf(f
, "<?xml version=\"1.0\"?>\n\n");
446 fprintf(f
, "<openbox_session>\n\n");
448 fprintf(f
, "<desktop>%d</desktop>\n", savedata
->desktop
);
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
;
456 if (WINDOW_IS_CLIENT(it
->data
))
457 c
= WINDOW_AS_CLIENT(it
->data
);
461 if (!client_normal(c
))
464 if (!c
->sm_client_id
) {
465 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have a "
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",
477 ob_debug_type(OB_DEBUG_SM
, "Saving state for client %s\n",
482 prew
= c
->area
.width
;
483 preh
= c
->area
.height
;
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
;
491 prex
= c
->pre_max_area
.x
;
492 prew
= c
->pre_max_area
.width
;
495 prey
= c
->pre_max_area
.y
;
496 preh
= c
->pre_max_area
.height
;
500 fprintf(f
, "<window id=\"%s\">\n", c
->sm_client_id
);
502 fprintf(f
, "<window command=\"%s\">\n", c
->wm_command
);
504 t
= g_markup_escape_text(c
->name
, -1);
505 fprintf(f
, "\t<name>%s</name>\n", t
);
508 t
= g_markup_escape_text(c
->class, -1);
509 fprintf(f
, "\t<class>%s</class>\n", t
);
512 t
= g_markup_escape_text(c
->role
, -1);
513 fprintf(f
, "\t<role>%s</role>\n", t
);
516 fprintf(f
, "\t<desktop>%d</desktop>\n", c
->desktop
);
517 fprintf(f
, "\t<x>%d</x>\n", prex
);
518 fprintf(f
, "\t<y>%d</y>\n", prey
);
519 fprintf(f
, "\t<width>%d</width>\n", prew
);
520 fprintf(f
, "\t<height>%d</height>\n", preh
);
522 fprintf(f
, "\t<shaded />\n");
524 fprintf(f
, "\t<iconic />\n");
526 fprintf(f
, "\t<skip_pager />\n");
528 fprintf(f
, "\t<skip_taskbar />\n");
530 fprintf(f
, "\t<fullscreen />\n");
532 fprintf(f
, "\t<above />\n");
534 fprintf(f
, "\t<below />\n");
536 fprintf(f
, "\t<max_horz />\n");
538 fprintf(f
, "\t<max_vert />\n");
540 fprintf(f
, "\t<undecorated />\n");
541 if (savedata
->focus_client
== c
)
542 fprintf(f
, "\t<focused />\n");
543 fprintf(f
, "</window>\n\n");
546 fprintf(f
, "</openbox_session>\n");
550 g_message(_("Error while saving the session to '%s': %s"),
551 ob_sm_save_file
, g_strerror(errno
));
559 static void session_state_free(ObSessionState
*state
)
563 g_free(state
->command
);
565 g_free(state
->class);
572 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
)
574 ob_debug_type(OB_DEBUG_SM
, "Comparing client against saved state: \n");
575 ob_debug_type(OB_DEBUG_SM
, " client id: %s \n", c
->sm_client_id
);
576 ob_debug_type(OB_DEBUG_SM
, " client name: %s \n", c
->name
);
577 ob_debug_type(OB_DEBUG_SM
, " client class: %s \n", c
->class);
578 ob_debug_type(OB_DEBUG_SM
, " client role: %s \n", c
->role
);
579 ob_debug_type(OB_DEBUG_SM
, " client command: %s \n",
580 c
->wm_command
? c
->wm_command
: "(null)");
581 ob_debug_type(OB_DEBUG_SM
, " state id: %s \n", s
->id
);
582 ob_debug_type(OB_DEBUG_SM
, " state name: %s \n", s
->name
);
583 ob_debug_type(OB_DEBUG_SM
, " state class: %s \n", s
->class);
584 ob_debug_type(OB_DEBUG_SM
, " state role: %s \n", s
->role
);
585 ob_debug_type(OB_DEBUG_SM
, " state command: %s \n",
586 s
->command
? s
->command
: "(null)");
588 if ((c
->sm_client_id
&& s
->id
&& !strcmp(c
->sm_client_id
, s
->id
)) ||
589 (c
->wm_command
&& s
->command
&& !strcmp(c
->wm_command
, s
->command
)))
591 return (!strcmp(s
->name
, c
->name
) &&
592 !strcmp(s
->class, c
->class) &&
593 !strcmp(s
->role
, c
->role
));
598 GList
* session_state_find(ObClient
*c
)
602 for (it
= session_saved_state
; it
; it
= g_list_next(it
)) {
603 ObSessionState
*s
= it
->data
;
604 if (!s
->matched
&& session_state_cmp(s
, c
)) {
612 static void session_load_file(const gchar
*path
)
617 if (!parse_load(path
, "openbox_session", &doc
, &node
))
620 if ((n
= parse_find_node("desktop", node
->children
)))
621 session_desktop
= parse_int(doc
, n
);
623 for (node
= parse_find_node("window", node
->children
); node
!= NULL
;
624 node
= parse_find_node("window", node
->next
))
626 ObSessionState
*state
;
628 state
= g_new0(ObSessionState
, 1);
630 if (!parse_attr_string("id", node
, &state
->id
))
631 if (!parse_attr_string("command", node
, &state
->command
))
632 goto session_load_bail
;
633 if (!(n
= parse_find_node("name", node
->children
)))
634 goto session_load_bail
;
635 state
->name
= parse_string(doc
, n
);
636 if (!(n
= parse_find_node("class", node
->children
)))
637 goto session_load_bail
;
638 state
->class = parse_string(doc
, n
);
639 if (!(n
= parse_find_node("role", node
->children
)))
640 goto session_load_bail
;
641 state
->role
= parse_string(doc
, n
);
642 if (!(n
= parse_find_node("desktop", node
->children
)))
643 goto session_load_bail
;
644 state
->desktop
= parse_int(doc
, n
);
645 if (!(n
= parse_find_node("x", node
->children
)))
646 goto session_load_bail
;
647 state
->x
= parse_int(doc
, n
);
648 if (!(n
= parse_find_node("y", node
->children
)))
649 goto session_load_bail
;
650 state
->y
= parse_int(doc
, n
);
651 if (!(n
= parse_find_node("width", node
->children
)))
652 goto session_load_bail
;
653 state
->w
= parse_int(doc
, n
);
654 if (!(n
= parse_find_node("height", node
->children
)))
655 goto session_load_bail
;
656 state
->h
= parse_int(doc
, n
);
659 parse_find_node("shaded", node
->children
) != NULL
;
661 parse_find_node("iconic", node
->children
) != NULL
;
663 parse_find_node("skip_pager", node
->children
) != NULL
;
664 state
->skip_taskbar
=
665 parse_find_node("skip_taskbar", node
->children
) != NULL
;
667 parse_find_node("fullscreen", node
->children
) != NULL
;
669 parse_find_node("above", node
->children
) != NULL
;
671 parse_find_node("below", node
->children
) != NULL
;
673 parse_find_node("max_horz", node
->children
) != NULL
;
675 parse_find_node("max_vert", node
->children
) != NULL
;
677 parse_find_node("undecorated", node
->children
) != NULL
;
679 parse_find_node("focused", node
->children
) != NULL
;
681 /* save this. they are in the file in stacking order, so preserve
683 session_saved_state
= g_list_append(session_saved_state
, state
);
687 session_state_free(state
);