]> git.lizzy.rs Git - bspwm.git/commitdiff
Use JSON as the output format of query -T
authorBastien Dejean <nihilhill@gmail.com>
Sun, 22 Nov 2015 13:41:00 +0000 (14:41 +0100)
committerBastien Dejean <nihilhill@gmail.com>
Sun, 22 Nov 2015 13:41:00 +0000 (14:41 +0100)
It is now easy to access any attribute by piping the output of
`query -T` to a JSON extractor/filter.

E.g.:
bspc query -T -d DESKTOP_SEL | jq -r .layout

And it also makes `restore -T` more robust.

25 files changed:
Makefile
Sourcedeps
bspwm.c
bspwm.h
desktop.c
ewmh.c
helpers.c
helpers.h
history.c
jsmn.c [new file with mode: 0644]
jsmn.h [new file with mode: 0644]
messages.c
messages.h
monitor.c
monitor.h
parse.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
query.c
query.h
restore.c
restore.h
rule.c
tree.c
types.h
window.c

index 85ba0d352acb8d6505784a68664d4d8320ebcefa..d67c9b25f90e749b705f6cb573c3b82311dd83c1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,8 +16,8 @@ DOCPREFIX = $(PREFIX)/share/doc/bspwm
 MD_DOCS = README.md doc/CONTRIBUTING.md doc/INSTALL.md doc/MISC.md doc/TODO.md
 XSESSIONS = $(PREFIX)/share/xsessions
 
-WM_SRC = bspwm.c helpers.c settings.c monitor.c desktop.c tree.c stack.c history.c \
-        events.c pointer.c window.c messages.c query.c restore.c rule.c ewmh.c subscribe.c
+WM_SRC = bspwm.c helpers.c jsmn.c settings.c monitor.c desktop.c tree.c stack.c history.c \
+        events.c pointer.c window.c messages.c parse.c query.c restore.c rule.c ewmh.c subscribe.c
 WM_OBJ = $(WM_SRC:.c=.o)
 CL_SRC = bspc.c helpers.c
 CL_OBJ = $(CL_SRC:.c=.o)
index 8dd8a79bf20aab7701a250826d6791ec4bca972f..558f4f885706001926cc17357e241b207ef1ba20 100644 (file)
@@ -5,14 +5,16 @@ events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h query.h settings.
 ewmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h
 helpers.o: helpers.c bspwm.h helpers.h types.h
 history.o: history.c bspwm.h helpers.h query.h types.h
-messages.o: messages.c bspwm.h common.h desktop.h ewmh.h helpers.h history.h messages.h monitor.h pointer.h query.h restore.h rule.h settings.h subscribe.h tree.h types.h window.h
+jsmn.o: jsmn.c jsmn.h
+messages.o: messages.c bspwm.h common.h desktop.h ewmh.h helpers.h history.h jsmn.h messages.h monitor.h parse.h pointer.h query.h restore.h rule.h settings.h subscribe.h tree.h types.h window.h
 monitor.o: monitor.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
+parse.o: parse.c helpers.h parse.h types.h
 pointer.o: pointer.c bspwm.h helpers.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
-query.o: query.c bspwm.h desktop.h helpers.h history.h messages.h monitor.h query.h subscribe.h tree.h types.h
-restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h restore.h settings.h stack.h tree.h types.h
-rule.o: rule.c bspwm.h ewmh.h helpers.h messages.h rule.h settings.h subscribe.h types.h window.h
+query.o: query.c bspwm.h desktop.h helpers.h history.h jsmn.h monitor.h parse.h query.h tree.h types.h
+restore.o: restore.c bspwm.h common.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h query.h restore.h settings.h stack.h tree.h types.h
+rule.o: rule.c bspwm.h ewmh.h helpers.h parse.h rule.h settings.h types.h window.h
 settings.o: settings.c bspwm.h helpers.h settings.h types.h
 stack.o: stack.c bspwm.h helpers.h stack.h types.h window.h
 subscribe.o: subscribe.c bspwm.h helpers.h settings.h subscribe.h tree.h types.h
 tree.o: tree.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
-window.o: window.c bspwm.h ewmh.h helpers.h messages.h monitor.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
+window.o: window.c bspwm.h ewmh.h helpers.h monitor.h parse.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
diff --git a/bspwm.c b/bspwm.c
index 152fc30bb1a748094d2d7512f839365f3515a045..92e739f35dd4f39c9882398454b8ce2472c0571c 100644 (file)
--- a/bspwm.c
+++ b/bspwm.c
@@ -201,7 +201,7 @@ int main(int argc, char *argv[])
 
 void init(void)
 {
-       num_monitors = num_desktops = num_clients = 0;
+       num_clients = 0;
        monitor_uid = desktop_uid = 0;
        mon = mon_head = mon_tail = pri_mon = NULL;
        history_head = history_tail = history_needle = NULL;
@@ -295,7 +295,7 @@ void setup(void)
                        for (int i = 0; i < n; i++) {
                                xcb_xinerama_screen_info_t info = xsi[i];
                                xcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};
-                               monitor_t *m = make_monitor(rect);
+                               monitor_t *m = make_monitor(&rect);
                                add_monitor(m);
                                add_desktop(m, make_desktop(NULL));
                        }
@@ -303,7 +303,7 @@ void setup(void)
                } else {
                        warn("Xinerama is inactive.\n");
                        xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-                       monitor_t *m = make_monitor(rect);
+                       monitor_t *m = make_monitor(&rect);
                        add_monitor(m);
                        add_desktop(m, make_desktop(NULL));
                }
@@ -331,16 +331,18 @@ void register_events(void)
 
 void cleanup(void)
 {
-       while (mon_head != NULL)
+       while (mon_head != NULL) {
                remove_monitor(mon_head);
-       while (rule_head != NULL)
+       }
+       while (rule_head != NULL) {
                remove_rule(rule_head);
-       while (stack_head != NULL)
-               remove_stack(stack_head);
-       while (subscribe_head != NULL)
+       }
+       while (subscribe_head != NULL) {
                remove_subscriber(subscribe_head);
-       while (pending_rule_head != NULL)
+       }
+       while (pending_rule_head != NULL) {
                remove_pending_rule(pending_rule_head);
+       }
        empty_history();
        free(frozen_pointer);
 }
diff --git a/bspwm.h b/bspwm.h
index 2c8748731c8a081bd56755c3596b9ba6b4ae6960..5a5093309ae368ddfe3d41552a6b0864c5e1e56c 100644 (file)
--- a/bspwm.h
+++ b/bspwm.h
@@ -36,7 +36,6 @@
 xcb_connection_t *dpy;
 int default_screen, screen_width, screen_height;
 uint32_t num_clients;
-uint32_t num_desktops;
 unsigned int num_monitors;
 unsigned int monitor_uid;
 unsigned int desktop_uid;
index 3498c372f5d9e83087903024c6d1468c889e4b8a..41a8a4d00d7e920c512b6383d0abfda8549c621d 100644 (file)
--- a/desktop.c
+++ b/desktop.c
@@ -122,10 +122,11 @@ void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
 desktop_t *make_desktop(const char *name)
 {
        desktop_t *d = malloc(sizeof(desktop_t));
-       if (name == NULL)
+       if (name == NULL) {
                snprintf(d->name, sizeof(d->name), "%s%d", DEFAULT_DESK_NAME, ++desktop_uid);
-       else
+       } else {
                snprintf(d->name, sizeof(d->name), "%s", name);
+       }
        d->prev = d->next = NULL;
        d->root = d->focus = NULL;
        initialize_desktop(d);
@@ -169,7 +170,6 @@ void add_desktop(monitor_t *m, desktop_t *d)
        put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add %s %s\n", m->name, d->name);
 
        insert_desktop(m, d);
-       num_desktops++;
        ewmh_update_number_of_desktops();
        ewmh_update_desktop_names();
        ewmh_update_wm_desktops();
@@ -209,8 +209,6 @@ void remove_desktop(monitor_t *m, desktop_t *d)
        empty_desktop(d);
        free(d);
 
-       num_desktops--;
-
        ewmh_update_current_desktop();
        ewmh_update_number_of_desktops();
        ewmh_update_desktop_names();
diff --git a/ewmh.c b/ewmh.c
index 415abc366e6859948c3bd57dd3e1aa1014815b5a..81d11c234acc79b2b735e28ec0bd5d43e289c54c 100644 (file)
--- a/ewmh.c
+++ b/ewmh.c
@@ -44,16 +44,27 @@ void ewmh_update_active_window(void)
 
 void ewmh_update_number_of_desktops(void)
 {
+       uint32_t num_desktops = 0;
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       num_desktops++;
+               }
+       }
+
        xcb_ewmh_set_number_of_desktops(ewmh, default_screen, num_desktops);
 }
 
 uint32_t ewmh_get_desktop_index(desktop_t *d)
 {
        uint32_t i = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++)
-                       if (d == cd)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {
+                       if (d == cd) {
                                return i;
+                       }
+               }
+       }
        return 0;
 }
 
@@ -84,12 +95,14 @@ void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
 
 void ewmh_update_wm_desktops(void)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
                for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
                        uint32_t i = ewmh_get_desktop_index(d);
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
                                xcb_ewmh_set_wm_desktop(ewmh, n->client->window, i);
+                       }
                }
+       }
 }
 
 void ewmh_update_desktop_names(void)
@@ -99,17 +112,22 @@ void ewmh_update_desktop_names(void)
        uint32_t names_len;
        i = 0;
 
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
                for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++)
+                       for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) {
                                names[i + j] = d->name[j];
+                       }
                        i += j;
-                       if (i < sizeof(names))
+                       if (i < sizeof(names)) {
                                names[i++] = '\0';
+                       }
                }
+       }
 
-       if (i < 1)
+       if (i < 1) {
+               xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);
                return;
+       }
 
        names_len = i - 1;
        xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
@@ -126,10 +144,13 @@ void ewmh_update_client_list(void)
        xcb_window_t wins[num_clients];
        unsigned int i = 0;
 
-       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))
+       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)) {
                                wins[i++] = n->client->window;
+                       }
+               }
+       }
 
        xcb_ewmh_set_client_list(ewmh, default_screen, num_clients, wins);
        xcb_ewmh_set_client_list_stacking(ewmh, default_screen, num_clients, wins);
@@ -137,11 +158,14 @@ void ewmh_update_client_list(void)
 
 bool ewmh_wm_state_add(client_t *c, xcb_atom_t state)
 {
-       if (c->num_states >= MAX_STATE)
+       if (c->num_states >= MAX_STATE) {
                return false;
-       for (int i = 0; i < c->num_states; i++)
-               if (c->wm_state[i] == state)
+       }
+       for (int i = 0; i < c->num_states; i++) {
+               if (c->wm_state[i] == state) {
                        return false;
+               }
+       }
        c->wm_state[c->num_states] = state;
        c->num_states++;
        xcb_ewmh_set_wm_state(ewmh, c->window, c->num_states, c->wm_state);
index eb4529e3722a379e5300075d8d66a18705fba85b..c342b6c65b08afa39608a6c1f5cc3befc79df6ea 100644 (file)
--- a/helpers.c
+++ b/helpers.c
@@ -24,6 +24,8 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <math.h>
 #include "bspwm.h"
 
@@ -45,6 +47,58 @@ void err(char *fmt, ...)
        exit(EXIT_FAILURE);
 }
 
