use the path properly for the session file
[dana/openbox.git] / openbox / session.c
1 /* This session code is largely inspired by metacity code. */
2
3 #ifndef USE_SM
4
5 void session_load(char *path) {}
6 void session_startup(int argc, char **argv) {}
7 void session_shutdown() {}
8
9 #else
10
11 #include "debug.h"
12 #include "openbox.h"
13 #include "session.h"
14 #include "client.h"
15 #include "prop.h"
16 #include "parser/parse.h"
17
18 #include <time.h>
19 #include <errno.h>
20 #include <stdio.h>
21
22 #ifdef HAVE_UNISTD_H
23 #  include <sys/types.h>
24 #  include <unistd.h>
25 #endif
26
27 #include <X11/SM/SMlib.h>
28
29 static SmcConn     sm_conn;
30 static gchar      *save_file;
31 static gint        sm_argc;
32 static gchar     **sm_argv;
33 static void sm_save_yourself(SmcConn conn, SmPointer data, int save_type,
34                              Bool shutdown, int interact_style, Bool fast);
35 static void sm_die(SmcConn conn, SmPointer data);
36 static void sm_save_complete(SmcConn conn, SmPointer data);
37 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data);
38
39 static void save_commands()
40 {
41     SmProp *props[2];
42     SmProp prop_cmd = { SmCloneCommand, SmLISTofARRAY8, 1, };
43     SmProp prop_res = { SmRestartCommand, SmLISTofARRAY8, };
44     gint i, j, n;
45     gboolean has_id = FALSE, has_file = FALSE;
46
47     for (i = 1; !has_id && !has_file && i < sm_argc - 1; ++i) {
48         if (!has_id && strcmp(sm_argv[i], "--sm-client-id") == 0)
49             has_id = TRUE;
50         if (!has_file && strcmp(sm_argv[i], "--sm-save-file") == 0)
51             has_file = TRUE;
52     }
53
54     n = (has_file ? sm_argc-2 : sm_argc);
55     n = (has_id ? n-2 : n);
56     prop_cmd.vals = g_new(SmPropValue, n);
57     prop_cmd.num_vals = n;
58     for (i = 0, j = 0; i < sm_argc; ++i, ++j) {
59         if (strcmp (sm_argv[i], "--sm-client-id") == 0 ||
60             strcmp (sm_argv[i], "--sm-save-file") == 0) {
61             ++i, --j; /* skip the next as well, keep j where it is */
62         } else {
63             prop_cmd.vals[j].value = sm_argv[i];
64             prop_cmd.vals[j].length = strlen(sm_argv[i]);
65         }
66     }
67
68     n = (has_file ? sm_argc : sm_argc+2);
69     n = (has_id ? n-2 : n);
70     prop_res.vals = g_new(SmPropValue, n);
71     prop_res.num_vals = n;
72     for (i = 0, j = 0; i < sm_argc; ++i, ++j) { 
73         if (strcmp (sm_argv[i], "--sm-client-id") == 0 ||
74             strcmp (sm_argv[i], "--sm-save-file") == 0) {
75             ++i, --j; /* skip the next as well, keep j where it is */
76         } else {
77             prop_res.vals[j].value = sm_argv[i];
78             prop_res.vals[j].length = strlen(sm_argv[i]);
79         }
80     }
81
82     prop_res.vals[j].value = "--sm-save-file";
83     prop_res.vals[j++].length = strlen("--sm-save-file");
84     prop_res.vals[j].value = save_file;
85     prop_res.vals[j++].length = strlen(save_file);
86
87     props[0] = &prop_res;
88     props[1] = &prop_cmd;
89     SmcSetProperties(sm_conn, 1, props);
90
91     g_free(prop_res.vals);
92     g_free(prop_cmd.vals);
93 }
94
95 void session_startup(int argc, char **argv)
96 {
97 #define SM_ERR_LEN 1024
98
99     SmcCallbacks cb;
100     char sm_err[SM_ERR_LEN];
101
102     sm_argc = argc;
103     sm_argv = argv;
104
105     cb.save_yourself.callback = sm_save_yourself;
106     cb.save_yourself.client_data = NULL;
107
108     cb.die.callback = sm_die;
109     cb.die.client_data = NULL;
110
111     cb.save_complete.callback = sm_save_complete;
112     cb.save_complete.client_data = NULL;
113
114     cb.shutdown_cancelled.callback = sm_shutdown_cancelled;
115     cb.shutdown_cancelled.client_data = NULL;
116
117     sm_conn = SmcOpenConnection(NULL, NULL, 1, 0,
118                                 SmcSaveYourselfProcMask |
119                                 SmcDieProcMask |
120                                 SmcSaveCompleteProcMask |
121                                 SmcShutdownCancelledProcMask,
122                                 &cb, ob_sm_id, &ob_sm_id,
123                                 SM_ERR_LEN, sm_err);
124     if (sm_conn == NULL)
125         g_warning("Failed to connect to session manager: %s", sm_err);
126     else {
127         SmPropValue val_prog;
128         SmPropValue val_uid;
129         SmPropValue val_hint; 
130         SmPropValue val_pri;
131         SmPropValue val_pid;
132         SmProp prop_prog = { SmProgram, SmARRAY8, 1, };
133         SmProp prop_uid = { SmUserID, SmARRAY8, 1, };
134         SmProp prop_hint = { SmRestartStyleHint, SmCARD8, 1, };
135         SmProp prop_pid = { SmProcessID, SmARRAY8, 1, };
136         SmProp prop_pri = { "_GSM_Priority", SmCARD8, 1, };
137         SmProp *props[6];
138         gulong hint, pri;
139         gchar pid[32];
140
141         val_prog.value = argv[0];
142         val_prog.length = strlen(argv[0]);
143
144         val_uid.value = g_strdup(g_get_user_name());
145         val_uid.length = strlen(val_uid.value);
146
147         hint = SmRestartImmediately;
148         val_hint.value = &hint;
149         val_hint.length = 1;
150
151         sprintf(pid, "%ld", (long)getpid());
152         val_pid.value = pid;
153         val_pid.length = strlen(pid);
154
155         /* priority with gnome-session-manager, low to run before other apps */
156         pri = 20;
157         val_pri.value = &pri;
158         val_pri.length = 1;
159
160         prop_prog.vals = &val_prog;
161         prop_uid.vals = &val_uid;
162         prop_hint.vals = &val_hint;
163         prop_pid.vals = &val_pid;
164         prop_pri.vals = &val_pri;
165
166         props[0] = &prop_prog;
167         props[1] = &prop_uid;
168         props[2] = &prop_hint;
169         props[3] = &prop_pid;
170         props[4] = &prop_pri;
171
172         SmcSetProperties(sm_conn, 5, props);
173
174         g_free(val_uid.value);
175
176         ob_debug("Connected to session manager with id %s\n", ob_sm_id);
177     }
178 }
179
180 void session_shutdown()
181 {
182     g_free(save_file);
183
184     if (sm_conn) {
185         SmPropValue val_hint;
186         SmProp prop_hint = { SmRestartStyleHint, SmCARD8, 1, };
187         SmProp *props[1];
188         gulong hint;
189
190         /* when we exit, we want to reset this to a more friendly state */
191         hint = SmRestartIfRunning;
192         val_hint.value = &hint;
193         val_hint.length = 1;
194
195         prop_hint.vals = &val_hint;
196
197         props[0] = &prop_hint;
198
199         SmcSetProperties(sm_conn, 1, props);
200
201         SmcCloseConnection(sm_conn, 0, NULL);
202     }
203 }
204
205 static void sm_save_yourself(SmcConn conn, SmPointer data, int save_type,
206                              Bool shutdown, int interact_style, Bool fast)
207 {
208     gchar *filename;
209     FILE *f;
210     GList *it;
211     gboolean success = TRUE;
212
213     ob_debug("got SAVE YOURSELF from session manager\n");
214
215     /* this algo is from metacity */
216     filename = g_strdup_printf("%d-%d-%u.obs",
217                                (int) time(NULL),
218                                (int) getpid(),
219                                g_random_int());
220     save_file = g_build_filename(g_get_home_dir(), ".openbox", "sessions",
221                                  filename, NULL);
222     g_free(filename);
223
224     f = fopen(save_file, "w");
225     if (!f) {
226         success = FALSE;
227         g_warning("unable to save the session to %s: %s",
228                   save_file, strerror(errno));
229     } else {
230         fprintf(f, "<?xml version=\"1.0\"?>\n\n");
231         fprintf(f, "<openbox_session id=\"%s\">\n\n", ob_sm_id);
232
233         for (it = client_list; it; it = g_list_next(it)) {
234             guint num;
235             gint32 *dimensions;
236             gint prex, prey, prew, preh;
237             ObClient *c = it->data;
238
239             if (!client_normal(c))
240                 continue;
241
242             prex = c->area.x;
243             prey = c->area.y;
244             prew = c->area.width;
245             preh = c->area.height;
246             if (PROP_GETA32(c->window, openbox_premax, cardinal,
247                             (guint32**)&dimensions, &num)) {
248                 if (num == 4) {
249                     prex = dimensions[0];
250                     prey = dimensions[1];
251                     prew = dimensions[2];
252                     preh = dimensions[3];
253                 }
254                 g_free(dimensions);
255             }
256
257             fprintf(f, "\t<window id=\"%s\">\n",
258                     g_markup_escape_text("XXX", -1));
259             fprintf(f, "\t\t<name>%s</name>\n",
260                     g_markup_escape_text(c->name, -1));
261             fprintf(f, "\t\t<class>%s</class>\n",
262                     g_markup_escape_text(c->class, -1));
263             fprintf(f, "\t\t<role>%s</role>\n",
264                     g_markup_escape_text(c->role, -1));
265             fprintf(f, "\t\t<desktop>%d</desktop>\n", c->desktop);
266             fprintf(f, "\t\t<x>%d</x>\n", prex);
267             fprintf(f, "\t\t<y>%d</y>\n", prey);
268             fprintf(f, "\t\t<width>%d</width>\n", prew);
269             fprintf(f, "\t\t<height>%d</height>\n", preh);
270             if (c->shaded)
271                 fprintf(f, "\t\t<shaded />\n");
272             if (c->iconic)
273                 fprintf(f, "\t\t<iconic />\n");
274             if (c->skip_pager)
275                 fprintf(f, "\t\t<skip_pager />\n");
276             if (c->skip_taskbar)
277                 fprintf(f, "\t\t<skip_taskbar />\n");
278             if (c->fullscreen)
279                 fprintf(f, "\t\t<fullscreen />\n");
280             if (c->above)
281                 fprintf(f, "\t\t<above />\n");
282             if (c->below)
283                 fprintf(f, "\t\t<below />\n");
284             if (c->max_horz)
285                 fprintf(f, "\t\t<max_horz />\n");
286             if (c->max_vert)
287                 fprintf(f, "\t\t<max_vert />\n");
288             fprintf(f, "\t</window>\n\n");
289         }
290
291         fprintf(f, "</openbox_session>\n");
292
293         if (fflush(f)) {
294             success = FALSE;
295             g_warning("error while saving the session to %s: %s",
296                       save_file, strerror(errno));
297         }
298         fclose(f);
299     }
300
301     save_commands();
302
303     SmcSaveYourselfDone(conn, success);
304 }
305
306 static void sm_die(SmcConn conn, SmPointer data)
307 {
308     ob_exit();
309     ob_debug("got DIE from session manager\n");
310 }
311
312 static void sm_save_complete(SmcConn conn, SmPointer data)
313 {
314     ob_debug("got SAVE COMPLETE from session manager\n");
315 }
316
317 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
318 {
319     ob_debug("got SHUTDOWN CANCELLED from session manager\n");
320 }
321
322 void session_load(char *path)
323 {
324     xmlDocPtr doc;
325     xmlNodePtr node, n;
326     gchar *sm_id;
327
328     if (!parse_load(path, "openbox_session", &doc, &node))
329         return;
330
331     if (!parse_attr_string("id", node, &sm_id))
332         return;
333     ob_sm_id = g_strdup(sm_id);
334
335     node = parse_find_node("window", node->xmlChildrenNode);
336     while (node) {
337         gchar *id, *name, *class, *role;
338         guint desktop;
339         gint x, y, w, h;
340         gboolean shaded, iconic, skip_pager, skip_taskbar, fullscreen;
341         gboolean above, below, max_horz, max_vert;
342
343         if (!parse_attr_string("id", node, &id))
344             goto session_load_bail;
345         if (!(n = parse_find_node("name", node->xmlChildrenNode)))
346             goto session_load_bail;
347         name = parse_string(doc, n);
348         if (!(n = parse_find_node("class", node->xmlChildrenNode)))
349             goto session_load_bail;
350         class = parse_string(doc, n);
351         if (!(n = parse_find_node("role", node->xmlChildrenNode)))
352             goto session_load_bail;
353         role = parse_string(doc, n);
354         if (!(n = parse_find_node("desktop", node->xmlChildrenNode)))
355             goto session_load_bail;
356         desktop = parse_int(doc, n);
357         if (!(n = parse_find_node("x", node->xmlChildrenNode)))
358             goto session_load_bail;
359         x = parse_int(doc, n);
360         if (!(n = parse_find_node("y", node->xmlChildrenNode)))
361             goto session_load_bail;
362         y = parse_int(doc, n);
363         if (!(n = parse_find_node("width", node->xmlChildrenNode)))
364             goto session_load_bail;
365         w = parse_int(doc, n);
366         if (!(n = parse_find_node("height", node->xmlChildrenNode)))
367             goto session_load_bail;
368         h = parse_int(doc, n);
369
370         shaded = parse_find_node("shaded", node->xmlChildrenNode) != NULL;
371         iconic = parse_find_node("iconic", node->xmlChildrenNode) != NULL;
372         skip_pager = parse_find_node("skip_pager", node->xmlChildrenNode)
373             != NULL;
374         skip_taskbar = parse_find_node("skip_taskbar", node->xmlChildrenNode)
375             != NULL;
376         fullscreen = parse_find_node("fullscreen", node->xmlChildrenNode)
377             != NULL;
378         above = parse_find_node("above", node->xmlChildrenNode) != NULL;
379         below = parse_find_node("below", node->xmlChildrenNode) != NULL;
380         max_horz = parse_find_node("max_horz", node->xmlChildrenNode) != NULL;
381         max_vert = parse_find_node("max_vert", node->xmlChildrenNode) != NULL;
382         
383         g_message("read session window %s", name);
384
385         /* XXX save this */
386
387     session_load_bail:
388
389         node = parse_find_node("window", node->next);
390     }
391
392     xmlFreeDoc(doc);
393
394     unlink(path);
395 }
396
397 #endif