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
; }
40 #include "parser/parse.h"
47 # include <sys/types.h>
51 #include <X11/SM/SMlib.h>
53 #define SM_ERR_LEN 1024
55 static SmcConn sm_conn
;
57 static gchar
**sm_argv
;
59 static gboolean
session_connect();
61 static void session_load_file(gchar
*path
);
62 static gboolean
session_save_to_file();
64 static void session_setup_program();
65 static void session_setup_user();
66 static void session_setup_restart_style(gboolean restart
);
67 static void session_setup_pid();
68 static void session_setup_priority();
69 static void session_setup_clone_command();
70 static void session_setup_restart_command();
72 static void sm_save_yourself(SmcConn conn
, SmPointer data
, gint save_type
,
73 Bool shutdown
, gint interact_style
, Bool fast
);
74 static void sm_die(SmcConn conn
, SmPointer data
);
75 static void sm_save_complete(SmcConn conn
, SmPointer data
);
76 static void sm_shutdown_cancelled(SmcConn conn
, SmPointer data
);
78 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
);
79 static void session_state_free(ObSessionState
*state
);
81 void session_startup(gint argc
, gchar
**argv
)
85 if (!ob_sm_use
) return;
90 dir
= g_build_filename(parse_xdg_data_home_path(),
91 "openbox", "sessions", NULL
);
92 if (!parse_mkdir_path(dir
, 0700)) {
93 g_message(_("Unable to make directory '%s': %s"),
94 dir
, g_strerror(errno
));
97 if (ob_sm_save_file
!= NULL
) {
98 ob_debug_type(OB_DEBUG_SM
, "Loading from session file %s\n",
100 session_load_file(ob_sm_save_file
);
104 /* this algo is from metacity */
105 filename
= g_strdup_printf("%u-%u-%u.obs",
109 ob_sm_save_file
= g_build_filename(dir
, filename
, NULL
);
113 if (session_connect()) {
114 session_setup_program();
115 session_setup_user();
116 session_setup_restart_style(TRUE
);
118 session_setup_priority();
119 session_setup_clone_command();
125 void session_shutdown(gboolean permanent
)
127 if (!ob_sm_use
) return;
130 /* if permanent is true then we will change our session state so that
131 the SM won't run us again */
132 session_setup_restart_style(!permanent
);
134 SmcCloseConnection(sm_conn
, 0, NULL
);
136 while (session_saved_state
) {
137 session_state_free(session_saved_state
->data
);
138 session_saved_state
= g_list_delete_link(session_saved_state
,
139 session_saved_state
);
144 /*! Connect to the session manager and set up our callback functions */
145 static gboolean
session_connect()
149 gchar sm_err
[SM_ERR_LEN
];
151 /* set up our callback functions */
152 cb
.save_yourself
.callback
= sm_save_yourself
;
153 cb
.save_yourself
.client_data
= NULL
;
154 cb
.die
.callback
= sm_die
;
155 cb
.die
.client_data
= NULL
;
156 cb
.save_complete
.callback
= sm_save_complete
;
157 cb
.save_complete
.client_data
= NULL
;
158 cb
.shutdown_cancelled
.callback
= sm_shutdown_cancelled
;
159 cb
.shutdown_cancelled
.client_data
= NULL
;
161 /* connect to the server */
163 ob_debug_type(OB_DEBUG_SM
, "Connecting to SM with id: %s\n",
164 oldid
? oldid
: "(null)");
165 sm_conn
= SmcOpenConnection(NULL
, NULL
, 1, 0,
166 SmcSaveYourselfProcMask
|
168 SmcSaveCompleteProcMask
|
169 SmcShutdownCancelledProcMask
,
170 &cb
, oldid
, &ob_sm_id
,
174 ob_debug("Failed to connect to session manager: %s\n", sm_err
);
175 return sm_conn
!= NULL
;
178 static void session_setup_program()
182 .length
= strlen(sm_argv
[0]) + 1
185 .name
= g_strdup(SmProgram
),
186 .type
= g_strdup(SmARRAY8
),
190 SmProp
*list
= &prop
;
191 SmcSetProperties(sm_conn
, 1, &list
);
196 static void session_setup_user()
198 char *user
= g_strdup(g_get_user_name());
202 .length
= strlen(user
) + 1
205 .name
= g_strdup(SmUserID
),
206 .type
= g_strdup(SmARRAY8
),
210 SmProp
*list
= &prop
;
211 SmcSetProperties(sm_conn
, 1, &list
);
217 static void session_setup_restart_style(gboolean restart
)
219 char restart_hint
= restart
? SmRestartImmediately
: SmRestartIfRunning
;
222 .value
= &restart_hint
,
226 .name
= g_strdup(SmRestartStyleHint
),
227 .type
= g_strdup(SmCARD8
),
231 SmProp
*list
= &prop
;
232 SmcSetProperties(sm_conn
, 1, &list
);
237 static void session_setup_pid()
239 gchar
*pid
= g_strdup_printf("%ld", (glong
) getpid());
243 .length
= strlen(pid
) + 1
246 .name
= g_strdup(SmProcessID
),
247 .type
= g_strdup(SmARRAY8
),
251 SmProp
*list
= &prop
;
252 SmcSetProperties(sm_conn
, 1, &list
);
258 /*! This is a gnome-session-manager extension */
259 static void session_setup_priority()
261 gchar priority
= 20; /* 20 is a lower prioity to run before other apps */
268 .name
= g_strdup("_GSM_Priority"),
269 .type
= g_strdup(SmCARD8
),
273 SmProp
*list
= &prop
;
274 SmcSetProperties(sm_conn
, 1, &list
);
279 static void session_setup_clone_command()
283 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
);
285 .name
= g_strdup(SmCloneCommand
),
286 .type
= g_strdup(SmLISTofARRAY8
),
291 for (i
= 0; i
< sm_argc
; ++i
) {
292 vals
[i
].value
= sm_argv
[i
];
293 vals
[i
].length
= strlen(sm_argv
[i
]) + 1;
296 SmProp
*list
= &prop
;
297 SmcSetProperties(sm_conn
, 1, &list
);
303 static void session_setup_restart_command()
307 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
+ 4);
309 .name
= g_strdup(SmRestartCommand
),
310 .type
= g_strdup(SmLISTofARRAY8
),
311 .num_vals
= sm_argc
+ 4,
315 for (i
= 0; i
< sm_argc
; ++i
) {
316 vals
[i
].value
= sm_argv
[i
];
317 vals
[i
].length
= strlen(sm_argv
[i
]) + 1;
320 vals
[i
].value
= g_strdup("--sm-save-file");
321 vals
[i
].length
= strlen("--sm-save-file") + 1;
322 vals
[i
+1].value
= ob_sm_save_file
;
323 vals
[i
+1].length
= strlen(ob_sm_save_file
) + 1;
325 vals
[i
+2].value
= g_strdup("--sm-client-id");
326 vals
[i
+2].length
= strlen("--sm-client-id") + 1;
327 vals
[i
+3].value
= ob_sm_id
;
328 vals
[i
+3].length
= strlen(ob_sm_id
) + 1;
330 SmProp
*list
= &prop
;
331 SmcSetProperties(sm_conn
, 1, &list
);
334 g_free(vals
[i
].value
);
335 g_free(vals
[i
+2].value
);
339 static void sm_save_yourself_2(SmcConn conn
, SmPointer data
)
343 /* save the current state */
344 ob_debug_type(OB_DEBUG_SM
, "Session save phase 2 requested\n");
345 ob_debug_type(OB_DEBUG_SM
,
346 " Saving session to file '%s'\n", ob_sm_save_file
);
347 success
= session_save_to_file();
349 /* tell the session manager how to restore this state */
350 if (success
) session_setup_restart_command();
352 ob_debug_type(OB_DEBUG_SM
, "Saving is done (success = %d)\n", success
);
353 SmcSaveYourselfDone(conn
, success
);
357 static void sm_save_yourself(SmcConn conn
, SmPointer data
, gint save_type
,
358 Bool shutdown
, gint interact_style
, Bool fast
)
360 ob_debug_type(OB_DEBUG_SM
, "Session save requested\n");
361 if (!SmcRequestSaveYourselfPhase2(conn
, sm_save_yourself_2
, data
)) {
362 ob_debug_type(OB_DEBUG_SM
, "Requst for phase 2 failed\n");
363 SmcSaveYourselfDone(conn
, FALSE
);
367 static void sm_die(SmcConn conn
, SmPointer data
)
369 ob_debug_type(OB_DEBUG_SM
, "Die requested\n");
373 static void sm_save_complete(SmcConn conn
, SmPointer data
)
375 ob_debug_type(OB_DEBUG_SM
, "Save complete\n");
378 static void sm_shutdown_cancelled(SmcConn conn
, SmPointer data
)
380 ob_debug_type(OB_DEBUG_SM
, "Shutdown cancelled\n");
383 static gboolean
session_save_to_file()
387 gboolean success
= TRUE
;
389 f
= fopen(ob_sm_save_file
, "w");
392 g_message(_("Unable to save the session to '%s': %s"),
393 ob_sm_save_file
, g_strerror(errno
));
395 fprintf(f
, "<?xml version=\"1.0\"?>\n\n");
396 fprintf(f
, "<openbox_session>\n\n");
398 fprintf(f
, "<desktop>%d</desktop>\n", screen_desktop
);
400 /* they are ordered top to bottom in stacking order */
401 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
402 gint prex
, prey
, prew
, preh
;
406 if (WINDOW_IS_CLIENT(it
->data
))
407 c
= WINDOW_AS_CLIENT(it
->data
);
411 if (!client_normal(c
))
414 if (!c
->sm_client_id
) {
415 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have a client "
416 "id set, so we can't save its state\n",
421 ob_debug_type(OB_DEBUG_SM
, "Saving state for client %s\n",
426 prew
= c
->area
.width
;
427 preh
= c
->area
.height
;
429 prex
= c
->pre_fullscreen_area
.x
;
430 prey
= c
->pre_fullscreen_area
.x
;
431 prew
= c
->pre_fullscreen_area
.width
;
432 preh
= c
->pre_fullscreen_area
.height
;
435 prex
= c
->pre_max_area
.x
;
436 prew
= c
->pre_max_area
.width
;
439 prey
= c
->pre_max_area
.y
;
440 preh
= c
->pre_max_area
.height
;
443 fprintf(f
, "<window id=\"%s\">\n", c
->sm_client_id
);
445 t
= g_markup_escape_text(c
->name
, -1);
446 fprintf(f
, "\t<name>%s</name>\n", t
);
449 t
= g_markup_escape_text(c
->class, -1);
450 fprintf(f
, "\t<class>%s</class>\n", t
);
453 t
= g_markup_escape_text(c
->role
, -1);
454 fprintf(f
, "\t<role>%s</role>\n", t
);
457 fprintf(f
, "\t<desktop>%d</desktop>\n", c
->desktop
);
458 fprintf(f
, "\t<x>%d</x>\n", prex
);
459 fprintf(f
, "\t<y>%d</y>\n", prey
);
460 fprintf(f
, "\t<width>%d</width>\n", prew
);
461 fprintf(f
, "\t<height>%d</height>\n", preh
);
463 fprintf(f
, "\t<shaded />\n");
465 fprintf(f
, "\t<iconic />\n");
467 fprintf(f
, "\t<skip_pager />\n");
469 fprintf(f
, "\t<skip_taskbar />\n");
471 fprintf(f
, "\t<fullscreen />\n");
473 fprintf(f
, "\t<above />\n");
475 fprintf(f
, "\t<below />\n");
477 fprintf(f
, "\t<max_horz />\n");
479 fprintf(f
, "\t<max_vert />\n");
481 fprintf(f
, "\t<undecorated />\n");
482 if (client_focused(c
))
483 fprintf(f
, "\t<focused />\n");
484 fprintf(f
, "</window>\n\n");
487 fprintf(f
, "</openbox_session>\n");
491 g_message(_("Error while saving the session to '%s': %s"),
492 ob_sm_save_file
, g_strerror(errno
));
500 static void session_state_free(ObSessionState
*state
)
505 g_free(state
->class);
512 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
)
514 ob_debug_type(OB_DEBUG_SM
, "Comparing client against saved state: \n");
515 ob_debug_type(OB_DEBUG_SM
, " client id: %s \n", c
->sm_client_id
);
516 ob_debug_type(OB_DEBUG_SM
, " client name: %s \n", c
->name
);
517 ob_debug_type(OB_DEBUG_SM
, " client class: %s \n", c
->class);
518 ob_debug_type(OB_DEBUG_SM
, " client role: %s \n", c
->role
);
519 ob_debug_type(OB_DEBUG_SM
, " state id: %s \n", s
->id
);
520 ob_debug_type(OB_DEBUG_SM
, " state name: %s \n", s
->name
);
521 ob_debug_type(OB_DEBUG_SM
, " state class: %s \n", s
->class);
522 ob_debug_type(OB_DEBUG_SM
, " state role: %s \n", s
->role
);
523 return (c
->sm_client_id
&&
524 !strcmp(s
->id
, c
->sm_client_id
) &&
525 !strcmp(s
->name
, c
->name
) &&
526 !strcmp(s
->class, c
->class) &&
527 !strcmp(s
->role
, c
->role
));
530 GList
* session_state_find(ObClient
*c
)
534 for (it
= session_saved_state
; it
; it
= g_list_next(it
)) {
535 ObSessionState
*s
= it
->data
;
536 if (!s
->matched
&& session_state_cmp(s
, c
)) {
544 static void session_load_file(gchar
*path
)
549 if (!parse_load(path
, "openbox_session", &doc
, &node
))
552 if ((n
= parse_find_node("desktop", node
->children
)))
553 session_desktop
= parse_int(doc
, n
);
555 for (node
= parse_find_node("window", node
->children
); node
!= NULL
;
556 node
= parse_find_node("window", node
->next
))
558 ObSessionState
*state
;
560 state
= g_new0(ObSessionState
, 1);
562 if (!parse_attr_string("id", node
, &state
->id
))
563 goto session_load_bail
;
564 if (!(n
= parse_find_node("name", node
->children
)))
565 goto session_load_bail
;
566 state
->name
= parse_string(doc
, n
);
567 if (!(n
= parse_find_node("class", node
->children
)))
568 goto session_load_bail
;
569 state
->class = parse_string(doc
, n
);
570 if (!(n
= parse_find_node("role", node
->children
)))
571 goto session_load_bail
;
572 state
->role
= parse_string(doc
, n
);
573 if (!(n
= parse_find_node("desktop", node
->children
)))
574 goto session_load_bail
;
575 state
->desktop
= parse_int(doc
, n
);
576 if (!(n
= parse_find_node("x", node
->children
)))
577 goto session_load_bail
;
578 state
->x
= parse_int(doc
, n
);
579 if (!(n
= parse_find_node("y", node
->children
)))
580 goto session_load_bail
;
581 state
->y
= parse_int(doc
, n
);
582 if (!(n
= parse_find_node("width", node
->children
)))
583 goto session_load_bail
;
584 state
->w
= parse_int(doc
, n
);
585 if (!(n
= parse_find_node("height", node
->children
)))
586 goto session_load_bail
;
587 state
->h
= parse_int(doc
, n
);
590 parse_find_node("shaded", node
->children
) != NULL
;
592 parse_find_node("iconic", node
->children
) != NULL
;
594 parse_find_node("skip_pager", node
->children
) != NULL
;
595 state
->skip_taskbar
=
596 parse_find_node("skip_taskbar", node
->children
) != NULL
;
598 parse_find_node("fullscreen", node
->children
) != NULL
;
600 parse_find_node("above", node
->children
) != NULL
;
602 parse_find_node("below", node
->children
) != NULL
;
604 parse_find_node("max_horz", node
->children
) != NULL
;
606 parse_find_node("max_vert", node
->children
) != NULL
;
608 parse_find_node("undecorated", node
->children
) != NULL
;
610 parse_find_node("focused", node
->children
) != NULL
;
612 /* save this. they are in the file in stacking order, so preserve
614 session_saved_state
= g_list_append(session_saved_state
, state
);
618 session_state_free(state
);