+char *read_string(const char *file_path, size_t *tlen)
+{
+       if (file_path == NULL) {
+               return NULL;
+       }
+
+       int fd = open(file_path, O_RDONLY);
+
+       if (fd == -1) {
+               perror("Read file: open");
+               return NULL;
+       }
+
+       char buf[BUFSIZ], *content;
+       size_t len = sizeof(buf);
+
+       if ((content = malloc(len * sizeof(char))) == NULL) {
+               perror("Read file: malloc");
+               return NULL;
+       }
+
+       int nb;
+       *tlen = 0;
+
+       while (true) {
+               nb = read(fd, buf, sizeof(buf));
+               if (nb < 0) {
+                       perror("Restore tree: read");
+                       free(content);
+                       return NULL;
+               } else if (nb == 0) {
+                       break;
+               } else {
+                       *tlen += nb;
+                       if (*tlen > len) {
+                               len *= 2;
+                               char *rcontent = realloc(content, len * sizeof(char));
+                               if (rcontent == NULL) {
+                                       perror("Read file: realloc");
+                                       free(content);
+                                       return NULL;
+                               } else {
+                                       content = rcontent;
+                               }
+                       }
+                       strncpy(content + (*tlen - nb), buf, nb);
+               }
+       }
+
+       return content;
+}
+
 bool get_color(char *col, xcb_window_t win, uint32_t *pxl)
 {
        xcb_colormap_t map = screen->default_colormap;
index da51f542c30f9808ef998ea216fa0dd5176d59da..7ca7870c446cc83eb0aea75623845ac51e7dd8e4 100644 (file)
--- a/helpers.h
+++ b/helpers.h
 #define LENGTH(x)         (sizeof(x) / sizeof(*x))
 #define MAX(A, B)         ((A) > (B) ? (A) : (B))
 #define MIN(A, B)         ((A) < (B) ? (A) : (B))
-#define BOOLSTR(A)        ((A) ? "true" : "false")
-#define ONOFFSTR(A)       ((A) ? "on" : "off")
-#define LAYERSTR(A)       ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
-#define STATESTR(A)       ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
+
 #define IS_TILED(c)       (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED)
 #define IS_FLOATING(c)    (c->state == STATE_FLOATING)
 #define IS_FULLSCREEN(c)  (c->state == STATE_FULLSCREEN)
 
+#define BOOL_STR(A)       ((A) ? "true" : "false")
+#define ON_OFF_STR(A)     ((A) ? "on" : "off")
+#define LAYOUT_STR(A)     ((A) == LAYOUT_TILED ? "tiled" : "monocle")
+#define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical")
+#define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual")
+#define SPLIT_DIR_STR(A)  ((A) == DIR_RIGHT ? "right" : ((A) == DIR_UP ? "up" : ((A) == DIR_LEFT ? "left" : "down")))
+#define STATE_STR(A)      ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
+#define LAYER_STR(A)      ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
+
 #define XCB_CONFIG_WINDOW_X_Y               (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y)
 #define XCB_CONFIG_WINDOW_WIDTH_HEIGHT      (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
 #define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT  (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
@@ -54,6 +60,7 @@
 
 void warn(char *fmt, ...);
 void err(char *fmt, ...);
+char *read_string(const char *file_path, size_t *tlen);
 bool get_color(char *col, xcb_window_t win, uint32_t *pxl);
 double distance(xcb_point_t a, xcb_point_t b);
 
index e2a425522994f9f1a170d8eb6375d0e2cfa6930c..3f3db237031f886e03f2c61fba4d948559076c3b 100644 (file)
--- a/history.c
+++ b/history.c
@@ -105,13 +105,13 @@ void history_remove(desktop_t *d, node_t *n)
                                /* remove duplicate entries */
                                while (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) ||
                                       (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) {
-                                       history_t *d = c->prev;
+                                       history_t *p = c->prev;
                                        if (history_head == c)
                                                history_head = history_tail;
                                        if (history_needle == c)
                                                history_needle = history_tail;
                                        free(c);
-                                       c = d;
+                                       c = p;
                                }
                                a->prev = c;
                        }
diff --git a/jsmn.c b/jsmn.c
new file mode 100644 (file)
index 0000000..2809669
--- /dev/null
+++ b/jsmn.c
@@ -0,0 +1,313 @@
+#include <stdlib.h>
+
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+               jsmntok_t *tokens, size_t num_tokens) {
+       jsmntok_t *tok;
+       if (parser->toknext >= num_tokens) {
+               return NULL;
+       }
+       tok = &tokens[parser->toknext++];
+       tok->start = tok->end = -1;
+       tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+       tok->parent = -1;
+#endif
+       return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+                            int start, int end) {
+       token->type = type;
+       token->start = start;
+       token->end = end;
+       token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+               size_t len, jsmntok_t *tokens, size_t num_tokens) {
+       jsmntok_t *token;
+       int start;
+
+       start = parser->pos;
+
+       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+               switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+                       /* In strict mode primitive must be followed by "," or "}" or "]" */
+                       case ':':
+#endif
+                       case '\t' : case '\r' : case '\n' : case ' ' :
+                       case ','  : case ']'  : case '}' :
+                               goto found;
+               }
+               if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+                       parser->pos = start;
+                       return JSMN_ERROR_INVAL;
+               }
+       }
+#ifdef JSMN_STRICT
+       /* In strict mode primitive must be followed by a comma/object/array */
+       parser->pos = start;
+       return JSMN_ERROR_PART;
+#endif
+
+found:
+       if (tokens == NULL) {
+               parser->pos--;
+               return 0;
+       }
+       token = jsmn_alloc_token(parser, tokens, num_tokens);
+       if (token == NULL) {
+               parser->pos = start;
+               return JSMN_ERROR_NOMEM;
+       }
+       jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+       token->parent = parser->toksuper;
+#endif
+       parser->pos--;
+       return 0;
+}
+
+/**
+ * Filsl next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+               size_t len, jsmntok_t *tokens, size_t num_tokens) {
+       jsmntok_t *token;
+
+       int start = parser->pos;
+
+       parser->pos++;
+
+       /* Skip starting quote */
+       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+               char c = js[parser->pos];
+
+               /* Quote: end of string */
+               if (c == '\"') {
+                       if (tokens == NULL) {
+                               return 0;
+                       }
+                       token = jsmn_alloc_token(parser, tokens, num_tokens);
+                       if (token == NULL) {
+                               parser->pos = start;
+                               return JSMN_ERROR_NOMEM;
+                       }
+                       jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+                       token->parent = parser->toksuper;
+#endif
+                       return 0;
+               }
+
+               /* Backslash: Quoted symbol expected */
+               if (c == '\\' && parser->pos + 1 < len) {
+                       int i;
+                       parser->pos++;
+                       switch (js[parser->pos]) {
+                               /* Allowed escaped symbols */
+                               case '\"': case '/' : case '\\' : case 'b' :
+                               case 'f' : case 'r' : case 'n'  : case 't' :
+                                       break;
+                               /* Allows escaped symbol \uXXXX */
+                               case 'u':
+                                       parser->pos++;
+                                       for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
+                                               /* If it isn't a hex character we have an error */
+                                               if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+                                                                       (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+                                                                       (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+                                                       parser->pos = start;
+                                                       return JSMN_ERROR_INVAL;
+                                               }
+                                               parser->pos++;
+                                       }
+                                       parser->pos--;
+                                       break;
+                               /* Unexpected symbol */
+                               default:
+                                       parser->pos = start;
+                                       return JSMN_ERROR_INVAL;
+                       }
+               }
+       }
+       parser->pos = start;
+       return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+               jsmntok_t *tokens, unsigned int num_tokens) {
+       int r;
+       int i;
+       jsmntok_t *token;
+       int count = parser->toknext;
+
+       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+               char c;
+               jsmntype_t type;
+
+               c = js[parser->pos];
+               switch (c) {
+                       case '{': case '[':
+                               count++;
+                               if (tokens == NULL) {
+                                       break;
+                               }
+                               token = jsmn_alloc_token(parser, tokens, num_tokens);
+                               if (token == NULL)
+                                       return JSMN_ERROR_NOMEM;
+                               if (parser->toksuper != -1) {
+                                       tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+                                       token->parent = parser->toksuper;
+#endif
+                               }
+                               token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+                               token->start = parser->pos;
+                               parser->toksuper = parser->toknext - 1;
+                               break;
+                       case '}': case ']':
+                               if (tokens == NULL)
+                                       break;
+                               type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+                               if (parser->toknext < 1) {
+                                       return JSMN_ERROR_INVAL;
+                               }
+                               token = &tokens[parser->toknext - 1];
+                               for (;;) {
+                                       if (token->start != -1 && token->end == -1) {
+                                               if (token->type != type) {
+                                                       return JSMN_ERROR_INVAL;
+                                               }
+                                               token->end = parser->pos + 1;
+                                               parser->toksuper = token->parent;
+                                               break;
+                                       }
+                                       if (token->parent == -1) {
+                                               break;
+                                       }
+                                       token = &tokens[token->parent];
+                               }
+#else
+                               for (i = parser->toknext - 1; i >= 0; i--) {
+                                       token = &tokens[i];
+                                       if (token->start != -1 && token->end == -1) {
+                                               if (token->type != type) {
+                                                       return JSMN_ERROR_INVAL;
+                                               }
+                                               parser->toksuper = -1;
+                                               token->end = parser->pos + 1;
+                                               break;
+                                       }
+                               }
+                               /* Error if unmatched closing bracket */
+                               if (i == -1) return JSMN_ERROR_INVAL;
+                               for (; i >= 0; i--) {
+                                       token = &tokens[i];
+                                       if (token->start != -1 && token->end == -1) {
+                                               parser->toksuper = i;
+                                               break;
+                                       }
+                               }
+#endif
+                               break;
+                       case '\"':
+                               r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+                               if (r < 0) return r;
+                               count++;
+                               if (parser->toksuper != -1 && tokens != NULL)
+                                       tokens[parser->toksuper].size++;
+                               break;
+                       case '\t' : case '\r' : case '\n' : case ' ':
+                               break;
+                       case ':':
+                               parser->toksuper = parser->toknext - 1;
+                               break;
+                       case ',':
+                               if (tokens != NULL && parser->toksuper != -1 &&
+                                               tokens[parser->toksuper].type != JSMN_ARRAY &&
+                                               tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+                                       parser->toksuper = tokens[parser->toksuper].parent;
+#else
+                                       for (i = parser->toknext - 1; i >= 0; i--) {
+                                               if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+                                                       if (tokens[i].start != -1 && tokens[i].end == -1) {
+                                                               parser->toksuper = i;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+#endif
+                               }
+                               break;
+#ifdef JSMN_STRICT
+                       /* In strict mode primitives are: numbers and booleans */
+                       case '-': case '0': case '1' : case '2': case '3' : case '4':
+                       case '5': case '6': case '7' : case '8': case '9':
+                       case 't': case 'f': case 'n' :
+                               /* And they must not be keys of the object */
+                               if (tokens != NULL && parser->toksuper != -1) {
+                                       jsmntok_t *t = &tokens[parser->toksuper];
+                                       if (t->type == JSMN_OBJECT ||
+                                                       (t->type == JSMN_STRING && t->size != 0)) {
+                                               return JSMN_ERROR_INVAL;
+                                       }
+                               }
+#else
+                       /* In non-strict mode every unquoted value is a primitive */
+                       default:
+#endif
+                               r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+                               if (r < 0) return r;
+                               count++;
+                               if (parser->toksuper != -1 && tokens != NULL)
+                                       tokens[parser->toksuper].size++;
+                               break;
+
+#ifdef JSMN_STRICT
+                       /* Unexpected char in strict mode */
+                       default:
+                               return JSMN_ERROR_INVAL;
+#endif
+               }
+       }
+
+       if (tokens != NULL) {
+               for (i = parser->toknext - 1; i >= 0; i--) {
+                       /* Unmatched opened object or array */
+                       if (tokens[i].start != -1 && tokens[i].end == -1) {
+                               return JSMN_ERROR_PART;
+                       }
+               }
+       }
+
+       return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+       parser->pos = 0;
+       parser->toknext = 0;
+       parser->toksuper = -1;
+}
+
diff --git a/jsmn.h b/jsmn.h
new file mode 100644 (file)
index 0000000..01ca99c
--- /dev/null
+++ b/jsmn.h
@@ -0,0 +1,76 @@
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * JSON type identifier. Basic types are:
+ *     o Object
+ *     o Array
+ *     o String
+ *     o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+       JSMN_UNDEFINED = 0,
+       JSMN_OBJECT = 1,
+       JSMN_ARRAY = 2,
+       JSMN_STRING = 3,
+       JSMN_PRIMITIVE = 4
+} jsmntype_t;
+
+enum jsmnerr {
+       /* Not enough tokens were provided */
+       JSMN_ERROR_NOMEM = -1,
+       /* Invalid character inside JSON string */
+       JSMN_ERROR_INVAL = -2,
+       /* The string is not a full JSON packet, more bytes expected */
+       JSMN_ERROR_PART = -3
+};
+
+/**
+ * JSON token description.
+ * @param              type    type (object, array, string etc.)
+ * @param              start   start position in JSON data string
+ * @param              end             end position in JSON data string
+ */
+typedef struct {
+       jsmntype_t type;
+       int start;
+       int end;
+       int size;
+#ifdef JSMN_PARENT_LINKS
+       int parent;
+#endif
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+       unsigned int pos; /* offset in the JSON string */
+       unsigned int toknext; /* next token to allocate */
+       int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+ * a single JSON object.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+               jsmntok_t *tokens, unsigned int num_tokens);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JSMN_H_ */
index c9f461be95825e35c742e01dcd3190d3890a93cc..c1762e8ae7f854755fc71dfb1263388cf610eaa5 100644 (file)
@@ -22,7 +22,6 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,6 +39,7 @@
 #include "window.h"
 #include "common.h"
 #include "subscribe.h"
