X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=restore.c;h=e7aead4ad5a698ba74f555ecd8b2e01bf1b10bd8;hb=a7821774c50aefa8c758b964814818b4634b77ce;hp=037496b27aa91b4da9de5c441c1b8d86e0d34fd2;hpb=f17cee7afdb710576f15d75b66e940bd26e9a4db;p=bspwm.git diff --git a/restore.c b/restore.c index 037496b..e7aead4 100644 --- a/restore.c +++ b/restore.c @@ -22,8 +22,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include +#include #include +#include #include "bspwm.h" #include "desktop.h" #include "ewmh.h" @@ -34,255 +36,524 @@ #include "tree.h" #include "settings.h" #include "restore.h" +#include "window.h" +#include "parse.h" -void restore_tree(char *file_path) +bool restore_tree(const char *file_path) { - if (file_path == NULL) - return; + size_t jslen; + char *json = read_string(file_path, &jslen); - FILE *snapshot = fopen(file_path, "r"); - if (snapshot == NULL) { - warn("Restore tree: can't open '%s'.\n", file_path); - return; + if (json == NULL) { + return false; } - char line[MAXLEN]; - char name[MAXLEN]; - coordinates_t loc; - monitor_t *m = NULL; - desktop_t *d = NULL; - node_t *n = NULL; - 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, top, right, bottom, left; - 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 == 1) { - if (m == NULL) - continue; - int wg, top, right, bottom, left; - unsigned int bw; - char floating, layout = 0, end = 0; - name[0] = '\0'; - loc.desktop = NULL; - sscanf(line + level, "%s %u %i %i,%i,%i,%i %c %c %c", name, - &bw, &wg, &top, &right, &bottom, &left, &layout, &floating, &end); - locate_desktop(name, &loc); - d = loc.desktop; - if (d == NULL) { - continue; - } - d->border_width = bw; - d->window_gap = wg; - d->top_padding = top; - d->right_padding = right; - d->bottom_padding = bottom; - d->left_padding = left; - if (layout == 'M') { - d->layout = LAYOUT_MONOCLE; - } else if (layout == 'T') { - d->layout = LAYOUT_TILED; - } - if (end != 0) { - m->desk = d; - } + 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 { - if (m == NULL || d == NULL) - continue; - node_t *birth = make_node(); - if (level == 2) { - 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 birth_rotation; - if (isupper(line[level])) { - char split_type; - sscanf(line + level, "%c %c %lf", &split_type, &birth_rotation, &n->split_ratio); - if (split_type == 'H') { - n->split_type = TYPE_HORIZONTAL; - } else if (split_type == 'V') { - n->split_type = TYPE_VERTICAL; - } - } else { - client_t *c = make_client(XCB_NONE, d->border_width); - num_clients++; - char urgent, locked, sticky, private, split_dir, split_mode, state, layer, end = 0; - sscanf(line + level, "%c %s %s %X %u %hux%hu%hi%hi %c%c %c%c %c%c%c%c %c", &birth_rotation, - c->class_name, c->instance_name, &c->window, &c->border_width, - &c->floating_rectangle.width, &c->floating_rectangle.height, - &c->floating_rectangle.x, &c->floating_rectangle.y, - &split_dir, &split_mode, &state, &layer, - &urgent, &locked, &sticky, &private, &end); - n->split_mode = (split_mode == '-' ? MODE_AUTOMATIC : MODE_MANUAL); - if (split_dir == 'U') { - n->split_dir = DIR_UP; - } else if (split_dir == 'R') { - n->split_dir = DIR_RIGHT; - } else if (split_dir == 'D') { - n->split_dir = DIR_DOWN; - } else if (split_dir == 'L') { - n->split_dir = DIR_LEFT; - } - if (state == 'f') { - c->state = STATE_FLOATING; - } else if (state == 'F') { - c->state = STATE_FULLSCREEN; - } else if (state == 'p') { - c->state = STATE_PSEUDO_TILED; - } - if (layer == 'b') { - c->layer = LAYER_BELOW; - } else if (layer == 'a') { - c->layer = LAYER_ABOVE; - } - c->urgent = (urgent == '-' ? false : true); - c->locked = (locked == '-' ? false : true); - c->sticky = (sticky == '-' ? false : true); - c->private = (private == '-' ? false : true); - n->client = c; - if (end != 0) { - d->focus = n; - } - if (c->sticky) { - m->num_sticky++; + 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; + uint32_t focused_monitor_id = 0, primary_monitor_id = 0; + + for (int i = 0; i < num; i++) { + if (keyeq("focusedMonitorId", t, json)) { + t++; + sscanf(json + t->start, "%u", &focused_monitor_id); + } else if (keyeq("primaryMonitorId", t, json)) { + t++; + sscanf(json + t->start, "%u", &primary_monitor_id); + } 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); } - if (birth_rotation == 'a') { - n->birth_rotation = 90; - } else if (birth_rotation == 'c') { - n->birth_rotation = 270; - } else if (birth_rotation == 'm') { - n->birth_rotation = 0; - } + 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; } - last_level = level; + t++; } - fclose(snapshot); + if (focused_monitor_id != 0) { + coordinates_t loc; + if (monitor_from_id(focused_monitor_id, &loc)) { + mon = loc.monitor; + } + } + + if (primary_monitor_id != 0) { + coordinates_t loc; + if (monitor_from_id(primary_monitor_id, &loc)) { + pri_mon = loc.monitor; + } + } 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)) { - uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)}; - xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values); - if (!IS_TILED(n->client)) { - n->vacant = true; - update_vacant_state(n->parent); - } - if (n->client->private) { - update_privacy_level(n, true); + if (n->client == NULL) { + continue; } + initialize_client(n); + 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); } - /* Has the side effect of restoring the node's rectangles and the client's tiled rectangles */ - arrange(m, d); } } + 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; } -void restore_history(char *file_path) +#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) { - if (file_path == NULL) - return; + int num = (*t)->size; + (*t)++; + monitor_t *m = make_monitor(NULL, UINT32_MAX); + uint32_t focused_desktop_id = 0; + + 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) + RESTORE_UINT(focusedDesktopId, &focused_desktop_id) + } 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("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)++; + } - FILE *snapshot = fopen(file_path, "r"); - if (snapshot == NULL) { - warn("Restore history: can't open '%s'.\n", file_path); - return; + if (focused_desktop_id != 0) { + for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { + if (d->id == focused_desktop_id) { + m->desk = d; + break; + } + } } - char line[MAXLEN]; - char mnm[SMALEN]; - char dnm[SMALEN]; - xcb_window_t win; + 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; - 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); + 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(hidden, &n->hidden) + 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; - } - node_t *n = (win == XCB_NONE ? NULL : loc.node); - if (!locate_desktop(dnm, &loc)) { - warn("Can't locate desktop '%s'.\n", dnm); + } 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) } - desktop_t *d = loc.desktop; - if (!locate_monitor(mnm, &loc)) { - warn("Can't locate monitor '%s'.\n", mnm); + + (*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(shown, &c->shown) + } 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)++; } - monitor_t *m = loc.monitor; - history_add(m, d, n); - } else { - warn("Can't parse history entry: '%s'\n", line); + + (*t)++; } + + return c; } +} - fclose(snapshot); +void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json) +{ + 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_stack(char *file_path) +void restore_padding(padding_t *p, jsmntok_t **t, char *json) { - if (file_path == NULL) - return; + int s = (*t)->size; + (*t)++; - FILE *snapshot = fopen(file_path, "r"); - if (snapshot == NULL) { - warn("Restore stack: can't open '%s'.\n", file_path); - return; + 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)++; } +} - char line[MAXLEN]; - xcb_window_t win; +void restore_history(jsmntok_t **t, char *json) +{ + int s = (*t)->size; + (*t)++; - 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); + 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)++; + uint32_t id = 0; + + for (int i = 0; i < s; i++) { + if (keyeq("monitorId", *t, json)) { + (*t)++; + sscanf(json + (*t)->start, "%u", &id); + loc->monitor = find_monitor(id); + } else if (keyeq("desktopId", *t, json)) { + (*t)++; + sscanf(json + (*t)->start, "%u", &id); + loc->desktop = find_desktop_in(id, loc->monitor); + } else if (keyeq("nodeId", *t, json)) { + (*t)++; + sscanf(json + (*t)->start, "%u", &id); + loc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id); + } + (*t)++; + } +} - fclose(snapshot); +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)++; + } +} + +#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) +{ + size_t n = key->end - key->start; + return (strlen(s) == n && strncmp(s, json + key->start, n) == 0); }