]> git.lizzy.rs Git - bspwm.git/blobdiff - restore.c
Transfer nodes moved via configure requests
[bspwm.git] / restore.c
index c18275c464cbcca885497537d9cbb1e3faa9dfca..b3b98b2dd9a647257c3db8e7c45faf7b290edc78 100644 (file)
--- a/restore.c
+++ b/restore.c
-/* * Copyright (c) 2012-2013 Bastien Dejean
+/* Copyright (c) 2012, Bastien Dejean
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
  *
- *  * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation and/or
- * other materials provided with the distribution.
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include "bspwm.h"
 #include "desktop.h"
 #include "ewmh.h"
 #include "history.h"
 #include "monitor.h"
-#include "window.h"
 #include "query.h"
-#include "settings.h"
 #include "stack.h"
 #include "tree.h"
+#include "settings.h"
 #include "restore.h"
+#include "window.h"
+#include "parse.h"
+
+bool restore_tree(const char *file_path)
+{
+       size_t jslen;
+       char *json = read_string(file_path, &jslen);
+
+       if (json == NULL) {
+               return false;
+       }
+
+       int nbtok = 256;
+       jsmn_parser parser;
+       jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
+
+       if (tokens == NULL) {
+               perror("Restore tree: malloc");
+               free(json);
+               return false;
+       }
+
+       jsmn_init(&parser);
+       int ret;
+
+       while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
+               nbtok *= 2;
+               jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
+               if (rtokens == NULL) {
+                       perror("Restore tree: realloc");
+                       free(tokens);
+                       free(json);
+                       return false;
+               } else {
+                       tokens = rtokens;
+               }
+       }
+
+       if (ret < 0) {
+               warn("Restore tree: jsmn_parse: ");
+               switch (ret) {
+                       case JSMN_ERROR_NOMEM:
+                               warn("not enough memory.\n");
+                               break;
+                       case JSMN_ERROR_INVAL:
+                               warn("found invalid character inside JSON string.\n");
+                               break;
+                       case JSMN_ERROR_PART:
+                               warn("not a full JSON packet.\n");
+                               break;
+                       default:
+                               warn("unknown error.\n");
+                               break;
+               }
+
+               free(tokens);
+               free(json);
+
+               return false;
+       }
+
+       int num = tokens[0].size;
+
+       if (num < 1) {
+               free(tokens);
+               free(json);
+
+               return false;
+       }
+
+       mon = NULL;
+       while (mon_head != NULL) {
+               remove_monitor(mon_head);
+       }
+
+       jsmntok_t *t = tokens + 1;
+       char *focusedMonitorName = NULL, *primaryMonitorName = NULL;
+
+       for (int i = 0; i < num; i++) {
+               if (keyeq("focusedMonitorName", t, json)) {
+                       free(focusedMonitorName);
+                       t++;
+                       focusedMonitorName = copy_string(json + t->start, t->end - t->start);
+               } else if (keyeq("primaryMonitorName", t, json)) {
+                       free(primaryMonitorName);
+                       t++;
+                       primaryMonitorName = copy_string(json + t->start, t->end - t->start);
+               } else if (keyeq("clientsCount", t, json)) {
+                       t++;
+                       sscanf(json + t->start, "%u", &clients_count);
+               } else if (keyeq("monitors", t, json)) {
+                       t++;
+                       int s = t->size;
+                       t++;
+                       for (int j = 0; j < s; j++) {
+                               monitor_t *m = restore_monitor(&t, json);
+                               if (m->desk == NULL) {
+                                       add_desktop(m, make_desktop(NULL, XCB_NONE));
+                               }
+                               add_monitor(m);
+                       }
+                       continue;
+               } else if (keyeq("focusHistory", t, json)) {
+                       t++;
+                       restore_history(&t, json);
+                       continue;
+               } else if (keyeq("stackingList", t, json)) {
+                       t++;
+                       restore_stack(&t, json);
+                       continue;
+               }
+               t++;
+       }
+
+       if (focusedMonitorName != NULL) {
+               coordinates_t loc;
+               if (locate_monitor(focusedMonitorName, &loc)) {
+                       mon = loc.monitor;
+               }
+       }
+
+       if (primaryMonitorName != NULL) {
+               coordinates_t loc;
+               if (locate_monitor(primaryMonitorName, &loc)) {
+                       pri_mon = loc.monitor;
+               }
+       }
+
+       free(focusedMonitorName);
+       free(primaryMonitorName);
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       refresh_presel_feebacks_in(d->root, d, m);
+                       restack_presel_feedback(d);
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                               if (n->client == NULL) {
+                                       continue;
+                               }
+                               uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+                               xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
+                       }
+               }
+       }
+
+       ewmh_update_client_list(false);
+       ewmh_update_client_list(true);
+       ewmh_update_active_window();
+       ewmh_update_number_of_desktops();
+       ewmh_update_current_desktop();
+       ewmh_update_desktop_names();
+
+       free(tokens);
+       free(json);
+
+       return true;
+}
+
+#define RESTORE_INT(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%i", p);
+
+#define RESTORE_UINT(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%u", p);
+
+#define RESTORE_USINT(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%hu", p);
+
+#define RESTORE_DOUBLE(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%lf", p);
+
+#define RESTORE_ANY(k, p, f) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               char *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \
+               f(val, p); \
+               free(val);
+
+#define RESTORE_BOOL(k, p)  RESTORE_ANY(k, p, parse_bool)
+
+monitor_t *restore_monitor(jsmntok_t **t, char *json)
+{
+       int num = (*t)->size;
+       (*t)++;
+       monitor_t *m = make_monitor(NULL, UINT32_MAX);
+       char *focusedDesktopName = NULL;
+
+       for (int i = 0; i < num; i++) {
+               if (keyeq("name", *t, json)) {
+                       (*t)++;
+                       snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+               RESTORE_UINT(id, &m->id)
+               RESTORE_UINT(randrId, &m->randr_id)
+               RESTORE_BOOL(wired, &m->wired)
+               RESTORE_UINT(stickyCount, &m->sticky_count)
+               RESTORE_INT(windowGap, &m->window_gap)
+               RESTORE_UINT(borderWidth, &m->border_width)
+               } else if (keyeq("padding", *t, json)) {
+                       (*t)++;
+                       restore_padding(&m->padding, t, json);
+                       continue;
+               } else if (keyeq("rectangle", *t, json)) {
+                       (*t)++;
+                       restore_rectangle(&m->rectangle, t, json);
+                       update_root(m, &m->rectangle);
+                       continue;
+               } else if (keyeq("focusedDesktopName", *t, json)) {
+                       free(focusedDesktopName);
+                       (*t)++;
+                       focusedDesktopName = copy_string(json + (*t)->start, (*t)->end - (*t)->start);
+               } else if (keyeq("desktops", *t, json)) {
+                       (*t)++;
+                       int s = (*t)->size;
+                       (*t)++;
+                       for (int j = 0; j < s; j++) {
+                               desktop_t *d = restore_desktop(t, json);
+                               add_desktop(m, d);
+                       }
+                       continue;
+               } else {
+                       warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                       (*t)++;
+               }
+               (*t)++;
+       }
+
+       if (focusedDesktopName != NULL) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       if (streq(focusedDesktopName, d->name)) {
+                               m->desk = d;
+                               break;
+                       }
+               }
+       }
+
+       free(focusedDesktopName);
+
+       return m;
+}
+
+desktop_t *restore_desktop(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+       desktop_t *d = make_desktop(NULL, UINT32_MAX);
+       xcb_window_t focusedNodeId = XCB_NONE;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("name", *t, json)) {
+                       (*t)++;
+                       snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+               RESTORE_UINT(id, &d->id)
+               RESTORE_ANY(layout, &d->layout, parse_layout)
+               RESTORE_INT(windowGap, &d->window_gap)
+               RESTORE_UINT(borderWidth, &d->border_width)
+               } else if (keyeq("focusedNodeId", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%u", &focusedNodeId);
+               } else if (keyeq("padding", *t, json)) {
+                       (*t)++;
+                       restore_padding(&d->padding, t, json);
+                       continue;
+               } else if (keyeq("root", *t, json)) {
+                       (*t)++;
+                       d->root = restore_node(t, json);
+                       continue;
+               } else {
+                       warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                       (*t)++;
+               }
+               (*t)++;
+       }
+
+       if (focusedNodeId != XCB_NONE) {
+               d->focus = find_by_id_in(d->root, focusedNodeId);
+       }
+
+       return d;
+}
+
+node_t *restore_node(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               /* hack to prevent a new ID from being generated */
+               node_t *n = make_node(UINT32_MAX);
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("id", *t, json)) {
+                               (*t)++;
+                               sscanf(json + (*t)->start, "%u", &n->id);
+                       RESTORE_ANY(splitType, &n->split_type, parse_split_type)
+                       RESTORE_DOUBLE(splitRatio, &n->split_ratio)
+                       RESTORE_INT(birthRotation, &n->birth_rotation)
+                       RESTORE_BOOL(vacant, &n->vacant)
+                       RESTORE_BOOL(sticky, &n->sticky)
+                       RESTORE_BOOL(private, &n->private)
+                       RESTORE_BOOL(locked, &n->locked)
+                       } else if (keyeq("presel", *t, json)) {
+                               (*t)++;
+                               n->presel = restore_presel(t, json);
+                               continue;
+                       } else if (keyeq("rectangle", *t, json)) {
+                               (*t)++;
+                               restore_rectangle(&n->rectangle, t, json);
+                               continue;
+                       } else if (keyeq("firstChild", *t, json)) {
+                               (*t)++;
+                               node_t *fc = restore_node(t, json);
+                               n->first_child = fc;
+                               if (fc != NULL) {
+                                       fc->parent = n;
+                               }
+                               continue;
+                       } else if (keyeq("secondChild", *t, json)) {
+                               (*t)++;
+                               node_t *sc = restore_node(t, json);
+                               n->second_child = sc;
+                               if (sc != NULL) {
+                                       sc->parent = n;
+                               }
+                               continue;
+                       } else if (keyeq("client", *t, json)) {
+                               (*t)++;
+                               n->client = restore_client(t, json);
+                               continue;
+                       } else {
+                               warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                               (*t)++;
+                       }
+                       (*t)++;
+               }
+
+               return n;
+       }
+}
+
+presel_t *restore_presel(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               presel_t *p = make_presel();
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("splitRatio", *t, json)) {
+                               (*t)++;
+                               sscanf(json + (*t)->start, "%lf", &p->split_ratio);
+                       RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
+                       }
+
+                       (*t)++;
+               }
+
+               return p;
+       }
+}
+
+
+client_t *restore_client(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               client_t *c = make_client();
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("className", *t, json)) {
+                               (*t)++;
+                               snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+                       } else if (keyeq("instanceName", *t, json)) {
+                               (*t)++;
+                               snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+                       RESTORE_ANY(state, &c->state, parse_client_state)
+                       RESTORE_ANY(lastState, &c->last_state, parse_client_state)
+                       RESTORE_ANY(layer, &c->layer, parse_stack_layer)
+                       RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
+                       RESTORE_UINT(borderWidth, &c->border_width)
+                       RESTORE_BOOL(urgent, &c->urgent)
+                       RESTORE_BOOL(visible, &c->visible)
+                       RESTORE_BOOL(icccmFocus, &c->icccm_focus)
+                       RESTORE_BOOL(icccmInput, &c->icccm_input)
+                       RESTORE_USINT(minWidth, &c->min_width)
+                       RESTORE_USINT(maxWidth, &c->max_width)
+                       RESTORE_USINT(minHeight, &c->min_height)
+                       RESTORE_USINT(maxHeight, &c->max_height)
+                       RESTORE_INT(wmStatesCount, &c->wm_states_count)
+                       } else if (keyeq("wmState", *t, json)) {
+                               (*t)++;
+                               restore_wm_state(c->wm_state, t, json);
+                               continue;
+                       } else if (keyeq("tiledRectangle", *t, json)) {
+                               (*t)++;
+                               restore_rectangle(&c->tiled_rectangle, t, json);
+                               continue;
+                       } else if (keyeq("floatingRectangle", *t, json)) {
+                               (*t)++;
+                               restore_rectangle(&c->floating_rectangle, t, json);
+                               continue;
+                       } else {
+                               warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                               (*t)++;
+                       }
+
+                       (*t)++;
+               }
 