+#include "parse.h"
 #include "messages.h"
 
 int handle_message(char *msg, int msg_len, FILE *rsp)
@@ -684,17 +684,30 @@ int cmd_query(char **args, int num, FILE *rsp)
                num--, args++;
        }
 
-       if (d != 1 || t > 1)
+       if (d != 1 || t > 1) {
                return MSG_SYNTAX;
+       }
 
-       if (dom == DOMAIN_HISTORY)
+       if (dom == DOMAIN_HISTORY) {
                query_history(trg, rsp);
-       else if (dom == DOMAIN_STACK)
+       } else if (dom == DOMAIN_STACK) {
                query_stack(rsp);
-       else if (dom == DOMAIN_WINDOW)
+       } else if (dom == DOMAIN_WINDOW) {
                query_windows(trg, rsp);
-       else
-               query_monitors(trg, dom, rsp);
+       } else if (dom == DOMAIN_DESKTOP || dom == DOMAIN_MONITOR) {
+               query_names(dom, trg, rsp);
+       } else {
+               if (trg.node != NULL) {
+                       query_node(trg.node, rsp);
+               } else if (trg.desktop != NULL) {
+                       query_desktop(trg.desktop, rsp);
+               } else if (trg.monitor != NULL) {
+                       query_monitor(trg.monitor, rsp);
+               } else {
+                       query_tree(rsp);
+               }
+               fprintf(rsp, "\n");
+       }
 
        return MSG_SUCCESS;
 }
