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 */
139 session_setup_restart_style(!permanent
);
141 SmcCloseConnection(sm_conn
, 0, NULL
);
143 while (session_saved_state
) {
144 session_state_free(session_saved_state
->data
);
145 session_saved_state
= g_list_delete_link(session_saved_state
,
146 session_saved_state
);
151 /*! Connect to the session manager and set up our callback functions */
152 static gboolean
session_connect()
156 gchar sm_err
[SM_ERR_LEN
];
158 /* set up our callback functions */
159 cb
.save_yourself
.callback
= sm_save_yourself
;
160 cb
.save_yourself
.client_data
= NULL
;
161 cb
.die
.callback
= sm_die
;
162 cb
.die
.client_data
= NULL
;
163 cb
.save_complete
.callback
= sm_save_complete
;
164 cb
.save_complete
.client_data
= NULL
;
165 cb
.shutdown_cancelled
.callback
= sm_shutdown_cancelled
;
166 cb
.shutdown_cancelled
.client_data
= NULL
;
168 /* connect to the server */
170 ob_debug_type(OB_DEBUG_SM
, "Connecting to SM with id: %s\n",
171 oldid
? oldid
: "(null)");
172 sm_conn
= SmcOpenConnection(NULL
, NULL
, 1, 0,
173 SmcSaveYourselfProcMask
|
175 SmcSaveCompleteProcMask
|
176 SmcShutdownCancelledProcMask
,
177 &cb
, oldid
, &ob_sm_id
,
181 ob_debug("Failed to connect to session manager: %s\n", sm_err
);
182 return sm_conn
!= NULL
;
185 static void session_setup_program()
189 .length
= strlen(sm_argv
[0]) + 1
192 .name
= g_strdup(SmProgram
),
193 .type
= g_strdup(SmARRAY8
),
197 SmProp
*list
= &prop
;
198 SmcSetProperties(sm_conn
, 1, &list
);
203 static void session_setup_user()
205 char *user
= g_strdup(g_get_user_name());
209 .length
= strlen(user
) + 1
212 .name
= g_strdup(SmUserID
),
213 .type
= g_strdup(SmARRAY8
),
217 SmProp
*list
= &prop
;
218 SmcSetProperties(sm_conn
, 1, &list
);
224 static void session_setup_restart_style(gboolean restart
)
226 char restart_hint
= restart
? SmRestartImmediately
: SmRestartIfRunning
;
229 .value
= &restart_hint
,
233 .name
= g_strdup(SmRestartStyleHint
),
234 .type
= g_strdup(SmCARD8
),
238 SmProp
*list
= &prop
;
239 SmcSetProperties(sm_conn
, 1, &list
);
244 static void session_setup_pid()
246 gchar
*pid
= g_strdup_printf("%ld", (glong
) getpid());
250 .length
= strlen(pid
) + 1
253 .name
= g_strdup(SmProcessID
),
254 .type
= g_strdup(SmARRAY8
),
258 SmProp
*list
= &prop
;
259 SmcSetProperties(sm_conn
, 1, &list
);
265 /*! This is a gnome-session-manager extension */
266 static void session_setup_priority()
268 gchar priority
= 20; /* 20 is a lower prioity to run before other apps */
275 .name
= g_strdup("_GSM_Priority"),
276 .type
= g_strdup(SmCARD8
),
280 SmProp
*list
= &prop
;
281 SmcSetProperties(sm_conn
, 1, &list
);
286 static void session_setup_clone_command()
290 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
);
292 .name
= g_strdup(SmCloneCommand
),
293 .type
= g_strdup(SmLISTofARRAY8
),
298 for (i
= 0; i
< sm_argc
; ++i
) {
299 vals
[i
].value
= sm_argv
[i
];
300 vals
[i
].length
= strlen(sm_argv
[i
]) + 1;
303 SmProp
*list
= &prop
;
304 SmcSetProperties(sm_conn
, 1, &list
);
310 static void session_setup_restart_command()
314 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
+ 4);
316 .name
= g_strdup(SmRestartCommand
),
317 .type
= g_strdup(SmLISTofARRAY8
),
318 .num_vals
= sm_argc
+ 4,
322 for (i
= 0; i
< sm_argc
; ++i
) {
323 vals
[i
].value
= sm_argv
[i
];
324 vals
[i
].length
= strlen(sm_argv
[i
]) + 1;
327 vals
[i
].value
= g_strdup("--sm-save-file");
328 vals
[i
].length
= strlen("--sm-save-file") + 1;
329 vals
[i
+1].value
= ob_sm_save_file
;
330 vals
[i
+1].length
= strlen(ob_sm_save_file
) + 1;
332 vals
[i
+2].value
= g_strdup("--sm-client-id");
333 vals
[i
+2].length
= strlen("--sm-client-id") + 1;
334 vals
[i
+3].value
= ob_sm_id
;
335 vals
[i
+3].length
= strlen(ob_sm_id
) + 1;
337 SmProp
*list
= &prop
;
338 SmcSetProperties(sm_conn
, 1, &list
);
341 g_free(vals
[i
].value
);
342 g_free(vals
[i
+2].value
);
346 static ObSMSaveData
*sm_save_get_data()
348 ObSMSaveData
*savedata
= g_new0(ObSMSaveData
, 1);
349 savedata
->focus_client
= focus_client
;
350 savedata
->desktop
= screen_desktop
;
354 static void sm_save_yourself_2(SmcConn conn
, SmPointer data
)
357 ObSMSaveData
*savedata
= data
;
359 /* save the current state */
360 ob_debug_type(OB_DEBUG_SM
, "Session save phase 2 requested\n");
361 ob_debug_type(OB_DEBUG_SM
,
362 " Saving session to file '%s'\n", ob_sm_save_file
);
363 if (savedata
== NULL
)
364 savedata
= sm_save_get_data();
365 success
= session_save_to_file(savedata
);
368 /* tell the session manager how to restore this state */
369 if (success
) session_setup_restart_command();
371 ob_debug_type(OB_DEBUG_SM
, "Saving is done (success = %d)\n", success
);
372 SmcSaveYourselfDone(conn
, success
);
376 static void sm_save_yourself(SmcConn conn
, SmPointer data
, gint save_type
,
377 Bool shutdown
, gint interact_style
, Bool fast
)
379 ObSMSaveData
*savedata
= NULL
;
382 ob_debug_type(OB_DEBUG_SM
, "Session save requested\n");
384 vendor
= SmcVendor(sm_conn
);
385 ob_debug_type(OB_DEBUG_SM
, "Session manager's vendor: %s\n", vendor
);
387 if (!strcmp(vendor
, "KDE")) {
388 /* ksmserver guarantees that phase 1 will complete before allowing any
389 clients interaction, so we can save this sanely here before they
390 get messed up from interaction */
391 savedata
= sm_save_get_data();
395 if (!SmcRequestSaveYourselfPhase2(conn
, sm_save_yourself_2
, savedata
)) {
396 ob_debug_type(OB_DEBUG_SM
, "Requst for phase 2 failed\n");
397 SmcSaveYourselfDone(conn
, FALSE
);
401 static void sm_die(SmcConn conn
, SmPointer data
)
403 ob_debug_type(OB_DEBUG_SM
, "Die requested\n");
407 static void sm_save_complete(SmcConn conn
, SmPointer data
)
409 ob_debug_type(OB_DEBUG_SM
, "Save complete\n");
412 static void sm_shutdown_cancelled(SmcConn conn
, SmPointer data
)
414 ob_debug_type(OB_DEBUG_SM
, "Shutdown cancelled\n");
417 static gboolean
session_save_to_file(const ObSMSaveData
*savedata
)
421 gboolean success
= TRUE
;
423 f
= fopen(ob_sm_save_file
, "w");
426 g_message(_("Unable to save the session to '%s': %s"),
427 ob_sm_save_file
, g_strerror(errno
));
429 fprintf(f
, "<?xml version=\"1.0\"?>\n\n");
430 fprintf(f
, "<openbox_session>\n\n");
432 fprintf(f
, "<desktop>%d</desktop>\n", savedata
->desktop
);
434 /* they are ordered top to bottom in stacking order */
435 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
436 gint prex
, prey
, prew
, preh
;
440 if (WINDOW_IS_CLIENT(it
->data
))
441 c
= WINDOW_AS_CLIENT(it
->data
);
445 if (!client_normal(c
))
448 if (!c
->sm_client_id
) {
449 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have a "
452 if (!c
->wm_command
) {
453 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have an "
454 "oldskool wm_command set either. We won't "
455 "be saving its data\n",
461 ob_debug_type(OB_DEBUG_SM
, "Saving state for client %s\n",
466 prew
= c
->area
.width
;
467 preh
= c
->area
.height
;
469 prex
= c
->pre_fullscreen_area
.x
;
470 prey
= c
->pre_fullscreen_area
.x
;
471 prew
= c
->pre_fullscreen_area
.width
;
472 preh
= c
->pre_fullscreen_area
.height
;
475 prex
= c
->pre_max_area
.x
;
476 prew
= c
->pre_max_area
.width
;
479 prey
= c
->pre_max_area
.y
;
480 preh
= c
->pre_max_area
.height
;
484 fprintf(f
, "<window id=\"%s\">\n", c
->sm_client_id
);
486 fprintf(f
, "<window command=\"%s\">\n", c
->wm_command
);
488 t
= g_markup_escape_text(c
->name
, -1);
489 fprintf(f
, "\t<name>%s</name>\n", t
);
492 t
= g_markup_escape_text(c
->class, -1);
493 fprintf(f
, "\t<class>%s</class>\n", t
);
496 t
= g_markup_escape_text(c
->role
, -1);
497 fprintf(f
, "\t<role>%s</role>\n", t
);
500 fprintf(f
, "\t<desktop>%d</desktop>\n", c
->desktop
);
501 fprintf(f
, "\t<x>%d</x>\n", prex
);
502 fprintf(f
, "\t<y>%d</y>\n", prey
);
503 fprintf(f
, "\t<width>%d</width>\n", prew
);
504 fprintf(f
, "\t<height>%d</height>\n", preh
);
506 fprintf(f
, "\t<shaded />\n");
508 fprintf(f
, "\t<iconic />\n");
510 fprintf(f
, "\t<skip_pager />\n");
512 fprintf(f
, "\t<skip_taskbar />\n");
514 fprintf(f
, "\t<fullscreen />\n");
516 fprintf(f
, "\t<above />\n");
518 fprintf(f
, "\t<below />\n");
520 fprintf(f
, "\t<max_horz />\n");
522 fprintf(f
, "\t<max_vert />\n");
524 fprintf(f
, "\t<undecorated />\n");
525 if (savedata
->focus_client
== c
)
526 fprintf(f
, "\t<focused />\n");
527 fprintf(f
, "</window>\n\n");
530 fprintf(f
, "</openbox_session>\n");
534 g_message(_("Error while saving the session to '%s': %s"),
535 ob_sm_save_file
, g_strerror(errno
));
543 static void session_state_free(ObSessionState
*state
)
547 g_free(state
->command
);
549 g_free(state
->class);
556 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
)
558 ob_debug_type(OB_DEBUG_SM
, "Comparing client against saved state: \n");
559 ob_debug_type(OB_DEBUG_SM
, " client id: %s \n", c
->sm_client_id
);
560 ob_debug_type(OB_DEBUG_SM
, " client name: %s \n", c
->name
);
561 ob_debug_type(OB_DEBUG_SM
, " client class: %s \n", c
->class);
562 ob_debug_type(OB_DEBUG_SM
, " client role: %s \n", c
->role
);
563 ob_debug_type(OB_DEBUG_SM
, " client command: %s \n",
564 c
->wm_command
? c
->wm_command
: "(null)");
565 ob_debug_type(OB_DEBUG_SM
, " state id: %s \n", s
->id
);
566 ob_debug_type(OB_DEBUG_SM
, " state name: %s \n", s
->name
);
567 ob_debug_type(OB_DEBUG_SM
, " state class: %s \n", s
->class);
568 ob_debug_type(OB_DEBUG_SM
, " state role: %s \n", s
->role
);
569 ob_debug_type(OB_DEBUG_SM
, " state command: %s \n",
570 s
->command
? s
->command
: "(null)");
572 if ((c
->sm_client_id
&& s
->id
&& !strcmp(c
->sm_client_id
, s
->id
)) ||
573 (c
->wm_command
&& s
->command
&& !strcmp(c
->wm_command
, s
->command
)))
575 return (!strcmp(s
->name
, c
->name
) &&
576 !strcmp(s
->class, c
->class) &&
577 !strcmp(s
->role
, c
->role
));
582 GList
* session_state_find(ObClient
*c
)
586 for (it
= session_saved_state
; it
; it
= g_list_next(it
)) {
587 ObSessionState
*s
= it
->data
;
588 if (!s
->matched
&& session_state_cmp(s
, c
)) {
596 static void session_load_file(const gchar
*path
)
601 if (!parse_load(path
, "openbox_session", &doc
, &node
))
604 if ((n
= parse_find_node("desktop", node
->children
)))
605 session_desktop
= parse_int(doc
, n
);
607 for (node
= parse_find_node("window", node
->children
); node
!= NULL
;
608 node
= parse_find_node("window", node
->next
))
610 ObSessionState
*state
;
612 state
= g_new0(ObSessionState
, 1);
614 if (!parse_attr_string("id", node
, &state
->id
))
615 if (!parse_attr_string("command", node
, &state
->command
))
616 goto session_load_bail
;
617 if (!(n
= parse_find_node("name", node
->children
)))
618 goto session_load_bail
;
619 state
->name
= parse_string(doc
, n
);
620 if (!(n
= parse_find_node("class", node
->children
)))
621 goto session_load_bail
;
622 state
->class = parse_string(doc
, n
);
623 if (!(n
= parse_find_node("role", node
->children
)))
624 goto session_load_bail
;
625 state
->role
= parse_string(doc
, n
);
626 if (!(n
= parse_find_node("desktop", node
->children
)))
627 goto session_load_bail
;
628 state
->desktop
= parse_int(doc
, n
);
629 if (!(n
= parse_find_node("x", node
->children
)))
630 goto session_load_bail
;
631 state
->x
= parse_int(doc
, n
);
632 if (!(n
= parse_find_node("y", node
->children
)))
633 goto session_load_bail
;
634 state
->y
= parse_int(doc
, n
);
635 if (!(n
= parse_find_node("width", node
->children
)))
636 goto session_load_bail
;
637 state
->w
= parse_int(doc
, n
);
638 if (!(n
= parse_find_node("height", node
->children
)))
639 goto session_load_bail
;
640 state
->h
= parse_int(doc
, n
);
643 parse_find_node("shaded", node
->children
) != NULL
;
645 parse_find_node("iconic", node
->children
) != NULL
;
647 parse_find_node("skip_pager", node
->children
) != NULL
;
648 state
->skip_taskbar
=
649 parse_find_node("skip_taskbar", node
->children
) != NULL
;
651 parse_find_node("fullscreen", node
->children
) != NULL
;
653 parse_find_node("above", node
->children
) != NULL
;
655 parse_find_node("below", node
->children
) != NULL
;
657 parse_find_node("max_horz", node
->children
) != NULL
;
659 parse_find_node("max_vert", node
->children
) != NULL
;
661 parse_find_node("undecorated", node
->children
) != NULL
;
663 parse_find_node("focused", node
->children
) != NULL
;
665 /* save this. they are in the file in stacking order, so preserve
667 session_saved_state
= g_list_append(session_saved_state
, state
);
671 session_state_free(state
);