]> git.lizzy.rs Git - bspwm.git/blob - src/restore.c
Handle `single_monocle` more carefully
[bspwm.git] / src / 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 <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include "bspwm.h"
30 #include "desktop.h"
31 #include "ewmh.h"
32 #include "history.h"
33 #include "pointer.h"
34 #include "monitor.h"
35 #include "query.h"
36 #include "stack.h"
37 #include "tree.h"
38 #include "settings.h"
39 #include "subscribe.h"
40 #include "restore.h"
41 #include "window.h"
42 #include "parse.h"
43
44 bool restore_state(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         int num = tokens[0].size;
103
104         if (num < 1) {
105                 free(tokens);
106                 free(json);
107
108                 return false;
109         }
110
111         mon = NULL;
112         while (mon_head != NULL) {
113                 remove_monitor(mon_head);
114         }
115
116         jsmntok_t *t = tokens + 1;
117         uint32_t focused_monitor_id = 0, primary_monitor_id = 0;
118         jsmntok_t *focus_history_token = NULL, *stacking_list_token = NULL;
119
120         for (int i = 0; i < num; i++) {
121                 if (keyeq("focusedMonitorId", t, json)) {
122                         t++;
123                         sscanf(json + t->start, "%u", &focused_monitor_id);
124                 } else if (keyeq("primaryMonitorId", t, json)) {
125                         t++;
126                         sscanf(json + t->start, "%u", &primary_monitor_id);
127                 } else if (keyeq("clientsCount", t, json)) {
128                         t++;
129                         sscanf(json + t->start, "%u", &clients_count);
130                 } else if (keyeq("monitors", t, json)) {
131                         t++;
132                         int s = t->size;
133                         t++;
134                         for (int j = 0; j < s; j++) {
135                                 monitor_t *m = restore_monitor(&t, json);
136                                 if (m->desk == NULL) {
137                                         add_desktop(m, make_desktop(NULL, XCB_NONE));
138                                 }
139                                 add_monitor(m);
140                         }
141                         continue;
142                 } else if (keyeq("focusHistory", t, json)) {
143                         t++;
144                         if (mon == NULL) {
145                                 focus_history_token = t;
146                         }
147                         restore_history(&t, json);
148                         continue;
149                 } else if (keyeq("stackingList", t, json)) {
150                         t++;
151                         if (mon == NULL) {
152                                 stacking_list_token = t;
153                         }
154                         restore_stack(&t, json);
155                         continue;
156                 } else if (keyeq("eventSubscribers", t, json)) {
157                         t++;
158                         restore_subscribers(&t, json);
159                         continue;
160                 }
161                 t++;
162         }
163
164         if (focused_monitor_id != 0) {
165                 coordinates_t loc;
166                 if (monitor_from_id(focused_monitor_id, &loc)) {
167                         mon = loc.monitor;
168                 }
169         }
170
171         if (primary_monitor_id != 0) {
172                 coordinates_t loc;
173                 if (monitor_from_id(primary_monitor_id, &loc)) {
174                         pri_mon = loc.monitor;
175                 }
176         }
177
178         if (focus_history_token != NULL) {
179                 restore_history(&focus_history_token, json);
180         }
181
182         if (stacking_list_token != NULL) {
183                 restore_stack(&stacking_list_token, json);
184         }
185
186         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
187                 m->id = xcb_generate_id(dpy);
188                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
189                         d->id = xcb_generate_id(dpy);
190                         regenerate_ids_in(d->root);
191                         refresh_presel_feedbacks(m, d, d->root);
192                         restack_presel_feedbacks(d);
193
194                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
195                                 if (n->client == NULL) {
196                                         continue;
197                                 }
198                                 initialize_client(n);
199                                 uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
200                                 xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
201                                 window_grab_buttons(n->id);
202                         }
203                 }
204         }
205
206         ewmh_update_number_of_desktops();
207         ewmh_update_desktop_names();
208         ewmh_update_desktop_viewport();
209         ewmh_update_current_desktop();
210         ewmh_update_client_list(false);
211         ewmh_update_client_list(true);
212         ewmh_update_active_window();
213
214         free(tokens);
215         free(json);
216
217         return true;
218 }
219
220 #define RESTORE_INT(k, p) \
221         } else if (keyeq(#k, *t, json)) { \
222                 (*t)++; \
223                 sscanf(json + (*t)->start, "%i", p);
224
225 #define RESTORE_UINT(k, p) \
226         } else if (keyeq(#k, *t, json)) { \
227                 (*t)++; \
228                 sscanf(json + (*t)->start, "%u", p);
229
230 #define RESTORE_USINT(k, p) \
231         } else if (keyeq(#k, *t, json)) { \
232                 (*t)++; \
233                 sscanf(json + (*t)->start, "%hu", p);
234
235 #define RESTORE_DOUBLE(k, p) \
236         } else if (keyeq(#k, *t, json)) { \
237                 (*t)++; \
238                 sscanf(json + (*t)->start, "%lf", p);
239
240 #define RESTORE_ANY(k, p, f) \
241         } else if (keyeq(#k, *t, json)) { \
242                 (*t)++; \
243                 char *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \
244                 f(val, p); \
245                 free(val);
246
247 #define RESTORE_BOOL(k, p)  RESTORE_ANY(k, p, parse_bool)
248
249 monitor_t *restore_monitor(jsmntok_t **t, char *json)
250 {
251         int num = (*t)->size;
252         (*t)++;
253         monitor_t *m = make_monitor(NULL, NULL, UINT32_MAX);
254         uint32_t focused_desktop_id = 0;
255
256         for (int i = 0; i < num; i++) {
257                 if (keyeq("name", *t, json)) {
258                         (*t)++;
259                         snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
260                 RESTORE_UINT(id, &m->id)
261                 RESTORE_UINT(randrId, &m->randr_id)
262                 RESTORE_BOOL(wired, &m->wired)
263                 RESTORE_UINT(stickyCount, &m->sticky_count)
264                 RESTORE_INT(windowGap, &m->window_gap)
265                 RESTORE_UINT(borderWidth, &m->border_width)
266                 RESTORE_UINT(focusedDesktopId, &focused_desktop_id)
267                 } else if (keyeq("padding", *t, json)) {
268                         (*t)++;
269                         restore_padding(&m->padding, t, json);
270                         continue;
271                 } else if (keyeq("rectangle", *t, json)) {
272                         (*t)++;
273                         restore_rectangle(&m->rectangle, t, json);
274                         update_root(m, &m->rectangle);
275                         continue;
276                 } else if (keyeq("desktops", *t, json)) {
277                         (*t)++;
278                         int s = (*t)->size;
279                         (*t)++;
280                         for (int j = 0; j < s; j++) {
281                                 desktop_t *d = restore_desktop(t, json);
282                                 add_desktop(m, d);
283                         }
284                         continue;
285                 } else {
286                         warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
287                         (*t)++;
288                 }
289                 (*t)++;
290         }
291
292         if (focused_desktop_id != 0) {
293                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
294                         if (d->id == focused_desktop_id) {
295                                 m->desk = d;
296                                 break;
297                         }
298                 }
299         }
300
301         return m;
302 }
303
304 desktop_t *restore_desktop(jsmntok_t **t, char *json)
305 {
306         int s = (*t)->size;
307         (*t)++;
308         desktop_t *d = make_desktop(NULL, UINT32_MAX);
309         xcb_window_t focusedNodeId = XCB_NONE;
310
311         for (int i = 0; i < s; i++) {
312                 if (keyeq("name", *t, json)) {
313                         (*t)++;
314                         snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
315                 RESTORE_UINT(id, &d->id)
316                 RESTORE_ANY(layout, &d->layout, parse_layout)
317                 RESTORE_ANY(userLayout, &d->user_layout, parse_layout)
318                 RESTORE_INT(windowGap, &d->window_gap)
319                 RESTORE_UINT(borderWidth, &d->border_width)
320                 } else if (keyeq("focusedNodeId", *t, json)) {
321                         (*t)++;
322                         sscanf(json + (*t)->start, "%u", &focusedNodeId);
323                 } else if (keyeq("padding", *t, json)) {
324                         (*t)++;
325                         restore_padding(&d->padding, t, json);
326                         continue;
327                 } else if (keyeq("root", *t, json)) {
328                         (*t)++;
329                         d->root = restore_node(t, json);
330                         continue;
331                 } else {
332                         warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
333                         (*t)++;
334                 }
335                 (*t)++;
336         }
337
338         if (focusedNodeId != XCB_NONE) {
339                 d->focus = find_by_id_in(d->root, focusedNodeId);
340         }
341
342         return d;
343 }
344
345 node_t *restore_node(jsmntok_t **t, char *json)
346 {
347         if ((*t)->type == JSMN_PRIMITIVE) {
348                 (*t)++;
349                 return NULL;
350         } else {
351                 int s = (*t)->size;
352                 (*t)++;
353                 /* hack to prevent a new ID from being generated */
354                 node_t *n = make_node(UINT32_MAX);
355
356                 for (int i = 0; i < s; i++) {
357                         if (keyeq("id", *t, json)) {
358                                 (*t)++;
359                                 sscanf(json + (*t)->start, "%u", &n->id);
360                         RESTORE_ANY(splitType, &n->split_type, parse_split_type)
361                         RESTORE_DOUBLE(splitRatio, &n->split_ratio)
362                         RESTORE_BOOL(vacant, &n->vacant)
363                         RESTORE_BOOL(hidden, &n->hidden)
364                         RESTORE_BOOL(sticky, &n->sticky)
365                         RESTORE_BOOL(private, &n->private)
366                         RESTORE_BOOL(locked, &n->locked)
367                         RESTORE_BOOL(marked, &n->marked)
368                         } else if (keyeq("presel", *t, json)) {
369                                 (*t)++;
370                                 n->presel = restore_presel(t, json);
371                                 continue;
372                         } else if (keyeq("rectangle", *t, json)) {
373                                 (*t)++;
374                                 restore_rectangle(&n->rectangle, t, json);
375                                 continue;
376                         } else if (keyeq("constraints", *t, json)) {
377                                 (*t)++;
378                                 restore_constraints(&n->constraints, t, json);
379                                 continue;
380                         } else if (keyeq("firstChild", *t, json)) {
381                                 (*t)++;
382                                 node_t *fc = restore_node(t, json);
383                                 n->first_child = fc;
384                                 if (fc != NULL) {
385                                         fc->parent = n;
386                                 }
387                                 continue;
388                         } else if (keyeq("secondChild", *t, json)) {
389                                 (*t)++;
390                                 node_t *sc = restore_node(t, json);
391                                 n->second_child = sc;
392                                 if (sc != NULL) {
393                                         sc->parent = n;
394                                 }
395                                 continue;
396                         } else if (keyeq("client", *t, json)) {
397                                 (*t)++;
398                                 n->client = restore_client(t, json);
399                                 continue;
400                         } else {
401                                 warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
402                                 (*t)++;
403                         }
404                         (*t)++;
405                 }
406
407                 return n;
408         }
409 }
410
411 presel_t *restore_presel(jsmntok_t **t, char *json)
412 {
413         if ((*t)->type == JSMN_PRIMITIVE) {
414                 (*t)++;
415                 return NULL;
416         } else {
417                 int s = (*t)->size;
418                 (*t)++;
419                 presel_t *p = make_presel();
420
421                 for (int i = 0; i < s; i++) {
422                         if (keyeq("splitRatio", *t, json)) {
423                                 (*t)++;
424                                 sscanf(json + (*t)->start, "%lf", &p->split_ratio);
425                         RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
426                         }
427
428                         (*t)++;
429                 }
430
431                 return p;
432         }
433 }
434
435
436 client_t *restore_client(jsmntok_t **t, char *json)
437 {
438         if ((*t)->type == JSMN_PRIMITIVE) {
439                 (*t)++;
440                 return NULL;
441         } else {
442                 int s = (*t)->size;
443                 (*t)++;
444                 client_t *c = make_client();
445
446                 for (int i = 0; i < s; i++) {
447                         if (keyeq("className", *t, json)) {
448                                 (*t)++;
449                                 snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
450                         } else if (keyeq("instanceName", *t, json)) {
451                                 (*t)++;
452                                 snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
453                         RESTORE_ANY(state, &c->state, parse_client_state)
454                         RESTORE_ANY(lastState, &c->last_state, parse_client_state)
455                         RESTORE_ANY(layer, &c->layer, parse_stack_layer)
456                         RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
457                         RESTORE_UINT(borderWidth, &c->border_width)
458                         RESTORE_BOOL(urgent, &c->urgent)
459                         RESTORE_BOOL(shown, &c->shown)
460                         } else if (keyeq("tiledRectangle", *t, json)) {
461                                 (*t)++;
462                                 restore_rectangle(&c->tiled_rectangle, t, json);
463                                 continue;
464                         } else if (keyeq("floatingRectangle", *t, json)) {
465                                 (*t)++;
466                                 restore_rectangle(&c->floating_rectangle, t, json);
467                                 continue;
468                         } else {
469                                 warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
470                                 (*t)++;
471                         }
472
473                         (*t)++;
474                 }
475
476                 return c;
477         }
478 }
479
480 void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
481 {
482         int s = (*t)->size;
483         (*t)++;
484
485         for (int i = 0; i < s; i++) {
486                 if (keyeq("x", *t, json)) {
487                         (*t)++;
488                         sscanf(json + (*t)->start, "%hi", &r->x);
489                 } else if (keyeq("y", *t, json)) {
490                         (*t)++;
491                         sscanf(json + (*t)->start, "%hi", &r->y);
492                 } else if (keyeq("width", *t, json)) {
493                         (*t)++;
494                         sscanf(json + (*t)->start, "%hu", &r->width);
495                 } else if (keyeq("height", *t, json)) {
496                         (*t)++;
497                         sscanf(json + (*t)->start, "%hu", &r->height);
498                 }
499                 (*t)++;
500         }
501 }
502
503 void restore_constraints(constraints_t *c, jsmntok_t **t, char *json)
504 {
505         int s = (*t)->size;
506         (*t)++;
507
508         for (int i = 0; i < s; i++) {
509                 if (keyeq("min_width", *t, json)) {
510                         (*t)++;
511                         sscanf(json + (*t)->start, "%hu", &c->min_width);
512                 } else if (keyeq("min_height", *t, json)) {
513                         (*t)++;
514                         sscanf(json + (*t)->start, "%hu", &c->min_height);
515                 }
516                 (*t)++;
517         }
518 }
519
520 void restore_padding(padding_t *p, jsmntok_t **t, char *json)
521 {
522         int s = (*t)->size;
523         (*t)++;
524
525         for (int i = 0; i < s; i++) {
526                 if (keyeq("top", *t, json)) {
527                         (*t)++;
528                         sscanf(json + (*t)->start, "%i", &p->top);
529                 } else if (keyeq("right", *t, json)) {
530                         (*t)++;
531                         sscanf(json + (*t)->start, "%i", &p->right);
532                 } else if (keyeq("bottom", *t, json)) {
533                         (*t)++;
534                         sscanf(json + (*t)->start, "%i", &p->bottom);
535                 } else if (keyeq("left", *t, json)) {
536                         (*t)++;
537                         sscanf(json + (*t)->start, "%i", &p->left);
538                 }
539                 (*t)++;
540         }
541 }
542
543 void restore_history(jsmntok_t **t, char *json)
544 {
545         int s = (*t)->size;
546         (*t)++;
547
548         for (int i = 0; i < s; i++) {
549                 coordinates_t loc = {NULL, NULL, NULL};
550                 restore_coordinates(&loc, t, json);
551                 if (loc.monitor != NULL && loc.desktop != NULL) {
552                         history_add(loc.monitor, loc.desktop, loc.node, true);
553                 }
554         }
555 }
556
557 void restore_subscribers(jsmntok_t **t, char *json)
558 {
559         int s = (*t)->size;
560         (*t)++;
561
562         for (int i = 0; i < s; i++) {
563                 subscriber_list_t *s = make_subscriber(NULL, NULL, 0, 0);
564                 restore_subscriber(s, t, json);
565                 add_subscriber(s);
566         }
567 }
568
569 void restore_subscriber(subscriber_list_t *s, jsmntok_t **t, char *json)
570 {
571         int n = (*t)->size;
572         (*t)++;
573
574         for (int i = 0; i < n; i++) {
575                 if (keyeq("fileDescriptor", *t, json)) {
576                         (*t)++;
577                         int fd;
578                         sscanf(json + (*t)->start, "%i", &fd);
579                         s->stream = fdopen(fd, "w");
580                 } else if (keyeq("fifoPath", *t, json)) {
581                         (*t)++;
582                         free(s->fifo_path);
583                         s->fifo_path = copy_string(json + (*t)->start, (*t)->end - (*t)->start);
584                 RESTORE_INT(field, &s->field)
585                 RESTORE_INT(count, &s->count)
586                 }
587                 (*t)++;
588         }
589 }
590
591 void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
592 {
593         int s = (*t)->size;
594         (*t)++;
595         uint32_t id = 0;
596
597         for (int i = 0; i < s; i++) {
598                 if (keyeq("monitorId", *t, json)) {
599                         (*t)++;
600                         sscanf(json + (*t)->start, "%u", &id);
601                         loc->monitor = find_monitor(id);
602                 } else if (keyeq("desktopId", *t, json)) {
603                         (*t)++;
604                         sscanf(json + (*t)->start, "%u", &id);
605                         loc->desktop = find_desktop_in(id, loc->monitor);
606                 } else if (keyeq("nodeId", *t, json)) {
607                         (*t)++;
608                         sscanf(json + (*t)->start, "%u", &id);
609                         loc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id);
610                 }
611                 (*t)++;
612         }
613 }
614
615 void restore_stack(jsmntok_t **t, char *json)
616 {
617         int s = (*t)->size;
618         (*t)++;
619
620         for (int i = 0; i < s; i++) {
621                 uint32_t id;
622                 sscanf(json + (*t)->start, "%u", &id);
623                 coordinates_t loc;
624                 if (locate_window(id, &loc)) {
625                         stack_insert_after(stack_tail, loc.node);
626                 }
627                 (*t)++;
628         }
629 }
630
631 #undef RESTORE_INT
632 #undef RESTORE_UINT
633 #undef RESTORE_USINT
634 #undef RESTORE_DOUBLE
635 #undef RESTORE_ANY
636 #undef RESTORE_BOOL
637
638 bool keyeq(char *s, jsmntok_t *key, char *json)
639 {
640         size_t n = key->end - key->start;
641         return (strlen(s) == n && strncmp(s, json + key->start, n) == 0);
642 }