@@ -790,24 +803,34 @@ int cmd_pointer(char **args, int num)
 
 int cmd_restore(char **args, int num)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
        while (num > 0) {
                if (streq("-T", *args) || streq("--tree", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       restore_tree(*args);
+                       }
+                       if (!restore_tree(*args)) {
+                               return MSG_FAILURE;
+                       }
                } else if (streq("-H", *args) || streq("--history", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       restore_history(*args);
+                       }
+                       if (!restore_history(*args)) {
+                               return MSG_FAILURE;
+                       }
                } else if (streq("-S", *args) || streq("--stack", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       restore_stack(*args);
+                       }
+                       if (!restore_stack(*args)) {
+                               return MSG_FAILURE;
+                       }
                } else {
                        return MSG_SYNTAX;
                }
@@ -912,7 +935,7 @@ int cmd_quit(char **args, int num)
 
 int set_setting(coordinates_t loc, char *name, char *value)
 {
-#define DESKWINDEFSET(k, v) \
+#define DESK_WIN_DEF_SET(k, v) \
                if (loc.node != NULL) \
                        loc.node->client->k = v; \
                else if (loc.desktop != NULL) \
@@ -926,9 +949,9 @@ int set_setting(coordinates_t loc, char *name, char *value)
                unsigned int bw;
                if (sscanf(value, "%u", &bw) != 1)
                        return MSG_FAILURE;
-               DESKWINDEFSET(border_width, bw)
-#undef DESKWINDEFSET
-#define DESKDEFSET(k, v) \
+               DESK_WIN_DEF_SET(border_width, bw)
+#undef DESK_WIN_DEF_SET
+#define DESK_DEF_SET(k, v) \
                if (loc.desktop != NULL) \
                        loc.desktop->k = v; \
                else if (loc.monitor != NULL) \
@@ -939,9 +962,9 @@ int set_setting(coordinates_t loc, char *name, char *value)
                int wg;
                if (sscanf(value, "%i", &wg) != 1)
                        return MSG_FAILURE;
-               DESKDEFSET(window_gap, wg)
-#undef DESKDEFSET
-#define MONDESKSET(k, v) \
+               DESK_DEF_SET(window_gap, wg)
+#undef DESK_DEF_SET
+#define MON_DESK_SET(k, v) \
                if (loc.desktop != NULL) \
                        loc.desktop->k = v; \
                else if (loc.monitor != NULL) \
@@ -953,30 +976,30 @@ int set_setting(coordinates_t loc, char *name, char *value)
                int tp;
                if (sscanf(value, "%i", &tp) != 1)
                        return MSG_FAILURE;
-               MONDESKSET(top_padding, tp)
+               MON_DESK_SET(top_padding, tp)
        } else if (streq("right_padding", name)) {
                int rp;
                if (sscanf(value, "%i", &rp) != 1)
                        return MSG_FAILURE;
-               MONDESKSET(right_padding, rp)
+               MON_DESK_SET(right_padding, rp)
        } else if (streq("bottom_padding", name)) {
                int bp;
                if (sscanf(value, "%i", &bp) != 1)
                        return MSG_FAILURE;
-               MONDESKSET(bottom_padding, bp)
+               MON_DESK_SET(bottom_padding, bp)
        } else if (streq("left_padding", name)) {
                int lp;
                if (sscanf(value, "%i", &lp) != 1)
                        return MSG_FAILURE;
-               MONDESKSET(left_padding, lp)
-#undef MONDESKSET
-#define SETSTR(s) \
+               MON_DESK_SET(left_padding, lp)
+#undef MON_DESK_SET
+#define SET_STR(s) \
        } else if (streq(#s, name)) { \
                if (snprintf(s, sizeof(s), "%s", value) < 0) \
                        return MSG_FAILURE;
-       SETSTR(external_rules_command)
-       SETSTR(status_prefix)
-#undef SETSTR
+       SET_STR(external_rules_command)
+       SET_STR(status_prefix)
+#undef SET_STR
        } else if (streq("split_ratio", name)) {
                double r;
                if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
@@ -984,24 +1007,24 @@ int set_setting(coordinates_t loc, char *name, char *value)
                else
                        return MSG_FAILURE;
                return MSG_SUCCESS;
-#define SETCOLOR(s) \
+#define SET_COLOR(s) \
        } else if (streq(#s, name)) { \
                snprintf(s, sizeof(s), "%s", value);
-       SETCOLOR(focused_border_color)
-       SETCOLOR(active_border_color)
-       SETCOLOR(normal_border_color)
-       SETCOLOR(presel_border_color)
-       SETCOLOR(focused_locked_border_color)
-       SETCOLOR(active_locked_border_color)
-       SETCOLOR(normal_locked_border_color)
-       SETCOLOR(focused_sticky_border_color)
-       SETCOLOR(active_sticky_border_color)
-       SETCOLOR(normal_sticky_border_color)
-       SETCOLOR(focused_private_border_color)
-       SETCOLOR(active_private_border_color)
-       SETCOLOR(normal_private_border_color)
-       SETCOLOR(urgent_border_color)
-#undef SETCOLOR
+       SET_COLOR(focused_border_color)
+       SET_COLOR(active_border_color)
+       SET_COLOR(normal_border_color)
+       SET_COLOR(presel_border_color)
+       SET_COLOR(focused_locked_border_color)
+       SET_COLOR(active_locked_border_color)
+       SET_COLOR(normal_locked_border_color)
+       SET_COLOR(focused_sticky_border_color)
+       SET_COLOR(active_sticky_border_color)
+       SET_COLOR(normal_sticky_border_color)
+       SET_COLOR(focused_private_border_color)
+       SET_COLOR(active_private_border_color)
+       SET_COLOR(normal_private_border_color)
+       SET_COLOR(urgent_border_color)
+#undef SET_COLOR
        } else if (streq("initial_polarity", name)) {
                child_polarity_t p;
                if (parse_child_polarity(value, &p)) {
@@ -1035,32 +1058,32 @@ int set_setting(coordinates_t loc, char *name, char *value)
                } else {
                        return MSG_FAILURE;
                }
-#define SETBOOL(s) \
+#define SET_BOOL(s) \
        } else if (streq(#s, name)) { \
                if (!parse_bool(value, &s)) \
                        return MSG_FAILURE;
-               SETBOOL(borderless_monocle)
-               SETBOOL(gapless_monocle)
-               SETBOOL(leaf_monocle)
-               SETBOOL(pointer_follows_focus)
-               SETBOOL(pointer_follows_monitor)
-               SETBOOL(auto_alternate)
-               SETBOOL(auto_cancel)
-               SETBOOL(history_aware_focus)
-               SETBOOL(focus_by_distance)
-               SETBOOL(ignore_ewmh_focus)
-               SETBOOL(center_pseudo_tiled)
-#undef SETBOOL
-#define SETMONBOOL(s) \
+               SET_BOOL(borderless_monocle)
+               SET_BOOL(gapless_monocle)
+               SET_BOOL(leaf_monocle)
+               SET_BOOL(pointer_follows_focus)
+               SET_BOOL(pointer_follows_monitor)
+               SET_BOOL(auto_alternate)
+               SET_BOOL(auto_cancel)
+               SET_BOOL(history_aware_focus)
+               SET_BOOL(focus_by_distance)
+               SET_BOOL(ignore_ewmh_focus)
+               SET_BOOL(center_pseudo_tiled)
+#undef SET_BOOL
+#define SET_MON_BOOL(s) \
        } else if (streq(#s, name)) { \
                if (!parse_bool(value, &s)) \
                        return MSG_FAILURE; \
                if (s) \
                        update_monitors();
-               SETMONBOOL(remove_disabled_monitors)
-               SETMONBOOL(remove_unplugged_monitors)
-               SETMONBOOL(merge_overlapping_monitors)
-#undef SETMONBOOL
+               SET_MON_BOOL(remove_disabled_monitors)
+               SET_MON_BOOL(remove_unplugged_monitors)
+               SET_MON_BOOL(merge_overlapping_monitors)
+#undef SET_MON_BOOL
        } else {
                return MSG_FAILURE;
        }
@@ -1096,7 +1119,7 @@ int get_setting(coordinates_t loc, char *name, FILE* rsp)
                fprintf(rsp, "%s", status_prefix);
        else if (streq("initial_polarity", name))
                fprintf(rsp, "%s", initial_polarity == FIRST_CHILD ? "first_child" : "second_child");
-#define MONDESKGET(k) \
+#define MON_DESK_GET(k) \
        else if (streq(#k, name)) \
                if (loc.desktop != NULL) \
                        fprintf(rsp, "%i", loc.desktop->k); \
@@ -1104,45 +1127,45 @@ int get_setting(coordinates_t loc, char *name, FILE* rsp)
                        fprintf(rsp, "%i", loc.monitor->k); \
                else \
                        return MSG_FAILURE;
-       MONDESKGET(top_padding)
-       MONDESKGET(right_padding)
-       MONDESKGET(bottom_padding)
-       MONDESKGET(left_padding)
+       MON_DESK_GET(top_padding)
+       MON_DESK_GET(right_padding)
+       MON_DESK_GET(bottom_padding)
+       MON_DESK_GET(left_padding)
 #undef DESKGET
-#define GETCOLOR(s) \
+#define GET_COLOR(s) \
        else if (streq(#s, name)) \
                fprintf(rsp, "%s", s);
-       GETCOLOR(focused_border_color)
-       GETCOLOR(active_border_color)
-       GETCOLOR(normal_border_color)
-       GETCOLOR(presel_border_color)
-       GETCOLOR(focused_locked_border_color)
-       GETCOLOR(active_locked_border_color)
-       GETCOLOR(normal_locked_border_color)
-       GETCOLOR(focused_sticky_border_color)
-       GETCOLOR(active_sticky_border_color)
-       GETCOLOR(normal_sticky_border_color)
-       GETCOLOR(urgent_border_color)
-#undef GETCOLOR
-#define GETBOOL(s) \
+       GET_COLOR(focused_border_color)
+       GET_COLOR(active_border_color)
+       GET_COLOR(normal_border_color)
+       GET_COLOR(presel_border_color)
+       GET_COLOR(focused_locked_border_color)
+       GET_COLOR(active_locked_border_color)
+       GET_COLOR(normal_locked_border_color)
+       GET_COLOR(focused_sticky_border_color)
+       GET_COLOR(active_sticky_border_color)
+       GET_COLOR(normal_sticky_border_color)
+       GET_COLOR(urgent_border_color)
+#undef GET_COLOR
+#define GET_BOOL(s) \
        else if (streq(#s, name)) \
-               fprintf(rsp, "%s", BOOLSTR(s));
-       GETBOOL(borderless_monocle)
-       GETBOOL(gapless_monocle)
-       GETBOOL(leaf_monocle)
-       GETBOOL(focus_follows_pointer)
-       GETBOOL(pointer_follows_focus)
-       GETBOOL(pointer_follows_monitor)
-       GETBOOL(auto_alternate)
-       GETBOOL(auto_cancel)
-       GETBOOL(history_aware_focus)
-       GETBOOL(focus_by_distance)
-       GETBOOL(ignore_ewmh_focus)
-       GETBOOL(center_pseudo_tiled)
-       GETBOOL(remove_disabled_monitors)
-       GETBOOL(remove_unplugged_monitors)
-       GETBOOL(merge_overlapping_monitors)
-#undef GETBOOL
+               fprintf(rsp, "%s", BOOL_STR(s));
+       GET_BOOL(borderless_monocle)
+       GET_BOOL(gapless_monocle)
+       GET_BOOL(leaf_monocle)
+       GET_BOOL(focus_follows_pointer)
+       GET_BOOL(pointer_follows_focus)
+       GET_BOOL(pointer_follows_monitor)
+       GET_BOOL(auto_alternate)
+       GET_BOOL(auto_cancel)
+       GET_BOOL(history_aware_focus)
+       GET_BOOL(focus_by_distance)
+       GET_BOOL(ignore_ewmh_focus)
+       GET_BOOL(center_pseudo_tiled)
+       GET_BOOL(remove_disabled_monitors)
+       GET_BOOL(remove_unplugged_monitors)
+       GET_BOOL(merge_overlapping_monitors)
+#undef GET_BOOL
        else
                return MSG_FAILURE;
        fprintf(rsp, "\n");
@@ -1210,211 +1233,3 @@ bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
        }
        return true;
 }
-
-bool parse_bool(char *value, bool *b)
-{
-       if (streq("true", value) || streq("on", value)) {
-               *b = true;
-               return true;
-       } else if (streq("false", value) || streq("off", value)) {
-               *b = false;
-               return true;
-       }
-       return false;
-}
-
-bool parse_layout(char *s, layout_t *l)
-{
-       if (streq("monocle", s)) {
-               *l = LAYOUT_MONOCLE;
-               return true;
-       } else if (streq("tiled", s)) {
-               *l = LAYOUT_TILED;
-               return true;
-       }
-       return false;
-}
-
-bool parse_client_state(char *s, client_state_t *t)
-{
-       if (streq("tiled", s)) {
-               *t = STATE_TILED;
-               return true;
-       } else if (streq("pseudo_tiled", s)) {
-               *t = STATE_PSEUDO_TILED;
-               return true;
-       } else if (streq("floating", s)) {
-               *t = STATE_FLOATING;
-               return true;
-       } else if (streq("fullscreen", s)) {
-               *t = STATE_FULLSCREEN;
-               return true;
-       }
-       return false;
-}
-
-bool parse_stack_layer(char *s, stack_layer_t *l)
-{
-       if (streq("below", s)) {
-               *l = LAYER_BELOW;
-               return true;
-       } else if (streq("normal", s)) {
-               *l = LAYER_NORMAL;
-               return true;
-       } else if (streq("above", s)) {
-               *l = LAYER_ABOVE;
-               return true;
-       }
-       return false;
-}
-
-bool parse_direction(char *s, direction_t *d)
-{
-       if (streq("right", s)) {
-               *d = DIR_RIGHT;
-               return true;
-       } else if (streq("down", s)) {
-               *d = DIR_DOWN;
-               return true;
-       } else if (streq("left", s)) {
-               *d = DIR_LEFT;
-               return true;
-       } else if (streq("up", s)) {
-               *d = DIR_UP;
-               return true;
-       }
-       return false;
-}
-
-bool parse_cycle_direction(char *s, cycle_dir_t *d)
-{
-       if (streq("next", s)) {
-               *d = CYCLE_NEXT;
-               return true;
-       } else if (streq("prev", s)) {
-               *d = CYCLE_PREV;
-               return true;
-       }
-       return false;
-}
-
-bool parse_circulate_direction(char *s, circulate_dir_t *d)
-{
-       if (streq("forward", s)) {
-               *d = CIRCULATE_FORWARD;
-               return true;
-       } else if (streq("backward", s)) {
-               *d = CIRCULATE_BACKWARD;
-               return true;
-       }
-       return false;
-}
-
-bool parse_history_direction(char *s, history_dir_t *d)
-{
-       if (streq("older", s)) {
-               *d = HISTORY_OLDER;
-               return true;
-       } else if (streq("newer", s)) {
-               *d = HISTORY_NEWER;
-               return true;
-       }
-       return false;
-}
-
-
-bool parse_flip(char *s, flip_t *f)
-{
-       if (streq("horizontal", s)) {
-               *f = FLIP_HORIZONTAL;
-               return true;
-       } else if (streq("vertical", s)) {
-               *f = FLIP_VERTICAL;
-               return true;
-       }
-       return false;
-}
-
-bool parse_pointer_action(char *s, pointer_action_t *a)
-{
-       if (streq("move", s)) {
-               *a = ACTION_MOVE;
-               return true;
-       } else if (streq("resize_corner", s)) {
-               *a = ACTION_RESIZE_CORNER;
-               return true;
-       } else if (streq("resize_side", s)) {
-               *a = ACTION_RESIZE_SIDE;
-               return true;
-       } else if (streq("focus", s)) {
-               *a = ACTION_FOCUS;
-               return true;
-       }
-       return false;
-}
-
-bool parse_child_polarity(char *s, child_polarity_t *p)
-{
-       if (streq("first_child", s)) {
-               *p = FIRST_CHILD;
-               return true;
-       } else if (streq("second_child", s)) {
-               *p = SECOND_CHILD;
-               return true;
-       }
-       return false;
-}
-
-bool parse_degree(char *s, int *d)
-{
-       int i = atoi(s);
-       while (i < 0)
-               i += 360;
-       while (i > 359)
-               i -= 360;
-       if ((i % 90) != 0) {
-               return false;
-       } else {
-               *d = i;
-               return true;
-       }
-}
-
-bool parse_window_id(char *s, long int *i)
-{
-       char *end;
-       errno = 0;
-       long int ret = strtol(s, &end, 0);
-       if (errno != 0 || *end != '\0')
-               return false;
-       else
-               *i = ret;
-       return true;
-}
-
-bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)
-{
-       *key = strtok(s, EQL_TOK);
-       char *v = strtok(NULL, EQL_TOK);
-       if (v == NULL) {
-               *state = ALTER_TOGGLE;
-               return true;
-       } else {
-               if (parse_bool(v, value)) {
-                       *state = ALTER_SET;
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-       return false;
-}
-
-bool parse_index(char *s, int *i)
-{
-       int idx;
-       if (sscanf(s, "^%i", &idx) != 1 || idx < 1)
-               return false;
-       *i = idx;
-       return true;
-}
index 7c95fefc095250875aeea97646de0708e72228ec..8603a61e8925aaa2d13a1c94c6cfab730a1cd217 100644 (file)
 #include "types.h"
 #include "subscribe.h"
 
-#define OPT_CHR  '-'
-#define CAT_CHR  '.'
-#define EQL_TOK  "="
-
 int handle_message(char *msg, int msg_len, FILE *rsp);
 int process_message(char **args, int num, FILE *rsp);
 int cmd_window(char **args, int num);
index f11dbb470fce9b5b9ebaa46ea7b36ae6c4a432c1..d5913c26f4624fa617cc1215483350a32269d2f1 100644 (file)
--- a/monitor.c
+++ b/monitor.c
 #include "window.h"
 #include "monitor.h"
 
-monitor_t *make_monitor(xcb_rectangle_t rect)
+monitor_t *make_monitor(xcb_rectangle_t *rect)
 {
        monitor_t *m = malloc(sizeof(monitor_t));
        snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
        m->prev = m->next = NULL;
        m->desk = m->desk_head = m->desk_tail = NULL;
-       m->rectangle = rect;
        m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
        m->wired = true;
        m->num_sticky = 0;
-       uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
-       m->root = xcb_generate_id(dpy);
-       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, m->root, root, rect.x, rect.y, rect.width, rect.height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
-       xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
-       window_lower(m->root);
-       if (focus_follows_pointer) {
-               window_show(m->root);
+       if (rect != NULL) {
+               update_root(m, rect);
+       } else {
+               m->root = XCB_NONE;
+               m->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
        }
        return m;
 }
 
+void update_root(monitor_t *m, xcb_rectangle_t *r)
+{
+       m->rectangle = *r;
+       if (m->root == XCB_NONE) {
+               uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
+               m->root = xcb_generate_id(dpy);
+               xcb_create_window(dpy, XCB_COPY_FROM_PARENT, m->root, root, r->x, r->y, r->width, r->height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
+               xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
+               window_lower(m->root);
+               if (focus_follows_pointer) {
+                       window_show(m->root);
+               }
+       } else {
+               window_move_resize(m->root, r->x, r->y, r->width, r->height);
+               put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry %s %ux%u+%i+%i\n", m->name, r->width, r->height, r->x, r->y);
+       }
+}
+
 void rename_monitor(monitor_t *m, const char *name)
 {
        put_status(SBSC_MASK_MONITOR_RENAME, "monitor_rename %s %s\n", m->name, name);
@@ -130,13 +145,6 @@ void translate_client(monitor_t *ms, monitor_t *md, client_t *c)
        c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
 }
 
-void update_root(monitor_t *m)
-{
-       xcb_rectangle_t r = m->rectangle;
-       window_move_resize(m->root, r.x, r.y, r.width, r.height);
-       put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry %s %ux%u+%i+%i\n", m->name, r.width, r.height, r.x, r.y);
-}
-
 void focus_monitor(monitor_t *m)
 {
        if (mon == m)
@@ -169,8 +177,6 @@ void add_monitor(monitor_t *m)
                m->prev = mon_tail;
                mon_tail = m;
        }
-
-       num_monitors++;
 }
 
 void remove_monitor(monitor_t *m)
@@ -214,7 +220,6 @@ void remove_monitor(monitor_t *m)
 
        xcb_destroy_window(dpy, m->root);
        free(m);
-       num_monitors--;
        put_status(SBSC_MASK_REPORT);
 }
 
@@ -381,8 +386,7 @@ bool update_monitors(void)
                                        xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
                                        mm = get_monitor_by_id(outputs[i]);
                                        if (mm != NULL) {
-                                               mm->rectangle = rect;
-                                               update_root(mm);
+                                               update_root(mm, &rect);
                                                for (desktop_t *d = mm->desk_head; d != NULL; d = d->next) {
                                                        for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
                                                                translate_client(mm, mm, n->client);
@@ -391,7 +395,7 @@ bool update_monitors(void)
                                                arrange(mm, mm->desk);
                                                mm->wired = true;
                                        } else {
-                                               mm = make_monitor(rect);
+                                               mm = make_monitor(&rect);
                                                char *name = (char *)xcb_randr_get_output_info_name(info);
                                                size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
                                                snprintf(mm->name, name_len, "%s", name);
@@ -471,5 +475,5 @@ bool update_monitors(void)
 
        free(sres);
        update_motion_recorder();
-       return (num_monitors > 0);
+       return (mon_head != NULL);
 }
index 44434e9e061f441bbf9bf710027ce0bd8ef261b3..da04324acf616c000c176348c78de7212d27fed1 100644 (file)
--- a/monitor.h
+++ b/monitor.h
 
 #define DEFAULT_MON_NAME     "MONITOR"
 
-monitor_t *make_monitor(xcb_rectangle_t rect);
+monitor_t *make_monitor(xcb_rectangle_t *rect);
+void update_root(monitor_t *m, xcb_rectangle_t *r);
 void rename_monitor(monitor_t *m, const char *name);
 monitor_t *find_monitor(char *name);
 monitor_t *get_monitor_by_id(xcb_randr_output_t id);
 void embrace_client(monitor_t *m, client_t *c);
 void translate_client(monitor_t *ms, monitor_t *md, client_t *c);
-void update_root(monitor_t *m);
 void focus_monitor(monitor_t *m);
 void add_monitor(monitor_t *m);
 void remove_monitor(monitor_t *m);
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..f9e28b7
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,237 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "helpers.h"
+#include "parse.h"
+
+bool parse_bool(char *value, bool *b)
+{
+       if (streq("true", value) || streq("on", value)) {
+               *b = true;
+               return true;
+       } else if (streq("false", value) || streq("off", value)) {
+               *b = false;
+               return true;
+       }
+       return false;
+}
+
+bool parse_split_type(char *s, split_type_t *t)
+{
+       if (streq("horizontal", s)) {
+               *t = TYPE_HORIZONTAL;
+               return true;
+       } else if (streq("vertical", s)) {
+               *t = TYPE_VERTICAL;
+               return true;
+       }
+       return false;
+}
+
+bool parse_split_mode(char *s, split_mode_t *m)
+{
+       if (streq("automatic", s)) {
+               *m = MODE_AUTOMATIC;
+               return true;
+       } else if (streq("vertical", s)) {
+               *m = MODE_MANUAL;
+               return true;
+       }
+       return false;
+}
+
+bool parse_layout(char *s, layout_t *l)
+{
+       if (streq("monocle", s)) {
+               *l = LAYOUT_MONOCLE;
+               return true;
+       } else if (streq("tiled", s)) {
+               *l = LAYOUT_TILED;
+               return true;
+       }
+       return false;
+}
+
+bool parse_client_state(char *s, client_state_t *t)
+{
+       if (streq("tiled", s)) {
+               *t = STATE_TILED;
+               return true;
+       } else if (streq("pseudo_tiled", s)) {
+               *t = STATE_PSEUDO_TILED;
+               return true;
+       } else if (streq("floating", s)) {
+               *t = STATE_FLOATING;
+               return true;
+       } else if (streq("fullscreen", s)) {
+               *t = STATE_FULLSCREEN;
+               return true;
+       }
+       return false;
+}
+
+bool parse_stack_layer(char *s, stack_layer_t *l)
+{
+       if (streq("below", s)) {
+               *l = LAYER_BELOW;
+               return true;
+       } else if (streq("normal", s)) {
+               *l = LAYER_NORMAL;
+               return true;
+       } else if (streq("above", s)) {
+               *l = LAYER_ABOVE;
+               return true;
+       }
+       return false;
+}
+
+bool parse_direction(char *s, direction_t *d)
+{
+       if (streq("right", s)) {
+               *d = DIR_RIGHT;
+               return true;
+       } else if (streq("down", s)) {
+               *d = DIR_DOWN;
+               return true;
+       } else if (streq("left", s)) {
+               *d = DIR_LEFT;
+               return true;
+       } else if (streq("up", s)) {
+               *d = DIR_UP;
+               return true;
+       }
+       return false;
+}
+
+bool parse_cycle_direction(char *s, cycle_dir_t *d)
+{
+       if (streq("next", s)) {
+               *d = CYCLE_NEXT;
+               return true;
+       } else if (streq("prev", s)) {
+               *d = CYCLE_PREV;
+               return true;
+       }
+       return false;
+}
+
+bool parse_circulate_direction(char *s, circulate_dir_t *d)
+{
+       if (streq("forward", s)) {
+               *d = CIRCULATE_FORWARD;
+               return true;
+       } else if (streq("backward", s)) {
+               *d = CIRCULATE_BACKWARD;
+               return true;
+       }
+       return false;
+}
+
+bool parse_history_direction(char *s, history_dir_t *d)
+{
+       if (streq("older", s)) {
+               *d = HISTORY_OLDER;
+               return true;
+       } else if (streq("newer", s)) {
+               *d = HISTORY_NEWER;
+               return true;
+       }
+       return false;
+}
+
+
+bool parse_flip(char *s, flip_t *f)
+{
+       if (streq("horizontal", s)) {
+               *f = FLIP_HORIZONTAL;
+               return true;
+       } else if (streq("vertical", s)) {
+               *f = FLIP_VERTICAL;
+               return true;
+       }
+       return false;
+}
+
+bool parse_pointer_action(char *s, pointer_action_t *a)
+{
+       if (streq("move", s)) {
+               *a = ACTION_MOVE;
+               return true;
+       } else if (streq("resize_corner", s)) {
+               *a = ACTION_RESIZE_CORNER;
+               return true;
+       } else if (streq("resize_side", s)) {
+               *a = ACTION_RESIZE_SIDE;
+               return true;
+       } else if (streq("focus", s)) {
+               *a = ACTION_FOCUS;
+               return true;
+       }
+       return false;
+}
+
+bool parse_child_polarity(char *s, child_polarity_t *p)
+{
+       if (streq("first_child", s)) {
+               *p = FIRST_CHILD;
+               return true;
+       } else if (streq("second_child", s)) {
+               *p = SECOND_CHILD;
+               return true;
+       }
+       return false;
+}
+
+bool parse_degree(char *s, int *d)
+{
+       int i = atoi(s);
+       while (i < 0)
+               i += 360;
+       while (i > 359)
+               i -= 360;
+       if ((i % 90) != 0) {
+               return false;
+       } else {
+               *d = i;
+               return true;
+       }
+}
+
+bool parse_window_id(char *s, long int *i)
+{
+       char *end;
+       errno = 0;
+       long int ret = strtol(s, &end, 0);
+       if (errno != 0 || *end != '\0')
+               return false;
+       else
+               *i = ret;
+       return true;
+}
+
+bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)
+{
+       *key = strtok(s, EQL_TOK);
+       char *v = strtok(NULL, EQL_TOK);
+       if (v == NULL) {
+               *state = ALTER_TOGGLE;
+               return true;
+       } else {
+               if (parse_bool(v, value)) {
+                       *state = ALTER_SET;
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+       return false;
+}
+
+bool parse_index(char *s, int *i)
+{
+       int idx;
+       if (sscanf(s, "^%i", &idx) != 1 || idx < 1)
+               return false;
+       *i = idx;
+       return true;
+}
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..19e9adb
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,28 @@
+#ifndef BSPWM_PARSE_H
+#define BSPWM_PARSE_H
+
+#include "types.h"
+
+#define OPT_CHR  '-'
+#define CAT_CHR  '.'
+#define EQL_TOK  "="
+
+bool parse_bool(char *value, bool *b);
+bool parse_split_type(char *s, split_type_t *t);
+bool parse_split_mode(char *s, split_mode_t *m);
+bool parse_layout(char *s, layout_t *l);
+bool parse_client_state(char *s, client_state_t *t);
+bool parse_stack_layer(char *s, stack_layer_t *l);
+bool parse_direction(char *s, direction_t *d);
+bool parse_cycle_direction(char *s, cycle_dir_t *d);
+bool parse_circulate_direction(char *s, circulate_dir_t *d);
+bool parse_history_direction(char *s, history_dir_t *d);
+bool parse_flip(char *s, flip_t *f);
+bool parse_pointer_action(char *s, pointer_action_t *a);
+bool parse_child_polarity(char *s, child_polarity_t *p);
+bool parse_degree(char *s, int *d);
+bool parse_window_id(char *s, long int *i);
+bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
+bool parse_index(char *s, int *i);
+
+#endif
diff --git a/query.c b/query.c
index 47110299d132976c371c34ac6c2466a84c983066..f7e04275636d62281ab01b8c526310d26526c640 100644 (file)
--- a/query.c
+++ b/query.c
 #include "bspwm.h"
 #include "desktop.h"
 #include "history.h"
-#include "messages.h"
+#include "parse.h"
 #include "monitor.h"
 #include "tree.h"
 #include "query.h"
+#include "jsmn.h"
 
-void query_monitors(coordinates_t loc, domain_t dom, FILE *rsp)
+void query_tree(FILE *rsp)
 {
+       fprintf(rsp, "{");
+       fprintf(rsp, "\"focusedMonitorName\": \"%s\", ", mon->name);
+       fprintf(rsp, "\"numClients\": %i, ", num_clients);
+       fprintf(rsp, "\"monitors\": ");
+       fprintf(rsp, "[");
        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (loc.monitor != NULL && m != loc.monitor)
-                       continue;
-               if (dom != DOMAIN_DESKTOP) {
-                       if (dom == DOMAIN_MONITOR) {
-                               fprintf(rsp, "%s\n", m->name);
-                               continue;
-                       } else {
-                               fprintf(rsp, "%s %ux%u%+i%+i %i,%i,%i,%i%s\n", m->name,
-                                        m->rectangle.width,m->rectangle.height, m->rectangle.x, m->rectangle.y,
-                                        m->top_padding, m->right_padding, m->bottom_padding, m->left_padding,
-                                        (m == mon ? " *" : ""));
-                       }
+               query_monitor(m, rsp);
+               if (m->next != NULL) {
+                       fprintf(rsp, ", ");
                }
-               query_desktops(m, dom, loc, (dom == DOMAIN_DESKTOP ? 0 : 1), rsp);
        }
+       fprintf(rsp, "]");
+       fprintf(rsp, "}");
+
 }
 
-void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, FILE *rsp)
+void query_monitor(monitor_t *m, FILE *rsp)
 {
+       fprintf(rsp, "{");
+       fprintf(rsp, "\"name\": \"%s\", ", m->name);
+       fprintf(rsp, "\"id\": %u, ", m->id);
+       fprintf(rsp, "\"wired\": %s, ", BOOL_STR(m->wired));
+       fprintf(rsp, "\"topPadding\": %i, ", m->top_padding);
+       fprintf(rsp, "\"rightPadding\": %i, ", m->right_padding);
+       fprintf(rsp, "\"bottomPadding\": %i, ", m->bottom_padding);
+       fprintf(rsp, "\"leftPadding\": %i, ", m->left_padding);
+       fprintf(rsp, "\"numSticky\": %i, ", m->num_sticky);
+       fprintf(rsp, "\"rectangle\": ");
+       query_rectangle(m->rectangle, rsp);
+       fprintf(rsp, ", ");
+       fprintf(rsp, "\"focusedDesktopName\": \"%s\", ", m->desk->name);
+       fprintf(rsp, "\"desktops\": ");
+       fprintf(rsp, "[");
        for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-               if (loc.desktop != NULL && d != loc.desktop)
-                       continue;
-               for (unsigned int i = 0; i < depth; i++)
-                       fprintf(rsp, "\t");
-               if (dom == DOMAIN_DESKTOP) {
-                       fprintf(rsp, "%s\n", d->name);
-                       continue;
-               } else {
-                       fprintf(rsp, "%s %u %i %i,%i,%i,%i %c%s\n", d->name, d->border_width,
-                               d->window_gap,
-                               d->top_padding, d->right_padding, d->bottom_padding, d->left_padding,
-                               (d->layout == LAYOUT_TILED ? 'T' : 'M'),
-                               (d == m->desk ? " *" : ""));
+               query_desktop(d, rsp);
+               if (d->next != NULL) {
+                       fprintf(rsp, ", ");
                }
-               query_tree(d, d->root, rsp, depth + 1);
        }
+       fprintf(rsp, "]");
+       fprintf(rsp, "}");
+}
+
+void query_desktop(desktop_t *d, FILE *rsp)
+{
+       fprintf(rsp, "{");
+       fprintf(rsp, "\"name\": \"%s\", ", d->name);
+       fprintf(rsp, "\"layout\": \"%s\", ", LAYOUT_STR(d->layout));
+       fprintf(rsp, "\"topPadding\": %i, ", d->top_padding);
+       fprintf(rsp, "\"rightPadding\": %i, ", d->right_padding);
+       fprintf(rsp, "\"bottomPadding\": %i, ", d->bottom_padding);
+       fprintf(rsp, "\"leftPadding\": %i, ", d->left_padding);
+       fprintf(rsp, "\"windowGap\": %i, ", d->window_gap);
+       fprintf(rsp, "\"borderWidth\": %u, ", d->border_width);
+       fprintf(rsp, "\"focusedWindow\": %u, ", d->focus != NULL ? d->focus->client->window : 0);
+       fprintf(rsp, "\"root\": ");
+       query_node(d->root, rsp);
+       fprintf(rsp, "}");
 }
 
-void query_tree(desktop_t *d, node_t *n, FILE *rsp, unsigned int depth)
+void query_node(node_t *n, FILE *rsp)
 {
-       if (n == NULL)
-               return;
-
-       for (unsigned int i = 0; i < depth; i++)
-               fprintf(rsp, "\t");
-
-       if (is_leaf(n)) {
-               client_t *c = n->client;
-               fprintf(rsp, "%c %s %s 0x%X %u %ux%u%+i%+i %c%c %c%c %c%c%c%c%s\n",
-                        (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')),
-                        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,
-                        (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))),
-                        (n->split_mode == MODE_AUTOMATIC ? '-' : 'p'),
-                        (c->state == STATE_TILED ? '-' : (c->state == STATE_FLOATING ? 'f' : (c->state == STATE_FULLSCREEN ? 'F' : 'p'))),
-                        (c->layer == LAYER_NORMAL ? '-' : (c->layer == LAYER_ABOVE ? 'a' : 'b')),
-                        (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (c->private ? 'i' : '-'),
-                        (n == d->focus ? " *" : ""));
+       if (n == NULL) {
+               fprintf(rsp, "null");
        } else {
-               fprintf(rsp, "%c %c %lf\n", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'),
-                       (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
+               fprintf(rsp, "{");
+               fprintf(rsp, "\"splitType\": \"%s\", ", SPLIT_TYPE_STR(n->split_type));
+               fprintf(rsp, "\"splitRatio\": %lf, ", n->split_ratio);
+               fprintf(rsp, "\"splitMode\": \"%s\", ", SPLIT_MODE_STR(n->split_mode));
+               fprintf(rsp, "\"splitDir\": \"%s\", ", SPLIT_DIR_STR(n->split_dir));
+               fprintf(rsp, "\"birthRotation\": %i, ", n->birth_rotation);
+               fprintf(rsp, "\"privacyLevel\": %i, ", n->privacy_level);
+               fprintf(rsp, "\"vacant\": %s, ", BOOL_STR(n->vacant));
+               fprintf(rsp, "\"rectangle\": ");
+               query_rectangle(n->rectangle, rsp);
+               fprintf(rsp, ", ");
+               fprintf(rsp, "\"firstChild\": ");
+               query_node(n->first_child, rsp);
+               fprintf(rsp, ", ");
+               fprintf(rsp, "\"secondChild\": ");
+               query_node(n->second_child, rsp);
+               fprintf(rsp, ", ");
+               fprintf(rsp, "\"client\": ");
+               query_client(n->client, rsp);
+               fprintf(rsp, "}");
        }
+}
+
+void query_client(client_t *c, FILE *rsp)
+{
+       if (c == NULL) {
+               fprintf(rsp, "null");
+       } else {
+               fprintf(rsp, "{");
+               fprintf(rsp, "\"window\": %u, ", c->window);
+               fprintf(rsp, "\"className\": \"%s\", ", c->class_name);
+               fprintf(rsp, "\"instanceName\": \"%s\", ", c->instance_name);
+               fprintf(rsp, "\"borderWidth\": %u, ", c->border_width);
+               fprintf(rsp, "\"state\": \"%s\", ", STATE_STR(c->state));
+               fprintf(rsp, "\"lastState\": \"%s\", ", STATE_STR(c->last_state));
+               fprintf(rsp, "\"layer\": \"%s\", ", LAYER_STR(c->layer));
+               fprintf(rsp, "\"lastLayer\": \"%s\", ", LAYER_STR(c->last_layer));
+               fprintf(rsp, "\"locked\": %s, ", BOOL_STR(c->locked));
+               fprintf(rsp, "\"sticky\": %s, ", BOOL_STR(c->sticky));
+               fprintf(rsp, "\"urgent\": %s, ", BOOL_STR(c->urgent));
+               fprintf(rsp, "\"private\": %s, ", BOOL_STR(c->private));
+               fprintf(rsp, "\"icccmFocus\": %s, ", BOOL_STR(c->icccm_focus));
+               fprintf(rsp, "\"icccmInput\": %s, ", BOOL_STR(c->icccm_input));
+               fprintf(rsp, "\"minWidth\": %u, ", c->min_width);
+               fprintf(rsp, "\"maxWidth\": %u, ", c->max_width);
+               fprintf(rsp, "\"minHeight\": %u, ", c->min_height);
+               fprintf(rsp, "\"maxHeight\": %u, ", c->max_height);
+               fprintf(rsp, "\"numStates\": %i, ", c->num_states);
+               fprintf(rsp, "\"wmState\": ");
+               query_wm_state(c->wm_state, c->num_states, rsp);
+               fprintf(rsp, ", ");
+               fprintf(rsp, "\"tiledRectangle\": ");
+               query_rectangle(c->tiled_rectangle, rsp);
+               fprintf(rsp, ", ");
+               fprintf(rsp, "\"floatingRectangle\": ");
+               query_rectangle(c->floating_rectangle, rsp);
+               fprintf(rsp, "}");
+       }
+}
+
+void query_rectangle(xcb_rectangle_t r, FILE *rsp)
+{
+               fprintf(rsp, "{\"x\": %i, \"y\": %i, \"width\": %u, \"height\": %u}", r.x, r.y, r.width, r.height);
+}
 
-       query_tree(d, n->first_child, rsp, depth + 1);
-       query_tree(d, n->second_child, rsp, depth + 1);
+void query_wm_state(xcb_atom_t *wm_state, int num_states, FILE *rsp)
+{
+       fprintf(rsp, "[");
+       for (int i = 0; i < num_states; i++) {
+               fprintf(rsp, "%u", wm_state[i]);
+               if (i < num_states - 1) {
+                       fprintf(rsp, ", ");
+               }
+       }
+       fprintf(rsp, "]");
 }
 
 void query_history(coordinates_t loc, FILE *rsp)
 {
        for (history_t *h = history_head; h != NULL; h = h->next) {
                if ((loc.monitor != NULL && h->loc.monitor != loc.monitor)
-                               || (loc.desktop != NULL && h->loc.desktop != loc.desktop))
+                   || (loc.desktop != NULL && h->loc.desktop != loc.desktop)) {
                        continue;
+               }
                xcb_window_t win = XCB_NONE;
-               if (h->loc.node != NULL)
+               if (h->loc.node != NULL) {
                        win = h->loc.node->client->window;
+               }
                fprintf(rsp, "%s %s 0x%X\n", h->loc.monitor->name, h->loc.desktop->name, win);
        }
 }
 
 void query_stack(FILE *rsp)
 {
-       for (stacking_list_t *s = stack_head; s != NULL; s = s->next)
+       for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
                fprintf(rsp, "0x%X\n", s->node->client->window);
+       }
 }
 
 void query_windows(coordinates_t loc, FILE *rsp)
@@ -141,6 +218,26 @@ void query_windows(coordinates_t loc, FILE *rsp)
        }
 }
 
+void query_names(domain_t dom, coordinates_t loc, FILE *rsp)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (loc.monitor != NULL && m != loc.monitor) {
+                       continue;
+               }
+               if (dom == DOMAIN_MONITOR) {
+                       fprintf(rsp, "%s\n", m->name);
+               }
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       if (loc.desktop != NULL && d != loc.desktop) {
+                               continue;
+                       }
+                       if (dom == DOMAIN_DESKTOP) {
+                               fprintf(rsp, "%s\n", d->name);
+                       }
+               }
+       }
+}
+
 client_select_t make_client_select(void)
 {
        client_select_t sel = {
@@ -258,7 +355,7 @@ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
        history_dir_t hdi;
        if (parse_direction(desc, &dir)) {
                dst->node = nearest_neighbor(ref->monitor, ref->desktop, ref->node, dir, sel);
-               if (dst->node == NULL && num_monitors > 1) {
+               if (dst->node == NULL && mon_head != mon_tail) {
                        monitor_t *m = nearest_monitor(ref->monitor, dir, make_desktop_select());
                        if (m != NULL) {
                                coordinates_t loc = {m, m->desk, m->desk->focus};
diff --git a/query.h b/query.h
index cdcd92e0344d8f70ab4f45e2f1498b10806d3053..59a35be02eddccae2ec0c885c669dd48430d156c 100644 (file)
--- a/query.h
+++ b/query.h
@@ -34,12 +34,17 @@ typedef enum {
        DOMAIN_STACK
 } domain_t;
 
-void query_monitors(coordinates_t loc, domain_t dom, FILE *rsp);
-void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, FILE *rsp);
-void query_tree(desktop_t *d, node_t *n, FILE *rsp, unsigned int depth);
+void query_tree(FILE *rsp);
+void query_monitor(monitor_t *m, FILE *rsp);
+void query_desktop(desktop_t *d, FILE *rsp);
+void query_node(node_t *n, FILE *rsp);
+void query_client(client_t *c, FILE *rsp);
+void query_rectangle(xcb_rectangle_t r, FILE *rsp);
+void query_wm_state(xcb_atom_t *wm_state, int num_states, FILE *rsp);
 void query_history(coordinates_t loc, FILE *rsp);
 void query_stack(FILE *rsp);
 void query_windows(coordinates_t loc, FILE *rsp);
+void query_names(domain_t dom, coordinates_t loc, FILE *rsp);
 client_select_t make_client_select(void);
 desktop_select_t make_desktop_select(void);
 void cleanup_client_select(client_select_t *sel);
index 3ec7e8448f83536e430c9565c28d5f44cd1a53e2..b0018e1fc5994b694a01433e5608b6bfaf8b7f45 100644 (file)
--- a/restore.c
+++ b/restore.c
@@ -24,6 +24,8 @@
 
 #include <ctype.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include "bspwm.h"
 #include "desktop.h"
 #include "ewmh.h"
 #include "tree.h"
 #include "settings.h"
 #include "restore.h"
+#include "helpers.h"
+#include "common.h"
+#include "parse.h"
+#include "jsmn.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;
+       int nbtok = 256;
+       jsmn_parser parser;
+       jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
 
-       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 layout = 0, end = 0;
-                       name[0] = '\0';
-                       loc.desktop = NULL;
-                       sscanf(line + level, "%s %u %i %i,%i,%i,%i %c %c", name,
-                              &bw, &wg, &top, &right, &bottom, &left, &layout, &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;
-                       }
+       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++;
-                               }
-                       }
-                       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;
+                       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;
+       }
+
+       while (mon_head != NULL) {
+               remove_monitor(mon_head);
+       }
+
+       int num = tokens[0].size;
+       jsmntok_t *t = tokens + 1;
+       char *focusedMonitorName = NULL;
+
+       for (int i = 0; i < num; i++) {
+               if (keyeq("focusedMonitorName", t, json)) {
+                       focusedMonitorName = copy_string(t+1, json);
+                       t++;
+               } else if (keyeq("numClients", t, json)) {
+                       t++;
+                       sscanf(json + t->start, "%i", &num_clients);
+               } 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);
+                               add_monitor(m);
+                               t++;
                        }
                }
-               last_level = level;
+               t++;
        }
 
-       fclose(snapshot);
+       if (focusedMonitorName != NULL) {
+               coordinates_t loc;
+               if (locate_monitor(focusedMonitorName, &loc)) {
+                       mon = loc.monitor;
+               }
+       }
+
+       free(focusedMonitorName);
 
        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[] = {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;
-                                       propagate_vacant_state(n);
+                       }
+               }
+       }
+
+       ewmh_update_client_list();
+       ewmh_update_number_of_desktops();
+       ewmh_update_current_desktop();
+       ewmh_update_desktop_names();
+
+       free(tokens);
+       free(json);
+
+       return true;
+}
+
+#define RESTORE_INT(o, k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%i", &o->p);
+
+#define RESTORE_UINT(o, k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%u", &o->p);
+
+#define RESTORE_USINT(o, k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%hu", &o->p);
+
+#define RESTORE_DOUBLE(o, k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%lf", &o->p);
+
+#define RESTORE_ANY(o, k, p, f) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               char *val = copy_string(*t, json); \
+               f(val, &o->p); \
+               free(val);
+
+#define RESTORE_BOOL(o, k, p)  RESTORE_ANY(o, k, p, parse_bool)
+
+monitor_t *restore_monitor(jsmntok_t **t, char *json)
+{
+       int num = (*t)->size;
+       (*t)++;
+       monitor_t *m = make_monitor(NULL);
+       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(m, id, id)
+               RESTORE_BOOL(m, wired, wired)
+               RESTORE_INT(m, topPadding, top_padding)
+               RESTORE_INT(m, rightPadding, right_padding)
+               RESTORE_INT(m, bottomPadding, bottom_padding)
+               RESTORE_INT(m, leftPadding, left_padding)
+               RESTORE_INT(m, numSticky, num_sticky)
+               } 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)) {
+                       (*t)++;
+                       focusedDesktopName = copy_string(*t, json);
+               } 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);
+       xcb_window_t focusedWindow = 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);
+               } else if (keyeq("layout", *t, json)) {
+                       (*t)++;
+                       char *val = copy_string(*t, json);
+                       layout_t lyt;
+                       if (parse_layout(val, &lyt)) {
+                               d->layout = lyt;
+                       }
+                       free(val);
+               RESTORE_INT(d, topPadding, top_padding)
+               RESTORE_INT(d, rightPadding, right_padding)
+               RESTORE_INT(d, bottomPadding, bottom_padding)
+               RESTORE_INT(d, leftPadding, left_padding)
+               RESTORE_INT(d, windowGap, window_gap)
+               RESTORE_UINT(d, borderWidth, border_width)
+               } else if (keyeq("focusedWindow", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%u", &focusedWindow);
+               } 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 (focusedWindow != XCB_NONE) {
+               for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+                       if (f->client->window == focusedWindow) {
+                               d->focus = f;
+                               break;
+                       }
+               }
+       }
+
+       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)++;
+               node_t *n = make_node();
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("splitType", *t, json)) {
+                               (*t)++;
+                               char *val = copy_string(*t, json);
+                               parse_split_type(val, &n->split_type);
+                               free(val);
+                       RESTORE_DOUBLE(n, splitRatio, split_ratio)
+                       RESTORE_ANY(n, splitMode, split_mode, parse_split_mode)
+                       RESTORE_ANY(n, splitDir, split_dir, parse_direction)
+                       RESTORE_INT(n, birthRotation, birth_rotation)
+                       RESTORE_INT(n, privacyLevel, privacy_level)
+                       RESTORE_ANY(n, vacant, vacant, parse_bool)
+                       } 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;
                                }
