From eb07d2fc06f6917c2590706fce01539786d1ed07 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Tue, 22 Dec 2015 19:25:45 +0100 Subject: [PATCH] Generalize window commands to nodes --- .gitignore | 1 + Makefile | 45 +- README.md | 8 +- Sourcedeps | 18 +- VERSION | 1 + bspc.c | 20 +- bspwm.c | 57 +- bspwm.h | 5 +- contrib/bash_completion | 2 +- contrib/zsh_completion | 2 +- desktop.c | 265 +++- desktop.h | 8 +- doc/TODO.md | 11 +- doc/bspwm.1 | 495 +++--- doc/bspwm.1.asciidoc | 399 ++--- events.c | 71 +- ewmh.c | 88 +- ewmh.h | 6 +- examples/bspwmrc | 13 +- .../pseudo_automatic_mode/external_rules | 6 +- examples/loop/bspwmrc | 6 +- examples/loop/profile | 4 +- examples/loop/sxhkdrc | 6 +- examples/panel/panel | 15 +- examples/panel/panel_bar | 99 +- examples/panel/panel_colors | 48 +- examples/panel/profile | 6 +- examples/sxhkdrc | 195 ++- helpers.c | 54 +- helpers.h | 7 +- history.c | 109 +- history.h | 10 +- jsmn.c | 2 - messages.c | 812 +++++----- messages.h | 22 +- monitor.c | 171 ++- monitor.h | 2 +- parse.c | 195 ++- parse.h | 9 +- pointer.c | 83 +- query.c | 430 +++--- query.h | 24 +- restore.c | 326 ++-- restore.h | 6 +- rule.c | 48 +- rule.h | 2 +- settings.c | 23 +- settings.h | 47 +- stack.c | 107 +- stack.h | 4 +- subscribe.c | 57 +- subscribe.h | 31 +- tests/Makefile | 13 + tests/README.md | 3 + tests/desktop/swap | 27 + tests/desktop/transfer | 26 + tests/node/flags | 27 + tests/node/insertion | 46 + tests/node/removal | 19 + tests/node/swap | 25 + tests/node/transfer | 24 + tests/prelude | 43 + tests/run | 39 + tests/test_window.c | 105 ++ tree.c | 1365 ++++++++++++----- tree.h | 65 +- types.h | 53 +- window.c | 622 +++----- window.h | 25 +- 69 files changed, 4225 insertions(+), 2783 deletions(-) create mode 100644 VERSION create mode 100644 tests/Makefile create mode 100644 tests/README.md create mode 100755 tests/desktop/swap create mode 100755 tests/desktop/transfer create mode 100755 tests/node/flags create mode 100755 tests/node/insertion create mode 100755 tests/node/removal create mode 100755 tests/node/swap create mode 100755 tests/node/transfer create mode 100644 tests/prelude create mode 100755 tests/run create mode 100644 tests/test_window.c diff --git a/.gitignore b/.gitignore index f635221..cda793a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ tags bspwm bspc *.o +tests/test_window diff --git a/Makefile b/Makefile index d67c9b2..4a34c3d 100644 --- 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 diff --git a/README.md b/README.md index 59537d1..a4e586b 100644 --- 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 diff --git a/Sourcedeps b/Sourcedeps index 558f4f8..286d5b7 100644 --- a/Sourcedeps +++ b/Sourcedeps @@ -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 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 416edd7..1df8437 100644 --- a/bspc.c +++ b/bspc.c @@ -22,14 +22,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include -#ifdef __OpenBSD__ +#include #include -#endif #include #include #include -#include #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 92e739f..3671ce9 100644 --- a/bspwm.c +++ b/bspwm.c @@ -24,28 +24,25 @@ #include #include -#include -#include -#ifdef __OpenBSD__ +#include #include -#endif +#include #include #include #include #include #include +#include #include #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 ea505cf..c3c304a 100644 --- a/bspwm.h +++ b/bspwm.h @@ -31,16 +31,16 @@ #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; diff --git a/contrib/bash_completion b/contrib/bash_completion index cfcdccb..2cf8f21 100644 --- a/contrib/bash_completion +++ b/contrib/bash_completion @@ -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=() diff --git a/contrib/zsh_completion b/contrib/zsh_completion index 4066b21..2c3808e 100644 --- a/contrib/zsh_completion +++ b/contrib/zsh_completion @@ -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 diff --git a/desktop.c b/desktop.c index 41a8a4d..78a4e10 100644 --- a/desktop.c +++ b/desktop.c @@ -22,14 +22,16 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include +#include #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); + + 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; + } + + if (md->desk != NULL) { + hide_desktop(d); + } + insert_desktop(md, d); - if (d == dd) { - if (ms->desk != NULL) { - show_desktop(ms->desk); - } - if (md->desk != d) { - hide_desktop(d); - } - } - - for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { - translate_client(ms, md, n->client); - } - + 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; } diff --git a/desktop.h b/desktop.h index 7ebc38d..858643f 100644 --- a/desktop.h +++ b/desktop.h @@ -28,19 +28,21 @@ #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); diff --git a/doc/TODO.md b/doc/TODO.md index 41ffb77..48ca92b 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -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. diff --git a/doc/bspwm.1 b/doc/bspwm.1 index de3825f..3339477 100644 --- a/doc/bspwm.1 +++ b/doc/bspwm.1 @@ -2,12 +2,12 @@ .\" Title: bspwm .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" 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 := (|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 := (|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 := (|DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.[! .nr an-break-flag 1 .br .ps +1 -\fBPrimary Selectors\fR +\fBDescriptors\fR .RS 4 .PP + +.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,58 @@ 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 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\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 +1|first +.RS 4 +Jumps to the first child\&. +.RE +.PP +2|second +.RS 4 +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 @@ -152,19 +206,29 @@ Selects the window newer than the focused window in the history\&. \fBModifiers\fR .RS 4 .PP -[!](tiled|pseudo_tiled|floating|fullscreen) +[!]focused .RS 4 -Only consider windows having or not having the given state\&. +Only consider focused or unfocused nodes\&. .RE .PP [!]automatic .RS 4 -Only consider windows in automatic or manual insertion mode\&. +Only consider nodes in automatic or manual insertion mode\&. .RE .PP -[!]focused +[!]local .RS 4 -Only consider focused or unfocused windows\&. +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 @@ -172,11 +236,6 @@ Only consider focused or unfocused windows\&. Only consider windows that have or don\(cqt have the same class as the current window\&. .RE .PP -[!]local -.RS 4 -Only consider windows in or not in the current desktop\&. -.RE -.PP [!](private|urgent|sticky|locked) .RS 4 Only consider windows that have or don\(cqt have the given flag set\&. @@ -206,7 +265,7 @@ DESKTOP_SEL := (|[MONITOR_SEL:](focused|^)CYCLE_DIR|last|older| .nr an-break-flag 1 .br .ps +1 -\fBPrimary Selectors\fR +\fBDescriptors\fR .RS 4 .PP @@ -292,7 +351,7 @@ MONITOR_SEL := (|^|DIR|CYCLE_DIR|last|primary|focused|older|new .nr an-break-flag 1 .br .ps +1 -\fBPrimary Selectors\fR +\fBDescriptors\fR .RS 4 .PP @@ -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\-w\fR, \fB\-\-to\-window\fR \fIWINDOW_SEL\fR +\fB\-n\fR, \fB\-\-to\-node\fR \fINODE_SEL\fR .RS 4 -Transplant the selected window to the given window\&. +Transplant the selected node to the given node\&. .RE .PP -\fB\-s\fR, \fB\-\-swap\fR \fIWINDOW_SEL\fR +\fB\-s\fR, \fB\-\-swap\fR \fINODE_SEL\fR .RS 4 -Swap the selected window with the given window\&. +Swap the selected node with the given node\&. .RE .PP -\fB\-p\fR, \fB\-\-presel\fR \fIDIR\fR|cancel +\fB\-p\fR, \fB\-\-presel\-dir\fR \fIDIR\fR|cancel .RS 4 -Preselect the splitting area of the selected window (or cancel the preselection)\&. +Preselect the splitting area of the selected node (or cancel the preselection)\&. .RE .PP -\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR +\fB\-o\fR, \fB\-\-presel\-ratio\fR \fIRATIO\fR .RS 4 -Set the splitting ratio of the selected window (0 < +Set the splitting ratio of the preselection area\&. +.RE +.PP +\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR|\(+-\fIPIXELS\fR +.RS 4 +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 -Set or change the splitting ratio of the edge located in the given direction in relation to the selected window\&. +Rotate the tree rooted at the selected node\&. .RE .PP -\fB\-R\fR, \fB\-\-rotate\fR \fIDIR\fR \fI90|270|180\fR +\fB\-F\fR, \fB\-\-flip\fR \fIhorizontal|vertical\fR .RS 4 -Rotate the tree holding the edge located in the given direction in relation to the selected window\&. +Flip the the tree rooted at selected node\&. .RE .PP -\fB\-t\fR, \fB\-\-state\fR tiled|pseudo_tiled|floating|fullscreen +\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 +Circulate the windows of the tree rooted at the selected node\&. +.RE +.PP +\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 +[\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 -.RS 4 -Load the focus history from the given file\&. -.RE -.PP -\fB\-S\fR, \fB\-\-stack\fR -.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 +Dump the current world state on standard output\&. +.RE +.PP +\fB\-l\fR, \fB\-\-load\-state\fR +.RS 4 +Load a world state from the given file\&. +.RE +.PP +\fB\-a\fR, \fB\-\-add\-monitor\fR WxH+X+Y +.RS 4 +Add a monitor for the given name and rectangle\&. +.RE +.PP +\fB\-r\fR, \fB\-\-remove\-monitor\fR +.RS 4 +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\-\-toggle\-visibility\fR +\fB\-h\fR, \fB\-\-record\-history\fR on|off .RS 4 -Toggle the visibility of all the windows\&. +Enable or disable the recording of node focus history\&. .RE .PP -\fB\-\-record\-history\fR on|off -.RS 4 -Enable or disable the recording of window focus history\&. -.RE -.PP -\fB\-\-subscribe\fR (all|report|monitor|desktop|window|\&...)* -.RS 4 -Continuously print status information\&. See the -\fBEVENTS\fR -section for the detailed description of each event\&. -.RE -.PP -\fB\-\-get\-status\fR +\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 ||* [\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 (|\fB)[:(|\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 ^|head|tail|||*\&... +\fB\-r\fR, \fB\-\-remove\fR ^|head|tail|(|\fB)[:(|\fR)]\&... .RS 4 Remove the given rules\&. .RE .PP -\fB\-l\fR, \fB\-\-list\fR [||*] +\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] [] +config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-n \fINODE_SEL\fR] [] .RS 4 Get or set the value of \&. .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 \fR +.RS 4 +A desktop is activated\&. +.RE +.PP \fIdesktop_layout tiled|monocle\fR .RS 4 The layout of a desktop changed\&. .RE .PP -\fIwindow_manage \fR +\fInode_manage \fR .RS 4 A window is managed\&. .RE .PP -\fIwindow_unmanage \fR +\fInode_unmanage \fR .RS 4 A window is unmanaged\&. .RE .PP -\fIwindow_swap \fR +\fInode_swap \fR .RS 4 -A window is swapped\&. +A node is swapped\&. .RE .PP -\fIwindow_transfer \fR +\fInode_transfer \fR .RS 4 -A window is transferred\&. +A node is transferred\&. .RE .PP -\fIwindow_focus \fR +\fInode_focus \fR .RS 4 -A window is focused\&. +A node is focused\&. .RE .PP -\fIwindow_activate \fR +\fInode_activate \fR .RS 4 -A window is activated\&. +A node is activated\&. .RE .PP -\fIwindow_geometry \fR +\fInode_presel (dir DIR|ratio RATIO|cancel)\fR +.RS 4 +A node is preselected\&. +.RE +.PP +\fInode_stack below|above \fR +.RS 4 +A node is stacked below or above another node\&. +.RE +.PP +\fInode_geometry \fR .RS 4 The geometry of a window changed\&. .RE .PP -\fIwindow_state tiled|pseudo_tiled|floating|fullscreen on|off\fR +\fInode_state tiled|pseudo_tiled|floating|fullscreen on|off\fR .RS 4 The state of a window changed\&. .RE .PP -\fIwindow_flag sticky|private|locked|urgent on|off\fR +\fInode_flag 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 below|normal|above\fR +\fInode_layer 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 diff --git a/doc/bspwm.1.asciidoc b/doc/bspwm.1.asciidoc index d063887..435a327 100644 --- a/doc/bspwm.1.asciidoc +++ b/doc/bspwm.1.asciidoc @@ -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 := (|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 := (|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 +^^^^^^^^^^^ + +:: + 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 := (|[MONITOR_SEL:](focused|^)CYCLE_DIR|last|older|newer)[.[!]occupied][.[!]focused][.[!]urgent][.[!]local] ---- -Primary Selectors -^^^^^^^^^^^^^^^^^ +Descriptors +^^^^^^^^^^^ :: Selects the desktop with the given name. @@ -175,8 +211,8 @@ Select a monitor. MONITOR_SEL := (|^|DIR|CYCLE_DIR|last|primary|focused|older|newer)[.[!]occupied][.[!]focused] ---- -Primary Selectors -^^^^^^^^^^^^^^^^^ +Descriptors +^^^^^^^^^^^ :: 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. -*-w*, *--to-window* 'WINDOW_SEL':: - Transplant the selected window to the given window. +*-n*, *--to-node* 'NODE_SEL':: + Transplant the selected node to the given node. -*-s*, *--swap* 'WINDOW_SEL':: - Swap the selected window with the given window. +*-s*, *--swap* 'NODE_SEL':: + Swap the selected node with the given node. -*-p*, *--presel* 'DIR'|cancel:: - Preselect the splitting area of the selected window (or cancel the preselection). +*-p*, *--presel-dir* [~]'DIR'|cancel:: + Preselect the splitting area of the selected node (or cancel the preselection). -*-r*, *--ratio* 'RATIO':: - Set the splitting ratio of the selected window (0 < 'RATIO' < 1). +*-o*, *--presel-ratio* 'RATIO':: + Set the splitting ratio of the preselection area. -*-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*, *--ratio* 'RATIO'|±'PIXELS':: + Set the splitting ratio of the selected node (0 < 'RATIO' < 1). -*-R*, *--rotate* 'DIR' '90|270|180':: - Rotate the tree holding 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. -*-t*, *--state* tiled|pseudo_tiled|floating|fullscreen:: +*-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. + +*-C*, *--circulate* forward|backward:: + Circulate the windows of the tree rooted at the selected node. + +*-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. +Options +^^^^^^^ -*-S*, *--stack*:: - Print the window stacking order. +[*-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'. -[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-w*, *--window* ['WINDOW_SEL']]:: - Constrain matches to the selected monitor, desktop or window. - -Restore -~~~~~~~ +Wm +~~ General Syntax ^^^^^^^^^^^^^^ -restore 'OPTIONS' +wm 'COMMANDS' -Options -^^^^^^^ +Commands +^^^^^^^^ -*-T*, *--tree* :: - Load the desktop trees from the given file. +*-d*, *--dump-state*:: + Dump the current world state on standard output. -*-H*, *--history* :: - Load the focus history from the given file. +*-l*, *--load-state* :: + Load a world state from the given file. -*-S*, *--stack* :: - Load the window stacking order from the given file. +*-a*, *--add-monitor* WxH+X+Y:: + Add a monitor for the given name and rectangle. -Control -~~~~~~~ +*-r*, *--remove-monitor* :: + Remove the monitor with the given name. -General Syntax -^^^^^^^^^^^^^^ - -control 'OPTIONS' - -Options -^^^^^^^ - -*--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* ||* [*-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* (|*)[:(|*)] [*-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* ^|head|tail|||*...:: +*-r*, *--remove* ^|head|tail|(|*)[:(|*)]...:: Remove the given rules. -*-l*, *--list* [||*]:: +*-l*, *--list*:: List the rules. Config @@ -517,9 +533,17 @@ Config General Syntax ^^^^^^^^^^^^^^ -config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-w 'WINDOW_SEL'] []:: +config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-n 'NODE_SEL'] []:: Get or set the value of . +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. +'normal_border_color':: + Color of the border of an unfocused window. '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. +'focused_border_color':: + Color of the border of a focused window of a focused monitor. -'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. - -'normal_private_border_color':: - Color of the border of an unfocused private window. - -'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 ':: A desktop is focused. +'desktop_activate ':: + A desktop is activated. + 'desktop_layout tiled|monocle':: The layout of a desktop changed. -'window_manage ':: +'node_manage ':: A window is managed. -'window_unmanage ':: +'node_unmanage ':: A window is unmanaged. -'window_swap ':: - A window is swapped. +'node_swap ':: + A node is swapped. -'window_transfer ':: - A window is transferred. +'node_transfer ':: + A node is transferred. -'window_focus ':: - A window is focused. +'node_focus ':: + A node is focused. -'window_activate ':: - A window is activated. +'node_activate ':: + A node is activated. -'window_geometry ':: +'node_presel (dir DIR|ratio RATIO|cancel)':: + A node is preselected. + +'node_stack below|above ':: + A node is stacked below or above another node. + +'node_geometry ':: The geometry of a window changed. -'window_state tiled|pseudo_tiled|floating|fullscreen on|off':: +'node_state tiled|pseudo_tiled|floating|fullscreen on|off':: The state of a window changed. -'window_flag sticky|private|locked|urgent on|off':: - One of the flags of a window changed. +'node_flag sticky|private|locked|urgent on|off':: + One of the flags of a node changed. -'window_layer below|normal|above':: +'node_layer 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 '' where '' 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 --------------------- diff --git a/events.c b/events.c index 90e48d3..272e894 100644 --- a/events.c +++ b/events.c @@ -23,6 +23,7 @@ */ #include +#include #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 81d11c2..6e2d23c 100644 --- a/ewmh.c +++ b/ewmh.c @@ -22,6 +22,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include #include #include "bspwm.h" @@ -32,27 +34,28 @@ 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 97743ea..579ae55 100644 --- 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 diff --git a/examples/bspwmrc b/examples/bspwmrc index aeac95f..0151e0c 100755 --- a/examples/bspwmrc +++ b/examples/bspwmrc @@ -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 diff --git a/examples/external_rules/pseudo_automatic_mode/external_rules b/examples/external_rules/pseudo_automatic_mode/external_rules index a62089c..b8fe0e6 100755 --- a/examples/external_rules/pseudo_automatic_mode/external_rules +++ b/examples/external_rules/pseudo_automatic_mode/external_rules @@ -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" } diff --git a/examples/loop/bspwmrc b/examples/loop/bspwmrc index 7a7988f..797a973 100755 --- a/examples/loop/bspwmrc +++ b/examples/loop/bspwmrc @@ -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 diff --git a/examples/loop/profile b/examples/loop/profile index c2366c2..790d657 100644 --- a/examples/loop/profile +++ b/examples/loop/profile @@ -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 diff --git a/examples/loop/sxhkdrc b/examples/loop/sxhkdrc index 6e68a29..40480cc 100644 --- a/examples/loop/sxhkdrc +++ b/examples/loop/sxhkdrc @@ -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} diff --git a/examples/panel/panel b/examples/panel/panel index 7f64fe5..0f22a8a 100755 --- a/examples/panel/panel +++ b/examples/panel/panel @@ -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 diff --git a/examples/panel/panel_bar b/examples/panel/panel_bar index 026ba78..8b50374 100755 --- a/examples/panel/panel_bar +++ b/examples/panel/panel_bar @@ -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 diff --git a/examples/panel/panel_colors b/examples/panel/panel_colors index a7fad64..4597036 100644 --- a/examples/panel/panel_colors +++ b/examples/panel/panel_colors @@ -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" diff --git a/examples/panel/profile b/examples/panel/profile index 921841d..0bac490 100644 --- a/examples/panel/profile +++ b/examples/panel/profile @@ -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 diff --git a/examples/sxhkdrc b/examples/sxhkdrc index 19cd449..2e04d0d 100644 --- a/examples/sxhkdrc +++ b/examples/sxhkdrc @@ -1,88 +1,133 @@ -# -# bspwm hotkeys -# - -super + alt + Escape - bspc quit - -super + w - bspc window -c - -super + n - bspc desktop -l next - -super + {t,p,s,f} - bspc window -t {tiled,pseudo_tiled,floating,fullscreen} - -super + {grave,Tab} - bspc {window,desktop} -f last - -super + apostrophe - bspc window -s last - -super + {o,i} - bspc control --record-history off; \ - bspc window {older,newer} -f; \ - bspc control --record-history on - -super + y - bspc window -w last.!automatic - -super + m - bspc window -s biggest - -super + {_,shift + }{h,j,k,l} - bspc window -{f,s} {left,down,up,right} - -super + {_,shift + }c - bspc window -f {next,prev} - -super + {comma,period} - bspc desktop -C {backward,forward} - -super + bracket{left,right} - bspc desktop -f {prev,next} - -super + ctrl + {h,j,k,l} - bspc window -p {left,down,up,right} - -super + ctrl + {_,shift + }space - bspc {window -p cancel,desktop -c} - -super + alt + {h,j,k,l} - bspc window -e {left -10,down +10,up -10,right +10} - -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} - -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}' - -~button1 - bspc pointer -g focus - -super + button{1-3} - ; bspc pointer -g {move,resize_side,resize_corner} - -super + @button{1-3} - bspc pointer -u - # # 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 + +# close and kill +super + {_,shift + }w + bspc node -{c,k} + +# alternate between the tiled and monocle layout +super + m + bspc desktop -l next + +# 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 + +# swap the current node and the biggest node +super + g + bspc node -s biggest + +# +# state/flags +# + +# set the window state +super + {t,shift + t,s,f} + bspc node -t {tiled,pseudo_tiled,floating,fullscreen} + +# set the node flags +super + ctrl + {x,y,z} + bspc node -g {locked,sticky,private} + +# +# focus/swap +# + +# focus the node in the given direction +super + {_,shift + }{h,j,k,l} + bspc node -{f,s} {west,south,north,east} + +# focus the node for the given path jump +super + {p,b,comma,period} + bspc node -f @{parent,brother,first,second} + +# 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 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 node @{_,/} -p cancel + +# +# resize tiled/floating +# + +# expand the tiled space in the given direction +super + alt + {h,j,k,l} + 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 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} + +# +# 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 diff --git a/helpers.c b/helpers.c index c342b6c..1b2ef33 100644 --- a/helpers.c +++ b/helpers.c @@ -22,10 +22,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include +#include +#include +#include #include +#include #include #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) diff --git a/helpers.h b/helpers.h index 7ca7870..40050cf 100644 --- a/helpers.h +++ b/helpers.h @@ -42,10 +42,12 @@ #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 diff --git a/history.c b/history.c index fad6ab9..8195600 100644 --- a/history.c +++ b/history.c @@ -23,7 +23,9 @@ */ #include +#include #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; + } } diff --git a/history.h b/history.h index b69fc70..92c36fc 100644 --- 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 2809669..bbf013d 100644 --- a/jsmn.c +++ b/jsmn.c @@ -1,5 +1,3 @@ -#include - #include "jsmn.h" /** diff --git a/messages.c b/messages.c index 6f33519..f5c40d5 100644 --- a/messages.c +++ b/messages.c @@ -25,10 +25,10 @@ #include #include #include +#include +#include #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)) + } + if (trg.node->vacant) { return MSG_FAILURE; - direction_t dir; - if (!parse_direction(*args, &dir)) - 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)) { - num--, args++; - 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); - } else { - return MSG_FAILURE; - } - } else if (streq("-l", *args) || streq("--layer", *args)) { + } else if (streq("-F", *args) || streq("--flip", *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); + flip_t flp; + if (parse_flip(*args, &flp)) { + flip_tree(trg.node, flp); + dirty = true; } else { return MSG_FAILURE; } } else if (streq("-R", *args) || streq("--rotate", *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, °)) { - rotate_tree(n, deg); + rotate_tree(trg.node, deg); + dirty = true; + } else { + return MSG_FAILURE; + } + } 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 < 1) { + return MSG_SYNTAX; + } + 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, °)) { - 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; - } - } else if (streq("-S", *args) || streq("--stack", *args)) { + char *name = *args; num--, args++; - if (num < 1) { - return MSG_SYNTAX; - } - if (!restore_stack(*args)) { - 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; + 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 { - subscriber_mask_t mask; - while (num > 0) { - if (parse_subscriber_mask(*args, &mask)) { - field |= mask; - } else { - return MSG_SYNTAX; - } - num--, args++; - } + return MSG_SYNTAX; } - add_subscriber(rsp, field); - return MSG_SUBSCRIBE; - } else if (streq("--get-status", *args)) { - print_report(rsp); - } else if (streq("--record-history", *args)) { + } else if (streq("-r", *args) || streq("--remove-monitor", *args)) { num--, args++; - if (num < 1) + if (num < 1) { return MSG_SYNTAX; + } + if (mon_head == mon_tail) { + return MSG_FAILURE; + } + monitor_t *m = find_monitor(*args); + if (m != NULL) { + remove_monitor(m); + } else { + return MSG_FAILURE; + } + } 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("-h", *args) || streq("--record-history", *args)) { + num--, args++; + 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; -} diff --git a/messages.h b/messages.h index 8603a61..65e3afb 100644 --- a/messages.h +++ b/messages.h @@ -30,33 +30,17 @@ 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 diff --git a/monitor.c b/monitor.c index 5de1820..a5d8b22 100644 --- a/monitor.c +++ b/monitor.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #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); + 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 - ms->rectangle.x; - int dy_s = c->floating_rectangle.y - ms->rectangle.y; + int dx_s = c->floating_rectangle.x - rs->x; + int dy_s = c->floating_rectangle.y - rs->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 nume_x = dx_s * (rd->width - c->floating_rectangle.width); + int nume_y = dy_s * (rd->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 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); + 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; + /* 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(); diff --git a/monitor.h b/monitor.h index f5fa972..10e2164 100644 --- 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 f9e28b7..b57da3c 100644 --- a/parse.c +++ b/parse.c @@ -1,7 +1,8 @@ -#include +#include #include #include -#include "helpers.h" +#include +#include #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 19e9adb..81dd3b9 100644 --- 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 diff --git a/pointer.c b/pointer.c index b1efe83..aa00b40 100644 --- a/pointer.c +++ b/pointer.c @@ -22,6 +22,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #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 8ea4edc..d0f997b 100644 --- a/query.c +++ b/query.c @@ -23,8 +23,6 @@ */ #include -#include -#include #include #include "bspwm.h" #include "desktop.h" @@ -33,13 +31,12 @@ #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; + query_coordinates(&h->loc, rsp); + if (h->next != NULL) { + fprintf(rsp, ","); } - xcb_window_t win = XCB_NONE; - if (h->loc.node != NULL) { - win = h->loc.node->client->window; - } - fprintf(rsp, "%s %s 0x%X\n", h->loc.monitor->name, h->loc.desktop->name, win); } + 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); - } -} - -void query_windows(coordinates_t loc, FILE *rsp) -{ - for (monitor_t *m = mon_head; m != NULL; m = m->next) { - 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) - 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); - } + fprintf(rsp, "%u", s->node->id); + if (s->next != NULL) { + fprintf(rsp, ","); } } + fprintf(rsp, "]"); } -void query_names(domain_t dom, 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) { continue; } - if (dom == DOMAIN_MONITOR) { - fprintf(rsp, "%s\n", m->name); - } for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { if (loc.desktop != NULL && d != loc.desktop) { continue; } - if (dom == DOMAIN_DESKTOP) { - fprintf(rsp, "%s\n", d->name); - } + query_node_ids_in(d->root, d, m, loc, sel, rsp); } } } -client_select_t make_client_select(void) +void query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t loc, node_select_t *sel, FILE *rsp) { - client_select_t sel = { + 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; + } + for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { + 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; + } + fprintf(rsp, "%s\n", d->name); + } + } +} + +void query_monitor_names(coordinates_t loc, monitor_select_t *sel, FILE *rsp) +{ + 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) { + if (loc->node == NULL) { 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) { - 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 0c25aae..cb0ea88 100644 --- a/query.h +++ b/query.h @@ -25,27 +25,31 @@ #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); diff --git a/restore.c b/restore.c index ba523ec..008dbc9 100644 --- a/restore.c +++ b/restore.c @@ -22,10 +22,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include +#include #include #include -#include #include "bspwm.h" #include "desktop.h" #include "ewmh.h" @@ -36,10 +36,8 @@ #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,6 +362,31 @@ node_t *restore_node(jsmntok_t **t, char *json) } } +presel_t *restore_presel(jsmntok_t **t, char *json) +{ + if ((*t)->type == JSMN_PRIMITIVE) { + (*t)++; + return NULL; + } else { + int s = (*t)->size; + (*t)++; + presel_t *p = make_presel(); + + for (int i = 0; i < s; i++) { + if (keyeq("splitRatio", *t, json)) { + (*t)++; + sscanf(json + (*t)->start, "%lf", &p->split_ratio); + RESTORE_ANY(splitDir, &p->split_dir, parse_direction) + } + + (*t)++; + } + + return p; + } +} + + client_t *restore_client(jsmntok_t **t, char *json) { if ((*t)->type == JSMN_PRIMITIVE) { @@ -355,34 +395,28 @@ client_t *restore_client(jsmntok_t **t, char *json) } else { int s = (*t)->size; (*t)++; - client_t *c = make_client(XCB_NONE, 0); + client_t *c = make_client(); for (int i = 0; i < s; i++) { - if (keyeq("window", *t, json)) { - (*t)++; - sscanf(json + (*t)->start, "%u", &c->window); - } else if (keyeq("className", *t, json)) { + 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; -} diff --git a/restore.h b/restore.h index a05fdc0..0871a54 100644 --- 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 b179a1a..3e03712 100644 --- a/rule.c +++ b/rule.c @@ -23,6 +23,9 @@ */ #include +#include +#include +#include #include #include #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 5821bac..0ccf396 100644 --- 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 diff --git a/settings.c b/settings.c index 9330e62..71f4965 100644 --- a/settings.c +++ b/settings.c @@ -22,15 +22,18 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include #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; diff --git a/settings.h b/settings.h index f373076..e50ec80 100644 --- a/settings.h +++ b/settings.h @@ -33,30 +33,20 @@ #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 @@ -69,29 +59,20 @@ 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 05d9b04..2ae9cef 100644 --- a/stack.c +++ b/stack.c @@ -25,6 +25,9 @@ #include #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); - } 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); - } + 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 { + 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 3929cf2..2799ee7 100644 --- 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 diff --git a/subscribe.c b/subscribe.c index 9931a8e..cff175a 100644 --- a/subscribe.c +++ b/subscribe.c @@ -22,8 +22,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include -#include +#include #include #include #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); diff --git a/subscribe.h b/subscribe.h index a140842..9ce019f 100644 --- a/subscribe.h +++ b/subscribe.h @@ -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 index 0000000..e00a826 --- /dev/null +++ b/tests/Makefile @@ -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 index 0000000..eb318fb --- /dev/null +++ b/tests/README.md @@ -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 index 0000000..e05e30a --- /dev/null +++ b/tests/desktop/swap @@ -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 index 0000000..35b7532 --- /dev/null +++ b/tests/desktop/transfer @@ -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 index 0000000..d2ab93e --- /dev/null +++ b/tests/node/flags @@ -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 index 0000000..75273d9 --- /dev/null +++ b/tests/node/insertion @@ -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 index 0000000..846fa78 --- /dev/null +++ b/tests/node/removal @@ -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 index 0000000..aa79734 --- /dev/null +++ b/tests/node/swap @@ -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 index 0000000..9907cd7 --- /dev/null +++ b/tests/node/transfer @@ -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 index 0000000..5c8874a --- /dev/null +++ b/tests/prelude @@ -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 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 index 0000000..1f41a2c --- /dev/null +++ b/tests/test_window.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include + +#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 775ea23..15cccb0 100644 --- a/tree.c +++ b/tree.c @@ -22,6 +22,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include +#include #include #include #include "bspwm.h" @@ -38,11 +41,13 @@ 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); + 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); } - - if (pointer_follows_focus && mon->desk->focus == n && frozen_pointer->action == ACTION_NONE) { - center_pointer(r); - } - } 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) { - if (d == NULL || n == NULL) - return; + presel_t *p = malloc(sizeof(presel_t)); + p->split_dir = DIR_EAST; + p->split_ratio = split_ratio; + p->feedback = XCB_NONE; + return p; +} - /* n: new leaf node */ - /* c: new container node */ +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 (n->presel == NULL) { + return; + } + + if (n->presel->feedback != XCB_NONE) { + xcb_destroy_window(dpy, n->presel->feedback); + } + + free(n->presel); + n->presel = NULL; + + 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; - } 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; + if (f->presel == NULL) { + if (p == NULL) { + if (initial_polarity == FIRST_CHILD) { + c->first_child = n; + c->second_child = f; } 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 = f; + c->second_child = n; } - break; - case MODE_MANUAL: - if (p != NULL) { - if (is_first_child(f)) - p->first_child = c; - else - p->second_child = c; + if (m->rectangle.width > m->rectangle.height) { + c->split_type = TYPE_VERTICAL; + } else { + c->split_type = TYPE_HORIZONTAL; } - 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; - } - if (d->root == f) + 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 { + g->second_child = c; + } + } else { d->root = c; - f->split_mode = MODE_AUTOMATIC; - break; + } + 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; + } + n->birth_rotation = rot; + if (!n->vacant) { + rotate_tree(p, rot); + } + } + } else { + if (p != NULL) { + if (is_first_child(f)) { + p->first_child = c; + } else { + p->second_child = c; + } + } + 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) { - if (d->focus != NULL && n != d->focus && stack_cmp(n->client, d->focus->client) < 0) { - neutralize_obscuring_windows(m, d, n); - } - stack(n, true); - if (d->focus != n) { - window_draw_border(d->focus, false, m == mon); - window_draw_border(n, true, m == mon); + 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) { + neutralize_occluding_windows(m, d, n); + } + stack(d, n, true); + if (d->focus != n) { + 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) - clear_input_focus(); - - if (m->num_sticky > 0 && d != m->desk) { - node_t *a = first_extrema(m->desk->root); - 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; + if (n == NULL && d->root != NULL) { + n = history_last_node(d, NULL); + if (n == NULL) { + n = first_extrema(d->root); } - sticky_still = true; - 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 (mon->desk != d || n == NULL || n->client == NULL) { + clear_input_focus(); + } + + if (m->sticky_count > 0 && d != m->desk) { + sticky_still = false; + transfer_sticky_nodes(m, m->desk, d, m->desk->root); + sticky_still = true; + if (n == NULL && d->focus != NULL) { + n = d->focus; } } + 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; } - 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); + unsigned int sc = 0; - bool focused = (ns == mon->desk->focus); - bool active = (ns == ds->focus); + if (sticky_still && ms->sticky_count > 0 && ds != dd && (sc = sticky_count(ns)) > 0) { + return false; + } - if (focused) { + 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 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 (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 fd48de0..7ef775d 100644 --- a/tree.h +++ b/tree.h @@ -27,18 +27,26 @@ 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 1e8918e..37136bf 100644 --- 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; diff --git a/window.c b/window.c index 693fdd5..2fdc955 100644 --- a/window.c +++ b/window.c @@ -22,7 +22,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include #include #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,80 +226,134 @@ 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; +} + +void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n) +{ + if (n == NULL || n->presel == NULL) { + return; + } + + bool exists = (n->presel->feedback != XCB_NONE); + if (!exists) { + initialize_presel_feedback(n); + } + + 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 (p->split_dir) { + case DIR_NORTH: + presel_rect.height = p->split_ratio * rect.height; + break; + case DIR_EAST: + presel_rect.width = (1 - p->split_ratio) * rect.width; + presel_rect.x = rect.width - presel_rect.width; + break; + case DIR_SOUTH: + presel_rect.height = (1 - p->split_ratio) * rect.height; + presel_rect.y = rect.height - presel_rect.height; + break; + case DIR_WEST: + presel_rect.width = p->split_ratio * rect.width; + break; + } + + window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y, + presel_rect.width, presel_rect.height); + + if (!exists && m->desk == d) { + window_show(p->feedback); + } +} + +void refresh_presel_feebacks_in(node_t *n, desktop_t *d, monitor_t *m) +{ + 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); + } +} + +void update_colors(void) +{ + 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); + } + } +} + +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); + } + } +} + +void draw_border(node_t *n, bool focused_node, bool focused_monitor) +{ + if (n == NULL) { + return; + } + + 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); + } + } +} + +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); } pointer_state_t *make_pointer_state(void) @@ -306,67 +368,22 @@ pointer_state_t *make_pointer_state(void) return p; } -/* 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, 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; - } -} - -void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt) -{ - xcb_rectangle_t rect = get_rectangle(NULL, c); - - switch (dir) { - case DIR_RIGHT: - pt->x = rect.x + rect.width; - pt->y = rect.y + (rect.height / 2); - break; - case DIR_DOWN: - pt->x = rect.x + (rect.width / 2); - pt->y = rect.y + rect.height; - break; - case DIR_LEFT: - pt->x = rect.x; - pt->y = rect.y + (rect.height / 2); - break; - case DIR_UP: - pt->x = rect.x + (rect.width / 2); - pt->y = rect.y; - 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) + if (qtr == NULL) { return; + } 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) + if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) { schedule_window(win); + } } free(qtr); @@ -374,274 +391,75 @@ void adopt_orphans(void) void window_close(node_t *n) { - if (n == NULL || n->client->locked) + if (n == NULL) { return; - - send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW); + } else if (n->client != NULL) { + send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW); + } else { + window_close(n->first_child); + window_close(n->second_child); + } } void window_kill(monitor_t *m, desktop_t *d, node_t *n) { - if (n == NULL) + if (n == NULL) { return; + } - xcb_window_t win = n->client->window; + for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) { + xcb_kill_client(dpy, f->id); + } - 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) +uint32_t get_border_color(bool focused_node, bool focused_monitor) { - if (n == NULL || n->client->layer == l) { - return; - } - - 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) -{ - 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; - } - - 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) -{ - if (n == NULL) { - return; - } - - 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) -{ - 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; + if (focused_monitor && focused_node) { + return get_color_pixel(focused_border_color); + } else if (focused_node) { + return get_color_pixel(active_border_color); } 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); - } - } - - stack(n, (d->focus == n)); -} - -void neutralize_obscuring_windows(monitor_t *m, desktop_t *d, node_t *n) -{ - 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); + return get_color_pixel(normal_border_color); } } -void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value) +void update_floating_rectangle(node_t *n) { - if (n == NULL || n->client->locked == value) - return; - client_t *c = n->client; - put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X locked %s\n", m->name, d->name, c->window, ON_OFF_STR(value)); + xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL); - c->locked = value; - window_draw_border(n, d->focus == n, m == mon); -} - -void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value) -{ - if (n == NULL || n->client->sticky == value) - 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 { - ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_STICKY); - m->num_sticky--; - } - - window_draw_border(n, d->focus == n, m == mon); -} - -void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value) -{ - if (n == NULL || n->client->private == value) - 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)); - - c->private = value; - update_privacy_level(n, value); - window_draw_border(n, d->focus == n, m == mon); -} - -void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value) -{ - 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); - } 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 pxl; -} - -void update_floating_rectangle(client_t *c) -{ - xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), 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; } diff --git a/window.h b/window.h index 7c030ee..ee2c36e 100644 --- a/window.h +++ b/window.h @@ -34,25 +34,19 @@ 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);