]> git.lizzy.rs Git - bspwm.git/blob - restore.c
Fix off-by-one token pointer in restore_tree
[bspwm.git] / restore.c
1 /* Copyright (c) 2012, Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  *    list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  *    this list of conditions and the following disclaimer in the documentation
11  *    and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <ctype.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include "bspwm.h"
30 #include "desktop.h"
31 #include "ewmh.h"
32 #include "history.h"
33 #include "monitor.h"
34 #include "query.h"
35 #include "stack.h"
36 #include "tree.h"
37 #include "settings.h"
38 #include "restore.h"
39 #include "helpers.h"
40 #include "common.h"
41 #include "parse.h"
42 #include "jsmn.h"
43
44 bool restore_tree(const char *file_path)
45 {
46         size_t jslen;
47         char *json = read_string(file_path, &jslen);
48
49         if (json == NULL) {
50                 return false;
51         }
52
53         int nbtok = 256;
54         jsmn_parser parser;
55         jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
56
57         if (tokens == NULL) {
58                 perror("Restore tree: malloc");
59                 free(json);
60                 return false;
61         }
62
63         jsmn_init(&parser);
64         int ret;
65
66         while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
67                 nbtok *= 2;
68                 jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
69                 if (rtokens == NULL) {
70                         perror("Restore tree: realloc");
71                         free(tokens);
72                         free(json);
73                         return false;
74                 } else {
75                         tokens = rtokens;
76                 }
77         }
78
79         if (ret < 0) {
80                 warn("Restore tree: jsmn_parse: ");
81                 switch (ret) {
82                         case JSMN_ERROR_NOMEM:
83                                 warn("not enough memory.\n");
84                                 break;
85                         case JSMN_ERROR_INVAL:
86                                 warn("found invalid character inside JSON string.\n");
87                                 break;
88                         case JSMN_ERROR_PART:
89                                 warn("not a full JSON packet.\n");
90                                 break;
91                         default:
92                                 warn("unknown error.\n");
93                                 break;
94                 }
95
96                 free(tokens);
97                 free(json);
98
99                 return false;
100         }
101
102         while (mon_head != NULL) {
103                 remove_monitor(mon_head);
104         }
105
106         int num = tokens[0].size;
107         jsmntok_t *t = tokens + 1;
108         char *focusedMonitorName = NULL;
109
110         for (int i = 0; i < num; i++) {
111                 if (keyeq("focusedMonitorName", t, json)) {
112                         focusedMonitorName = copy_string(t+1, json);
113                         t++;
114                 } else if (keyeq("numClients", t, json)) {
115                         t++;
116                         sscanf(json + t->start, "%u", &num_clients);
117                 } else if (keyeq("monitors", t, json)) {
118                         t++;
119                         int s = t->size;
120                         t++;
121                         for (int j = 0; j < s; j++) {
122                                 monitor_t *m = restore_monitor(&t, json);
123                                 add_monitor(m);
124                         }
125                 }
126                 t++;
127         }
128
129         if (focusedMonitorName != NULL) {
130                 coordinates_t loc;
131                 if (locate_monitor(focusedMonitorName, &loc)) {
132                         mon = loc.monitor;
133                 }
134         }
135
136         free(focusedMonitorName);
137
138         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
139                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
140                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
141                                 uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
142                                 xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
143                         }
144                 }
145         }
146
147         ewmh_update_client_list();
148         ewmh_update_number_of_desktops();
149         ewmh_update_current_desktop();
150         ewmh_update_desktop_names();
151
152         free(tokens);
153         free(json);
154
155         return true;
156 }
157
158 #define RESTORE_INT(o, k, p) \
159         } else if (keyeq(#k, *t, json)) { \
160                 (*t)++; \
161                 sscanf(json + (*t)->start, "%i", &o->p);
162
163 #define RESTORE_UINT(o, k, p) \
164         } else if (keyeq(#k, *t, json)) { \
165                 (*t)++; \
166                 sscanf(json + (*t)->start, "%u", &o->p);
167
168 #define RESTORE_USINT(o, k, p) \
169         } else if (keyeq(#k, *t, json)) { \
170                 (*t)++; \
171                 sscanf(json + (*t)->start, "%hu", &o->p);
172
173 #define RESTORE_DOUBLE(o, k, p) \
174         } else if (keyeq(#k, *t, json)) { \
175                 (*t)++; \
176                 sscanf(json + (*t)->start, "%lf", &o->p);
177
178 #define RESTORE_ANY(o, k, p, f) \
179         } else if (keyeq(#k, *t, json)) { \
180                 (*t)++; \
181                 char *val = copy_string(*t, json); \
182                 f(val, &o->p); \
183                 free(val);
184
185 #define RESTORE_BOOL(o, k, p)  RESTORE_ANY(o, k, p, parse_bool)
186
187 monitor_t *restore_monitor(jsmntok_t **t, char *json)
188 {
189         int num = (*t)->size;
190         (*t)++;
191         monitor_t *m = make_monitor(NULL);
192         char *focusedDesktopName = NULL;
193
194         for (int i = 0; i < num; i++) {
195                 if (keyeq("name", *t, json)) {
196                         (*t)++;
197                         snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
198                 RESTORE_UINT(m, id, id)
199                 RESTORE_BOOL(m, wired, wired)
200                 RESTORE_INT(m, topPadding, top_padding)
201                 RESTORE_INT(m, rightPadding, right_padding)
202                 RESTORE_INT(m, bottomPadding, bottom_padding)
203                 RESTORE_INT(m, leftPadding, left_padding)
204                 RESTORE_INT(m, numSticky, num_sticky)
205                 } else if (keyeq("rectangle", *t, json)) {
206                         (*t)++;
207                         restore_rectangle(&m->rectangle, t, json);
208                         update_root(m, &m->rectangle);
209                         continue;
210                 } else if (keyeq("focusedDesktopName", *t, json)) {
211                         (*t)++;
212                         focusedDesktopName = copy_string(*t, json);
213                 } else if (keyeq("desktops", *t, json)) {
214                         (*t)++;
215                         int s = (*t)->size;
216                         (*t)++;
217                         for (int j = 0; j < s; j++) {
218                                 desktop_t *d = restore_desktop(t, json);
219                                 add_desktop(m, d);
220                         }
221                         continue;
222                 } else {
223                         warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
224                         (*t)++;
225                 }
226                 (*t)++;
227         }
228
229         if (focusedDesktopName != NULL) {
230                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
231                         if (streq(focusedDesktopName, d->name)) {
232                                 m->desk = d;
233                                 break;
234                         }
235                 }
236         }
237
238         free(focusedDesktopName);
239
240         return m;
241 }
242
243 desktop_t *restore_desktop(jsmntok_t **t, char *json)
244 {
245         int s = (*t)->size;
246         (*t)++;
247         desktop_t *d = make_desktop(NULL);
248         xcb_window_t focusedWindow = XCB_NONE;
249
250         for (int i = 0; i < s; i++) {
251                 if (keyeq("name", *t, json)) {
252                         (*t)++;
253                         snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
254                 } else if (keyeq("layout", *t, json)) {
255                         (*t)++;
256                         char *val = copy_string(*t, json);
257                         layout_t lyt;
258                         if (parse_layout(val, &lyt)) {
259                                 d->layout = lyt;
260                         }
261                         free(val);
262                 RESTORE_INT(d, topPadding, top_padding)
263                 RESTORE_INT(d, rightPadding, right_padding)
264                 RESTORE_INT(d, bottomPadding, bottom_padding)
265                 RESTORE_INT(d, leftPadding, left_padding)
266                 RESTORE_INT(d, windowGap, window_gap)
267                 RESTORE_UINT(d, borderWidth, border_width)
268                 } else if (keyeq("focusedWindow", *t, json)) {
269                         (*t)++;
270                         sscanf(json + (*t)->start, "%u", &focusedWindow);
271                 } else if (keyeq("root", *t, json)) {
272                         (*t)++;
273                         d->root = restore_node(t, json);
274                         continue;
275                 } else {
276                         warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
277                         (*t)++;
278                 }
279                 (*t)++;
280         }
281
282         if (focusedWindow != XCB_NONE) {
283                 for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
284                         if (f->client->window == focusedWindow) {
285                                 d->focus = f;
286                                 break;
287                         }
288                 }
289         }
290
291         return d;
292 }
293
294 node_t *restore_node(jsmntok_t **t, char *json)
295 {
296         if ((*t)->type == JSMN_PRIMITIVE) {
297                 (*t)++;
298                 return NULL;
299         } else {
300                 int s = (*t)->size;
301                 (*t)++;
302                 node_t *n = make_node();
303
304                 for (int i = 0; i < s; i++) {
305                         if (keyeq("splitType", *t, json)) {
306                                 (*t)++;
307                                 char *val = copy_string(*t, json);
308                                 parse_split_type(val, &n->split_type);
309                                 free(val);
310                         RESTORE_DOUBLE(n, splitRatio, split_ratio)
311                         RESTORE_ANY(n, splitMode, split_mode, parse_split_mode)
312                         RESTORE_ANY(n, splitDir, split_dir, parse_direction)
313                         RESTORE_INT(n, birthRotation, birth_rotation)
314                         RESTORE_INT(n, privacyLevel, privacy_level)
315                         RESTORE_ANY(n, vacant, vacant, parse_bool)
316                         } else if (keyeq("rectangle", *t, json)) {
317                                 (*t)++;
318                                 restore_rectangle(&n->rectangle, t, json);
319                                 continue;
320                         } else if (keyeq("firstChild", *t, json)) {
321                                 (*t)++;
322                                 node_t *fc = restore_node(t, json);
323                                 n->first_child = fc;
324                                 if (fc != NULL) {
325                                         fc->parent = n;
326                                 }
327                                 continue;
328                         } else if (keyeq("secondChild", *t, json)) {
329                                 (*t)++;
330                                 node_t *sc = restore_node(t, json);
331                                 n->second_child = sc;
332                                 if (sc != NULL) {
333                                         sc->parent = n;
334                                 }
335                                 continue;
336                         } else if (keyeq("client", *t, json)) {
337                                 (*t)++;
338                                 n->client = restore_client(t, json);
339                                 continue;
340                         }
341                         (*t)++;
342                 }
343
344                 return n;
345         }
346 }
347
348 client_t *restore_client(jsmntok_t **t, char *json)
349 {
350         if ((*t)->type == JSMN_PRIMITIVE) {
351                 (*t)++;
352                 return NULL;
353         } else {
354                 int s = (*t)->size;
355                 (*t)++;
356                 client_t *c = make_client(XCB_NONE, 0);
357
358                 for (int i = 0; i < s; i++) {
359                         if (keyeq("window", *t, json)) {
360                                 (*t)++;
361                                 sscanf(json + (*t)->start, "%u", &c->window);
362                         } else if (keyeq("className", *t, json)) {
363                                 (*t)++;
364                                 snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
365                         } else if (keyeq("instanceName", *t, json)) {
366                                 (*t)++;
367                                 snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
368                         RESTORE_ANY(c, state, state, parse_client_state)
369                         RESTORE_ANY(c, lastState, last_state, parse_client_state)
370                         RESTORE_ANY(c, layer, layer, parse_stack_layer)
371                         RESTORE_ANY(c, lastLayer, last_layer, parse_stack_layer)
372                         RESTORE_UINT(c, borderWidth, border_width)
373                         RESTORE_BOOL(c, locked, locked)
374                         RESTORE_BOOL(c, sticky, sticky)
375                         RESTORE_BOOL(c, urgent, urgent)
376                         RESTORE_BOOL(c, private, private)
377                         RESTORE_BOOL(c, icccmFocus, icccm_focus)
378                         RESTORE_BOOL(c, icccmInput, icccm_input)
379                         RESTORE_USINT(c, minWidth, min_width)
380                         RESTORE_USINT(c, maxWidth, max_width)
381                         RESTORE_USINT(c, minHeight, min_height)
382                         RESTORE_USINT(c, maxHeight, max_height)
383                         RESTORE_INT(c, numStates, num_states)
384                         } else if (keyeq("wmState", *t, json)) {
385                                 (*t)++;
386                                 restore_wm_state(c->wm_state, t, json);
387                                 continue;
388                         } else if (keyeq("tiledRectangle", *t, json)) {
389                                 (*t)++;
390                                 restore_rectangle(&c->tiled_rectangle, t, json);
391                                 continue;
392                         } else if (keyeq("floatingRectangle", *t, json)) {
393                                 (*t)++;
394                                 restore_rectangle(&c->floating_rectangle, t, json);
395                                 continue;
396                         }
397
398                         (*t)++;
399                 }
400
401                 return c;
402         }
403 }
404
405 void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
406 {
407         int s = (*t)->size;
408         (*t)++;
409
410         for (int i = 0; i < s; i++) {
411                 if (keyeq("x", *t, json)) {
412                         (*t)++;
413                         sscanf(json + (*t)->start, "%hi", &r->x);
414                 } else if (keyeq("y", *t, json)) {
415                         (*t)++;
416                         sscanf(json + (*t)->start, "%hi", &r->y);
417                 } else if (keyeq("width", *t, json)) {
418                         (*t)++;
419                         sscanf(json + (*t)->start, "%hu", &r->width);
420                 } else if (keyeq("height", *t, json)) {
421                         (*t)++;
422                         sscanf(json + (*t)->start, "%hu", &r->height);
423                 }
424                 (*t)++;
425         }
426 }
427
428 void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json)
429 {
430         int s = (*t)->size;
431         (*t)++;
432
433         for (int i = 0; i < s; i++) {
434                 sscanf(json + (*t)->start, "%u", &w[i]);
435                 (*t)++;
436         }
437 }
438
439 #undef RESTORE_INT
440 #undef RESTORE_UINT
441 #undef RESTORE_USINT
442 #undef RESTORE_DOUBLE
443 #undef RESTORE_ANY
444 #undef RESTORE_BOOL
445
446 bool keyeq(char *s, jsmntok_t *key, char *json)
447 {
448         return (strncmp(s, json + key->start, key->end - key->start) == 0);
449 }
450
451 char *copy_string(jsmntok_t *tok, char *json)
452 {
453         size_t len = tok->end - tok->start + 1;
454         char *res = malloc(len * sizeof(char));
455         if (res == NULL) {
456                 perror("Copy string: malloc");
457                 return NULL;
458         }
459         strncpy(res, json+tok->start, len-1);
460         res[len-1] = '\0';
461         return res;
462 }
463
464 bool restore_history(const char *file_path)
465 {
466         if (file_path == NULL) {
467                 return false;
468         }
469
470         FILE *snapshot = fopen(file_path, "r");
471         if (snapshot == NULL) {
472                 perror("Restore history: fopen");
473                 return false;
474         }
475
476         char line[MAXLEN];
477         char mnm[SMALEN];
478         char dnm[SMALEN];
479         xcb_window_t win;
480
481         empty_history();
482
483         while (fgets(line, sizeof(line), snapshot) != NULL) {
484                 if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
485                         coordinates_t loc;
486                         if (win != XCB_NONE && !locate_window(win, &loc)) {
487                                 warn("Can't locate window 0x%X.\n", win);
488                                 continue;
489                         }
490                         node_t *n = (win == XCB_NONE ? NULL : loc.node);
491                         if (!locate_desktop(dnm, &loc)) {
492                                 warn("Can't locate desktop '%s'.\n", dnm);
493                                 continue;
494                         }
495                         desktop_t *d = loc.desktop;
496                         if (!locate_monitor(mnm, &loc)) {
497                                 warn("Can't locate monitor '%s'.\n", mnm);
498                                 continue;
499                         }
500                         monitor_t *m = loc.monitor;
501                         history_add(m, d, n);
502                 } else {
503                         warn("Can't parse history entry: '%s'\n", line);
504                 }
505         }
506
507         fclose(snapshot);
508         return true;
509 }
510
511 bool restore_stack(const char *file_path)
512 {
513         if (file_path == NULL) {
514                 return false;
515         }
516
517         FILE *snapshot = fopen(file_path, "r");
518         if (snapshot == NULL) {
519                 perror("Restore stack: fopen");
520                 return false;
521         }
522
523         char line[MAXLEN];
524         xcb_window_t win;
525
526         while (stack_head != NULL) {
527                 remove_stack(stack_head);
528         }
529
530         while (fgets(line, sizeof(line), snapshot) != NULL) {
531                 if (sscanf(line, "%X", &win) == 1) {
532                         coordinates_t loc;
533                         if (locate_window(win, &loc)) {
534                                 stack_insert_after(stack_tail, loc.node);
535                         } else {
536                                 warn("Can't locate window 0x%X.\n", win);
537                         }
538                 } else {
539                         warn("Can't parse stack entry: '%s'\n", line);
540                 }
541         }
542
543         fclose(snapshot);
544         return true;
545 }