-                               if (n->client->private) {
-                                       update_privacy_level(n, true);
+                               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;
                        }
-                       /* Has the side effect of restoring the node's rectangles and the client's tiled rectangles */
-                       arrange(m, d);
+                       (*t)++;
                }
+
+               return n;
        }
+}
 
-       ewmh_update_current_desktop();
+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(XCB_NONE, 0);
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("window", *t, json)) {
+                               (*t)++;
+                               sscanf(json + (*t)->start, "%u", &c->window);
+                       } else 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(c, state, state, parse_client_state)
+                       RESTORE_ANY(c, lastState, last_state, parse_client_state)
+                       RESTORE_ANY(c, layer, layer, parse_stack_layer)
+                       RESTORE_ANY(c, lastLayer, last_layer, parse_stack_layer)
+                       RESTORE_UINT(c, borderWidth, border_width)
+                       RESTORE_BOOL(c, locked, locked)
+                       RESTORE_BOOL(c, sticky, sticky)
+                       RESTORE_BOOL(c, urgent, urgent)
+                       RESTORE_BOOL(c, private, private)
+                       RESTORE_BOOL(c, icccmFocus, icccm_focus)
+                       RESTORE_BOOL(c, icccmInput, icccm_input)
+                       RESTORE_USINT(c, minWidth, min_width)
+                       RESTORE_USINT(c, maxWidth, max_width)
+                       RESTORE_USINT(c, minHeight, min_height)
+                       RESTORE_USINT(c, maxHeight, max_height)
+                       RESTORE_INT(c, numStates, num_states)
+                       } 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;
+                       }
+
+                       (*t)++;
+               }
+
+               return c;
+       }
+}
+
+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_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)
+{
+       return (strncmp(s, json + key->start, key->end - key->start) == 0);
+}
+
+char *copy_string(jsmntok_t *tok, char *json)
+{
+       size_t len = tok->end - tok->start + 1;
+       char *res = malloc(len * sizeof(char));
+       if (res == NULL) {
+               perror("Copy string: malloc");
+               return NULL;
+       }
+       strncpy(res, json+tok->start, len-1);
+       res[len-1] = '\0';
+       return res;
 }
 