-void restore_tree(char *file_path)
+               return c;
+       }
+}
+
+void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
 {
-    if (file_path == NULL)
-        return;
-
-    FILE *snapshot = fopen(file_path, "r");
-    if (snapshot == NULL) {
-        warn("Restore tree: can't open file\n");
-        return;
-    }
-
-    PUTS("restore tree");
-
-    char line[MAXLEN];
-    char name[MAXLEN];
-    coordinates_t loc;
-    monitor_t *m = NULL;
-    desktop_t *d = NULL;
-    node_t *n = NULL;
-    num_clients = 0;
-    unsigned int level, last_level = 0;
-
-    while (fgets(line, sizeof(line), snapshot) != NULL) {
-        unsigned int len = strlen(line);
-        level = 0;
-
-        while (level < len && isspace(line[level]))
-            level++;
-
-        if (level == 0) {
-            int x, y, left, right, top, bottom;
-            unsigned int w, h;
-            char end = 0;
-            name[0] = '\0';
-            sscanf(line + level, "%s %ux%u%i%i %i,%i,%i,%i %c", name, &w, &h, &x, &y, &top, &right, &bottom, &left, &end);
-            m = find_monitor(name);
-            if (m == NULL)
-                continue;
-            m->rectangle = (xcb_rectangle_t) {x, y, w, h};
-            m->top_padding = top;
-            m->right_padding = right;
-            m->bottom_padding = bottom;
-            m->left_padding = left;
-            if (end != 0)
-                mon = m;
-        } else if (level == 2) {
-            if (m == NULL)
-                continue;
-            int wg;
-            unsigned int bw;
-            char floating, layout = 0, end = 0;
-            name[0] = '\0';
-            loc.desktop = NULL;
-            sscanf(line + level, "%s %u %i %c %c %c", name, &bw, &wg, &layout, &floating, &end);
-            locate_desktop(name, &loc);
-            d = loc.desktop;
-            if (d == NULL)
-                continue;
-            d->border_width = bw;
-            d->window_gap = wg;
-            if (layout == 'M')
-                d->layout = LAYOUT_MONOCLE;
-            else if (layout == 'T')
-                d->layout = LAYOUT_TILED;
-            d->floating = (floating == '-' ? false : true);
-            if (end != 0)
-                m->desk = d;
-
-        } else {
-            if (m == NULL || d == NULL)
-                continue;
-            node_t *birth = make_node();
-            if (level == 4) {
-                empty_desktop(d);
-                d->root = birth;
-            } else if (n != NULL) {
-                if (level > last_level) {
-                    n->first_child = birth;
-                } else {
-                    do {
-                        n = n->parent;
-                    } while (n != NULL && n->second_child != NULL);
-                    if (n == NULL)
-                        continue;
-                    n->second_child = birth;
-                }
-                birth->parent = n;
-            }
-            n = birth;
-
-            char br;
-            if (isupper(line[level])) {
-                char st;
-                sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio);
-                if (st == 'H')
-                    n->split_type = TYPE_HORIZONTAL;
-                else if (st == 'V')
-                    n->split_type = TYPE_VERTICAL;
-            } else {
-                client_t *c = make_client(XCB_NONE);
-                num_clients++;
-                char floating, transient, fullscreen, urgent, locked, sticky, frame, private, sd, sm, end = 0;
-                sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &transient, &fullscreen, &urgent, &locked, &sticky, &frame, &private, &sm, &end);
-                c->floating = (floating == '-' ? false : true);
-                c->transient = (transient == '-' ? false : true);
-                c->fullscreen = (fullscreen == '-' ? false : true);
-                c->urgent = (urgent == '-' ? false : true);
-                c->locked = (locked == '-' ? false : true);
-                c->sticky = (sticky == '-' ? false : true);
-                c->frame = (frame == '-' ? false : true);
-                c->private = (private == '-' ? false : true);
-                n->split_mode = (sm == '-' ? MODE_AUTOMATIC : MODE_MANUAL);
-                if (sd == 'U')
-                    n->split_dir = DIR_UP;
-                else if (sd == 'R')
-                    n->split_dir = DIR_RIGHT;
-                else if (sd == 'D')
-                    n->split_dir = DIR_DOWN;
-                else if (sd == 'L')
-                    n->split_dir = DIR_LEFT;
-                n->client = c;
-                if (end != 0)
-                    d->focus = n;
-                if (c->sticky)
-                    m->num_sticky++;
-            }
-            if (br == 'a')
-                n->birth_rotation = 90;
-            else if (br == 'c')
-                n->birth_rotation = 270;
-            else if (br == 'm')
-                n->birth_rotation = 0;
-        }
-        last_level = level;
-    }
-
-    fclose(snapshot);
-
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                uint32_t values[] = {get_event_mask(n->client)};
-                xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
-                if (n->client->floating) {
-                    n->vacant = true;
-                    update_vacant_state(n->parent);
-                }
-                if (n->client->private)
-                    update_privacy_level(n, true);
-            }
-    ewmh_update_current_desktop();
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("x", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hi", &r->x);
+               } else if (keyeq("y", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hi", &r->y);
+               } else if (keyeq("width", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hu", &r->width);
+               } else if (keyeq("height", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hu", &r->height);
+               }
+               (*t)++;
+       }
 }
 
-void restore_history(char *file_path)
+void restore_padding(padding_t *p, jsmntok_t **t, char *json)
 {
-    if (file_path == NULL)
-        return;
-
-    FILE *snapshot = fopen(file_path, "r");
-    if (snapshot == NULL) {
-        warn("Restore history: can't open '%s'.\n", file_path);
-        return;
-    }
-
-    PUTS("restore history");
-
-    char line[MAXLEN];
-    char mnm[SMALEN];
-    char dnm[SMALEN];
-    xcb_window_t win;
-
-    while (fgets(line, sizeof(line), snapshot) != NULL) {
-        if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
-            coordinates_t loc;
-            if (win != XCB_NONE && !locate_window(win, &loc)) {
-                warn("Can't locate window 0x%X.\n", win);
-                continue;
-            }
-            node_t *n = (win == XCB_NONE ? NULL : loc.node);
-            if (!locate_desktop(dnm, &loc)) {
-                warn("Can't locate desktop '%s'.\n", dnm);
-                continue;
-            }
-            desktop_t *d = loc.desktop;
-            if (!locate_monitor(mnm, &loc)) {
-                warn("Can't locate monitor '%s'.\n", mnm);
-                continue;
-            }
-            monitor_t *m = loc.monitor;
-            history_add(m, d, n);
-        } else {
-            warn("Can't parse history entry: '%s'\n", line);
-        }
-    }
-
-    fclose(snapshot);
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("top", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->top);
+               } else if (keyeq("right", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->right);
+               } else if (keyeq("bottom", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->bottom);
+               } else if (keyeq("left", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->left);
+               }
+               (*t)++;
+       }
 }
 
