]> git.lizzy.rs Git - bspwm.git/blob - restore.c
Fix num_clients restore format
[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                                 t++;
125                         }
126                 }
127                 t++;
128         }
129
130         if (focusedMonitorName != NULL) {
131                 coordinates_t loc;
132                 if (locate_monitor(focusedMonitorName, &loc)) {
133                         mon = loc.monitor;
134                 }
135         }
136
137         free(focusedMonitorName);
138
139         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
140                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
141                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
142                                 uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
143                                 xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
144                         }
145                 }
146         }
147
148         ewmh_update_client_list();
149         ewmh_update_number_of_desktops();
150         ewmh_update_current_desktop();
151         ewmh_update_desktop_names();
152
153         free(tokens);
154         free(json);
155
156         return true;
157 }
158
159 #define RESTORE_INT(o, k, p) \
160         } else if (keyeq(#k, *t, json)) { \
161                 (*t)++; \
162                 sscanf(json + (*t)->start, "%i", &o->p);
163
164 #define RESTORE_UINT(o, k, p) \
165         } else if (keyeq(#k, *t, json)) { \
166                 (*t)++; \
167                 sscanf(json + (*t)->start, "%u", &o->p);
168
169 #define RESTORE_USINT(o, k, p) \
170         } else if (keyeq(#k, *t, json)) { \
171                 (*t)++; \
172                 sscanf(json + (*t)->start, "%hu", &o->p);
173
174 #define RESTORE_DOUBLE(o, k, p) \
175         } else if (keyeq(#k, *t, json)) { \
176                 (*t)++; \
177                 sscanf(json + (*t)->start, "%lf", &o->p);
178
179 #define RESTORE_ANY(o, k, p, f) \
180         } else if (keyeq(#k, *t, json)) { \
181                 (*t)++; \
182                 char *val = copy_string(*t, json); \
183                 f(val, &o->p); \
184                 free(val);
185
186 #define RESTORE_BOOL(o, k, p)  RESTORE_ANY(o, k, p, parse_bool)
187
188 monitor_t *restore_monitor(jsmntok_t **t, char *json)
189 {
190         int num = (*t)->size;
191         (*t)++;
192         monitor_t *m = make_monitor(NULL);
193         char *focusedDesktopName = NULL;
194
195         for (int i = 0; i < num; i++) {
196                 if (keyeq("name", *t, json)) {
197                         (*t)++;
198                         snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
199                 RESTORE_UINT(m, id, id)
200                 RESTORE_BOOL(m, wired, wired)
201                 RESTORE_INT(m, topPadding, top_padding)
202                 RESTORE_INT(m, rightPadding, right_padding)
203                 RESTORE_INT(m, bottomPadding, bottom_padding)
204                 RESTORE_INT(m, leftPadding, left_padding)
205                 RESTORE_INT(m, numSticky, num_sticky)
206                 } else if (keyeq("rectangle", *t, json)) {
207                         (*t)++;
208                         restore_rectangle(&m->rectangle, t, json);
209                         update_root(m, &m->rectangle);
210                         continue;
211                 } else if (keyeq("focusedDesktopName", *t, json)) {
212                         (*t)++;
213                         focusedDesktopName = copy_string(*t, json);
214                 } else if (keyeq("desktops", *t, json)) {
215                         (*t)++;
216                         int s = (*t)->size;
217                         (*t)++;
218                         for (int j = 0; j < s; j++) {
219                                 desktop_t *d = restore_desktop(t, json);
220                                 add_desktop(m, d);
221                         }
222                         continue;
223                 } else {
224                         warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
225                         (*t)++;
226                 }
227                 (*t)++;
228         }
229
230         if (focusedDesktopName != NULL) {
231                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
232                         if (streq(focusedDesktopName, d->name)) {
233                                 m->desk = d;
234                                 break;
235                         }
236                 }
237         }
238
239         free(focusedDesktopName);
240
241         return m;
242 }
243
244 desktop_t *restore_desktop(jsmntok_t **t, char *json)
245 {
246         int s = (*t)->size;
247         (*t)++;
248         desktop_t *d = make_desktop(NULL);
249         xcb_window_t focusedWindow = XCB_NONE;
250
251         for (int i = 0; i < s; i++) {
252                 if (keyeq("name", *t, json)) {
253                         (*t)++;
254                         snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
255                 } else if (keyeq("layout", *t, json)) {
256                         (*t)++;
257                         char *val = copy_string(*t, json);
258                         layout_t lyt;
259                         if (parse_layout(val, &lyt)) {
260                                 d->layout = lyt;
261                         }
262                         free(val);
263                 RESTORE_INT(d, topPadding, top_padding)
264                 RESTORE_INT(d, rightPadding, right_padding)
265                 RESTORE_INT(d, bottomPadding, bottom_padding)
266                 RESTORE_INT(d, leftPadding, left_padding)
267                 RESTORE_INT(d, windowGap, window_gap)
268                 RESTORE_UINT(d, borderWidth, border_width)
269                 } else if (keyeq("focusedWindow", *t, json)) {
270                         (*t)++;
271                         sscanf(json + (*t)->start, "%u", &focusedWindow);
272                 } else if (keyeq("root", *t, json)) {
273                         (*t)++;
274                         d->root = restore_node(t, json);
275                         continue;
276                 } else {
277                         warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
278                         (*t)++;
279                 }
280                 (*t)++;
281         }
282
283         if (focusedWindow != XCB_NONE) {
284                 for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
285                         if (f->client->window == focusedWindow) {
286                                 d->focus = f;
287                                 break;
288                         }
289                 }
290         }
291
292         return d;
293 }
294
295 node_t *restore_node(jsmntok_t **t, char *json)
296 {
297         if ((*t)->type == JSMN_PRIMITIVE) {
298                 (*t)++;
299                 return NULL;
300         } else {
301                 int s = (*t)->size;
302                 (*t)++;
303                 node_t *n = make_node();
304
305                 for (int i = 0; i < s; i++) {
306                         if (keyeq("splitType", *t, json)) {
307                                 (*t)++;
308                                 char *val = copy_string(*t, json);
309                                 parse_split_type(val, &n->split_type);
310                                 free(val);
311                         RESTORE_DOUBLE(n, splitRatio, split_ratio)
312                         RESTORE_ANY(n, splitMode, split_mode, parse_split_mode)
313                         RESTORE_ANY(n, splitDir, split_dir, parse_direction)
314                         RESTORE_INT(n, birthRotation, birth_rotation)
315                         RESTORE_INT(n, privacyLevel, privacy_level)
316                         RESTORE_ANY(n, vacant, vacant, parse_bool)
317                         } else if (keyeq("rectangle", *t, json)) {
318                                 (*t)++;
319                                 restore_rectangle(&n->rectangle, t, json);
320                                 continue;
321                         } else if (keyeq("firstChild", *t, json)) {
322                                 (*t)++;
323                                 node_t *fc = restore_node(t, json);
324                                 n->first_child = fc;
325                                 if (fc != NULL) {
326                                         fc->parent = n;
327                                 }
328                                 continue;
329                         } else if (keyeq("secondChild", *t, json)) {
330                                 (*t)++;
331                                 node_t *sc = restore_node(t, json);
332                                 n->second_child = sc;
333                                 if (sc != NULL) {
334                                         sc->parent = n;
335                                 }
336                                 continue;
337                         } else if (keyeq("client", *t, json)) {
338                                 (*t)++;
339                                 n->client = restore_client(t, json);
340                                 continue;
341                         }
342                         (*t)++;
343                 }
344
345                 return n;
346         }
347 }
348
349 client_t *restore_client(jsmntok_t **t, char *json)
350 {
351         if ((*t)->type == JSMN_PRIMITIVE) {
352                 (*t)++;
353                 return NULL;
354         } else {
355                 int s = (*t)->size;
356                 (*t)++;
357                 client_t *c = make_client(XCB_NONE, 0);
358
359                 for (int i = 0; i < s; i++) {
360                         if (keyeq("window", *t, json)) {
361                                 (*t)++;
362                                 sscanf(json + (*t)->start, "%u", &c->window);
363                         } else if (keyeq("className", *t, json)) {
364                                 (*t)++;
365                                 snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
366                         } else if (keyeq("instanceName", *t, json)) {
367                                 (*t)++;
368                                 snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
369                         RESTORE_ANY(c, state, state, parse_client_state)
370                         RESTORE_ANY(c, lastState, last_state, parse_client_state)
371                         RESTORE_ANY(c, layer, layer, parse_stack_layer)
372                         RESTORE_ANY(c, lastLayer, last_layer, parse_stack_layer)
373                         RESTORE_UINT(c, borderWidth, border_width)
374                         RESTORE_BOOL(c, locked, locked)
375                         RESTORE_BOOL(c, sticky, sticky)
376                         RESTORE_BOOL(c, urgent, urgent)
377                         RESTORE_BOOL(c, private, private)
378                         RESTORE_BOOL(c, icccmFocus, icccm_focus)
379                         RESTORE_BOOL(c, icccmInput, icccm_input)
380                         RESTORE_USINT(c, minWidth, min_width)
381                         RESTORE_USINT(c, maxWidth, max_width)
382                         RESTORE_USINT(c, minHeight, min_height)
383                         RESTORE_USINT(c, maxHeight, max_height)
384                         RESTORE_INT(c, numStates, num_states)
385                         } else if (keyeq("wmState", *t, json)) {
386                                 (*t)++;
387                                 restore_wm_state(c->wm_state, t, json);
388                                 continue;
389                         } else if (keyeq("tiledRectangle", *t, json)) {
390                                 (*t)++;
391                                 restore_rectangle(&c->tiled_rectangle, t, json);
392                                 continue;
393                         } else if (keyeq("floatingRectangle", *t, json)) {
394                                 (*t)++;
395                                 restore_rectangle(&c->floating_rectangle, t, json);
396                                 continue;
397                         }
398
399                         (*t)++;
400                 }
401
402                 return c;
403         }
404 }
405
406 void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
407 {
408         int s = (*t)->size;
409         (*t)++;
410
411         for (int i = 0; i < s; i++) {
412                 if (keyeq("x", *t, json)) {
413                         (*t)++;
414                         sscanf(json + (*t)->start, "%hi", &r->x);
415                 } else if (keyeq("y", *t, json)) {
416                         (*t)++;
417                         sscanf(json + (*t)->start, "%hi", &r->y);
418                 } else if (keyeq("width", *t, json)) {
419                         (*t)++;
420                         sscanf(json + (*t)->start, "%hu", &r->width);
421                 } else if (keyeq("height", *t, json)) {
422                         (*t)++;
423                         sscanf(json + (*t)->start, "%hu", &r->height);
424                 }
425                 (*t)++;
426         }
427 }
428
429 void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json)
430 {
431         int s = (*t)->size;
432         (*t)++;
433
434         for (int i = 0; i < s; i++) {
435                 sscanf(json + (*t)->start, "%u", &w[i]);
436                 (*t)++;
437         }
438 }
439
440 #undef RESTORE_INT
441 #undef RESTORE_UINT
442 #undef RESTORE_USINT
443 #undef RESTORE_DOUBLE
444 #undef RESTORE_ANY
445 #undef RESTORE_BOOL
446
447 bool keyeq(char *s, jsmntok_t *key, char *json)
448 {
449         return (strncmp(s, json + key->start, key->end - key->start) == 0);
450 }
451
452 char *copy_string(jsmntok_t *tok, char *json)
453 {
454         size_t len = tok->end - tok->start + 1;
455         char *res = malloc(len * sizeof(char));
456         if (res == NULL) {
457                 perror("Copy string: malloc");
458                 return NULL;
459         }
460         strncpy(res, json+tok->start, len-1);
461         res[len-1] = '\0';
462         return res;
463 }
464
465 bool restore_history(const char *file_path)
466 {
467         if (file_path == NULL) {
468                 return false;
469         }
470
471         FILE *snapshot = fopen(file_path, "r");
472         if (snapshot == NULL) {
473                 perror("Restore history: fopen");
474                 return false;
475         }
476
477         char line[MAXLEN];
478         char mnm[SMALEN];
479         char dnm[SMALEN];
480         xcb_window_t win;
481
482         empty_history();
483
484         while (fgets(line, sizeof(line), snapshot) != NULL) {
485                 if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
486                         coordinates_t loc;
487                         if (win != XCB_NONE && !locate_window(win, &loc)) {
488                                 warn("Can't locate window 0x%X.\n", win);
489                                 continue;
490                         }
491                         node_t *n = (win == XCB_NONE ? NULL : loc.node);
492                         if (!locate_desktop(dnm, &loc)) {
493                                 warn("Can't locate desktop '%s'.\n", dnm);
494                                 continue;
495                         }
496                         desktop_t *d = loc.desktop;
497                         if (!locate_monitor(mnm, &loc)) {
498                                 warn("Can't locate monitor '%s'.\n", mnm);
499                                 continue;
500                         }
501                         monitor_t *m = loc.monitor;
502                         history_add(m, d, n);
503                 } else {
504                         warn("Can't parse history entry: '%s'\n", line);
505                 }
506         }
507
508         fclose(snapshot);
509         return true;
510 }
511
512 bool restore_stack(const char *file_path)
513 {
514         if (file_path == NULL) {
515                 return false;
516         }
517
518         FILE *snapshot = fopen(file_path, "r");
519         if (snapshot == NULL) {
520                 perror("Restore stack: fopen");
521                 return false;
522         }
523
524         char line[MAXLEN];
525         xcb_window_t win;
526
527         while (stack_head != NULL) {
528                 remove_stack(stack_head);
529         }
530
531         while (fgets(line, sizeof(line), snapshot) != NULL) {
532                 if (sscanf(line, "%X", &win) == 1) {
533                         coordinates_t loc;
534                         if (locate_window(win, &loc)) {
535                                 stack_insert_after(stack_tail, loc.node);
536                         } else {
537                                 warn("Can't locate window 0x%X.\n", win);
538                         }
539                 } else {
540                         warn("Can't parse stack entry: '%s'\n", line);
541                 }
542         }
543
544         fclose(snapshot);
545         return true;
546 }