-void restore_history(char *file_path)
+bool restore_history(const char *file_path)
 {
-       if (file_path == NULL)
-               return;
+       if (file_path == NULL) {
+               return false;
+       }
 
        FILE *snapshot = fopen(file_path, "r");
        if (snapshot == NULL) {
-               warn("Restore history: can't open '%s'.\n", file_path);
-               return;
+               perror("Restore history: fopen");
+               return false;
        }
 
        char line[MAXLEN];
@@ -231,6 +479,8 @@ void restore_history(char *file_path)
        char dnm[SMALEN];
        xcb_window_t win;
 
+       empty_history();
+
        while (fgets(line, sizeof(line), snapshot) != NULL) {
                if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
                        coordinates_t loc;
@@ -256,33 +506,41 @@ void restore_history(char *file_path)
        }
 
        fclose(snapshot);
+       return true;
 }
 
-void restore_stack(char *file_path)
+bool restore_stack(const char *file_path)
 {
-       if (file_path == NULL)
-               return;
+       if (file_path == NULL) {
+               return false;
+       }
 
        FILE *snapshot = fopen(file_path, "r");
        if (snapshot == NULL) {
-               warn("Restore stack: can't open '%s'.\n", file_path);
-               return;
+               perror("Restore stack: fopen");
+               return false;
        }
 
        char line[MAXLEN];
        xcb_window_t win;
 
+       while (stack_head != NULL) {
+               remove_stack(stack_head);
+       }
+
        while (fgets(line, sizeof(line), snapshot) != NULL) {
                if (sscanf(line, "%X", &win) == 1) {
                        coordinates_t loc;
-                       if (locate_window(win, &loc))
+                       if (locate_window(win, &loc)) {
                                stack_insert_after(stack_tail, loc.node);
-                       else
+                       } else {
                                warn("Can't locate window 0x%X.\n", win);
+                       }
                } else {
                        warn("Can't parse stack entry: '%s'\n", line);
                }
        }
 
        fclose(snapshot);
+       return true;
 }