-void restore_stack(char *file_path)
+void restore_history(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               coordinates_t loc = {NULL, NULL, NULL};
+               restore_coordinates(&loc, t, json);
+               if (loc.monitor != NULL && loc.desktop != NULL) {
+                       history_add(loc.monitor, loc.desktop, loc.node);
+               }
+       }
+}
+
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("monitorName", *t, json)) {
+                       (*t)++;
+                       char *name = copy_string(json + (*t)->start, (*t)->end - (*t)->start);
+                       loc->monitor = find_monitor(name);
+                       free(name);
+               } else if (keyeq("desktopName", *t, json)) {
+                       (*t)++;
+                       char *name = copy_string(json + (*t)->start, (*t)->end - (*t)->start);
+                       loc->desktop = find_desktop_in(name, loc->monitor);
+                       free(name);
+               } else if (keyeq("nodeId", *t, json)) {
+                       (*t)++;
+                       uint32_t id;
+                       sscanf(json + (*t)->start, "%u", &id);
+                       loc->node = find_by_id_in(loc->desktop!=NULL?loc->desktop->root:NULL, id);
+               }
+               (*t)++;
+       }
+}
+
+void restore_stack(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               uint32_t id;
+               sscanf(json + (*t)->start, "%u", &id);
+               coordinates_t loc;
+               if (locate_window(id, &loc)) {
+                       stack_insert_after(stack_tail, loc.node);
+               }
+               (*t)++;
+       }
+}
+
+void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               sscanf(json + (*t)->start, "%u", &w[i]);
+               (*t)++;
+       }
+}
+
+#undef RESTORE_INT
+#undef RESTORE_UINT
+#undef RESTORE_USINT
+#undef RESTORE_DOUBLE
+#undef RESTORE_ANY
+#undef RESTORE_BOOL
+
+bool keyeq(char *s, jsmntok_t *key, char *json)
 {
-    if (file_path == NULL)
-        return;
-
-    FILE *snapshot = fopen(file_path, "r");
-    if (snapshot == NULL) {
-        warn("Restore stack: can't open '%s'.\n", file_path);
-        return;
-    }
-
-    PUTS("restore stack");
-
-    char line[MAXLEN];
-    xcb_window_t win;
-
-    while (fgets(line, sizeof(line), snapshot) != NULL) {
-        if (sscanf(line, "%X", &win) == 1) {
-            coordinates_t loc;
-            if (locate_window(win, &loc))
-                stack_insert_after(stack_tail, loc.node);
-            else
-                warn("Can't locate window 0x%X.\n", win);
-        } else {
-            warn("Can't parse stack entry: '%s'\n", line);
-        }
-    }
-
-    fclose(snapshot);
+       size_t n = key->end - key->start;
+       return (strlen(s) == n && strncmp(s, json + key->start, n) == 0);
 }