1 /* Copyright (c) 2012, Bastien Dejean
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
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.
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.
44 bool restore_tree(const char *file_path)
47 char *json = read_string(file_path, &jslen);
55 jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
58 perror("Restore tree: malloc");
66 while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
68 jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
69 if (rtokens == NULL) {
70 perror("Restore tree: realloc");
80 warn("Restore tree: jsmn_parse: ");
82 case JSMN_ERROR_NOMEM:
83 warn("not enough memory.\n");
85 case JSMN_ERROR_INVAL:
86 warn("found invalid character inside JSON string.\n");
89 warn("not a full JSON packet.\n");
92 warn("unknown error.\n");
102 while (mon_head != NULL) {
103 remove_monitor(mon_head);
106 int num = tokens[0].size;
107 jsmntok_t *t = tokens + 1;
108 char *focusedMonitorName = NULL;
110 for (int i = 0; i < num; i++) {
111 if (keyeq("focusedMonitorName", t, json)) {
112 focusedMonitorName = copy_string(t+1, json);
114 } else if (keyeq("numClients", t, json)) {
116 sscanf(json + t->start, "%u", &num_clients);
117 } else if (keyeq("monitors", t, json)) {
121 for (int j = 0; j < s; j++) {
122 monitor_t *m = restore_monitor(&t, json);
129 if (focusedMonitorName != NULL) {
131 if (locate_monitor(focusedMonitorName, &loc)) {
136 free(focusedMonitorName);
138 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
139 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
140 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
141 uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
142 xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
147 ewmh_update_client_list();
148 ewmh_update_number_of_desktops();
149 ewmh_update_current_desktop();
150 ewmh_update_desktop_names();
158 #define RESTORE_INT(o, k, p) \
159 } else if (keyeq(#k, *t, json)) { \
161 sscanf(json + (*t)->start, "%i", &o->p);
163 #define RESTORE_UINT(o, k, p) \
164 } else if (keyeq(#k, *t, json)) { \
166 sscanf(json + (*t)->start, "%u", &o->p);
168 #define RESTORE_USINT(o, k, p) \
169 } else if (keyeq(#k, *t, json)) { \
171 sscanf(json + (*t)->start, "%hu", &o->p);
173 #define RESTORE_DOUBLE(o, k, p) \
174 } else if (keyeq(#k, *t, json)) { \
176 sscanf(json + (*t)->start, "%lf", &o->p);
178 #define RESTORE_ANY(o, k, p, f) \
179 } else if (keyeq(#k, *t, json)) { \
181 char *val = copy_string(*t, json); \
185 #define RESTORE_BOOL(o, k, p) RESTORE_ANY(o, k, p, parse_bool)
187 monitor_t *restore_monitor(jsmntok_t **t, char *json)
189 int num = (*t)->size;
191 monitor_t *m = make_monitor(NULL);
192 char *focusedDesktopName = NULL;
194 for (int i = 0; i < num; i++) {
195 if (keyeq("name", *t, json)) {
197 snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
198 RESTORE_UINT(m, id, id)
199 RESTORE_BOOL(m, wired, wired)
200 RESTORE_INT(m, topPadding, top_padding)
201 RESTORE_INT(m, rightPadding, right_padding)
202 RESTORE_INT(m, bottomPadding, bottom_padding)
203 RESTORE_INT(m, leftPadding, left_padding)
204 RESTORE_INT(m, numSticky, num_sticky)
205 } else if (keyeq("rectangle", *t, json)) {
207 restore_rectangle(&m->rectangle, t, json);
208 update_root(m, &m->rectangle);
210 } else if (keyeq("focusedDesktopName", *t, json)) {
212 focusedDesktopName = copy_string(*t, json);
213 } else if (keyeq("desktops", *t, json)) {
217 for (int j = 0; j < s; j++) {
218 desktop_t *d = restore_desktop(t, json);
223 warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
229 if (focusedDesktopName != NULL) {
230 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
231 if (streq(focusedDesktopName, d->name)) {
238 free(focusedDesktopName);
243 desktop_t *restore_desktop(jsmntok_t **t, char *json)
247 desktop_t *d = make_desktop(NULL);
248 xcb_window_t focusedWindow = XCB_NONE;
250 for (int i = 0; i < s; i++) {
251 if (keyeq("name", *t, json)) {
253 snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
254 } else if (keyeq("layout", *t, json)) {
256 char *val = copy_string(*t, json);
258 if (parse_layout(val, &lyt)) {
262 RESTORE_INT(d, topPadding, top_padding)
263 RESTORE_INT(d, rightPadding, right_padding)
264 RESTORE_INT(d, bottomPadding, bottom_padding)
265 RESTORE_INT(d, leftPadding, left_padding)
266 RESTORE_INT(d, windowGap, window_gap)
267 RESTORE_UINT(d, borderWidth, border_width)
268 } else if (keyeq("focusedWindow", *t, json)) {
270 sscanf(json + (*t)->start, "%u", &focusedWindow);
271 } else if (keyeq("root", *t, json)) {
273 d->root = restore_node(t, json);
276 warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
282 if (focusedWindow != XCB_NONE) {
283 for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
284 if (f->client->window == focusedWindow) {
294 node_t *restore_node(jsmntok_t **t, char *json)
296 if ((*t)->type == JSMN_PRIMITIVE) {
302 node_t *n = make_node();
304 for (int i = 0; i < s; i++) {
305 if (keyeq("splitType", *t, json)) {
307 char *val = copy_string(*t, json);
308 parse_split_type(val, &n->split_type);
310 RESTORE_DOUBLE(n, splitRatio, split_ratio)
311 RESTORE_ANY(n, splitMode, split_mode, parse_split_mode)
312 RESTORE_ANY(n, splitDir, split_dir, parse_direction)
313 RESTORE_INT(n, birthRotation, birth_rotation)
314 RESTORE_INT(n, privacyLevel, privacy_level)
315 RESTORE_ANY(n, vacant, vacant, parse_bool)
316 } else if (keyeq("rectangle", *t, json)) {
318 restore_rectangle(&n->rectangle, t, json);
320 } else if (keyeq("firstChild", *t, json)) {
322 node_t *fc = restore_node(t, json);
328 } else if (keyeq("secondChild", *t, json)) {
330 node_t *sc = restore_node(t, json);
331 n->second_child = sc;
336 } else if (keyeq("client", *t, json)) {
338 n->client = restore_client(t, json);
348 client_t *restore_client(jsmntok_t **t, char *json)
350 if ((*t)->type == JSMN_PRIMITIVE) {
356 client_t *c = make_client(XCB_NONE, 0);
358 for (int i = 0; i < s; i++) {
359 if (keyeq("window", *t, json)) {
361 sscanf(json + (*t)->start, "%u", &c->window);
362 } else if (keyeq("className", *t, json)) {
364 snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
365 } else if (keyeq("instanceName", *t, json)) {
367 snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
368 RESTORE_ANY(c, state, state, parse_client_state)
369 RESTORE_ANY(c, lastState, last_state, parse_client_state)
370 RESTORE_ANY(c, layer, layer, parse_stack_layer)
371 RESTORE_ANY(c, lastLayer, last_layer, parse_stack_layer)
372 RESTORE_UINT(c, borderWidth, border_width)
373 RESTORE_BOOL(c, locked, locked)
374 RESTORE_BOOL(c, sticky, sticky)
375 RESTORE_BOOL(c, urgent, urgent)
376 RESTORE_BOOL(c, private, private)
377 RESTORE_BOOL(c, icccmFocus, icccm_focus)
378 RESTORE_BOOL(c, icccmInput, icccm_input)
379 RESTORE_USINT(c, minWidth, min_width)
380 RESTORE_USINT(c, maxWidth, max_width)
381 RESTORE_USINT(c, minHeight, min_height)
382 RESTORE_USINT(c, maxHeight, max_height)
383 RESTORE_INT(c, numStates, num_states)
384 } else if (keyeq("wmState", *t, json)) {
386 restore_wm_state(c->wm_state, t, json);
388 } else if (keyeq("tiledRectangle", *t, json)) {
390 restore_rectangle(&c->tiled_rectangle, t, json);
392 } else if (keyeq("floatingRectangle", *t, json)) {
394 restore_rectangle(&c->floating_rectangle, t, json);
405 void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
410 for (int i = 0; i < s; i++) {
411 if (keyeq("x", *t, json)) {
413 sscanf(json + (*t)->start, "%hi", &r->x);
414 } else if (keyeq("y", *t, json)) {
416 sscanf(json + (*t)->start, "%hi", &r->y);
417 } else if (keyeq("width", *t, json)) {
419 sscanf(json + (*t)->start, "%hu", &r->width);
420 } else if (keyeq("height", *t, json)) {
422 sscanf(json + (*t)->start, "%hu", &r->height);
428 void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json)
433 for (int i = 0; i < s; i++) {
434 sscanf(json + (*t)->start, "%u", &w[i]);
442 #undef RESTORE_DOUBLE
446 bool keyeq(char *s, jsmntok_t *key, char *json)
448 return (strncmp(s, json + key->start, key->end - key->start) == 0);
451 char *copy_string(jsmntok_t *tok, char *json)
453 size_t len = tok->end - tok->start + 1;
454 char *res = malloc(len * sizeof(char));
456 perror("Copy string: malloc");
459 strncpy(res, json+tok->start, len-1);
464 bool restore_history(const char *file_path)
466 if (file_path == NULL) {
470 FILE *snapshot = fopen(file_path, "r");
471 if (snapshot == NULL) {
472 perror("Restore history: fopen");
483 while (fgets(line, sizeof(line), snapshot) != NULL) {
484 if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
486 if (win != XCB_NONE && !locate_window(win, &loc)) {
487 warn("Can't locate window 0x%X.\n", win);
490 node_t *n = (win == XCB_NONE ? NULL : loc.node);
491 if (!locate_desktop(dnm, &loc)) {
492 warn("Can't locate desktop '%s'.\n", dnm);
495 desktop_t *d = loc.desktop;
496 if (!locate_monitor(mnm, &loc)) {
497 warn("Can't locate monitor '%s'.\n", mnm);
500 monitor_t *m = loc.monitor;
501 history_add(m, d, n);
503 warn("Can't parse history entry: '%s'\n", line);
511 bool restore_stack(const char *file_path)
513 if (file_path == NULL) {
517 FILE *snapshot = fopen(file_path, "r");
518 if (snapshot == NULL) {
519 perror("Restore stack: fopen");
526 while (stack_head != NULL) {
527 remove_stack(stack_head);
530 while (fgets(line, sizeof(line), snapshot) != NULL) {
531 if (sscanf(line, "%X", &win) == 1) {
533 if (locate_window(win, &loc)) {
534 stack_insert_after(stack_tail, loc.node);
536 warn("Can't locate window 0x%X.\n", win);
539 warn("Can't parse stack entry: '%s'\n", line);