index aba26a8f948eb60642153e51dad1c2d5c96c15c6..a05fdc024f96e2bdd27138ea9b8a214856964f19 100644 (file)
--- a/restore.h
+++ b/restore.h
 #ifndef BSPWM_RESTORE_H
 #define BSPWM_RESTORE_H
 
-void restore_tree(char *file_path);
-void restore_history(char *file_path);
-void restore_stack(char *file_path);
+#include "jsmn.h"
+
+bool restore_tree(const char *file_path);
+monitor_t *restore_monitor(jsmntok_t **t, char *json);
+desktop_t *restore_desktop(jsmntok_t **t, char *json);
+node_t *restore_node(jsmntok_t **t, char *json);
+client_t *restore_client(jsmntok_t **t, char *json);
+void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json);
+void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json);
+bool keyeq(char *s, jsmntok_t *key, char *json);
+char *copy_string(jsmntok_t *tok, char *json);
+bool restore_history(const char *file_path);
+bool restore_stack(const char *file_path);
 
 #endif
diff --git a/rule.c b/rule.c
index ecdd87d9405d7f268a60606806384f7cb0ddb277..b179a1a0100b2cc40fcc00654b9b918c06e8c80c 100644 (file)
--- a/rule.c
+++ b/rule.c
@@ -28,7 +28,7 @@
 #include "bspwm.h"
 #include "ewmh.h"
 #include "window.h"
