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;
27 gint session_num_desktops
= 0;
28 gboolean session_desktop_layout_present
= FALSE
;
29 ObDesktopLayout session_desktop_layout
;
30 GSList
*session_desktop_names
= NULL
;
33 void session_startup(gint argc
, gchar
**argv
) {}
34 void session_shutdown(gboolean permanent
) {}
35 GList
* session_state_find(struct _ObClient
*c
) { return NULL
; }
44 #include "parser/parse.h"
51 # include <sys/types.h>
55 #include <X11/SM/SMlib.h>
57 #define SM_ERR_LEN 1024
59 static SmcConn sm_conn
;
61 static gchar
**sm_argv
;
63 /* Data saved from the first level save yourself */
65 ObClient
*focus_client
;
69 static gboolean
session_connect();
71 static void session_load_file(const gchar
*path
);
72 static gboolean
session_save_to_file(const ObSMSaveData
*savedata
);
74 static void session_setup_program();
75 static void session_setup_user();
76 static void session_setup_restart_style(gboolean restart
);
77 static void session_setup_pid();
78 static void session_setup_priority();
79 static void session_setup_clone_command();
80 static void session_setup_restart_command();
82 static void sm_save_yourself(SmcConn conn
, SmPointer data
, gint save_type
,
83 Bool shutdown
, gint interact_style
, Bool fast
);
84 static void sm_die(SmcConn conn
, SmPointer data
);
85 static void sm_save_complete(SmcConn conn
, SmPointer data
);
86 static void sm_shutdown_cancelled(SmcConn conn
, SmPointer data
);
88 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
);
89 static void session_state_free(ObSessionState
*state
);
91 void session_startup(gint argc
, gchar
**argv
)
95 if (!ob_sm_use
) return;
100 dir
= g_build_filename(parse_xdg_data_home_path(),
101 "openbox", "sessions", NULL
);
102 if (!parse_mkdir_path(dir
, 0700)) {
103 g_message(_("Unable to make directory '%s': %s"),
104 dir
, g_strerror(errno
));
107 if (ob_sm_save_file
!= NULL
) {
109 ob_debug_type(OB_DEBUG_SM
, "Loading from session file %s\n",
111 session_load_file(ob_sm_save_file
);
116 /* this algo is from metacity */
117 filename
= g_strdup_printf("%u-%u-%u.obs",
121 ob_sm_save_file
= g_build_filename(dir
, filename
, NULL
);
125 if (session_connect()) {
126 session_setup_program();
127 session_setup_user();
128 session_setup_restart_style(TRUE
);
130 session_setup_priority();
131 session_setup_clone_command();
137 void session_shutdown(gboolean permanent
)
139 if (!ob_sm_use
) return;
142 /* if permanent is true then we will change our session state so that
143 the SM won't run us again */
145 session_setup_restart_style(FALSE
);
147 SmcCloseConnection(sm_conn
, 0, NULL
);
149 while (session_saved_state
) {
150 session_state_free(session_saved_state
->data
);
151 session_saved_state
= g_list_delete_link(session_saved_state
,
152 session_saved_state
);
157 /*! Connect to the session manager and set up our callback functions */
158 static gboolean
session_connect()
162 gchar sm_err
[SM_ERR_LEN
];
164 /* set up our callback functions */
165 cb
.save_yourself
.callback
= sm_save_yourself
;
166 cb
.save_yourself
.client_data
= NULL
;
167 cb
.die
.callback
= sm_die
;
168 cb
.die
.client_data
= NULL
;
169 cb
.save_complete
.callback
= sm_save_complete
;
170 cb
.save_complete
.client_data
= NULL
;
171 cb
.shutdown_cancelled
.callback
= sm_shutdown_cancelled
;
172 cb
.shutdown_cancelled
.client_data
= NULL
;
174 /* connect to the server */
176 ob_debug_type(OB_DEBUG_SM
, "Connecting to SM with id: %s\n",
177 oldid
? oldid
: "(null)");
178 sm_conn
= SmcOpenConnection(NULL
, NULL
, 1, 0,
179 SmcSaveYourselfProcMask
|
181 SmcSaveCompleteProcMask
|
182 SmcShutdownCancelledProcMask
,
183 &cb
, oldid
, &ob_sm_id
,
184 SM_ERR_LEN
-1, sm_err
);
186 ob_debug_type(OB_DEBUG_SM
, "Connected to SM with id: %s\n", ob_sm_id
);
188 ob_debug("Failed to connect to session manager: %s\n", sm_err
);
189 return sm_conn
!= NULL
;
192 static void session_setup_program()
196 .length
= strlen(sm_argv
[0]) + 1
199 .name
= g_strdup(SmProgram
),
200 .type
= g_strdup(SmARRAY8
),
204 SmProp
*list
= &prop
;
205 ob_debug_type(OB_DEBUG_SM
, "Setting program: %s\n", sm_argv
[0]);
206 SmcSetProperties(sm_conn
, 1, &list
);
211 static void session_setup_user()
213 char *user
= g_strdup(g_get_user_name());
217 .length
= strlen(user
) + 1
220 .name
= g_strdup(SmUserID
),
221 .type
= g_strdup(SmARRAY8
),
225 SmProp
*list
= &prop
;
226 ob_debug_type(OB_DEBUG_SM
, "Setting user: %s\n", user
);
227 SmcSetProperties(sm_conn
, 1, &list
);
233 static void session_setup_restart_style(gboolean restart
)
235 gchar restart_hint
= restart
? SmRestartImmediately
: SmRestartIfRunning
;
238 .value
= &restart_hint
,
242 .name
= g_strdup(SmRestartStyleHint
),
243 .type
= g_strdup(SmCARD8
),
247 SmProp
*list
= &prop
;
248 ob_debug_type(OB_DEBUG_SM
, "Setting restart: %d\n", restart
);
249 SmcSetProperties(sm_conn
, 1, &list
);
254 static void session_setup_pid()
256 gchar
*pid
= g_strdup_printf("%ld", (glong
) getpid());
260 .length
= strlen(pid
) + 1
263 .name
= g_strdup(SmProcessID
),
264 .type
= g_strdup(SmARRAY8
),
268 SmProp
*list
= &prop
;
269 ob_debug_type(OB_DEBUG_SM
, "Setting pid: %s\n", pid
);
270 SmcSetProperties(sm_conn
, 1, &list
);
276 /*! This is a gnome-session-manager extension */
277 static void session_setup_priority()
279 gchar priority
= 20; /* 20 is a lower prioity to run before other apps */
286 .name
= g_strdup("_GSM_Priority"),
287 .type
= g_strdup(SmCARD8
),
291 SmProp
*list
= &prop
;
292 ob_debug_type(OB_DEBUG_SM
, "Setting priority: %d\n", priority
);
293 SmcSetProperties(sm_conn
, 1, &list
);
298 static void session_setup_clone_command()
302 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
);
304 .name
= g_strdup(SmCloneCommand
),
305 .type
= g_strdup(SmLISTofARRAY8
),
309 SmProp
*list
= &prop
;
311 ob_debug_type(OB_DEBUG_SM
, "Setting clone command: (%d)\n", sm_argc
);
312 for (i
= 0; i
< sm_argc
; ++i
) {
313 vals
[i
].value
= sm_argv
[i
];
314 vals
[i
].length
= strlen(sm_argv
[i
]) + 1;
315 ob_debug_type(OB_DEBUG_SM
, " %s\n", vals
[i
].value
);
318 SmcSetProperties(sm_conn
, 1, &list
);
324 static void session_setup_restart_command()
328 SmPropValue
*vals
= g_new(SmPropValue
, sm_argc
+ 4);
330 .name
= g_strdup(SmRestartCommand
),
331 .type
= g_strdup(SmLISTofARRAY8
),
332 .num_vals
= sm_argc
+ 4,
335 SmProp
*list
= &prop
;
337 ob_debug_type(OB_DEBUG_SM
, "Setting restart command: (%d)\n", sm_argc
+4);
338 for (i
= 0; i
< sm_argc
; ++i
) {
339 vals
[i
].value
= sm_argv
[i
];
340 vals
[i
].length
= strlen(sm_argv
[i
]) + 1;
341 ob_debug_type(OB_DEBUG_SM
, " %s\n", vals
[i
].value
);
344 vals
[i
].value
= g_strdup("--sm-client-id");
345 vals
[i
].length
= strlen("--sm-client-id") + 1;
346 vals
[i
+1].value
= ob_sm_id
;
347 vals
[i
+1].length
= strlen(ob_sm_id
) + 1;
348 ob_debug_type(OB_DEBUG_SM
, " %s\n", vals
[i
].value
);
349 ob_debug_type(OB_DEBUG_SM
, " %s\n", vals
[i
+1].value
);
351 vals
[i
+2].value
= g_strdup("--sm-save-file");
352 vals
[i
+2].length
= strlen("--sm-save-file") + 1;
353 vals
[i
+3].value
= ob_sm_save_file
;
354 vals
[i
+3].length
= strlen(ob_sm_save_file
) + 1;
355 ob_debug_type(OB_DEBUG_SM
, " %s\n", vals
[i
+2].value
);
356 ob_debug_type(OB_DEBUG_SM
, " %s\n", vals
[i
+3].value
);
358 SmcSetProperties(sm_conn
, 1, &list
);
361 g_free(vals
[i
].value
);
362 g_free(vals
[i
+2].value
);
366 static ObSMSaveData
*sm_save_get_data()
368 ObSMSaveData
*savedata
= g_new0(ObSMSaveData
, 1);
369 /* save the active desktop and client.
370 we don't bother to preemptively save the other desktop state like
371 number and names of desktops, cuz those shouldn't be changing during
373 savedata
->focus_client
= focus_client
;
374 savedata
->desktop
= screen_desktop
;
378 static void sm_save_yourself_2(SmcConn conn
, SmPointer data
)
381 ObSMSaveData
*savedata
= data
;
383 /* save the current state */
384 ob_debug_type(OB_DEBUG_SM
, "Session save phase 2 requested\n");
385 ob_debug_type(OB_DEBUG_SM
,
386 " Saving session to file '%s'\n", ob_sm_save_file
);
387 if (savedata
== NULL
)
388 savedata
= sm_save_get_data();
389 success
= session_save_to_file(savedata
);
392 /* tell the session manager how to restore this state */
393 if (success
) session_setup_restart_command();
395 ob_debug_type(OB_DEBUG_SM
, "Saving is done (success = %d)\n", success
);
396 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
;
406 ob_debug_type(OB_DEBUG_SM
, "Session save requested\n");
408 vendor
= SmcVendor(sm_conn
);
409 ob_debug_type(OB_DEBUG_SM
, "Session manager's vendor: %s\n", vendor
);
411 if (!strcmp(vendor
, "KDE")) {
412 /* ksmserver guarantees that phase 1 will complete before allowing any
413 clients interaction, so we can save this sanely here before they
414 get messed up from interaction */
415 savedata
= sm_save_get_data();
419 if (!SmcRequestSaveYourselfPhase2(conn
, sm_save_yourself_2
, savedata
)) {
420 ob_debug_type(OB_DEBUG_SM
, "Requst for phase 2 failed\n");
422 SmcSaveYourselfDone(conn
, FALSE
);
426 static void sm_die(SmcConn conn
, SmPointer data
)
428 ob_debug_type(OB_DEBUG_SM
, "Die requested\n");
432 static void sm_save_complete(SmcConn conn
, SmPointer data
)
434 ob_debug_type(OB_DEBUG_SM
, "Save complete\n");
437 static void sm_shutdown_cancelled(SmcConn conn
, SmPointer data
)
439 ob_debug_type(OB_DEBUG_SM
, "Shutdown cancelled\n");
442 static gboolean
session_save_to_file(const ObSMSaveData
*savedata
)
446 gboolean success
= TRUE
;
448 f
= fopen(ob_sm_save_file
, "w");
451 g_message(_("Unable to save the session to '%s': %s"),
452 ob_sm_save_file
, g_strerror(errno
));
454 fprintf(f
, "<?xml version=\"1.0\"?>\n\n");
455 fprintf(f
, "<openbox_session>\n\n");
457 fprintf(f
, "<desktop>%d</desktop>\n", savedata
->desktop
);
459 fprintf(f
, "<numdesktops>%d</numdesktops>\n", screen_num_desktops
);
461 fprintf(f
, "<desktoplayout>\n");
462 fprintf(f
, " <orientation>%d</orientation>\n",
463 screen_desktop_layout
.orientation
);
464 fprintf(f
, " <startcorner>%d</startcorner>\n",
465 screen_desktop_layout
.start_corner
);
466 fprintf(f
, " <columns>%d</columns>\n",
467 screen_desktop_layout
.columns
);
468 fprintf(f
, " <rows>%d</rows>\n",
469 screen_desktop_layout
.rows
);
470 fprintf(f
, "</desktoplayout>\n");
472 if (screen_desktop_names
) {
475 fprintf(f
, "<desktopnames>\n");
476 for (i
= 0; screen_desktop_names
[i
]; ++i
)
477 fprintf(f
, " <name>%s</name>\n", screen_desktop_names
[i
]);
478 fprintf(f
, "</desktopnames>\n");
481 /* they are ordered top to bottom in stacking order */
482 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
483 gint prex
, prey
, prew
, preh
;
487 if (WINDOW_IS_CLIENT(it
->data
))
488 c
= WINDOW_AS_CLIENT(it
->data
);
492 if (!client_normal(c
))
495 if (!c
->sm_client_id
) {
496 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have a "
499 if (!c
->wm_command
) {
500 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have an "
501 "oldskool wm_command set either. We won't "
502 "be saving its data\n",
508 ob_debug_type(OB_DEBUG_SM
, "Saving state for client %s\n",
513 prew
= c
->area
.width
;
514 preh
= c
->area
.height
;
516 prex
= c
->pre_fullscreen_area
.x
;
517 prey
= c
->pre_fullscreen_area
.x
;
518 prew
= c
->pre_fullscreen_area
.width
;
519 preh
= c
->pre_fullscreen_area
.height
;
522 prex
= c
->pre_max_area
.x
;
523 prew
= c
->pre_max_area
.width
;
526 prey
= c
->pre_max_area
.y
;
527 preh
= c
->pre_max_area
.height
;
531 fprintf(f
, "<window id=\"%s\">\n", c
->sm_client_id
);
533 fprintf(f
, "<window command=\"%s\">\n", c
->wm_command
);
535 t
= g_markup_escape_text(c
->name
, -1);
536 fprintf(f
, "\t<name>%s</name>\n", t
);
539 t
= g_markup_escape_text(c
->class, -1);
540 fprintf(f
, "\t<class>%s</class>\n", t
);
543 t
= g_markup_escape_text(c
->role
, -1);
544 fprintf(f
, "\t<role>%s</role>\n", t
);
547 fprintf(f
, "\t<windowtype>%d</windowtype>\n", c
->type
);
549 fprintf(f
, "\t<desktop>%d</desktop>\n", c
->desktop
);
550 fprintf(f
, "\t<x>%d</x>\n", prex
);
551 fprintf(f
, "\t<y>%d</y>\n", prey
);
552 fprintf(f
, "\t<width>%d</width>\n", prew
);
553 fprintf(f
, "\t<height>%d</height>\n", preh
);
555 fprintf(f
, "\t<shaded />\n");
557 fprintf(f
, "\t<iconic />\n");
559 fprintf(f
, "\t<skip_pager />\n");
561 fprintf(f
, "\t<skip_taskbar />\n");
563 fprintf(f
, "\t<fullscreen />\n");
565 fprintf(f
, "\t<above />\n");
567 fprintf(f
, "\t<below />\n");
569 fprintf(f
, "\t<max_horz />\n");
571 fprintf(f
, "\t<max_vert />\n");
573 fprintf(f
, "\t<undecorated />\n");
574 if (savedata
->focus_client
== c
)
575 fprintf(f
, "\t<focused />\n");
576 fprintf(f
, "</window>\n\n");
579 fprintf(f
, "</openbox_session>\n");
583 g_message(_("Error while saving the session to '%s': %s"),
584 ob_sm_save_file
, g_strerror(errno
));
592 static void session_state_free(ObSessionState
*state
)
596 g_free(state
->command
);
598 g_free(state
->class);
605 static gboolean
session_state_cmp(ObSessionState
*s
, ObClient
*c
)
607 ob_debug_type(OB_DEBUG_SM
, "Comparing client against saved state: \n");
608 ob_debug_type(OB_DEBUG_SM
, " client id: %s \n", c
->sm_client_id
);
609 ob_debug_type(OB_DEBUG_SM
, " client name: %s \n", c
->name
);
610 ob_debug_type(OB_DEBUG_SM
, " client class: %s \n", c
->class);
611 ob_debug_type(OB_DEBUG_SM
, " client role: %s \n", c
->role
);
612 ob_debug_type(OB_DEBUG_SM
, " client type: %d \n", c
->type
);
613 ob_debug_type(OB_DEBUG_SM
, " client command: %s \n",
614 c
->wm_command
? c
->wm_command
: "(null)");
615 ob_debug_type(OB_DEBUG_SM
, " state id: %s \n", s
->id
);
616 ob_debug_type(OB_DEBUG_SM
, " state name: %s \n", s
->name
);
617 ob_debug_type(OB_DEBUG_SM
, " state class: %s \n", s
->class);
618 ob_debug_type(OB_DEBUG_SM
, " state role: %s \n", s
->role
);
619 ob_debug_type(OB_DEBUG_SM
, " state type: %d \n", s
->type
);
620 ob_debug_type(OB_DEBUG_SM
, " state command: %s \n",
621 s
->command
? s
->command
: "(null)");
623 if ((c
->sm_client_id
&& s
->id
&& !strcmp(c
->sm_client_id
, s
->id
)) ||
624 (c
->wm_command
&& s
->command
&& !strcmp(c
->wm_command
, s
->command
)))
626 return (!strcmp(s
->name
, c
->name
) &&
627 !strcmp(s
->class, c
->class) &&
628 !strcmp(s
->role
, c
->role
) &&
629 /* the check for type is to catch broken clients, like
630 firefox, which open a different window on startup
631 with the same info as the one we saved. only do this
632 check for old windows that dont use xsmp, others should
634 (!s
->command
|| c
->type
== s
->type
));
639 GList
* session_state_find(ObClient
*c
)
643 for (it
= session_saved_state
; it
; it
= g_list_next(it
)) {
644 ObSessionState
*s
= it
->data
;
645 if (!s
->matched
&& session_state_cmp(s
, c
)) {
653 static void session_load_file(const gchar
*path
)
656 xmlNodePtr node
, n
, m
;
659 if (!parse_load(path
, "openbox_session", &doc
, &node
))
662 if ((n
= parse_find_node("desktop", node
->children
)))
663 session_desktop
= parse_int(doc
, n
);
665 if ((n
= parse_find_node("numdesktops", node
->children
)))
666 session_num_desktops
= parse_int(doc
, n
);
668 if ((n
= parse_find_node("desktoplayout", node
->children
))) {
669 /* make sure they are all there for it to be valid */
670 if ((m
= parse_find_node("orientation", n
->children
)))
671 session_desktop_layout
.orientation
= parse_int(doc
, m
);
672 if (m
&& (m
= parse_find_node("startcorner", n
->children
)))
673 session_desktop_layout
.start_corner
= parse_int(doc
, m
);
674 if (m
&& (m
= parse_find_node("columns", n
->children
)))
675 session_desktop_layout
.columns
= parse_int(doc
, m
);
676 if (m
&& (m
= parse_find_node("rows", n
->children
)))
677 session_desktop_layout
.rows
= parse_int(doc
, m
);
678 session_desktop_layout_present
= m
!= NULL
;
681 if ((n
= parse_find_node("desktopnames", node
->children
))) {
682 for (m
= parse_find_node("name", n
->children
); m
;
683 m
= parse_find_node("name", m
->next
))
685 session_desktop_names
= g_slist_append(session_desktop_names
,
686 parse_string(doc
, m
));
690 for (node
= parse_find_node("window", node
->children
); node
!= NULL
;
691 node
= parse_find_node("window", node
->next
))
693 ObSessionState
*state
;
695 state
= g_new0(ObSessionState
, 1);
697 if (!parse_attr_string("id", node
, &state
->id
))
698 if (!parse_attr_string("command", node
, &state
->command
))
699 goto session_load_bail
;
700 if (!(n
= parse_find_node("name", node
->children
)))
701 goto session_load_bail
;
702 state
->name
= parse_string(doc
, n
);
703 if (!(n
= parse_find_node("class", node
->children
)))
704 goto session_load_bail
;
705 state
->class = parse_string(doc
, n
);
706 if (!(n
= parse_find_node("role", node
->children
)))
707 goto session_load_bail
;
708 state
->role
= parse_string(doc
, n
);
709 if (!(n
= parse_find_node("windowtype", node
->children
)))
710 goto session_load_bail
;
711 state
->type
= parse_int(doc
, n
);
712 if (!(n
= parse_find_node("desktop", node
->children
)))
713 goto session_load_bail
;
714 state
->desktop
= parse_int(doc
, n
);
715 if (!(n
= parse_find_node("x", node
->children
)))
716 goto session_load_bail
;
717 state
->x
= parse_int(doc
, n
);
718 if (!(n
= parse_find_node("y", node
->children
)))
719 goto session_load_bail
;
720 state
->y
= parse_int(doc
, n
);
721 if (!(n
= parse_find_node("width", node
->children
)))
722 goto session_load_bail
;
723 state
->w
= parse_int(doc
, n
);
724 if (!(n
= parse_find_node("height", node
->children
)))
725 goto session_load_bail
;
726 state
->h
= parse_int(doc
, n
);
729 parse_find_node("shaded", node
->children
) != NULL
;
731 parse_find_node("iconic", node
->children
) != NULL
;
733 parse_find_node("skip_pager", node
->children
) != NULL
;
734 state
->skip_taskbar
=
735 parse_find_node("skip_taskbar", node
->children
) != NULL
;
737 parse_find_node("fullscreen", node
->children
) != NULL
;
739 parse_find_node("above", node
->children
) != NULL
;
741 parse_find_node("below", node
->children
) != NULL
;
743 parse_find_node("max_horz", node
->children
) != NULL
;
745 parse_find_node("max_vert", node
->children
) != NULL
;
747 parse_find_node("undecorated", node
->children
) != NULL
;
749 parse_find_node("focused", node
->children
) != NULL
;
751 /* save this. they are in the file in stacking order, so preserve
753 session_saved_state
= g_list_append(session_saved_state
, state
);
757 session_state_free(state
);
760 /* Remove any duplicates. This means that if two windows (or more) are
761 saved with the same session state, we won't restore a session for any
762 of them because we don't know what window to put what on. AHEM FIREFOX.
764 This is going to be an O(2^n) kind of operation unfortunately.
766 for (it
= session_saved_state
; it
; it
= inext
) {
768 gboolean founddup
= FALSE
;
769 ObSessionState
*s1
= it
->data
;
771 inext
= g_list_next(it
);
773 for (jt
= g_list_next(it
); jt
; jt
= jnext
) {
774 ObSessionState
*s2
= jt
->data
;
777 jnext
= g_list_next(jt
);
779 if (s1
->id
&& s2
->id
)
780 match
= strcmp(s1
->id
, s2
->id
) == 0;
781 else if (s1
->command
&& s2
->command
)
782 match
= strcmp(s1
->command
, s2
->command
) == 0;
787 !strcmp(s1
->name
, s2
->name
) &&
788 !strcmp(s1
->class, s2
->class) &&
789 !strcmp(s1
->role
, s2
->role
))
791 session_state_free(s2
);
792 session_saved_state
=
793 g_list_delete_link(session_saved_state
, jt
);
799 session_state_free(s1
);
800 session_saved_state
= g_list_delete_link(session_saved_state
, it
);