]> Dogcows Code - chaz/openbox/blob - openbox/session.c
refactor all the session stuff. yay, it works properly now.
[chaz/openbox] / openbox / session.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 session.c for the Openbox window manager
4 Copyright (c) 2003-2007 Dana Jansens
5
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.
10
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.
15
16 See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 /* This session code is largely inspired by metacity code. */
20
21 #include "session.h"
22
23 struct _ObClient;
24
25 GList *session_saved_state = NULL;
26 gint session_desktop = -1;
27
28 #ifndef USE_SM
29 void session_startup(gint argc, gchar **argv) {}
30 void session_shutdown(gboolean permanent) {}
31 GList* session_state_find(struct _ObClient *c) { return NULL; }
32 #else
33
34 #include "debug.h"
35 #include "openbox.h"
36 #include "client.h"
37 #include "prop.h"
38 #include "screen.h"
39 #include "gettext.h"
40 #include "parser/parse.h"
41
42 #include <time.h>
43 #include <errno.h>
44 #include <stdio.h>
45
46 #ifdef HAVE_UNISTD_H
47 # include <sys/types.h>
48 # include <unistd.h>
49 #endif
50
51 #include <X11/SM/SMlib.h>
52
53 #define SM_ERR_LEN 1024
54
55 static SmcConn sm_conn;
56 static gint sm_argc;
57 static gchar **sm_argv;
58
59 static gboolean session_connect();
60
61 static void session_load_file(gchar *path);
62 static gboolean session_save_to_file();
63
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();
71
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);
77
78 static gboolean session_state_cmp(ObSessionState *s, ObClient *c);
79 static void session_state_free(ObSessionState *state);
80
81 void session_startup(gint argc, gchar **argv)
82 {
83 gchar *dir;
84
85 if (!ob_sm_use) return;
86
87 sm_argc = argc;
88 sm_argv = argv;
89
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));
95 }
96
97 if (ob_sm_save_file != NULL) {
98 ob_debug_type(OB_DEBUG_SM, "Loading from session file %s\n",
99 ob_sm_save_file);
100 session_load_file(ob_sm_save_file);
101 } else {
102 gchar *filename;
103
104 /* this algo is from metacity */
105 filename = g_strdup_printf("%u-%u-%u.obs",
106 (guint)time(NULL),
107 (guint)getpid(),
108 g_random_int());
109 ob_sm_save_file = g_build_filename(dir, filename, NULL);
110 g_free(filename);
111 }
112
113 if (session_connect()) {
114 session_setup_program();
115 session_setup_user();
116 session_setup_restart_style(TRUE);
117 session_setup_pid();
118 session_setup_priority();
119 session_setup_clone_command();
120 }
121
122 g_free(dir);
123 }
124
125 void session_shutdown(gboolean permanent)
126 {
127 if (!ob_sm_use) return;
128
129 if (sm_conn) {
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);
133
134 SmcCloseConnection(sm_conn, 0, NULL);
135
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);
140 }
141 }
142 }
143
144 /*! Connect to the session manager and set up our callback functions */
145 static gboolean session_connect()
146 {
147 SmcCallbacks cb;
148 gchar *oldid;
149 gchar sm_err[SM_ERR_LEN];
150
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;
160
161 /* connect to the server */
162 oldid = ob_sm_id;
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 |
167 SmcDieProcMask |
168 SmcSaveCompleteProcMask |
169 SmcShutdownCancelledProcMask,
170 &cb, oldid, &ob_sm_id,
171 SM_ERR_LEN, sm_err);
172 g_free(oldid);
173 if (sm_conn == NULL)
174 ob_debug("Failed to connect to session manager: %s\n", sm_err);
175 return sm_conn != NULL;
176 }
177
178 static void session_setup_program()
179 {
180 SmPropValue vals = {
181 .value = sm_argv[0],
182 .length = strlen(sm_argv[0]) + 1
183 };
184 SmProp prop = {
185 .name = g_strdup(SmProgram),
186 .type = g_strdup(SmARRAY8),
187 .num_vals = 1,
188 .vals = &vals
189 };
190 SmProp *list = &prop;
191 SmcSetProperties(sm_conn, 1, &list);
192 g_free(prop.name);
193 g_free(prop.type);
194 }
195
196 static void session_setup_user()
197 {
198 char *user = g_strdup(g_get_user_name());
199
200 SmPropValue vals = {
201 .value = user,
202 .length = strlen(user) + 1
203 };
204 SmProp prop = {
205 .name = g_strdup(SmUserID),
206 .type = g_strdup(SmARRAY8),
207 .num_vals = 1,
208 .vals = &vals
209 };
210 SmProp *list = &prop;
211 SmcSetProperties(sm_conn, 1, &list);
212 g_free(prop.name);
213 g_free(prop.type);
214 g_free(user);
215 }
216
217 static void session_setup_restart_style(gboolean restart)
218 {
219 char restart_hint = restart ? SmRestartImmediately : SmRestartIfRunning;
220
221 SmPropValue vals = {
222 .value = &restart_hint,
223 .length = 1
224 };
225 SmProp prop = {
226 .name = g_strdup(SmRestartStyleHint),
227 .type = g_strdup(SmCARD8),
228 .num_vals = 1,
229 .vals = &vals
230 };
231 SmProp *list = &prop;
232 SmcSetProperties(sm_conn, 1, &list);
233 g_free(prop.name);
234 g_free(prop.type);
235 }
236
237 static void session_setup_pid()
238 {
239 gchar *pid = g_strdup_printf("%ld", (glong) getpid());
240
241 SmPropValue vals = {
242 .value = pid,
243 .length = strlen(pid) + 1
244 };
245 SmProp prop = {
246 .name = g_strdup(SmProcessID),
247 .type = g_strdup(SmARRAY8),
248 .num_vals = 1,
249 .vals = &vals
250 };
251 SmProp *list = &prop;
252 SmcSetProperties(sm_conn, 1, &list);
253 g_free(prop.name);
254 g_free(prop.type);
255 g_free(pid);
256 }
257
258 /*! This is a gnome-session-manager extension */
259 static void session_setup_priority()
260 {
261 gchar priority = 20; /* 20 is a lower prioity to run before other apps */
262
263 SmPropValue vals = {
264 .value = &priority,
265 .length = 1
266 };
267 SmProp prop = {
268 .name = g_strdup("_GSM_Priority"),
269 .type = g_strdup(SmCARD8),
270 .num_vals = 1,
271 .vals = &vals
272 };
273 SmProp *list = &prop;
274 SmcSetProperties(sm_conn, 1, &list);
275 g_free(prop.name);
276 g_free(prop.type);
277 }
278
279 static void session_setup_clone_command()
280 {
281 gint i;
282
283 SmPropValue *vals = g_new(SmPropValue, sm_argc);
284 SmProp prop = {
285 .name = g_strdup(SmCloneCommand),
286 .type = g_strdup(SmLISTofARRAY8),
287 .num_vals = sm_argc,
288 .vals = vals
289 };
290
291 for (i = 0; i < sm_argc; ++i) {
292 vals[i].value = sm_argv[i];
293 vals[i].length = strlen(sm_argv[i]) + 1;
294 }
295
296 SmProp *list = &prop;
297 SmcSetProperties(sm_conn, 1, &list);
298 g_free(prop.name);
299 g_free(prop.type);
300 g_free(vals);
301 }
302
303 static void session_setup_restart_command()
304 {
305 gint i;
306
307 SmPropValue *vals = g_new(SmPropValue, sm_argc + 4);
308 SmProp prop = {
309 .name = g_strdup(SmRestartCommand),
310 .type = g_strdup(SmLISTofARRAY8),
311 .num_vals = sm_argc + 4,
312 .vals = vals
313 };
314
315 for (i = 0; i < sm_argc; ++i) {
316 vals[i].value = sm_argv[i];
317 vals[i].length = strlen(sm_argv[i]) + 1;
318 }
319
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;
324
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;
329
330 SmProp *list = &prop;
331 SmcSetProperties(sm_conn, 1, &list);
332 g_free(prop.name);
333 g_free(prop.type);
334 g_free(vals[i].value);
335 g_free(vals[i+2].value);
336 g_free(vals);
337 }
338
339 static void sm_save_yourself_2(SmcConn conn, SmPointer data)
340 {
341 gboolean success;
342
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();
348
349 /* tell the session manager how to restore this state */
350 if (success) session_setup_restart_command();
351
352 ob_debug_type(OB_DEBUG_SM, "Saving is done (success = %d)\n", success);
353 SmcSaveYourselfDone(conn, success);
354 }
355
356
357 static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type,
358 Bool shutdown, gint interact_style, Bool fast)
359 {
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);
364 }
365 }
366
367 static void sm_die(SmcConn conn, SmPointer data)
368 {
369 ob_debug_type(OB_DEBUG_SM, "Die requested\n");
370 ob_exit(0);
371 }
372
373 static void sm_save_complete(SmcConn conn, SmPointer data)
374 {
375 ob_debug_type(OB_DEBUG_SM, "Save complete\n");
376 }
377
378 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
379 {
380 ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled\n");
381 }
382
383 static gboolean session_save_to_file()
384 {
385 FILE *f;
386 GList *it;
387 gboolean success = TRUE;
388
389 f = fopen(ob_sm_save_file, "w");
390 if (!f) {
391 success = FALSE;
392 g_message(_("Unable to save the session to '%s': %s"),
393 ob_sm_save_file, g_strerror(errno));
394 } else {
395 fprintf(f, "<?xml version=\"1.0\"?>\n\n");
396 fprintf(f, "<openbox_session>\n\n");
397
398 fprintf(f, "<desktop>%d</desktop>\n", screen_desktop);
399
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;
403 ObClient *c;
404 gchar *t;
405
406 if (WINDOW_IS_CLIENT(it->data))
407 c = WINDOW_AS_CLIENT(it->data);
408 else
409 continue;
410
411 if (!client_normal(c))
412 continue;
413
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",
417 c->title);
418 continue;
419 }
420
421 ob_debug_type(OB_DEBUG_SM, "Saving state for client %s\n",
422 c->title);
423
424 prex = c->area.x;
425 prey = c->area.y;
426 prew = c->area.width;
427 preh = c->area.height;
428 if (c->fullscreen) {
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;
433 }
434 if (c->max_horz) {
435 prex = c->pre_max_area.x;
436 prew = c->pre_max_area.width;
437 }
438 if (c->max_vert) {
439 prey = c->pre_max_area.y;
440 preh = c->pre_max_area.height;
441 }
442
443 fprintf(f, "<window id=\"%s\">\n", c->sm_client_id);
444
445 t = g_markup_escape_text(c->name, -1);
446 fprintf(f, "\t<name>%s</name>\n", t);
447 g_free(t);
448
449 t = g_markup_escape_text(c->class, -1);
450 fprintf(f, "\t<class>%s</class>\n", t);
451 g_free(t);
452
453 t = g_markup_escape_text(c->role, -1);
454 fprintf(f, "\t<role>%s</role>\n", t);
455 g_free(t);
456
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);
462 if (c->shaded)
463 fprintf(f, "\t<shaded />\n");
464 if (c->iconic)
465 fprintf(f, "\t<iconic />\n");
466 if (c->skip_pager)
467 fprintf(f, "\t<skip_pager />\n");
468 if (c->skip_taskbar)
469 fprintf(f, "\t<skip_taskbar />\n");
470 if (c->fullscreen)
471 fprintf(f, "\t<fullscreen />\n");
472 if (c->above)
473 fprintf(f, "\t<above />\n");
474 if (c->below)
475 fprintf(f, "\t<below />\n");
476 if (c->max_horz)
477 fprintf(f, "\t<max_horz />\n");
478 if (c->max_vert)
479 fprintf(f, "\t<max_vert />\n");
480 if (c->undecorated)
481 fprintf(f, "\t<undecorated />\n");
482 if (client_focused(c))
483 fprintf(f, "\t<focused />\n");
484 fprintf(f, "</window>\n\n");
485 }
486
487 fprintf(f, "</openbox_session>\n");
488
489 if (fflush(f)) {
490 success = FALSE;
491 g_message(_("Error while saving the session to '%s': %s"),
492 ob_sm_save_file, g_strerror(errno));
493 }
494 fclose(f);
495 }
496
497 return success;
498 }
499
500 static void session_state_free(ObSessionState *state)
501 {
502 if (state) {
503 g_free(state->id);
504 g_free(state->name);
505 g_free(state->class);
506 g_free(state->role);
507
508 g_free(state);
509 }
510 }
511
512 static gboolean session_state_cmp(ObSessionState *s, ObClient *c)
513 {
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));
528 }
529
530 GList* session_state_find(ObClient *c)
531 {
532 GList *it;
533
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)) {
537 s->matched = TRUE;
538 break;
539 }
540 }
541 return it;
542 }
543
544 static void session_load_file(gchar *path)
545 {
546 xmlDocPtr doc;
547 xmlNodePtr node, n;
548
549 if (!parse_load(path, "openbox_session", &doc, &node))
550 return;
551
552 if ((n = parse_find_node("desktop", node->children)))
553 session_desktop = parse_int(doc, n);
554
555 for (node = parse_find_node("window", node->children); node != NULL;
556 node = parse_find_node("window", node->next))
557 {
558 ObSessionState *state;
559
560 state = g_new0(ObSessionState, 1);
561
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);
588
589 state->shaded =
590 parse_find_node("shaded", node->children) != NULL;
591 state->iconic =
592 parse_find_node("iconic", node->children) != NULL;
593 state->skip_pager =
594 parse_find_node("skip_pager", node->children) != NULL;
595 state->skip_taskbar =
596 parse_find_node("skip_taskbar", node->children) != NULL;
597 state->fullscreen =
598 parse_find_node("fullscreen", node->children) != NULL;
599 state->above =
600 parse_find_node("above", node->children) != NULL;
601 state->below =
602 parse_find_node("below", node->children) != NULL;
603 state->max_horz =
604 parse_find_node("max_horz", node->children) != NULL;
605 state->max_vert =
606 parse_find_node("max_vert", node->children) != NULL;
607 state->undecorated =
608 parse_find_node("undecorated", node->children) != NULL;
609 state->focused =
610 parse_find_node("focused", node->children) != NULL;
611
612 /* save this. they are in the file in stacking order, so preserve
613 that order here */
614 session_saved_state = g_list_append(session_saved_state, state);
615 continue;
616
617 session_load_bail:
618 session_state_free(state);
619 }
620
621 xmlFreeDoc(doc);
622 }
623
624 #endif
This page took 0.065782 seconds and 5 git commands to generate.