-#include "messages.h"
+#include "parse.h"
 #include "settings.h"
 #include "rule.h"
 
diff --git a/tree.c b/tree.c
index 3410f659b567a0959fbab504cf337171e67d2359..775ea23635ad4acf5fdfc0ff9e1198edbaf82e63 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -307,7 +307,7 @@ void activate_node(monitor_t *m, desktop_t *d, node_t *n)
                }
        }
        d->focus = n;
-       put_status(SBSC_MASK_WINDOW_ACTIVATE, "window_activate %s %s 0x%X\n", m->name, d->name, n->client->window);
+       put_status(SBSC_MASK_WINDOW_ACTIVATE, "window_activate %s %s 0x%X\n", m->name, d->name, n!=NULL?n->client->window:0);
 }
 
 void focus_node(monitor_t *m, desktop_t *d, node_t *n)
@@ -423,25 +423,27 @@ client_t *make_client(xcb_window_t win, unsigned int border_width)
        c->border_width = border_width;
        c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false;
        c->icccm_input = true;
-       xcb_icccm_get_wm_protocols_reply_t protocols;
-       if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
-               if (has_proto(WM_TAKE_FOCUS, &protocols)) {
-                       c->icccm_focus = true;
-               }
-               xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
-       }
        c->num_states = 0;
-       xcb_ewmh_get_atoms_reply_t wm_state;
-       if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
-               for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_STATE; i++) {
-                       ewmh_wm_state_add(c, wm_state.atoms[i]);
+       if (win != XCB_NONE) {
+               xcb_icccm_get_wm_protocols_reply_t protocols;
+               if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
+                       if (has_proto(WM_TAKE_FOCUS, &protocols)) {
+                               c->icccm_focus = true;
+                       }
+                       xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
+               }
+               xcb_ewmh_get_atoms_reply_t wm_state;
+               if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
+                       for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_STATE; i++) {
+                               ewmh_wm_state_add(c, wm_state.atoms[i]);
+                       }
+                       xcb_ewmh_get_atoms_reply_wipe(&wm_state);
+               }
+               xcb_icccm_wm_hints_t hints;
+               if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1
+                   && (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {
+                       c->icccm_input = hints.input;
                }
-               xcb_ewmh_get_atoms_reply_wipe(&wm_state);
-       }
-       xcb_icccm_wm_hints_t hints;
-       if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1
-           && (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {
-               c->icccm_input = hints.input;
        }
        return c;
 }
@@ -533,13 +535,16 @@ node_t *second_extrema(node_t *n)
 
 node_t *next_leaf(node_t *n, node_t *r)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
+       }
        node_t *p = n;
-       while (is_second_child(p) && p != r)
+       while (is_second_child(p) && p != r) {
                p = p->parent;
-       if (p == r)
+       }
+       if (p == r) {
                return NULL;
+       }
        return first_extrema(p->parent->second_child);
 }
 
@@ -947,8 +952,11 @@ void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
                                d->focus = first_extrema(d->root);
                }
        }
-       if (n->client->sticky)
+
+       if (n->client->sticky) {
                m->num_sticky--;
+       }
+
        put_status(SBSC_MASK_REPORT);
 }
 
@@ -968,17 +976,20 @@ void remove_node(monitor_t *m, desktop_t *d, node_t *n)
        num_clients--;
        ewmh_update_client_list();
 
-       if (focused)
+       if (focused) {
                update_current();
+       }
 }
 
 void destroy_tree(node_t *n)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return;
+       }
        node_t *first_tree = n->first_child;
        node_t *second_tree = n->second_child;
        if (n->client != NULL) {
+               remove_stack_node(n);
                free(n->client);
                num_clients--;
        }
diff --git a/types.h b/types.h
index eb88b5c6d5285381e5f59cf8dd34598cf919b4e4..03e9ac0438ce1adadf3f354a0f4614aac347ccf8 100644 (file)
--- a/types.h
+++ b/types.h
@@ -157,14 +157,14 @@ typedef struct {
        bool sticky;
        bool urgent;
        bool private;
-       bool icccm_focus;
-       bool icccm_input;
        client_state_t state;
        client_state_t last_state;
        stack_layer_t layer;
        stack_layer_t last_layer;
        xcb_rectangle_t floating_rectangle;
        xcb_rectangle_t tiled_rectangle;
+       bool icccm_focus;
+       bool icccm_input;
        uint16_t min_width;
        uint16_t max_width;
        uint16_t min_height;
@@ -209,19 +209,19 @@ typedef struct monitor_t monitor_t;
 struct monitor_t {
        char name[SMALEN];
        xcb_randr_output_t id;
-       xcb_rectangle_t rectangle;
        xcb_window_t root;
        bool wired;
        int top_padding;
        int right_padding;
        int bottom_padding;
        int left_padding;
+       int num_sticky;
+       xcb_rectangle_t rectangle;
        desktop_t *desk;
        desktop_t *desk_head;
        desktop_t *desk_tail;
        monitor_t *prev;
        monitor_t *next;
-       int num_sticky;
 };
 
 typedef struct {
index 5cc82fe3611cecf32f9e3511e5f6a6edc97ea36e..1807d907228020542d1e181526481c32c4bb263b 100644 (file)
--- a/window.c
+++ b/window.c
@@ -33,7 +33,7 @@
 #include "stack.h"
 #include "tree.h"
 #include "subscribe.h"
-#include "messages.h"
+#include "parse.h"
 #include "window.h"
 
 void schedule_window(xcb_window_t win)
@@ -321,7 +321,7 @@ xcb_rectangle_t get_rectangle(monitor_t *m, client_t *c)
                        break;
                case STATE_FULLSCREEN:
                default:
-                       return m->rectangle;
+                       return m != NULL ? m->rectangle : (xcb_rectangle_t) {0, 0, screen_width, screen_height};
                        break;
         }
 }
@@ -397,7 +397,7 @@ void set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
 
        c->layer = l;
 
-       put_status(SBSC_MASK_WINDOW_LAYER, "window_layer %s %s 0x%X %s\n", m->name, d->name, c->window, LAYERSTR(l));
+       put_status(SBSC_MASK_WINDOW_LAYER, "window_layer %s %s 0x%X %s\n", m->name, d->name, c->window, LAYER_STR(l));
 
        if (d->focus == n) {
                neutralize_obscuring_windows(m, d, n);
@@ -429,7 +429,7 @@ void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
                        break;
        }
 
-       put_status(SBSC_MASK_WINDOW_STATE, "window_state %s %s 0x%X %s off\n", m->name, d->name, c->window, STATESTR(c->last_state));
+       put_status(SBSC_MASK_WINDOW_STATE, "window_state %s %s 0x%X %s off\n", m->name, d->name, c->window, STATE_STR(c->last_state));
 
        switch (c->state) {
                case STATE_TILED:
@@ -443,7 +443,7 @@ void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
                        break;
        }
 
-       put_status(SBSC_MASK_WINDOW_STATE, "window_state %s %s 0x%X %s on\n", m->name, d->name, c->window, STATESTR(c->state));
+       put_status(SBSC_MASK_WINDOW_STATE, "window_state %s %s 0x%X %s on\n", m->name, d->name, c->window, STATE_STR(c->state));
 }
 
 void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
@@ -511,7 +511,7 @@ void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
 
        client_t *c = n->client;
 
-       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X locked %s\n", m->name, d->name, c->window, ONOFFSTR(value));
+       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X locked %s\n", m->name, d->name, c->window, ON_OFF_STR(value));
 
        c->locked = value;
        window_draw_border(n, d->focus == n, m == mon);
@@ -524,7 +524,7 @@ void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
 
        client_t *c = n->client;
 
-       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X sticky %s\n", m->name, d->name, c->window, ONOFFSTR(value));
+       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X sticky %s\n", m->name, d->name, c->window, ON_OFF_STR(value));
 
        if (d != m->desk)
                transfer_node(m, d, n, m, m->desk, m->desk->focus);
@@ -548,7 +548,7 @@ void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
 
        client_t *c = n->client;
 
-       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X private %s\n", m->name, d->name, c->window, ONOFFSTR(value));
+       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X private %s\n", m->name, d->name, c->window, ON_OFF_STR(value));
 
        c->private = value;
        update_privacy_level(n, value);
@@ -562,7 +562,7 @@ void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value)
        n->client->urgent = value;
        window_draw_border(n, d->focus == n, m == mon);
 
-       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X urgent %s\n", m->name, d->name, n->client->window, ONOFFSTR(value));
+       put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X urgent %s\n", m->name, d->name, n->client->window, ON_OFF_STR(value));
        put_status(SBSC_MASK_REPORT);
 }