]> git.lizzy.rs Git - bspwm.git/commitdiff
Generalize window commands to nodes
authorBastien Dejean <nihilhill@gmail.com>
Tue, 22 Dec 2015 18:25:45 +0000 (19:25 +0100)
committerBastien Dejean <nihilhill@gmail.com>
Tue, 22 Dec 2015 18:25:45 +0000 (19:25 +0100)
69 files changed:
.gitignore
Makefile
README.md
Sourcedeps
VERSION [new file with mode: 0644]
bspc.c
bspwm.c
bspwm.h
contrib/bash_completion
contrib/zsh_completion
desktop.c
desktop.h
doc/TODO.md
doc/bspwm.1
doc/bspwm.1.asciidoc
events.c
ewmh.c
ewmh.h
examples/bspwmrc
examples/external_rules/pseudo_automatic_mode/external_rules
examples/loop/bspwmrc
examples/loop/profile
examples/loop/sxhkdrc
examples/panel/panel
examples/panel/panel_bar
examples/panel/panel_colors
examples/panel/profile
examples/sxhkdrc
helpers.c
helpers.h
history.c
history.h
jsmn.c
messages.c
messages.h
monitor.c
monitor.h
parse.c
parse.h
pointer.c
query.c
query.h
restore.c
restore.h
rule.c
rule.h
settings.c
settings.h
stack.c
stack.h
subscribe.c
subscribe.h
tests/Makefile [new file with mode: 0644]
tests/README.md [new file with mode: 0644]
tests/desktop/swap [new file with mode: 0755]
tests/desktop/transfer [new file with mode: 0755]
tests/node/flags [new file with mode: 0755]
tests/node/insertion [new file with mode: 0755]
tests/node/removal [new file with mode: 0755]
tests/node/swap [new file with mode: 0755]
tests/node/transfer [new file with mode: 0755]
tests/prelude [new file with mode: 0644]
tests/run [new file with mode: 0755]
tests/test_window.c [new file with mode: 0644]
tree.c
tree.h
types.h
window.c
window.h

index f63522125dab84720a7c4aabef1b57a1d7779727..cda793a3d51f4e32d1448d458d1f355b6356e152 100644 (file)
@@ -2,3 +2,4 @@ tags
 bspwm
 bspc
 *.o
+tests/test_window
index d67c9b25f90e749b705f6cb573c3b82311dd83c1..4a34c3dfd3a882c78b9c35f8c0419c0636c25fb4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,46 +1,37 @@
-VERSION = 0.9
+VERSION = $(shell git describe || cat VERSION)
 
-CC      ?= gcc
-LIBS     = -lm -lxcb -lxcb-util -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama
-CFLAGS  += -std=c99 -pedantic -Wall -Wextra -I$(PREFIX)/include
-CFLAGS  += -D_POSIX_C_SOURCE=200112L -DVERSION=\"$(VERSION)\"
-LDFLAGS += -L$(PREFIX)/lib
+CPPFLAGS += -D_POSIX_C_SOURCE=200112L -DVERSION=\"$(VERSION)\"
+CFLAGS   += -std=c99 -pedantic -Wall -Wextra
+LDLIBS    = -lm -lxcb -lxcb-util -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama
 
-PREFIX   ?= /usr/local
-BINPREFIX = $(PREFIX)/bin
-MANPREFIX = $(PREFIX)/share/man
-BASHCPL = $(PREFIX)/share/bash-completion/completions
-ZSHCPL = $(PREFIX)/share/zsh/site-functions
-DOCPREFIX = $(PREFIX)/share/doc/bspwm
+PREFIX    ?= /usr/local
+BINPREFIX ?= $(PREFIX)/bin
+MANPREFIX ?= $(PREFIX)/share/man
+DOCPREFIX ?= $(PREFIX)/share/doc/bspwm
+BASHCPL   ?= $(PREFIX)/share/bash-completion/completions
+ZSHCPL    ?= $(PREFIX)/share/zsh/site-functions
 
 MD_DOCS = README.md doc/CONTRIBUTING.md doc/INSTALL.md doc/MISC.md doc/TODO.md
-XSESSIONS = $(PREFIX)/share/xsessions
+XSESSIONS ?= $(PREFIX)/share/xsessions
 
 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)
+CLI_SRC = bspc.c helpers.c
+CLI_OBJ = $(CLI_SRC:.c=.o)
 
-all: CFLAGS += -Os
-all: LDFLAGS += -s
 all: bspwm bspc
 
-debug: CFLAGS += -O0 -g -DDEBUG
+debug: CFLAGS += -O0 -g
 debug: bspwm bspc
 
 include Sourcedeps
 
-$(WM_OBJ) $(CL_OBJ): Makefile
-
-.c.o:
-       $(CC) $(CFLAGS) $(OPTFLAGS) -c -o $@ $<
+$(WM_OBJ) $(CLI_OBJ): Makefile
 
 bspwm: $(WM_OBJ)
-       $(CC) -o $@ $(WM_OBJ) $(LDFLAGS) $(LIBS)
 
-bspc: $(CL_OBJ)
-       $(CC) -o $@ $(CL_OBJ) $(LDFLAGS) $(LIBS)
+bspc: $(CLI_OBJ)
 
 install:
        mkdir -p "$(DESTDIR)$(BINPREFIX)"
@@ -73,6 +64,6 @@ doc:
        a2x -v -d manpage -f manpage -a revnumber=$(VERSION) doc/bspwm.1.asciidoc
 
 clean:
-       rm -f $(WM_OBJ) $(CL_OBJ) bspwm bspc
+       rm -f $(WM_OBJ) $(CLI_OBJ) bspwm bspc
 
-.PHONY: all debug install uninstall doc deps clean
+.PHONY: all debug install uninstall doc clean
index 59537d1233bf259b3aff6536e1d31b9edb011b76..a4e586bc7ff1eefad6513c1dceb82af7d5fe37b8 100644 (file)
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ The automatic mode generates window spirals that rotate clockwise (resp. anti-cl
 
 ### Manual Mode
 
-The user can specify a region in the insertion point where the next new window should appear by sending a *window --presel DIR* message to *bspwm*.
+The user can specify a region in the insertion point where the next new window should appear by sending a *node -p|--presel-dir DIR* message to *bspwm*.
 
 The *DIR* argument allows to specify how the insertion point should be split (horizontally or vertically) and if the new window should be the first or the second child of the new internal node (the insertion point will become its *brother*).
 
@@ -130,13 +130,13 @@ For example, let's consider the following scenario:
 
 In state *X*, the insertion point is *1*.
 
-We send the following message to *bspwm*: *window --presel up*.
+We send the following message to *bspwm*: *node -p north*.
 
 Then add a new window: *4*, this leads to state *Y*: the new internal node, *c* becomes *a*'s first child.
 
-Finally we send another message: *window --presel left* and add window *5*.
+Finally we send another message: *node -p west* and add window *5*.
 
-The ratio of the preselection (that ends up being the ratio of the split of the new internal node) can be change with the *window --ratio* message.
+The ratio of the preselection (that ends up being the ratio of the split of the new internal node) can be changed with the *node -o|--presel-ratio* message.
 
 ## Supported protocols and standards
 
index 558f4f885706001926cc17357e241b207ef1ba20..286d5b7292471bcf2fad2e8841c4f463860ccc8c 100644 (file)
@@ -1,20 +1,20 @@
 bspc.o: bspc.c common.h helpers.h
-bspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h rule.h settings.h stack.h subscribe.h types.h window.h
+bspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h rule.h settings.h subscribe.h types.h window.h
 desktop.o: desktop.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
 events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
 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
+history.o: history.c bspwm.h helpers.h query.h tree.h types.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
+messages.o: messages.c bspwm.h common.h desktop.h helpers.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 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
+parse.o: parse.c helpers.h parse.h subscribe.h types.h
+pointer.o: pointer.c bspwm.h helpers.h monitor.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 monitor.h parse.h query.h subscribe.h tree.h types.h
+restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h query.h restore.h settings.h stack.h subscribe.h tree.h types.h
+rule.o: rule.c bspwm.h ewmh.h helpers.h parse.h rule.h settings.h subscribe.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
+stack.o: stack.c bspwm.h ewmh.h helpers.h stack.h subscribe.h tree.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 monitor.h parse.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..9a7d84f
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.9
\ No newline at end of file
diff --git a/bspc.c b/bspc.c
index 416edd727a7595991a33eb28c09d24da4ef29721..1df843758b65707b45bd5ff2f520a92e3ab020d3 100644 (file)
--- a/bspc.c
+++ b/bspc.c
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
-#ifdef __OpenBSD__
+#include <sys/stat.h>
 #include <sys/types.h>
-#endif
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <unistd.h>
-#include <ctype.h>
 #include "helpers.h"
 #include "common.h"
 
@@ -39,14 +38,16 @@ int main(int argc, char *argv[])
        struct sockaddr_un sock_address;
        char msg[BUFSIZ], rsp[BUFSIZ];
 
-       if (argc < 2)
+       if (argc < 2) {
                err("No arguments given.\n");
+       }
 
        sock_address.sun_family = AF_UNIX;
        char *sp;
 
-       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
                err("Failed to create the socket.\n");
+       }
 
        sp = getenv(SOCKET_ENV_VAR);
        if (sp != NULL) {
@@ -54,13 +55,15 @@ int main(int argc, char *argv[])
        } else {
                char *host = NULL;
                int dn = 0, sn = 0;
-               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0)
+               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
                        snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);
+               }
                free(host);
        }
 
-       if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
+       if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
                err("Failed to connect to the socket.\n");
+       }
 
        argc--, argv++;
        int msg_len = 0;
@@ -70,8 +73,9 @@ int main(int argc, char *argv[])
                msg_len += n;
        }
 
-       if (send(fd, msg, msg_len, 0) == -1)
+       if (send(fd, msg, msg_len, 0) == -1) {
                err("Failed to send the data.\n");
+       }
 
        int ret = 0, nb;
        while ((nb = recv(fd, rsp, sizeof(rsp)-1, 0)) > 0) {
diff --git a/bspwm.c b/bspwm.c
index 92e739f35dd4f39c9882398454b8ce2472c0571c..3671ce9f3f52135a0bcce0a697f667c7f5ce4fea 100644 (file)
--- a/bspwm.c
+++ b/bspwm.c
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#ifdef __OpenBSD__
+#include <sys/stat.h>
 #include <sys/types.h>
-#endif
+#include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <signal.h>
 #include <unistd.h>
+#include <stdbool.h>
 #include <xcb/xinerama.h>
 #include "types.h"
 #include "desktop.h"
 #include "monitor.h"
 #include "settings.h"
 #include "messages.h"
-#include "subscribe.h"
 #include "events.h"
 #include "common.h"
 #include "window.h"
 #include "history.h"
-#include "stack.h"
 #include "ewmh.h"
 #include "rule.h"
 #include "bspwm.h"
@@ -83,8 +80,9 @@ int main(int argc, char *argv[])
        } else {
                char *host = NULL;
                int dn = 0, sn = 0;
-               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0)
+               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
                        snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, host, dn, sn);
+               }
                free(host);
        }
 
@@ -93,28 +91,33 @@ int main(int argc, char *argv[])
 
        sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
 
-       if (sock_fd == -1)
+       if (sock_fd == -1) {
                err("Couldn't create the socket.\n");
+       }
 
        unlink(socket_path);
-       if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
+       if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
                err("Couldn't bind a name to the socket.\n");
+       }
 
-       if (listen(sock_fd, SOMAXCONN) == -1)
+       if (listen(sock_fd, SOMAXCONN) == -1) {
                err("Couldn't listen to the socket.\n");
+       }
 
        if (config_path[0] == '\0') {
                char *config_home = getenv(CONFIG_HOME_ENV);
-               if (config_home != NULL)
+               if (config_home != NULL) {
                        snprintf(config_path, sizeof(config_path), "%s/%s/%s", config_home, WM_NAME, CONFIG_NAME);
-               else
+               } else {
                        snprintf(config_path, sizeof(config_path), "%s/%s/%s/%s", getenv("HOME"), ".config", WM_NAME, CONFIG_NAME);
+               }
        }
 
        dpy = xcb_connect(NULL, &default_screen);
 
-       if (!check_connection(dpy))
+       if (!check_connection(dpy)) {
                exit(EXIT_FAILURE);
+       }
 
        load_settings();
        setup();
@@ -137,10 +140,12 @@ int main(int argc, char *argv[])
                FD_SET(sock_fd, &descriptors);
                FD_SET(dpy_fd, &descriptors);
                max_fd = MAX(sock_fd, dpy_fd);
+
                for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
                        FD_SET(pr->fd, &descriptors);
-                       if (pr->fd > max_fd)
+                       if (pr->fd > max_fd) {
                                max_fd = pr->fd;
+                       }
                }
 
                if (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {
@@ -163,8 +168,9 @@ int main(int argc, char *argv[])
                                        if (rsp != NULL) {
                                                int ret = handle_message(msg, n, rsp);
                                                if (ret != MSG_SUBSCRIBE) {
-                                                       if (ret != MSG_SUCCESS)
+                                                       if (ret != MSG_SUCCESS) {
                                                                fprintf(rsp, "%c", ret);
+                                                       }
                                                        fflush(rsp);
                                                        fclose(rsp);
                                                }
@@ -181,10 +187,12 @@ int main(int argc, char *argv[])
                                        free(event);
                                }
                        }
+
                }
 
-               if (!check_connection(dpy))
+               if (!check_connection(dpy)) {
                        running = false;
+               }
        }
 
        cleanup();
@@ -201,7 +209,7 @@ int main(int argc, char *argv[])
 
 void init(void)
 {
-       num_clients = 0;
+       clients_count = 0;
        monitor_uid = desktop_uid = 0;
        mon = mon_head = mon_tail = pri_mon = NULL;
        history_head = history_tail = history_needle = NULL;
@@ -210,7 +218,7 @@ void init(void)
        subscribe_head = subscribe_tail = NULL;
        pending_rule_head = pending_rule_tail = NULL;
        last_motion_time = last_motion_x = last_motion_y = 0;
-       visible = auto_raise = sticky_still = record_history = true;
+       auto_raise = sticky_still = record_history = true;
        randr_base = 0;
        exit_status = 0;
 }
@@ -220,14 +228,16 @@ void setup(void)
        init();
        ewmh_init();
        screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
-       if (screen == NULL)
+
+       if (screen == NULL) {
                err("Can't acquire the default screen.\n");
+       }
+
        root = screen->root;
        register_events();
 
        screen_width = screen->width_in_pixels;
        screen_height = screen->height_in_pixels;
-       root_depth = screen->root_depth;
 
        meta_window = xcb_generate_id(dpy);
        xcb_create_window(dpy, XCB_COPY_FROM_PARENT, meta_window, root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_NONE, NULL);
@@ -280,6 +290,7 @@ void setup(void)
                randr = false;
                warn("Couldn't retrieve monitors via RandR.\n");
                bool xinerama_is_active = false;
+
                if (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {
                        xcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);
                        if (xia != NULL) {
@@ -314,8 +325,9 @@ void setup(void)
        ewmh_update_current_desktop();
        frozen_pointer = make_pointer_state();
        xcb_get_input_focus_reply_t *ifo = xcb_get_input_focus_reply(dpy, xcb_get_input_focus(dpy), NULL);
-       if (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE))
+       if (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE)) {
                clear_input_focus();
+       }
        free(ifo);
 }
 
@@ -331,6 +343,8 @@ void register_events(void)
 
 void cleanup(void)
 {
+       mon = NULL;
+
        while (mon_head != NULL) {
                remove_monitor(mon_head);
        }
@@ -343,6 +357,7 @@ void cleanup(void)
        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 ea505cf2236340481b6fc9aa1bf39248aed35c6d..c3c304afa78962c109b7dd0fa5b1f176717c2964 100644 (file)
--- a/bspwm.h
+++ b/bspwm.h
 #define CLIENT_EVENT_MASK   (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
 #define META_WINDOW_IC      "wm\0Bspwm"
 #define ROOT_WINDOW_IC      "root\0Bspwm"
+#define PRESEL_FEEDBACK_IC  "presel_feedback\0Bspwm"
 #define MOTION_RECORDER_IC  "motion_recorder\0Bspwm"
 
 xcb_connection_t *dpy;
 int default_screen, screen_width, screen_height;
-uint32_t num_clients;
+uint32_t clients_count;
 unsigned int monitor_uid;
 unsigned int desktop_uid;
 xcb_screen_t *screen;
 xcb_window_t root;
-uint8_t root_depth;
 char config_path[MAXLEN];
 
 monitor_t *mon;
@@ -66,7 +66,6 @@ xcb_atom_t WM_TAKE_FOCUS;
 xcb_atom_t WM_DELETE_WINDOW;
 int exit_status;
 
-bool visible;
 bool auto_raise;
 bool sticky_still;
 bool record_history;
index cfcdccbb8a4db23b4ec619bcb3dfa55b882fc6ed..2cf8f2153a1c36062988557e10d9c70e1d6eaf1a 100644 (file)
@@ -1,7 +1,7 @@
 _bspc() {
        local commands='window desktop monitor query pointer rule restore control config quit'
 
-       local settings='external_rules_command status_prefix focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color focused_sticky_border_color normal_sticky_border_color focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio initial_polarity borderless_monocle gapless_monocle leaf_monocle focus_follows_pointer pointer_follows_focus pointer_follows_monitor history_aware_focus focus_by_distance ignore_ewmh_focus center_pseudo_tiled remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'
+       local settings='external_rules_command status_prefix normal_border_color active_border_color focused_border_color presel_feedback_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio initial_polarity borderless_monocle gapless_monocle leaf_monocle focus_follows_pointer pointer_follows_focus pointer_follows_monitor history_aware_focus focus_by_distance ignore_ewmh_focus center_pseudo_tiled remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'
 
        COMPREPLY=()
 
index 4066b21b4372f1052da6de7c3cce7a6e4916e79a..2c3808e31e1487ce2baf7588a6f79e77c5a6103b 100644 (file)
@@ -3,7 +3,7 @@
 _bspc() {
        local -a commands settings
        commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'rule' 'restore' 'control' 'config' 'quit')
-       settings=('external_rules_command' 'status_prefix' 'focused_border_color' 'active_border_color' 'normal_border_color' 'presel_border_color' 'focused_locked_border_color' 'active_locked_border_color' 'normal_locked_border_color' 'focused_sticky_border_color' 'normal_sticky_border_color' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'initial_polarity' 'borderless_monocle' 'gapless_monocle' 'leaf_monocle' 'focus_follows_pointer' 'pointer_follows_focus' 'pointer_follows_monitor' 'history_aware_focus' 'focus_by_distance' 'ignore_ewmh_focus' 'center_pseudo_tiled' 'remove_disabled_monitors' 'remove_unplugged_monitors' 'merge_overlapping_monitors')
+       settings=('external_rules_command' 'status_prefix' 'normal_border_color' 'active_border_color' 'focused_border_color' 'presel_feedback_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'initial_polarity' 'borderless_monocle' 'gapless_monocle' 'leaf_monocle' 'focus_follows_pointer' 'pointer_follows_focus' 'pointer_follows_monitor' 'history_aware_focus' 'focus_by_distance' 'ignore_ewmh_focus' 'center_pseudo_tiled' 'remove_disabled_monitors' 'remove_unplugged_monitors' 'merge_overlapping_monitors')
        if (( CURRENT == 2 )) ; then
                _values 'command' "$commands[@]"
        elif (( CURRENT == 3 )) ; then
index 41a8a4d00d7e920c512b6383d0abfda8549c621d..78a4e101e3333134b308c2b36d8f70b64c90be04 100644 (file)
--- a/desktop.c
+++ b/desktop.c
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
 #include "bspwm.h"
 #include "ewmh.h"
 #include "history.h"
 #include "monitor.h"
 #include "query.h"
 #include "tree.h"
-#include "window.h"
 #include "desktop.h"
 #include "subscribe.h"
 #include "settings.h"
@@ -38,33 +40,48 @@ void focus_desktop(monitor_t *m, desktop_t *d)
 {
        focus_monitor(m);
 
-       if (d == mon->desk)
-               return;
+       show_desktop(d);
+       if (m->desk != d) {
+               hide_desktop(m->desk);
+       }
+
+       m->desk = d;
+       ewmh_update_current_desktop();
 
        put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus %s %s\n", m->name, d->name);
+}
+
+void activate_desktop(monitor_t *m, desktop_t *d)
+{
+       if (d == m->desk) {
+               return;
+       }
 
        show_desktop(d);
-       hide_desktop(mon->desk);
+       hide_desktop(m->desk);
 
-       mon->desk = d;
+       m->desk = d;
 
-       ewmh_update_current_desktop();
+       put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate %s %s\n", m->name, d->name);
        put_status(SBSC_MASK_REPORT);
 }
 
 desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel)
 {
        desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
-       if (f == NULL)
+       if (f == NULL) {
                f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
+       }
 
        while (f != d) {
                coordinates_t loc = {m, f, NULL};
-               if (desktop_matches(&loc, &loc, sel))
+               if (desktop_matches(&loc, &loc, sel)) {
                        return f;
+               }
                f = (dir == CYCLE_PREV ? f->prev : f->next);
-               if (f == NULL)
+               if (f == NULL) {
                        f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
+               }
        }
 
        return NULL;
@@ -72,51 +89,66 @@ desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_
 
 void change_layout(monitor_t *m, desktop_t *d, layout_t l)
 {
-       put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout %s %s %s\n", m->name, d->name, l==LAYOUT_TILED?"tiled":"monocle");
        d->layout = l;
        arrange(m, d);
+
+       put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout %s %s %s\n", m->name, d->name, l==LAYOUT_TILED?"tiled":"monocle");
+
        if (d == m->desk) {
                put_status(SBSC_MASK_REPORT);
        }
 }
 
-void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
 {
-       if (ms == md) {
-               return;
+       if (ms == NULL || md == NULL || d == NULL || ms == md) {
+               return false;
        }
 
-       put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer %s %s %s\n", ms->name, d->name, md->name);
+       bool was_active = (d == ms->desk);
 
-       desktop_t *dd = ms->desk;
        unlink_desktop(ms, d);
-       insert_desktop(md, d);
 
-       if (d == dd) {
-               if (ms->desk != NULL) {
-                       show_desktop(ms->desk);
-               }
-               if (md->desk != d) {
-                       hide_desktop(d);
-               }
+       if (ms->sticky_count > 0 && was_active && ms->desk != NULL) {
+               sticky_still = false;
+               transfer_sticky_nodes(ms, d, ms->desk, d->root);
+               sticky_still = true;
        }
 
-       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-               translate_client(ms, md, n->client);
+       if (md->desk != NULL) {
+               hide_desktop(d);
        }
 
+       insert_desktop(md, d);
+
+       history_transfer_desktop(md, d);
+       adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
        arrange(md, d);
 
-       if (d != dd && md->desk == d) {
-               show_desktop(d);
+       if (was_active && ms->desk != NULL) {
+               if (mon == ms) {
+                       focus_node(ms, ms->desk, ms->desk->focus);
+               } else {
+                       activate_node(ms, ms->desk, ms->desk->focus);
+               }
        }
 
-       history_transfer_desktop(md, d);
+       if (md->desk == d) {
+               if (mon == md) {
+                       focus_node(md, d, d->focus);
+               } else {
+                       activate_node(md, d, d->focus);
+               }
+       }
 
        ewmh_update_wm_desktops();
        ewmh_update_desktop_names();
        ewmh_update_current_desktop();
+
+       put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer %s %s %s\n", ms->name, d->name, md->name);
        put_status(SBSC_MASK_REPORT);
+
+       return true;
 }
 
 desktop_t *make_desktop(const char *name)
@@ -176,9 +208,24 @@ void add_desktop(monitor_t *m, desktop_t *d)
        put_status(SBSC_MASK_REPORT);
 }
 
-void empty_desktop(desktop_t *d)
+desktop_t *find_desktop_in(const char *name, monitor_t *m)
 {
-       destroy_tree(d->root);
+       if (m == NULL) {
+               return NULL;
+       }
+
+       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+               if (streq(d->name, name)) {
+                       return d;
+               }
+       }
+
+       return NULL;
+}
+
+void empty_desktop(monitor_t *m, desktop_t *d)
+{
+       destroy_tree(m, d, d->root);
        d->root = d->focus = NULL;
 }
 
@@ -186,17 +233,30 @@ void unlink_desktop(monitor_t *m, desktop_t *d)
 {
        desktop_t *prev = d->prev;
        desktop_t *next = d->next;
-       desktop_t *last_desk = history_get_desktop(m, d);
-       if (prev != NULL)
+
+       if (prev != NULL) {
                prev->next = next;
-       if (next != NULL)
+       }
+
+       if (next != NULL) {
                next->prev = prev;
-       if (m->desk_head == d)
+       }
+
+       if (m->desk_head == d) {
                m->desk_head = next;
-       if (m->desk_tail == d)
+       }
+
+       if (m->desk_tail == d) {
                m->desk_tail = prev;
-       if (m->desk == d)
-               m->desk = (last_desk == NULL ? (prev == NULL ? next : prev) : last_desk);
+       }
+
+       if (m->desk == d) {
+               m->desk = history_last_desktop(m, d);
+               if (m->desk == NULL) {
+                       m->desk = (prev == NULL ? next : prev);
+               }
+       }
+
        d->prev = d->next = NULL;
 }
 
@@ -204,22 +264,33 @@ void remove_desktop(monitor_t *m, desktop_t *d)
 {
        put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove %s %s\n", m->name, d->name);
 
+       bool was_focused = (mon != NULL && d == mon->desk);
+       bool was_active = (d == m->desk);
+       history_remove(d, NULL, false);
        unlink_desktop(m, d);
-       history_remove(d, NULL);
-       empty_desktop(d);
+       empty_desktop(m, d);
        free(d);
 
        ewmh_update_current_desktop();
        ewmh_update_number_of_desktops();
        ewmh_update_desktop_names();
 
+       if (mon != NULL && m->desk != NULL) {
+               if (was_focused) {
+                       update_focused();
+               } else if (was_active) {
+                       activate_node(m, m->desk, m->desk->focus);
+               }
+       }
+
        put_status(SBSC_MASK_REPORT);
 }
 
 void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
 {
-       if (ds == NULL || dd == NULL || ds == dd)
+       if (ds == NULL || dd == NULL || ds == dd) {
                return;
+       }
        node_t *n = first_extrema(ds->root);
        while (n != NULL) {
                node_t *next = next_leaf(n, ds->root);
@@ -228,10 +299,12 @@ void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
        }
 }
 
-void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
+bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
 {
-       if (d1 == NULL || d2 == NULL || d1 == d2) {
-               return;
+       if (d1 == NULL || d2 == NULL || d1 == d2 ||
+           (m1->desk == d1 && m1->sticky_count > 0) ||
+           (m2->desk == d2 && m2->sticky_count > 0)) {
+               return false;
        }
 
        put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap %s %s %s %s\n", m1->name, d1->name, m2->name, d2->name);
@@ -240,27 +313,40 @@ void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
        bool d2_focused = (m2->desk == d2);
 
        if (m1 != m2) {
-               if (m1->desk == d1)
+               if (m1->desk == d1) {
                        m1->desk = d2;
-               if (m1->desk_head == d1)
+               }
+               if (m1->desk_head == d1) {
                        m1->desk_head = d2;
-               if (m1->desk_tail == d1)
+               }
+               if (m1->desk_tail == d1) {
                        m1->desk_tail = d2;
-               if (m2->desk == d2)
+               }
+               if (m2->desk == d2) {
                        m2->desk = d1;
-               if (m2->desk_head == d2)
+               }
+               if (m2->desk_head == d2) {
                        m2->desk_head = d1;
-               if (m2->desk_tail == d2)
+               }
+               if (m2->desk_tail == d2) {
                        m2->desk_tail = d1;
+               }
        } else {
-               if (m1->desk_head == d1)
+               if (m1->desk == d1) {
+                       m1->desk = d2;
+               } else if (m1->desk == d2) {
+                       m1->desk = d1;
+               }
+               if (m1->desk_head == d1) {
                        m1->desk_head = d2;
-               else if (m1->desk_head == d2)
+               } else if (m1->desk_head == d2) {
                        m1->desk_head = d1;
-               if (m1->desk_tail == d1)
+               }
+               if (m1->desk_tail == d1) {
                        m1->desk_tail = d2;
-               else if (m1->desk_tail == d2)
+               } else if (m1->desk_tail == d2) {
                        m1->desk_tail = d1;
+               }
        }
 
        desktop_t *p1 = d1->prev;
@@ -268,14 +354,18 @@ void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
        desktop_t *p2 = d2->prev;
        desktop_t *n2 = d2->next;
 
-       if (p1 != NULL && p1 != d2)
+       if (p1 != NULL && p1 != d2) {
                p1->next = d2;
-       if (n1 != NULL && n1 != d2)
+       }
+       if (n1 != NULL && n1 != d2) {
                n1->prev = d2;
-       if (p2 != NULL && p2 != d1)
+       }
+       if (p2 != NULL && p2 != d1) {
                p2->next = d1;
-       if (n2 != NULL && n2 != d1)
+       }
+       if (n2 != NULL && n2 != d1) {
                n2->prev = d1;
+       }
 
        d1->prev = p2 == d1 ? d2 : p2;
        d1->next = n2 == d1 ? d2 : n2;
@@ -283,49 +373,64 @@ void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
        d2->next = n1 == d2 ? d1 : n1;
 
        if (m1 != m2) {
-               for (node_t *n = first_extrema(d1->root); n != NULL; n = next_leaf(n, d1->root))
-                       translate_client(m1, m2, n->client);
-               for (node_t *n = first_extrema(d2->root); n != NULL; n = next_leaf(n, d2->root))
-                       translate_client(m2, m1, n->client);
+               adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
+               adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
                history_swap_desktops(m1, d1, m2, d2);
                arrange(m1, d2);
                arrange(m2, d1);
-               if (d1_focused && !d2_focused) {
-                       hide_desktop(d1);
-                       show_desktop(d2);
-               } else if (!d1_focused && d2_focused) {
-                       show_desktop(d1);
-                       hide_desktop(d2);
-               }
        }
 
-       update_input_focus();
+       if (d1_focused && !d2_focused) {
+               hide_desktop(d1);
+               show_desktop(d2);
+       } else if (!d1_focused && d2_focused) {
+               show_desktop(d1);
+               hide_desktop(d2);
+       }
+
+       if (d1 == mon->desk) {
+               focus_node(m2, d1, d1->focus);
+       } else if (d1 == m2->desk) {
+               activate_node(m2, d1, d1->focus);
+       }
+
+       if (d2 == mon->desk) {
+               focus_node(m1, d2, d2->focus);
+       } else if (d2 == m1->desk) {
+               activate_node(m1, d2, d2->focus);
+       }
+
        ewmh_update_wm_desktops();
        ewmh_update_desktop_names();
        ewmh_update_current_desktop();
+
        put_status(SBSC_MASK_REPORT);
+
+       return true;
 }
 
 void show_desktop(desktop_t *d)
 {
-       if (!visible)
+       if (d == NULL) {
                return;
-       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
-               window_show(n->client->window);
+       }
+       show_node(d->root);
 }
 
 void hide_desktop(desktop_t *d)
 {
-       if (!visible)
+       if (d == NULL) {
                return;
-       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
-               window_hide(n->client->window);
+       }
+       hide_node(d->root);
 }
 
 bool is_urgent(desktop_t *d)
 {
-       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
-               if (n->client->urgent)
+       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+               if (n->client->urgent) {
                        return true;
+               }
+       }
        return false;
 }
index 7ebc38d10a906c18e07f56cbd1766cfc16f2880b..858643fa88c662cbbc661b941e7fa15d408392d3 100644 (file)
--- a/desktop.h
+++ b/desktop.h
 #define DEFAULT_DESK_NAME    "Desktop"
 
 void focus_desktop(monitor_t *m, desktop_t *d);
+void activate_desktop(monitor_t *m, desktop_t *d);
 desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel);
 void change_layout(monitor_t *m, desktop_t *d, layout_t l);
-void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
 desktop_t *make_desktop(const char *name);
 void rename_desktop(monitor_t *m, desktop_t *d, const char *name);
 void initialize_desktop(desktop_t *d);
 void insert_desktop(monitor_t *m, desktop_t *d);
 void add_desktop(monitor_t *m, desktop_t *d);
-void empty_desktop(desktop_t *d);
+desktop_t *find_desktop_in(const char *name, monitor_t *m);
+void empty_desktop(monitor_t *m, desktop_t *d);
 void unlink_desktop(monitor_t *m, desktop_t *d);
 void remove_desktop(monitor_t *m, desktop_t *d);
 void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);
-void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
+bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
 void show_desktop(desktop_t *d);
 void hide_desktop(desktop_t *d);
 bool is_urgent(desktop_t *d);
index 41ffb77a095a0d35506f75c3425463fc16c665d6..48ca92b71c2d943d4a9433114326bc894b18e147 100644 (file)
@@ -1,7 +1,12 @@
-- Internal nodes selectors/actions: labels?
-- Clean up ewmh.c.
+- Add `--activate` command for desktops and monitors.
+- Provide a class and instance name flag for rules
+- Write tests.
+- Make panel example respond to pointer events.
+- Implement all the MUSTs in the EWMH specification.
+- Add zoom feature (view point distinct from root).
+- Add receptacles (leaves with NULL client pointer).
 - Handle window size constraints specified by size hints.
+- Add support for showing/hiding nodes.
 - Set more attributes in `make_client` (instead of doing it in `apply_rules`) and don't pass `XCB_NONE` as argument.
-- Invisible state.
 - Use BSD `sys/{queue/tree}.h` for {list,tree} structures?
 - Handle malloc failure everywhere.
index de3825f632fe294eac7bf8e68beeb52e71696ee9..3339477160e2e0c622d5f7314e2cc53ed392bceb 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: bspwm
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: 11/26/2015
+.\"      Date: 12/22/2015
 .\"    Manual: Bspwm Manual
-.\"    Source: Bspwm 0.9
+.\"    Source: Bspwm 0.9-104-ge434521
 .\"  Language: English
 .\"
-.TH "BSPWM" "1" "11/26/2015" "Bspwm 0\&.9" "Bspwm Manual"
+.TH "BSPWM" "1" "12/22/2015" "Bspwm 0\&.9\-104\-ge434521" "Bspwm Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -33,7 +33,7 @@ bspwm \- Binary space partitioning window manager
 .sp
 \fBbspwm\fR [\fB\-h\fR|\fB\-v\fR|\fB\-c\fR \fICONFIG_PATH\fR]
 .sp
-\fBbspc\fR \fICOMMAND\fR [\fIARGUMENTS\fR]
+\fBbspc\fR \fIDOMAIN\fR [\fISELECTOR\fR] \fICOMMANDS\fR
 .SH "DESCRIPTION"
 .sp
 \fBbspwm\fR is a tiling window manager that represents windows as the leaves of a full binary tree\&.
@@ -61,7 +61,7 @@ Use the given configuration file\&.
 .RS 4
 .\}
 .nf
-DIR         := left | right | up | down
+DIR         := north | west | south | east
 CYCLE_DIR   := next | prev
 .fi
 .if n \{\
@@ -69,32 +69,40 @@ CYCLE_DIR   := next | prev
 .\}
 .SH "SELECTORS"
 .sp
-Selectors are used to select a target window, desktop, or monitor\&. A selector can either describe the target relatively or name it globally\&.
+Selectors are used to select a target node, desktop, or monitor\&. A selector can either describe the target relatively or name it globally\&.
 .sp
-Descriptive (relative) selectors consist of a primary selector and any number of non\-conflicting modifiers as follows:
+Selectors consist of a descriptor and any number of non\-conflicting modifiers as follows:
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-PRIMARY_SELECTOR[\&.MODIFIER]*
+DESCRIPTOR(\&.MODIFIER)*
 .fi
 .if n \{\
 .RE
 .\}
 .sp
-For obvious reasons, neither desktop nor monitor names may be valid descriptive selectors\&.
-.sp
 An exclamation mark can be prepended to certain modifiers in order to reverse their meaning\&.
-.SS "Window"
+.SS "Node"
 .sp
-Select a window\&.
+Select a node\&.
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-WINDOW_SEL := (<window_id>|DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.[!]automatic][\&.[!](tiled|pseudo_tiled|floating|fullscreen)][\&.[!](below|normal|above)][\&.[!]local][\&.[!]same_class][\&.[!]focused][\&.[!](urgent|sticky|private|locked)]
+NODE_SEL := (<node_id>|PATH|DIR|CYCLE_DIR|last|older|newer|biggest|focused)[\&.[!]focused][\&.[!]automatic][\&.[!]local][\&.[!]leaf][\&.[!]STATE][\&.[!]FLAG][\&.[!]LAYER][\&.[!]same_class]
+
+STATE := tiled|pseudo_tiled|floating|fullscreen
+
+FLAG := urgent|sticky|private|locked
+
+LAYER := below|normal|above
+
+PATH := @[DESK_SEL:][[/]JUMP](/JUMP)*
+
+JUMP := first|1|second|2|brother|parent|DIR
 .fi
 .if n \{\
 .RE
@@ -105,12 +113,22 @@ WINDOW_SEL := (<window_id>|DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.[!
 .nr an-break-flag 1
 .br
 .ps +1
-\fBPrimary Selectors\fR
+\fBDescriptors\fR
+.RS 4
+.PP
+<node_id>
 .RS 4
+Selects the node with the given ID\&.
+.RE
+.PP
+\fIPATH\fR
+.RS 4
+Selects the node at the given path\&.
+.RE
 .PP
 \fIDIR\fR
 .RS 4
-Selects the window in the given (spacial) direction relative to the active window\&.
+Selects the window in the given (spacial) direction relative to the active node\&.
 .RE
 .PP
 \fICYCLE_DIR\fR
@@ -125,22 +143,22 @@ Selects the biggest window on the current desktop\&.
 .PP
 last
 .RS 4
-Selects the previously focused window\&.
+Selects the previously focused node\&.
 .RE
 .PP
 focused
 .RS 4
-Selects the currently focused window\&.
+Selects the currently focused node\&.
 .RE
 .PP
 older
 .RS 4
-Selects the window older than the focused window in the history\&.
+Selects the node older than the focused node in the history\&.
 .RE
 .PP
 newer
 .RS 4
-Selects the window newer than the focused window in the history\&.
+Selects the node newer than the focused node in the history\&.
 .RE
 .RE
 .sp
@@ -149,32 +167,73 @@ Selects the window newer than the focused window in the history\&.
 .nr an-break-flag 1
 .br
 .ps +1
-\fBModifiers\fR
+\fBPath Jumps\fR
 .RS 4
+.sp
+The initial node is the focused node (or the root if the path starts with \fI/\fR) of the focused desktop (or the selected desktop if the path has a \fIDESK_SEL\fR prefix)\&.
 .PP
-[!](tiled|pseudo_tiled|floating|fullscreen)
+1|first
 .RS 4
-Only consider windows having or not having the given state\&.
+Jumps to the first child\&.
 .RE
 .PP
-[!]automatic
+2|second
 .RS 4
-Only consider windows in automatic or manual insertion mode\&.
+Jumps to the second child\&.
 .RE
 .PP
+brother
+.RS 4
+Jumps to the brother node\&.
+.RE
+.PP
+parent
+.RS 4
+Jumps to the parent node\&.
+.RE
+.PP
+\fIDIR\fR
+.RS 4
+Jumps to the node holding the edge in the given direction\&.
+.RE
+.RE
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBModifiers\fR
+.RS 4
+.PP
 [!]focused
 .RS 4
-Only consider focused or unfocused windows\&.
+Only consider focused or unfocused nodes\&.
 .RE
 .PP
-[!]same_class
+[!]automatic
 .RS 4
-Only consider windows that have or don\(cqt have the same class as the current window\&.
+Only consider nodes in automatic or manual insertion mode\&.
 .RE
 .PP
 [!]local
 .RS 4
-Only consider windows in or not in the current desktop\&.
+Only consider nodes in or not in the current desktop\&.
+.RE
+.PP
+[!]leaf
+.RS 4
+Only consider leaves or internal nodes\&.
+.RE
+.PP
+[!](tiled|pseudo_tiled|floating|fullscreen)
+.RS 4
+Only consider windows in or not in the given state\&.
+.RE
+.PP
+[!]same_class
+.RS 4
+Only consider windows that have or don\(cqt have the same class as the current window\&.
 .RE
 .PP
 [!](private|urgent|sticky|locked)
@@ -206,7 +265,7 @@ DESKTOP_SEL := (<desktop_name>|[MONITOR_SEL:](focused|^<n>)CYCLE_DIR|last|older|
 .nr an-break-flag 1
 .br
 .ps +1
-\fBPrimary Selectors\fR
+\fBDescriptors\fR
 .RS 4
 .PP
 <desktop_name>
@@ -292,7 +351,7 @@ MONITOR_SEL := (<monitor_name>|^<n>|DIR|CYCLE_DIR|last|primary|focused|older|new
 .nr an-break-flag 1
 .br
 .ps +1
-\fBPrimary Selectors\fR
+\fBDescriptors\fR
 .RS 4
 .PP
 <monitor_name>
@@ -380,12 +439,12 @@ fullscreen
 .RS 4
 Fills its monitor rectangle and has no borders\&. It is send in the ABOVE layer by default\&.
 .RE
-.SH "WINDOW FLAGS"
+.SH "NODE FLAGS"
 .PP
 locked
 .RS 4
 Ignores the
-\fBwindow \-\-close\fR
+\fBnode \-\-close\fR
 message\&.
 .RE
 .PP
@@ -408,8 +467,8 @@ Has its urgency hint set\&. This flag is set externally\&.
 There\(cqs three stacking layers: BELOW, NORMAL and ABOVE\&.
 .sp
 In each layer, the window are orderered as follow: tiled & pseudo\-tiled < fullscreen < floating\&.
-.SH "COMMANDS"
-.SS "Window"
+.SH "DOMAINS"
+.SS "Node"
 .sp
 .it 1 an-trap
 .nr an-no-space-flag 1
@@ -419,7 +478,7 @@ In each layer, the window are orderered as follow: tiled & pseudo\-tiled < fulls
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-window [\fIWINDOW_SEL\fR] \fIOPTIONS\fR
+node [\fINODE_SEL\fR] \fICOMMANDS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -427,69 +486,89 @@ window [\fIWINDOW_SEL\fR] \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCommands\fR
 .RS 4
 .PP
-\fB\-f\fR, \fB\-\-focus\fR [\fIWINDOW_SEL\fR]
+\fB\-f\fR, \fB\-\-focus\fR [\fINODE_SEL\fR]
 .RS 4
-Focus the selected or given window\&.
+Focus the selected or given node\&.
 .RE
 .PP
-\fB\-a\fR, \fB\-\-activate\fR [\fIWINDOW_SEL\fR]
+\fB\-a\fR, \fB\-\-activate\fR [\fINODE_SEL\fR]
 .RS 4
-Activate the selected or given window\&.
+Activate the selected or given node\&.
 .RE
 .PP
 \fB\-d\fR, \fB\-\-to\-desktop\fR \fIDESKTOP_SEL\fR
 .RS 4
-Send the selected window to the given desktop\&.
+Send the selected node to the given desktop\&.
 .RE
 .PP
 \fB\-m\fR, \fB\-\-to\-monitor\fR \fIMONITOR_SEL\fR
 .RS 4
-Send the selected window to the given monitor\&.
+Send the selected node to the given monitor\&.
+.RE
+.PP
+\fB\-n\fR, \fB\-\-to\-node\fR \fINODE_SEL\fR
+.RS 4
+Transplant the selected node to the given node\&.
 .RE
 .PP
-\fB\-w\fR, \fB\-\-to\-window\fR \fIWINDOW_SEL\fR
+\fB\-s\fR, \fB\-\-swap\fR \fINODE_SEL\fR
 .RS 4
-Transplant the selected window to the given window\&.
+Swap the selected node with the given node\&.
 .RE
 .PP
-\fB\-s\fR, \fB\-\-swap\fR \fIWINDOW_SEL\fR
+\fB\-p\fR, \fB\-\-presel\-dir\fR \fIDIR\fR|cancel
 .RS 4
-Swap the selected window with the given window\&.
+Preselect the splitting area of the selected node (or cancel the preselection)\&.
 .RE
 .PP
-\fB\-p\fR, \fB\-\-presel\fR \fIDIR\fR|cancel
+\fB\-o\fR, \fB\-\-presel\-ratio\fR \fIRATIO\fR
 .RS 4
-Preselect the splitting area of the selected window (or cancel the preselection)\&.
+Set the splitting ratio of the preselection area\&.
 .RE
 .PP
-\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR
+\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR|\(+-\fIPIXELS\fR
 .RS 4
-Set the splitting ratio of the selected window (0 <
+Set the splitting ratio of the selected node (0 <
 \fIRATIO\fR
 < 1)\&.
 .RE
 .PP
-\fB\-e\fR, \fB\-\-edge\fR \fIDIR\fR \fIRATIO\fR|\(+-\fIPIXELS\fR
+\fB\-R\fR, \fB\-\-rotate\fR \fI90|270|180\fR
+.RS 4
+Rotate the tree rooted at the selected node\&.
+.RE
+.PP
+\fB\-F\fR, \fB\-\-flip\fR \fIhorizontal|vertical\fR
 .RS 4
-Set or change the splitting ratio of the edge located in the given direction in relation to the selected window\&.
+Flip the the tree rooted at selected node\&.
 .RE
 .PP
-\fB\-R\fR, \fB\-\-rotate\fR \fIDIR\fR \fI90|270|180\fR
+\fB\-E\fR, \fB\-\-equalize\fR
+.RS 4
+Reset the split ratios of the tree rooted at the selected node to their default value\&.
+.RE
+.PP
+\fB\-B\fR, \fB\-\-balance\fR
+.RS 4
+Adjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area\&.
+.RE
+.PP
+\fB\-C\fR, \fB\-\-circulate\fR forward|backward
 .RS 4
-Rotate the tree holding the edge located in the given direction in relation to the selected window\&.
+Circulate the windows of the tree rooted at the selected node\&.
 .RE
 .PP
-\fB\-t\fR, \fB\-\-state\fR tiled|pseudo_tiled|floating|fullscreen
+\fB\-t\fR, \fB\-\-state\fR [~](tiled|pseudo_tiled|floating|fullscreen)
 .RS 4
 Set the state of the selected window\&.
 .RE
 .PP
 \fB\-g\fR, \fB\-\-flag\fR locked|sticky|private[=on|off]
 .RS 4
-Set or toggle the given flag for the selected window\&.
+Set or toggle the given flag for the selected node\&.
 .RE
 .PP
 \fB\-l\fR, \fB\-\-layer\fR below|normal|above
@@ -499,12 +578,12 @@ Set the stacking layer of the selected window\&.
 .PP
 \fB\-c\fR, \fB\-\-close\fR
 .RS 4
-Close the selected window\&.
+Close the windows rooted at the selected node\&.
 .RE
 .PP
 \fB\-k\fR, \fB\-\-kill\fR
 .RS 4
-Kill the selected window\&.
+Kill the windows rooted at the selected node\&.
 .RE
 .RE
 .SS "Desktop"
@@ -517,7 +596,7 @@ Kill the selected window\&.
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-desktop [\fIDESKTOP_SEL\fR] \fIOPTIONS\fR
+desktop [\fIDESKTOP_SEL\fR] \fICOMMANDS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -525,7 +604,7 @@ desktop [\fIDESKTOP_SEL\fR] \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCOMMANDS\fR
 .RS 4
 .PP
 \fB\-f\fR, \fB\-\-focus\fR [\fIDESKTOP_SEL\fR]
@@ -533,6 +612,11 @@ desktop [\fIDESKTOP_SEL\fR] \fIOPTIONS\fR
 Focus the selected or given desktop\&.
 .RE
 .PP
+\fB\-a\fR, \fB\-\-activate\fR [\fIDESKTOP_SEL\fR]
+.RS 4
+Activate the selected or given desktop\&.
+.RE
+.PP
 \fB\-m\fR, \fB\-\-to\-monitor\fR \fIMONITOR_SEL\fR
 .RS 4
 Send the selected desktop to the given monitor\&.
@@ -562,36 +646,6 @@ Bubble the selected desktop in the given direction\&.
 .RS 4
 Remove the selected desktop\&.
 .RE
-.PP
-\fB\-c\fR, \fB\-\-cancel\-presel\fR
-.RS 4
-Cancel the preselection of all the windows of the selected desktop\&.
-.RE
-.PP
-\fB\-F\fR, \fB\-\-flip\fR \fIhorizontal|vertical\fR
-.RS 4
-Flip the tree of the selected desktop\&.
-.RE
-.PP
-\fB\-R\fR, \fB\-\-rotate\fR \fI90|270|180\fR
-.RS 4
-Rotate the tree of the selected desktop\&.
-.RE
-.PP
-\fB\-E\fR, \fB\-\-equalize\fR
-.RS 4
-Reset the split ratios of the tree of the selected desktop\&.
-.RE
-.PP
-\fB\-B\fR, \fB\-\-balance\fR
-.RS 4
-Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area\&.
-.RE
-.PP
-\fB\-C\fR, \fB\-\-circulate\fR forward|backward
-.RS 4
-Circulate the leaves of the tree of the selected desktop\&.
-.RE
 .RE
 .SS "Monitor"
 .sp
@@ -603,7 +657,7 @@ Circulate the leaves of the tree of the selected desktop\&.
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-monitor [\fIMONITOR_SEL\fR] \fIOPTIONS\fR
+monitor [\fIMONITOR_SEL\fR] \fICOMMANDS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -611,7 +665,7 @@ monitor [\fIMONITOR_SEL\fR] \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCommands\fR
 .RS 4
 .PP
 \fB\-f\fR, \fB\-\-focus\fR [\fIMONITOR_SEL\fR]
@@ -659,7 +713,7 @@ Swap the selected monitor with the given monitor\&.
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-query \fIOPTIONS\fR
+query \fICOMMANDS\fR [\fIOPTIONS\fR]
 .RE
 .sp
 .it 1 an-trap
@@ -667,12 +721,12 @@ query \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCommands\fR
 .RS 4
 .PP
-\fB\-W\fR, \fB\-\-windows\fR
+\fB\-N\fR, \fB\-\-nodes\fR
 .RS 4
-List the IDs of the matching windows\&.
+List the IDs of the matching nodes\&.
 .RE
 .PP
 \fB\-D\fR, \fB\-\-desktops\fR
@@ -689,33 +743,6 @@ List the names of the matching monitors\&.
 .RS 4
 Print a JSON representation of the matching item\&.
 .RE
-.PP
-\fB\-H\fR, \fB\-\-history\fR
-.RS 4
-Print the focus history as it relates to the query\&.
-.RE
-.PP
-\fB\-S\fR, \fB\-\-stack\fR
-.RS 4
-Print the window stacking order\&.
-.RE
-.PP
-[\fB\-m\fR,\fB\-\-monitor\fR [\fIMONITOR_SEL\fR]] | [\fB\-d\fR,\fB\-\-desktop\fR [\fIDESKTOP_SEL\fR]] | [\fB\-w\fR, \fB\-\-window\fR [\fIWINDOW_SEL\fR]]
-.RS 4
-Constrain matches to the selected monitor, desktop or window\&.
-.RE
-.RE
-.SS "Restore"
-.sp
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBGeneral Syntax\fR
-.RS 4
-.sp
-restore \fIOPTIONS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -726,22 +753,16 @@ restore \fIOPTIONS\fR
 \fBOptions\fR
 .RS 4
 .PP
-\fB\-T\fR, \fB\-\-tree\fR <file_path>
+[\fB\-m\fR,\fB\-\-monitor\fR [\fIMONITOR_SEL\fR]] | [\fB\-d\fR,\fB\-\-desktop\fR [\fIDESKTOP_SEL\fR]] | [\fB\-n\fR, \fB\-\-node\fR [\fINODE_SEL\fR]]
 .RS 4
-Load the desktop trees from the given file\&.
-.RE
-.PP
-\fB\-H\fR, \fB\-\-history\fR <file_path>
-.RS 4
-Load the focus history from the given file\&.
-.RE
-.PP
-\fB\-S\fR, \fB\-\-stack\fR <file_path>
-.RS 4
-Load the window stacking order from the given file\&.
+Constrain matches to the selected monitor, desktop or node\&. The descriptor can be omitted for
+\fI\-M\fR,
+\fI\-D\fR
+and
+\fI\-N\fR\&.
 .RE
 .RE
-.SS "Control"
+.SS "Wm"
 .sp
 .it 1 an-trap
 .nr an-no-space-flag 1
@@ -751,7 +772,7 @@ Load the window stacking order from the given file\&.
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-control \fIOPTIONS\fR
+wm \fICOMMANDS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -759,32 +780,40 @@ control \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCommands\fR
 .RS 4
 .PP
-\fB\-\-adopt\-orphans\fR
+\fB\-d\fR, \fB\-\-dump\-state\fR
 .RS 4
-Manage all the unmanaged windows remaining from a previous session\&.
+Dump the current world state on standard output\&.
 .RE
 .PP
-\fB\-\-toggle\-visibility\fR
+\fB\-l\fR, \fB\-\-load\-state\fR <file_path>
 .RS 4
-Toggle the visibility of all the windows\&.
+Load a world state from the given file\&.
 .RE
 .PP
-\fB\-\-record\-history\fR on|off
+\fB\-a\fR, \fB\-\-add\-monitor\fR <name> WxH+X+Y
 .RS 4
-Enable or disable the recording of window focus history\&.
+Add a monitor for the given name and rectangle\&.
 .RE
 .PP
-\fB\-\-subscribe\fR (all|report|monitor|desktop|window|\&...)*
+\fB\-r\fR, \fB\-\-remove\-monitor\fR <name>
 .RS 4
-Continuously print status information\&. See the
-\fBEVENTS\fR
-section for the detailed description of each event\&.
+Remove the monitor with the given name\&.
+.RE
+.PP
+\fB\-o\fR, \fB\-\-adopt\-orphans\fR
+.RS 4
+Manage all the unmanaged windows remaining from a previous session\&.
 .RE
 .PP
-\fB\-\-get\-status\fR
+\fB\-h\fR, \fB\-\-record\-history\fR on|off
+.RS 4
+Enable or disable the recording of node focus history\&.
+.RE
+.PP
+\fB\-g\fR, \fB\-\-get\-status\fR
 .RS 4
 Print the current status information\&.
 .RE
@@ -799,7 +828,7 @@ Print the current status information\&.
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-pointer \fIOPTIONS\fR
+pointer \fICOMMANDS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -807,7 +836,7 @@ pointer \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCommands\fR
 .RS 4
 .PP
 \fB\-g\fR, \fB\-\-grab\fR focus|move|resize_side|resize_corner
@@ -835,7 +864,7 @@ Terminate the current pointer action\&.
 \fBGeneral Syntax\fR
 .RS 4
 .sp
-rule \fIOPTIONS\fR
+rule \fICOMMANDS\fR
 .RE
 .sp
 .it 1 an-trap
@@ -843,20 +872,20 @@ rule \fIOPTIONS\fR
 .nr an-break-flag 1
 .br
 .ps +1
-\fBOptions\fR
+\fBCommands\fR
 .RS 4
 .PP
-\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name>|* [\fB\-o\fR|\fB\-\-one\-shot\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]
+\fB\-a\fR, \fB\-\-add\fR (<class_name>|\fB)[:(<instance_name>|\fR)] [\fB\-o\fR|\fB\-\-one\-shot\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]
 .RS 4
 Create a new rule\&.
 .RE
 .PP
-\fB\-r\fR, \fB\-\-remove\fR ^<n>|head|tail|<class_name>|<instance_name>|*\&...
+\fB\-r\fR, \fB\-\-remove\fR ^<n>|head|tail|(<class_name>|\fB)[:(<instance_name>|\fR)]\&...
 .RS 4
 Remove the given rules\&.
 .RE
 .PP
-\fB\-l\fR, \fB\-\-list\fR [<class_name>|<instance_name>|*]
+\fB\-l\fR, \fB\-\-list\fR
 .RS 4
 List the rules\&.
 .RE
@@ -871,11 +900,28 @@ List the rules\&.
 \fBGeneral Syntax\fR
 .RS 4
 .PP
-config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-w \fIWINDOW_SEL\fR] <key> [<value>]
+config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-n \fINODE_SEL\fR] <key> [<value>]
 .RS 4
 Get or set the value of <key>\&.
 .RE
 .RE
+.SS "Subscribe"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
+.RS 4
+.PP
+subscribe (all|report|monitor|desktop|window|\&...)*
+.RS 4
+Continuously print status information\&. See the
+\fBEVENTS\fR
+section for the detailed description of each event\&.
+.RE
+.RE
 .SS "Quit"
 .sp
 .it 1 an-trap
@@ -911,14 +957,14 @@ Unknown command\&.
 .RE
 .SH "SETTINGS"
 .sp
-Colors are either \fI#RRGGBB\fR or X color names, booleans are \fItrue\fR, \fIon\fR, \fIfalse\fR or \fIoff\fR\&.
+Colors are in the form \fI#RRGGBB\fR, booleans are \fItrue\fR, \fIon\fR, \fIfalse\fR or \fIoff\fR\&.
 .sp
 All the boolean settings are \fIfalse\fR by default unless stated otherwise\&.
 .SS "Global Settings"
 .PP
-\fIfocused_border_color\fR
+\fInormal_border_color\fR
 .RS 4
-Color of the border of a focused window of a focused monitor\&.
+Color of the border of an unfocused window\&.
 .RE
 .PP
 \fIactive_border_color\fR
@@ -926,66 +972,16 @@ Color of the border of a focused window of a focused monitor\&.
 Color of the border of a focused window of an unfocused monitor\&.
 .RE
 .PP
-\fInormal_border_color\fR
+\fIfocused_border_color\fR
 .RS 4
-Color of the border of an unfocused window\&.
+Color of the border of a focused window of a focused monitor\&.
 .RE
 .PP
-\fIpresel_border_color\fR
+\fIpresel_feedback_color\fR
 .RS 4
 Color of the
-\fBwindow \-\-presel\fR
-message feedback\&.
-.RE
-.PP
-\fIfocused_locked_border_color\fR
-.RS 4
-Color of the border of a focused locked window of a focused monitor\&.
-.RE
-.PP
-\fIactive_locked_border_color\fR
-.RS 4
-Color of the border of a focused locked window of an unfocused monitor\&.
-.RE
-.PP
-\fInormal_locked_border_color\fR
-.RS 4
-Color of the border of an unfocused locked window\&.
-.RE
-.PP
-\fIfocused_sticky_border_color\fR
-.RS 4
-Color of the border of a focused sticky window of a focused monitor\&.
-.RE
-.PP
-\fIactive_sticky_border_color\fR
-.RS 4
-Color of the border of a focused sticky window of an unfocused monitor\&.
-.RE
-.PP
-\fInormal_sticky_border_color\fR
-.RS 4
-Color of the border of an unfocused sticky window\&.
-.RE
-.PP
-\fIfocused_private_border_color\fR
-.RS 4
-Color of the border of a focused private window of a focused monitor\&.
-.RE
-.PP
-\fIactive_private_border_color\fR
-.RS 4
-Color of the border of a focused private window of an unfocused monitor\&.
-.RE
-.PP
-\fInormal_private_border_color\fR
-.RS 4
-Color of the border of an unfocused private window\&.
-.RE
-.PP
-\fIurgent_border_color\fR
-.RS 4
-Color of the border of an urgent window\&.
+\fBnode \-\-presel\-{dir,ratio}\fR
+message feedback area\&.
 .RE
 .PP
 \fIsplit_ratio\fR
@@ -1038,7 +1034,7 @@ Remove gaps of tiled windows for the
 desktop layout\&.
 .RE
 .PP
-\fIleaf_monocle\fR
+\fIsingle_monocle\fR
 .RS 4
 Set the desktop layout to
 \fBmonocle\fR
@@ -1165,57 +1161,72 @@ A desktop is transferred\&.
 A desktop is focused\&.
 .RE
 .PP
+\fIdesktop_activate <monitor_name> <desktop_name>\fR
+.RS 4
+A desktop is activated\&.
+.RE
+.PP
 \fIdesktop_layout <monitor_name> <desktop_name> tiled|monocle\fR
 .RS 4
 The layout of a desktop changed\&.
 .RE
 .PP
-\fIwindow_manage <monitor_name> <desktop_name> <window_id> <ip_id>\fR
+\fInode_manage <monitor_name> <desktop_name> <node_id> <ip_id>\fR
 .RS 4
 A window is managed\&.
 .RE
 .PP
-\fIwindow_unmanage <monitor_name> <desktop_name> <window_id>\fR
+\fInode_unmanage <monitor_name> <desktop_name> <node_id>\fR
 .RS 4
 A window is unmanaged\&.
 .RE
 .PP
-\fIwindow_swap <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>\fR
+\fInode_swap <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>\fR
 .RS 4
-A window is swapped\&.
+A node is swapped\&.
 .RE
 .PP
-\fIwindow_transfer <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>\fR
+\fInode_transfer <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>\fR
 .RS 4
-A window is transferred\&.
+A node is transferred\&.
 .RE
 .PP
-\fIwindow_focus <monitor_name> <desktop_name> <window_id>\fR
+\fInode_focus <monitor_name> <desktop_name> <node_id>\fR
 .RS 4
-A window is focused\&.
+A node is focused\&.
 .RE
 .PP
-\fIwindow_activate <monitor_name> <desktop_name> <window_id>\fR
+\fInode_activate <monitor_name> <desktop_name> <node_id>\fR
 .RS 4
-A window is activated\&.
+A node is activated\&.
 .RE
 .PP
-\fIwindow_geometry <monitor_name> <desktop_name> <window_id> <window_geometry>\fR
+\fInode_presel <monitor_name> <desktop_name> <node_id> (dir DIR|ratio RATIO|cancel)\fR
+.RS 4
+A node is preselected\&.
+.RE
+.PP
+\fInode_stack <node_id_1> below|above <node_id_2>\fR
+.RS 4
+A node is stacked below or above another node\&.
+.RE
+.PP
+\fInode_geometry <monitor_name> <desktop_name> <node_id> <node_geometry>\fR
 .RS 4
 The geometry of a window changed\&.
 .RE
 .PP
-\fIwindow_state <monitor_name> <desktop_name> <window_id> tiled|pseudo_tiled|floating|fullscreen on|off\fR
+\fInode_state <monitor_name> <desktop_name> <node_id> tiled|pseudo_tiled|floating|fullscreen on|off\fR
 .RS 4
 The state of a window changed\&.
 .RE
 .PP
-\fIwindow_flag <monitor_name> <desktop_name> <window_id> sticky|private|locked|urgent on|off\fR
+\fInode_flag <monitor_name> <desktop_name> <node_id> sticky|private|locked|urgent on|off\fR
 .RS 4
-One of the flags of a window changed\&.
+One of the flags of a node changed\&.
 .RE
 .PP
-\fIwindow_layer <monitor_name> <desktop_name> <window_id> below|normal|above\fR
+\fInode_layer <monitor_name> <desktop_name> <node_id> below|normal|above\fR
 .RS 4
 The layer of a window changed\&.
 .RE
@@ -1271,6 +1282,16 @@ Urgent unfocused desktop\&.
 .RS 4
 Layout of the focused desktop of a monitor\&.
 .RE
+.PP
+\fIT(T|P|F|=|@)\fR
+.RS 4
+State of the focused node of a focused desktop\&.
+.RE
+.PP
+\fIG(S?P?L?)\fR
+.RS 4
+Active flags of the focused node of a focused desktop\&.
+.RE
 .SH "ENVIRONMENT VARIABLES"
 .PP
 \fIBSPWM_SOCKET\fR
index d063887f59c43f5ec6569b1d66fd0e263d99916a..435a327b331e51563cea0331e30ea8d1ec2ff06b 100644 (file)
@@ -15,7 +15,7 @@ Synopsis
 
 *bspwm* [*-h*|*-v*|*-c* 'CONFIG_PATH']
 
-*bspc* 'COMMAND' ['ARGUMENTS']
+*bspc* 'DOMAIN' ['SELECTOR'] 'COMMANDS'
 
 Description
 -----------
@@ -41,40 +41,53 @@ Common Definitions
 ------------------
 
 ----
-DIR         := left | right | up | down
+DIR         := north | west | south | east
 CYCLE_DIR   := next | prev
 ----
 
 Selectors
 ---------
 
-Selectors are used to select a target window, desktop, or monitor. A selector
+Selectors are used to select a target node, desktop, or monitor. A selector
 can either describe the target relatively or name it globally.
 
-Descriptive (relative) selectors consist of a primary selector and any number
-of non-conflicting modifiers as follows:
+Selectors consist of a descriptor and any number of non-conflicting modifiers
+as follows:
 
-       PRIMARY_SELECTOR[.MODIFIER]*
-
-For obvious reasons, neither desktop nor monitor names may be valid descriptive
-selectors.
+       DESCRIPTOR(.MODIFIER)*
 
 An exclamation mark can be prepended to certain modifiers in order to reverse their meaning.
 
-Window
-~~~~~~
+Node
+~~~~
 
-Select a window.
+Select a node.
 
 ----
-WINDOW_SEL := (<window_id>|DIR|CYCLE_DIR|biggest|last|focused|older|newer)[.[!]automatic][.[!](tiled|pseudo_tiled|floating|fullscreen)][.[!](below|normal|above)][.[!]local][.[!]same_class][.[!]focused][.[!](urgent|sticky|private|locked)]
+NODE_SEL := (<node_id>|PATH|DIR|CYCLE_DIR|last|older|newer|biggest|focused)[.[!]focused][.[!]automatic][.[!]local][.[!]leaf][.[!]STATE][.[!]FLAG][.[!]LAYER][.[!]same_class]
+
+STATE := tiled|pseudo_tiled|floating|fullscreen
+
+FLAG := urgent|sticky|private|locked
+
+LAYER := below|normal|above
+
+PATH := @[DESK_SEL:][[/]JUMP](/JUMP)*
+
+JUMP := first|1|second|2|brother|parent|DIR
 ----
 
-Primary Selectors
-^^^^^^^^^^^^^^^^^
+Descriptors
+^^^^^^^^^^^
+
+<node_id>::
+       Selects the node with the given ID.
+
+'PATH'::
+       Selects the node at the given path.
 
 'DIR'::
-       Selects the window in the given (spacial) direction relative to the active window.
+       Selects the window in the given (spacial) direction relative to the active node.
 
 'CYCLE_DIR'::
        Selects the window in the given (cyclic) direction.
@@ -83,35 +96,58 @@ biggest::
        Selects the biggest window on the current desktop.
 
 last::
-       Selects the previously focused window.
+       Selects the previously focused node.
 
 focused::
-       Selects the currently focused window.
+       Selects the currently focused node.
 
 older::
-       Selects the window older than the focused window in the history.
+       Selects the node older than the focused node in the history.
 
 newer::
-       Selects the window newer than the focused window in the history.
+       Selects the node newer than the focused node in the history.
+
+Path Jumps
+^^^^^^^^^^
+
+The initial node is the focused node (or the root if the path starts with '/') of the focused desktop (or the selected desktop if the path has a 'DESK_SEL' prefix).
+
+1|first::
+       Jumps to the first child.
+
+2|second::
+       Jumps to the second child.
+
+brother::
+       Jumps to the brother node.
+
+parent::
+       Jumps to the parent node.
+
+'DIR'::
+       Jumps to the node holding the edge in the given direction.
 
 Modifiers
 ^^^^^^^^^
 
-[!](tiled|pseudo_tiled|floating|fullscreen)::
-       Only consider windows having or not having the given state.
+[!]focused::
+       Only consider focused or unfocused nodes.
 
 [!]automatic::
-       Only consider windows in automatic or manual insertion mode.
+       Only consider nodes in automatic or manual insertion mode.
 
-[!]focused::
-       Only consider focused or unfocused windows.
+[!]local::
+       Only consider nodes in or not in the current desktop.
+
+[!]leaf::
+       Only consider leaves or internal nodes.
+
+[!](tiled|pseudo_tiled|floating|fullscreen)::
+       Only consider windows in or not in the given state.
 
 [!]same_class::
        Only consider windows that have or don't have the same class as the current window.
 
-[!]local::
-       Only consider windows in or not in the current desktop.
-
 [!](private|urgent|sticky|locked)::
        Only consider windows that have or don't have the given flag set.
 
@@ -127,8 +163,8 @@ Select a desktop.
 DESKTOP_SEL := (<desktop_name>|[MONITOR_SEL:](focused|^<n>)CYCLE_DIR|last|older|newer)[.[!]occupied][.[!]focused][.[!]urgent][.[!]local]
 ----
 
-Primary Selectors
-^^^^^^^^^^^^^^^^^
+Descriptors
+^^^^^^^^^^^
 
 <desktop_name>::
        Selects the desktop with the given name.
@@ -175,8 +211,8 @@ Select a monitor.
 MONITOR_SEL := (<monitor_name>|^<n>|DIR|CYCLE_DIR|last|primary|focused|older|newer)[.[!]occupied][.[!]focused]
 ----
 
-Primary Selectors
-^^^^^^^^^^^^^^^^^
+Descriptors
+^^^^^^^^^^^
 
 <monitor_name>::
        Selects the monitor with the given name.
@@ -231,11 +267,11 @@ fullscreen::
        Fills its monitor rectangle and has no borders. It is send in the ABOVE layer by default.
 
 
-Window Flags
--------------
+Node Flags
+----------
 
 locked::
-       Ignores the *window --close* message.
+       Ignores the *node --close* message.
 
 sticky::
        Stays in the focused desktop of its monitor.
@@ -255,63 +291,75 @@ There's three stacking layers: BELOW, NORMAL and ABOVE.
 In each layer, the window are orderered as follow: tiled & pseudo-tiled < fullscreen < floating.
 
 
-Commands
---------
+Domains
+-------
 
-Window
-~~~~~~
+Node
+~~~~
 
 General Syntax
 ^^^^^^^^^^^^^^
 
-window ['WINDOW_SEL'] 'OPTIONS'
+node ['NODE_SEL'] 'COMMANDS'
 
-Options
-^^^^^^^
-*-f*, *--focus* ['WINDOW_SEL']::
-       Focus the selected or given window.
+Commands
+^^^^^^^^
+*-f*, *--focus* ['NODE_SEL']::
+       Focus the selected or given node.
 
-*-a*, *--activate* ['WINDOW_SEL']::
-       Activate the selected or given window.
+*-a*, *--activate* ['NODE_SEL']::
+       Activate the selected or given node.
 
 *-d*, *--to-desktop* 'DESKTOP_SEL'::
-       Send the selected window to the given desktop.
+       Send the selected node to the given desktop.
 
 *-m*, *--to-monitor* 'MONITOR_SEL'::
-       Send the selected window to the given monitor.
+       Send the selected node to the given monitor.
+
+*-n*, *--to-node* 'NODE_SEL'::
+       Transplant the selected node to the given node.
 
-*-w*, *--to-window* 'WINDOW_SEL'::
-       Transplant the selected window to the given window.
+*-s*, *--swap* 'NODE_SEL'::
+       Swap the selected node with the given node.
 
-*-s*, *--swap* 'WINDOW_SEL'::
-       Swap the selected window with the given window.
+*-p*, *--presel-dir* [~]'DIR'|cancel::
+       Preselect the splitting area of the selected node (or cancel the preselection).
 
-*-p*, *--presel* 'DIR'|cancel::
-       Preselect the splitting area of the selected window (or cancel the preselection).
+*-o*, *--presel-ratio* 'RATIO'::
+       Set the splitting ratio of the preselection area.
 
-*-r*, *--ratio* 'RATIO'::
-       Set the splitting ratio of the selected window (0 < 'RATIO' < 1).
+*-r*, *--ratio* 'RATIO'|±'PIXELS'::
+       Set the splitting ratio of the selected node (0 < 'RATIO' < 1).
 
-*-e*, *--edge* 'DIR' 'RATIO'|±'PIXELS'::
-       Set or change the splitting ratio of the edge located in the given direction in relation to the selected window.
+*-R*, *--rotate* '90|270|180'::
+       Rotate the tree rooted at the selected node.
+
+*-F*, *--flip* 'horizontal|vertical'::
+       Flip the the tree rooted at selected node.
+
+*-E*, *--equalize*::
+       Reset the split ratios of the tree rooted at the selected node to their default value.
+
+*-B*, *--balance*::
+       Adjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area.
 
-*-R*, *--rotate* 'DIR' '90|270|180'::
-       Rotate the tree holding the edge located in the given direction in relation to the selected window.
+*-C*, *--circulate* forward|backward::
+       Circulate the windows of the tree rooted at the selected node.
 
-*-t*, *--state* tiled|pseudo_tiled|floating|fullscreen::
+*-t*, *--state* [~](tiled|pseudo_tiled|floating|fullscreen)::
        Set the state of the selected window.
 
 *-g*, *--flag* locked|sticky|private[=on|off]::
-       Set or toggle the given flag for the selected window.
+       Set or toggle the given flag for the selected node.
 
 *-l*, *--layer* below|normal|above::
        Set the stacking layer of the selected window.
 
 *-c*, *--close*::
-       Close the selected window.
+       Close the windows rooted at the selected node.
 
 *-k*, *--kill*::
-       Kill the selected window.
+       Kill the windows rooted at the selected node.
 
 Desktop
 ~~~~~~~
@@ -319,13 +367,16 @@ Desktop
 General Syntax
 ^^^^^^^^^^^^^^
 
-desktop ['DESKTOP_SEL'] 'OPTIONS'
+desktop ['DESKTOP_SEL'] 'COMMANDS'
 
-Options
-^^^^^^^
+COMMANDS
+^^^^^^^^
 *-f*, *--focus* ['DESKTOP_SEL']::
        Focus the selected or given desktop.
 
+*-a*, *--activate* ['DESKTOP_SEL']::
+       Activate the selected or given desktop.
+
 *-m*, *--to-monitor* 'MONITOR_SEL'::
        Send the selected desktop to the given monitor.
 
@@ -344,35 +395,16 @@ Options
 *-r*, *--remove*::
        Remove the selected desktop.
 
-*-c*, *--cancel-presel*::
-       Cancel the preselection of all the windows of the selected desktop.
-
-*-F*, *--flip* 'horizontal|vertical'::
-       Flip the tree of the selected desktop.
-
-*-R*, *--rotate* '90|270|180'::
-       Rotate the tree of the selected desktop.
-
-*-E*, *--equalize*::
-       Reset the split ratios of the tree of the selected desktop.
-
-*-B*, *--balance*::
-       Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area.
-
-*-C*, *--circulate* forward|backward::
-       Circulate the leaves of the tree of the selected desktop.
-
-
 Monitor
 ~~~~~~~
 
 General Syntax
 ^^^^^^^^^^^^^^
 
-monitor ['MONITOR_SEL'] 'OPTIONS'
+monitor ['MONITOR_SEL'] 'COMMANDS'
 
-Options
-^^^^^^^
+Commands
+^^^^^^^^
 *-f*, *--focus* ['MONITOR_SEL']::
        Focus the selected or given monitor.
 
@@ -400,12 +432,13 @@ Query
 General Syntax
 ^^^^^^^^^^^^^^
 
-query 'OPTIONS'
+query 'COMMANDS' ['OPTIONS']
 
-Options
-^^^^^^^
-*-W*, *--windows*::
-       List the IDs of the matching windows.
+Commands
+^^^^^^^^
+
+*-N*, *--nodes*::
+       List the IDs of the matching nodes.
 
 *-D*, *--desktops*::
        List the names of the matching desktops.
@@ -416,59 +449,42 @@ Options
 *-T*, *--tree*::
        Print a JSON representation of the matching item.
 
-*-H*, *--history*::
-       Print the focus history as it relates to the query.
-
-*-S*, *--stack*::
-       Print the window stacking order.
+Options
+^^^^^^^
 
-[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-w*, *--window* ['WINDOW_SEL']]::
-       Constrain matches to the selected monitor, desktop or window.
+[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-n*, *--node* ['NODE_SEL']]::
+       Constrain matches to the selected monitor, desktop or node. The descriptor can be omitted for '-M', '-D' and '-N'.
 
-Restore
-~~~~~~~
+Wm
+~~
 
 General Syntax
 ^^^^^^^^^^^^^^
 
-restore 'OPTIONS'
-
-Options
-^^^^^^^
-
-*-T*, *--tree* <file_path>::
-       Load the desktop trees from the given file.
-
-*-H*, *--history* <file_path>::
-       Load the focus history from the given file.
+wm 'COMMANDS'
 
-*-S*, *--stack* <file_path>::
-       Load the window stacking order from the given file.
+Commands
+^^^^^^^^
 
-Control
-~~~~~~~
+*-d*, *--dump-state*::
+       Dump the current world state on standard output.
 
-General Syntax
-^^^^^^^^^^^^^^
+*-l*, *--load-state* <file_path>::
+       Load a world state from the given file.
 
-control 'OPTIONS'
+*-a*, *--add-monitor* <name> WxH+X+Y::
+       Add a monitor for the given name and rectangle.
 
-Options
-^^^^^^^
+*-r*, *--remove-monitor* <name>::
+       Remove the monitor with the given name.
 
-*--adopt-orphans*::
+*-o*, *--adopt-orphans*::
        Manage all the unmanaged windows remaining from a previous session.
 
-*--toggle-visibility*::
-       Toggle the visibility of all the windows.
+*-h*, *--record-history* on|off::
+       Enable or disable the recording of node focus history.
 
-*--record-history* on|off::
-       Enable or disable the recording of window focus history.
-
-*--subscribe* (all|report|monitor|desktop|window|...)*::
-       Continuously print status information. See the *EVENTS* section for the detailed description of each event.
-
-*--get-status*::
+*-g*, *--get-status*::
        Print the current status information.
 
 Pointer
@@ -477,10 +493,10 @@ Pointer
 General Syntax
 ^^^^^^^^^^^^^^
 
-pointer 'OPTIONS'
+pointer 'COMMANDS'
 
-Options
-^^^^^^^
+Commands
+^^^^^^^^
 
 *-g*, *--grab* focus|move|resize_side|resize_corner::
        Initiate the given pointer action.
@@ -497,18 +513,18 @@ Rule
 General Syntax
 ^^^^^^^^^^^^^^
 
-rule 'OPTIONS'
+rule 'COMMANDS'
 
-Options
-^^^^^^^
+Commands
+^^^^^^^^
 
-*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]::
+*-a*, *--add* (<class_name>|*)[:(<instance_name>|*)] [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]::
        Create a new rule.
 
-*-r*, *--remove* ^<n>|head|tail|<class_name>|<instance_name>|*...::
+*-r*, *--remove* ^<n>|head|tail|(<class_name>|*)[:(<instance_name>|*)]...::
        Remove the given rules.
 
-*-l*, *--list* [<class_name>|<instance_name>|*]::
+*-l*, *--list*::
        List the rules.
 
 Config
@@ -517,9 +533,17 @@ Config
 General Syntax
 ^^^^^^^^^^^^^^
 
-config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-w 'WINDOW_SEL'] <key> [<value>]::
+config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-n 'NODE_SEL'] <key> [<value>]::
        Get or set the value of <key>.
 
+Subscribe
+~~~~~~~~~
+
+General Syntax
+^^^^^^^^^^^^^^
+subscribe (all|report|monitor|desktop|window|...)*::
+       Continuously print status information. See the *EVENTS* section for the detailed description of each event.
+
 Quit
 ~~~~
 
@@ -544,54 +568,24 @@ If the server can't handle a message, *bspc* will return with one of the followi
 
 Settings
 --------
-Colors are either '#RRGGBB' or http://en.wikipedia.org/wiki/X11_color_names[X color names], booleans are 'true', 'on', 'false' or 'off'.
+Colors are in the form '#RRGGBB', booleans are 'true', 'on', 'false' or 'off'.
 
 All the boolean settings are 'false' by default unless stated otherwise.
 
 Global Settings
 ~~~~~~~~~~~~~~~
 
-'focused_border_color'::
-       Color of the border of a focused window of a focused monitor.
-
-'active_border_color'::
-       Color of the border of a focused window of an unfocused monitor.
-
 'normal_border_color'::
        Color of the border of an unfocused window.
 
-'presel_border_color'::
-       Color of the *window --presel* message feedback.
-
-'focused_locked_border_color'::
-       Color of the border of a focused locked window of a focused monitor.
-
-'active_locked_border_color'::
-       Color of the border of a focused locked window of an unfocused monitor.
-
-'normal_locked_border_color'::
-       Color of the border of an unfocused locked window.
-
-'focused_sticky_border_color'::
-       Color of the border of a focused sticky window of a focused monitor.
-
-'active_sticky_border_color'::
-       Color of the border of a focused sticky window of an unfocused monitor.
-
-'normal_sticky_border_color'::
-       Color of the border of an unfocused sticky window.
-
-'focused_private_border_color'::
-       Color of the border of a focused private window of a focused monitor.
-
-'active_private_border_color'::
-       Color of the border of a focused private window of an unfocused monitor.
+'active_border_color'::
+       Color of the border of a focused window of an unfocused monitor.
 
-'normal_private_border_color'::
-       Color of the border of an unfocused private window.
+'focused_border_color'::
+       Color of the border of a focused window of a focused monitor.
 
-'urgent_border_color'::
-       Color of the border of an urgent window.
+'presel_feedback_color'::
+       Color of the *node --presel-{dir,ratio}* message feedback area.
 
 'split_ratio'::
        Default split ratio.
@@ -617,7 +611,7 @@ Global Settings
 'gapless_monocle'::
        Remove gaps of tiled windows for the *monocle* desktop layout.
 
-'leaf_monocle'::
+'single_monocle'::
        Set the desktop layout to *monocle* if there's only one tiled window in the tree.
 
 'focus_follows_pointer'::
@@ -705,37 +699,46 @@ Events
 'desktop_focus <monitor_name> <desktop_name>'::
        A desktop is focused.
 
+'desktop_activate <monitor_name> <desktop_name>'::
+       A desktop is activated.
+
 'desktop_layout <monitor_name> <desktop_name> tiled|monocle'::
        The layout of a desktop changed.
 
-'window_manage <monitor_name> <desktop_name> <window_id> <ip_id>'::
+'node_manage <monitor_name> <desktop_name> <node_id> <ip_id>'::
        A window is managed.
 
-'window_unmanage <monitor_name> <desktop_name> <window_id>'::
+'node_unmanage <monitor_name> <desktop_name> <node_id>'::
        A window is unmanaged.
 
-'window_swap <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>'::
-       A window is swapped.
+'node_swap <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>'::
+       A node is swapped.
+
+'node_transfer <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>'::
+       A node is transferred.
 
-'window_transfer <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>'::
-       A window is transferred.
+'node_focus <monitor_name> <desktop_name> <node_id>'::
+       A node is focused.
 
-'window_focus <monitor_name> <desktop_name> <window_id>'::
-       A window is focused.
+'node_activate <monitor_name> <desktop_name> <node_id>'::
+       A node is activated.
 
-'window_activate <monitor_name> <desktop_name> <window_id>'::
-       A window is activated.
+'node_presel <monitor_name> <desktop_name> <node_id> (dir DIR|ratio RATIO|cancel)'::
+       A node is preselected.
 
-'window_geometry <monitor_name> <desktop_name> <window_id> <window_geometry>'::
+'node_stack <node_id_1> below|above <node_id_2>'::
+       A node is stacked below or above another node.
+
+'node_geometry <monitor_name> <desktop_name> <node_id> <node_geometry>'::
        The geometry of a window changed.
 
-'window_state <monitor_name> <desktop_name> <window_id> tiled|pseudo_tiled|floating|fullscreen on|off'::
+'node_state <monitor_name> <desktop_name> <node_id> tiled|pseudo_tiled|floating|fullscreen on|off'::
        The state of a window changed.
 
-'window_flag <monitor_name> <desktop_name> <window_id> sticky|private|locked|urgent on|off'::
-       One of the flags of a window changed.
+'node_flag <monitor_name> <desktop_name> <node_id> sticky|private|locked|urgent on|off'::
+       One of the flags of a node changed.
 
-'window_layer <monitor_name> <desktop_name> <window_id> below|normal|above'::
+'node_layer <monitor_name> <desktop_name> <node_id> below|normal|above'::
        The layer of a window changed.
 
 Please note that *bspwm* initializes monitors before it reads messages on its socket, therefore the initial monitor events can't be received.
@@ -774,6 +777,12 @@ Each item has the form '<type><value>' where '<type>' is the first character of
 'L(T|M)'::
        Layout of the focused desktop of a monitor.
 
+'T(T|P|F|=|@)'::
+       State of the focused node of a focused desktop.
+
+'G(S?P?L?)'::
+       Active flags of the focused node of a focused desktop.
+
 Environment Variables
 ---------------------
 
index 90e48d345087b2f54c7bdf0ed9f261e7e7cbef20..272e89463f493c5837bf2a24e20987710119b19f 100644 (file)
--- a/events.c
+++ b/events.c
@@ -23,6 +23,7 @@
  */
 
 #include <stdlib.h>
+#include <stdbool.h>
 #include "bspwm.h"
 #include "ewmh.h"
 #include "monitor.h"
@@ -68,8 +69,9 @@ void handle_event(xcb_generic_event_t *evt)
                        process_error(evt);
                        break;
                default:
-                       if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
+                       if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
                                update_monitors();
+                       }
                        break;
        }
 }
@@ -91,14 +93,18 @@ void configure_request(xcb_generic_event_t *evt)
        int w = 0, h = 0;
 
        if (is_managed && !IS_FLOATING(c)) {
-               if (e->value_mask & XCB_CONFIG_WINDOW_X)
+               if (e->value_mask & XCB_CONFIG_WINDOW_X) {
                        c->floating_rectangle.x = e->x;
-               if (e->value_mask & XCB_CONFIG_WINDOW_Y)
+               }
+               if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
                        c->floating_rectangle.y = e->y;
-               if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
+               }
+               if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
                        w = e->width;
-               if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
+               }
+               if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
                        h = e->height;
+               }
 
                if (w != 0) {
                        restrain_floating_width(c, &w);
@@ -111,14 +117,13 @@ void configure_request(xcb_generic_event_t *evt)
                }
 
                xcb_configure_notify_event_t evt;
-               xcb_window_t win = c->window;
                unsigned int bw = c->border_width;
 
-               xcb_rectangle_t rect = get_rectangle(loc.monitor, c);
+               xcb_rectangle_t rect = get_rectangle(loc.monitor, loc.desktop, loc.node);
 
                evt.response_type = XCB_CONFIGURE_NOTIFY;
-               evt.event = win;
-               evt.window = win;
+               evt.event = e->window;
+               evt.window = e->window;
                evt.above_sibling = XCB_NONE;
                evt.x = rect.x;
                evt.y = rect.y;
@@ -127,7 +132,7 @@ void configure_request(xcb_generic_event_t *evt)
                evt.border_width = bw;
                evt.override_redirect = false;
 
-               xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
+               xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
 
                if (c->state == STATE_PSEUDO_TILED) {
                        arrange(loc.monitor, loc.desktop);
@@ -140,15 +145,17 @@ void configure_request(xcb_generic_event_t *evt)
                if (e->value_mask & XCB_CONFIG_WINDOW_X) {
                        mask |= XCB_CONFIG_WINDOW_X;
                        values[i++] = e->x;
-                       if (is_managed)
+                       if (is_managed) {
                                c->floating_rectangle.x = e->x;
+                       }
                }
 
                if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
                        mask |= XCB_CONFIG_WINDOW_Y;
                        values[i++] = e->y;
-                       if (is_managed)
+                       if (is_managed) {
                                c->floating_rectangle.y = e->y;
+                       }
                }
 
                if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
@@ -176,26 +183,27 @@ void configure_request(xcb_generic_event_t *evt)
                        values[i++] = e->border_width;
                }
 
-               if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
+               if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
                        mask |= XCB_CONFIG_WINDOW_SIBLING;
                        values[i++] = e->sibling;
                }
 
-               if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
+               if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
                        mask |= XCB_CONFIG_WINDOW_STACK_MODE;
                        values[i++] = e->stack_mode;
                }
 
                xcb_configure_window(dpy, e->window, mask, values);
 
-               if (is_managed && (mask & XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT)) {
+               if (is_managed && mask & XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT) {
                        xcb_rectangle_t r = c->floating_rectangle;
-                       put_status(SBSC_MASK_WINDOW_GEOMETRY, "window_geometry %s %s 0x%X %ux%u+%i+%i\n", loc.monitor->name, loc.desktop->name, c->window, r.width, r.height, r.x, r.y);
+                       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry %s %s 0x%X %ux%u+%i+%i\n", loc.monitor->name, loc.desktop->name, e->window, r.width, r.height, r.x, r.y);
                }
        }
 
        if (is_managed) {
-               translate_client(monitor_from_client(c), loc.monitor, c);
+               monitor_t *m = monitor_from_client(c);
+               adapt_geometry(&m->rectangle, &loc.monitor->rectangle, loc.node);
        }
 }
 
@@ -222,14 +230,15 @@ void property_notify(xcb_generic_event_t *evt)
        }
 
        coordinates_t loc;
-       if (!locate_window(e->window, &loc))
+       if (!locate_window(e->window, &loc)) {
                        return;
+       }
 
        if (e->atom == XCB_ATOM_WM_HINTS) {
                xcb_icccm_wm_hints_t hints;
                if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
                    (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
-                       set_urgency(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
+                       set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
        } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
                client_t *c = loc.node->client;
                xcb_size_hints_t size_hints;
@@ -255,27 +264,31 @@ void client_message(xcb_generic_event_t *evt)
 
        if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
                coordinates_t loc;
-               if (ewmh_locate_desktop(e->data.data32[0], &loc))
+               if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
                        focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
+               }
                return;
        }
 
        coordinates_t loc;
-       if (!locate_window(e->window, &loc))
+       if (!locate_window(e->window, &loc)) {
                return;
+       }
 
        if (e->type == ewmh->_NET_WM_STATE) {
                handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
                handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
        } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
                if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
-                   loc.node == mon->desk->focus)
+                   loc.node == mon->desk->focus) {
                        return;
+               }
                focus_node(loc.monitor, loc.desktop, loc.node);
        } else if (e->type == ewmh->_NET_WM_DESKTOP) {
                coordinates_t dloc;
-               if (ewmh_locate_desktop(e->data.data32[0], &dloc))
+               if (ewmh_locate_desktop(e->data.data32[0], &dloc)) {
                        transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
+               }
        } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
                window_close(loc.node);
        }
@@ -291,7 +304,7 @@ void focus_in(xcb_generic_event_t *evt)
                return;
        }
 
-       if (mon->desk->focus != NULL && e->event == mon->desk->focus->client->window) {
+       if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) {
                return;
        }
 
@@ -309,7 +322,7 @@ void enter_notify(xcb_generic_event_t *evt)
 
        if (e->mode != XCB_NOTIFY_MODE_NORMAL ||
            (mon->desk->focus != NULL &&
-            mon->desk->focus->client->window == win)) {
+            mon->desk->focus->id == win)) {
                return;
        }
 
@@ -409,15 +422,15 @@ void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsig
                } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
                        set_sticky(m, d, n, false);
                } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_sticky(m, d, n, !n->client->sticky);
+                       set_sticky(m, d, n, !n->sticky);
                }
        } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
                if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_urgency(m, d, n, true);
+                       set_urgent(m, d, n, true);
                } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       set_urgency(m, d, n, false);
+                       set_urgent(m, d, n, false);
                } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_urgency(m, d, n, !n->client->urgent);
+                       set_urgent(m, d, n, !n->client->urgent);
                }
        }
 }
diff --git a/ewmh.c b/ewmh.c
index 81d11c234acc79b2b735e28ec0bd5d43e289c54c..6e2d23c5d3b401b53bf055576dcefb56b6137f79 100644 (file)
--- a/ewmh.c
+++ b/ewmh.c
@@ -22,6 +22,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdlib.h>
+#include <sys/types.h>
 #include <string.h>
 #include <unistd.h>
 #include "bspwm.h"
 void ewmh_init(void)
 {
        ewmh = malloc(sizeof(xcb_ewmh_connection_t));
-       if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0)
+       if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
                err("Can't initialize EWMH atoms.\n");
+       }
 }
 
 void ewmh_update_active_window(void)
 {
-       xcb_window_t win = (mon->desk->focus == NULL ? XCB_NONE : mon->desk->focus->client->window);
+       xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
        xcb_ewmh_set_active_window(ewmh, default_screen, win);
 }
 
 void ewmh_update_number_of_desktops(void)
 {
-       uint32_t num_desktops = 0;
+       uint32_t desktops_count = 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++;
+                       desktops_count++;
                }
        }
 
-       xcb_ewmh_set_number_of_desktops(ewmh, default_screen, num_desktops);
+       xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
 }
 
 uint32_t ewmh_get_desktop_index(desktop_t *d)
@@ -70,19 +73,24 @@ uint32_t ewmh_get_desktop_index(desktop_t *d)
 
 bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
                        if (i == 0) {
                                loc->monitor = m;
                                loc->desktop = d;
                                loc->node = NULL;
                                return true;
                        }
+               }
+       }
        return false;
 }
 
 void ewmh_update_current_desktop(void)
 {
+       if (mon == NULL) {
+               return;
+       }
        uint32_t i = ewmh_get_desktop_index(mon->desk);
        xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
 }
@@ -90,7 +98,9 @@ void ewmh_update_current_desktop(void)
 void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
 {
        uint32_t i = ewmh_get_desktop_index(d);
-       xcb_ewmh_set_wm_desktop(ewmh, n->client->window, i);
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               xcb_ewmh_set_wm_desktop(ewmh, f->id, i);
+       }
 }
 
 void ewmh_update_wm_desktops(void)
@@ -99,7 +109,7 @@ void ewmh_update_wm_desktops(void)
                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)) {
-                               xcb_ewmh_set_wm_desktop(ewmh, n->client->window, i);
+                               xcb_ewmh_set_wm_desktop(ewmh, n->id, i);
                        }
                }
        }
@@ -133,56 +143,70 @@ void ewmh_update_desktop_names(void)
        xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
 }
 
-void ewmh_update_client_list(void)
+void ewmh_update_client_list(bool stacking)
 {
-       if (num_clients == 0) {
+       if (clients_count == 0) {
                xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);
                xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);
                return;
        }
 
-       xcb_window_t wins[num_clients];
+       xcb_window_t wins[clients_count];
        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)) {
-                               wins[i++] = n->client->window;
+       if (stacking) {
+               for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+                       wins[i++] = s->node->id;
+               }
+               xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);
+       } else {
+               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->id;
+                               }
                        }
                }
+               xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);
        }
-
-       xcb_ewmh_set_client_list(ewmh, default_screen, num_clients, wins);
-       xcb_ewmh_set_client_list_stacking(ewmh, default_screen, num_clients, wins);
 }
 
-bool ewmh_wm_state_add(client_t *c, xcb_atom_t state)
+bool ewmh_wm_state_add(node_t *n, xcb_atom_t state)
 {
-       if (c->num_states >= MAX_STATE) {
+       client_t *c = n->client;
+
+       if (c == NULL || c->wm_states_count >= MAX_WM_STATES) {
                return false;
        }
-       for (int i = 0; i < c->num_states; i++) {
+
+       for (int i = 0; i < c->wm_states_count; 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);
+
+       c->wm_state[c->wm_states_count] = state;
+       c->wm_states_count++;
+       xcb_ewmh_set_wm_state(ewmh, n->id, c->wm_states_count, c->wm_state);
        return true;
 }
 
-bool ewmh_wm_state_remove(client_t *c, xcb_atom_t state)
+bool ewmh_wm_state_remove(node_t *n, xcb_atom_t state)
 {
-       for (int i = 0; i < c->num_states; i++)
-               if (c->wm_state[i] == state)
-               {
-                       for (int j = i; j < (c->num_states - 1); j++)
+       client_t *c = n->client;
+       if (c == NULL) {
+               return false;
+       }
+       for (int i = 0; i < c->wm_states_count; i++) {
+               if (c->wm_state[i] == state) {
+                       for (int j = i; j < (c->wm_states_count - 1); j++) {
                                c->wm_state[j] = c->wm_state[j + 1];
-                       c->num_states--;
-                       xcb_ewmh_set_wm_state(ewmh, c->window, c->num_states, c->wm_state);
+                       }
+                       c->wm_states_count--;
+                       xcb_ewmh_set_wm_state(ewmh, n->id, c->wm_states_count, c->wm_state);
                        return true;
                }
+       }
        return false;
 }
 
diff --git a/ewmh.h b/ewmh.h
index 97743eaef5aae88ed3d7bd6303f2e19ad0a725d2..579ae552630c7e9cf523ea0320e70a5469b47a8d 100644 (file)
--- a/ewmh.h
+++ b/ewmh.h
@@ -38,9 +38,9 @@ void ewmh_update_current_desktop(void);
 void ewmh_set_wm_desktop(node_t *n, desktop_t *d);
 void ewmh_update_wm_desktops(void);
 void ewmh_update_desktop_names(void);
-void ewmh_update_client_list(void);
-bool ewmh_wm_state_add(client_t *c, xcb_atom_t state);
-bool ewmh_wm_state_remove(client_t *c, xcb_atom_t state);
+void ewmh_update_client_list(bool stacking);
+bool ewmh_wm_state_add(node_t *n, xcb_atom_t state);
+bool ewmh_wm_state_remove(node_t *n, xcb_atom_t state);
 void ewmh_set_supporting(xcb_window_t win);
 
 #endif
index aeac95f00c6992e97fdae7ad7d1c7d762062097d..0151e0c1eeb907ae55d8b79c781d3096f50d5064 100755 (executable)
@@ -2,13 +2,14 @@
 
 sxhkd &
 
-bspc config border_width        2
-bspc config window_gap         12
+bspc config border_width         2
+bspc config window_gap          12
 
-bspc config split_ratio         0.52
-bspc config borderless_monocle  true
-bspc config gapless_monocle     true
-bspc config focus_by_distance   true
+bspc config split_ratio          0.52
+bspc config borderless_monocle   true
+bspc config gapless_monocle      true
+bspc config focus_by_distance    true
+bspc config history_aware_focus  true
 
 bspc monitor -d I II III IV V VI VII VIII IX X
 
index a62089c6bb40d2298d5ba2772f42702ea7c8d3e7..b8fe0e6d2ea27d67d5c886b4b974a8341e803744 100755 (executable)
@@ -1,14 +1,14 @@
 #! /bin/sh
 
-fwid=$(bspc query -W -w focused.automatic)
+fwid=$(bspc query -N -n focused.automatic)
 
 if [ -n "$fwid" ] ; then
        wattr wh $fwid | {
                read width height
                if [ $width -gt $height ] ; then
-                       echo "split_dir=left"
+                       echo "split_dir=west"
                else
-                       echo "split_dir=down"
+                       echo "split_dir=south"
                fi
                echo "split_ratio=0.5"
        }
index 7a7988f571acaf380f1b425af86090fbf5efed95..797a973d6058551c27a00b029392e5fa9f533f6e 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 
-if [ -e "$BSPWM_TREE" ] ; then
-       bspc restore -T "$BSPWM_TREE" -H "$BSPWM_HISTORY" -S "$BSPWM_STACK"
-       rm "$BSPWM_TREE" "$BSPWM_HISTORY" "$BSPWM_STACK"
+if [ -e "$BSPWM_STATE" ] ; then
+       bspc wm -l "$BSPWM_STATE"
+       rm "$BSPWM_STATE"
 fi
index c2366c233a01f074d392de5d1de50e826eb54ff6..790d65729bddb836b67ff06b62d4e37a6ebf6ab5 100644 (file)
@@ -1,3 +1 @@
-export BSPWM_TREE=/tmp/bspwm-tree.json
-export BSPWM_HISTORY=/tmp/bspwm-history.txt
-export BSPWM_STACK=/tmp/bspwm-stack.txt
+export BSPWM_STATE=/tmp/bspwm-state.json
index 6e68a298282d93397a5e6206a2bf7c869c8c02c2..40480ccab66dfff70bbe93a914a310da75c7d12d 100644 (file)
@@ -1,6 +1,4 @@
+# refresh or quit bspwm
 super + alt + {_,shift + }Escape
-       {bspc query -T > "$BSPWM_TREE"; \
-               bspc query -H > "$BSPWM_HISTORY"; \
-               bspc query -S > "$BSPWM_STACK"; \
-               bspc quit,\
+       {bspc wm -d > "$BSPWM_STATE" && bspc quit, \
         bspc quit 1}
index 7f64fe57b9994bd247ccd19935d16e69a4711a2e..0f22a8a684ac2a624c8885aeff867eedc6486173 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 
-if [ $(pgrep -cx panel) -gt 1 ] ; then
+if xdo id -a "$PANEL_WM_NAME" > /dev/null ; then
        printf "%s\n" "The panel is already running." >&2
        exit 1
 fi
@@ -11,12 +11,21 @@ trap 'trap - TERM; kill 0' INT TERM QUIT EXIT
 mkfifo "$PANEL_FIFO"
 
 bspc config top_padding $PANEL_HEIGHT
-bspc control --subscribe > "$PANEL_FIFO" &
+bspc subscribe report > "$PANEL_FIFO" &
 xtitle -sf 'T%s' > "$PANEL_FIFO" &
 clock -sf 'S%a %H:%M' > "$PANEL_FIFO" &
 
 . panel_colors
 
-cat "$PANEL_FIFO" | panel_bar | lemonbar -n "$PANEL_WM_NAME" -g x$PANEL_HEIGHT -f "$PANEL_FONT_FAMILY" -F "$COLOR_FOREGROUND" -B "$COLOR_BACKGROUND" &
+cat "$PANEL_FIFO" | panel_bar | lemonbar -n "$PANEL_WM_NAME" -g x$PANEL_HEIGHT -f "$PANEL_FONT" -F "$COLOR_DEFAULT_FG" -B "$COLOR_DEFAULT_BG" &
+
+wid=$(xdo id -a "$PANEL_WM_NAME")
+tries_left=20
+while [ -z "$wid" -a "$tries_left" -gt 0 ] ; do
+       sleep 0.05
+       wid=$(xdo id -a "$PANEL_WM_NAME")
+       tries_left=$((tries_left - 1))
+done
+[ -n "$wid" ] && xdo above -t "$(xdo id -N Bspwm -n root | sort | head -n 1)" "$wid"
 
 wait
index 026ba788b2b70147078c837485a7afe8ae952796..8b50374e791b5450ebd72c8d547ce3ff94c07308 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 #
-# Example panel for LemonBoy's bar
+# Example panel for lemonbar
 
 . panel_colors
 
@@ -10,65 +10,80 @@ while read -r line ; do
        case $line in
                S*)
                        # clock output
-                       sys_infos="%{F$COLOR_STATUS_FG}%{B$COLOR_STATUS_BG} ${line#?} %{B-}%{F-}"
+                       sys="%{F$COLOR_SYS_FG}%{B$COLOR_SYS_BG} ${line#?} %{B-}%{F-}"
                        ;;
                T*)
                        # xtitle output
                        title="%{F$COLOR_TITLE_FG}%{B$COLOR_TITLE_BG} ${line#?} %{B-}%{F-}"
                        ;;
                W*)
-                       # bspwm internal state
-                       wm_infos=""
+                       # bspwm's state
+                       wm=""
                        IFS=':'
                        set -- ${line#?}
                        while [ $# -gt 0 ] ; do
                                item=$1
                                name=${item#?}
                                case $item in
-                                       M*)
-                                               # active monitor
-                                               if [ $num_mon -gt 1 ] ; then
-                                                       wm_infos="$wm_infos %{F$COLOR_ACTIVE_MONITOR_FG}%{B$COLOR_ACTIVE_MONITOR_BG} ${name} %{B-}%{F-}  "
-                                               fi
+                                       [mM]*)
+                                               [ $num_mon -lt 2 ] && shift && continue
+                                               case $item in
+                                                       m*)
+                                                               # monitor
+                                                               FG=$COLOR_MONITOR_FG
+                                                               BG=$COLOR_MONITOR_BG
+                                                               ;;
+                                                       M*)
+                                                               # focused monitor
+                                                               FG=$COLOR_FOCUSED_MONITOR_FG
+                                                               BG=$COLOR_FOCUSED_MONITOR_BG
+                                                               ;;
+                                               esac
+                                               wm="${wm}%{F${FG}}%{B${BG}} ${name} %{B-}%{F-}"
                                                ;;
-                                       m*)
-                                               # inactive monitor
-                                               if [ $num_mon -gt 1 ] ; then
-                                                       wm_infos="$wm_infos %{F$COLOR_INACTIVE_MONITOR_FG}%{B$COLOR_INACTIVE_MONITOR_BG} ${name} %{B-}%{F-}  "
-                                               fi
+                                       [fFoOuU]*)
+                                               case $item in
+                                                       f*)
+                                                               # free desktop
+                                                               FG=$COLOR_FREE_FG
+                                                               BG=$COLOR_FREE_BG
+                                                               ;;
+                                                       F*)
+                                                               # focused free desktop
+                                                               FG=$COLOR_FOCUSED_FREE_FG
+                                                               BG=$COLOR_FOCUSED_FREE_BG
+                                                               ;;
+                                                       o*)
+                                                               # occupied desktop
+                                                               FG=$COLOR_OCCUPIED_FG
+                                                               BG=$COLOR_OCCUPIED_BG
+                                                               ;;
+                                                       O*)
+                                                               # focused occupied desktop
+                                                               FG=$COLOR_FOCUSED_OCCUPIED_FG
+                                                               BG=$COLOR_FOCUSED_OCCUPIED_BG
+                                                               ;;
+                                                       u*)
+                                                               # urgent desktop
+                                                               FG=$COLOR_URGENT_FG
+                                                               BG=$COLOR_URGENT_BG
+                                                               ;;
+                                                       U*)
+                                                               # focused urgent desktop
+                                                               FG=$COLOR_FOCUSED_URGENT_FG
+                                                               BG=$COLOR_FOCUSED_URGENT_BG
+                                                               ;;
+                                               esac
+                                               wm="${wm}%{F${FG}}%{B${BG}} ${name} %{B-}%{F-}"
                                                ;;
-                                       O*)
-                                               # focused occupied desktop
-                                               wm_infos="${wm_infos}%{F$COLOR_FOCUSED_OCCUPIED_FG}%{B$COLOR_FOCUSED_OCCUPIED_BG}%{U$COLOR_FOREGROUND}%{+u} ${name} %{-u}%{B-}%{F-}"
-                                               ;;
-                                       F*)
-                                               # focused free desktop
-                                               wm_infos="${wm_infos}%{F$COLOR_FOCUSED_FREE_FG}%{B$COLOR_FOCUSED_FREE_BG}%{U$COLOR_FOREGROUND}%{+u} ${name} %{-u}%{B-}%{F-}"
-                                               ;;
-                                       U*)
-                                               # focused urgent desktop
-                                               wm_infos="${wm_infos}%{F$COLOR_FOCUSED_URGENT_FG}%{B$COLOR_FOCUSED_URGENT_BG}%{U$COLOR_FOREGROUND}%{+u} ${name} %{-u}%{B-}%{F-}"
-                                               ;;
-                                       o*)
-                                               # occupied desktop
-                                               wm_infos="${wm_infos}%{F$COLOR_OCCUPIED_FG}%{B$COLOR_OCCUPIED_BG} ${name} %{B-}%{F-}"
-                                               ;;
-                                       f*)
-                                               # free desktop
-                                               wm_infos="${wm_infos}%{F$COLOR_FREE_FG}%{B$COLOR_FREE_BG} ${name} %{B-}%{F-}"
-                                               ;;
-                                       u*)
-                                               # urgent desktop
-                                               wm_infos="${wm_infos}%{F$COLOR_URGENT_FG}%{B$COLOR_URGENT_BG} ${name} %{B-}%{F-}"
-                                               ;;
-                                       L*)
-                                               # layout
-                                               wm_infos="$wm_infos  %{F$COLOR_LAYOUT_FG}%{B$COLOR_LAYOUT_BG} ${name} %{B-}%{F-}"
+                                       [LTG]*)
+                                               # layout, state and flags
+                                               wm="${wm}%{F$COLOR_STATE_FG}%{B$COLOR_STATE_BG} ${name} %{B-}%{F-}"
                                                ;;
                                esac
                                shift
                        done
                        ;;
        esac
-       printf "%s\n" "%{l}${wm_infos}%{c}${title}%{r}${sys_infos}"
+       printf "%s\n" "%{l}${wm}%{c}${title}%{r}${sys}"
 done
index a7fad644ad86f794968dcfe01f7d2f7b42a1faa5..459703606c2453c19cd1d07f2f44532470aba2f8 100644 (file)
@@ -1,24 +1,24 @@
-COLOR_FOREGROUND='#FFA3A6AB'
-COLOR_BACKGROUND='#FF34322E'
-COLOR_ACTIVE_MONITOR_FG='#FF34322E'
-COLOR_ACTIVE_MONITOR_BG='#FF58C5F1'
-COLOR_INACTIVE_MONITOR_FG='#FF58C5F1'
-COLOR_INACTIVE_MONITOR_BG='#FF34322E'
-COLOR_FOCUSED_OCCUPIED_FG='#FFF6F9FF'
-COLOR_FOCUSED_OCCUPIED_BG='#FF5C5955'
-COLOR_FOCUSED_FREE_FG='#FFF6F9FF'
-COLOR_FOCUSED_FREE_BG='#FF6D561C'
-COLOR_FOCUSED_URGENT_FG='#FF34322E'
-COLOR_FOCUSED_URGENT_BG='#FFF9A299'
-COLOR_OCCUPIED_FG='#FFA3A6AB'
-COLOR_OCCUPIED_BG='#FF34322E'
-COLOR_FREE_FG='#FF6F7277'
-COLOR_FREE_BG='#FF34322E'
-COLOR_URGENT_FG='#FFF9A299'
-COLOR_URGENT_BG='#FF34322E'
-COLOR_LAYOUT_FG='#FFA3A6AB'
-COLOR_LAYOUT_BG='#FF34322E'
-COLOR_TITLE_FG='#FFA3A6AB'
-COLOR_TITLE_BG='#FF34322E'
-COLOR_STATUS_FG='#FFA3A6AB'
-COLOR_STATUS_BG='#FF34322E'
+COLOR_DEFAULT_FG="#a7a5a5"
+COLOR_DEFAULT_BG="#333232"
+COLOR_MONITOR_FG="#8dbcdf"
+COLOR_MONITOR_BG="#333232"
+COLOR_FOCUSED_MONITOR_FG="#b1d0e8"
+COLOR_FOCUSED_MONITOR_BG="#144b6c"
+COLOR_FREE_FG="#737171"
+COLOR_FREE_BG="#333232"
+COLOR_FOCUSED_FREE_FG="#000000"
+COLOR_FOCUSED_FREE_BG="#504e4e"
+COLOR_OCCUPIED_FG="#a7a5a5"
+COLOR_OCCUPIED_BG="#333232"
+COLOR_FOCUSED_OCCUPIED_FG="#d6d3d2"
+COLOR_FOCUSED_OCCUPIED_BG="#504e4e"
+COLOR_URGENT_FG="#f15d66"
+COLOR_URGENT_BG="#333232"
+COLOR_FOCUSED_URGENT_FG="#501d1f"
+COLOR_FOCUSED_URGENT_BG="#d5443e"
+COLOR_STATE_FG="#89b09c"
+COLOR_STATE_BG="#333232"
+COLOR_TITLE_FG="#a8a2c0"
+COLOR_TITLE_BG="#333232"
+COLOR_SYS_FG="#b1a57d"
+COLOR_SYS_BG="#333232"
index 921841d37976ac97c08cb417cce38dc18e4ca7c7..0bac490c57e917430b98571cb0ff1f5e211f4154 100644 (file)
@@ -1,5 +1,5 @@
 PANEL_FIFO=/tmp/panel-fifo
 PANEL_HEIGHT=24
-PANEL_FONT_FAMILY="-*-terminus-medium-r-normal-*-12-*-*-*-c-*-*-1"
-PANEL_WM_NAME=lemonpanel
-export PANEL_FIFO PANEL_HEIGHT PANEL_FONT_FAMILY PANEL_WM_NAME
+PANEL_FONT="-*-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
+PANEL_WM_NAME=bspwm_panel
+export PANEL_FIFO PANEL_HEIGHT PANEL_FONT PANEL_WM_NAME
index 19cd4495a9b0b9d177d03b7abd958a9f6ac7eb04..2e04d0d305832d210d5c8f8988a167a515f72c43 100644 (file)
+#
+# wm independent hotkeys
+#
+
+# terminal emulator
+super + Return
+       urxvt
+
+# program launcher
+super + space
+       dmenu_run
+
+# make sxhkd reload its configuration files:
+super + Escape
+       pkill -USR1 -x sxhkd
+
 #
 # bspwm hotkeys
 #
 
+# quit bspwm normally
 super + alt + Escape
        bspc quit
 
-super + w
-       bspc window -c
+# close and kill
+super + {_,shift + }w
+       bspc node -{c,k}
 
-super + n
+# alternate between the tiled and monocle layout
+super + m
        bspc desktop -l next
 
-super + {t,p,s,f}
-       bspc window -t {tiled,pseudo_tiled,floating,fullscreen}
+# if the current node is automatic, send it to the last manual, otherwise pull the last leaf
+super + y
+       bspc query -N -n focused.automatic && bspc node -n last.!automatic || bspc node last.leaf -n focused
 
-super + {grave,Tab}
-       bspc {window,desktop} -f last
+# swap the current node and the biggest node
+super + g
+       bspc node -s biggest
 
-super + apostrophe
-       bspc window -s last
+#
+# state/flags
+#
 
-super + {o,i}
-       bspc control --record-history off; \
-       bspc window {older,newer} -f; \
-       bspc control --record-history on
+# set the window state
+super + {t,shift + t,s,f}
+       bspc node -t {tiled,pseudo_tiled,floating,fullscreen}
 
-super + y
-       bspc window -w last.!automatic
+# set the node flags
+super + ctrl + {x,y,z}
+       bspc node -g {locked,sticky,private}
 
-super + m
-       bspc window -s biggest
+#
+# focus/swap
+#
 
+# focus the node in the given direction
 super + {_,shift + }{h,j,k,l}
-       bspc window -{f,s} {left,down,up,right}
+       bspc node -{f,s} {west,south,north,east}
 
-super + {_,shift + }c
-       bspc window -f {next,prev}
+# focus the node for the given path jump
+super + {p,b,comma,period}
+       bspc node -f @{parent,brother,first,second}
 
-super + {comma,period}
-       bspc desktop -C {backward,forward}
+# focus the next/previous node
+super + {_,shift + }c
+       bspc node -f {next,prev}
 
+# focus the next/previous desktop
 super + bracket{left,right}
        bspc desktop -f {prev,next}
 
+# focus the last node/desktop
+super + {grave,Tab}
+       bspc {node,desktop} -f last
+
+# focus the older or newer node in the focus history
+super + {o,i}
+       bspc wm -h off; \
+       bspc node {older,newer} -f; \
+       bspc wm -h on
+
+# focus or send to the given desktop
+super + {_,shift + }{1-9,0}
+       bspc {desktop -f,node -d} '^{1-9,10}'
+
+#
+# preselect
+#
+
+# preselect the direction
 super + ctrl + {h,j,k,l}
-       bspc window -p {left,down,up,right}
+       bspc node -p {west,south,north,east}
+
+# preselect the ratio
+super + ctrl + {1-9}
+       bspc node -o 0.{1-9}
 
+# cancel the preselection for the focused node or desktop
 super + ctrl + {_,shift + }space
-       bspc {window -p cancel,desktop -c}
+       bspc node @{_,/} -p cancel
 
+#
+# resize tiled/floating
+#
+
+# expand the tiled space in the given direction
 super + alt + {h,j,k,l}
-       bspc window -e {left -10,down +10,up -10,right +10}
+       bspc node {@west -r -10,@south -r +10,@north -r -10,@east -r +10}
 
+# contract the tiled space in the given direction
 super + alt + shift + {h,j,k,l}
-       bspc window -e {right -10,up +10,down -10,left +10}
-
-super + ctrl + {1-9}
-       bspc window -r 0.{1-9}
+       bspc node {@east -r -10,@north -r +10,@south -r -10,@west -r +10}
 
+# move a floating window
 super + {Left,Down,Up,Right}
        xdo move {-x -20,-y +20,-y -20,-x +20}
 
-super + {_,shift + }{1-9,0}
-       bspc {desktop -f,window -d} '^{1-9,10}'
+#
+# pointer focus/move/resize
+#
 
+# focus
 ~button1
        bspc pointer -g focus
 
+# start move/resize
 super + button{1-3}
        ; bspc pointer -g {move,resize_side,resize_corner}
 
+# end move/resize
 super + @button{1-3}
        bspc pointer -u
-
-#
-# wm independent hotkeys
-#
-
-super + Return
-       urxvt
-
-super + space
-       dmenu_run
-
-# make sxhkd reload its configuration files:
-super + Escape
-       pkill -USR1 -x sxhkd
index c342b6c65b08afa39608a6c1f5cc3befc79df6ea..1b2ef330ef24b97be77de250677c8c2b27f04f9f 100644 (file)
--- a/helpers.c
+++ b/helpers.c
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <fcntl.h>
+#include <ctype.h>
 #include <math.h>
 #include "bspwm.h"
 
@@ -99,38 +104,31 @@ char *read_string(const char *file_path, size_t *tlen)
        return content;
 }
 
-bool get_color(char *col, xcb_window_t win, uint32_t *pxl)
-{
-       xcb_colormap_t map = screen->default_colormap;
-       xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
-       if (reply != NULL)
-               map = reply->colormap;
-       free(reply);
 
-       if (col[0] == '#') {
-               unsigned int red, green, blue;
-               if (sscanf(col + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
-                       /* 2**16 - 1 == 0xffff and 0x101 * 0xij == 0xijij */
-                       red *= 0x101;
-                       green *= 0x101;
-                       blue *= 0x101;
-                       xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(dpy, xcb_alloc_color(dpy, map, red, green, blue), NULL);
-                       if (reply != NULL) {
-                               *pxl = reply->pixel;
-                               free(reply);
-                               return true;
-                       }
-               }
+/* Adapted from i3wm */
+uint32_t get_color_pixel(const char *color)
+{
+       unsigned int red, green, blue;
+       if (sscanf(color + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
+               /* We set the first 8 bits high to have 100% opacity in case of a 32 bit
+                * color depth visual. */
+               return (0xFF << 24) | (red << 16 | green << 8 | blue);
        } else {
-               xcb_alloc_named_color_reply_t *reply = xcb_alloc_named_color_reply(dpy, xcb_alloc_named_color(dpy, map, strlen(col), col), NULL);
-               if (reply != NULL) {
-                       *pxl = reply->pixel;
-                       free(reply);
-                       return true;
+               return screen->black_pixel;
+       }
+}
+
+bool is_hex_color(const char *color)
+{
+       if (color[0] != '#' || strlen(color) != 7) {
+               return false;
+       }
+       for (int i = 1; i < 7; i++) {
+               if (!isxdigit(color[i])) {
+                       return false;
                }
        }
-       *pxl = 0;
-       return false;
+       return true;
 }
 
 double distance(xcb_point_t a, xcb_point_t b)
index 7ca7870c446cc83eb0aea75623845ac51e7dd8e4..40050cf80ff0573286b9091d08582c0b9dd0d9f7 100644 (file)
--- a/helpers.h
+++ b/helpers.h
 #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 LAYOUT_CHR(A)     ((A) == LAYOUT_TILED ? 'T' : 'M')
 #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 SPLIT_DIR_STR(A)  ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east")))
 #define STATE_STR(A)      ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
+#define STATE_CHR(A)      ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))
 #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)
@@ -61,7 +63,8 @@
 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);
+uint32_t get_color_pixel(const char *color);
+bool is_hex_color(const char *color);
 double distance(xcb_point_t a, xcb_point_t b);
 
 #endif
index fad6ab947268cc261eeb46d15340cb4edcc66b4d..8195600ace3be17e65c49e8f8bf89816fd10cdd0 100644 (file)
--- a/history.c
+++ b/history.c
@@ -23,7 +23,9 @@
  */
 
 #include <stdlib.h>
+#include <stdbool.h>
 #include "bspwm.h"
+#include "tree.h"
 #include "query.h"
 
 history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
@@ -37,16 +39,19 @@ history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
 
 void history_add(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (!record_history)
+       if (!record_history) {
                return;
+       }
        history_needle = NULL;
        history_t *h = make_history(m, d, n);
        if (history_head == NULL) {
                history_head = history_tail = h;
        } else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) {
-               for (history_t *hh = history_tail; hh != NULL; hh = hh->prev)
-                       if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop))
+               for (history_t *hh = history_tail; hh != NULL; hh = hh->prev) {
+                       if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) {
                                hh->latest = false;
+                       }
+               }
                history_tail->next = h;
                h->prev = history_tail;
                history_tail = h;
@@ -57,48 +62,55 @@ void history_add(monitor_t *m, desktop_t *d, node_t *n)
 
 void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n)
 {
-       for (history_t *h = history_head; h != NULL; h = h->next)
-               if (h->loc.node == n) {
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (is_descendant(h->loc.node, n)) {
                        h->loc.monitor = m;
                        h->loc.desktop = d;
                }
+       }
 }
 
 void history_transfer_desktop(monitor_t *m, desktop_t *d)
 {
-       for (history_t *h = history_head; h != NULL; h = h->next)
-               if (h->loc.desktop == d)
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (h->loc.desktop == d) {
                        h->loc.monitor = m;
+               }
+       }
 }
 
 void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
 {
-       for (history_t *h = history_head; h != NULL; h = h->next)
-               if (h->loc.node == n1) {
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (is_descendant(h->loc.node, n1)) {
                        h->loc.monitor = m2;
                        h->loc.desktop = d2;
-               } else if (h->loc.node == n2) {
+               } else if (is_descendant(h->loc.node, n2)) {
                        h->loc.monitor = m1;
                        h->loc.desktop = d1;
                }
+       }
 }
 
 void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
 {
-       for (history_t *h = history_head; h != NULL; h = h->next)
-               if (h->loc.desktop == d1)
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (h->loc.desktop == d1) {
                        h->loc.monitor = m2;
-               else if (h->loc.desktop == d2)
+               } else if (h->loc.desktop == d2) {
                        h->loc.monitor = m1;
+               }
+       }
 }
 
-void history_remove(desktop_t *d, node_t *n)
+void history_remove(desktop_t *d, node_t *n, bool deep)
 {
    /* removing from the newest to the oldest is required */
    /* for maintaining the *latest* attribute */
        history_t *b = history_tail;
        while (b != NULL) {
-               if ((n != NULL && n == b->loc.node) || (n == NULL && d == b->loc.desktop)) {
+               if ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) ||
+                   (n == NULL && d == b->loc.desktop)) {
                        history_t *a = b->next;
                        history_t *c = b->prev;
                        if (a != NULL) {
@@ -106,23 +118,29 @@ void history_remove(desktop_t *d, node_t *n)
                                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 *p = c->prev;
-                                       if (history_head == c)
+                                       if (history_head == c) {
                                                history_head = history_tail;
-                                       if (history_needle == c)
+                                       }
+                                       if (history_needle == c) {
                                                history_needle = history_tail;
+                                       }
                                        free(c);
                                        c = p;
                                }
                                a->prev = c;
                        }
-                       if (c != NULL)
+                       if (c != NULL) {
                                c->next = a;
-                       if (history_tail == b)
+                       }
+                       if (history_tail == b) {
                                history_tail = c;
-                       if (history_head == b)
+                       }
+                       if (history_head == b) {
                                history_head = a;
-                       if (history_needle == b)
+                       }
+                       if (history_needle == b) {
                                history_needle = c;
+                       }
                        free(b);
                        b = c;
                } else {
@@ -142,44 +160,53 @@ void empty_history(void)
        history_head = history_tail = NULL;
 }
 
-node_t *history_get_node(desktop_t *d, node_t *n)
+node_t *history_last_node(desktop_t *d, node_t *n)
 {
-       for (history_t *h = history_tail; h != NULL; h = h->prev)
-               if (h->latest && h->loc.node != NULL && h->loc.node != n && h->loc.desktop == d)
+       for (history_t *h = history_tail; h != NULL; h = h->prev) {
+               if (h->latest && h->loc.node != NULL && !is_descendant(h->loc.node, n) && h->loc.desktop == d) {
                        return h->loc.node;
+               }
+       }
        return NULL;
 }
 
-desktop_t *history_get_desktop(monitor_t *m, desktop_t *d)
+desktop_t *history_last_desktop(monitor_t *m, desktop_t *d)
 {
-       for (history_t *h = history_tail; h != NULL; h = h->prev)
-               if (h->latest && h->loc.desktop != d && h->loc.monitor == m)
+       for (history_t *h = history_tail; h != NULL; h = h->prev) {
+               if (h->latest && h->loc.desktop != d && h->loc.monitor == m) {
                        return h->loc.desktop;
+               }
+       }
        return NULL;
 }
 
-monitor_t *history_get_monitor(monitor_t *m)
+monitor_t *history_last_monitor(monitor_t *m)
 {
-       for (history_t *h = history_tail; h != NULL; h = h->prev)
-               if (h->latest && h->loc.monitor != m)
+       for (history_t *h = history_tail; h != NULL; h = h->prev) {
+               if (h->latest && h->loc.monitor != m) {
                        return h->loc.monitor;
+               }
+       }
        return NULL;
 }
 
-bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, client_select_t sel)
+bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel)
 {
-       if (history_needle == NULL || record_history)
+       if (history_needle == NULL || record_history) {
                history_needle = history_tail;
+       }
 
        history_t *h;
        for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
                if (!h->latest ||
                    h->loc.node == NULL ||
                    h->loc.node == ref->node ||
-                   !node_matches(&h->loc, ref, sel))
+                   !node_matches(&h->loc, ref, sel)) {
                        continue;
-               if (!record_history)
+               }
+               if (!record_history) {
                        history_needle = h;
+               }
                *dst = h->loc;
                return true;
        }
@@ -188,17 +215,20 @@ bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst
 
 bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel)
 {
-       if (history_needle == NULL || record_history)
+       if (history_needle == NULL || record_history) {
                history_needle = history_tail;
+       }
 
        history_t *h;
        for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
                if (!h->latest ||
                    h->loc.desktop == ref->desktop ||
-                   !desktop_matches(&h->loc, ref, sel))
+                   !desktop_matches(&h->loc, ref, sel)) {
                        continue;
-               if (!record_history)
+               }
+               if (!record_history) {
                        history_needle = h;
+               }
                *dst = h->loc;
                return true;
        }
@@ -237,8 +267,9 @@ int history_rank(desktop_t *d, node_t *n)
                h = h->prev;
                i++;
        }
-       if (h == NULL)
+       if (h == NULL) {
                return -1;
-       else
+       } else {
                return i;
+       }
 }
index b69fc70d3347e5fb6020cb81ff363aefa808eb34..92c36fc9efca88c2dce81c1111e6112076a26a02 100644 (file)
--- a/history.h
+++ b/history.h
@@ -33,12 +33,12 @@ void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n);
 void history_transfer_desktop(monitor_t *m, desktop_t *d);
 void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
 void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
-void history_remove(desktop_t *d, node_t *n);
+void history_remove(desktop_t *d, node_t *n, bool deep);
 void empty_history(void);
-node_t *history_get_node(desktop_t *d, node_t *n);
-desktop_t *history_get_desktop(monitor_t *m, desktop_t *d);
-monitor_t *history_get_monitor(monitor_t *m);
-bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, client_select_t sel);
+node_t *history_last_node(desktop_t *d, node_t *n);
+desktop_t *history_last_desktop(monitor_t *m, desktop_t *d);
+monitor_t *history_last_monitor(monitor_t *m);
+bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel);
 bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel);
 bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel);
 int history_rank(desktop_t *d, node_t *n);
diff --git a/jsmn.c b/jsmn.c
index 2809669b314df52eb29144a8bb2c00e6f00ab51d..bbf013de4c68c35c1f87d46c9e28602830cd77da 100644 (file)
--- a/jsmn.c
+++ b/jsmn.c
@@ -1,5 +1,3 @@
-#include <stdlib.h>
-
 #include "jsmn.h"
 
 /**
index 6f3351939b51b7434022708f4a1c299d4506add3..f5c40d592491bf9e01919b3832e120e4bb13afc0 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
 #include "bspwm.h"
 #include "desktop.h"
-#include "ewmh.h"
-#include "history.h"
 #include "monitor.h"
 #include "pointer.h"
 #include "query.h"
@@ -38,7 +38,6 @@
 #include "tree.h"
 #include "window.h"
 #include "common.h"
-#include "subscribe.h"
 #include "parse.h"
 #include "messages.h"
 
@@ -47,8 +46,9 @@ int handle_message(char *msg, int msg_len, FILE *rsp)
        int cap = INIT_CAP;
        int num = 0;
        char **args = malloc(cap * sizeof(char *));
-       if (args == NULL)
+       if (args == NULL) {
                return MSG_FAILURE;
+       }
 
        for (int i = 0, j = 0; i < msg_len; i++) {
                if (msg[i] == 0) {
@@ -80,18 +80,18 @@ int handle_message(char *msg, int msg_len, FILE *rsp)
 
 int process_message(char **args, int num, FILE *rsp)
 {
-       if (streq("window", *args)) {
-               return cmd_window(++args, --num);
+       if (streq("node", *args)) {
+               return cmd_node(++args, --num);
        } else if (streq("desktop", *args)) {
                return cmd_desktop(++args, --num);
        } else if (streq("monitor", *args)) {
                return cmd_monitor(++args, --num);
        } else if (streq("query", *args)) {
                return cmd_query(++args, --num, rsp);
-       } else if (streq("restore", *args)) {
-               return cmd_restore(++args, --num);
-       } else if (streq("control", *args)) {
-               return cmd_control(++args, --num, rsp);
+       } else if (streq("subscribe", *args)) {
+               return cmd_subscribe(++args, --num, rsp);
+       } else if (streq("wm", *args)) {
+               return cmd_wm(++args, --num, rsp);
        } else if (streq("rule", *args)) {
                return cmd_rule(++args, --num, rsp);
        } else if (streq("pointer", *args)) {
@@ -105,23 +105,26 @@ int process_message(char **args, int num, FILE *rsp)
        return MSG_UNKNOWN;
 }
 
-int cmd_window(char **args, int num)
+int cmd_node(char **args, int num)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
 
        coordinates_t ref = {mon, mon->desk, mon->desk->focus};
        coordinates_t trg = ref;
 
        if ((*args)[0] != OPT_CHR) {
-               if (node_from_desc(*args, &ref, &trg))
+               if (node_from_desc(*args, &ref, &trg)) {
                        num--, args++;
-               else
+               } else {
                        return MSG_FAILURE;
+               }
        }
 
-       if (trg.node == NULL)
+       if (trg.node == NULL) {
                return MSG_FAILURE;
+       }
 
        bool dirty = false;
 
@@ -130,16 +133,18 @@ int cmd_window(char **args, int num)
                        coordinates_t dst = trg;
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!node_from_desc(*args, &trg, &dst))
+                               if (!node_from_desc(*args, &trg, &dst)) {
                                        return MSG_FAILURE;
+                               }
                        }
                        focus_node(dst.monitor, dst.desktop, dst.node);
                } else if (streq("-a", *args) || streq("--activate", *args)) {
                        coordinates_t dst = trg;
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!node_from_desc(*args, &trg, &dst))
+                               if (!node_from_desc(*args, &trg, &dst)) {
                                        return MSG_FAILURE;
+                               }
                        }
                        if (dst.desktop == mon->desk) {
                                return MSG_FAILURE;
@@ -152,58 +157,92 @@ int cmd_window(char **args, int num)
                                if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
                                        trg.monitor = dst.monitor;
                                        trg.desktop = dst.desktop;
+                               } else {
+                                       return MSG_FAILURE;
                                }
                        } else {
                                return MSG_FAILURE;
                        }
                } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        coordinates_t dst;
                        if (monitor_from_desc(*args, &trg, &dst)) {
                                if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
                                        trg.monitor = dst.monitor;
                                        trg.desktop = dst.monitor->desk;
+                               } else {
+                                       return MSG_FAILURE;
                                }
                        } else {
                                return MSG_FAILURE;
                        }
-               } else if (streq("-w", *args) || streq("--to-window", *args)) {
+               } else if (streq("-n", *args) || streq("--to-node", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        coordinates_t dst;
                        if (node_from_desc(*args, &trg, &dst)) {
                                if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
                                        trg.monitor = dst.monitor;
                                        trg.desktop = dst.desktop;
+                               } else {
+                                       return MSG_FAILURE;
                                }
                        } else {
                                return MSG_FAILURE;
                        }
                } else if (streq("-s", *args) || streq("--swap", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        coordinates_t dst;
                        if (node_from_desc(*args, &trg, &dst)) {
                                if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
-                                       if (trg.desktop != dst.desktop)
-                                               arrange(trg.monitor, trg.desktop);
                                        trg.monitor = dst.monitor;
                                        trg.desktop = dst.desktop;
-                                       dirty = true;
+                               } else {
+                                       return MSG_FAILURE;
                                }
                        } else {
                                return MSG_FAILURE;
                        }
+               } else if (streq("-l", *args) || streq("--layer", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               return MSG_SYNTAX;
+                       }
+                       if (trg.node->client == NULL) {
+                               return MSG_FAILURE;
+                       }
+                       stack_layer_t lyr;
+                       if (parse_stack_layer(*args, &lyr)) {
+                               set_layer(trg.monitor, trg.desktop, trg.node, lyr);
+                       } else {
+                               return MSG_FAILURE;
+                       }
                } else if (streq("-t", *args) || streq("--state", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        client_state_t cst;
+                       bool alternate = false;
+                       if ((*args)[0] == '~') {
+                               alternate = true;
+                               (*args)++;
+                       }
                        if (parse_client_state(*args, &cst)) {
+                               if (trg.node->client == NULL) {
+                                       return MSG_FAILURE;
+                               }
+                               if (alternate && trg.node->client->state == cst) {
+                                       cst = trg.node->client->last_state;
+                               }
                                set_state(trg.monitor, trg.desktop, trg.node, cst);
                                dirty = true;
                        } else {
@@ -211,8 +250,9 @@ int cmd_window(char **args, int num)
                        }
                } else if (streq("-g", *args) || streq("--flag", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        char *key = strtok(*args, EQL_TOK);
                        char *val = strtok(NULL, EQL_TOK);
                        alter_state_t a;
@@ -227,120 +267,138 @@ int cmd_window(char **args, int num)
                                }
                        }
                        if (streq("locked", key)) {
-                               set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->locked));
+                               set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
                        } else if (streq("sticky", key)) {
-                               set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->sticky));
+                               set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
                        } else if (streq("private", key)) {
-                               set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->private));
+                               set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
                        } else {
                                return MSG_FAILURE;
                        }
-               } else if (streq("-p", *args) || streq("--presel", *args)) {
+               } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       if (IS_FLOATING(trg.node->client) ||
-                           trg.desktop->layout != LAYOUT_TILED)
+                       }
+                       if (trg.node->vacant) {
                                return MSG_FAILURE;
+                       }
                        if (streq("cancel", *args)) {
-                               reset_mode(&trg);
+                               cancel_presel(trg.monitor, trg.desktop, trg.node);
                        } else {
+                               bool alternate = false;
+                               if ((*args)[0] == '~') {
+                                       alternate = true;
+                                       (*args)++;
+                               }
                                direction_t dir;
                                if (parse_direction(*args, &dir)) {
-                                       double rat = trg.node->split_ratio;
-                                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                                               num--, args++;
-                                               if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1)
-                                                       return MSG_FAILURE;
+                                       if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
+                                               cancel_presel(trg.monitor, trg.desktop, trg.node);
+                                       } else {
+                                               presel_dir(trg.monitor, trg.desktop, trg.node, dir);
+                                               draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
                                        }
-                                       trg.node->split_mode = MODE_MANUAL;
-                                       trg.node->split_dir = dir;
-                                       trg.node->split_ratio = rat;
-                                       window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
                                } else {
                                        return MSG_FAILURE;
                                }
                        }
-               } else if (streq("-e", *args) || streq("--edge", *args)) {
+               } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
                        num--, args++;
-                       if (num < 2)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       if (IS_FLOATING(trg.node->client))
-                               return MSG_FAILURE;
-                       direction_t dir;
-                       if (!parse_direction(*args, &dir))
+                       }
+                       if (trg.node->vacant) {
                                return MSG_FAILURE;
-                       node_t *n = find_fence(trg.node, dir);
-                       if (n == NULL)
+                       }
+                       double rat;
+                       if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
                                return MSG_FAILURE;
+                       } else {
+                               presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
+                               draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
+                       }
+               } else if (streq("-r", *args) || streq("--ratio", *args)) {
                        num--, args++;
+                       if (num < 1) {
+                               return MSG_SYNTAX;
+                       }
                        if ((*args)[0] == '+' || (*args)[0] == '-') {
                                int pix;
                                if (sscanf(*args, "%i", &pix) == 1) {
-                                       int max = (n->split_type == TYPE_HORIZONTAL ? n->rectangle.height : n->rectangle.width);
-                                       double rat = ((max * n->split_ratio) + pix) / max;
-                                       if (rat > 0 && rat < 1)
-                                               n->split_ratio = rat;
-                                       else
+                                       int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
+                                       double rat = ((max * trg.node->split_ratio) + pix) / max;
+                                       if (rat > 0 && rat < 1) {
+                                               trg.node->split_ratio = rat;
+                                       } else {
                                                return MSG_FAILURE;
+                                       }
                                } else {
                                        return MSG_FAILURE;
                                }
                        } else {
                                double rat;
-                               if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1)
-                                       n->split_ratio = rat;
-                               else
+                               if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+                                       trg.node->split_ratio = rat;
+                               } else {
                                        return MSG_FAILURE;
+                               }
                        }
                        dirty = true;
-               } else if (streq("-r", *args) || streq("--ratio", *args)) {
+               } else if (streq("-F", *args) || streq("--flip", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       double rat;
-                       if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
-                               trg.node->split_ratio = rat;
-                               window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
+                       }
+                       flip_t flp;
+                       if (parse_flip(*args, &flp)) {
+                               flip_tree(trg.node, flp);
+                               dirty = true;
                        } else {
                                return MSG_FAILURE;
                        }
-               } else if (streq("-l", *args) || streq("--layer", *args)) {
+               } else if (streq("-R", *args) || streq("--rotate", *args)) {
                        num--, args++;
                        if (num < 1) {
                                return MSG_SYNTAX;
                        }
-                       stack_layer_t lyr;
-                       if (parse_stack_layer(*args, &lyr)) {
-                               set_layer(trg.monitor, trg.desktop, trg.node, lyr);
+                       int deg;
+                       if (parse_degree(*args, &deg)) {
+                               rotate_tree(trg.node, deg);
+                               dirty = true;
                        } else {
                                return MSG_FAILURE;
                        }
-               } else if (streq("-R", *args) || streq("--rotate", *args)) {
+               } else if (streq("-E", *args) || streq("--equalize", *args)) {
+                       equalize_tree(trg.node);
+                       dirty = true;
+               } else if (streq("-B", *args) || streq("--balance", *args)) {
+                       balance_tree(trg.node);
+                       dirty = true;
+               } else if (streq("-C", *args) || streq("--circulate", *args)) {
                        num--, args++;
-                       if (num < 2)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       direction_t dir;
-                       if (!parse_direction(*args, &dir))
-                               return MSG_FAILURE;
-                       node_t *n = find_fence(trg.node, dir);
-                       if (n == NULL)
-                               return MSG_FAILURE;
-                       num--, args++;
-                       int deg;
-                       if (parse_degree(*args, &deg)) {
-                               rotate_tree(n, deg);
+                       }
+                       circulate_dir_t cir;
+                       if (parse_circulate_direction(*args, &cir)) {
+                               circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
                                dirty = true;
                        } else {
                                return MSG_FAILURE;
                        }
                } else if (streq("-c", *args) || streq("--close", *args)) {
-                       if (num > 1)
+                       if (num > 1) {
                                return MSG_SYNTAX;
+                       }
+                       if (locked_count(trg.node) > 0) {
+                               return MSG_FAILURE;
+                       }
                        window_close(trg.node);
                } else if (streq("-k", *args) || streq("--kill", *args)) {
-                       if (num > 1)
+                       if (num > 1) {
                                return MSG_SYNTAX;
+                       }
                        window_kill(trg.monitor, trg.desktop, trg.node);
                        dirty = true;
                } else {
@@ -350,25 +408,28 @@ int cmd_window(char **args, int num)
                num--, args++;
        }
 
-       if (dirty)
+       if (dirty) {
                arrange(trg.monitor, trg.desktop);
+       }
 
        return MSG_SUCCESS;
 }
 
 int cmd_desktop(char **args, int num)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
 
        coordinates_t ref = {mon, mon->desk, NULL};
        coordinates_t trg = ref;
 
        if ((*args)[0] != OPT_CHR) {
-               if (desktop_from_desc(*args, &ref, &trg))
+               if (desktop_from_desc(*args, &ref, &trg)) {
                        num--, args++;
-               else
+               } else {
                        return MSG_FAILURE;
+               }
        }
 
        bool dirty = false;
@@ -378,37 +439,58 @@ int cmd_desktop(char **args, int num)
                        coordinates_t dst = trg;
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!desktop_from_desc(*args, &trg, &dst))
+                               if (!desktop_from_desc(*args, &trg, &dst)) {
                                        return MSG_FAILURE;
+                               }
                        }
                        focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
+               } else if (streq("-a", *args) || streq("--activate", *args)) {
+                       coordinates_t dst = trg;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               if (!desktop_from_desc(*args, &trg, &dst)) {
+                                       return MSG_FAILURE;
+                               }
+                       }
+                       activate_desktop(dst.monitor, dst.desktop);
                } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       if (trg.monitor->desk_head == trg.monitor->desk_tail)
+                       }
+                       if (trg.monitor->desk_head == trg.monitor->desk_tail) {
                                return MSG_FAILURE;
+                       }
                        coordinates_t dst;
                        if (monitor_from_desc(*args, &trg, &dst)) {
-                               transfer_desktop(trg.monitor, dst.monitor, trg.desktop);
-                               trg.monitor = dst.monitor;
-                               update_current();
+                               if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
+                                       trg.monitor = dst.monitor;
+                               } else {
+                                       return MSG_FAILURE;
+                               }
                        } else {
                                return MSG_FAILURE;
                        }
                } else if (streq("-s", *args) || streq("--swap", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        coordinates_t dst;
-                       if (desktop_from_desc(*args, &trg, &dst))
-                               swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop);
-                       else
+                       if (desktop_from_desc(*args, &trg, &dst)) {
+                               if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
+                                       trg.monitor = dst.monitor;
+                               } else {
+                                       return MSG_FAILURE;
+                               }
+                       } else {
                                return MSG_FAILURE;
+                       }
                } else if (streq("-b", *args) || streq("--bubble", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        cycle_dir_t cyc;
                        if (parse_cycle_direction(*args, &cyc)) {
                                desktop_t *d = trg.desktop;
@@ -434,95 +516,58 @@ int cmd_desktop(char **args, int num)
                        }
                } else if (streq("-l", *args) || streq("--layout", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        layout_t lyt;
                        cycle_dir_t cyc;
-                       if (parse_cycle_direction(*args, &cyc))
+                       if (parse_cycle_direction(*args, &cyc)) {
                                change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
-                       else if (parse_layout(*args, &lyt))
+                       } else if (parse_layout(*args, &lyt)) {
                                change_layout(trg.monitor, trg.desktop, lyt);
-                       else
+                       } else {
                                return MSG_FAILURE;
+                       }
                } else if (streq("-n", *args) || streq("--rename", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        rename_desktop(trg.monitor, trg.desktop, *args);
                } else if (streq("-r", *args) || streq("--remove", *args)) {
                        if (trg.desktop->root == NULL &&
                            trg.monitor->desk_head != trg.monitor->desk_tail) {
                                remove_desktop(trg.monitor, trg.desktop);
-                               show_desktop(trg.monitor->desk);
-                               update_current();
                                return MSG_SUCCESS;
                        } else {
                                return MSG_FAILURE;
                        }
-               } else if (streq("-c", *args) || streq("--cancel-presel", *args)) {
-                       reset_mode(&trg);
-               } else if (streq("-F", *args) || streq("--flip", *args)) {
-                       num--, args++;
-                       if (num < 1)
-                               return MSG_SYNTAX;
-                       flip_t flp;
-                       if (parse_flip(*args, &flp)) {
-                               flip_tree(trg.desktop->root, flp);
-                               dirty = true;
-                       } else {
-                               return MSG_FAILURE;
-                       }
-               } else if (streq("-R", *args) || streq("--rotate", *args)) {
-                       num--, args++;
-                       if (num < 1)
-                               return MSG_SYNTAX;
-                       int deg;
-                       if (parse_degree(*args, &deg)) {
-                               rotate_tree(trg.desktop->root, deg);
-                               dirty = true;
-                       } else {
-                               return MSG_FAILURE;
-                       }
-               } else if (streq("-E", *args) || streq("--equalize", *args)) {
-                       equalize_tree(trg.desktop->root);
-                       dirty = true;
-               } else if (streq("-B", *args) || streq("--balance", *args)) {
-                       balance_tree(trg.desktop->root);
-                       dirty = true;
-               } else if (streq("-C", *args) || streq("--circulate", *args)) {
-                       num--, args++;
-                       if (num < 1)
-                               return MSG_SYNTAX;
-                       circulate_dir_t cir;
-                       if (parse_circulate_direction(*args, &cir)) {
-                               circulate_leaves(trg.monitor, trg.desktop, cir);
-                               dirty = true;
-                       } else {
-                               return MSG_FAILURE;
-                       }
                }
                num--, args++;
        }
 
-       if (dirty)
+       if (dirty) {
                arrange(trg.monitor, trg.desktop);
+       }
 
        return MSG_SUCCESS;
 }
 
 int cmd_monitor(char **args, int num)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
 
        coordinates_t ref = {mon, NULL, NULL};
        coordinates_t trg = ref;
 
        if ((*args)[0] != OPT_CHR) {
-               if (monitor_from_desc(*args, &ref, &trg))
+               if (monitor_from_desc(*args, &ref, &trg)) {
                        num--, args++;
-               else
+               } else {
                        return MSG_FAILURE;
+               }
        }
 
        while (num > 0) {
@@ -530,14 +575,16 @@ int cmd_monitor(char **args, int num)
                        coordinates_t dst = trg;
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!monitor_from_desc(*args, &trg, &dst))
+                               if (!monitor_from_desc(*args, &trg, &dst)) {
                                        return MSG_FAILURE;
+                               }
                        }
                        focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
                } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        desktop_t *d = trg.monitor->desk_head;
                        while (num > 0 && d != NULL) {
                                rename_desktop(trg.monitor, d, *args);
@@ -553,24 +600,27 @@ int cmd_monitor(char **args, int num)
                        }
                        while (d != NULL) {
                                desktop_t *next = d->next;
-                               if (d == mon->desk)
+                               if (d == mon->desk) {
                                        focus_node(trg.monitor, d->prev, d->prev->focus);
+                               }
                                merge_desktops(trg.monitor, d, mon, mon->desk);
                                remove_desktop(trg.monitor, d);
                                d = next;
                        }
                } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        while (num > 0) {
                                add_desktop(trg.monitor, make_desktop(*args));
                                num--, args++;
                        }
                } else if (streq("-r", *args) || streq("--remove-desktops", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        while (num > 0) {
                                coordinates_t dst;
                                if (locate_desktop(*args, &dst) && dst.monitor->desk_head != dst.monitor->desk_tail && dst.desktop->root == NULL) {
@@ -581,16 +631,18 @@ int cmd_monitor(char **args, int num)
                        }
                } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        desktop_t *d = trg.monitor->desk_head;
                        while (d != NULL && num > 0) {
                                desktop_t *next = d->next;
                                coordinates_t dst;
                                if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
                                        swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
-                                       if (next == dst.desktop)
+                                       if (next == dst.desktop) {
                                                next = d;
+                                       }
                                }
                                d = next;
                                num--, args++;
@@ -603,13 +655,15 @@ int cmd_monitor(char **args, int num)
                        rename_monitor(trg.monitor, *args);
                } else if (streq("-s", *args) || streq("--swap", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        coordinates_t dst;
-                       if (monitor_from_desc(*args, &trg, &dst))
+                       if (monitor_from_desc(*args, &trg, &dst)) {
                                swap_monitors(trg.monitor, dst.monitor);
-                       else
+                       } else {
                                return MSG_FAILURE;
+                       }
                } else {
                        return MSG_SYNTAX;
                }
@@ -623,8 +677,11 @@ int cmd_query(char **args, int num, FILE *rsp)
 {
        coordinates_t ref = {mon, mon->desk, mon->desk->focus};
        coordinates_t trg = {NULL, NULL, NULL};
+       monitor_select_t *monitor_sel = NULL;
+       desktop_select_t *desktop_sel = NULL;
+       node_select_t *node_sel = NULL;
        domain_t dom = DOMAIN_TREE;
-       int d = 0, t = 0;
+       int d = 0, t = 0, ret = MSG_SUCCESS;
 
        while (num > 0) {
                if (streq("-T", *args) || streq("--tree", *args)) {
@@ -633,55 +690,81 @@ int cmd_query(char **args, int num, FILE *rsp)
                        dom = DOMAIN_MONITOR, d++;
                } else if (streq("-D", *args) || streq("--desktops", *args)) {
                        dom = DOMAIN_DESKTOP, d++;
-               } else if (streq("-W", *args) || streq("--windows", *args)) {
-                       dom = DOMAIN_WINDOW, d++;
-               } else if (streq("-H", *args) || streq("--history", *args)) {
-                       dom = DOMAIN_HISTORY, d++;
-               } else if (streq("-S", *args) || streq("--stack", *args)) {
-                       dom = DOMAIN_STACK, d++;
+               } else if (streq("-N", *args) || streq("--nodes", *args)) {
+                       dom = DOMAIN_NODE, d++;
                } else if (streq("-m", *args) || streq("--monitor", *args)) {
-                       trg.monitor = ref.monitor;
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!monitor_from_desc(*args, &ref, &trg))
-                                       return MSG_FAILURE;
+                               if ((*args)[0] == '.') {
+                                       monitor_sel = malloc(sizeof(monitor_select_t));
+                                       *monitor_sel = make_monitor_select();
+                                       if (!parse_monitor_modifiers(*args, monitor_sel)) {
+                                               ret = MSG_FAILURE;
+                                               goto end;
+                                       }
+                               } else if (!monitor_from_desc(*args, &ref, &trg)) {
+                                       ret = MSG_FAILURE;
+                                       goto end;
+                               }
+                       } else {
+                               trg.monitor = ref.monitor;
                        }
                        t++;
                } else if (streq("-d", *args) || streq("--desktop", *args)) {
-                       trg.monitor = ref.monitor;
-                       trg.desktop = ref.desktop;
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!desktop_from_desc(*args, &ref, &trg))
-                                       return MSG_FAILURE;
+                               if ((*args)[0] == '.') {
+                                       desktop_sel = malloc(sizeof(desktop_select_t));
+                                       *desktop_sel = make_desktop_select();
+                                       if (!parse_desktop_modifiers(*args, desktop_sel)) {
+                                               ret = MSG_FAILURE;
+                                               goto end;
+                                       }
+                               } else if (!desktop_from_desc(*args, &ref, &trg)) {
+                                       ret = MSG_FAILURE;
+                                       goto end;
+                               }
+                       } else {
+                               trg.monitor = ref.monitor;
+                               trg.desktop = ref.desktop;
                        }
                        t++;
-               } else if (streq("-w", *args) || streq("--window", *args)) {
-                       trg = ref;
+               } else if (streq("-n", *args) || streq("--node", *args)) {
                        if (num > 1 && *(args + 1)[0] != OPT_CHR) {
                                num--, args++;
-                               if (!node_from_desc(*args, &ref, &trg))
-                                       return MSG_FAILURE;
+                               if ((*args)[0] == '.') {
+                                       node_sel = malloc(sizeof(node_select_t));
+                                       *node_sel = make_node_select();
+                                       if (!parse_node_modifiers(*args, node_sel)) {
+                                               ret = MSG_FAILURE;
+                                               goto end;
+                                       }
+                               } else if (!node_from_desc(*args, &ref, &trg)) {
+                                       ret = MSG_FAILURE;
+                                       goto end;
+                               }
+                       } else {
+                               trg = ref;
                        }
                        t++;
                } else {
-                       return MSG_SYNTAX;
+                       ret = MSG_SYNTAX;
+                       goto end;
                }
                num--, args++;
        }
 
        if (d != 1 || t > 1) {
-               return MSG_SYNTAX;
+               ret = MSG_SYNTAX;
+               goto end;
        }
 
-       if (dom == DOMAIN_HISTORY) {
-               query_history(trg, rsp);
-       } else if (dom == DOMAIN_STACK) {
-               query_stack(rsp);
-       } else if (dom == DOMAIN_WINDOW) {
-               query_windows(trg, rsp);
-       } else if (dom == DOMAIN_DESKTOP || dom == DOMAIN_MONITOR) {
-               query_names(dom, trg, rsp);
+       if (dom == DOMAIN_NODE) {
+               query_node_ids(trg, node_sel, rsp);
+       } else if (dom == DOMAIN_DESKTOP) {
+               query_desktop_names(trg, desktop_sel, rsp);
+       } else if (dom == DOMAIN_MONITOR) {
+               query_monitor_names(trg, monitor_sel, rsp);
        } else {
                if (trg.node != NULL) {
                        query_node(trg.node, rsp);
@@ -690,35 +773,50 @@ int cmd_query(char **args, int num, FILE *rsp)
                } else if (trg.monitor != NULL) {
                        query_monitor(trg.monitor, rsp);
                } else {
-                       query_tree(rsp);
+                       ret = MSG_SYNTAX;
+                       goto end;
                }
                fprintf(rsp, "\n");
        }
 
-       return MSG_SUCCESS;
+end:
+       free(monitor_sel);
+       free(desktop_sel);
+       free(node_sel);
+
+       return ret;
 }
 
 int cmd_rule(char **args, int num, FILE *rsp)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
+
        while (num > 0) {
                if (streq("-a", *args) || streq("--add", *args)) {
                        num--, args++;
-                       if (num < 2)
+                       if (num < 2) {
                                return MSG_SYNTAX;
+                       }
                        rule_t *rule = make_rule();
-                       snprintf(rule->cause, sizeof(rule->cause), "%s", *args);
+                       char *class_name = strtok(*args, COL_TOK);
+                       char *instance_name = strtok(NULL, COL_TOK);
+                       snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
+                       snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
                        num--, args++;
                        size_t i = 0;
                        while (num > 0) {
                                if (streq("-o", *args) || streq("--one-shot", *args)) {
                                        rule->one_shot = true;
                                } else {
-                                       for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++)
+                                       for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
                                                rule->effect[i] = (*args)[j];
-                                       if (num > 1 && i < sizeof(rule->effect))
+                                       }
+                                       if (num > 1 && i < sizeof(rule->effect)) {
                                                rule->effect[i++] = ' ';
+                                       }
+
                                }
                                num--, args++;
                        }
@@ -726,23 +824,25 @@ int cmd_rule(char **args, int num, FILE *rsp)
                        add_rule(rule);
                } else if (streq("-r", *args) || streq("--remove", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        int idx;
                        while (num > 0) {
-                               if (parse_index(*args, &idx))
+                               if (parse_index(*args, &idx)) {
                                        remove_rule_by_index(idx - 1);
-                               else if (streq("tail", *args))
+                               } else if (streq("tail", *args)) {
                                        remove_rule(rule_tail);
-                               else if (streq("head", *args))
+                               } else if (streq("head", *args)) {
                                        remove_rule(rule_head);
-                               else
+                               } else {
                                        remove_rule_by_cause(*args);
+                               }
                                num--, args++;
                        }
                } else if (streq("-l", *args) || streq("--list", *args)) {
                        num--, args++;
-                       list_rules(num > 0 ? *args : NULL, rsp);
+                       list_rules(rsp);
                } else {
                        return MSG_SYNTAX;
                }
@@ -754,28 +854,33 @@ int cmd_rule(char **args, int num, FILE *rsp)
 
 int cmd_pointer(char **args, int num)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
        while (num > 0) {
                if (streq("-t", *args) || streq("--track", *args)) {
                        num--, args++;
-                       if (num < 2)
+                       if (num < 2) {
                                return MSG_SYNTAX;
+                       }
                        int x, y;
-                       if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1)
+                       if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1) {
                                track_pointer(x, y);
-                       else
+                       } else {
                                return MSG_FAILURE;
+                       }
                        num--, args++;
                } else if (streq("-g", *args) || streq("--grab", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        pointer_action_t pac;
-                       if (parse_pointer_action(*args, &pac))
+                       if (parse_pointer_action(*args, &pac)) {
                                grab_pointer(pac);
-                       else
+                       } else {
                                return MSG_FAILURE;
+                       }
                } else if (streq("-u", *args) || streq("--ungrab", *args)) {
                        ungrab_pointer();
                } else {
@@ -787,13 +892,16 @@ int cmd_pointer(char **args, int num)
        return MSG_SUCCESS;
 }
 
-int cmd_restore(char **args, int num)
+int cmd_wm(char **args, int num, FILE *rsp)
 {
        if (num < 1) {
                return MSG_SYNTAX;
        }
        while (num > 0) {
-               if (streq("-T", *args) || streq("--tree", *args)) {
+               if (streq("-d", *args) || streq("--dump-state", *args)) {
+                       query_tree(rsp);
+                       fprintf(rsp, "\n");
+               } else if (streq("-l", *args) || streq("--load-state", *args)) {
                        num--, args++;
                        if (num < 1) {
                                return MSG_SYNTAX;
@@ -801,69 +909,51 @@ int cmd_restore(char **args, int num)
                        if (!restore_tree(*args)) {
                                return MSG_FAILURE;
                        }
-               } else if (streq("-H", *args) || streq("--history", *args)) {
+               } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
                        num--, args++;
-                       if (num < 1) {
+                       if (num < 2) {
                                return MSG_SYNTAX;
                        }
-                       if (!restore_history(*args)) {
-                               return MSG_FAILURE;
+                       char *name = *args;
+                       num--, args++;
+                       xcb_rectangle_t r;
+                       if (parse_rectangle(*args, &r)) {
+                               monitor_t *m = make_monitor(&r);
+                               snprintf(m->name, sizeof(m->name), "%s", name);
+                               add_monitor(m);
+                               add_desktop(m, make_desktop(NULL));
+                       } else {
+                               return MSG_SYNTAX;
                        }
-               } else if (streq("-S", *args) || streq("--stack", *args)) {
+               } else if (streq("-r", *args) || streq("--remove-monitor", *args)) {
                        num--, args++;
                        if (num < 1) {
                                return MSG_SYNTAX;
                        }
-                       if (!restore_stack(*args)) {
+                       if (mon_head == mon_tail) {
                                return MSG_FAILURE;
                        }
-               } else {
-                       return MSG_SYNTAX;
-               }
-               num--, args++;
-       }
-
-       return MSG_SUCCESS;
-}
-
-int cmd_control(char **args, int num, FILE *rsp)
-{
-       if (num < 1)
-               return MSG_SYNTAX;
-       while (num > 0) {
-               if (streq("--adopt-orphans", *args)) {
-                       adopt_orphans();
-               } else if (streq("--toggle-visibility", *args)) {
-                       toggle_visibility();
-               } else if (streq("--subscribe", *args)) {
-                       num--, args++;
-                       int field = 0;
-                       if (num < 1) {
-                               field = SBSC_MASK_REPORT;
+                       monitor_t *m = find_monitor(*args);
+                       if (m != NULL) {
+                               remove_monitor(m);
                        } else {
-                               subscriber_mask_t mask;
-                               while (num > 0) {
-                                       if (parse_subscriber_mask(*args, &mask)) {
-                                               field |= mask;
-                                       } else {
-                                               return MSG_SYNTAX;
-                                       }
-                                       num--, args++;
-                               }
+                               return MSG_FAILURE;
                        }
-                       add_subscriber(rsp, field);
-                       return MSG_SUBSCRIBE;
-               } else if (streq("--get-status", *args)) {
+               } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
+                       adopt_orphans();
+               } else if (streq("-g", *args) || streq("--get-status", *args)) {
                        print_report(rsp);
-               } else if (streq("--record-history", *args)) {
+               } else if (streq("-h", *args) || streq("--record-history", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
+                       }
                        bool b;
-                       if (parse_bool(*args, &b))
+                       if (parse_bool(*args, &b)) {
                                record_history = b;
-                       else
+                       } else {
                                return MSG_SYNTAX;
+                       }
                } else {
                        return MSG_SYNTAX;
                }
@@ -875,52 +965,85 @@ int cmd_control(char **args, int num, FILE *rsp)
 
 int cmd_config(char **args, int num, FILE *rsp)
 {
-       if (num < 1)
+       if (num < 1) {
                return MSG_SYNTAX;
+       }
+
        coordinates_t ref = {mon, mon->desk, mon->desk->focus};
        coordinates_t trg = {NULL, NULL, NULL};
+
        if ((*args)[0] == OPT_CHR) {
                if (streq("-m", *args) || streq("--monitor", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       if (!monitor_from_desc(*args, &ref, &trg))
+                       }
+                       if (!monitor_from_desc(*args, &ref, &trg)) {
                                return MSG_FAILURE;
+                       }
                } else if (streq("-d", *args) || streq("--desktop", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       if (!desktop_from_desc(*args, &ref, &trg))
+                       }
+                       if (!desktop_from_desc(*args, &ref, &trg)) {
                                return MSG_FAILURE;
-               } else if (streq("-w", *args) || streq("--window", *args)) {
+                       }
+               } else if (streq("-n", *args) || streq("--node", *args)) {
                        num--, args++;
-                       if (num < 1)
+                       if (num < 1) {
                                return MSG_SYNTAX;
-                       if (!node_from_desc(*args, &ref, &trg))
+                       }
+                       if (!node_from_desc(*args, &ref, &trg)) {
                                return MSG_FAILURE;
+                       }
                } else {
                        return MSG_SYNTAX;
                }
                num--, args++;
        }
-       if (num == 2)
+       if (num == 2) {
                return set_setting(trg, *args, *(args + 1));
-       else if (num == 1)
+       } else if (num == 1) {
                return get_setting(trg, *args, rsp);
-       else
+       } else {
                return MSG_SYNTAX;
+       }
+}
+
+int cmd_subscribe(char **args, int num, FILE *rsp)
+{
+       int field = 0;
+       if (num < 1) {
+               field = SBSC_MASK_REPORT;
+       } else {
+               subscriber_mask_t mask;
+               while (num > 0) {
+                       if (parse_subscriber_mask(*args, &mask)) {
+                               field |= mask;
+                       } else {
+                               return MSG_SYNTAX;
+                       }
+                       num--, args++;
+               }
+       }
+
+       add_subscriber(rsp, field);
+       return MSG_SUBSCRIBE;
 }
 
 int cmd_quit(char **args, int num)
 {
-       if (num > 0 && sscanf(*args, "%i", &exit_status) != 1)
-               return MSG_FAILURE;
+       if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
+               return MSG_SYNTAX;
+       }
        running = false;
        return MSG_SUCCESS;
 }
 
 int set_setting(coordinates_t loc, char *name, char *value)
 {
+       bool colors_changed = false;
 #define DESK_WIN_DEF_SET(k, v) \
                if (loc.node != NULL) \
                        loc.node->client->k = v; \
@@ -960,23 +1083,27 @@ int set_setting(coordinates_t loc, char *name, char *value)
                                m->k = v;
        } else if (streq("top_padding", name)) {
                int tp;
-               if (sscanf(value, "%i", &tp) != 1)
+               if (sscanf(value, "%i", &tp) != 1) {
                        return MSG_FAILURE;
+               }
                MON_DESK_SET(top_padding, tp)
        } else if (streq("right_padding", name)) {
                int rp;
-               if (sscanf(value, "%i", &rp) != 1)
+               if (sscanf(value, "%i", &rp) != 1) {
                        return MSG_FAILURE;
+               }
                MON_DESK_SET(right_padding, rp)
        } else if (streq("bottom_padding", name)) {
                int bp;
-               if (sscanf(value, "%i", &bp) != 1)
+               if (sscanf(value, "%i", &bp) != 1) {
                        return MSG_FAILURE;
+               }
                MON_DESK_SET(bottom_padding, bp)
        } else if (streq("left_padding", name)) {
                int lp;
-               if (sscanf(value, "%i", &lp) != 1)
+               if (sscanf(value, "%i", &lp) != 1) {
                        return MSG_FAILURE;
+               }
                MON_DESK_SET(left_padding, lp)
 #undef MON_DESK_SET
 #define SET_STR(s) \
@@ -988,28 +1115,24 @@ int set_setting(coordinates_t loc, char *name, char *value)
 #undef SET_STR
        } else if (streq("split_ratio", name)) {
                double r;
-               if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
+               if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
                        split_ratio = r;
-               else
+               } else {
                        return MSG_FAILURE;
+               }
                return MSG_SUCCESS;
 #define SET_COLOR(s) \
        } else if (streq(#s, name)) { \
-               snprintf(s, sizeof(s), "%s", value);
-       SET_COLOR(focused_border_color)
-       SET_COLOR(active_border_color)
+               if (!is_hex_color(value)) { \
+                       return MSG_FAILURE; \
+               } else { \
+                       snprintf(s, sizeof(s), "%s", value); \
+               colors_changed = true; \
+               }
        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)
+       SET_COLOR(active_border_color)
+       SET_COLOR(focused_border_color)
+       SET_COLOR(presel_feedback_color)
 #undef SET_COLOR
        } else if (streq("initial_polarity", name)) {
                child_polarity_t p;
@@ -1020,13 +1143,16 @@ int set_setting(coordinates_t loc, char *name, char *value)
                }
        } else if (streq("focus_follows_pointer", name)) {
                bool b;
-               if (parse_bool(value, &b) && b != focus_follows_pointer) {
+               if (parse_bool(value, &b)) {
+                       if (b == focus_follows_pointer) {
+                               return MSG_SUCCESS;
+                       }
                        focus_follows_pointer = b;
                        uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 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)) {
-                                               xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
+                                               xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
                                        }
                                }
                        }
@@ -1050,7 +1176,7 @@ int set_setting(coordinates_t loc, char *name, char *value)
                        return MSG_FAILURE;
                SET_BOOL(borderless_monocle)
                SET_BOOL(gapless_monocle)
-               SET_BOOL(leaf_monocle)
+               SET_BOOL(single_monocle)
                SET_BOOL(pointer_follows_focus)
                SET_BOOL(pointer_follows_monitor)
                SET_BOOL(history_aware_focus)
@@ -1072,9 +1198,14 @@ int set_setting(coordinates_t loc, char *name, char *value)
                return MSG_FAILURE;
        }
 
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
                        arrange(m, d);
+                       if (colors_changed) {
+                               update_colors_in(d->root, d, m);
+                       }
+               }
+       }
 
        return MSG_SUCCESS;
 }
@@ -1119,24 +1250,17 @@ int get_setting(coordinates_t loc, char *name, FILE* rsp)
 #define GET_COLOR(s) \
        else if (streq(#s, name)) \
                fprintf(rsp, "%s", 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)
+       GET_COLOR(active_border_color)
+       GET_COLOR(focused_border_color)
+       GET_COLOR(presel_feedback_color)
 #undef GET_COLOR
 #define GET_BOOL(s) \
        else if (streq(#s, name)) \
                fprintf(rsp, "%s", BOOL_STR(s));
        GET_BOOL(borderless_monocle)
        GET_BOOL(gapless_monocle)
-       GET_BOOL(leaf_monocle)
+       GET_BOOL(single_monocle)
        GET_BOOL(focus_follows_pointer)
        GET_BOOL(pointer_follows_focus)
        GET_BOOL(pointer_follows_monitor)
@@ -1153,65 +1277,3 @@ int get_setting(coordinates_t loc, char *name, FILE* rsp)
        fprintf(rsp, "\n");
        return MSG_SUCCESS;
 }
-
-bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
-{
-       if (streq("all", s)) {
-               *mask = SBSC_MASK_ALL;
-       } else if (streq("window", s)) {
-               *mask = SBSC_MASK_WINDOW;
-       } else if (streq("desktop", s)) {
-               *mask = SBSC_MASK_DESKTOP;
-       } else if (streq("monitor", s)) {
-               *mask = SBSC_MASK_MONITOR;
-       } else if (streq("window_manage", s)) {
-               *mask = SBSC_MASK_WINDOW_MANAGE;
-       } else if (streq("window_unmanage", s)) {
-               *mask = SBSC_MASK_WINDOW_UNMANAGE;
-       } else if (streq("window_swap", s)) {
-               *mask = SBSC_MASK_WINDOW_SWAP;
-       } else if (streq("window_transfer", s)) {
-               *mask = SBSC_MASK_WINDOW_TRANSFER;
-       } else if (streq("window_focus", s)) {
-               *mask = SBSC_MASK_WINDOW_FOCUS;
-       } else if (streq("window_activate", s)) {
-               *mask = SBSC_MASK_WINDOW_ACTIVATE;
-       } else if (streq("window_geometry", s)) {
-               *mask = SBSC_MASK_WINDOW_GEOMETRY;
-       } else if (streq("window_state", s)) {
-               *mask = SBSC_MASK_WINDOW_STATE;
-       } else if (streq("window_flag", s)) {
-               *mask = SBSC_MASK_WINDOW_FLAG;
-       } else if (streq("window_layer", s)) {
-               *mask = SBSC_MASK_WINDOW_LAYER;
-       } else if (streq("desktop_add", s)) {
-               *mask = SBSC_MASK_DESKTOP_ADD;
-       } else if (streq("desktop_rename", s)) {
-               *mask = SBSC_MASK_DESKTOP_RENAME;
-       } else if (streq("desktop_remove", s)) {
-               *mask = SBSC_MASK_DESKTOP_REMOVE;
-       } else if (streq("desktop_swap", s)) {
-               *mask = SBSC_MASK_DESKTOP_SWAP;
-       } else if (streq("desktop_transfer", s)) {
-               *mask = SBSC_MASK_DESKTOP_TRANSFER;
-       } else if (streq("desktop_focus", s)) {
-               *mask = SBSC_MASK_DESKTOP_FOCUS;
-       } else if (streq("desktop_layout", s)) {
-               *mask = SBSC_MASK_DESKTOP_LAYOUT;
-       } else if (streq("monitor_add", s)) {
-               *mask = SBSC_MASK_MONITOR_ADD;
-       } else if (streq("monitor_rename", s)) {
-               *mask = SBSC_MASK_MONITOR_RENAME;
-       } else if (streq("monitor_remove", s)) {
-               *mask = SBSC_MASK_MONITOR_REMOVE;
-       } else if (streq("monitor_focus", s)) {
-               *mask = SBSC_MASK_MONITOR_FOCUS;
-       } else if (streq("monitor_geometry", s)) {
-               *mask = SBSC_MASK_MONITOR_GEOMETRY;
-       } else if (streq("report", s)) {
-               *mask = SBSC_MASK_REPORT;
-       } else {
-               return false;
-       }
-       return true;
-}
index 8603a61e8925aaa2d13a1c94c6cfab730a1cd217..65e3afb1a0f1188ca339587b2cccccbab65a16e9 100644 (file)
 
 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);
+int cmd_node(char **args, int num);
 int cmd_desktop(char **args, int num);
 int cmd_monitor(char **args, int num);
 int cmd_query(char **args, int num, FILE *rsp);
 int cmd_rule(char **args, int num, FILE *rsp);
 int cmd_pointer(char **args, int num);
-int cmd_restore(char **args, int num);
-int cmd_control(char **args, int num, FILE *rsp);
+int cmd_wm(char **args, int num, FILE *rsp);
 int cmd_config(char **args, int num, FILE *rsp);
+int cmd_subscribe(char **args, int num, FILE *rsp);
 int cmd_quit(char **args, int num);
 int set_setting(coordinates_t loc, char *name, char *value);
 int get_setting(coordinates_t loc, char *name, FILE* rsp);
-bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
-bool parse_bool(char *value, bool *b);
-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
index 5de18202ab8e33712fb46d7f04803833b89bee95..a5d8b22bf67c68d8cbf434498d7609b26cbf0dbb 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -25,6 +25,8 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
 #include "bspwm.h"
 #include "desktop.h"
 #include "ewmh.h"
@@ -40,12 +42,13 @@ 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->id = XCB_NONE;
        m->root = XCB_NONE;
        m->prev = m->next = NULL;
        m->desk = m->desk_head = m->desk_tail = NULL;
        m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
        m->wired = true;
-       m->num_sticky = 0;
+       m->sticky_count = 0;
        if (rect != NULL) {
                update_root(m, rect);
        } else {
@@ -83,74 +86,83 @@ void rename_monitor(monitor_t *m, const char *name)
 
 monitor_t *find_monitor(char *name)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               if (streq(m->name, name))
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (streq(m->name, name)) {
                        return m;
+               }
+       }
        return NULL;
 }
 
 monitor_t *get_monitor_by_id(xcb_randr_output_t id)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               if (m->id == id)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (m->id == id) {
                        return m;
+               }
+       }
        return NULL;
 }
 
 void embrace_client(monitor_t *m, client_t *c)
 {
-       if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x)
+       if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x) {
                c->floating_rectangle.x = m->rectangle.x;
-       else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width))
+       } else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width)) {
                c->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;
-       if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y)
+       }
+       if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y) {
                c->floating_rectangle.y = m->rectangle.y;
-       else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height))
+       } else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height)) {
                c->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;
+       }
 }
 
-void translate_client(monitor_t *ms, monitor_t *md, client_t *c)
+void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
 {
-       if (frozen_pointer->action != ACTION_NONE || ms == md)
+       if (frozen_pointer->action != ACTION_NONE) {
                return;
+       }
 
-       /* Clip the rectangle to fit into the monitor.  Without this, the fitting
-        * algorithm doesn't work as expected. This also conserves the
-        * out-of-bounds regions */
-       int left_adjust = MAX((ms->rectangle.x - c->floating_rectangle.x), 0);
-       int top_adjust = MAX((ms->rectangle.y - c->floating_rectangle.y), 0);
-       int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (ms->rectangle.x + ms->rectangle.width), 0);
-       int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (ms->rectangle.y + ms->rectangle.height), 0);
-       c->floating_rectangle.x += left_adjust;
-       c->floating_rectangle.y += top_adjust;
-       c->floating_rectangle.width -= (left_adjust + right_adjust);
-       c->floating_rectangle.height -= (top_adjust + bottom_adjust);
-
-       int dx_s = c->floating_rectangle.x - ms->rectangle.x;
-       int dy_s = c->floating_rectangle.y - ms->rectangle.y;
-
-       int nume_x = dx_s * (md->rectangle.width - c->floating_rectangle.width);
-       int nume_y = dy_s * (md->rectangle.height - c->floating_rectangle.height);
-
-       int deno_x = ms->rectangle.width - c->floating_rectangle.width;
-       int deno_y = ms->rectangle.height - c->floating_rectangle.height;
-
-       int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
-       int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
-
-       /* Translate and undo clipping */
-       c->floating_rectangle.width += left_adjust + right_adjust;
-       c->floating_rectangle.height += top_adjust + bottom_adjust;
-       c->floating_rectangle.x = md->rectangle.x + dx_d - left_adjust;
-       c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               client_t *c = f->client;
+               /* Clip the rectangle to fit into the monitor.  Without this, the fitting
+                * algorithm doesn't work as expected. This also conserves the
+                * out-of-bounds regions */
+               int left_adjust = MAX((rs->x - c->floating_rectangle.x), 0);
+               int top_adjust = MAX((rs->y - c->floating_rectangle.y), 0);
+               int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (rs->x + rs->width), 0);
+               int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (rs->y + rs->height), 0);
+               c->floating_rectangle.x += left_adjust;
+               c->floating_rectangle.y += top_adjust;
+               c->floating_rectangle.width -= (left_adjust + right_adjust);
+               c->floating_rectangle.height -= (top_adjust + bottom_adjust);
+
+               int dx_s = c->floating_rectangle.x - rs->x;
+               int dy_s = c->floating_rectangle.y - rs->y;
+
+               int nume_x = dx_s * (rd->width - c->floating_rectangle.width);
+               int nume_y = dy_s * (rd->height - c->floating_rectangle.height);
+
+               int deno_x = rs->width - c->floating_rectangle.width;
+               int deno_y = rs->height - c->floating_rectangle.height;
+
+               int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
+               int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
+
+               /* Translate and undo clipping */
+               c->floating_rectangle.width += left_adjust + right_adjust;
+               c->floating_rectangle.height += top_adjust + bottom_adjust;
+               c->floating_rectangle.x = rd->x + dx_d - left_adjust;
+               c->floating_rectangle.y = rd->y + dy_d - top_adjust;
+       }
 }
 
 void focus_monitor(monitor_t *m)
 {
-       if (mon == m)
+       if (mon == m) {
                return;
-
-       put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus %s\n", m->name);
+       }
 
        mon = m;
 
@@ -158,16 +170,13 @@ void focus_monitor(monitor_t *m)
                center_pointer(m->rectangle);
        }
 
-       ewmh_update_current_desktop();
-       put_status(SBSC_MASK_REPORT);
+       put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus %s\n", m->name);
 }
 
 void add_monitor(monitor_t *m)
 {
        xcb_rectangle_t r = m->rectangle;
 
-       put_status(SBSC_MASK_MONITOR_ADD, "monitor_add %s 0x%X %ux%u+%i+%i\n", m->name, m->id, r.width, r.height, r.x, r.y);
-
        if (mon == NULL) {
                mon = m;
                mon_head = m;
@@ -177,19 +186,21 @@ void add_monitor(monitor_t *m)
                m->prev = mon_tail;
                mon_tail = m;
        }
+
+       put_status(SBSC_MASK_MONITOR_ADD, "monitor_add %s 0x%X %ux%u+%i+%i\n", m->name, m->id, r.width, r.height, r.x, r.y);
+
+       put_status(SBSC_MASK_REPORT);
 }
 
 void remove_monitor(monitor_t *m)
 {
-       put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove %s\n", m->name);
-
        while (m->desk_head != NULL) {
                remove_desktop(m, m->desk_head);
        }
 
        monitor_t *prev = m->prev;
        monitor_t *next = m->next;
-       monitor_t *last_mon = history_get_monitor(m);
+       monitor_t *last_mon = history_last_monitor(m);
 
        if (prev != NULL) {
                prev->next = next;
@@ -214,12 +225,15 @@ void remove_monitor(monitor_t *m)
        if (mon == m) {
                mon = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
                if (mon != NULL && mon->desk != NULL) {
-                       update_current();
+                       update_focused();
                }
        }
 
+       put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove %s\n", m->name);
+
        xcb_destroy_window(dpy, m->root);
        free(m);
+
        put_status(SBSC_MASK_REPORT);
 }
 
@@ -232,39 +246,47 @@ void merge_monitors(monitor_t *ms, monitor_t *md)
        desktop_t *d = ms->desk_head;
        while (d != NULL) {
                desktop_t *next = d->next;
-               if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
+               if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL) {
                        transfer_desktop(ms, md, d);
+               }
                d = next;
        }
 }
 
 void swap_monitors(monitor_t *m1, monitor_t *m2)
 {
-       if (m1 == NULL || m2 == NULL || m1 == m2)
+       if (m1 == NULL || m2 == NULL || m1 == m2) {
                return;
+       }
 
-       if (mon_head == m1)
+       if (mon_head == m1) {
                mon_head = m2;
-       else if (mon_head == m2)
+       } else if (mon_head == m2) {
                mon_head = m1;
-       if (mon_tail == m1)
+       }
+       if (mon_tail == m1) {
                mon_tail = m2;
-       else if (mon_tail == m2)
+       } else if (mon_tail == m2) {
                mon_tail = m1;
+       }
 
        monitor_t *p1 = m1->prev;
        monitor_t *n1 = m1->next;
        monitor_t *p2 = m2->prev;
        monitor_t *n2 = m2->next;
 
-       if (p1 != NULL && p1 != m2)
+       if (p1 != NULL && p1 != m2) {
                p1->next = m2;
-       if (n1 != NULL && n1 != m2)
+       }
+       if (n1 != NULL && n1 != m2) {
                n1->prev = m2;
-       if (p2 != NULL && p2 != m1)
+       }
+       if (p2 != NULL && p2 != m1) {
                p2->next = m1;
-       if (n2 != NULL && n2 != m1)
+       }
+       if (n2 != NULL && n2 != m1) {
                n2->prev = m1;
+       }
 
        m1->prev = p2 == m1 ? m2 : p2;
        m1->next = n2 == m1 ? m2 : n2;
@@ -274,6 +296,7 @@ void swap_monitors(monitor_t *m1, monitor_t *m2)
        ewmh_update_wm_desktops();
        ewmh_update_desktop_names();
        ewmh_update_current_desktop();
+
        put_status(SBSC_MASK_REPORT);
 }
 
@@ -350,10 +373,10 @@ monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
                        continue;
                }
                xcb_rectangle_t r = f->rectangle;
-               if ((dir == DIR_LEFT && r.x < rect.x) ||
-                   (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
-                   (dir == DIR_UP && r.y < rect.y) ||
-                   (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
+               if ((dir == DIR_WEST && r.x < rect.x) ||
+                   (dir == DIR_EAST && r.x >= (rect.x + rect.width)) ||
+                   (dir == DIR_NORTH && r.y < rect.y) ||
+                   (dir == DIR_SOUTH && r.y >= (rect.y + rect.height))) {
                        int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
                                abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
                        if (d < dmin) {
@@ -368,8 +391,9 @@ monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
 bool update_monitors(void)
 {
        xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
-       if (sres == NULL)
+       if (sres == NULL) {
                return false;
+       }
 
        monitor_t *m, *mm = NULL;
 
@@ -377,11 +401,13 @@ bool update_monitors(void)
        xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);
 
        xcb_randr_get_output_info_cookie_t cookies[len];
-       for (int i = 0; i < len; i++)
+       for (int i = 0; i < len; i++) {
                cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
+       }
 
-       for (m = mon_head; m != NULL; m = m->next)
+       for (m = mon_head; m != NULL; m = m->next) {
                m->wired = false;
+       }
 
        for (int i = 0; i < len; i++) {
                xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
@@ -392,10 +418,11 @@ 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) {
+                                               xcb_rectangle_t last_rect = mm->rectangle;
                                                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);
+                                                               adapt_geometry(&last_rect, &rect, n);
                                                        }
                                                }
                                                arrange(mm, mm->desk);
@@ -412,8 +439,9 @@ bool update_monitors(void)
                                free(cir);
                        } else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
                                m = get_monitor_by_id(outputs[i]);
-                               if (m != NULL)
+                               if (m != NULL) {
                                        m->wired = true;
+                               }
                        }
                }
                free(info);
@@ -424,12 +452,14 @@ bool update_monitors(void)
        if (gpo != NULL) {
                pri_mon = get_monitor_by_id(gpo->output);
                if (!running && pri_mon != NULL) {
-                       if (mon != pri_mon)
+                       if (mon != pri_mon) {
                                mon = pri_mon;
+                       }
                        add_desktop(pri_mon, make_desktop(NULL));
                        ewmh_update_current_desktop();
                }
        }
+
        free(gpo);
 
        /* handle overlapping monitors */
@@ -472,12 +502,15 @@ bool update_monitors(void)
        }
 
        /* add one desktop to each new monitor */
-       for (m = mon_head; m != NULL; m = m->next)
-               if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
+       for (m = mon_head; m != NULL; m = m->next) {
+               if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon)) {
                        add_desktop(m, make_desktop(NULL));
+               }
+       }
 
-       if (!running && pri_mon != NULL && mon_head != pri_mon)
+       if (!running && pri_mon != NULL && mon_head != pri_mon) {
                swap_monitors(mon_head, pri_mon);
+       }
 
        free(sres);
        update_motion_recorder();
index f5fa972ece6c8b8611e1b572e6db28bf193ee49c..10e21642124c43158cc82d832d0204c66502c09d 100644 (file)
--- a/monitor.h
+++ b/monitor.h
@@ -33,7 +33,7 @@ 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 adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n);
 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
index f9e28b7733600cc1b3d4062183425d6a86305960..b57da3c73831bf63f39ebb2810eb08bd0a85020d 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -1,7 +1,8 @@
-#include <errno.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "helpers.h"
+#include <stdbool.h>
+#include <errno.h>
 #include "parse.h"
 
 bool parse_bool(char *value, bool *b)
@@ -87,17 +88,17 @@ bool parse_stack_layer(char *s, stack_layer_t *l)
 
 bool parse_direction(char *s, direction_t *d)
 {
-       if (streq("right", s)) {
-               *d = DIR_RIGHT;
+       if (streq("north", s)) {
+               *d = DIR_NORTH;
                return true;
-       } else if (streq("down", s)) {
-               *d = DIR_DOWN;
+       } else if (streq("west", s)) {
+               *d = DIR_WEST;
                return true;
-       } else if (streq("left", s)) {
-               *d = DIR_LEFT;
+       } else if (streq("south", s)) {
+               *d = DIR_SOUTH;
                return true;
-       } else if (streq("up", s)) {
-               *d = DIR_UP;
+       } else if (streq("east", s)) {
+               *d = DIR_EAST;
                return true;
        }
        return false;
@@ -197,15 +198,16 @@ bool parse_degree(char *s, int *d)
        }
 }
 
-bool parse_window_id(char *s, long int *i)
+bool parse_id(char *s, uint32_t *i)
 {
        char *end;
        errno = 0;
-       long int ret = strtol(s, &end, 0);
-       if (errno != 0 || *end != '\0')
+       uint32_t ret = strtol(s, &end, 0);
+       if (errno != 0 || *end != '\0') {
                return false;
-       else
+       } else {
                *i = ret;
+       }
        return true;
 }
 
@@ -230,8 +232,171 @@ bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *sta
 bool parse_index(char *s, int *i)
 {
        int idx;
-       if (sscanf(s, "^%i", &idx) != 1 || idx < 1)
+       if (sscanf(s, "^%i", &idx) != 1 || idx < 1) {
                return false;
+       }
        *i = idx;
        return true;
 }
+
+bool parse_rectangle(char *s, xcb_rectangle_t *r)
+{
+       uint16_t w, h;
+       int16_t x, y;
+       if (sscanf(s, "%hux%hu+%hi+%hi", &w, &h, &x, &y) != 4) {
+               return false;
+       }
+       r->width = w;
+       r->height = h;
+       r->x = x;
+       r->y = y;
+       return true;
+}
+
+bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
+{
+       if (streq("all", s)) {
+               *mask = SBSC_MASK_ALL;
+       } else if (streq("node", s)) {
+               *mask = SBSC_MASK_NODE;
+       } else if (streq("desktop", s)) {
+               *mask = SBSC_MASK_DESKTOP;
+       } else if (streq("monitor", s)) {
+               *mask = SBSC_MASK_MONITOR;
+       } else if (streq("node_manage", s)) {
+               *mask = SBSC_MASK_NODE_MANAGE;
+       } else if (streq("node_unmanage", s)) {
+               *mask = SBSC_MASK_NODE_UNMANAGE;
+       } else if (streq("node_swap", s)) {
+               *mask = SBSC_MASK_NODE_SWAP;
+       } else if (streq("node_transfer", s)) {
+               *mask = SBSC_MASK_NODE_TRANSFER;
+       } else if (streq("node_focus", s)) {
+               *mask = SBSC_MASK_NODE_FOCUS;
+       } else if (streq("node_presel", s)) {
+               *mask = SBSC_MASK_NODE_PRESEL;
+       } else if (streq("node_stack", s)) {
+               *mask = SBSC_MASK_NODE_STACK;
+       } else if (streq("node_activate", s)) {
+               *mask = SBSC_MASK_NODE_ACTIVATE;
+       } else if (streq("node_geometry", s)) {
+               *mask = SBSC_MASK_NODE_GEOMETRY;
+       } else if (streq("node_state", s)) {
+               *mask = SBSC_MASK_NODE_STATE;
+       } else if (streq("node_flag", s)) {
+               *mask = SBSC_MASK_NODE_FLAG;
+       } else if (streq("node_layer", s)) {
+               *mask = SBSC_MASK_NODE_LAYER;
+       } else if (streq("desktop_add", s)) {
+               *mask = SBSC_MASK_DESKTOP_ADD;
+       } else if (streq("desktop_rename", s)) {
+               *mask = SBSC_MASK_DESKTOP_RENAME;
+       } else if (streq("desktop_remove", s)) {
+               *mask = SBSC_MASK_DESKTOP_REMOVE;
+       } else if (streq("desktop_swap", s)) {
+               *mask = SBSC_MASK_DESKTOP_SWAP;
+       } else if (streq("desktop_transfer", s)) {
+               *mask = SBSC_MASK_DESKTOP_TRANSFER;
+       } else if (streq("desktop_focus", s)) {
+               *mask = SBSC_MASK_DESKTOP_FOCUS;
+       } else if (streq("desktop_activate", s)) {
+               *mask = SBSC_MASK_DESKTOP_ACTIVATE;
+       } else if (streq("desktop_layout", s)) {
+               *mask = SBSC_MASK_DESKTOP_LAYOUT;
+       } else if (streq("monitor_add", s)) {
+               *mask = SBSC_MASK_MONITOR_ADD;
+       } else if (streq("monitor_rename", s)) {
+               *mask = SBSC_MASK_MONITOR_RENAME;
+       } else if (streq("monitor_remove", s)) {
+               *mask = SBSC_MASK_MONITOR_REMOVE;
+       } else if (streq("monitor_focus", s)) {
+               *mask = SBSC_MASK_MONITOR_FOCUS;
+       } else if (streq("monitor_geometry", s)) {
+               *mask = SBSC_MASK_MONITOR_GEOMETRY;
+       } else if (streq("report", s)) {
+               *mask = SBSC_MASK_REPORT;
+       } else {
+               return false;
+       }
+       return true;
+}
+
+
+#define GET_MOD(k) \
+       } else if (streq(#k, tok)) { \
+               sel->k = OPTION_TRUE; \
+       } else if (streq("!" #k, tok)) { \
+               sel->k = OPTION_FALSE;
+
+bool parse_monitor_modifiers(char *desc, monitor_select_t *sel)
+{
+       char *tok;
+       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+               tok[0] = '\0';
+               tok++;
+               if (streq("occupied", tok)) {
+                       sel->occupied = OPTION_TRUE;
+               } else if (streq("!occupied", tok)) {
+                       sel->occupied = OPTION_FALSE;
+               GET_MOD(focused)
+               } else {
+                       return false;
+               }
+       }
+       return true;
+}
+
+bool parse_desktop_modifiers(char *desc, desktop_select_t *sel)
+{
+       char *tok;
+       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+               tok[0] = '\0';
+               tok++;
+               if (streq("occupied", tok)) {
+                       sel->occupied = OPTION_TRUE;
+               } else if (streq("!occupied", tok)) {
+                       sel->occupied = OPTION_FALSE;
+               GET_MOD(focused)
+               GET_MOD(urgent)
+               GET_MOD(local)
+               } else {
+                       return false;
+               }
+       }
+       return true;
+
+}
+
+bool parse_node_modifiers(char *desc, node_select_t *sel)
+{
+       char *tok;
+       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+               tok[0] = '\0';
+               tok++;
+               if (streq("tiled", tok)) {
+                       sel->tiled = OPTION_TRUE;
+               } else if (streq("!tiled", tok)) {
+                       sel->tiled = OPTION_FALSE;
+               GET_MOD(automatic)
+               GET_MOD(focused)
+               GET_MOD(local)
+               GET_MOD(leaf)
+               GET_MOD(pseudo_tiled)
+               GET_MOD(floating)
+               GET_MOD(fullscreen)
+               GET_MOD(locked)
+               GET_MOD(sticky)
+               GET_MOD(private)
+               GET_MOD(urgent)
+               GET_MOD(same_class)
+               GET_MOD(below)
+               GET_MOD(normal)
+               GET_MOD(above)
+               } else {
+                       return false;
+               }
+       }
+       return true;
+}
+
+#undef GET_MOD
diff --git a/parse.h b/parse.h
index 19e9adbc890de5ab340e7673b8f28b2ec06b05dc..81dd3b9b76e83f9bc7679f51e6219a071abbcf98 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -2,10 +2,12 @@
 #define BSPWM_PARSE_H
 
 #include "types.h"
+#include "subscribe.h"
 
 #define OPT_CHR  '-'
 #define CAT_CHR  '.'
 #define EQL_TOK  "="
+#define COL_TOK  ":"
 
 bool parse_bool(char *value, bool *b);
 bool parse_split_type(char *s, split_type_t *t);
@@ -21,8 +23,13 @@ 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_id(char *s, uint32_t *i);
 bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
 bool parse_index(char *s, int *i);
+bool parse_rectangle(char *s, xcb_rectangle_t *r);
+bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
+bool parse_monitor_modifiers(char *desc, monitor_select_t *sel);
+bool parse_desktop_modifiers(char *desc, desktop_select_t *sel);
+bool parse_node_modifiers(char *desc, node_select_t *sel);
 
 #endif
index b1efe83a333c7cbbc86dd4380aac0012a05d0bf1..aa00b40828b88c8f2a85f7a71df700d7181853c8 100644 (file)
--- a/pointer.c
+++ b/pointer.c
@@ -22,6 +22,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdbool.h>
 #include "bspwm.h"
 #include "query.h"
 #include "settings.h"
@@ -30,7 +31,6 @@
 #include "monitor.h"
 #include "subscribe.h"
 #include "window.h"
-#include "pointer.h"
 
 void grab_pointer(pointer_action_t pac)
 {
@@ -41,15 +41,15 @@ void grab_pointer(pointer_action_t pac)
 
        coordinates_t loc;
        if (locate_window(win, &loc)) {
-               client_t *c = NULL;
+               client_t *c = loc.node->client;
+
                frozen_pointer->position = pos;
                frozen_pointer->action = pac;
-               c = loc.node->client;
                frozen_pointer->monitor = loc.monitor;
                frozen_pointer->desktop = loc.desktop;
                frozen_pointer->node = loc.node;
                frozen_pointer->client = c;
-               frozen_pointer->window = c->window;
+               frozen_pointer->window = loc.node->id;
                frozen_pointer->horizontal_fence = NULL;
                frozen_pointer->vertical_fence = NULL;
 
@@ -61,7 +61,7 @@ void grab_pointer(pointer_action_t pac)
                                        focus_node(loc.monitor, loc.desktop, loc.node);
                                        pointer_follows_monitor = backup;
                                } else if (focus_follows_pointer) {
-                                       stack(loc.node, true);
+                                       stack(loc.desktop, loc.node, true);
                                }
                                frozen_pointer->action = ACTION_NONE;
                                break;
@@ -87,71 +87,77 @@ void grab_pointer(pointer_action_t pac)
                                        float diag_a = ratio * y;
                                        float diag_b = W - diag_a;
                                        if (x < diag_a) {
-                                               if (x < diag_b)
+                                               if (x < diag_b) {
                                                        frozen_pointer->side = SIDE_LEFT;
-                                               else
+                                               } else {
                                                        frozen_pointer->side = SIDE_BOTTOM;
+                                               }
                                        } else {
-                                               if (x < diag_b)
+                                               if (x < diag_b) {
                                                        frozen_pointer->side = SIDE_TOP;
-                                               else
+                                               } else {
                                                        frozen_pointer->side = SIDE_RIGHT;
+                                               }
                                        }
                                } else if (pac == ACTION_RESIZE_CORNER) {
                                        int16_t mid_x = frozen_pointer->rectangle.x + (frozen_pointer->rectangle.width / 2);
                                        int16_t mid_y = frozen_pointer->rectangle.y + (frozen_pointer->rectangle.height / 2);
                                        if (pos.x > mid_x) {
-                                               if (pos.y > mid_y)
+                                               if (pos.y > mid_y) {
                                                        frozen_pointer->corner = CORNER_BOTTOM_RIGHT;
-                                               else
+                                               } else {
                                                        frozen_pointer->corner = CORNER_TOP_RIGHT;
+                                               }
                                        } else {
-                                               if (pos.y > mid_y)
+                                               if (pos.y > mid_y) {
                                                        frozen_pointer->corner = CORNER_BOTTOM_LEFT;
-                                               else
+                                               } else {
                                                        frozen_pointer->corner = CORNER_TOP_LEFT;
+                                               }
                                        }
                                }
                                if (frozen_pointer->is_tiled) {
                                        if (pac == ACTION_RESIZE_SIDE) {
                                                switch (frozen_pointer->side) {
                                                        case SIDE_TOP:
-                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
+                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
                                                                break;
                                                        case SIDE_RIGHT:
-                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
+                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
                                                                break;
                                                        case SIDE_BOTTOM:
-                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
+                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
                                                                break;
                                                        case SIDE_LEFT:
-                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
+                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
                                                                break;
                                                }
                                        } else if (pac == ACTION_RESIZE_CORNER) {
                                                switch (frozen_pointer->corner) {
                                                        case CORNER_TOP_LEFT:
-                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
-                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
+                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
+                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
                                                                break;
                                                        case CORNER_TOP_RIGHT:
-                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
-                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
+                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
+                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
                                                                break;
                                                        case CORNER_BOTTOM_RIGHT:
-                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
-                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
+                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
+                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
                                                                break;
                                                        case CORNER_BOTTOM_LEFT:
-                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
-                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
+                                                               frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
+                                                               frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
                                                                break;
                                                }
                                        }
-                                       if (frozen_pointer->horizontal_fence != NULL)
+                                       if (frozen_pointer->horizontal_fence != NULL) {
                                                frozen_pointer->horizontal_ratio = frozen_pointer->horizontal_fence->split_ratio;
-                                       if (frozen_pointer->vertical_fence != NULL)
+                                       }
+                                       if (frozen_pointer->vertical_fence != NULL) {
                                                frozen_pointer->vertical_ratio = frozen_pointer->vertical_fence->split_ratio;
+                                       }
                                }
                                break;
                        case ACTION_NONE:
@@ -160,8 +166,9 @@ void grab_pointer(pointer_action_t pac)
        } else {
                if (pac == ACTION_FOCUS) {
                        monitor_t *m = monitor_from_point(pos);
-                       if (m != NULL && m != mon)
+                       if (m != NULL && m != mon) {
                                focus_node(m, m->desk, m->desk->focus);
+                       }
                }
                frozen_pointer->action = ACTION_NONE;
        }
@@ -169,8 +176,9 @@ void grab_pointer(pointer_action_t pac)
 
 void track_pointer(int root_x, int root_y)
 {
-       if (frozen_pointer->action == ACTION_NONE)
+       if (frozen_pointer->action == ACTION_NONE) {
                return;
+       }
 
        int delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
 
@@ -192,13 +200,13 @@ void track_pointer(int root_x, int root_y)
                        if (frozen_pointer->is_tiled) {
                                xcb_window_t pwin = XCB_NONE;
                                query_pointer(&pwin, NULL);
-                               if (pwin == win)
+                               if (pwin == win) {
                                        return;
+                               }
                                coordinates_t loc;
                                bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
                                if (is_managed && !IS_FLOATING(loc.node->client) && loc.monitor == m) {
                                        swap_nodes(m, d, n, m, d, loc.node);
-                                       arrange(m, d);
                                } else {
                                        if (is_managed && loc.monitor == m) {
                                                return;
@@ -214,8 +222,9 @@ void track_pointer(int root_x, int root_y)
                                        }
                                        bool focused = (n == mon->desk->focus);
                                        transfer_node(m, d, n, loc.monitor, loc.desktop, loc.desktop->focus);
-                                       if (focused)
+                                       if (focused) {
                                                focus_node(loc.monitor, loc.desktop, n);
+                                       }
                                        frozen_pointer->monitor = loc.monitor;
                                        frozen_pointer->desktop = loc.desktop;
                                }
@@ -227,12 +236,14 @@ void track_pointer(int root_x, int root_y)
                                c->floating_rectangle.y = y;
                                xcb_point_t pt = (xcb_point_t) {root_x, root_y};
                                monitor_t *pmon = monitor_from_point(pt);
-                               if (pmon == NULL || pmon == m)
+                               if (pmon == NULL || pmon == m) {
                                        return;
+                               }
                                bool focused = (n == mon->desk->focus);
                                transfer_node(m, d, n, pmon, pmon->desk, pmon->desk->focus);
-                               if (focused)
+                               if (focused) {
                                        focus_node(pmon, pmon->desk, n);
+                               }
                                frozen_pointer->monitor = pmon;
                                frozen_pointer->desktop = pmon->desk;
                        }
@@ -342,8 +353,8 @@ void track_pointer(int root_x, int root_y)
 void ungrab_pointer(void)
 {
        if (frozen_pointer->action != ACTION_NONE) {
-               xcb_rectangle_t r = get_rectangle(frozen_pointer->monitor, frozen_pointer->client);
-               put_status(SBSC_MASK_WINDOW_GEOMETRY, "window_geometry %s %s 0x%X %ux%u+%i+%i\n", frozen_pointer->monitor->name, frozen_pointer->desktop->name, frozen_pointer->window, r.width, r.height, r.x, r.y);
+               xcb_rectangle_t r = get_rectangle(frozen_pointer->monitor, frozen_pointer->desktop, frozen_pointer->node);
+               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry %s %s 0x%X %ux%u+%i+%i\n", frozen_pointer->monitor->name, frozen_pointer->desktop->name, frozen_pointer->window, r.width, r.height, r.x, r.y);
        }
        frozen_pointer->action = ACTION_NONE;
 }
diff --git a/query.c b/query.c
index 8ea4edc835e404b7825479ac88eb537544820d6c..d0f997b3a2cf38248dd1815be78fe41cb35f72e5 100644 (file)
--- a/query.c
+++ b/query.c
@@ -23,8 +23,6 @@
  */
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
 #include <string.h>
 #include "bspwm.h"
 #include "desktop.h"
 #include "monitor.h"
 #include "tree.h"
 #include "query.h"
-#include "jsmn.h"
 
 void query_tree(FILE *rsp)
 {
        fprintf(rsp, "{");
        fprintf(rsp, "\"focusedMonitorName\":\"%s\",", mon->name);
-       fprintf(rsp, "\"numClients\":%i,", num_clients);
+       fprintf(rsp, "\"clientsCount\":%i,", clients_count);
        fprintf(rsp, "\"monitors\":");
        fprintf(rsp, "[");
        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
@@ -49,6 +46,12 @@ void query_tree(FILE *rsp)
                }
        }
        fprintf(rsp, "]");
+       fprintf(rsp,",");
+       fprintf(rsp, "\"focusHistory\":");
+       query_history(rsp);
+       fprintf(rsp,",");
+       fprintf(rsp, "\"stackingList\":");
+       query_stack(rsp);
        fprintf(rsp, "}");
 
 }
@@ -63,7 +66,7 @@ void query_monitor(monitor_t *m, FILE *rsp)
        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, "\"stickyCount\":%i,", m->sticky_count);
        fprintf(rsp, "\"rectangle\":");
        query_rectangle(m->rectangle, rsp);
        fprintf(rsp,",");
@@ -91,7 +94,7 @@ void query_desktop(desktop_t *d, FILE *rsp)
        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, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0);
        fprintf(rsp, "\"root\":");
        query_node(d->root, rsp);
        fprintf(rsp, "}");
@@ -103,13 +106,17 @@ void query_node(node_t *n, FILE *rsp)
                fprintf(rsp, "null");
        } else {
                fprintf(rsp, "{");
+               fprintf(rsp, "\"id\":%u,", n->id);
                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, "\"sticky\":%s,", BOOL_STR(n->sticky));
+               fprintf(rsp, "\"private\":%s,", BOOL_STR(n->private));
+               fprintf(rsp, "\"locked\":%s,", BOOL_STR(n->locked));
+               fprintf(rsp, "\"presel\":");
+               query_presel(n->presel, rsp);
+               fprintf(rsp,",");
                fprintf(rsp, "\"rectangle\":");
                query_rectangle(n->rectangle, rsp);
                fprintf(rsp,",");
@@ -125,13 +132,21 @@ void query_node(node_t *n, FILE *rsp)
        }
 }
 
+void query_presel(presel_t *p, FILE *rsp)
+{
+       if (p == NULL) {
+               fprintf(rsp, "null");
+       } else {
+               fprintf(rsp, "{\"splitDir\":\"%s\",\"splitRatio\":%lf}", SPLIT_DIR_STR(p->split_dir), p->split_ratio);
+       }
+}
+
 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);
@@ -139,19 +154,16 @@ void query_client(client_t *c, FILE *rsp)
                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, "\"wmStatesCount\":%i,", c->wm_states_count);
                fprintf(rsp, "\"wmState\":");
-               query_wm_state(c->wm_state, c->num_states, rsp);
+               query_wm_state(c->wm_state, c->wm_states_count, rsp);
                fprintf(rsp,",");
                fprintf(rsp, "\"tiledRectangle\":");
                query_rectangle(c->tiled_rectangle, rsp);
@@ -167,80 +179,116 @@ 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);
 }
 
-void query_wm_state(xcb_atom_t *wm_state, int num_states, FILE *rsp)
+void query_wm_state(xcb_atom_t *wm_state, int wm_states_count, FILE *rsp)
 {
        fprintf(rsp, "[");
-       for (int i = 0; i < num_states; i++) {
+       for (int i = 0; i < wm_states_count; i++) {
                fprintf(rsp, "%u", wm_state[i]);
-               if (i < num_states - 1) {
+               if (i < wm_states_count - 1) {
                        fprintf(rsp, ",");
                }
        }
        fprintf(rsp, "]");
 }
 
-void query_history(coordinates_t loc, FILE *rsp)
+void query_history(FILE *rsp)
 {
+       fprintf(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)) {
-                       continue;
-               }
-               xcb_window_t win = XCB_NONE;
-               if (h->loc.node != NULL) {
-                       win = h->loc.node->client->window;
+               query_coordinates(&h->loc, rsp);
+               if (h->next != NULL) {
+                       fprintf(rsp, ",");
                }
-               fprintf(rsp, "%s %s 0x%X\n", h->loc.monitor->name, h->loc.desktop->name, win);
        }
+       fprintf(rsp, "]");
+}
+
+void query_coordinates(coordinates_t *loc, FILE *rsp)
+{
+       fprintf(rsp, "{\"monitorName\":\"%s\",\"desktopName\":\"%s\",\"nodeId\":%u}", loc->monitor->name, loc->desktop->name, loc->node!=NULL?loc->node->id:0);
 }
 
 void query_stack(FILE *rsp)
 {
+       fprintf(rsp, "[");
        for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
-               fprintf(rsp, "0x%X\n", s->node->client->window);
+               fprintf(rsp, "%u", s->node->id);
+               if (s->next != NULL) {
+                       fprintf(rsp, ",");
+               }
        }
+       fprintf(rsp, "]");
 }
 
-void query_windows(coordinates_t loc, FILE *rsp)
+void query_node_ids(coordinates_t loc, node_select_t *sel, FILE *rsp)
 {
        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (loc.monitor != NULL && m != loc.monitor)
+               if (loc.monitor != NULL && m != loc.monitor) {
                        continue;
+               }
                for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       if (loc.desktop != NULL && d != loc.desktop)
+                       if (loc.desktop != NULL && d != loc.desktop) {
                                continue;
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                               if (loc.node != NULL && n != loc.node)
-                                       continue;
-                               fprintf(rsp, "0x%X\n", n->client->window);
                        }
+                       query_node_ids_in(d->root, d, m, loc, sel, rsp);
                }
        }
 }
 
-void query_names(domain_t dom, coordinates_t loc, FILE *rsp)
+void query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t loc, node_select_t *sel, FILE *rsp)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+               coordinates_t trg = {m, d, n};
+               if ((loc.node == NULL || n == loc.node) &&
+                   (sel == NULL || node_matches(&trg, &ref, *sel))) {
+                       fprintf(rsp, "0x%07X\n", n->id);
+               }
+               query_node_ids_in(n->first_child, d, m, loc, sel, rsp);
+               query_node_ids_in(n->second_child, d, m, loc, sel, rsp);
+       }
+}
+
+void query_desktop_names(coordinates_t loc, desktop_select_t *sel, 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) {
+                       coordinates_t ref = {mon, mon->desk, NULL};
+                       coordinates_t trg = {m, d, NULL};
+                       if ((loc.desktop != NULL && d != loc.desktop) ||
+                           (sel != NULL && !desktop_matches(&trg, &ref, *sel))) {
                                continue;
                        }
-                       if (dom == DOMAIN_DESKTOP) {
-                               fprintf(rsp, "%s\n", d->name);
-                       }
+                       fprintf(rsp, "%s\n", d->name);
                }
        }
 }
 
-client_select_t make_client_select(void)
+void query_monitor_names(coordinates_t loc, monitor_select_t *sel, FILE *rsp)
 {
-       client_select_t sel = {
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               coordinates_t ref = {mon, NULL, NULL};
+               coordinates_t trg = {m, NULL, NULL};
+               if ((loc.monitor != NULL && m != loc.monitor) ||
+                       (sel != NULL && !monitor_matches(&trg, &ref, *sel))) {
+                       continue;
+               }
+               fprintf(rsp, "%s\n", m->name);
+       }
+}
+
+node_select_t make_node_select(void)
+{
+       node_select_t sel = {
+               .automatic = OPTION_NONE,
+               .focused = OPTION_NONE,
+               .local = OPTION_NONE,
+               .leaf = OPTION_NONE,
                .tiled = OPTION_NONE,
                .pseudo_tiled = OPTION_NONE,
                .floating = OPTION_NONE,
@@ -250,9 +298,6 @@ client_select_t make_client_select(void)
                .private = OPTION_NONE,
                .urgent = OPTION_NONE,
                .same_class = OPTION_NONE,
-               .automatic = OPTION_NONE,
-               .local = OPTION_NONE,
-               .focused = OPTION_NONE,
                .below = OPTION_NONE,
                .normal = OPTION_NONE,
                .above = OPTION_NONE
@@ -280,40 +325,12 @@ monitor_select_t make_monitor_select(void)
        return sel;
 }
 
-#define GET_MOD(k) \
-       } else if (streq(#k, tok)) { \
-               sel.k = OPTION_TRUE; \
-       } else if (streq("!" #k, tok)) { \
-               sel.k = OPTION_FALSE;
-
 bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
 {
-       client_select_t sel = make_client_select();
-       char *tok;
-       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
-               tok[0] = '\0';
-               tok++;
-               if (streq("tiled", tok)) {
-                       sel.tiled = OPTION_TRUE;
-               } else if (streq("!tiled", tok)) {
-                       sel.tiled = OPTION_FALSE;
-               GET_MOD(pseudo_tiled)
-               GET_MOD(floating)
-               GET_MOD(fullscreen)
-               GET_MOD(locked)
-               GET_MOD(sticky)
-               GET_MOD(private)
-               GET_MOD(urgent)
-               GET_MOD(same_class)
-               GET_MOD(automatic)
-               GET_MOD(local)
-               GET_MOD(focused)
-               GET_MOD(below)
-               GET_MOD(normal)
-               GET_MOD(above)
-               } else {
-                       return false;
-               }
+       node_select_t sel = make_node_select();
+
+       if (!parse_node_modifiers(desc, &sel)) {
+               return false;
        }
 
        dst->monitor = ref->monitor;
@@ -351,9 +368,44 @@ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
                        dst->desktop = mon->desk;
                        dst->node = mon->desk->focus;
                }
+       } else if (*desc == '@') {
+               desc++;
+               char *colon;
+               if ((colon = strrchr(desc, ':')) != NULL) {
+                       *colon = '\0';
+                       if (desktop_from_desc(desc, ref, dst)) {
+                               desc = colon + 1;
+                       } else {
+                               return false;
+                       }
+               }
+               dst->node = (*desc == '/' ? dst->desktop->root : dst->desktop->focus);
+               char *move = strtok(desc, PTH_TOK);
+               while (move != NULL && dst->node != NULL) {
+                       if (streq("first", move) || streq("1", move)) {
+                               dst->node = dst->node->first_child;
+                       } else if (streq("second", move) || streq("2", move)) {
+                               dst->node = dst->node->second_child;
+                       } else if (streq("parent", move)) {
+                               dst->node = dst->node->parent;
+                       } else if (streq("brother", move)) {
+                               dst->node = brother_tree(dst->node);
+                       } else {
+                               direction_t dir;
+                               if (parse_direction(move, &dir)) {
+                                       dst->node = find_fence(dst->node, dir);
+                               } else {
+                                       return false;
+                               }
+                       }
+                       move = strtok(NULL, PTH_TOK);
+               }
+               if (dst->node != NULL) {
+                       return node_matches(dst, ref, sel);
+               }
        } else {
-               long int wid;
-               if (parse_window_id(desc, &wid) && locate_window(wid, dst)) {
+               uint32_t id;
+               if (parse_id(desc, &id) && find_by_id(id, dst)) {
                        return node_matches(dst, ref, sel);
                }
        }
@@ -364,20 +416,9 @@ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
 bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
 {
        desktop_select_t sel = make_desktop_select();
-       char *tok;
-       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
-               tok[0] = '\0';
-               tok++;
-               if (streq("occupied", tok)) {
-                       sel.occupied = OPTION_TRUE;
-               } else if (streq("!occupied", tok)) {
-                       sel.occupied = OPTION_FALSE;
-               GET_MOD(focused)
-               GET_MOD(urgent)
-               GET_MOD(local)
-               } else {
-                       return false;
-               }
+
+       if (!parse_desktop_modifiers(desc, &sel)) {
+               return false;
        }
 
        dst->desktop = NULL;
@@ -429,18 +470,9 @@ bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
 bool monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
 {
        monitor_select_t sel = make_monitor_select();
-       char *tok;
-       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
-               tok[0] = '\0';
-               tok++;
-               if (streq("occupied", tok)) {
-                       sel.occupied = OPTION_TRUE;
-               } else if (streq("!occupied", tok)) {
-                       sel.occupied = OPTION_FALSE;
-               GET_MOD(focused)
-               } else {
-                       return false;
-               }
+
+       if (!parse_monitor_modifiers(desc, &sel)) {
+               return false;
        }
 
        dst->monitor = NULL;
@@ -482,127 +514,94 @@ bool monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
        return (dst->monitor != NULL);
 }
 
-#undef GET_MOD
-
 bool locate_window(xcb_window_t win, coordinates_t *loc)
 {
-       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))
-                               if (n->client->window == win) {
+       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)) {
+                               if (n->id == win) {
                                        loc->monitor = m;
                                        loc->desktop = d;
                                        loc->node = n;
                                        return true;
                                }
+                       }
+               }
+       }
        return false;
 }
 
 bool locate_desktop(char *name, coordinates_t *loc)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
                        if (streq(d->name, name)) {
                                loc->monitor = m;
                                loc->desktop = d;
                                return true;
                        }
+               }
+       }
        return false;
 }
 
 bool locate_monitor(char *name, coordinates_t *loc)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
                if (streq(m->name, name)) {
                        loc->monitor = m;
                        return true;
                }
+       }
        return false;
 }
 
 bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm)
 {
        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (mm != NULL && m != mm)
+               if (mm != NULL && m != mm) {
                        continue;
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--)
+               }
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
                        if (i == 1) {
                                loc->monitor = m;
                                loc->desktop = d;
                                loc->node = NULL;
                                return true;
                        }
+               }
        }
        return false;
 }
 
 bool monitor_from_index(int i, coordinates_t *loc)
 {
-       for (monitor_t *m = mon_head; m != NULL; m = m->next, i--)
+       for (monitor_t *m = mon_head; m != NULL; m = m->next, i--) {
                if (i == 1) {
                        loc->monitor = m;
                        loc->desktop = NULL;
                        loc->node = NULL;
                        return true;
                }
+       }
        return false;
 }
 
-bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel)
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
 {
-       if (loc->node == NULL)
-               return false;
-
-#define WSTATE(prop) \
-       if (sel.prop != OPTION_NONE && \
-           !loc->node->client->prop \
-           ? sel.prop == OPTION_TRUE \
-           : sel.prop == OPTION_FALSE) { \
-               return false; \
-       }
-       WSTATE(locked)
-       WSTATE(sticky)
-       WSTATE(private)
-       WSTATE(urgent)
-#undef MATCHSTATE
-
-       if (sel.tiled != OPTION_NONE &&
-           loc->node->client->state != STATE_TILED
-           ? sel.tiled == OPTION_TRUE
-           : sel.tiled == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.pseudo_tiled != OPTION_NONE &&
-           loc->node->client->state != STATE_PSEUDO_TILED
-           ? sel.pseudo_tiled == OPTION_TRUE
-           : sel.pseudo_tiled == OPTION_FALSE) {
+       if (loc->node == NULL) {
                return false;
        }
 
-       if (sel.floating != OPTION_NONE &&
-           loc->node->client->state != STATE_FLOATING
-           ? sel.floating == OPTION_TRUE
-           : sel.floating == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.fullscreen != OPTION_NONE &&
-           loc->node->client->state != STATE_FULLSCREEN
-           ? sel.fullscreen == OPTION_TRUE
-           : sel.fullscreen == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.same_class != OPTION_NONE && ref->node != NULL &&
-           streq(loc->node->client->class_name, ref->node->client->class_name)
-           ? sel.same_class == OPTION_FALSE
-           : sel.same_class == OPTION_TRUE) {
+       if (sel.focused != OPTION_NONE &&
+           loc->node != mon->desk->focus
+           ? sel.focused == OPTION_TRUE
+           : sel.focused == OPTION_FALSE) {
                return false;
        }
 
        if (sel.automatic != OPTION_NONE &&
-           loc->node->split_mode == MODE_MANUAL
+           loc->node->presel != NULL
            ? sel.automatic == OPTION_TRUE
            : sel.automatic == OPTION_FALSE) {
                return false;
@@ -615,33 +614,80 @@ bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel)
                return false;
        }
 
-       if (sel.below != OPTION_NONE &&
-           loc->node->client->layer != LAYER_BELOW
-           ? sel.below == OPTION_TRUE
-           : sel.below == OPTION_FALSE) {
+       if (sel.leaf != OPTION_NONE &&
+           loc->node->client == NULL
+           ? sel.leaf == OPTION_TRUE
+           : sel.leaf == OPTION_FALSE) {
                return false;
        }
 
-       if (sel.normal != OPTION_NONE &&
-           loc->node->client->layer != LAYER_NORMAL
-           ? sel.normal == OPTION_TRUE
-           : sel.normal == OPTION_FALSE) {
+#define NFLAG(p) \
+       if (sel.p != OPTION_NONE && \
+           !loc->node->p \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       NFLAG(sticky)
+       NFLAG(private)
+       NFLAG(locked)
+#undef WFLAG
+
+       if (loc->node->client == NULL &&
+               (sel.same_class != OPTION_NONE ||
+                sel.tiled != OPTION_NONE ||
+                sel.pseudo_tiled != OPTION_NONE ||
+                sel.floating != OPTION_NONE ||
+                sel.fullscreen != OPTION_NONE ||
+                sel.below != OPTION_NONE ||
+                sel.normal != OPTION_NONE ||
+                sel.above != OPTION_NONE ||
+                sel.urgent != OPTION_NONE)) {
                return false;
        }
 
-       if (sel.above != OPTION_NONE &&
-           loc->node->client->layer != LAYER_ABOVE
-           ? sel.above == OPTION_TRUE
-           : sel.above == OPTION_FALSE) {
+       if (ref->node != NULL && ref->node->client != NULL &&
+           sel.same_class != OPTION_NONE &&
+           streq(loc->node->client->class_name, ref->node->client->class_name)
+           ? sel.same_class == OPTION_FALSE
+           : sel.same_class == OPTION_TRUE) {
                return false;
        }
 
-       if (sel.focused != OPTION_NONE &&
-           loc->node != mon->desk->focus
-           ? sel.focused == OPTION_TRUE
-           : sel.focused == OPTION_FALSE) {
-               return false;
+#define WSTATE(p, e) \
+       if (sel.p != OPTION_NONE && \
+           loc->node->client->state != e \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       WSTATE(tiled, STATE_TILED)
+       WSTATE(pseudo_tiled, STATE_PSEUDO_TILED)
+       WSTATE(floating, STATE_FLOATING)
+       WSTATE(fullscreen, STATE_FULLSCREEN)
+#undef WSTATE
+
+#define WLAYER(p, e) \
+       if (sel.p != OPTION_NONE && \
+           loc->node->client->layer != e \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       WLAYER(below, LAYER_BELOW)
+       WLAYER(normal, LAYER_NORMAL)
+       WLAYER(above, LAYER_ABOVE)
+#undef WLAYER
+
+#define WFLAG(p) \
+       if (sel.p != OPTION_NONE && \
+           !loc->node->client->p \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
        }
+       WFLAG(urgent)
+#undef WFLAG
 
        return true;
 }
diff --git a/query.h b/query.h
index 0c25aae4c20f2dd6b8f7cc4f1b40d9518a39a56a..cb0ea8861d6b106dd151fc05296ee2cb09828c7d 100644 (file)
--- a/query.h
+++ b/query.h
 #ifndef BSPWM_QUERY_H
 #define BSPWM_QUERY_H
 
+#define PTH_TOK  "/"
+
 typedef enum {
+       DOMAIN_TREE,
        DOMAIN_MONITOR,
        DOMAIN_DESKTOP,
-       DOMAIN_WINDOW,
-       DOMAIN_TREE,
-       DOMAIN_HISTORY,
-       DOMAIN_STACK
+       DOMAIN_NODE
 } domain_t;
 
 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_presel(presel_t *p, 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_wm_state(xcb_atom_t *wm_state, int wm_states_count, FILE *rsp);
+void query_history(FILE *rsp);
+void query_coordinates(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);
+void query_node_ids(coordinates_t loc, node_select_t *sel, FILE *rsp);
+void query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t loc, node_select_t *sel, FILE *rsp);
+void query_desktop_names(coordinates_t loc, desktop_select_t *sel, FILE *rsp);
+void query_monitor_names(coordinates_t loc, monitor_select_t *sel, FILE *rsp);
+node_select_t make_node_select(void);
 desktop_select_t make_desktop_select(void);
 monitor_select_t make_monitor_select(void);
 bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
@@ -56,7 +60,7 @@ bool locate_desktop(char *name, coordinates_t *loc);
 bool locate_monitor(char *name, coordinates_t *loc);
 bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm);
 bool monitor_from_index(int i, coordinates_t *loc);
-bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel);
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel);
 bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
 bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel);
 
index ba523ecc7e39e96e1a5da3398bec87833e84d3e4..008dbc9f1ef6eebc1e38b23f446cf1246e9dc48d 100644 (file)
--- a/restore.c
+++ b/restore.c
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <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 "window.h"
 #include "parse.h"
-#include "jsmn.h"
 
 bool restore_tree(const char *file_path)
 {
@@ -99,6 +97,8 @@ bool restore_tree(const char *file_path)
                return false;
        }
 
+       mon = NULL;
+
        while (mon_head != NULL) {
                remove_monitor(mon_head);
        }
@@ -112,9 +112,9 @@ bool restore_tree(const char *file_path)
                        free(focusedMonitorName);
                        focusedMonitorName = copy_string(t+1, json);
                        t++;
-               } else if (keyeq("numClients", t, json)) {
+               } else if (keyeq("clientsCount", t, json)) {
                        t++;
-                       sscanf(json + t->start, "%u", &num_clients);
+                       sscanf(json + t->start, "%u", &clients_count);
                } else if (keyeq("monitors", t, json)) {
                        t++;
                        int s = t->size;
@@ -123,6 +123,15 @@ bool restore_tree(const char *file_path)
                                monitor_t *m = restore_monitor(&t, json);
                                add_monitor(m);
                        }
+                       continue;
+               } else if (keyeq("focusHistory", t, json)) {
+                       t++;
+                       restore_history(&t, json);
+                       continue;
+               } else if (keyeq("stackingList", t, json)) {
+                       t++;
+                       restore_stack(&t, json);
+                       continue;
                }
                t++;
        }
@@ -138,14 +147,18 @@ bool restore_tree(const char *file_path)
 
        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
                for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       refresh_presel_feebacks_in(d->root, d, m);
+                       restack_presel_feedback(d);
                        for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
                                uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
-                               xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
+                               xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
                        }
                }
        }
 
-       ewmh_update_client_list();
+       ewmh_update_client_list(false);
+       ewmh_update_client_list(true);
+       ewmh_update_active_window();
        ewmh_update_number_of_desktops();
        ewmh_update_current_desktop();
        ewmh_update_desktop_names();
@@ -156,34 +169,34 @@ bool restore_tree(const char *file_path)
        return true;
 }
 
-#define RESTORE_INT(o, k, p) \
+#define RESTORE_INT(k, p) \
        } else if (keyeq(#k, *t, json)) { \
                (*t)++; \
-               sscanf(json + (*t)->start, "%i", &o->p);
+               sscanf(json + (*t)->start, "%i", p);
 
-#define RESTORE_UINT(o, k, p) \
+#define RESTORE_UINT(k, p) \
        } else if (keyeq(#k, *t, json)) { \
                (*t)++; \
-               sscanf(json + (*t)->start, "%u", &o->p);
+               sscanf(json + (*t)->start, "%u", p);
 
-#define RESTORE_USINT(o, k, p) \
+#define RESTORE_USINT(k, p) \
        } else if (keyeq(#k, *t, json)) { \
                (*t)++; \
-               sscanf(json + (*t)->start, "%hu", &o->p);
+               sscanf(json + (*t)->start, "%hu", p);
 
-#define RESTORE_DOUBLE(o, k, p) \
+#define RESTORE_DOUBLE(k, p) \
        } else if (keyeq(#k, *t, json)) { \
                (*t)++; \
-               sscanf(json + (*t)->start, "%lf", &o->p);
+               sscanf(json + (*t)->start, "%lf", p);
 
-#define RESTORE_ANY(o, k, p, f) \
+#define RESTORE_ANY(k, p, f) \
        } else if (keyeq(#k, *t, json)) { \
                (*t)++; \
                char *val = copy_string(*t, json); \
-               f(val, &o->p); \
+               f(val, p); \
                free(val);
 
-#define RESTORE_BOOL(o, k, p)  RESTORE_ANY(o, k, p, parse_bool)
+#define RESTORE_BOOL(k, p)  RESTORE_ANY(k, p, parse_bool)
 
 monitor_t *restore_monitor(jsmntok_t **t, char *json)
 {
@@ -196,13 +209,13 @@ monitor_t *restore_monitor(jsmntok_t **t, char *json)
                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)
+               RESTORE_UINT(id, &m->id)
+               RESTORE_BOOL(wired, &m->wired)
+               RESTORE_INT(topPadding, &m->top_padding)
+               RESTORE_INT(rightPadding, &m->right_padding)
+               RESTORE_INT(bottomPadding, &m->bottom_padding)
+               RESTORE_INT(leftPadding, &m->left_padding)
+               RESTORE_UINT(stickyCount, &m->sticky_count)
                } else if (keyeq("rectangle", *t, json)) {
                        (*t)++;
                        restore_rectangle(&m->rectangle, t, json);
@@ -247,7 +260,7 @@ 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;
+       xcb_window_t focusedNodeId = XCB_NONE;
 
        for (int i = 0; i < s; i++) {
                if (keyeq("name", *t, json)) {
@@ -261,15 +274,15 @@ desktop_t *restore_desktop(jsmntok_t **t, char *json)
                                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)) {
+               RESTORE_INT(topPadding, &d->top_padding)
+               RESTORE_INT(rightPadding, &d->right_padding)
+               RESTORE_INT(bottomPadding, &d->bottom_padding)
+               RESTORE_INT(leftPadding, &d->left_padding)
+               RESTORE_INT(windowGap, &d->window_gap)
+               RESTORE_UINT(borderWidth, &d->border_width)
+               } else if (keyeq("focusedNodeId", *t, json)) {
                        (*t)++;
-                       sscanf(json + (*t)->start, "%u", &focusedWindow);
+                       sscanf(json + (*t)->start, "%u", &focusedNodeId);
                } else if (keyeq("root", *t, json)) {
                        (*t)++;
                        d->root = restore_node(t, json);
@@ -281,13 +294,8 @@ desktop_t *restore_desktop(jsmntok_t **t, char *json)
                (*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;
-                       }
-               }
+       if (focusedNodeId != XCB_NONE) {
+               d->focus = find_by_id_in(d->root, focusedNodeId);
        }
 
        return d;
@@ -301,20 +309,24 @@ node_t *restore_node(jsmntok_t **t, char *json)
        } else {
                int s = (*t)->size;
                (*t)++;
-               node_t *n = make_node();
+               /* hack to prevent a new ID from being generated */
+               node_t *n = make_node(UINT32_MAX);
 
                for (int i = 0; i < s; i++) {
-                       if (keyeq("splitType", *t, json)) {
+                       if (keyeq("id", *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)
+                               sscanf(json + (*t)->start, "%u", &n->id);
+                       RESTORE_ANY(splitType, &n->split_type, parse_split_type)
+                       RESTORE_DOUBLE(splitRatio, &n->split_ratio)
+                       RESTORE_INT(birthRotation, &n->birth_rotation)
+                       RESTORE_ANY(vacant, &n->vacant, parse_bool)
+                       RESTORE_ANY(sticky, &n->sticky, parse_bool)
+                       RESTORE_ANY(private, &n->private, parse_bool)
+                       RESTORE_ANY(locked, &n->locked, parse_bool)
+                       } else if (keyeq("presel", *t, json)) {
+                               (*t)++;
+                               n->presel = restore_presel(t, json);
+                               continue;
                        } else if (keyeq("rectangle", *t, json)) {
                                (*t)++;
                                restore_rectangle(&n->rectangle, t, json);
@@ -339,6 +351,9 @@ node_t *restore_node(jsmntok_t **t, char *json)
                                (*t)++;
                                n->client = restore_client(t, json);
                                continue;
+                       } else {
+                               warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                               (*t)++;
                        }
                        (*t)++;
                }
@@ -347,7 +362,7 @@ node_t *restore_node(jsmntok_t **t, char *json)
        }
 }
 
-client_t *restore_client(jsmntok_t **t, char *json)
+presel_t *restore_presel(jsmntok_t **t, char *json)
 {
        if ((*t)->type == JSMN_PRIMITIVE) {
                (*t)++;
@@ -355,34 +370,53 @@ client_t *restore_client(jsmntok_t **t, char *json)
        } else {
                int s = (*t)->size;
                (*t)++;
-               client_t *c = make_client(XCB_NONE, 0);
+               presel_t *p = make_presel();
 
                for (int i = 0; i < s; i++) {
-                       if (keyeq("window", *t, json)) {
+                       if (keyeq("splitRatio", *t, json)) {
                                (*t)++;
-                               sscanf(json + (*t)->start, "%u", &c->window);
-                       } else if (keyeq("className", *t, json)) {
+                               sscanf(json + (*t)->start, "%lf", &p->split_ratio);
+                       RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
+                       }
+
+                       (*t)++;
+               }
+
+               return p;
+       }
+}
+
+
+client_t *restore_client(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               client_t *c = make_client();
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("className", *t, json)) {
                                (*t)++;
                                snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
                        } else if (keyeq("instanceName", *t, json)) {
                                (*t)++;
                                snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
-                       RESTORE_ANY(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)
+                       RESTORE_ANY(state, &c->state, parse_client_state)
+                       RESTORE_ANY(lastState, &c->last_state, parse_client_state)
+                       RESTORE_ANY(layer, &c->layer, parse_stack_layer)
+                       RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
+                       RESTORE_UINT(borderWidth, &c->border_width)
+                       RESTORE_BOOL(urgent, &c->urgent)
+                       RESTORE_BOOL(icccmFocus, &c->icccm_focus)
+                       RESTORE_BOOL(icccmInput, &c->icccm_input)
+                       RESTORE_USINT(minWidth, &c->min_width)
+                       RESTORE_USINT(maxWidth, &c->max_width)
+                       RESTORE_USINT(minHeight, &c->min_height)
+                       RESTORE_USINT(maxHeight, &c->max_height)
+                       RESTORE_INT(wmStatesCount, &c->wm_states_count)
                        } else if (keyeq("wmState", *t, json)) {
                                (*t)++;
                                restore_wm_state(c->wm_state, t, json);
@@ -395,6 +429,9 @@ client_t *restore_client(jsmntok_t **t, char *json)
                                (*t)++;
                                restore_rectangle(&c->floating_rectangle, t, json);
                                continue;
+                       } else {
+                               warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                               (*t)++;
                        }
 
                        (*t)++;
@@ -427,6 +464,62 @@ void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
        }
 }
 
+void restore_history(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               coordinates_t loc = {NULL, NULL, NULL};
+               restore_coordinates(&loc, t, json);
+               if (loc.monitor != NULL && loc.desktop != NULL) {
+                       history_add(loc.monitor, loc.desktop, loc.node);
+               }
+       }
+}
+
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("monitorName", *t, json)) {
+                       (*t)++;
+                       char *name = copy_string(*t, json);
+                       loc->monitor = find_monitor(name);
+                       free(name);
+               } else if (keyeq("desktopName", *t, json)) {
+                       (*t)++;
+                       char *name = copy_string(*t, json);
+                       loc->desktop = find_desktop_in(name, loc->monitor);
+                       free(name);
+               } else if (keyeq("nodeId", *t, json)) {
+                       (*t)++;
+                       uint32_t id;
+                       sscanf(json + (*t)->start, "%u", &id);
+                       loc->node = find_by_id_in(loc->desktop!=NULL?loc->desktop->root:NULL, id);
+               }
+               (*t)++;
+       }
+}
+
+void restore_stack(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               uint32_t id;
+               sscanf(json + (*t)->start, "%u", &id);
+               coordinates_t loc;
+               if (locate_window(id, &loc)) {
+                       stack_insert_after(stack_tail, loc.node);
+               }
+               (*t)++;
+       }
+}
+
 void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json)
 {
        int s = (*t)->size;
@@ -463,86 +556,3 @@ char *copy_string(jsmntok_t *tok, char *json)
        res[len-1] = '\0';
        return res;
 }
-
-bool restore_history(const char *file_path)
-{
-       if (file_path == NULL) {
-               return false;
-       }
-
-       FILE *snapshot = fopen(file_path, "r");
-       if (snapshot == NULL) {
-               perror("Restore history: fopen");
-               return false;
-       }
-
-       char line[MAXLEN];
-       char mnm[SMALEN];
-       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;
-                       if (win != XCB_NONE && !locate_window(win, &loc)) {
-                               warn("Can't locate window 0x%X.\n", win);
-                               continue;
-                       }
-                       node_t *n = (win == XCB_NONE ? NULL : loc.node);
-                       if (!locate_desktop(dnm, &loc)) {
-                               warn("Can't locate desktop '%s'.\n", dnm);
-                               continue;
-                       }
-                       desktop_t *d = loc.desktop;
-                       if (!locate_monitor(mnm, &loc)) {
-                               warn("Can't locate monitor '%s'.\n", mnm);
-                               continue;
-                       }
-                       monitor_t *m = loc.monitor;
-                       history_add(m, d, n);
-               } else {
-                       warn("Can't parse history entry: '%s'\n", line);
-               }
-       }
-
-       fclose(snapshot);
-       return true;
-}
-
-bool restore_stack(const char *file_path)
-{
-       if (file_path == NULL) {
-               return false;
-       }
-
-       FILE *snapshot = fopen(file_path, "r");
-       if (snapshot == NULL) {
-               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)) {
-                               stack_insert_after(stack_tail, loc.node);
-                       } else {
-                               warn("Can't locate window 0x%X.\n", win);
-                       }
-               } else {
-                       warn("Can't parse stack entry: '%s'\n", line);
-               }
-       }
-
-       fclose(snapshot);
-       return true;
-}
index a05fdc024f96e2bdd27138ea9b8a214856964f19..0871a5490fa4774fe731b3e886fda3277cc61ee7 100644 (file)
--- a/restore.h
+++ b/restore.h
@@ -31,12 +31,14 @@ 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);
+presel_t *restore_presel(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_history(jsmntok_t **t, char *json);
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);
+void restore_stack(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 b179a1a0100b2cc40fcc00654b9b918c06e8c80c..3e0371211871037cf80089ad3d2fec7c7e24cc7f 100644 (file)
--- a/rule.c
+++ b/rule.c
@@ -23,6 +23,9 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
 #include <string.h>
 #include <unistd.h>
 #include "bspwm.h"
@@ -35,7 +38,7 @@
 rule_t *make_rule(void)
 {
        rule_t *r = malloc(sizeof(rule_t));
-       r->cause[0] = r->effect[0] = '\0';
+       r->class_name[0] = r->instance_name[0] = r->effect[0] = '\0';
        r->next = r->prev = NULL;
        r->one_shot = false;
        return r;
@@ -72,21 +75,26 @@ void remove_rule(rule_t *r)
 void remove_rule_by_cause(char *cause)
 {
        rule_t *r = rule_head;
+       char *class_name = strtok(cause, COL_TOK);
+       char *instance_name = strtok(NULL, COL_TOK);
        while (r != NULL) {
                rule_t *next = r->next;
-               if (streq(r->cause, cause))
+               if ((streq(class_name, MATCH_ANY) || streq(r->class_name, class_name)) &&
+                   (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name))) {
                        remove_rule(r);
+               }
                r = next;
        }
 }
 
 bool remove_rule_by_index(int idx)
 {
-       for (rule_t *r = rule_head; r != NULL; r = r->next, idx--)
+       for (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {
                if (idx == 0) {
                        remove_rule(r);
                        return true;
                }
+       }
        return false;
 }
 
@@ -111,8 +119,9 @@ pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *
 
 void add_pending_rule(pending_rule_t *pr)
 {
-       if (pr == NULL)
+       if (pr == NULL) {
                return;
+       }
        if (pending_rule_head == NULL) {
                pending_rule_head = pending_rule_tail = pr;
        } else {
@@ -124,18 +133,23 @@ void add_pending_rule(pending_rule_t *pr)
 
 void remove_pending_rule(pending_rule_t *pr)
 {
-       if (pr == NULL)
+       if (pr == NULL) {
                return;
+       }
        pending_rule_t *a = pr->prev;
        pending_rule_t *b = pr->next;
-       if (a != NULL)
+       if (a != NULL) {
                a->next = b;
-       if (b != NULL)
+       }
+       if (b != NULL) {
                b->prev = a;
-       if (pr == pending_rule_head)
+       }
+       if (pr == pending_rule_head) {
                pending_rule_head = b;
-       if (pr == pending_rule_tail)
+       }
+       if (pr == pending_rule_tail) {
                pending_rule_tail = a;
+       }
        close(pr->fd);
        free(pr->csq);
        free(pr);
@@ -231,9 +245,8 @@ void apply_rules(xcb_window_t win, rule_consequence_t *csq)
        rule_t *rule = rule_head;
        while (rule != NULL) {
                rule_t *next = rule->next;
-               if (streq(rule->cause, MATCH_ANY) ||
-                   streq(rule->cause, csq->class_name) ||
-                   streq(rule->cause, csq->instance_name)) {
+               if ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&
+                   (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name))) {
                        char effect[MAXLEN];
                        snprintf(effect, sizeof(effect), "%s", rule->effect);
                        char *key = strtok(effect, CSQ_BLK);
@@ -282,8 +295,9 @@ bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
 
 void parse_rule_consequence(int fd, rule_consequence_t *csq)
 {
-       if (fd == -1)
+       if (fd == -1) {
                return;
+       }
        char data[BUFSIZ];
        int nb;
        while ((nb = read(fd, data, sizeof(data))) > 0) {
@@ -306,7 +320,7 @@ void parse_key_value(char *key, char *value, rule_consequence_t *csq)
                snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
        } else if (streq("desktop", key)) {
                snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
-       } else if (streq("window", key)) {
+       } else if (streq("node", key)) {
                snprintf(csq->node_desc, sizeof(csq->node_desc), "%s", value);
        } else if (streq("split_dir", key)) {
                snprintf(csq->split_dir, sizeof(csq->split_dir), "%s", value);
@@ -348,11 +362,9 @@ void parse_key_value(char *key, char *value, rule_consequence_t *csq)
        }
 }
 
-void list_rules(char *pattern, FILE *rsp)
+void list_rules(FILE *rsp)
 {
        for (rule_t *r = rule_head; r != NULL; r = r->next) {
-               if (pattern != NULL && !streq(pattern, r->cause))
-                       continue;
-               fprintf(rsp, "%s => %s\n", r->cause, r->effect);
+               fprintf(rsp, "%s:%s => %s\n", r->class_name, r->instance_name, r->effect);
        }
 }
diff --git a/rule.h b/rule.h
index 5821bac2cbfb07b3bc6effac8b6da5bcef023872..0ccf39651c84d37850c0cd390a5066b0cd1c7760 100644 (file)
--- a/rule.h
+++ b/rule.h
@@ -41,6 +41,6 @@ void apply_rules(xcb_window_t win, rule_consequence_t *csq);
 bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
 void parse_rule_consequence(int fd, rule_consequence_t *csq);
 void parse_key_value(char *key, char *value, rule_consequence_t *csq);
-void list_rules(char *pattern, FILE *rsp);
+void list_rules(FILE *rsp);
 
 #endif
index 9330e625d4e50d0e2dc2ed43214cfb1f8a000583..71f4965547fa66322ee8dc7fa9801b9cf5707ff8 100644 (file)
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <unistd.h>
+#include <sys/types.h>
 #include "bspwm.h"
 #include "settings.h"
 
 void run_config(void)
 {
        if (fork() == 0) {
-               if (dpy != NULL)
+               if (dpy != NULL) {
                        close(xcb_get_file_descriptor(dpy));
+               }
                setsid();
                execl(config_path, config_path, NULL);
                err("Couldn't execute the configuration file.\n");
@@ -43,28 +46,18 @@ void load_settings(void)
        snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
 
        snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
-       snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
        snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
-       snprintf(presel_border_color, sizeof(presel_border_color), "%s", PRESEL_BORDER_COLOR);
-       snprintf(focused_locked_border_color, sizeof(focused_locked_border_color), "%s", FOCUSED_LOCKED_BORDER_COLOR);
-       snprintf(active_locked_border_color, sizeof(active_locked_border_color), "%s", ACTIVE_LOCKED_BORDER_COLOR);
-       snprintf(normal_locked_border_color, sizeof(normal_locked_border_color), "%s", NORMAL_LOCKED_BORDER_COLOR);
-       snprintf(focused_sticky_border_color, sizeof(focused_sticky_border_color), "%s", FOCUSED_STICKY_BORDER_COLOR);
-       snprintf(active_sticky_border_color, sizeof(active_sticky_border_color), "%s", ACTIVE_STICKY_BORDER_COLOR);
-       snprintf(normal_sticky_border_color, sizeof(normal_sticky_border_color), "%s", NORMAL_STICKY_BORDER_COLOR);
-       snprintf(focused_private_border_color, sizeof(focused_private_border_color), "%s", FOCUSED_PRIVATE_BORDER_COLOR);
-       snprintf(active_private_border_color, sizeof(active_private_border_color), "%s", ACTIVE_PRIVATE_BORDER_COLOR);
-       snprintf(normal_private_border_color, sizeof(normal_private_border_color), "%s", NORMAL_PRIVATE_BORDER_COLOR);
-       snprintf(urgent_border_color, sizeof(urgent_border_color), "%s", URGENT_BORDER_COLOR);
+       snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
+       snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR);
 
-       split_ratio = SPLIT_RATIO;
        window_gap = WINDOW_GAP;
        border_width = BORDER_WIDTH;
+       split_ratio = SPLIT_RATIO;
        initial_polarity = FIRST_CHILD;
 
        borderless_monocle = BORDERLESS_MONOCLE;
        gapless_monocle = GAPLESS_MONOCLE;
-       leaf_monocle = LEAF_MONOCLE;
+       single_monocle = SINGLE_MONOCLE;
        focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
        pointer_follows_focus = POINTER_FOLLOWS_FOCUS;
        pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
index f373076c854e82a9f380ec443235f333c03f4eb0..e50ec80fe49ac1dc22220108e8aab430914e54ce 100644 (file)
 #define EXTERNAL_RULES_COMMAND  ""
 #define STATUS_PREFIX           "W"
 
-#define FOCUSED_BORDER_COLOR          "#7E7F89"
-#define ACTIVE_BORDER_COLOR           "#545350"
-#define NORMAL_BORDER_COLOR           "#3F3E3B"
-#define PRESEL_BORDER_COLOR           "#E8E8F4"
-#define FOCUSED_LOCKED_BORDER_COLOR   "#C7B579"
-#define ACTIVE_LOCKED_BORDER_COLOR    "#545350"
-#define NORMAL_LOCKED_BORDER_COLOR    "#3F3E3B"
-#define FOCUSED_STICKY_BORDER_COLOR   "#E3A5DA"
-#define ACTIVE_STICKY_BORDER_COLOR    "#545350"
-#define NORMAL_STICKY_BORDER_COLOR    "#3F3E3B"
-#define FOCUSED_PRIVATE_BORDER_COLOR  "#42CAD9"
-#define ACTIVE_PRIVATE_BORDER_COLOR   "#5C5955"
-#define NORMAL_PRIVATE_BORDER_COLOR   "#34322E"
-#define URGENT_BORDER_COLOR           "#EFA29A"
+#define NORMAL_BORDER_COLOR           "#30302f"
+#define ACTIVE_BORDER_COLOR           "#474645"
+#define FOCUSED_BORDER_COLOR          "#817f7f"
+#define PRESEL_FEEDBACK_COLOR         "#f4d775"
 
-#define SPLIT_RATIO    0.5
-#define WINDOW_GAP     6
-#define BORDER_WIDTH   1
+#define WINDOW_GAP           6
+#define BORDER_WIDTH         1
+#define SPLIT_RATIO          0.5
 
 #define HISTORY_AWARE_FOCUS         false
 #define FOCUS_BY_DISTANCE           false
 #define BORDERLESS_MONOCLE          false
 #define GAPLESS_MONOCLE             false
-#define LEAF_MONOCLE                false
+#define SINGLE_MONOCLE              false
 #define FOCUS_FOLLOWS_POINTER       false
 #define POINTER_FOLLOWS_FOCUS       false
 #define POINTER_FOLLOWS_MONITOR     false
 char external_rules_command[MAXLEN];
 char status_prefix[MAXLEN];
 
-char focused_border_color[MAXLEN];
-char active_border_color[MAXLEN];
 char normal_border_color[MAXLEN];
-char presel_border_color[MAXLEN];
-char focused_locked_border_color[MAXLEN];
-char active_locked_border_color[MAXLEN];
-char normal_locked_border_color[MAXLEN];
-char focused_sticky_border_color[MAXLEN];
-char active_sticky_border_color[MAXLEN];
-char normal_sticky_border_color[MAXLEN];
-char focused_private_border_color[MAXLEN];
-char active_private_border_color[MAXLEN];
-char normal_private_border_color[MAXLEN];
-char urgent_border_color[MAXLEN];
+char active_border_color[MAXLEN];
+char focused_border_color[MAXLEN];
+char presel_feedback_color[MAXLEN];
 
-double split_ratio;
 int window_gap;
 unsigned int border_width;
+double split_ratio;
+
 child_polarity_t initial_polarity;
 
 bool borderless_monocle;
 bool gapless_monocle;
-bool leaf_monocle;
+bool single_monocle;
 bool focus_follows_pointer;
 bool pointer_follows_focus;
 bool pointer_follows_monitor;
diff --git a/stack.c b/stack.c
index 05d9b04f6b192186d0d0789358616974518a87ff..2ae9cef1885438df2429f36580c68373ec026323 100644 (file)
--- a/stack.c
+++ b/stack.c
@@ -25,6 +25,9 @@
 #include <stdlib.h>
 #include "bspwm.h"
 #include "window.h"
+#include "subscribe.h"
+#include "ewmh.h"
+#include "tree.h"
 #include "stack.h"
 
 stacking_list_t *make_stack(node_t *n)
@@ -47,13 +50,15 @@ void stack_insert_after(stacking_list_t *a, node_t *n)
                }
                remove_stack_node(n);
                stacking_list_t *b = a->next;
-               if (b != NULL)
+               if (b != NULL) {
                        b->prev = s;
+               }
                s->next = b;
                s->prev = a;
                a->next = s;
-               if (stack_tail == a)
+               if (stack_tail == a) {
                        stack_tail = s;
+               }
        }
 }
 
@@ -69,39 +74,48 @@ void stack_insert_before(stacking_list_t *a, node_t *n)
                }
                remove_stack_node(n);
                stacking_list_t *b = a->prev;
-               if (b != NULL)
+               if (b != NULL) {
                        b->next = s;
+               }
                s->prev = b;
                s->next = a;
                a->prev = s;
-               if (stack_head == a)
+               if (stack_head == a) {
                        stack_head = s;
+               }
        }
 }
 
 void remove_stack(stacking_list_t *s)
 {
-       if (s == NULL)
+       if (s == NULL) {
                return;
+       }
        stacking_list_t *a = s->prev;
        stacking_list_t *b = s->next;
-       if (a != NULL)
+       if (a != NULL) {
                a->next = b;
-       if (b != NULL)
+       }
+       if (b != NULL) {
                b->prev = a;
-       if (s == stack_head)
+       }
+       if (s == stack_head) {
                stack_head = b;
-       if (s == stack_tail)
+       }
+       if (s == stack_tail) {
                stack_tail = a;
+       }
        free(s);
 }
 
 void remove_stack_node(node_t *n)
 {
-       for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
-               if (s->node == n) {
-                       remove_stack(s);
-                       return;
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+                       if (s->node == f) {
+                               remove_stack(s);
+                               break;
+                       }
                }
        }
 }
@@ -148,26 +162,57 @@ stacking_list_t *limit_below(node_t *n)
        return s;
 }
 
-void stack(node_t *n, bool focused)
+void stack(desktop_t *d, node_t *n, bool focused)
 {
-       if (IS_FLOATING(n->client) && !auto_raise) {
-               return;
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (IS_FLOATING(f->client) && !auto_raise) {
+                       continue;
+               }
+
+               if (stack_head == NULL) {
+                       stack_insert_after(NULL, f);
+               } else {
+                       stacking_list_t *s = (focused ? limit_above(f) : limit_below(f));
+                       if (s == NULL) {
+                               continue;
+                       }
+                       int i = stack_cmp(f->client, s->node->client);
+                       if (i < 0 || (i == 0 && !focused)) {
+                               stack_insert_before(s, f);
+                               window_below(f->id, s->node->id);
+                               put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%X below 0x%X\n", f->id, s->node->id);
+                       } else {
+                               stack_insert_after(s, f);
+                               window_above(f->id, s->node->id);
+                               put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%X above 0x%X\n", f->id, s->node->id);
+                       }
+               }
        }
 
-       if (stack_head == NULL) {
-               stack_insert_after(NULL, n);
+       ewmh_update_client_list(true);
+       restack_presel_feedback(d);
+}
+
+void restack_presel_feedback(desktop_t *d)
+{
+       stacking_list_t *s = stack_tail;
+       while (s != NULL && !IS_TILED(s->node->client)) {
+               s = s->prev;
+       }
+       if (s != NULL) {
+               restack_presel_feedback_in(d->root, s->node);
+       }
+}
+
+void restack_presel_feedback_in(node_t *r, node_t *n)
+{
+       if (r == NULL) {
+               return;
        } else {
-               stacking_list_t *s = (focused ? limit_above(n) : limit_below(n));
-               if (s == NULL) {
-                       return;
-               }
-               int i = stack_cmp(n->client, s->node->client);
-               if (i < 0 || (i == 0 && !focused)) {
-                       stack_insert_before(s, n);
-                       window_below(n->client->window, s->node->client->window);
-               } else {
-                       stack_insert_after(s, n);
-                       window_above(n->client->window, s->node->client->window);
+               if (r->presel != NULL) {
+                       window_above(r->presel->feedback, n->id);
                }
+               restack_presel_feedback_in(r->first_child, n);
+               restack_presel_feedback_in(r->second_child, n);
        }
 }
diff --git a/stack.h b/stack.h
index 3929cf29ca49ee7f4cf30bb821a092df20d2c3fc..2799ee78b37d4366e8433e3510d66c14f8b6e164 100644 (file)
--- a/stack.h
+++ b/stack.h
@@ -34,6 +34,8 @@ int stack_level(client_t *c);
 int stack_cmp(client_t *c1, client_t *c2);
 stacking_list_t *limit_above(node_t *n);
 stacking_list_t *limit_below(node_t *n);
-void stack(node_t *n, bool focused);
+void stack(desktop_t *d, node_t *n, bool focused);
+void restack_presel_feedback(desktop_t *d);
+void restack_presel_feedback_in(node_t *r, node_t *n);
 
 #endif
index 9931a8ef4a55aec68e50590e7c5d6afa5e2b4bf2..cff175a2d65b450485fbc2361f1ae28894160891 100644 (file)
@@ -22,8 +22,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
+#include <stdbool.h>
 #include <ctype.h>
 #include <stdarg.h>
 #include "bspwm.h"
@@ -42,18 +43,23 @@ subscriber_list_t *make_subscriber_list(FILE *stream, int field)
 
 void remove_subscriber(subscriber_list_t *sb)
 {
-       if (sb == NULL)
+       if (sb == NULL) {
                return;
+       }
        subscriber_list_t *a = sb->prev;
        subscriber_list_t *b = sb->next;
-       if (a != NULL)
+       if (a != NULL) {
                a->next = b;
-       if (b != NULL)
+       }
+       if (b != NULL) {
                b->prev = a;
-       if (sb == subscribe_head)
+       }
+       if (sb == subscribe_head) {
                subscribe_head = b;
-       if (sb == subscribe_tail)
+       }
+       if (sb == subscribe_tail) {
                subscribe_tail = a;
+       }
        fclose(sb->stream);
        free(sb);
 }
@@ -78,17 +84,44 @@ int print_report(FILE *stream)
        fprintf(stream, "%s", status_prefix);
        bool urgent = false;
        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               fprintf(stream, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
+               fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name);
                for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
-                       for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
+                       for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root)) {
                                urgent |= n->client->urgent;
+                       }
                        char c = (urgent ? 'u' : (d->root == NULL ? 'f' : 'o'));
-                       if (m->desk == d)
+                       if (m->desk == d) {
                                c = toupper(c);
-                       fprintf(stream, "%c%s:", c, d->name);
+                       }
+                       fprintf(stream, ":%c%s", c, d->name);
+               }
+               if (m->desk != NULL) {
+                       fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout));
+                       if (m->desk->focus != NULL) {
+                               node_t *n = m->desk->focus;
+                               if (n->client != NULL) {
+                                       fprintf(stream, ":T%c", STATE_CHR(n->client->state));
+                               } else {
+                                       fprintf(stream, ":T@");
+                               }
+                               int i = 0;
+                               char flags[4];
+                               if (n->sticky) {
+                                       flags[i++] = 'S';
+                               }
+                               if (n->private) {
+                                       flags[i++] = 'P';
+                               }
+                               if (n->locked) {
+                                       flags[i++] = 'L';
+                               }
+                               flags[i] = '\0';
+                               fprintf(stream, ":G%s", flags);
+                       }
+               }
+               if (m != mon_tail) {
+                       fprintf(stream, "%s", ":");
                }
-               if (m->desk != NULL)
-                       fprintf(stream, "L%c%s", (m->desk->layout == LAYOUT_TILED ? 'T' : 'M'), (m != mon_tail ? ":" : ""));
        }
        fprintf(stream, "%s", "\n");
        return fflush(stream);
index a140842da75887ba4a6c93e6a255b56b91665700..9ce019f03412172ef8563d523bf8b65d66c05285 100644 (file)
@@ -38,21 +38,24 @@ typedef enum {
        SBSC_MASK_DESKTOP_SWAP = 1 << 9,
        SBSC_MASK_DESKTOP_TRANSFER = 1 << 10,
        SBSC_MASK_DESKTOP_FOCUS = 1 << 11,
-       SBSC_MASK_DESKTOP_LAYOUT = 1 << 12,
-       SBSC_MASK_WINDOW_MANAGE = 1 << 13,
-       SBSC_MASK_WINDOW_UNMANAGE = 1 << 14,
-       SBSC_MASK_WINDOW_SWAP = 1 << 15,
-       SBSC_MASK_WINDOW_TRANSFER = 1 << 16,
-       SBSC_MASK_WINDOW_FOCUS = 1 << 17,
-       SBSC_MASK_WINDOW_ACTIVATE = 1 << 18,
-       SBSC_MASK_WINDOW_GEOMETRY = 1 << 19,
-       SBSC_MASK_WINDOW_STATE = 1 << 20,
-       SBSC_MASK_WINDOW_FLAG = 1 << 21,
-       SBSC_MASK_WINDOW_LAYER = 1 << 22,
+       SBSC_MASK_DESKTOP_ACTIVATE = 1 << 12,
+       SBSC_MASK_DESKTOP_LAYOUT = 1 << 13,
+       SBSC_MASK_NODE_MANAGE = 1 << 14,
+       SBSC_MASK_NODE_UNMANAGE = 1 << 15,
+       SBSC_MASK_NODE_SWAP = 1 << 16,
+       SBSC_MASK_NODE_TRANSFER = 1 << 17,
+       SBSC_MASK_NODE_FOCUS = 1 << 18,
+       SBSC_MASK_NODE_PRESEL = 1 << 19,
+       SBSC_MASK_NODE_STACK = 1 << 20,
+       SBSC_MASK_NODE_ACTIVATE = 1 << 21,
+       SBSC_MASK_NODE_GEOMETRY = 1 << 22,
+       SBSC_MASK_NODE_STATE = 1 << 23,
+       SBSC_MASK_NODE_FLAG = 1 << 24,
+       SBSC_MASK_NODE_LAYER = 1 << 25,
        SBSC_MASK_MONITOR = (1 << 6) - (1 << 1),
-       SBSC_MASK_DESKTOP = (1 << 13) - (1 << 6),
-       SBSC_MASK_WINDOW = (1 << 23) - (1 << 13),
-       SBSC_MASK_ALL = (1 << 23) - 1
+       SBSC_MASK_DESKTOP = (1 << 14) - (1 << 6),
+       SBSC_MASK_NODE = (1 << 26) - (1 << 14),
+       SBSC_MASK_ALL = (1 << 26) - 1
 } subscriber_mask_t;
 
 subscriber_list_t *make_subscriber_list(FILE *stream, int field);
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644 (file)
index 0000000..e00a826
--- /dev/null
@@ -0,0 +1,13 @@
+OUT      = test_window
+CFLAGS  += -std=c99 -pedantic -Wall -Wextra
+LDLIBS   = -lxcb -lxcb-icccm
+
+SRC = $(wildcard *.c)
+OBJ = $(SRC:.c=.o)
+
+all: $(OUT)
+
+clean:
+       $(RM) $(OUT) $(OBJ)
+
+.PHONY: all clean
diff --git a/tests/README.md b/tests/README.md
new file mode 100644 (file)
index 0000000..eb318fb
--- /dev/null
@@ -0,0 +1,3 @@
+- Install *jshon*.
+- Run `make` once.
+- Run `./run`.
diff --git a/tests/desktop/swap b/tests/desktop/swap
new file mode 100755 (executable)
index 0000000..e05e30a
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc wm -a "TEST-SWAP-A" 1024x512+0+0
+bspc wm -a "TEST-SWAP-B" 1024x512+0+512
+
+bspc monitor -f "TEST-SWAP-A"
+window add 3
+
+bspc monitor -f "TEST-SWAP-B"
+window add 2
+
+nodes_a=$(bspc query -N -m "TEST-SWAP-A")
+nodes_b=$(bspc query -N -m "TEST-SWAP-B")
+
+bspc desktop "TEST-SWAP-A:^1" -s "TEST-SWAP-B:^1"
+
+[ "$(bspc query -N -m 'TEST-SWAP-A')" = "$nodes_b" ] || fail "Wrong nodes in first monitor"
+[ "$(bspc query -N -m 'TEST-SWAP-B')" = "$nodes_a" ] || fail "Wrong nodes in second monitor"
+
+window remove 3
+bspc monitor -f "TEST-SWAP-A"
+window remove 2
+
+bspc wm -r "TEST-SWAP-A"
+bspc wm -r "TEST-SWAP-B"
diff --git a/tests/desktop/transfer b/tests/desktop/transfer
new file mode 100755 (executable)
index 0000000..35b7532
--- /dev/null
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc wm -a "TEST-TRANSFER-A" 1024x512+0+0
+bspc wm -a "TEST-TRANSFER-B" 1024x512+0+512
+
+bspc monitor "TEST-TRANSFER-A" -a source
+bspc monitor -f "TEST-TRANSFER-A"
+
+window add 3
+
+root_rectangle_y=$(bspc query -T -n @/ | jshon -e rectangle -e y)
+
+bspc desktop "TEST-TRANSFER-A:focused" -m "TEST-TRANSFER-B"
+
+[ "$(bspc query -D -m "TEST-TRANSFER-A" | wc -l)" -eq 1 ] || fail "Invalid number of desktop in source after transfer."
+
+bspc desktop "TEST-TRANSFER-B:^2" -f
+
+[ "$(bspc query -T -n @/ | jshon -e rectangle -e y)" -ne "$root_rectangle_y" ] || fail "Wrong tiled rectangle for root in destination."
+
+window remove 3
+
+bspc wm -r "TEST-TRANSFER-A"
+bspc wm -r "TEST-TRANSFER-B"
diff --git a/tests/node/flags b/tests/node/flags
new file mode 100755 (executable)
index 0000000..d2ab93e
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc monitor -a "test-sticky-a"
+bspc monitor -a "test-sticky-b"
+
+bspc desktop -f "test-sticky-a"
+
+window add 3
+bspc node -g sticky
+
+sticky_node_id=$(bspc query -N -n)
+
+bspc rule -a Test:test -o desktop="test-sticky-b"
+
+window add
+
+bspc desktop -f "test-sticky-b"
+
+bspc query -N -d | grep "$sticky_node_id" > /dev/null || fail "Sticky node is missing in destination."
+
+window remove 2
+bspc desktop -f "test-sticky-a"
+window remove 2
+
+bspc monitor -r "test-sticky-a" "test-sticky-b"
diff --git a/tests/node/insertion b/tests/node/insertion
new file mode 100755 (executable)
index 0000000..75273d9
--- /dev/null
@@ -0,0 +1,46 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc monitor -a "test-insertion"
+bspc desktop -f "test-insertion"
+
+# Automatic mode
+
+window add 2
+
+split_type_a=$(bspc query -T -n @/ | jshon -e splitType -u)
+
+window add
+
+split_type_b=$(bspc query -T -n @/2 | jshon -e splitType -u)
+
+[ "$split_type_a" = "$split_type_b" ] && fail "Non-vacant node insertion should rotate brother."
+
+split_type_a=$(bspc query -T -n @/ | jshon -e splitType -u)
+
+bspc rule -a Test:test -o state=floating
+window add
+
+split_type_b=$(bspc query -T -n @/2 | jshon -e splitType -u)
+
+[ "$split_type_a" = "$split_type_b" ] || fail "Vacant node insertion shouldn't rotate brother."
+
+window remove
+
+# Manual mode
+
+for dir in north west south east ; do
+       child=1
+       split_type=vertical
+       [ "$dir" = "south" -o "$dir" = "east" ] && child=2
+       [ "$dir" = "north" -o "$dir" = "south" ] && split_type=horizontal
+       bspc node -p $dir
+       window add
+       [ "$(bspc query -N -n)" = "$(bspc query -N -n @parent/${child})" ] || fail "Wrong child polarity for ${dir} preselection."
+       [ "$(bspc query -T -n @parent | jshon -e splitType -u)" = "$split_type" ] || fail "Wrong split type for ${dir} preselection."
+done
+
+window remove 7
+
+bspc monitor -r "test-insertion"
diff --git a/tests/node/removal b/tests/node/removal
new file mode 100755 (executable)
index 0000000..846fa78
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc monitor -a "test-removal"
+bspc desktop -f "test-removal"
+
+window add 3
+
+next_focus=$(bspc query -N -n);
+
+bspc node -f @/2/1
+bspc node @/2 -k
+
+[ "$(bspc query -N -n)" = "$next_focus" ] || fail "Invalid focus after removal."
+
+window remove
+
+bspc monitor -r "test-removal"
diff --git a/tests/node/swap b/tests/node/swap
new file mode 100755 (executable)
index 0000000..aa79734
--- /dev/null
@@ -0,0 +1,25 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc monitor -a "test-swap-a" "test-swap-b"
+bspc desktop -f "test-swap-a"
+
+window add 5
+next_focus_b=$(bspc query -N -n @/2/2/1)
+bspc desktop -f "test-swap-b"
+window add 3
+
+bspc node -f @test-swap-a:/2/2/1
+bspc node -a @test-swap-b:/1
+
+bspc node @/2 -s @test-swap-b:/1
+
+[ "$(bspc query -N -n @test-swap-b:)" = "$next_focus_b" ] || fail "Invalid focus after swap."
+
+window remove 2
+bspc desktop -f "test-swap-b"
+window remove 1 2
+window remove 4
+
+bspc monitor -r "test-swap-a" "test-swap-b"
diff --git a/tests/node/transfer b/tests/node/transfer
new file mode 100755 (executable)
index 0000000..9907cd7
--- /dev/null
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+. ./prelude
+
+bspc monitor -a "test-transfer-a" "test-transfer-b"
+bspc desktop -f "test-transfer-a"
+
+window add 5
+
+next_focus_a=$(bspc query -N -n @/1)
+next_focus_b=$(bspc query -N -n @/2/2/1)
+
+bspc node -f $next_focus_b
+bspc node @/2 -d "test-transfer-b"
+
+[ "$next_focus_a" = "$(bspc query -N -n)" ] || fail "Invalid focus after transfer from source."
+[ "$next_focus_b" = "$(bspc query -N -n @test-transfer-b:)" ] || fail "Invalid focus after transfer in destination."
+
+window remove
+bspc desktop -f "test-transfer-b"
+window remove 1 2
+window remove 2
+
+bspc monitor -r "test-transfer-a" "test-transfer-b"
diff --git a/tests/prelude b/tests/prelude
new file mode 100644 (file)
index 0000000..5c8874a
--- /dev/null
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+fail() {
+       echo "$@" 1>&2
+       exit 1
+}
+
+clients_count() {
+       bspc wm -d | jshon -e clientsCount
+}
+
+window() {
+    local cmd=$1
+    local iter=${2:-1}
+    local delta=${3:-1}
+       local interval=${4:-0.05}
+       local max_tries=${5:-40}
+    while [ $iter -gt 0 ] ; do
+               local cur=$(clients_count)
+               local trg
+               local tries=0
+               case "$cmd" in
+                       add)
+                               trg=$((cur + delta))
+                               ./test_window &
+                               ;;
+                       remove)
+                               trg=$((cur - delta))
+                               bspc node -c
+                               ;;
+                       *)
+                               fail "window: unknown command: ${cmd}."
+                               ;;
+               esac
+               while [ $cur -ne $trg ] ; do
+                       cur=$(clients_count)
+                       sleep $interval
+                       tries=$((tries + 1))
+                       [ $tries -ge $max_tries ] && break
+               done
+               iter=$((iter - 1))
+       done
+}
diff --git a/tests/run b/tests/run
new file mode 100755 (executable)
index 0000000..3496bd0
--- /dev/null
+++ b/tests/run
@@ -0,0 +1,39 @@
+#! /bin/sh
+
+focus_follows_pointer=$(bspc config focus_follows_pointer)
+initial_polarity=$(bspc config initial_polarity)
+bspc config initial_polarity first_child
+bspc config focus_follows_pointer false
+
+cleanup () {
+       bspc config initial_polarity "$initial_polarity"
+       bspc config focus_follows_pointer "$focus_follows_pointer"
+}
+
+abort() {
+       cleanup
+       echo "One test failed." 1>&2
+       exit 1
+}
+
+echo "Node"
+echo "-> Insertion"
+./node/insertion || abort
+echo "-> Removal"
+./node/removal || abort
+echo "-> Transfer"
+./node/transfer || abort
+echo "-> Swap"
+./node/swap || abort
+echo "-> Flags"
+./node/flags || abort
+
+echo "Desktop"
+echo "-> Transfer"
+./desktop/transfer || abort
+echo "-> Swap"
+./desktop/swap || abort
+
+cleanup
+
+echo "All tests passed."
diff --git a/tests/test_window.c b/tests/test_window.c
new file mode 100644 (file)
index 0000000..1f41a2c
--- /dev/null
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_icccm.h>
+
+#define TEST_WINDOW_IC  "test\0Test"
+
+bool get_atom(xcb_connection_t *dpy, char *name, xcb_atom_t *atom)
+{
+       bool ret = true;
+       xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
+       if (reply != NULL) {
+               *atom = reply->atom;
+       } else {
+               ret = false;
+       }
+       free(reply);
+       return ret;
+}
+
+void check_request(xcb_connection_t *dpy, xcb_void_cookie_t cookie, char *msg)
+{
+       xcb_generic_error_t *err = xcb_request_check(dpy, cookie);
+       if (err != NULL) {
+               fprintf(stderr, "%s: error code: %u.\n", msg, err->error_code);
+               xcb_disconnect(dpy);
+               exit(-1);
+       }
+}
+
+xcb_gc_t get_font_gc(xcb_connection_t *dpy, xcb_window_t win, const char *font_name)
+{
+       xcb_void_cookie_t ck;
+       xcb_font_t font = xcb_generate_id(dpy);
+       ck = xcb_open_font_checked(dpy, font, strlen(font_name), font_name);
+       check_request(dpy, ck, "Can't open font");
+       xcb_gcontext_t gc = xcb_generate_id(dpy);
+       uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
+       uint32_t values[] = {0xffcccccc, 0xff111111, font};
+       xcb_create_gc(dpy, gc, win, mask, values);
+       xcb_close_font(dpy, font);
+       return gc;
+}
+
+void render_text(xcb_connection_t *dpy, xcb_window_t win, int16_t x, int16_t y)
+{
+       char id[8];
+       xcb_void_cookie_t ck;
+       snprintf(id, sizeof(id), "%07x", win);
+       xcb_gcontext_t gc = get_font_gc(dpy, win, "-*-fixed-medium-*-*-*-18-*-*-*-*-*-*-*");
+       /* Don't work with the _checked ! */
+       ck = xcb_image_text_8_checked(dpy, strlen(id), win, gc, x, y, id);
+       check_request(dpy, ck, "Can't draw text");
+       xcb_free_gc(dpy, gc);
+}
+
+
+int main(void)
+{
+       xcb_connection_t *dpy = xcb_connect(NULL, NULL);
+       if (dpy == NULL) {
+               fprintf(stderr, "Can't connect to X.\n");
+               return EXIT_FAILURE;
+       }
+       xcb_atom_t WM_PROTOCOLS, WM_DELETE_WINDOW;
+       if (!get_atom(dpy, "WM_PROTOCOLS", &WM_PROTOCOLS) ||
+           !get_atom(dpy, "WM_DELETE_WINDOW", &WM_DELETE_WINDOW)) {
+               fprintf(stderr, "Can't get required atoms.\n");
+               xcb_disconnect(dpy);
+               return EXIT_FAILURE;
+       }
+       xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
+       if (screen == NULL) {
+               fprintf(stderr, "Can't get current screen.\n");
+               xcb_disconnect(dpy);
+               return EXIT_FAILURE;
+       }
+       xcb_window_t win = xcb_generate_id(dpy);
+       uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+       uint32_t values[] = {0xff111111, XCB_EVENT_MASK_EXPOSURE};
+       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, screen->root, 0, 0, 320, 240, 2,
+                         XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
+       xcb_icccm_set_wm_class(dpy, win, sizeof(TEST_WINDOW_IC), TEST_WINDOW_IC);
+       xcb_map_window(dpy, win);
+       xcb_flush(dpy);
+       xcb_generic_event_t *evt;
+       bool running = true;
+       while (running && (evt = xcb_wait_for_event(dpy)) != NULL) {
+               uint8_t rt = XCB_EVENT_RESPONSE_TYPE(evt);
+               if (rt == XCB_CLIENT_MESSAGE)  {
+                       xcb_client_message_event_t *cme = (xcb_client_message_event_t *) evt;
+                       if (cme->type == WM_PROTOCOLS && cme->data.data32[0] == WM_DELETE_WINDOW) {
+                               running = false;
+                       }
+               } else if (rt == XCB_EXPOSE) {
+                       render_text(dpy, win, 12, 24);
+               }
+               free(evt);
+       }
+       xcb_destroy_window(dpy, win);
+       xcb_disconnect(dpy);
+       return EXIT_SUCCESS;
+}
diff --git a/tree.c b/tree.c
index 775ea23635ad4acf5fdfc0ff9e1198edbaf82e63..15cccb0ff620e86d71ab32661979ac2ea8858999 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -22,6 +22,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
 #include <float.h>
 #include <limits.h>
 #include "bspwm.h"
 
 void arrange(monitor_t *m, desktop_t *d)
 {
-       if (d->root == NULL)
+       if (d->root == NULL) {
                return;
+       }
 
        layout_t set_layout = d->layout;
-       if (leaf_monocle && tiled_count(d) == 1) {
+
+       if (single_monocle && tiled_count(d->root) == 1) {
                d->layout = LAYOUT_MONOCLE;
        }
 
@@ -52,6 +57,7 @@ void arrange(monitor_t *m, desktop_t *d)
        rect.y += m->top_padding + d->top_padding + wg;
        rect.width -= m->left_padding + d->left_padding + d->right_padding + m->right_padding + wg;
        rect.height -= m->top_padding + d->top_padding + d->bottom_padding + m->bottom_padding + wg;
+
        apply_layout(m, d, d->root, rect, rect);
 
        d->layout = set_layout;
@@ -59,11 +65,23 @@ void arrange(monitor_t *m, desktop_t *d)
 
 void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return;
+       }
 
        n->rectangle = rect;
 
+       if (pointer_follows_focus && mon->desk->focus == n && frozen_pointer->action == ACTION_NONE) {
+               xcb_rectangle_t r = rect;
+               r.width -= d->window_gap;
+               r.height -= d->window_gap;
+               center_pointer(r);
+       }
+
+       if (n->presel != NULL) {
+               draw_presel_feedback(m, d, n);
+       }
+
        if (is_leaf(n)) {
 
                unsigned int bw;
@@ -104,18 +122,12 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
                        r = m->rectangle;
                }
 
-               window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
-               window_border_width(n->client->window, bw);
-               window_draw_border(n, d->focus == n, m == mon);
+               window_move_resize(n->id, r.x, r.y, r.width, r.height);
+               window_border_width(n->id, bw);
 
                if (frozen_pointer->action == ACTION_NONE) {
-                       put_status(SBSC_MASK_WINDOW_GEOMETRY, "window_geometry %s %s 0x%X %ux%u+%i+%i\n", m->name, d->name, n->client->window, r.width, r.height, r.x, r.y);
-               }
-
-               if (pointer_follows_focus && mon->desk->focus == n && frozen_pointer->action == ACTION_NONE) {
-                       center_pointer(r);
+                       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry %s %s 0x%X %ux%u+%i+%i\n", m->name, d->name, n->id, r.width, r.height, r.x, r.y);
                }
-
        } else {
                xcb_rectangle_t first_rect;
                xcb_rectangle_t second_rect;
@@ -140,245 +152,335 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
        }
 }
 
-void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+presel_t *make_presel(void)
+{
+       presel_t *p = malloc(sizeof(presel_t));
+       p->split_dir = DIR_EAST;
+       p->split_ratio = split_ratio;
+       p->feedback = XCB_NONE;
+       return p;
+}
+
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)
+{
+       if (n->presel == NULL) {
+               n->presel = make_presel();
+       }
+
+       n->presel->split_dir = dir;
+
+       put_status(SBSC_MASK_NODE_PRESEL, "node_presel %s %s 0x%X dir %s\n", m->name, d->name, n->id, SPLIT_DIR_STR(dir));
+}
+
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)
+{
+       if (n->presel == NULL) {
+               n->presel = make_presel();
+       }
+
+       n->presel->split_ratio = ratio;
+
+       put_status(SBSC_MASK_NODE_PRESEL, "node_presel %s %s 0x%X ratio %lf\n", m->name, d->name, n->id, ratio);
+}
+
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (d == NULL || n == NULL)
+       if (n->presel == NULL) {
                return;
+       }
+
+       if (n->presel->feedback != XCB_NONE) {
+               xcb_destroy_window(dpy, n->presel->feedback);
+       }
+
+       free(n->presel);
+       n->presel = NULL;
 
-       /* n: new leaf node */
-       /* c: new container node */
+       put_status(SBSC_MASK_NODE_PRESEL, "node_presel %s %s 0x%X cancel\n", m->name, d->name, n->id);
+}
+
+node_t *find_public(desktop_t *d)
+{
+       unsigned int b_manual_area = 0;
+       unsigned int b_automatic_area = 0;
+       node_t *b_manual = NULL;
+       node_t *b_automatic = NULL;
+       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+               if (n->vacant) {
+                       continue;
+               }
+               unsigned int n_area = node_area(NULL, d, n);
+               if (n_area > b_manual_area && (n->presel != NULL || !n->private)) {
+                       b_manual = n;
+                       b_manual_area = n_area;
+               }
+               if (n_area > b_automatic_area &&
+                   n->presel == NULL && !n->private && private_count(n->parent) == 0) {
+                       b_automatic = n;
+                       b_automatic_area = n_area;
+               }
+       }
+       if (b_automatic != NULL) {
+               return b_automatic;
+       } else {
+               return b_manual;
+       }
+}
+
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+{
+       if (d == NULL || n == NULL) {
+               return NULL;
+       }
+
+       /* n: inserted node */
+       /* c: new internal node */
        /* f: focus or insertion anchor */
        /* p: parent of focus */
        /* g: grand parent of focus */
 
-       if (f == NULL)
+       if (f == NULL) {
                f = d->root;
+       }
 
        if (f == NULL) {
                d->root = n;
        } else {
-               node_t *c = make_node();
+               node_t *c = make_node(XCB_NONE);
                node_t *p = f->parent;
-               if ((f->client->private ||
-                    (p != NULL && p->privacy_level > 0)) &&
-                   f->split_mode == MODE_AUTOMATIC) {
-                       node_t *closest = NULL;
-                       node_t *public = NULL;
-                       closest_public(d, f, &closest, &public);
-                       if (public != NULL) {
-                               f = public;
+               if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+                       node_t *k = find_public(d);
+                       if (k != NULL) {
+                               f = k;
                                p = f->parent;
-                       } else {
-                               if (closest != NULL) {
-                                       f = closest;
-                                       p = f->parent;
-                               }
-                               f->split_mode = MODE_MANUAL;
-                               xcb_rectangle_t rect = f->client->tiled_rectangle;
-                               f->split_dir = (rect.width >= rect.height ? DIR_LEFT : DIR_UP);
-                               if (f->client->private) {
-                                       get_opposite(f->split_dir, &f->split_dir);
-                                       update_privacy_level(f, false);
-                               }
+                       }
+                       if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+                               xcb_rectangle_t rect = get_rectangle(m, d, f);
+                               presel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));
                        }
                }
-               if (p != NULL && f->split_mode == MODE_AUTOMATIC &&
-                   (p->first_child->vacant || p->second_child->vacant)) {
+               if (f->presel == NULL && p != NULL && p->vacant) {
                        f = p;
                        p = f->parent;
                }
                n->parent = c;
                c->birth_rotation = f->birth_rotation;
-               switch (f->split_mode) {
-                       case MODE_AUTOMATIC:
-                               if (p == NULL) {
-                                       if (initial_polarity == FIRST_CHILD) {
-                                               c->first_child = n;
-                                               c->second_child = f;
+               if (f->presel == NULL) {
+                       if (p == NULL) {
+                               if (initial_polarity == FIRST_CHILD) {
+                                       c->first_child = n;
+                                       c->second_child = f;
+                               } else {
+                                       c->first_child = f;
+                                       c->second_child = n;
+                               }
+                               if (m->rectangle.width > m->rectangle.height) {
+                                       c->split_type = TYPE_VERTICAL;
+                               } else {
+                                       c->split_type = TYPE_HORIZONTAL;
+                               }
+                               f->parent = c;
+                               d->root = c;
+                       } else {
+                               node_t *g = p->parent;
+                               c->parent = g;
+                               if (g != NULL) {
+                                       if (is_first_child(p)) {
+                                               g->first_child = c;
                                        } else {
-                                               c->first_child = f;
-                                               c->second_child = n;
+                                               g->second_child = c;
                                        }
-                                       if (m->rectangle.width > m->rectangle.height)
-                                               c->split_type = TYPE_VERTICAL;
-                                       else
-                                               c->split_type = TYPE_HORIZONTAL;
-                                       f->parent = c;
+                               } else {
                                        d->root = c;
+                               }
+                               c->split_type = p->split_type;
+                               c->split_ratio = p->split_ratio;
+                               p->parent = c;
+                               int rot;
+                               if (is_first_child(f)) {
+                                       c->first_child = n;
+                                       c->second_child = p;
+                                       rot = 90;
                                } else {
-                                       node_t *g = p->parent;
-                                       c->parent = g;
-                                       if (g != NULL) {
-                                               if (is_first_child(p))
-                                                       g->first_child = c;
-                                               else
-                                                       g->second_child = c;
-                                       } else {
-                                               d->root = c;
-                                       }
-                                       c->split_type = p->split_type;
-                                       c->split_ratio = p->split_ratio;
-                                       p->parent = c;
-                                       int rot;
-                                       if (is_first_child(f)) {
-                                               c->first_child = n;
-                                               c->second_child = p;
-                                               rot = 90;
-                                       } else {
-                                               c->first_child = p;
-                                               c->second_child = n;
-                                               rot = 270;
-                                       }
-                                       if (IS_TILED(n->client)) {
-                                               rotate_tree(p, rot);
-                                       }
-                                       n->birth_rotation = rot;
+                                       c->first_child = p;
+                                       c->second_child = n;
+                                       rot = 270;
                                }
-                               break;
-                       case MODE_MANUAL:
-                               if (p != NULL) {
-                                       if (is_first_child(f))
-                                               p->first_child = c;
-                                       else
-                                               p->second_child = c;
+                               n->birth_rotation = rot;
+                               if (!n->vacant) {
+                                       rotate_tree(p, rot);
                                }
-                               c->split_ratio = f->split_ratio;
-                               c->parent = p;
-                               f->parent = c;
-                               f->birth_rotation = 0;
-                               switch (f->split_dir) {
-                                       case DIR_LEFT:
-                                               c->split_type = TYPE_VERTICAL;
-                                               c->first_child = n;
-                                               c->second_child = f;
-                                               break;
-                                       case DIR_RIGHT:
-                                               c->split_type = TYPE_VERTICAL;
-                                               c->first_child = f;
-                                               c->second_child = n;
-                                               break;
-                                       case DIR_UP:
-                                               c->split_type = TYPE_HORIZONTAL;
-                                               c->first_child = n;
-                                               c->second_child = f;
-                                               break;
-                                       case DIR_DOWN:
-                                               c->split_type = TYPE_HORIZONTAL;
-                                               c->first_child = f;
-                                               c->second_child = n;
-                                               break;
+                       }
+               } else {
+                       if (p != NULL) {
+                               if (is_first_child(f)) {
+                                       p->first_child = c;
+                               } else {
+                                       p->second_child = c;
                                }
-                               if (d->root == f)
-                                       d->root = c;
-                               f->split_mode = MODE_AUTOMATIC;
-                               break;
+                       }
+                       c->split_ratio = f->presel->split_ratio;
+                       c->parent = p;
+                       f->parent = c;
+                       f->birth_rotation = 0;
+                       switch (f->presel->split_dir) {
+                               case DIR_WEST:
+                                       c->split_type = TYPE_VERTICAL;
+                                       c->first_child = n;
+                                       c->second_child = f;
+                                       break;
+                               case DIR_EAST:
+                                       c->split_type = TYPE_VERTICAL;
+                                       c->first_child = f;
+                                       c->second_child = n;
+                                       break;
+                               case DIR_NORTH:
+                                       c->split_type = TYPE_HORIZONTAL;
+                                       c->first_child = n;
+                                       c->second_child = f;
+                                       break;
+                               case DIR_SOUTH:
+                                       c->split_type = TYPE_HORIZONTAL;
+                                       c->first_child = f;
+                                       c->second_child = n;
+                                       break;
+                       }
+                       if (d->root == f) {
+                               d->root = c;
+                       }
+                       cancel_presel(m, d, f);
                }
                if (f->vacant) {
-                       propagate_vacant_state(f);
-               }
-               if (f->client != NULL) {
-                       if (f->client->private) {
-                               update_privacy_level(f, true);
-                       }
+                       propagate_vacant_state(m, d, f);
                }
        }
-       if (n->client->private) {
-               update_privacy_level(n, true);
-       }
+
        if (d->focus == NULL) {
                d->focus = n;
        }
-       if (n->client->sticky) {
-               m->num_sticky++;
-       }
-       put_status(SBSC_MASK_REPORT);
+
+       return f;
 }
 
 void activate_node(monitor_t *m, desktop_t *d, node_t *n)
 {
+       if (n == NULL && d->root != NULL) {
+               n = history_last_node(d, NULL);
+               if (n == NULL) {
+                       n = first_extrema(d->root);
+               }
+       }
+
        if (n != NULL) {
-               if (d->focus != NULL && n != d->focus && stack_cmp(n->client, d->focus->client) < 0) {
-                       neutralize_obscuring_windows(m, d, n);
+               if (d->focus != NULL && n != d->focus) {
+                       neutralize_occluding_windows(m, d, n);
                }
-               stack(n, true);
+               stack(d, n, true);
                if (d->focus != n) {
-                       window_draw_border(d->focus, false, m == mon);
-                       window_draw_border(n, true, m == mon);
+                       for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+                               if (!is_descendant(f, n)) {
+                                       window_draw_border(f->id, get_border_color(false, (m == mon)));
+                               }
+                       }
                }
+               draw_border(n, true, (m == mon));
        }
+
        d->focus = n;
-       put_status(SBSC_MASK_WINDOW_ACTIVATE, "window_activate %s %s 0x%X\n", m->name, d->name, n!=NULL?n->client->window:0);
+
+       put_status(SBSC_MASK_NODE_ACTIVATE, "node_activate %s %s 0x%X\n", m->name, d->name, n!=NULL?n->id:0);
+       put_status(SBSC_MASK_REPORT);
+}
+
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else if (n->sticky) {
+               transfer_node(m, ds, n, m, dd, dd->focus);
+       } else {
+               /* we need references to the children because n might be freed after
+                * the first recursive call */
+               node_t *first_child = n->first_child;
+               node_t *second_child = n->second_child;
+               transfer_sticky_nodes(m, ds, dd, first_child);
+               transfer_sticky_nodes(m, ds, dd, second_child);
+       }
 }
 
 void focus_node(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (mon->desk != d || n == NULL)
+       if (n == NULL && d->root != NULL) {
+               n = history_last_node(d, NULL);
+               if (n == NULL) {
+                       n = first_extrema(d->root);
+               }
+       }
+
+       if (mon->desk != d || n == NULL || n->client == NULL) {
                clear_input_focus();
+       }
 
-       if (m->num_sticky > 0 && d != m->desk) {
-               node_t *a = first_extrema(m->desk->root);
+       if (m->sticky_count > 0 && d != m->desk) {
                sticky_still = false;
-               while (a != NULL) {
-                       node_t *b = next_leaf(a, m->desk->root);
-                       if (a->client->sticky)
-                               transfer_node(m, m->desk, a, m, d, d->focus);
-                       a = b;
-               }
+               transfer_sticky_nodes(m, m->desk, d, m->desk->root);
                sticky_still = true;
-               if (n == NULL && d->focus != NULL)
+               if (n == NULL && d->focus != NULL) {
                        n = d->focus;
+               }
        }
 
-       if (n != NULL) {
-               if (n->client->urgent) {
-                       n->client->urgent = false;
-                       put_status(SBSC_MASK_REPORT);
-               }
-               if (d->focus != NULL && n != d->focus && stack_cmp(n->client, d->focus->client) < 0) {
-                       neutralize_obscuring_windows(m, d, n);
-               }
+       if (d->focus != NULL && n != d->focus) {
+               neutralize_occluding_windows(m, d, n);
+       }
+
+       if (n != NULL && n->client != NULL && n->client->urgent) {
+               set_urgent(m, d, n, false);
        }
 
        if (mon != m) {
-               for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next) {
-                       window_draw_border(cd->focus, true, false);
+               for (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {
+                       draw_border(e->focus, true, false);
                }
-               for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next) {
-                       if (cd != d) {
-                               window_draw_border(cd->focus, true, true);
+               for (desktop_t *e = m->desk_head; e != NULL; e = e->next) {
+                       if (e == d) {
+                               continue;
                        }
-               }
-               if (d->focus == n) {
-                       window_draw_border(n, true, true);
+                       draw_border(e->focus, true, true);
                }
        }
 
        if (d->focus != n) {
-               window_draw_border(d->focus, false, true);
-               window_draw_border(n, true, true);
+               for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+                       if (!is_descendant(f, n)) {
+                               window_draw_border(f->id, get_border_color(false, true));
+                       }
+               }
        }
 
+       draw_border(n, true, true);
+
        focus_desktop(m, d);
 
        d->focus = n;
+       ewmh_update_active_window();
+       history_add(m, d, n);
+
+       put_status(SBSC_MASK_REPORT);
 
        if (n == NULL) {
-               history_add(m, d, NULL);
-               ewmh_update_active_window();
                return;
-       } else {
-               stack(n, true);
        }
 
-       put_status(SBSC_MASK_WINDOW_FOCUS, "window_focus %s %s 0x%X\n", m->name, d->name, n->client->window);
+       put_status(SBSC_MASK_NODE_FOCUS, "node_focus %s %s 0x%X\n", m->name, d->name, n->id);
 
-       history_add(m, d, n);
+       stack(d, n, true);
        set_input_focus(n);
 
        if (focus_follows_pointer) {
                xcb_window_t win = XCB_NONE;
                query_pointer(&win, NULL);
-               if (win != n->client->window) {
+               if (win != n->id) {
                        enable_motion_recorder();
                } else {
                        disable_motion_recorder();
@@ -386,45 +488,83 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
        }
 
        if (pointer_follows_focus) {
-               center_pointer(get_rectangle(m, n->client));
+               center_pointer(get_rectangle(m, d, n));
        }
-
-       ewmh_update_active_window();
 }
 
-void update_current(void)
+void update_focused(void)
 {
        focus_node(mon, mon->desk, mon->desk->focus);
 }
 
-node_t *make_node(void)
+void hide_node(node_t *n)
 {
+       if (n == NULL) {
+               return;
+       } else {
+               if (n->presel != NULL) {
+                       window_hide(n->presel->feedback);
+               }
+               if (n->client != NULL) {
+                       window_hide(n->id);
+               }
+               hide_node(n->first_child);
+               hide_node(n->second_child);
+       }
+}
+
+void show_node(node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (n->client != NULL) {
+                       window_show(n->id);
+               }
+               if (n->presel != NULL) {
+                       window_show(n->presel->feedback);
+               }
+               show_node(n->first_child);
+               show_node(n->second_child);
+       }
+}
+
+node_t *make_node(uint32_t id)
+{
+       if (id == XCB_NONE) {
+               id = xcb_generate_id(dpy);
+       }
        node_t *n = malloc(sizeof(node_t));
+       n->id = id;
        n->parent = n->first_child = n->second_child = NULL;
+       n->vacant = n->sticky = n->private = n->locked = false;
        n->split_ratio = split_ratio;
-       n->split_mode = MODE_AUTOMATIC;
        n->split_type = TYPE_VERTICAL;
-       n->split_dir = DIR_RIGHT;
        n->birth_rotation = 0;
-       n->privacy_level = 0;
+       n->presel = NULL;
        n->client = NULL;
-       n->vacant = false;
        return n;
 }
 
-client_t *make_client(xcb_window_t win, unsigned int border_width)
+client_t *make_client(void)
 {
        client_t *c = malloc(sizeof(client_t));
-       c->window = win;
        c->state = c->last_state = STATE_TILED;
        c->layer = c->last_layer = LAYER_NORMAL;
        snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
        snprintf(c->instance_name, sizeof(c->instance_name), "%s", MISSING_VALUE);
        c->border_width = border_width;
-       c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false;
+       c->urgent = c->icccm_focus = false;
        c->icccm_input = true;
-       c->num_states = 0;
+       c->wm_states_count = 0;
+       return c;
+}
+
+void initialize_client(node_t *n)
+{
+       xcb_window_t win = n->id;
        if (win != XCB_NONE) {
+               client_t *c = n->client;
                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)) {
@@ -434,8 +574,8 @@ client_t *make_client(xcb_window_t win, unsigned int border_width)
                }
                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]);
+                       for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_WM_STATES; i++) {
+                               ewmh_wm_state_add(n, wm_state.atoms[i]);
                        }
                        xcb_ewmh_get_atoms_reply_wipe(&wm_state);
                }
@@ -445,7 +585,6 @@ client_t *make_client(xcb_window_t win, unsigned int border_width)
                        c->icccm_input = hints.input;
                }
        }
-       return c;
 }
 
 bool is_leaf(node_t *n)
@@ -463,74 +602,49 @@ bool is_second_child(node_t *n)
        return (n != NULL && n->parent != NULL && n->parent->second_child == n);
 }
 
-void reset_mode(coordinates_t *loc)
+unsigned int clients_count_in(node_t *n)
 {
-       if (loc->node != NULL) {
-               loc->node->split_mode = MODE_AUTOMATIC;
-               window_draw_border(loc->node, loc->desktop->focus == loc->node, mon == loc->monitor);
-       } else if (loc->desktop != NULL) {
-               for (node_t *a = first_extrema(loc->desktop->root); a != NULL; a = next_leaf(a, loc->desktop->root)) {
-                       a->split_mode = MODE_AUTOMATIC;
-                       window_draw_border(a, loc->desktop->focus == a, mon == loc->monitor);
-               }
+       if (n == NULL) {
+               return 0;
+       } else {
+               return (n->client != NULL ? 1 : 0) +
+                       clients_count_in(n->first_child) +
+                       clients_count_in(n->second_child);
        }
 }
 
 node_t *brother_tree(node_t *n)
 {
-       if (n == NULL || n->parent == NULL)
+       if (n == NULL || n->parent == NULL) {
                return NULL;
-       if (is_first_child(n))
+       }
+       if (is_first_child(n)) {
                return n->parent->second_child;
-       else
+       } else {
                return n->parent->first_child;
-}
-
-void closest_public(desktop_t *d, node_t *n, node_t **closest, node_t **public)
-{
-       if (n == NULL)
-               return;
-       node_t *prev = prev_leaf(n, d->root);
-       node_t *next = next_leaf(n, d->root);
-       while (prev != NULL || next != NULL) {
-#define TESTLOOP(n) \
-               if (n != NULL) { \
-                       if (IS_TILED(n->client)) { \
-                               if (n->privacy_level == 0) { \
-                                       if (n->parent == NULL || n->parent->privacy_level == 0) { \
-                                               *public = n; \
-                                               return; \
-                                       } else if (*closest == NULL) { \
-                                               *closest = n; \
-                                       } \
-                               } \
-                       } \
-                       n = n##_leaf(n, d->root); \
-               }
-               TESTLOOP(prev)
-               TESTLOOP(next)
-#undef TESTLOOP
        }
 }
 
 node_t *first_extrema(node_t *n)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
-       else if (n->first_child == NULL)
+       } else if (n->first_child == NULL) {
                return n;
-       else
+       } else {
                return first_extrema(n->first_child);
+       }
 }
 
 node_t *second_extrema(node_t *n)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
-       else if (n->second_child == NULL)
+       } else if (n->second_child == NULL) {
                return n;
-       else
+       } else {
                return second_extrema(n->second_child);
+       }
 }
 
 node_t *next_leaf(node_t *n, node_t *r)
@@ -550,48 +664,53 @@ node_t *next_leaf(node_t *n, node_t *r)
 
 node_t *prev_leaf(node_t *n, node_t *r)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
+       }
        node_t *p = n;
-       while (is_first_child(p) && p != r)
+       while (is_first_child(p) && p != r) {
                p = p->parent;
-       if (p == r)
+       }
+       if (p == r) {
                return NULL;
+       }
        return second_extrema(p->parent->first_child);
 }
 
 node_t *next_tiled_leaf(desktop_t *d, node_t *n, node_t *r)
 {
        node_t *next = next_leaf(n, r);
-       if (next == NULL || IS_TILED(next->client))
+       if (next == NULL || IS_TILED(next->client)) {
                return next;
-       else
+       } else {
                return next_tiled_leaf(d, next, r);
+       }
 }
 
 node_t *prev_tiled_leaf(desktop_t *d, node_t *n, node_t *r)
 {
        node_t *prev = prev_leaf(n, r);
-       if (prev == NULL || IS_TILED(prev->client))
+       if (prev == NULL || IS_TILED(prev->client)) {
                return prev;
-       else
+       } else {
                return prev_tiled_leaf(d, prev, r);
+       }
 }
 
 /* Returns true if *b* is adjacent to *a* in the direction *dir* */
 bool is_adjacent(node_t *a, node_t *b, direction_t dir)
 {
        switch (dir) {
-               case DIR_RIGHT:
+               case DIR_EAST:
                        return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
                        break;
-               case DIR_DOWN:
+               case DIR_SOUTH:
                        return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
                        break;
-               case DIR_LEFT:
+               case DIR_WEST:
                        return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
                        break;
-               case DIR_UP:
+               case DIR_NORTH:
                        return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
                        break;
        }
@@ -602,16 +721,17 @@ node_t *find_fence(node_t *n, direction_t dir)
 {
        node_t *p;
 
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
+       }
 
        p = n->parent;
 
        while (p != NULL) {
-               if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
-                   (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
-                   (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
-                   (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
+               if ((dir == DIR_NORTH && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
+                   (dir == DIR_WEST && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
+                   (dir == DIR_SOUTH && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
+                   (dir == DIR_EAST && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
                        return p;
                p = p->parent;
        }
@@ -619,15 +739,17 @@ node_t *find_fence(node_t *n, direction_t dir)
        return NULL;
 }
 
-node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel)
 {
-       if (n == NULL || IS_FULLSCREEN(n->client) ||
-           (d->layout == LAYOUT_MONOCLE && IS_TILED(n->client)))
+       if (n == NULL || (n->client != NULL && IS_FULLSCREEN(n->client)) ||
+           (d->layout == LAYOUT_MONOCLE && (n->client != NULL && IS_TILED(n->client)))) {
                return NULL;
+       }
 
        node_t *nearest = NULL;
-       if (history_aware_focus)
+       if (history_aware_focus) {
                nearest = nearest_from_history(m, d, n, dir, sel);
+       }
     if (nearest == NULL) {
         if (focus_by_distance) {
             nearest = nearest_from_distance(m, d, n, dir, sel);
@@ -638,7 +760,60 @@ node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir,
     return nearest;
 }
 
-node_t *nearest_from_tree(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+/* returns *true* if *a* is a child of *b* */
+bool is_child(node_t *a, node_t *b)
+{
+       if (a == NULL || b == NULL) {
+               return false;
+       }
+       return (a->parent != NULL && a->parent == b);
+}
+
+/* returns *true* if *a* is a descendant of *b* */
+bool is_descendant(node_t *a, node_t *b)
+{
+       if (a == NULL || b == NULL) {
+               return false;
+       }
+       while (a != b && a != NULL) {
+               a = a->parent;
+       }
+       return a == b;
+}
+
+bool find_by_id(uint32_t id, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       node_t *n = find_by_id_in(d->root, id);
+                       if (n != NULL) {
+                               loc->monitor = m;
+                               loc->desktop = d;
+                               loc->node = n;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+node_t *find_by_id_in(node_t *r, uint32_t id)
+{
+       if (r == NULL) {
+               return NULL;
+       } else if (r->id == id) {
+               return r;
+       } else {
+               node_t *f = find_by_id_in(r->first_child, id);
+               if (f != NULL) {
+                       return f;
+               } else {
+                       return find_by_id_in(r->second_child, id);
+               }
+       }
+}
+
+node_t *nearest_from_tree(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel)
 {
     if (n == NULL) {
         return NULL;
@@ -646,14 +821,15 @@ node_t *nearest_from_tree(monitor_t *m, desktop_t *d, node_t *n, direction_t dir
 
     node_t *fence = find_fence(n, dir);
 
-    if (fence == NULL)
+    if (fence == NULL) {
         return NULL;
+    }
 
     node_t *nearest = NULL;
 
-    if (dir == DIR_UP || dir == DIR_LEFT) {
+    if (dir == DIR_NORTH || dir == DIR_WEST) {
         nearest = second_extrema(fence->first_child);
-    } else if (dir == DIR_DOWN || dir == DIR_RIGHT) {
+    } else if (dir == DIR_SOUTH || dir == DIR_EAST) {
         nearest = first_extrema(fence->second_child);
     }
 
@@ -667,30 +843,34 @@ node_t *nearest_from_tree(monitor_t *m, desktop_t *d, node_t *n, direction_t dir
        }
 }
 
-node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel)
 {
-       if (n == NULL || !IS_TILED(n->client)) {
+       if (n == NULL || (n->client != NULL && !IS_TILED(n->client))) {
                return NULL;
        }
 
        node_t *target = find_fence(n, dir);
-       if (target == NULL)
+       if (target == NULL) {
                return NULL;
-       if (dir == DIR_UP || dir == DIR_LEFT)
+       }
+       if (dir == DIR_NORTH || dir == DIR_WEST) {
                target = target->first_child;
-       else if (dir == DIR_DOWN || dir == DIR_RIGHT)
+       } else if (dir == DIR_SOUTH || dir == DIR_EAST) {
                target = target->second_child;
+       }
 
        node_t *nearest = NULL;
        int min_rank = INT_MAX;
        coordinates_t ref = {m, d, n};
 
        for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
-               if (a->vacant || !is_adjacent(n, a, dir) || a == n)
+               if (a->vacant || !is_adjacent(n, a, dir) || a == n) {
                        continue;
+               }
                coordinates_t loc = {m, d, a};
-               if (!node_matches(&loc, &ref, sel))
+               if (!node_matches(&loc, &ref, sel)) {
                        continue;
+               }
 
                int rank = history_rank(d, a);
                if (rank >= 0 && rank < min_rank) {
@@ -702,21 +882,24 @@ node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t
        return nearest;
 }
 
-node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
+       }
 
        node_t *target = NULL;
 
-       if (IS_TILED(n->client)) {
+       if (n->client != NULL && IS_TILED(n->client)) {
                target = find_fence(n, dir);
-               if (target == NULL)
+               if (target == NULL) {
                        return NULL;
-               if (dir == DIR_UP || dir == DIR_LEFT)
+               }
+               if (dir == DIR_NORTH || dir == DIR_WEST) {
                        target = target->first_child;
-               else if (dir == DIR_DOWN || dir == DIR_RIGHT)
+               } else if (dir == DIR_SOUTH || dir == DIR_EAST) {
                        target = target->second_child;
+               }
        } else {
                target = d->root;
        }
@@ -725,7 +908,7 @@ node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t
        direction_t dir2;
        xcb_point_t pt;
        xcb_point_t pt2;
-       get_side_handle(n->client, dir, &pt);
+       get_side_handle(m, d, n, dir, &pt);
        get_opposite(dir, &dir2);
        double ds = DBL_MAX;
        coordinates_t ref = {m, d, n};
@@ -735,10 +918,11 @@ node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t
                if (a == n ||
                    !node_matches(&loc, &ref, sel) ||
                    IS_TILED(a->client) != IS_TILED(n->client) ||
-                   (IS_TILED(a->client) && !is_adjacent(n, a, dir)))
+                   (IS_TILED(a->client) && !is_adjacent(n, a, dir))) {
                        continue;
+               }
 
-               get_side_handle(a->client, dir2, &pt2);
+               get_side_handle(m, d, a, dir2, &pt2);
                double ds2 = distance(pt, pt2);
                if (ds2 < ds) {
                        ds = ds2;
@@ -752,33 +936,37 @@ node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t
 void get_opposite(direction_t src, direction_t *dst)
 {
        switch (src) {
-               case DIR_RIGHT:
-                       *dst = DIR_LEFT;
+               case DIR_EAST:
+                       *dst = DIR_WEST;
                        break;
-               case DIR_DOWN:
-                       *dst = DIR_UP;
+               case DIR_SOUTH:
+                       *dst = DIR_NORTH;
                        break;
-               case DIR_LEFT:
-                       *dst = DIR_RIGHT;
+               case DIR_WEST:
+                       *dst = DIR_EAST;
                        break;
-               case DIR_UP:
-                       *dst = DIR_DOWN;
+               case DIR_NORTH:
+                       *dst = DIR_SOUTH;
                        break;
        }
 }
 
-int tiled_area(node_t *n)
+unsigned int node_area(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (n == NULL)
-               return -1;
-       xcb_rectangle_t rect = n->client->tiled_rectangle;
+       if (n == NULL) {
+               return 0;
+       }
+       xcb_rectangle_t rect = get_rectangle(m, d, n);
        return rect.width * rect.height;
 }
 
-int tiled_count(desktop_t *d)
+int tiled_count(node_t *n)
 {
+       if (n == NULL) {
+               return 0;
+       }
        int cnt = 0;
-       for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
                if (IS_TILED(f->client)) {
                        cnt++;
                }
@@ -786,36 +974,36 @@ int tiled_count(desktop_t *d)
        return cnt;
 }
 
-node_t *find_biggest(monitor_t *m, desktop_t *d, node_t *n, client_select_t sel)
+node_t *find_biggest(monitor_t *m, desktop_t *d, node_t *n, node_select_t sel)
 {
-       if (d == NULL)
+       if (d == NULL) {
                return NULL;
+       }
 
-       node_t *r = NULL;
-       int r_area = tiled_area(r);
+       node_t *b = NULL;
+       unsigned int b_area = 0;
        coordinates_t ref = {m, d, n};
 
        for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
                coordinates_t loc = {m, d, f};
-               if (IS_FLOATING(f->client) || !node_matches(&loc, &ref, sel))
+               if (f->vacant || !node_matches(&loc, &ref, sel)) {
                        continue;
-               int f_area = tiled_area(f);
-               if (r == NULL) {
-                       r = f;
-                       r_area = f_area;
-               } else if (f_area > r_area) {
-                       r = f;
-                       r_area = f_area;
+               }
+               unsigned int f_area = node_area(m, d, f);
+               if (f_area > b_area) {
+                       b = f;
+                       b_area = f_area;
                }
        }
 
-       return r;
+       return b;
 }
 
 void rotate_tree(node_t *n, int deg)
 {
-       if (n == NULL || is_leaf(n) || deg == 0)
+       if (n == NULL || is_leaf(n) || deg == 0) {
                return;
+       }
 
        node_t *tmp;
 
@@ -829,10 +1017,11 @@ void rotate_tree(node_t *n, int deg)
        }
 
        if (deg != 180) {
-               if (n->split_type == TYPE_HORIZONTAL)
+               if (n->split_type == TYPE_HORIZONTAL) {
                        n->split_type = TYPE_VERTICAL;
-               else if (n->split_type == TYPE_VERTICAL)
+               } else if (n->split_type == TYPE_VERTICAL) {
                        n->split_type = TYPE_HORIZONTAL;
+               }
        }
 
        rotate_tree(n->first_child, deg);
@@ -846,8 +1035,9 @@ void rotate_brother(node_t *n)
 
 void unrotate_tree(node_t *n, int rot)
 {
-       if (rot == 0)
+       if (rot == 0) {
                return;
+       }
        rotate_tree(n, 360 - rot);
 }
 
@@ -858,8 +1048,9 @@ void unrotate_brother(node_t *n)
 
 void flip_tree(node_t *n, flip_t flp)
 {
-       if (n == NULL || is_leaf(n))
+       if (n == NULL || is_leaf(n)) {
                return;
+       }
 
        node_t *tmp;
 
@@ -896,16 +1087,18 @@ int balance_tree(node_t *n)
                int b1 = balance_tree(n->first_child);
                int b2 = balance_tree(n->second_child);
                int b = b1 + b2;
-               if (b1 > 0 && b2 > 0)
+               if (b1 > 0 && b2 > 0) {
                        n->split_ratio = (double) b1 / b;
+               }
                return b;
        }
 }
 
 void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (d == NULL || n == NULL)
+       if (d == NULL || n == NULL) {
                return;
+       }
 
        node_t *p = n->parent;
 
@@ -913,99 +1106,106 @@ void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
                d->root = NULL;
                d->focus = NULL;
        } else {
-               if (n->client->private)
-                       update_privacy_level(n, false);
+               if (d->focus == p || is_descendant(d->focus, n)) {
+                       d->focus = NULL;
+               }
 
-               node_t *b;
+               node_t *b = brother_tree(n);
                node_t *g = p->parent;
 
-               if (is_first_child(n)) {
-                       b = p->second_child;
-                       if (!n->vacant)
-                               unrotate_tree(b, n->birth_rotation);
-               } else {
-                       b = p->first_child;
-                       if (!n->vacant)
-                               unrotate_tree(b, n->birth_rotation);
+               if (!n->vacant) {
+                       unrotate_tree(b, n->birth_rotation);
                }
 
                b->parent = g;
 
                if (g != NULL) {
-                       if (is_first_child(p))
+                       if (is_first_child(p)) {
                                g->first_child = b;
-                       else
+                       } else {
                                g->second_child = b;
+                       }
                } else {
                        d->root = b;
                }
 
                b->birth_rotation = p->birth_rotation;
                n->parent = NULL;
-               free(p);
-               propagate_vacant_state(b);
 
-               if (n == d->focus) {
-                       d->focus = history_get_node(d, n);
-                       // fallback to the first extrema (`n` is not reachable)
-                       if (d->focus == NULL)
-                               d->focus = first_extrema(d->root);
+               history_remove(d, p, false);
+               cancel_presel(m, d, p);
+               if (p->sticky) {
+                       m->sticky_count--;
                }
-       }
+               free(p);
 
-       if (n->client->sticky) {
-               m->num_sticky--;
+               propagate_vacant_state(m, d, b);
        }
-
-       put_status(SBSC_MASK_REPORT);
 }
 
 void remove_node(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return;
+       }
 
-       bool focused = (n == mon->desk->focus);
+       node_t *last_focus = d->focus;
 
        unlink_node(m, d, n);
-       history_remove(d, n);
+       history_remove(d, n, true);
        remove_stack_node(n);
+       cancel_presel(m, d, n);
+       if (m->sticky_count > 0) {
+               m->sticky_count -= sticky_count(n);
+       }
+       clients_count -= clients_count_in(n);
        free(n->client);
        free(n);
 
-       num_clients--;
-       ewmh_update_client_list();
+       ewmh_update_client_list(false);
 
-       if (focused) {
-               update_current();
+       if (d->focus != last_focus) {
+               if (d == mon->desk) {
+                       focus_node(m, d, d->focus);
+               } else {
+                       activate_node(m, d, d->focus);
+               }
        }
 }
 
-void destroy_tree(node_t *n)
+void destroy_tree(monitor_t *m, desktop_t *d, node_t *n)
 {
        if (n == NULL) {
                return;
        }
-       node_t *first_tree = n->first_child;
-       node_t *second_tree = n->second_child;
+       node_t *first_child = n->first_child;
+       node_t *second_child = n->second_child;
        if (n->client != NULL) {
                remove_stack_node(n);
-               free(n->client);
-               num_clients--;
+               clients_count--;
+       }
+       if (n->sticky) {
+               m->sticky_count--;
        }
+       cancel_presel(m, d, n);
+       free(n->client);
        free(n);
-       destroy_tree(first_tree);
-       destroy_tree(second_tree);
+       if (first_child != NULL && second_child != NULL) {
+               first_child->parent = second_child->parent = NULL;
+               destroy_tree(m, d, first_child);
+               destroy_tree(m, d, second_child);
+       }
 }
 
 bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
 {
-       if (n1 == NULL || n2 == NULL || n1 == n2
-           || (d1 != d2 && (n1->client->sticky || n2->client->sticky))) {
+       if (n1 == NULL || n2 == NULL || n1 == n2 || is_descendant(n1, n2) || is_descendant(n2, n1) ||
+           (d1 != d2 && ((m1->sticky_count > 0 && sticky_count(n1) > 0) ||
+                         (m2->sticky_count > 0 && sticky_count(n2) > 0)))) {
                return false;
        }
 
-       put_status(SBSC_MASK_WINDOW_SWAP, "window_swap %s %s 0x%X %s %s 0x%X\n", m1->name, d1->name, n1->client->window, m2->name, d2->name, n2->client->window);
+       put_status(SBSC_MASK_NODE_SWAP, "node_swap %s %s 0x%X %s %s 0x%X\n", m1->name, d1->name, n1->id, m2->name, d2->name, n2->id);
 
        node_t *pn1 = n1->parent;
        node_t *pn2 = n2->parent;
@@ -1013,68 +1213,97 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
        bool n2_first_child = is_first_child(n2);
        int br1 = n1->birth_rotation;
        int br2 = n2->birth_rotation;
-       int pl1 = n1->privacy_level;
-       int pl2 = n2->privacy_level;
+       bool n1_held_focus = is_descendant(d1->focus, n1);
+       bool n2_held_focus = is_descendant(d2->focus, n2);
+       node_t *last_d1_focus = d1->focus;
+       node_t *last_d2_focus = d2->focus;
 
        if (pn1 != NULL) {
-               if (n1_first_child)
+               if (n1_first_child) {
                        pn1->first_child = n2;
-               else
+               } else {
                        pn1->second_child = n2;
+               }
        }
 
        if (pn2 != NULL) {
-               if (n2_first_child)
+               if (n2_first_child) {
                        pn2->first_child = n1;
-               else
+               } else {
                        pn2->second_child = n1;
+               }
        }
 
        n1->parent = pn2;
        n2->parent = pn1;
        n1->birth_rotation = br2;
        n2->birth_rotation = br1;
-       n1->privacy_level = pl2;
-       n2->privacy_level = pl1;
 
        if (n1->vacant != n2->vacant) {
-               propagate_vacant_state(n1);
-               propagate_vacant_state(n2);
-       }
-
-       if (n1->client->private != n2->client->private) {
-               n1->client->private = !n1->client->private;
-               n2->client->private = !n2->client->private;
+               propagate_vacant_state(m2, d2, n1);
+               propagate_vacant_state(m1, d1, n2);
        }
 
        if (d1 != d2) {
-               if (d1->root == n1)
+               if (d1->root == n1) {
                        d1->root = n2;
-               if (d1->focus == n1)
-                       d1->focus = n2;
-               if (d2->root == n2)
+               }
+
+               if (d2->root == n2) {
                        d2->root = n1;
-               if (d2->focus == n2)
-                       d2->focus = n1;
+               }
+
+               if (n1_held_focus) {
+                       d1->focus = n2_held_focus ? last_d2_focus : n2;
+               }
+
+               if (n2_held_focus) {
+                       d2->focus = n1_held_focus ? last_d1_focus : n1;
+               }
 
                if (m1 != m2) {
-                       translate_client(m2, m1, n2->client);
-                       translate_client(m1, m2, n1->client);
+                       adapt_geometry(&m2->rectangle, &m1->rectangle, n2);
+                       adapt_geometry(&m1->rectangle, &m2->rectangle, n1);
                }
 
                ewmh_set_wm_desktop(n1, d2);
                ewmh_set_wm_desktop(n2, d1);
+
                history_swap_nodes(m1, d1, n1, m2, d2, n2);
 
                if (m1->desk != d1 && m2->desk == d2) {
-                       window_show(n1->client->window);
-                       window_hide(n2->client->window);
+                       show_node(n1);
+                       hide_node(n2);
                } else if (m1->desk == d1 && m2->desk != d2) {
-                       window_hide(n1->client->window);
-                       window_show(n2->client->window);
+                       hide_node(n1);
+                       show_node(n2);
                }
 
-               update_input_focus();
+               if (n1_held_focus) {
+                       if (d1 == mon->desk) {
+                               focus_node(m1, d1, d1->focus);
+                       } else {
+                               activate_node(m1, d1, d1->focus);
+                       }
+               } else {
+                       draw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));
+               }
+
+               if (n2_held_focus) {
+                       if (d2 == mon->desk) {
+                               focus_node(m2, d2, d2->focus);
+                       } else {
+                               activate_node(m2, d2, d2->focus);
+                       }
+               } else {
+                       draw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));
+               }
+       }
+
+       arrange(m1, d1);
+
+       if (d1 != d2) {
+               arrange(m2, d2);
        }
 
        return true;
@@ -1082,16 +1311,23 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
 
 bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
 {
-       if (ns == NULL || ns == nd || (sticky_still && ns->client->sticky)) {
+       if (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {
+               return false;
+       }
+
+       unsigned int sc = 0;
+
+       if (sticky_still && ms->sticky_count > 0 && ds != dd && (sc = sticky_count(ns)) > 0) {
                return false;
        }
 
-       put_status(SBSC_MASK_WINDOW_TRANSFER, "window_transfer %s %s 0x%X %s %s 0x%X\n", ms->name, ds->name, ns->client->window, md->name, dd->name, nd!=NULL?nd->client->window:0);
+       put_status(SBSC_MASK_NODE_TRANSFER, "node_transfer %s %s 0x%X %s %s 0x%X\n", ms->name, ds->name, ns->id, md->name, dd->name, nd!=NULL?nd->id:0);
 
-       bool focused = (ns == mon->desk->focus);
-       bool active = (ns == ds->focus);
+       bool held_focus = is_descendant(ds->focus, ns);
+       /* avoid ending up with a dangling pointer (because of unlink_node) */
+       node_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;
 
-       if (focused) {
+       if (held_focus && ds == mon->desk) {
                clear_input_focus();
        }
 
@@ -1099,34 +1335,49 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk
        insert_node(md, dd, ns, nd);
 
        if (md != ms) {
-               translate_client(ms, md, ns->client);
+               adapt_geometry(&ms->rectangle, &md->rectangle, ns);
        }
 
        if (ds != dd) {
                ewmh_set_wm_desktop(ns, dd);
-               if (!ns->client->sticky) {
+               if (sc == 0) {
                        if (ds == ms->desk && dd != md->desk) {
-                               window_hide(ns->client->window);
+                               hide_node(ns);
                        } else if (ds != ms->desk && dd == md->desk) {
-                               window_show(ns->client->window);
+                               show_node(ns);
                        }
                }
        }
 
        history_transfer_node(md, dd, ns);
-       stack(ns, false);
+       stack(dd, ns, false);
 
        if (ds == dd) {
-               if (focused) {
-                       focus_node(md, dd, ns);
-               } else if (active) {
-                       activate_node(md, dd, ns);
+               if (held_focus) {
+                       if (ds == mon->desk) {
+                               focus_node(ms, ds, last_ds_focus);
+                       } else {
+                               activate_node(ms, ds, last_ds_focus);
+                       }
+               } else {
+                       draw_border(ns, is_descendant(ns, ds->focus), (ms == mon));
                }
        } else {
-               if (focused) {
-                       update_current();
-               } else if (ns == mon->desk->focus) {
-                       update_input_focus();
+               if (held_focus) {
+                       if (ds == mon->desk) {
+                               focus_node(ms, ds, ds->focus);
+                       } else {
+                               activate_node(ms, ds, ds->focus);
+                       }
+               }
+               if (dd->focus == ns) {
+                       if (dd == mon->desk) {
+                               focus_node(md, dd, held_focus ? last_ds_focus : dd->focus);
+                       } else {
+                               activate_node(md, dd, held_focus ? last_ds_focus : dd->focus);
+                       }
+               } else {
+                       draw_border(ns, is_descendant(ns, dd->focus), (md == mon));
                }
        }
 
@@ -1139,46 +1390,55 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk
        return true;
 }
 
-node_t *closest_node(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
+node_t *closest_node(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, node_select_t sel)
 {
-       if (n == NULL)
+       if (n == NULL) {
                return NULL;
+       }
 
        node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
-       if (f == NULL)
+       if (f == NULL) {
                f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
+       }
 
        coordinates_t ref = {m, d, n};
        while (f != n) {
                coordinates_t loc = {m, d, f};
-               if (node_matches(&loc, &ref, sel))
+               if (node_matches(&loc, &ref, sel)) {
                        return f;
+               }
                f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
-               if (f == NULL)
+               if (f == NULL) {
                        f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
+               }
        }
        return NULL;
 }
 
-void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir)
+void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir)
 {
-       if (d == NULL || d->root == NULL || d->focus == NULL || is_leaf(d->root))
+       if (d == NULL || d->root == NULL || d->focus == NULL || is_leaf(n)) {
                return;
+       }
        node_t *p = d->focus->parent;
        bool focus_first_child = is_first_child(d->focus);
-       if (dir == CIRCULATE_FORWARD)
-               for (node_t *s = second_extrema(d->root), *f = prev_tiled_leaf(d, s, d->root); f != NULL; s = prev_tiled_leaf(d, f, d->root), f = prev_tiled_leaf(d, s, d->root))
+       if (dir == CIRCULATE_FORWARD) {
+               for (node_t *s = second_extrema(n), *f = prev_tiled_leaf(d, s, n); f != NULL; s = prev_tiled_leaf(d, f, n), f = prev_tiled_leaf(d, s, n)) {
                        swap_nodes(m, d, f, m, d, s);
-       else
-               for (node_t *f = first_extrema(d->root), *s = next_tiled_leaf(d, f, d->root); s != NULL; f = next_tiled_leaf(d, s, d->root), s = next_tiled_leaf(d, f, d->root))
+               }
+       } else {
+               for (node_t *f = first_extrema(n), *s = next_tiled_leaf(d, f, n); s != NULL; f = next_tiled_leaf(d, s, n), s = next_tiled_leaf(d, f, n)) {
                        swap_nodes(m, d, f, m, d, s);
-       if (focus_first_child)
+               }
+       }
+       if (focus_first_child) {
                focus_node(m, d, p->first_child);
-       else
+       } else {
                focus_node(m, d, p->second_child);
+       }
 }
 
-void set_vacant_state(node_t *n, bool value)
+void set_vacant_state(monitor_t *m, desktop_t *d, node_t *n, bool value)
 {
        n->vacant = value;
 
@@ -1188,10 +1448,14 @@ void set_vacant_state(node_t *n, bool value)
                rotate_brother(n);
        }
 
-       propagate_vacant_state(n);
+       if (value) {
+               cancel_presel(m, d, n);
+       }
+
+       propagate_vacant_state(m, d, n);
 }
 
-void propagate_vacant_state(node_t *n)
+void propagate_vacant_state(monitor_t *m, desktop_t *d, node_t *n)
 {
        if (n == NULL) {
                return;
@@ -1201,13 +1465,272 @@ void propagate_vacant_state(node_t *n)
 
        while (p != NULL) {
                p->vacant = (p->first_child->vacant && p->second_child->vacant);
+               if (p->vacant) {
+                       cancel_presel(m, d, p);
+               }
                p = p->parent;
        }
 }
 
-void update_privacy_level(node_t *n, bool value)
+void set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
 {
-       int v = (value ? 1 : -1);
-       for (node_t *p = n; p != NULL; p = p->parent)
-               p->privacy_level += v;
+       if (n == NULL || n->client->layer == l) {
+               return;
+       }
+
+       n->client->last_layer = n->client->layer;
+       n->client->layer = l;
+
+       put_status(SBSC_MASK_NODE_LAYER, "node_layer %s %s 0x%X %s\n", m->name, d->name, n->id, LAYER_STR(l));
+
+       if (d->focus == n) {
+               neutralize_occluding_windows(m, d, n);
+       }
+
+       stack(d, n, (d->focus == n));
 }
+
+void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
+{
+       if (n == NULL || n->client->state == s) {
+               return;
+       }
+
+       client_t *c = n->client;
+
+       c->last_state = c->state;
+       c->state = s;
+
+       switch (c->last_state) {
+               case STATE_TILED:
+               case STATE_PSEUDO_TILED:
+                       break;
+               case STATE_FLOATING:
+                       set_floating(m, d, n, false);
+                       break;
+               case STATE_FULLSCREEN:
+                       set_fullscreen(m, d, n, false);
+                       break;
+       }
+
+       put_status(SBSC_MASK_NODE_STATE, "node_state %s %s 0x%X %s off\n", m->name, d->name, n->id, STATE_STR(c->last_state));
+
+       switch (c->state) {
+               case STATE_TILED:
+               case STATE_PSEUDO_TILED:
+                       break;
+               case STATE_FLOATING:
+                       set_floating(m, d, n, true);
+                       break;
+               case STATE_FULLSCREEN:
+                       set_fullscreen(m, d, n, true);
+                       break;
+       }
+
+       put_status(SBSC_MASK_NODE_STATE, "node_state %s %s 0x%X %s on\n", m->name, d->name, n->id, STATE_STR(c->state));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+}
+
+void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       cancel_presel(m, d, n);
+       set_vacant_state(m, d, n, value);
+
+       if (!value && d->focus == n) {
+               neutralize_occluding_windows(m, d, n);
+       }
+
+       stack(d, n, (d->focus == n));
+}
+
+void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       client_t *c = n->client;
+
+       cancel_presel(m, d, n);
+       set_vacant_state(m, d, n, value);
+
+       if (value) {
+               ewmh_wm_state_add(n, ewmh->_NET_WM_STATE_FULLSCREEN);
+               c->last_layer = c->layer;
+               c->layer = LAYER_ABOVE;
+       } else {
+               ewmh_wm_state_remove(n, ewmh->_NET_WM_STATE_FULLSCREEN);
+               c->layer = c->last_layer;
+               if (d->focus == n) {
+                       neutralize_occluding_windows(m, d, n);
+               }
+       }
+
+       stack(d, n, (d->focus == n));
+}
+
+void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n)
+{
+       bool dirty = false;
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
+                       if (a != f && IS_FULLSCREEN(a->client) && stack_cmp(f->client, a->client) < 0) {
+                               set_state(m, d, a, a->client->last_state);
+                               dirty = true;
+                       }
+               }
+       }
+       if (dirty) {
+               arrange(m, d);
+       }
+}
+
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->locked == value) {
+               return;
+       }
+
+       n->locked = value;
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag %s %s 0x%X locked %s\n", m->name, d->name, n->id, ON_OFF_STR(value));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+}
+
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->sticky == value) {
+               return;
+       }
+
+       if (d != m->desk) {
+               transfer_node(m, d, n, m, m->desk, m->desk->focus);
+       }
+
+       n->sticky = value;
+
+       if (value) {
+               ewmh_wm_state_add(n, ewmh->_NET_WM_STATE_STICKY);
+               m->sticky_count++;
+       } else {
+               ewmh_wm_state_remove(n, ewmh->_NET_WM_STATE_STICKY);
+               m->sticky_count--;
+       }
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag %s %s 0x%X sticky %s\n", m->name, d->name, n->id, ON_OFF_STR(value));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+
+}
+
+void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->private == value) {
+               return;
+       }
+
+       n->private = value;
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag %s %s 0x%X private %s\n", m->name, d->name, n->id, ON_OFF_STR(value));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+}
+
+void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (value && mon->desk->focus == n) {
+               return;
+       }
+
+       n->client->urgent = value;
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag %s %s 0x%X urgent %s\n", m->name, d->name, n->id, ON_OFF_STR(value));
+       put_status(SBSC_MASK_REPORT);
+}
+
+/* Returns true if a contains b */
+bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
+{
+       return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
+               a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
+}
+
+xcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n)
+{
+       client_t *c = n->client;
+       if (c != NULL) {
+               switch (c->state) {
+                       case STATE_TILED:
+                       case STATE_PSEUDO_TILED:
+                               return c->tiled_rectangle;
+                               break;
+                       case STATE_FLOATING:
+                               return c->floating_rectangle;
+                               break;
+                       case STATE_FULLSCREEN:
+                       default:
+                               return m != NULL ? m->rectangle : (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+                               break;
+                }
+       } else {
+               int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
+               xcb_rectangle_t rect = n->rectangle;
+               rect.width -= wg;
+               rect.height -= wg;
+               return rect;
+       }
+}
+
+void get_side_handle(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, xcb_point_t *pt)
+{
+       xcb_rectangle_t rect = get_rectangle(m, d, n);
+
+       switch (dir) {
+               case DIR_EAST:
+                       pt->x = rect.x + rect.width;
+                       pt->y = rect.y + (rect.height / 2);
+                       break;
+               case DIR_SOUTH:
+                       pt->x = rect.x + (rect.width / 2);
+                       pt->y = rect.y + rect.height;
+                       break;
+               case DIR_WEST:
+                       pt->x = rect.x;
+                       pt->y = rect.y + (rect.height / 2);
+                       break;
+               case DIR_NORTH:
+                       pt->x = rect.x + (rect.width / 2);
+                       pt->y = rect.y;
+                       break;
+       }
+}
+
+#define DEF_FLAG_COUNT(flag) \
+       unsigned int flag##_count(node_t *n) \
+       { \
+               if (n == NULL) { \
+                       return 0; \
+               } else { \
+                       return ((n->flag ? 1 : 0) + \
+                               flag##_count(n->first_child) + \
+                               flag##_count(n->second_child)); \
+               } \
+       }
+       DEF_FLAG_COUNT(sticky)
+       DEF_FLAG_COUNT(private)
+       DEF_FLAG_COUNT(locked)
+#undef DEF_FLAG_COUNT
diff --git a/tree.h b/tree.h
index fd48de03ddd9a52639eb3dda73ab2bbff11814d8..7ef775df0806bcaf5736d03eebd04b778fd64e8d 100644 (file)
--- a/tree.h
+++ b/tree.h
 
 void arrange(monitor_t *m, desktop_t *d);
 void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect);
-void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
+presel_t *make_presel(void);
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n);
+node_t *find_public(desktop_t *d);
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
 void activate_node(monitor_t *m, desktop_t *d, node_t *n);
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n);
 void focus_node(monitor_t *m, desktop_t *d, node_t *n);
-void update_current(void);
-node_t *make_node(void);
-client_t *make_client(xcb_window_t win, unsigned int border_width);
+void update_focused(void);
+void hide_node(node_t *n);
+void show_node(node_t *n);
+node_t *make_node(uint32_t id);
+client_t *make_client(void);
+void initialize_client(node_t *n);
 bool is_leaf(node_t *n);
 bool is_first_child(node_t *n);
 bool is_second_child(node_t *n);
-void reset_mode(coordinates_t *loc);
+unsigned int clients_count_in(node_t *n);
 node_t *brother_tree(node_t *n);
-void closest_public(desktop_t *d, node_t *n, node_t **closest, node_t **public);
 node_t *first_extrema(node_t *n);
 node_t *second_extrema(node_t *n);
 node_t *next_leaf(node_t *n, node_t *r);
@@ -47,14 +55,18 @@ node_t *next_tiled_leaf(desktop_t *d, node_t *n, node_t *r);
 node_t *prev_tiled_leaf(desktop_t *d, node_t *n, node_t *r);
 bool is_adjacent(node_t *a, node_t *b, direction_t dir);
 node_t *find_fence(node_t *n, direction_t dir);
-node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel);
-node_t *nearest_from_tree(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel);
-node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel);
-node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel);
+node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel);
+bool is_child(node_t *a, node_t *b);
+bool is_descendant(node_t *a, node_t *b);
+bool find_by_id(uint32_t id, coordinates_t *loc);
+node_t *find_by_id_in(node_t *r, uint32_t id);
+node_t *nearest_from_tree(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel);
+node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel);
+node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, node_select_t sel);
 void get_opposite(direction_t src, direction_t *dst);
-int tiled_area(node_t *n);
-int tiled_count(desktop_t *d);
-node_t *find_biggest(monitor_t *m, desktop_t *d, node_t *n, client_select_t sel);
+unsigned int node_area(monitor_t *m, desktop_t *d, node_t *n);
+int tiled_count(node_t *n);
+node_t *find_biggest(monitor_t *m, desktop_t *d, node_t *n, node_select_t sel);
 void rotate_tree(node_t *n, int deg);
 void rotate_brother(node_t *n);
 void unrotate_tree(node_t *n, int rot);
@@ -64,13 +76,28 @@ void equalize_tree(node_t *n);
 int balance_tree(node_t *n);
 void unlink_node(monitor_t *m, desktop_t *d, node_t *n);
 void remove_node(monitor_t *m, desktop_t *d, node_t *n);
-void destroy_tree(node_t *n);
+void destroy_tree(monitor_t *m, desktop_t *d, node_t *n);
 bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
 bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd);
-node_t *closest_node(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel);
-void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir);
-void set_vacant_state(node_t *n, bool value);
-void propagate_vacant_state(node_t *n);
-void update_privacy_level(node_t *n, bool value);
+node_t *closest_node(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, node_select_t sel);
+void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);
+void set_vacant_state(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_vacant_state(monitor_t *m, desktop_t *d, node_t *n);
+void set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
+void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
+void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);
+bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
+xcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n);
+void get_side_handle(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, xcb_point_t *pt);
+
+unsigned int sticky_count(node_t *n);
+unsigned int private_count(node_t *n);
+unsigned int locked_count(node_t *n);
 
 #endif
diff --git a/types.h b/types.h
index 1e8918ead12a319b2563a0232ce26250f91b057b..37136bff43c5fdad61ad9acb6a5a59298680e93f 100644 (file)
--- a/types.h
+++ b/types.h
@@ -31,7 +31,7 @@
 #include "helpers.h"
 
 #define MISSING_VALUE        "N/A"
-#define MAX_STATE            4
+#define MAX_WM_STATES        4
 
 typedef enum {
        TYPE_HORIZONTAL,
@@ -83,10 +83,10 @@ typedef enum {
 } history_dir_t;
 
 typedef enum {
-       DIR_RIGHT,
-       DIR_DOWN,
-       DIR_LEFT,
-       DIR_UP
+       DIR_NORTH,
+       DIR_WEST,
+       DIR_SOUTH,
+       DIR_EAST
 } direction_t;
 
 typedef enum {
@@ -127,6 +127,10 @@ typedef enum {
 } child_polarity_t;
 
 typedef struct {
+       option_bool_t automatic;
+       option_bool_t focused;
+       option_bool_t local;
+       option_bool_t leaf;
        option_bool_t tiled;
        option_bool_t pseudo_tiled;
        option_bool_t floating;
@@ -136,13 +140,10 @@ typedef struct {
        option_bool_t private;
        option_bool_t urgent;
        option_bool_t same_class;
-       option_bool_t automatic;
-       option_bool_t local;
-       option_bool_t focused;
        option_bool_t below;
        option_bool_t normal;
        option_bool_t above;
-} client_select_t;
+} node_select_t;
 
 typedef struct {
        option_bool_t occupied;
@@ -157,14 +158,10 @@ typedef struct {
 } monitor_select_t;
 
 typedef struct {
-       xcb_window_t window;
        char class_name[3 * SMALEN / 2];
        char instance_name[3 * SMALEN / 2];
        unsigned int border_width;
-       bool locked;                            /* protects window from being closed */
-       bool sticky;
        bool urgent;
-       bool private;
        client_state_t state;
        client_state_t last_state;
        stack_layer_t layer;
@@ -177,24 +174,33 @@ typedef struct {
        uint16_t max_width;
        uint16_t min_height;
        uint16_t max_height;
-       xcb_atom_t wm_state[MAX_STATE];
-       int num_states;
+       xcb_atom_t wm_state[MAX_WM_STATES];
+       int wm_states_count;
 } client_t;
 
+typedef struct presel_t presel_t;
+struct presel_t {
+       double split_ratio;
+       direction_t split_dir;
+       xcb_window_t feedback;
+};
+
 typedef struct node_t node_t;
 struct node_t {
+       uint32_t id;
        split_type_t split_type;
        double split_ratio;
-       split_mode_t split_mode;
-       direction_t split_dir;
        int birth_rotation;
+       presel_t *presel;
        xcb_rectangle_t rectangle;
-       bool vacant;                            /* vacant nodes only hold floating clients */
-       int privacy_level;
+       bool vacant;
+       bool sticky;
+       bool private;
+       bool locked;
        node_t *first_child;
        node_t *second_child;
        node_t *parent;
-       client_t *client;                       /* NULL except for leaves */
+       client_t *client;
 };
 
 typedef struct desktop_t desktop_t;
@@ -223,7 +229,7 @@ struct monitor_t {
        int right_padding;
        int bottom_padding;
        int left_padding;
-       int num_sticky;
+       unsigned int sticky_count;
        xcb_rectangle_t rectangle;
        desktop_t *desk;
        desktop_t *desk_head;
@@ -264,7 +270,8 @@ struct subscriber_list_t {
 
 typedef struct rule_t rule_t;
 struct rule_t {
-       char cause[MAXLEN];
+       char class_name[MAXLEN];
+       char instance_name[MAXLEN];
        char effect[MAXLEN];
        bool one_shot;
        rule_t *prev;
@@ -278,9 +285,9 @@ typedef struct {
        char desktop_desc[MAXLEN];
        char node_desc[MAXLEN];
        char split_dir[SMALEN];
+       double split_ratio;
        stack_layer_t *layer;
        client_state_t *state;
-       double split_ratio;
        uint16_t min_width;
        uint16_t max_width;
        uint16_t min_height;
index 693fdd596203049ca76b38da1d645a29cf2672ea..2fdc9554c9d7ad8971940bd555caddf353f11ba4 100644 (file)
--- a/window.c
+++ b/window.c
@@ -22,7 +22,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <string.h>
 #include "bspwm.h"
 #include "ewmh.h"
@@ -32,7 +34,6 @@
 #include "settings.h"
 #include "stack.h"
 #include "tree.h"
-#include "subscribe.h"
 #include "parse.h"
 #include "window.h"
 
@@ -47,13 +48,16 @@ void schedule_window(xcb_window_t win)
                free(wa);
        }
 
-       if (override_redirect || locate_window(win, &loc))
+       if (override_redirect || locate_window(win, &loc)) {
                return;
+       }
 
        /* ignore pending windows */
-       for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next)
-               if (pr->win == win)
+       for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+               if (pr->win == win) {
                        return;
+               }
+       }
 
        rule_consequence_t *csq = make_rule_conquence();
        apply_rules(win, csq);
@@ -113,27 +117,33 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
        if (csq->split_dir[0] != '\0' && f != NULL) {
                direction_t dir;
                if (parse_direction(csq->split_dir, &dir)) {
-                       f->split_mode = MODE_MANUAL;
-                       f->split_dir = dir;
+                       presel_dir(m, d, f, dir);
                }
        }
 
        if (csq->split_ratio != 0 && f != NULL) {
-               f->split_ratio = csq->split_ratio;
+               presel_ratio(m, d, f, csq->split_ratio);
        }
 
-       client_t *c = make_client(win, csq->border ? d->border_width : 0);
-       update_floating_rectangle(c);
+       node_t *n = make_node(win);
+       client_t *c = make_client();
+       c->border_width = csq->border ? d->border_width : 0;
+       n->client = c;
+       initialize_client(n);
+       update_floating_rectangle(n);
+
        if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
                csq->center = true;
        }
+
        c->min_width = csq->min_width;
        c->max_width = csq->max_width;
        c->min_height = csq->min_height;
        c->max_height = csq->max_height;
+
        monitor_t *mm = monitor_from_client(c);
        embrace_client(mm, c);
-       translate_client(mm, m, c);
+       adapt_geometry(&mm->rectangle, &m->rectangle, n);
 
        if (csq->center) {
                window_center(m, c);
@@ -142,11 +152,10 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
        snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
        snprintf(c->instance_name, sizeof(c->instance_name), "%s", csq->instance_name);
 
-       node_t *n = make_node();
-       n->client = c;
+       f = insert_node(m, d, n, f);
+       clients_count++;
 
-       put_status(SBSC_MASK_WINDOW_MANAGE, "window_manage %s %s 0x%X 0x%X\n", m->name, d->name, win, f!=NULL?f->client->window:0);
-       insert_node(m, d, n, f);
+       put_status(SBSC_MASK_NODE_MANAGE, "node_manage %s %s 0x%X 0x%X\n", m->name, d->name, win, f!=NULL?f->id:0);
 
        if (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {
                c->last_layer = c->layer = f->client->layer;
@@ -174,17 +183,16 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
        } else if (csq->focus) {
                activate_node(m, d, n);
        } else {
-               stack(n, false);
+               stack(d, n, false);
        }
 
        uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
-       xcb_change_window_attributes(dpy, c->window, XCB_CW_EVENT_MASK, values);
+       xcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);
 
-       if (visible) {
-               if (d == m->desk)
-                       window_show(n->client->window);
-               else
-                       window_hide(n->client->window);
+       if (d == m->desk) {
+               window_show(n->id);
+       } else {
+               window_hide(n->id);
        }
 
        /* the same function is already called in `focus_node` but has no effects on unmapped windows */
@@ -192,9 +200,8 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
                xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
        }
 
-       num_clients++;
        ewmh_set_wm_desktop(n, d);
-       ewmh_update_client_list();
+       ewmh_update_client_list(false);
        free(csq->layer);
        free(csq->state);
 }
@@ -203,10 +210,11 @@ void unmanage_window(xcb_window_t win)
 {
        coordinates_t loc;
        if (locate_window(win, &loc)) {
-               put_status(SBSC_MASK_WINDOW_UNMANAGE, "window_unmanage %s %s 0x%X\n", loc.monitor, loc.desktop, win);
+               put_status(SBSC_MASK_NODE_UNMANAGE, "node_unmanage %s %s 0x%X\n", loc.monitor, loc.desktop, win);
                remove_node(loc.monitor, loc.desktop, loc.node);
-               if (frozen_pointer->window == win)
+               if (frozen_pointer->window == win) {
                        frozen_pointer->action = ACTION_NONE;
+               }
                arrange(loc.monitor, loc.desktop);
        } else {
                for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
@@ -218,430 +226,240 @@ void unmanage_window(xcb_window_t win)
        }
 }
 
-void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
+void initialize_presel_feedback(node_t *n)
 {
-       if (n == NULL || n->client->border_width < 1) {
+       if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
                return;
        }
 
-       xcb_window_t win = n->client->window;
-       uint32_t border_color_pxl = get_border_color(n->client, focused_window, focused_monitor);
+       xcb_window_t win = xcb_generate_id(dpy);
+       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                                 XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL, (uint32_t []) {get_color_pixel(presel_feedback_color)});
 
-       if (n->split_mode == MODE_AUTOMATIC) {
-               xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
-       } else {
-               unsigned int border_width = n->client->border_width;
-               uint32_t presel_border_color_pxl;
-               get_color(presel_border_color, win, &presel_border_color_pxl);
-
-               xcb_rectangle_t actual_rectangle = get_rectangle(NULL, n->client);
-
-               uint16_t width = actual_rectangle.width;
-               uint16_t height = actual_rectangle.height;
-
-               uint16_t full_width = width + 2 * border_width;
-               uint16_t full_height = height + 2 * border_width;
-
-               xcb_rectangle_t border_rectangles[] =
-               {
-                       { width, 0, 2 * border_width, height + 2 * border_width },
-                       { 0, height, width + 2 * border_width, 2 * border_width }
-               };
-
-               xcb_rectangle_t *presel_rectangles;
-
-               uint8_t win_depth = root_depth;
-               xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);
-               if (geo != NULL)
-                       win_depth = geo->depth;
-               free(geo);
-
-               xcb_pixmap_t pixmap = xcb_generate_id(dpy);
-               xcb_create_pixmap(dpy, win_depth, pixmap, win, full_width, full_height);
-
-               xcb_gcontext_t gc = xcb_generate_id(dpy);
-               xcb_create_gc(dpy, gc, pixmap, 0, NULL);
-
-               xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &border_color_pxl);
-               xcb_poly_fill_rectangle(dpy, pixmap, gc, LENGTH(border_rectangles), border_rectangles);
-
-               uint16_t fence = (int16_t) (n->split_ratio * ((n->split_dir == DIR_UP || n->split_dir == DIR_DOWN) ? height : width));
-               presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
-               switch (n->split_dir) {
-                       case DIR_UP:
-                               presel_rectangles[0] = (xcb_rectangle_t) {width, 0, 2 * border_width, fence};
-                               presel_rectangles[1] = (xcb_rectangle_t) {0, height + border_width, full_width, border_width};
-                               break;
-                       case DIR_DOWN:
-                               presel_rectangles[0] = (xcb_rectangle_t) {width, fence + 1, 2 * border_width, height + border_width - (fence + 1)};
-                               presel_rectangles[1] = (xcb_rectangle_t) {0, height, full_width, border_width};
-                               break;
-                       case DIR_LEFT:
-                               presel_rectangles[0] = (xcb_rectangle_t) {0, height, fence, 2 * border_width};
-                               presel_rectangles[1] = (xcb_rectangle_t) {width + border_width, 0, border_width, full_height};
-                               break;
-                       case DIR_RIGHT:
-                               presel_rectangles[0] = (xcb_rectangle_t) {fence + 1, height, width + border_width - (fence + 1), 2 * border_width};
-                               presel_rectangles[1] = (xcb_rectangle_t) {width, 0, border_width, full_height};
-                               break;
-               }
-               xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
-               xcb_poly_fill_rectangle(dpy, pixmap, gc, 2, presel_rectangles);
-               xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXMAP, &pixmap);
-               free(presel_rectangles);
-               xcb_free_gc(dpy, gc);
-               xcb_free_pixmap(dpy, pixmap);
+       xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
+       stacking_list_t *s = stack_tail;
+       while (s != NULL && !IS_TILED(s->node->client)) {
+               s = s->prev;
        }
+       if (s != NULL) {
+               window_above(win, s->node->id);
+       }
+       n->presel->feedback = win;
 }
 
-pointer_state_t *make_pointer_state(void)
-{
-       pointer_state_t *p = malloc(sizeof(pointer_state_t));
-       p->monitor = NULL;
-       p->desktop = NULL;
-       p->node = p->vertical_fence = p->horizontal_fence = NULL;
-       p->client = NULL;
-       p->window = XCB_NONE;
-       p->action = ACTION_NONE;
-       return p;
-}
-
-/* Returns true if a contains b */
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
 {
-       return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
-               a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
-}
+       if (n == NULL || n->presel == NULL) {
+               return;
+       }
 
-xcb_rectangle_t get_rectangle(monitor_t *m, client_t *c)
-{
-       switch (c->state) {
-               case STATE_TILED:
-                       return c->tiled_rectangle;
-                       break;
-               case STATE_PSEUDO_TILED:
-               case STATE_FLOATING:
-                       return c->floating_rectangle;
-                       break;
-               case STATE_FULLSCREEN:
-               default:
-                       return m != NULL ? m->rectangle : (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-                       break;
-        }
-}
+       bool exists = (n->presel->feedback != XCB_NONE);
+       if (!exists) {
+               initialize_presel_feedback(n);
+       }
 
-void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt)
-{
-       xcb_rectangle_t rect = get_rectangle(NULL, c);
+       presel_t *p = n->presel;
+       xcb_rectangle_t rect = n->rectangle;
+       rect.x = rect.y = 0;
+       rect.width -= d->window_gap;
+       rect.height -= d->window_gap;
+       xcb_rectangle_t presel_rect = rect;
 
-       switch (dir) {
-               case DIR_RIGHT:
-                       pt->x = rect.x + rect.width;
-                       pt->y = rect.y + (rect.height / 2);
+       switch (p->split_dir) {
+               case DIR_NORTH:
+                       presel_rect.height = p->split_ratio * rect.height;
                        break;
-               case DIR_DOWN:
-                       pt->x = rect.x + (rect.width / 2);
-                       pt->y = rect.y + rect.height;
+               case DIR_EAST:
+                       presel_rect.width = (1 - p->split_ratio) * rect.width;
+                       presel_rect.x = rect.width - presel_rect.width;
                        break;
-               case DIR_LEFT:
-                       pt->x = rect.x;
-                       pt->y = rect.y + (rect.height / 2);
+               case DIR_SOUTH:
+                       presel_rect.height = (1 - p->split_ratio) * rect.height;
+                       presel_rect.y = rect.height - presel_rect.height;
                        break;
-               case DIR_UP:
-                       pt->x = rect.x + (rect.width / 2);
-                       pt->y = rect.y;
+               case DIR_WEST:
+                       presel_rect.width = p->split_ratio * rect.width;
                        break;
        }
-}
 
-void adopt_orphans(void)
-{
-       xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
-       if (qtr == NULL)
-               return;
+       window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
+                          presel_rect.width, presel_rect.height);
 
-       int len = xcb_query_tree_children_length(qtr);
-       xcb_window_t *wins = xcb_query_tree_children(qtr);
-       for (int i = 0; i < len; i++) {
-               uint32_t idx;
-               xcb_window_t win = wins[i];
-               if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1)
-                       schedule_window(win);
+       if (!exists && m->desk == d) {
+               window_show(p->feedback);
        }
-
-       free(qtr);
-}
-
-void window_close(node_t *n)
-{
-       if (n == NULL || n->client->locked)
-               return;
-
-       send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
 }
 
-void window_kill(monitor_t *m, desktop_t *d, node_t *n)
+void refresh_presel_feebacks_in(node_t *n, desktop_t *d, monitor_t *m)
 {
-       if (n == NULL)
-               return;
-
-       xcb_window_t win = n->client->window;
-
-       xcb_kill_client(dpy, win);
-       remove_node(m, d, n);
-}
-
-void set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
-{
-       if (n == NULL || n->client->layer == l) {
+       if (n == NULL) {
                return;
+       } else {
+               if (n->presel != NULL) {
+                       draw_presel_feedback(m, d, n);
+               }
+               refresh_presel_feebacks_in(n->first_child, d, m);
+               refresh_presel_feebacks_in(n->second_child, d, m);
        }
-
-       client_t *c = n->client;
-
-       c->last_layer = c->layer;
-       c->layer = 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);
-       }
-
-       stack(n, (d->focus == n));
 }
 
-void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
+void update_colors(void)
 {
-       if (n == NULL || n->client->state == s) {
-               return;
-       }
-
-       client_t *c = n->client;
-
-       c->last_state = c->state;
-       c->state = s;
-
-       switch (c->last_state) {
-               case STATE_TILED:
-               case STATE_PSEUDO_TILED:
-                       break;
-               case STATE_FLOATING:
-                       set_floating(m, d, n, false);
-                       break;
-               case STATE_FULLSCREEN:
-                       set_fullscreen(m, d, n, false);
-                       break;
-       }
-
-       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:
-               case STATE_PSEUDO_TILED:
-                       break;
-               case STATE_FLOATING:
-                       set_floating(m, d, n, true);
-                       break;
-               case STATE_FULLSCREEN:
-                       set_fullscreen(m, d, n, true);
-                       break;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       update_colors_in(d->root, d, m);
+               }
        }
-
-       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)
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
 {
        if (n == NULL) {
                return;
+       } else {
+               if (n->presel != NULL) {
+                       uint32_t pxl = get_color_pixel(presel_feedback_color);
+                       xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
+                       if (d == m->desk) {
+                               /* hack to induce back pixel refresh */
+                               window_hide(n->presel->feedback);
+                               window_show(n->presel->feedback);
+                       }
+               }
+               if (n == d->focus) {
+                       draw_border(n, true, (m == mon));
+               } else if (n->client != NULL) {
+                       draw_border(n, false, (m == mon));
+               } else {
+                       update_colors_in(n->first_child, d, m);
+                       update_colors_in(n->second_child, d, m);
+               }
        }
-
-       n->split_mode = MODE_AUTOMATIC;
-       set_vacant_state(n, value);
-
-       if (!value && d->focus == n) {
-               neutralize_obscuring_windows(m, d, n);
-       }
-
-       stack(n, (d->focus == n));
 }
 
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void draw_border(node_t *n, bool focused_node, bool focused_monitor)
 {
        if (n == NULL) {
                return;
        }
 
-       client_t *c = n->client;
-
-       n->split_mode = MODE_AUTOMATIC;
-       set_vacant_state(n, value);
-
-       if (value) {
-               ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_FULLSCREEN);
-               c->last_layer = c->layer;
-               c->layer = LAYER_ABOVE;
-       } else {
-               ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_FULLSCREEN);
-               c->layer = c->last_layer;
-               if (d->focus == n) {
-                       neutralize_obscuring_windows(m, d, n);
+       uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client->border_width > 0) {
+                       window_draw_border(f->id, border_color_pxl);
                }
        }
+}
 
-       stack(n, (d->focus == n));
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
+{
+       xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
 }
 
-void neutralize_obscuring_windows(monitor_t *m, desktop_t *d, node_t *n)
+pointer_state_t *make_pointer_state(void)
 {
-       bool dirty = false;
-       for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
-               if (a != n) {
-                       if (IS_FULLSCREEN(a->client) && stack_cmp(n->client, a->client) < 0) {
-                               set_state(m, d, a, a->client->last_state);
-                               dirty = true;
-                       }
-               }
-       }
-       if (dirty) {
-               arrange(m, d);
-       }
+       pointer_state_t *p = malloc(sizeof(pointer_state_t));
+       p->monitor = NULL;
+       p->desktop = NULL;
+       p->node = p->vertical_fence = p->horizontal_fence = NULL;
+       p->client = NULL;
+       p->window = XCB_NONE;
+       p->action = ACTION_NONE;
+       return p;
 }
 
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void adopt_orphans(void)
 {
-       if (n == NULL || n->client->locked == value)
+       xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
+       if (qtr == NULL) {
                return;
+       }
 
-       client_t *c = n->client;
+       int len = xcb_query_tree_children_length(qtr);
+       xcb_window_t *wins = xcb_query_tree_children(qtr);
 
-       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));
+       for (int i = 0; i < len; i++) {
+               uint32_t idx;
+               xcb_window_t win = wins[i];
+               if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
+                       schedule_window(win);
+               }
+       }
 
-       c->locked = value;
-       window_draw_border(n, d->focus == n, m == mon);
+       free(qtr);
 }
 
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void window_close(node_t *n)
 {
-       if (n == NULL || n->client->sticky == value)
+       if (n == NULL) {
                return;
-
-       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, ON_OFF_STR(value));
-
-       if (d != m->desk)
-               transfer_node(m, d, n, m, m->desk, m->desk->focus);
-
-       c->sticky = value;
-       if (value) {
-               ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_STICKY);
-               m->num_sticky++;
+       } else if (n->client != NULL) {
+               send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
        } else {
-               ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_STICKY);
-               m->num_sticky--;
+               window_close(n->first_child);
+               window_close(n->second_child);
        }
-
-       window_draw_border(n, d->focus == n, m == mon);
 }
 
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void window_kill(monitor_t *m, desktop_t *d, node_t *n)
 {
-       if (n == NULL || n->client->private == value)
+       if (n == NULL) {
                return;
+       }
 
-       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, ON_OFF_STR(value));
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               xcb_kill_client(dpy, f->id);
+       }
 
-       c->private = value;
-       update_privacy_level(n, value);
-       window_draw_border(n, d->focus == n, m == mon);
+       remove_node(m, d, n);
 }
 
-void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value)
+uint32_t get_border_color(bool focused_node, bool focused_monitor)
 {
-       if (value && mon->desk->focus == n)
-               return;
-       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, ON_OFF_STR(value));
-       put_status(SBSC_MASK_REPORT);
-}
-
-uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor)
-{
-       if (c == NULL)
-               return 0;
-
-       uint32_t pxl = 0;
-
-       if (focused_monitor && focused_window) {
-               if (c->locked)
-                       get_color(focused_locked_border_color, c->window, &pxl);
-               else if (c->sticky)
-                       get_color(focused_sticky_border_color, c->window, &pxl);
-               else if (c->private)
-                       get_color(focused_private_border_color, c->window, &pxl);
-               else
-                       get_color(focused_border_color, c->window, &pxl);
-       } else if (focused_window) {
-               if (c->urgent)
-                       get_color(urgent_border_color, c->window, &pxl);
-               else if (c->locked)
-                       get_color(active_locked_border_color, c->window, &pxl);
-               else if (c->sticky)
-                       get_color(active_sticky_border_color, c->window, &pxl);
-               else if (c->private)
-                       get_color(active_private_border_color, c->window, &pxl);
-               else
-                       get_color(active_border_color, c->window, &pxl);
+       if (focused_monitor && focused_node) {
+               return get_color_pixel(focused_border_color);
+       } else if (focused_node) {
+               return get_color_pixel(active_border_color);
        } else {
-               if (c->urgent)
-                       get_color(urgent_border_color, c->window, &pxl);
-               else if (c->locked)
-                       get_color(normal_locked_border_color, c->window, &pxl);
-               else if (c->sticky)
-                       get_color(normal_sticky_border_color, c->window, &pxl);
-               else if (c->private)
-                       get_color(normal_private_border_color, c->window, &pxl);
-               else
-                       get_color(normal_border_color, c->window, &pxl);
+               return get_color_pixel(normal_border_color);
        }
-
-       return pxl;
 }
 
-void update_floating_rectangle(client_t *c)
+void update_floating_rectangle(node_t *n)
 {
-       xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), NULL);
+       client_t *c = n->client;
+
+       xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
 
-       if (geo != NULL)
+       if (geo != NULL) {
                c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
+       }
 
        free(geo);
 }
 
 void restrain_floating_width(client_t *c, int *width)
 {
-       if (*width < 1)
+       if (*width < 1) {
                *width = 1;
-       if (c->min_width > 0 && *width < c->min_width)
+       }
+       if (c->min_width > 0 && *width < c->min_width) {
                *width = c->min_width;
-       else if (c->max_width > 0 && *width > c->max_width)
+       } else if (c->max_width > 0 && *width > c->max_width) {
                *width = c->max_width;
+       }
 }
 
 void restrain_floating_height(client_t *c, int *height)
 {
-       if (*height < 1)
+       if (*height < 1) {
                *height = 1;
-       if (c->min_height > 0 && *height < c->min_height)
+       }
+       if (c->min_height > 0 && *height < c->min_height) {
                *height = c->min_height;
-       else if (c->max_height > 0 && *height > c->max_height)
+       } else if (c->max_height > 0 && *height > c->max_height) {
                *height = c->max_height;
+       }
 }
 
 void restrain_floating_size(client_t *c, int *width, int *height)
@@ -657,13 +475,16 @@ void query_pointer(xcb_window_t *win, xcb_point_t *pt)
        xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
 
        if (qpr != NULL) {
-               if (win != NULL)
+               if (win != NULL) {
                        *win = qpr->child;
-               if (pt != NULL)
+               }
+               if (pt != NULL) {
                        *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
-               free(qpr);
+               }
        }
 
+       free(qpr);
+
        window_raise(motion_recorder);
 }
 
@@ -701,22 +522,25 @@ void window_center(monitor_t *m, client_t *c)
 {
        xcb_rectangle_t *r = &c->floating_rectangle;
        xcb_rectangle_t a = m->rectangle;
-       if (r->width >= a.width)
+       if (r->width >= a.width) {
                r->x = a.x;
-       else
+       } else {
                r->x = a.x + (a.width - r->width) / 2;
-       if (r->height >= a.height)
+       }
+       if (r->height >= a.height) {
                r->y = a.y;
-       else
+       } else {
                r->y = a.y + (a.height - r->height) / 2;
+       }
        r->x -= c->border_width;
        r->y -= c->border_width;
 }
 
 void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
 {
-       if (w2 == XCB_NONE)
+       if (w2 == XCB_NONE) {
                return;
+       }
        uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
        uint32_t values[] = {w2, mode};
        xcb_configure_window(dpy, w1, mask, values);
@@ -743,10 +567,11 @@ void window_set_visibility(xcb_window_t win, bool visible)
        uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
        uint32_t values_on[] = {ROOT_EVENT_MASK};
        xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
-       if (visible)
+       if (visible) {
                xcb_map_window(dpy, win);
-       else
+       } else {
                xcb_unmap_window(dpy, win);
+       }
        xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
 }
 
@@ -760,18 +585,6 @@ void window_show(xcb_window_t win)
        window_set_visibility(win, true);
 }
 
-void toggle_visibility(void)
-{
-       visible = !visible;
-       if (!visible)
-               clear_input_focus();
-       for (monitor_t *m = mon_head; m != NULL; m = m->next)
-               for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n, m->desk->root))
-                       window_set_visibility(n->client->window, visible);
-       if (visible)
-               update_input_focus();
-}
-
 void enable_motion_recorder(void)
 {
        window_raise(motion_recorder);
@@ -801,13 +614,13 @@ void update_input_focus(void)
 
 void set_input_focus(node_t *n)
 {
-       if (n == NULL) {
+       if (n == NULL || n->client == NULL) {
                clear_input_focus();
        } else {
                if (n->client->icccm_input) {
-                       xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->client->window, XCB_CURRENT_TIME);
+                       xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
                } else if (n->client->icccm_focus) {
-                       send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
+                       send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
                }
        }
 }
@@ -829,10 +642,11 @@ void center_pointer(xcb_rectangle_t r)
 void get_atom(char *name, xcb_atom_t *atom)
 {
        xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
-       if (reply != NULL)
+       if (reply != NULL) {
                *atom = reply->atom;
-       else
+       } else {
                *atom = XCB_NONE;
+       }
        free(reply);
 }
 
@@ -843,9 +657,11 @@ void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
 
 bool has_proto(xcb_atom_t atom, xcb_icccm_get_wm_protocols_reply_t *protocols)
 {
-       for (uint32_t i = 0; i < protocols->atoms_len; i++)
-               if (protocols->atoms[i] == atom)
+       for (uint32_t i = 0; i < protocols->atoms_len; i++) {
+               if (protocols->atoms[i] == atom) {
                        return true;
+               }
+       }
        return false;
 }
 
index 7c030ee0474fc19ae035a0499be3995c3dd78ffc..ee2c36ee3634903756f779830801346b5a412360 100644 (file)
--- a/window.h
+++ b/window.h
 void schedule_window(xcb_window_t win);
 void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);
 void unmanage_window(xcb_window_t win);
-void window_draw_border(node_t *n, bool focused_window, bool focused_monitor);
+void initialize_presel_feedback(node_t *n);
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n);
+void refresh_presel_feebacks_in(node_t *n, desktop_t *d, monitor_t *m);
+void update_colors(void);
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m);
+void draw_border(node_t *n, bool focused_node, bool focused_monitor);
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl);
 pointer_state_t *make_pointer_state(void);
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
-xcb_rectangle_t get_rectangle(monitor_t *m, client_t *c);
-void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt);
 void adopt_orphans(void);
 void window_close(node_t *n);
 void window_kill(monitor_t *m, desktop_t *d, node_t *n);
-void set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
-void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
-void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void neutralize_obscuring_windows(monitor_t *m, desktop_t *d, node_t *n);
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value);
-uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor);
-void update_floating_rectangle(client_t *c);
+uint32_t get_border_color(bool focused_node, bool focused_monitor);
+void update_floating_rectangle(node_t *n);
 void restrain_floating_width(client_t *c, int *width);
 void restrain_floating_height(client_t *c, int *height);
 void restrain_floating_size(client_t *c, int *width, int *height);
@@ -70,7 +64,6 @@ void window_lower(xcb_window_t win);
 void window_set_visibility(xcb_window_t win, bool visible);
 void window_hide(xcb_window_t win);
 void window_show(xcb_window_t win);
-void toggle_visibility(void);
 void enable_motion_recorder(void);
 void disable_motion_recorder(void);
 void update_motion_recorder(void);