bspwm
bspc
*.o
+tests/test_window
-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)"
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
### 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*).
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
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
--- /dev/null
+0.9
\ No newline at end of file
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
#include <stdlib.h>
-#ifdef __OpenBSD__
+#include <sys/stat.h>
#include <sys/types.h>
-#endif
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
-#include <ctype.h>
#include "helpers.h"
#include "common.h"
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) {
} 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;
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) {
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#ifdef __OpenBSD__
+#include <sys/stat.h>
#include <sys/types.h>
-#endif
+#include <sys/time.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <unistd.h>
+#include <stdbool.h>
#include <xcb/xinerama.h>
#include "types.h"
#include "desktop.h"
#include "monitor.h"
#include "settings.h"
#include "messages.h"
-#include "subscribe.h"
#include "events.h"
#include "common.h"
#include "window.h"
#include "history.h"
-#include "stack.h"
#include "ewmh.h"
#include "rule.h"
#include "bspwm.h"
} 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);
}
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();
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) {
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);
}
free(event);
}
}
+
}
- if (!check_connection(dpy))
+ if (!check_connection(dpy)) {
running = false;
+ }
}
cleanup();
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;
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;
}
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);
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) {
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);
}
void cleanup(void)
{
+ mon = NULL;
+
while (mon_head != NULL) {
remove_monitor(mon_head);
}
while (pending_rule_head != NULL) {
remove_pending_rule(pending_rule_head);
}
+
empty_history();
free(frozen_pointer);
}
#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;
xcb_atom_t WM_DELETE_WINDOW;
int exit_status;
-bool visible;
bool auto_raise;
bool sticky_still;
bool record_history;
_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=()
_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
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
#include "bspwm.h"
#include "ewmh.h"
#include "history.h"
#include "monitor.h"
#include "query.h"
#include "tree.h"
-#include "window.h"
#include "desktop.h"
#include "subscribe.h"
#include "settings.h"
{
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;
void change_layout(monitor_t *m, desktop_t *d, layout_t l)
{
- put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout %s %s %s\n", m->name, d->name, l==LAYOUT_TILED?"tiled":"monocle");
d->layout = l;
arrange(m, d);
+
+ put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout %s %s %s\n", m->name, d->name, l==LAYOUT_TILED?"tiled":"monocle");
+
if (d == m->desk) {
put_status(SBSC_MASK_REPORT);
}
}
-void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
{
- if (ms == md) {
- return;
+ if (ms == NULL || md == NULL || d == NULL || ms == md) {
+ return false;
}
- put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer %s %s %s\n", ms->name, d->name, md->name);
+ bool was_active = (d == ms->desk);
- desktop_t *dd = ms->desk;
unlink_desktop(ms, d);
- insert_desktop(md, d);
- if (d == dd) {
- if (ms->desk != NULL) {
- show_desktop(ms->desk);
- }
- if (md->desk != d) {
- hide_desktop(d);
- }
+ if (ms->sticky_count > 0 && was_active && ms->desk != NULL) {
+ sticky_still = false;
+ transfer_sticky_nodes(ms, d, ms->desk, d->root);
+ sticky_still = true;
}
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- translate_client(ms, md, n->client);
+ if (md->desk != NULL) {
+ hide_desktop(d);
}
+ insert_desktop(md, d);
+
+ history_transfer_desktop(md, d);
+ adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
arrange(md, d);
- if (d != dd && md->desk == d) {
- show_desktop(d);
+ if (was_active && ms->desk != NULL) {
+ if (mon == ms) {
+ focus_node(ms, ms->desk, ms->desk->focus);
+ } else {
+ activate_node(ms, ms->desk, ms->desk->focus);
+ }
}
- history_transfer_desktop(md, d);
+ if (md->desk == d) {
+ if (mon == md) {
+ focus_node(md, d, d->focus);
+ } else {
+ activate_node(md, d, d->focus);
+ }
+ }
ewmh_update_wm_desktops();
ewmh_update_desktop_names();
ewmh_update_current_desktop();
+
+ put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer %s %s %s\n", ms->name, d->name, md->name);
put_status(SBSC_MASK_REPORT);
+
+ return true;
}
desktop_t *make_desktop(const char *name)
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;
}
{
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;
}
{
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);
}
}
-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);
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;
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;
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;
}
#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);
-- 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.
.\" Title: bspwm
.\" Author: [see the "Author" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 11/26/2015
+.\" Date: 12/22/2015
.\" Manual: Bspwm Manual
-.\" Source: Bspwm 0.9
+.\" Source: Bspwm 0.9-104-ge434521
.\" Language: English
.\"
-.TH "BSPWM" "1" "11/26/2015" "Bspwm 0\&.9" "Bspwm Manual"
+.TH "BSPWM" "1" "12/22/2015" "Bspwm 0\&.9\-104\-ge434521" "Bspwm Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.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\&.
.RS 4
.\}
.nf
-DIR := left | right | up | down
+DIR := north | west | south | east
CYCLE_DIR := next | prev
.fi
.if n \{\
.\}
.SH "SELECTORS"
.sp
-Selectors are used to select a target window, desktop, or monitor\&. A selector can either describe the target relatively or name it globally\&.
+Selectors are used to select a target node, desktop, or monitor\&. A selector can either describe the target relatively or name it globally\&.
.sp
-Descriptive (relative) selectors consist of a primary selector and any number of non\-conflicting modifiers as follows:
+Selectors consist of a descriptor and any number of non\-conflicting modifiers as follows:
.sp
.if n \{\
.RS 4
.\}
.nf
-PRIMARY_SELECTOR[\&.MODIFIER]*
+DESCRIPTOR(\&.MODIFIER)*
.fi
.if n \{\
.RE
.\}
.sp
-For obvious reasons, neither desktop nor monitor names may be valid descriptive selectors\&.
-.sp
An exclamation mark can be prepended to certain modifiers in order to reverse their meaning\&.
-.SS "Window"
+.SS "Node"
.sp
-Select a window\&.
+Select a node\&.
.sp
.if n \{\
.RS 4
.\}
.nf
-WINDOW_SEL := (<window_id>|DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.[!]automatic][\&.[!](tiled|pseudo_tiled|floating|fullscreen)][\&.[!](below|normal|above)][\&.[!]local][\&.[!]same_class][\&.[!]focused][\&.[!](urgent|sticky|private|locked)]
+NODE_SEL := (<node_id>|PATH|DIR|CYCLE_DIR|last|older|newer|biggest|focused)[\&.[!]focused][\&.[!]automatic][\&.[!]local][\&.[!]leaf][\&.[!]STATE][\&.[!]FLAG][\&.[!]LAYER][\&.[!]same_class]
+
+STATE := tiled|pseudo_tiled|floating|fullscreen
+
+FLAG := urgent|sticky|private|locked
+
+LAYER := below|normal|above
+
+PATH := @[DESK_SEL:][[/]JUMP](/JUMP)*
+
+JUMP := first|1|second|2|brother|parent|DIR
.fi
.if n \{\
.RE
.nr an-break-flag 1
.br
.ps +1
-\fBPrimary Selectors\fR
+\fBDescriptors\fR
+.RS 4
+.PP
+<node_id>
.RS 4
+Selects the node with the given ID\&.
+.RE
+.PP
+\fIPATH\fR
+.RS 4
+Selects the node at the given path\&.
+.RE
.PP
\fIDIR\fR
.RS 4
-Selects the window in the given (spacial) direction relative to the active window\&.
+Selects the window in the given (spacial) direction relative to the active node\&.
.RE
.PP
\fICYCLE_DIR\fR
.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
.nr an-break-flag 1
.br
.ps +1
-\fBModifiers\fR
+\fBPath Jumps\fR
.RS 4
+.sp
+The initial node is the focused node (or the root if the path starts with \fI/\fR) of the focused desktop (or the selected desktop if the path has a \fIDESK_SEL\fR prefix)\&.
.PP
-[!](tiled|pseudo_tiled|floating|fullscreen)
+1|first
.RS 4
-Only consider windows having or not having the given state\&.
+Jumps to the first child\&.
.RE
.PP
-[!]automatic
+2|second
.RS 4
-Only consider windows in automatic or manual insertion mode\&.
+Jumps to the second child\&.
.RE
.PP
+brother
+.RS 4
+Jumps to the brother node\&.
+.RE
+.PP
+parent
+.RS 4
+Jumps to the parent node\&.
+.RE
+.PP
+\fIDIR\fR
+.RS 4
+Jumps to the node holding the edge in the given direction\&.
+.RE
+.RE
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBModifiers\fR
+.RS 4
+.PP
[!]focused
.RS 4
-Only consider focused or unfocused windows\&.
+Only consider focused or unfocused nodes\&.
.RE
.PP
-[!]same_class
+[!]automatic
.RS 4
-Only consider windows that have or don\(cqt have the same class as the current window\&.
+Only consider nodes in automatic or manual insertion mode\&.
.RE
.PP
[!]local
.RS 4
-Only consider windows in or not in the current desktop\&.
+Only consider nodes in or not in the current desktop\&.
+.RE
+.PP
+[!]leaf
+.RS 4
+Only consider leaves or internal nodes\&.
+.RE
+.PP
+[!](tiled|pseudo_tiled|floating|fullscreen)
+.RS 4
+Only consider windows in or not in the given state\&.
+.RE
+.PP
+[!]same_class
+.RS 4
+Only consider windows that have or don\(cqt have the same class as the current window\&.
.RE
.PP
[!](private|urgent|sticky|locked)
.nr an-break-flag 1
.br
.ps +1
-\fBPrimary Selectors\fR
+\fBDescriptors\fR
.RS 4
.PP
<desktop_name>
.nr an-break-flag 1
.br
.ps +1
-\fBPrimary Selectors\fR
+\fBDescriptors\fR
.RS 4
.PP
<monitor_name>
.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
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
\fBGeneral Syntax\fR
.RS 4
.sp
-window [\fIWINDOW_SEL\fR] \fIOPTIONS\fR
+node [\fINODE_SEL\fR] \fICOMMANDS\fR
.RE
.sp
.it 1 an-trap
.nr an-break-flag 1
.br
.ps +1
-\fBOptions\fR
+\fBCommands\fR
.RS 4
.PP
-\fB\-f\fR, \fB\-\-focus\fR [\fIWINDOW_SEL\fR]
+\fB\-f\fR, \fB\-\-focus\fR [\fINODE_SEL\fR]
.RS 4
-Focus the selected or given window\&.
+Focus the selected or given node\&.
.RE
.PP
-\fB\-a\fR, \fB\-\-activate\fR [\fIWINDOW_SEL\fR]
+\fB\-a\fR, \fB\-\-activate\fR [\fINODE_SEL\fR]
.RS 4
-Activate the selected or given window\&.
+Activate the selected or given node\&.
.RE
.PP
\fB\-d\fR, \fB\-\-to\-desktop\fR \fIDESKTOP_SEL\fR
.RS 4
-Send the selected window to the given desktop\&.
+Send the selected node to the given desktop\&.
.RE
.PP
\fB\-m\fR, \fB\-\-to\-monitor\fR \fIMONITOR_SEL\fR
.RS 4
-Send the selected window to the given monitor\&.
+Send the selected node to the given monitor\&.
+.RE
+.PP
+\fB\-n\fR, \fB\-\-to\-node\fR \fINODE_SEL\fR
+.RS 4
+Transplant the selected node to the given node\&.
.RE
.PP
-\fB\-w\fR, \fB\-\-to\-window\fR \fIWINDOW_SEL\fR
+\fB\-s\fR, \fB\-\-swap\fR \fINODE_SEL\fR
.RS 4
-Transplant the selected window to the given window\&.
+Swap the selected node with the given node\&.
.RE
.PP
-\fB\-s\fR, \fB\-\-swap\fR \fIWINDOW_SEL\fR
+\fB\-p\fR, \fB\-\-presel\-dir\fR \fIDIR\fR|cancel
.RS 4
-Swap the selected window with the given window\&.
+Preselect the splitting area of the selected node (or cancel the preselection)\&.
.RE
.PP
-\fB\-p\fR, \fB\-\-presel\fR \fIDIR\fR|cancel
+\fB\-o\fR, \fB\-\-presel\-ratio\fR \fIRATIO\fR
.RS 4
-Preselect the splitting area of the selected window (or cancel the preselection)\&.
+Set the splitting ratio of the preselection area\&.
.RE
.PP
-\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR
+\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR|\(+-\fIPIXELS\fR
.RS 4
-Set the splitting ratio of the selected window (0 <
+Set the splitting ratio of the selected node (0 <
\fIRATIO\fR
< 1)\&.
.RE
.PP
-\fB\-e\fR, \fB\-\-edge\fR \fIDIR\fR \fIRATIO\fR|\(+-\fIPIXELS\fR
+\fB\-R\fR, \fB\-\-rotate\fR \fI90|270|180\fR
+.RS 4
+Rotate the tree rooted at the selected node\&.
+.RE
+.PP
+\fB\-F\fR, \fB\-\-flip\fR \fIhorizontal|vertical\fR
.RS 4
-Set or change the splitting ratio of the edge located in the given direction in relation to the selected window\&.
+Flip the the tree rooted at selected node\&.
.RE
.PP
-\fB\-R\fR, \fB\-\-rotate\fR \fIDIR\fR \fI90|270|180\fR
+\fB\-E\fR, \fB\-\-equalize\fR
+.RS 4
+Reset the split ratios of the tree rooted at the selected node to their default value\&.
+.RE
+.PP
+\fB\-B\fR, \fB\-\-balance\fR
+.RS 4
+Adjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area\&.
+.RE
+.PP
+\fB\-C\fR, \fB\-\-circulate\fR forward|backward
.RS 4
-Rotate the tree holding the edge located in the given direction in relation to the selected window\&.
+Circulate the windows of the tree rooted at the selected node\&.
.RE
.PP
-\fB\-t\fR, \fB\-\-state\fR tiled|pseudo_tiled|floating|fullscreen
+\fB\-t\fR, \fB\-\-state\fR [~](tiled|pseudo_tiled|floating|fullscreen)
.RS 4
Set the state of the selected window\&.
.RE
.PP
\fB\-g\fR, \fB\-\-flag\fR locked|sticky|private[=on|off]
.RS 4
-Set or toggle the given flag for the selected window\&.
+Set or toggle the given flag for the selected node\&.
.RE
.PP
\fB\-l\fR, \fB\-\-layer\fR below|normal|above
.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"
\fBGeneral Syntax\fR
.RS 4
.sp
-desktop [\fIDESKTOP_SEL\fR] \fIOPTIONS\fR
+desktop [\fIDESKTOP_SEL\fR] \fICOMMANDS\fR
.RE
.sp
.it 1 an-trap
.nr an-break-flag 1
.br
.ps +1
-\fBOptions\fR
+\fBCOMMANDS\fR
.RS 4
.PP
\fB\-f\fR, \fB\-\-focus\fR [\fIDESKTOP_SEL\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\&.
.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
\fBGeneral Syntax\fR
.RS 4
.sp
-monitor [\fIMONITOR_SEL\fR] \fIOPTIONS\fR
+monitor [\fIMONITOR_SEL\fR] \fICOMMANDS\fR
.RE
.sp
.it 1 an-trap
.nr an-break-flag 1
.br
.ps +1
-\fBOptions\fR
+\fBCommands\fR
.RS 4
.PP
\fB\-f\fR, \fB\-\-focus\fR [\fIMONITOR_SEL\fR]
\fBGeneral Syntax\fR
.RS 4
.sp
-query \fIOPTIONS\fR
+query \fICOMMANDS\fR [\fIOPTIONS\fR]
.RE
.sp
.it 1 an-trap
.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
.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
\fBOptions\fR
.RS 4
.PP
-\fB\-T\fR, \fB\-\-tree\fR <file_path>
+[\fB\-m\fR,\fB\-\-monitor\fR [\fIMONITOR_SEL\fR]] | [\fB\-d\fR,\fB\-\-desktop\fR [\fIDESKTOP_SEL\fR]] | [\fB\-n\fR, \fB\-\-node\fR [\fINODE_SEL\fR]]
.RS 4
-Load the desktop trees from the given file\&.
-.RE
-.PP
-\fB\-H\fR, \fB\-\-history\fR <file_path>
-.RS 4
-Load the focus history from the given file\&.
-.RE
-.PP
-\fB\-S\fR, \fB\-\-stack\fR <file_path>
-.RS 4
-Load the window stacking order from the given file\&.
+Constrain matches to the selected monitor, desktop or node\&. The descriptor can be omitted for
+\fI\-M\fR,
+\fI\-D\fR
+and
+\fI\-N\fR\&.
.RE
.RE
-.SS "Control"
+.SS "Wm"
.sp
.it 1 an-trap
.nr an-no-space-flag 1
\fBGeneral Syntax\fR
.RS 4
.sp
-control \fIOPTIONS\fR
+wm \fICOMMANDS\fR
.RE
.sp
.it 1 an-trap
.nr an-break-flag 1
.br
.ps +1
-\fBOptions\fR
+\fBCommands\fR
.RS 4
.PP
-\fB\-\-adopt\-orphans\fR
+\fB\-d\fR, \fB\-\-dump\-state\fR
.RS 4
-Manage all the unmanaged windows remaining from a previous session\&.
+Dump the current world state on standard output\&.
.RE
.PP
-\fB\-\-toggle\-visibility\fR
+\fB\-l\fR, \fB\-\-load\-state\fR <file_path>
.RS 4
-Toggle the visibility of all the windows\&.
+Load a world state from the given file\&.
.RE
.PP
-\fB\-\-record\-history\fR on|off
+\fB\-a\fR, \fB\-\-add\-monitor\fR <name> WxH+X+Y
.RS 4
-Enable or disable the recording of window focus history\&.
+Add a monitor for the given name and rectangle\&.
.RE
.PP
-\fB\-\-subscribe\fR (all|report|monitor|desktop|window|\&...)*
+\fB\-r\fR, \fB\-\-remove\-monitor\fR <name>
.RS 4
-Continuously print status information\&. See the
-\fBEVENTS\fR
-section for the detailed description of each event\&.
+Remove the monitor with the given name\&.
+.RE
+.PP
+\fB\-o\fR, \fB\-\-adopt\-orphans\fR
+.RS 4
+Manage all the unmanaged windows remaining from a previous session\&.
.RE
.PP
-\fB\-\-get\-status\fR
+\fB\-h\fR, \fB\-\-record\-history\fR on|off
+.RS 4
+Enable or disable the recording of node focus history\&.
+.RE
+.PP
+\fB\-g\fR, \fB\-\-get\-status\fR
.RS 4
Print the current status information\&.
.RE
\fBGeneral Syntax\fR
.RS 4
.sp
-pointer \fIOPTIONS\fR
+pointer \fICOMMANDS\fR
.RE
.sp
.it 1 an-trap
.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
\fBGeneral Syntax\fR
.RS 4
.sp
-rule \fIOPTIONS\fR
+rule \fICOMMANDS\fR
.RE
.sp
.it 1 an-trap
.nr an-break-flag 1
.br
.ps +1
-\fBOptions\fR
+\fBCommands\fR
.RS 4
.PP
-\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name>|* [\fB\-o\fR|\fB\-\-one\-shot\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]
+\fB\-a\fR, \fB\-\-add\fR (<class_name>|\fB)[:(<instance_name>|\fR)] [\fB\-o\fR|\fB\-\-one\-shot\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]
.RS 4
Create a new rule\&.
.RE
.PP
-\fB\-r\fR, \fB\-\-remove\fR ^<n>|head|tail|<class_name>|<instance_name>|*\&...
+\fB\-r\fR, \fB\-\-remove\fR ^<n>|head|tail|(<class_name>|\fB)[:(<instance_name>|\fR)]\&...
.RS 4
Remove the given rules\&.
.RE
.PP
-\fB\-l\fR, \fB\-\-list\fR [<class_name>|<instance_name>|*]
+\fB\-l\fR, \fB\-\-list\fR
.RS 4
List the rules\&.
.RE
\fBGeneral Syntax\fR
.RS 4
.PP
-config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-w \fIWINDOW_SEL\fR] <key> [<value>]
+config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-n \fINODE_SEL\fR] <key> [<value>]
.RS 4
Get or set the value of <key>\&.
.RE
.RE
+.SS "Subscribe"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
+.RS 4
+.PP
+subscribe (all|report|monitor|desktop|window|\&...)*
+.RS 4
+Continuously print status information\&. See the
+\fBEVENTS\fR
+section for the detailed description of each event\&.
+.RE
+.RE
.SS "Quit"
.sp
.it 1 an-trap
.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
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
desktop layout\&.
.RE
.PP
-\fIleaf_monocle\fR
+\fIsingle_monocle\fR
.RS 4
Set the desktop layout to
\fBmonocle\fR
A desktop is focused\&.
.RE
.PP
+\fIdesktop_activate <monitor_name> <desktop_name>\fR
+.RS 4
+A desktop is activated\&.
+.RE
+.PP
\fIdesktop_layout <monitor_name> <desktop_name> tiled|monocle\fR
.RS 4
The layout of a desktop changed\&.
.RE
.PP
-\fIwindow_manage <monitor_name> <desktop_name> <window_id> <ip_id>\fR
+\fInode_manage <monitor_name> <desktop_name> <node_id> <ip_id>\fR
.RS 4
A window is managed\&.
.RE
.PP
-\fIwindow_unmanage <monitor_name> <desktop_name> <window_id>\fR
+\fInode_unmanage <monitor_name> <desktop_name> <node_id>\fR
.RS 4
A window is unmanaged\&.
.RE
.PP
-\fIwindow_swap <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>\fR
+\fInode_swap <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>\fR
.RS 4
-A window is swapped\&.
+A node is swapped\&.
.RE
.PP
-\fIwindow_transfer <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>\fR
+\fInode_transfer <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>\fR
.RS 4
-A window is transferred\&.
+A node is transferred\&.
.RE
.PP
-\fIwindow_focus <monitor_name> <desktop_name> <window_id>\fR
+\fInode_focus <monitor_name> <desktop_name> <node_id>\fR
.RS 4
-A window is focused\&.
+A node is focused\&.
.RE
.PP
-\fIwindow_activate <monitor_name> <desktop_name> <window_id>\fR
+\fInode_activate <monitor_name> <desktop_name> <node_id>\fR
.RS 4
-A window is activated\&.
+A node is activated\&.
.RE
.PP
-\fIwindow_geometry <monitor_name> <desktop_name> <window_id> <window_geometry>\fR
+\fInode_presel <monitor_name> <desktop_name> <node_id> (dir DIR|ratio RATIO|cancel)\fR
+.RS 4
+A node is preselected\&.
+.RE
+.PP
+\fInode_stack <node_id_1> below|above <node_id_2>\fR
+.RS 4
+A node is stacked below or above another node\&.
+.RE
+.PP
+\fInode_geometry <monitor_name> <desktop_name> <node_id> <node_geometry>\fR
.RS 4
The geometry of a window changed\&.
.RE
.PP
-\fIwindow_state <monitor_name> <desktop_name> <window_id> tiled|pseudo_tiled|floating|fullscreen on|off\fR
+\fInode_state <monitor_name> <desktop_name> <node_id> tiled|pseudo_tiled|floating|fullscreen on|off\fR
.RS 4
The state of a window changed\&.
.RE
.PP
-\fIwindow_flag <monitor_name> <desktop_name> <window_id> sticky|private|locked|urgent on|off\fR
+\fInode_flag <monitor_name> <desktop_name> <node_id> sticky|private|locked|urgent on|off\fR
.RS 4
-One of the flags of a window changed\&.
+One of the flags of a node changed\&.
.RE
.PP
-\fIwindow_layer <monitor_name> <desktop_name> <window_id> below|normal|above\fR
+\fInode_layer <monitor_name> <desktop_name> <node_id> below|normal|above\fR
.RS 4
The layer of a window changed\&.
.RE
.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
*bspwm* [*-h*|*-v*|*-c* 'CONFIG_PATH']
-*bspc* 'COMMAND' ['ARGUMENTS']
+*bspc* 'DOMAIN' ['SELECTOR'] 'COMMANDS'
Description
-----------
------------------
----
-DIR := left | right | up | down
+DIR := north | west | south | east
CYCLE_DIR := next | prev
----
Selectors
---------
-Selectors are used to select a target window, desktop, or monitor. A selector
+Selectors are used to select a target node, desktop, or monitor. A selector
can either describe the target relatively or name it globally.
-Descriptive (relative) selectors consist of a primary selector and any number
-of non-conflicting modifiers as follows:
+Selectors consist of a descriptor and any number of non-conflicting modifiers
+as follows:
- PRIMARY_SELECTOR[.MODIFIER]*
-
-For obvious reasons, neither desktop nor monitor names may be valid descriptive
-selectors.
+ DESCRIPTOR(.MODIFIER)*
An exclamation mark can be prepended to certain modifiers in order to reverse their meaning.
-Window
-~~~~~~
+Node
+~~~~
-Select a window.
+Select a node.
----
-WINDOW_SEL := (<window_id>|DIR|CYCLE_DIR|biggest|last|focused|older|newer)[.[!]automatic][.[!](tiled|pseudo_tiled|floating|fullscreen)][.[!](below|normal|above)][.[!]local][.[!]same_class][.[!]focused][.[!](urgent|sticky|private|locked)]
+NODE_SEL := (<node_id>|PATH|DIR|CYCLE_DIR|last|older|newer|biggest|focused)[.[!]focused][.[!]automatic][.[!]local][.[!]leaf][.[!]STATE][.[!]FLAG][.[!]LAYER][.[!]same_class]
+
+STATE := tiled|pseudo_tiled|floating|fullscreen
+
+FLAG := urgent|sticky|private|locked
+
+LAYER := below|normal|above
+
+PATH := @[DESK_SEL:][[/]JUMP](/JUMP)*
+
+JUMP := first|1|second|2|brother|parent|DIR
----
-Primary Selectors
-^^^^^^^^^^^^^^^^^
+Descriptors
+^^^^^^^^^^^
+
+<node_id>::
+ Selects the node with the given ID.
+
+'PATH'::
+ Selects the node at the given path.
'DIR'::
- Selects the window in the given (spacial) direction relative to the active window.
+ Selects the window in the given (spacial) direction relative to the active node.
'CYCLE_DIR'::
Selects the window in the given (cyclic) direction.
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.
DESKTOP_SEL := (<desktop_name>|[MONITOR_SEL:](focused|^<n>)CYCLE_DIR|last|older|newer)[.[!]occupied][.[!]focused][.[!]urgent][.[!]local]
----
-Primary Selectors
-^^^^^^^^^^^^^^^^^
+Descriptors
+^^^^^^^^^^^
<desktop_name>::
Selects the desktop with the given name.
MONITOR_SEL := (<monitor_name>|^<n>|DIR|CYCLE_DIR|last|primary|focused|older|newer)[.[!]occupied][.[!]focused]
----
-Primary Selectors
-^^^^^^^^^^^^^^^^^
+Descriptors
+^^^^^^^^^^^
<monitor_name>::
Selects the monitor with the given name.
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.
In each layer, the window are orderered as follow: tiled & pseudo-tiled < fullscreen < floating.
-Commands
---------
+Domains
+-------
-Window
-~~~~~~
+Node
+~~~~
General Syntax
^^^^^^^^^^^^^^
-window ['WINDOW_SEL'] 'OPTIONS'
+node ['NODE_SEL'] 'COMMANDS'
-Options
-^^^^^^^
-*-f*, *--focus* ['WINDOW_SEL']::
- Focus the selected or given window.
+Commands
+^^^^^^^^
+*-f*, *--focus* ['NODE_SEL']::
+ Focus the selected or given node.
-*-a*, *--activate* ['WINDOW_SEL']::
- Activate the selected or given window.
+*-a*, *--activate* ['NODE_SEL']::
+ Activate the selected or given node.
*-d*, *--to-desktop* 'DESKTOP_SEL'::
- Send the selected window to the given desktop.
+ Send the selected node to the given desktop.
*-m*, *--to-monitor* 'MONITOR_SEL'::
- Send the selected window to the given monitor.
+ Send the selected node to the given monitor.
+
+*-n*, *--to-node* 'NODE_SEL'::
+ Transplant the selected node to the given node.
-*-w*, *--to-window* 'WINDOW_SEL'::
- Transplant the selected window to the given window.
+*-s*, *--swap* 'NODE_SEL'::
+ Swap the selected node with the given node.
-*-s*, *--swap* 'WINDOW_SEL'::
- Swap the selected window with the given window.
+*-p*, *--presel-dir* [~]'DIR'|cancel::
+ Preselect the splitting area of the selected node (or cancel the preselection).
-*-p*, *--presel* 'DIR'|cancel::
- Preselect the splitting area of the selected window (or cancel the preselection).
+*-o*, *--presel-ratio* 'RATIO'::
+ Set the splitting ratio of the preselection area.
-*-r*, *--ratio* 'RATIO'::
- Set the splitting ratio of the selected window (0 < 'RATIO' < 1).
+*-r*, *--ratio* 'RATIO'|±'PIXELS'::
+ Set the splitting ratio of the selected node (0 < 'RATIO' < 1).
-*-e*, *--edge* 'DIR' 'RATIO'|±'PIXELS'::
- Set or change the splitting ratio of the edge located in the given direction in relation to the selected window.
+*-R*, *--rotate* '90|270|180'::
+ Rotate the tree rooted at the selected node.
+
+*-F*, *--flip* 'horizontal|vertical'::
+ Flip the the tree rooted at selected node.
+
+*-E*, *--equalize*::
+ Reset the split ratios of the tree rooted at the selected node to their default value.
+
+*-B*, *--balance*::
+ Adjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area.
-*-R*, *--rotate* 'DIR' '90|270|180'::
- Rotate the tree holding the edge located in the given direction in relation to the selected window.
+*-C*, *--circulate* forward|backward::
+ Circulate the windows of the tree rooted at the selected node.
-*-t*, *--state* tiled|pseudo_tiled|floating|fullscreen::
+*-t*, *--state* [~](tiled|pseudo_tiled|floating|fullscreen)::
Set the state of the selected window.
*-g*, *--flag* locked|sticky|private[=on|off]::
- Set or toggle the given flag for the selected window.
+ Set or toggle the given flag for the selected node.
*-l*, *--layer* below|normal|above::
Set the stacking layer of the selected window.
*-c*, *--close*::
- Close the selected window.
+ Close the windows rooted at the selected node.
*-k*, *--kill*::
- Kill the selected window.
+ Kill the windows rooted at the selected node.
Desktop
~~~~~~~
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.
*-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.
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.
*-T*, *--tree*::
Print a JSON representation of the matching item.
-*-H*, *--history*::
- Print the focus history as it relates to the query.
-
-*-S*, *--stack*::
- Print the window stacking order.
+Options
+^^^^^^^
-[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-w*, *--window* ['WINDOW_SEL']]::
- Constrain matches to the selected monitor, desktop or window.
+[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-n*, *--node* ['NODE_SEL']]::
+ Constrain matches to the selected monitor, desktop or node. The descriptor can be omitted for '-M', '-D' and '-N'.
-Restore
-~~~~~~~
+Wm
+~~
General Syntax
^^^^^^^^^^^^^^
-restore 'OPTIONS'
-
-Options
-^^^^^^^
-
-*-T*, *--tree* <file_path>::
- Load the desktop trees from the given file.
-
-*-H*, *--history* <file_path>::
- Load the focus history from the given file.
+wm 'COMMANDS'
-*-S*, *--stack* <file_path>::
- Load the window stacking order from the given file.
+Commands
+^^^^^^^^
-Control
-~~~~~~~
+*-d*, *--dump-state*::
+ Dump the current world state on standard output.
-General Syntax
-^^^^^^^^^^^^^^
+*-l*, *--load-state* <file_path>::
+ Load a world state from the given file.
-control 'OPTIONS'
+*-a*, *--add-monitor* <name> WxH+X+Y::
+ Add a monitor for the given name and rectangle.
-Options
-^^^^^^^
+*-r*, *--remove-monitor* <name>::
+ Remove the monitor with the given name.
-*--adopt-orphans*::
+*-o*, *--adopt-orphans*::
Manage all the unmanaged windows remaining from a previous session.
-*--toggle-visibility*::
- Toggle the visibility of all the windows.
+*-h*, *--record-history* on|off::
+ Enable or disable the recording of node focus history.
-*--record-history* on|off::
- Enable or disable the recording of window focus history.
-
-*--subscribe* (all|report|monitor|desktop|window|...)*::
- Continuously print status information. See the *EVENTS* section for the detailed description of each event.
-
-*--get-status*::
+*-g*, *--get-status*::
Print the current status information.
Pointer
General Syntax
^^^^^^^^^^^^^^
-pointer 'OPTIONS'
+pointer 'COMMANDS'
-Options
-^^^^^^^
+Commands
+^^^^^^^^
*-g*, *--grab* focus|move|resize_side|resize_corner::
Initiate the given pointer action.
General Syntax
^^^^^^^^^^^^^^
-rule 'OPTIONS'
+rule 'COMMANDS'
-Options
-^^^^^^^
+Commands
+^^^^^^^^
-*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]::
+*-a*, *--add* (<class_name>|*)[:(<instance_name>|*)] [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL] [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO] [(locked|sticky|private|center|follow|manage|focus|border)=(on|off)]::
Create a new rule.
-*-r*, *--remove* ^<n>|head|tail|<class_name>|<instance_name>|*...::
+*-r*, *--remove* ^<n>|head|tail|(<class_name>|*)[:(<instance_name>|*)]...::
Remove the given rules.
-*-l*, *--list* [<class_name>|<instance_name>|*]::
+*-l*, *--list*::
List the rules.
Config
General Syntax
^^^^^^^^^^^^^^
-config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-w 'WINDOW_SEL'] <key> [<value>]::
+config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-n 'NODE_SEL'] <key> [<value>]::
Get or set the value of <key>.
+Subscribe
+~~~~~~~~~
+
+General Syntax
+^^^^^^^^^^^^^^
+subscribe (all|report|monitor|desktop|window|...)*::
+ Continuously print status information. See the *EVENTS* section for the detailed description of each event.
+
Quit
~~~~
Settings
--------
-Colors are either '#RRGGBB' or http://en.wikipedia.org/wiki/X11_color_names[X color names], booleans are 'true', 'on', 'false' or 'off'.
+Colors are in the form '#RRGGBB', booleans are 'true', 'on', 'false' or 'off'.
All the boolean settings are 'false' by default unless stated otherwise.
Global Settings
~~~~~~~~~~~~~~~
-'focused_border_color'::
- Color of the border of a focused window of a focused monitor.
-
-'active_border_color'::
- Color of the border of a focused window of an unfocused monitor.
-
'normal_border_color'::
Color of the border of an unfocused window.
-'presel_border_color'::
- Color of the *window --presel* message feedback.
-
-'focused_locked_border_color'::
- Color of the border of a focused locked window of a focused monitor.
-
-'active_locked_border_color'::
- Color of the border of a focused locked window of an unfocused monitor.
-
-'normal_locked_border_color'::
- Color of the border of an unfocused locked window.
-
-'focused_sticky_border_color'::
- Color of the border of a focused sticky window of a focused monitor.
-
-'active_sticky_border_color'::
- Color of the border of a focused sticky window of an unfocused monitor.
-
-'normal_sticky_border_color'::
- Color of the border of an unfocused sticky window.
-
-'focused_private_border_color'::
- Color of the border of a focused private window of a focused monitor.
-
-'active_private_border_color'::
- Color of the border of a focused private window of an unfocused monitor.
+'active_border_color'::
+ Color of the border of a focused window of an unfocused monitor.
-'normal_private_border_color'::
- Color of the border of an unfocused private window.
+'focused_border_color'::
+ Color of the border of a focused window of a focused monitor.
-'urgent_border_color'::
- Color of the border of an urgent window.
+'presel_feedback_color'::
+ Color of the *node --presel-{dir,ratio}* message feedback area.
'split_ratio'::
Default split ratio.
'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'::
'desktop_focus <monitor_name> <desktop_name>'::
A desktop is focused.
+'desktop_activate <monitor_name> <desktop_name>'::
+ A desktop is activated.
+
'desktop_layout <monitor_name> <desktop_name> tiled|monocle'::
The layout of a desktop changed.
-'window_manage <monitor_name> <desktop_name> <window_id> <ip_id>'::
+'node_manage <monitor_name> <desktop_name> <node_id> <ip_id>'::
A window is managed.
-'window_unmanage <monitor_name> <desktop_name> <window_id>'::
+'node_unmanage <monitor_name> <desktop_name> <node_id>'::
A window is unmanaged.
-'window_swap <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>'::
- A window is swapped.
+'node_swap <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>'::
+ A node is swapped.
+
+'node_transfer <src_monitor_name> <src_desktop_name> <src_node_id> <dst_monitor_name> <dst_desktop_name> <dst_node_id>'::
+ A node is transferred.
-'window_transfer <src_monitor_name> <src_desktop_name> <src_window_id> <dst_monitor_name> <dst_desktop_name> <dst_window_id>'::
- A window is transferred.
+'node_focus <monitor_name> <desktop_name> <node_id>'::
+ A node is focused.
-'window_focus <monitor_name> <desktop_name> <window_id>'::
- A window is focused.
+'node_activate <monitor_name> <desktop_name> <node_id>'::
+ A node is activated.
-'window_activate <monitor_name> <desktop_name> <window_id>'::
- A window is activated.
+'node_presel <monitor_name> <desktop_name> <node_id> (dir DIR|ratio RATIO|cancel)'::
+ A node is preselected.
-'window_geometry <monitor_name> <desktop_name> <window_id> <window_geometry>'::
+'node_stack <node_id_1> below|above <node_id_2>'::
+ A node is stacked below or above another node.
+
+'node_geometry <monitor_name> <desktop_name> <node_id> <node_geometry>'::
The geometry of a window changed.
-'window_state <monitor_name> <desktop_name> <window_id> tiled|pseudo_tiled|floating|fullscreen on|off'::
+'node_state <monitor_name> <desktop_name> <node_id> tiled|pseudo_tiled|floating|fullscreen on|off'::
The state of a window changed.
-'window_flag <monitor_name> <desktop_name> <window_id> sticky|private|locked|urgent on|off'::
- One of the flags of a window changed.
+'node_flag <monitor_name> <desktop_name> <node_id> sticky|private|locked|urgent on|off'::
+ One of the flags of a node changed.
-'window_layer <monitor_name> <desktop_name> <window_id> below|normal|above'::
+'node_layer <monitor_name> <desktop_name> <node_id> below|normal|above'::
The layer of a window changed.
Please note that *bspwm* initializes monitors before it reads messages on its socket, therefore the initial monitor events can't be received.
'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
---------------------
*/
#include <stdlib.h>
+#include <stdbool.h>
#include "bspwm.h"
#include "ewmh.h"
#include "monitor.h"
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;
}
}
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);
}
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;
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);
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) {
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);
}
}
}
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;
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);
}
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;
}
if (e->mode != XCB_NOTIFY_MODE_NORMAL ||
(mon->desk->focus != NULL &&
- mon->desk->focus->client->window == win)) {
+ mon->desk->focus->id == win)) {
return;
}
} 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);
}
}
}
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdlib.h>
+#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "bspwm.h"
void ewmh_init(void)
{
ewmh = malloc(sizeof(xcb_ewmh_connection_t));
- if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0)
+ if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
err("Can't initialize EWMH atoms.\n");
+ }
}
void ewmh_update_active_window(void)
{
- xcb_window_t win = (mon->desk->focus == NULL ? XCB_NONE : mon->desk->focus->client->window);
+ xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
xcb_ewmh_set_active_window(ewmh, default_screen, win);
}
void ewmh_update_number_of_desktops(void)
{
- uint32_t num_desktops = 0;
+ uint32_t desktops_count = 0;
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- num_desktops++;
+ desktops_count++;
}
}
- xcb_ewmh_set_number_of_desktops(ewmh, default_screen, num_desktops);
+ xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
}
uint32_t ewmh_get_desktop_index(desktop_t *d)
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);
}
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)
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);
}
}
}
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;
}
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
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
#! /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"
}
#! /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
-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
+# 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}
#! /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
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
#! /bin/sh
#
-# Example panel for LemonBoy's bar
+# Example panel for lemonbar
. panel_colors
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
-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"
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
+#
+# wm independent hotkeys
+#
+
+# terminal emulator
+super + Return
+ urxvt
+
+# program launcher
+super + space
+ dmenu_run
+
+# make sxhkd reload its configuration files:
+super + Escape
+ pkill -USR1 -x sxhkd
+
#
# bspwm hotkeys
#
+# quit bspwm normally
super + alt + Escape
bspc quit
-super + w
- bspc window -c
+# close and kill
+super + {_,shift + }w
+ bspc node -{c,k}
-super + n
+# alternate between the tiled and monocle layout
+super + m
bspc desktop -l next
-super + {t,p,s,f}
- bspc window -t {tiled,pseudo_tiled,floating,fullscreen}
+# if the current node is automatic, send it to the last manual, otherwise pull the last leaf
+super + y
+ bspc query -N -n focused.automatic && bspc node -n last.!automatic || bspc node last.leaf -n focused
-super + {grave,Tab}
- bspc {window,desktop} -f last
+# swap the current node and the biggest node
+super + g
+ bspc node -s biggest
-super + apostrophe
- bspc window -s last
+#
+# state/flags
+#
-super + {o,i}
- bspc control --record-history off; \
- bspc window {older,newer} -f; \
- bspc control --record-history on
+# set the window state
+super + {t,shift + t,s,f}
+ bspc node -t {tiled,pseudo_tiled,floating,fullscreen}
-super + y
- bspc window -w last.!automatic
+# set the node flags
+super + ctrl + {x,y,z}
+ bspc node -g {locked,sticky,private}
-super + m
- bspc window -s biggest
+#
+# focus/swap
+#
+# focus the node in the given direction
super + {_,shift + }{h,j,k,l}
- bspc window -{f,s} {left,down,up,right}
+ bspc node -{f,s} {west,south,north,east}
-super + {_,shift + }c
- bspc window -f {next,prev}
+# focus the node for the given path jump
+super + {p,b,comma,period}
+ bspc node -f @{parent,brother,first,second}
-super + {comma,period}
- bspc desktop -C {backward,forward}
+# focus the next/previous node
+super + {_,shift + }c
+ bspc node -f {next,prev}
+# focus the next/previous desktop
super + bracket{left,right}
bspc desktop -f {prev,next}
+# focus the last node/desktop
+super + {grave,Tab}
+ bspc {node,desktop} -f last
+
+# focus the older or newer node in the focus history
+super + {o,i}
+ bspc wm -h off; \
+ bspc node {older,newer} -f; \
+ bspc wm -h on
+
+# focus or send to the given desktop
+super + {_,shift + }{1-9,0}
+ bspc {desktop -f,node -d} '^{1-9,10}'
+
+#
+# preselect
+#
+
+# preselect the direction
super + ctrl + {h,j,k,l}
- bspc window -p {left,down,up,right}
+ bspc node -p {west,south,north,east}
+
+# preselect the ratio
+super + ctrl + {1-9}
+ bspc node -o 0.{1-9}
+# cancel the preselection for the focused node or desktop
super + ctrl + {_,shift + }space
- bspc {window -p cancel,desktop -c}
+ bspc node @{_,/} -p cancel
+#
+# resize tiled/floating
+#
+
+# expand the tiled space in the given direction
super + alt + {h,j,k,l}
- bspc window -e {left -10,down +10,up -10,right +10}
+ bspc node {@west -r -10,@south -r +10,@north -r -10,@east -r +10}
+# contract the tiled space in the given direction
super + alt + shift + {h,j,k,l}
- bspc window -e {right -10,up +10,down -10,left +10}
-
-super + ctrl + {1-9}
- bspc window -r 0.{1-9}
+ bspc node {@east -r -10,@north -r +10,@south -r -10,@west -r +10}
+# move a floating window
super + {Left,Down,Up,Right}
xdo move {-x -20,-y +20,-y -20,-x +20}
-super + {_,shift + }{1-9,0}
- bspc {desktop -f,window -d} '^{1-9,10}'
+#
+# pointer focus/move/resize
+#
+# focus
~button1
bspc pointer -g focus
+# start move/resize
super + button{1-3}
; bspc pointer -g {move,resize_side,resize_corner}
+# end move/resize
super + @button{1-3}
bspc pointer -u
-
-#
-# wm independent hotkeys
-#
-
-super + Return
- urxvt
-
-super + space
- dmenu_run
-
-# make sxhkd reload its configuration files:
-super + Escape
- pkill -USR1 -x sxhkd
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <fcntl.h>
+#include <ctype.h>
#include <math.h>
#include "bspwm.h"
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)
#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)
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
*/
#include <stdlib.h>
+#include <stdbool.h>
#include "bspwm.h"
+#include "tree.h"
#include "query.h"
history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
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;
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) {
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 {
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;
}
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;
}
h = h->prev;
i++;
}
- if (h == NULL)
+ if (h == NULL) {
return -1;
- else
+ } else {
return i;
+ }
}
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);
-#include <stdlib.h>
-
#include "jsmn.h"
/**
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
#include "bspwm.h"
#include "desktop.h"
-#include "ewmh.h"
-#include "history.h"
#include "monitor.h"
#include "pointer.h"
#include "query.h"
#include "tree.h"
#include "window.h"
#include "common.h"
-#include "subscribe.h"
#include "parse.h"
#include "messages.h"
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) {
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)) {
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;
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;
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 {
}
} 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;
}
}
if (streq("locked", key)) {
- set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->locked));
+ set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
} else if (streq("sticky", key)) {
- set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->sticky));
+ set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
} else if (streq("private", key)) {
- set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->private));
+ set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
} else {
return MSG_FAILURE;
}
- } else if (streq("-p", *args) || streq("--presel", *args)) {
+ } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
num--, args++;
- if (num < 1)
+ if (num < 1) {
return MSG_SYNTAX;
- if (IS_FLOATING(trg.node->client) ||
- trg.desktop->layout != LAYOUT_TILED)
+ }
+ if (trg.node->vacant) {
return MSG_FAILURE;
+ }
if (streq("cancel", *args)) {
- reset_mode(&trg);
+ cancel_presel(trg.monitor, trg.desktop, trg.node);
} else {
+ bool alternate = false;
+ if ((*args)[0] == '~') {
+ alternate = true;
+ (*args)++;
+ }
direction_t dir;
if (parse_direction(*args, &dir)) {
- double rat = trg.node->split_ratio;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1)
- return MSG_FAILURE;
+ if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
+ cancel_presel(trg.monitor, trg.desktop, trg.node);
+ } else {
+ presel_dir(trg.monitor, trg.desktop, trg.node, dir);
+ draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
}
- trg.node->split_mode = MODE_MANUAL;
- trg.node->split_dir = dir;
- trg.node->split_ratio = rat;
- window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
} else {
return MSG_FAILURE;
}
}
- } else if (streq("-e", *args) || streq("--edge", *args)) {
+ } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
num--, args++;
- if (num < 2)
+ if (num < 1) {
return MSG_SYNTAX;
- if (IS_FLOATING(trg.node->client))
- return MSG_FAILURE;
- direction_t dir;
- if (!parse_direction(*args, &dir))
+ }
+ if (trg.node->vacant) {
return MSG_FAILURE;
- node_t *n = find_fence(trg.node, dir);
- if (n == NULL)
+ }
+ double rat;
+ if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
return MSG_FAILURE;
+ } else {
+ presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
+ draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
+ }
+ } else if (streq("-r", *args) || streq("--ratio", *args)) {
num--, args++;
+ if (num < 1) {
+ return MSG_SYNTAX;
+ }
if ((*args)[0] == '+' || (*args)[0] == '-') {
int pix;
if (sscanf(*args, "%i", &pix) == 1) {
- int max = (n->split_type == TYPE_HORIZONTAL ? n->rectangle.height : n->rectangle.width);
- double rat = ((max * n->split_ratio) + pix) / max;
- if (rat > 0 && rat < 1)
- n->split_ratio = rat;
- else
+ int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
+ double rat = ((max * trg.node->split_ratio) + pix) / max;
+ if (rat > 0 && rat < 1) {
+ trg.node->split_ratio = rat;
+ } else {
return MSG_FAILURE;
+ }
} else {
return MSG_FAILURE;
}
} else {
double rat;
- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1)
- n->split_ratio = rat;
- else
+ if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+ trg.node->split_ratio = rat;
+ } else {
return MSG_FAILURE;
+ }
}
dirty = true;
- } else if (streq("-r", *args) || streq("--ratio", *args)) {
+ } else if (streq("-F", *args) || streq("--flip", *args)) {
num--, args++;
- if (num < 1)
+ if (num < 1) {
return MSG_SYNTAX;
- double rat;
- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
- trg.node->split_ratio = rat;
- window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
+ }
+ flip_t flp;
+ if (parse_flip(*args, &flp)) {
+ flip_tree(trg.node, flp);
+ dirty = true;
} else {
return MSG_FAILURE;
}
- } else if (streq("-l", *args) || streq("--layer", *args)) {
+ } else if (streq("-R", *args) || streq("--rotate", *args)) {
num--, args++;
if (num < 1) {
return MSG_SYNTAX;
}
- stack_layer_t lyr;
- if (parse_stack_layer(*args, &lyr)) {
- set_layer(trg.monitor, trg.desktop, trg.node, lyr);
+ int deg;
+ if (parse_degree(*args, °)) {
+ rotate_tree(trg.node, deg);
+ dirty = true;
} else {
return MSG_FAILURE;
}
- } else if (streq("-R", *args) || streq("--rotate", *args)) {
+ } else if (streq("-E", *args) || streq("--equalize", *args)) {
+ equalize_tree(trg.node);
+ dirty = true;
+ } else if (streq("-B", *args) || streq("--balance", *args)) {
+ balance_tree(trg.node);
+ dirty = true;
+ } else if (streq("-C", *args) || streq("--circulate", *args)) {
num--, args++;
- if (num < 2)
+ if (num < 1) {
return MSG_SYNTAX;
- direction_t dir;
- if (!parse_direction(*args, &dir))
- return MSG_FAILURE;
- node_t *n = find_fence(trg.node, dir);
- if (n == NULL)
- return MSG_FAILURE;
- num--, args++;
- int deg;
- if (parse_degree(*args, °)) {
- rotate_tree(n, deg);
+ }
+ circulate_dir_t cir;
+ if (parse_circulate_direction(*args, &cir)) {
+ circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
dirty = true;
} else {
return MSG_FAILURE;
}
} else if (streq("-c", *args) || streq("--close", *args)) {
- if (num > 1)
+ if (num > 1) {
return MSG_SYNTAX;
+ }
+ if (locked_count(trg.node) > 0) {
+ return MSG_FAILURE;
+ }
window_close(trg.node);
} else if (streq("-k", *args) || streq("--kill", *args)) {
- if (num > 1)
+ if (num > 1) {
return MSG_SYNTAX;
+ }
window_kill(trg.monitor, trg.desktop, trg.node);
dirty = true;
} else {
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;
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;
}
} 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) {
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);
}
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) {
}
} 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++;
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;
}
{
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)) {
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);
} 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++;
}
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;
}
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 {
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;
if (!restore_tree(*args)) {
return MSG_FAILURE;
}
- } else if (streq("-H", *args) || streq("--history", *args)) {
+ } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
num--, args++;
- if (num < 1) {
+ if (num < 2) {
return MSG_SYNTAX;
}
- if (!restore_history(*args)) {
- return MSG_FAILURE;
+ char *name = *args;
+ num--, args++;
+ xcb_rectangle_t r;
+ if (parse_rectangle(*args, &r)) {
+ monitor_t *m = make_monitor(&r);
+ snprintf(m->name, sizeof(m->name), "%s", name);
+ add_monitor(m);
+ add_desktop(m, make_desktop(NULL));
+ } else {
+ return MSG_SYNTAX;
}
- } else if (streq("-S", *args) || streq("--stack", *args)) {
+ } else if (streq("-r", *args) || streq("--remove-monitor", *args)) {
num--, args++;
if (num < 1) {
return MSG_SYNTAX;
}
- if (!restore_stack(*args)) {
+ if (mon_head == mon_tail) {
return MSG_FAILURE;
}
- } else {
- return MSG_SYNTAX;
- }
- num--, args++;
- }
-
- return MSG_SUCCESS;
-}
-
-int cmd_control(char **args, int num, FILE *rsp)
-{
- if (num < 1)
- return MSG_SYNTAX;
- while (num > 0) {
- if (streq("--adopt-orphans", *args)) {
- adopt_orphans();
- } else if (streq("--toggle-visibility", *args)) {
- toggle_visibility();
- } else if (streq("--subscribe", *args)) {
- num--, args++;
- int field = 0;
- if (num < 1) {
- field = SBSC_MASK_REPORT;
+ monitor_t *m = find_monitor(*args);
+ if (m != NULL) {
+ remove_monitor(m);
} else {
- subscriber_mask_t mask;
- while (num > 0) {
- if (parse_subscriber_mask(*args, &mask)) {
- field |= mask;
- } else {
- return MSG_SYNTAX;
- }
- num--, args++;
- }
+ return MSG_FAILURE;
}
- add_subscriber(rsp, field);
- return MSG_SUBSCRIBE;
- } else if (streq("--get-status", *args)) {
+ } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
+ adopt_orphans();
+ } else if (streq("-g", *args) || streq("--get-status", *args)) {
print_report(rsp);
- } else if (streq("--record-history", *args)) {
+ } else if (streq("-h", *args) || streq("--record-history", *args)) {
num--, args++;
- if (num < 1)
+ if (num < 1) {
return MSG_SYNTAX;
+ }
bool b;
- if (parse_bool(*args, &b))
+ if (parse_bool(*args, &b)) {
record_history = b;
- else
+ } else {
return MSG_SYNTAX;
+ }
} else {
return MSG_SYNTAX;
}
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; \
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) \
#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;
}
} 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);
}
}
}
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)
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;
}
#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)
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;
-}
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
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
#include "bspwm.h"
#include "desktop.h"
#include "ewmh.h"
{
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 {
monitor_t *find_monitor(char *name)
{
- for (monitor_t *m = mon_head; m != NULL; m = m->next)
- if (streq(m->name, name))
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (streq(m->name, name)) {
return m;
+ }
+ }
return NULL;
}
monitor_t *get_monitor_by_id(xcb_randr_output_t id)
{
- for (monitor_t *m = mon_head; m != NULL; m = m->next)
- if (m->id == id)
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (m->id == id) {
return m;
+ }
+ }
return NULL;
}
void embrace_client(monitor_t *m, client_t *c)
{
- if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x)
+ if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x) {
c->floating_rectangle.x = m->rectangle.x;
- else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width))
+ } else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width)) {
c->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;
- if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y)
+ }
+ if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y) {
c->floating_rectangle.y = m->rectangle.y;
- else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height))
+ } else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height)) {
c->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;
+ }
}
-void translate_client(monitor_t *ms, monitor_t *md, client_t *c)
+void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
{
- if (frozen_pointer->action != ACTION_NONE || ms == md)
+ if (frozen_pointer->action != ACTION_NONE) {
return;
+ }
- /* Clip the rectangle to fit into the monitor. Without this, the fitting
- * algorithm doesn't work as expected. This also conserves the
- * out-of-bounds regions */
- int left_adjust = MAX((ms->rectangle.x - c->floating_rectangle.x), 0);
- int top_adjust = MAX((ms->rectangle.y - c->floating_rectangle.y), 0);
- int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (ms->rectangle.x + ms->rectangle.width), 0);
- int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (ms->rectangle.y + ms->rectangle.height), 0);
- c->floating_rectangle.x += left_adjust;
- c->floating_rectangle.y += top_adjust;
- c->floating_rectangle.width -= (left_adjust + right_adjust);
- c->floating_rectangle.height -= (top_adjust + bottom_adjust);
-
- int dx_s = c->floating_rectangle.x - ms->rectangle.x;
- int dy_s = c->floating_rectangle.y - ms->rectangle.y;
-
- int nume_x = dx_s * (md->rectangle.width - c->floating_rectangle.width);
- int nume_y = dy_s * (md->rectangle.height - c->floating_rectangle.height);
-
- int deno_x = ms->rectangle.width - c->floating_rectangle.width;
- int deno_y = ms->rectangle.height - c->floating_rectangle.height;
-
- int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
- int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
-
- /* Translate and undo clipping */
- c->floating_rectangle.width += left_adjust + right_adjust;
- c->floating_rectangle.height += top_adjust + bottom_adjust;
- c->floating_rectangle.x = md->rectangle.x + dx_d - left_adjust;
- c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ client_t *c = f->client;
+ /* Clip the rectangle to fit into the monitor. Without this, the fitting
+ * algorithm doesn't work as expected. This also conserves the
+ * out-of-bounds regions */
+ int left_adjust = MAX((rs->x - c->floating_rectangle.x), 0);
+ int top_adjust = MAX((rs->y - c->floating_rectangle.y), 0);
+ int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (rs->x + rs->width), 0);
+ int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (rs->y + rs->height), 0);
+ c->floating_rectangle.x += left_adjust;
+ c->floating_rectangle.y += top_adjust;
+ c->floating_rectangle.width -= (left_adjust + right_adjust);
+ c->floating_rectangle.height -= (top_adjust + bottom_adjust);
+
+ int dx_s = c->floating_rectangle.x - rs->x;
+ int dy_s = c->floating_rectangle.y - rs->y;
+
+ int nume_x = dx_s * (rd->width - c->floating_rectangle.width);
+ int nume_y = dy_s * (rd->height - c->floating_rectangle.height);
+
+ int deno_x = rs->width - c->floating_rectangle.width;
+ int deno_y = rs->height - c->floating_rectangle.height;
+
+ int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
+ int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
+
+ /* Translate and undo clipping */
+ c->floating_rectangle.width += left_adjust + right_adjust;
+ c->floating_rectangle.height += top_adjust + bottom_adjust;
+ c->floating_rectangle.x = rd->x + dx_d - left_adjust;
+ c->floating_rectangle.y = rd->y + dy_d - top_adjust;
+ }
}
void focus_monitor(monitor_t *m)
{
- if (mon == m)
+ if (mon == m) {
return;
-
- put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus %s\n", m->name);
+ }
mon = m;
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;
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;
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);
}
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;
ewmh_update_wm_desktops();
ewmh_update_desktop_names();
ewmh_update_current_desktop();
+
put_status(SBSC_MASK_REPORT);
}
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) {
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;
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);
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);
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);
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 */
}
/* 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();
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);
-#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "helpers.h"
+#include <stdbool.h>
+#include <errno.h>
#include "parse.h"
bool parse_bool(char *value, bool *b)
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;
}
}
-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;
}
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
#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);
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
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdbool.h>
#include "bspwm.h"
#include "query.h"
#include "settings.h"
#include "monitor.h"
#include "subscribe.h"
#include "window.h"
-#include "pointer.h"
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;
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;
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:
} 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;
}
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;
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;
}
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;
}
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;
}
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;
}
*/
#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
#include <string.h>
#include "bspwm.h"
#include "desktop.h"
#include "monitor.h"
#include "tree.h"
#include "query.h"
-#include "jsmn.h"
void query_tree(FILE *rsp)
{
fprintf(rsp, "{");
fprintf(rsp, "\"focusedMonitorName\":\"%s\",", mon->name);
- fprintf(rsp, "\"numClients\":%i,", num_clients);
+ fprintf(rsp, "\"clientsCount\":%i,", clients_count);
fprintf(rsp, "\"monitors\":");
fprintf(rsp, "[");
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
}
}
fprintf(rsp, "]");
+ fprintf(rsp,",");
+ fprintf(rsp, "\"focusHistory\":");
+ query_history(rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"stackingList\":");
+ query_stack(rsp);
fprintf(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,",");
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, "}");
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,",");
}
}
+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);
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);
fprintf(rsp, "{\"x\":%i,\"y\":%i,\"width\":%u,\"height\":%u}", r.x, r.y, r.width, r.height);
}
-void query_wm_state(xcb_atom_t *wm_state, int num_states, FILE *rsp)
+void query_wm_state(xcb_atom_t *wm_state, int wm_states_count, FILE *rsp)
{
fprintf(rsp, "[");
- for (int i = 0; i < num_states; i++) {
+ for (int i = 0; i < wm_states_count; i++) {
fprintf(rsp, "%u", wm_state[i]);
- if (i < num_states - 1) {
+ if (i < wm_states_count - 1) {
fprintf(rsp, ",");
}
}
fprintf(rsp, "]");
}
-void query_history(coordinates_t loc, FILE *rsp)
+void query_history(FILE *rsp)
{
+ fprintf(rsp, "[");
for (history_t *h = history_head; h != NULL; h = h->next) {
- if ((loc.monitor != NULL && h->loc.monitor != loc.monitor)
- || (loc.desktop != NULL && h->loc.desktop != loc.desktop)) {
- continue;
- }
- xcb_window_t win = XCB_NONE;
- if (h->loc.node != NULL) {
- win = h->loc.node->client->window;
+ query_coordinates(&h->loc, rsp);
+ if (h->next != NULL) {
+ fprintf(rsp, ",");
}
- fprintf(rsp, "%s %s 0x%X\n", h->loc.monitor->name, h->loc.desktop->name, win);
}
+ fprintf(rsp, "]");
+}
+
+void query_coordinates(coordinates_t *loc, FILE *rsp)
+{
+ fprintf(rsp, "{\"monitorName\":\"%s\",\"desktopName\":\"%s\",\"nodeId\":%u}", loc->monitor->name, loc->desktop->name, loc->node!=NULL?loc->node->id:0);
}
void query_stack(FILE *rsp)
{
+ fprintf(rsp, "[");
for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
- fprintf(rsp, "0x%X\n", s->node->client->window);
+ fprintf(rsp, "%u", s->node->id);
+ if (s->next != NULL) {
+ fprintf(rsp, ",");
+ }
}
+ fprintf(rsp, "]");
}
-void query_windows(coordinates_t loc, FILE *rsp)
+void query_node_ids(coordinates_t loc, node_select_t *sel, FILE *rsp)
{
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (loc.monitor != NULL && m != loc.monitor)
+ if (loc.monitor != NULL && m != loc.monitor) {
continue;
+ }
for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (loc.desktop != NULL && d != loc.desktop)
+ if (loc.desktop != NULL && d != loc.desktop) {
continue;
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (loc.node != NULL && n != loc.node)
- continue;
- fprintf(rsp, "0x%X\n", n->client->window);
}
+ query_node_ids_in(d->root, d, m, loc, sel, rsp);
}
}
}
-void query_names(domain_t dom, coordinates_t loc, FILE *rsp)
+void query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t loc, node_select_t *sel, FILE *rsp)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+ coordinates_t trg = {m, d, n};
+ if ((loc.node == NULL || n == loc.node) &&
+ (sel == NULL || node_matches(&trg, &ref, *sel))) {
+ fprintf(rsp, "0x%07X\n", n->id);
+ }
+ query_node_ids_in(n->first_child, d, m, loc, sel, rsp);
+ query_node_ids_in(n->second_child, d, m, loc, sel, rsp);
+ }
+}
+
+void query_desktop_names(coordinates_t loc, desktop_select_t *sel, FILE *rsp)
{
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
if (loc.monitor != NULL && m != loc.monitor) {
continue;
}
- if (dom == DOMAIN_MONITOR) {
- fprintf(rsp, "%s\n", m->name);
- }
for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (loc.desktop != NULL && d != loc.desktop) {
+ coordinates_t ref = {mon, mon->desk, NULL};
+ coordinates_t trg = {m, d, NULL};
+ if ((loc.desktop != NULL && d != loc.desktop) ||
+ (sel != NULL && !desktop_matches(&trg, &ref, *sel))) {
continue;
}
- if (dom == DOMAIN_DESKTOP) {
- fprintf(rsp, "%s\n", d->name);
- }
+ fprintf(rsp, "%s\n", d->name);
}
}
}
-client_select_t make_client_select(void)
+void query_monitor_names(coordinates_t loc, monitor_select_t *sel, FILE *rsp)
{
- client_select_t sel = {
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ coordinates_t ref = {mon, NULL, NULL};
+ coordinates_t trg = {m, NULL, NULL};
+ if ((loc.monitor != NULL && m != loc.monitor) ||
+ (sel != NULL && !monitor_matches(&trg, &ref, *sel))) {
+ continue;
+ }
+ fprintf(rsp, "%s\n", m->name);
+ }
+}
+
+node_select_t make_node_select(void)
+{
+ node_select_t sel = {
+ .automatic = OPTION_NONE,
+ .focused = OPTION_NONE,
+ .local = OPTION_NONE,
+ .leaf = OPTION_NONE,
.tiled = OPTION_NONE,
.pseudo_tiled = OPTION_NONE,
.floating = OPTION_NONE,
.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
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;
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);
}
}
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;
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;
return (dst->monitor != NULL);
}
-#undef GET_MOD
-
bool locate_window(xcb_window_t win, coordinates_t *loc)
{
- for (monitor_t *m = mon_head; m != NULL; m = m->next)
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
- if (n->client->window == win) {
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->id == win) {
loc->monitor = m;
loc->desktop = d;
loc->node = n;
return true;
}
+ }
+ }
+ }
return false;
}
bool locate_desktop(char *name, coordinates_t *loc)
{
- for (monitor_t *m = mon_head; m != NULL; m = m->next)
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
if (streq(d->name, name)) {
loc->monitor = m;
loc->desktop = d;
return true;
}
+ }
+ }
return false;
}
bool locate_monitor(char *name, coordinates_t *loc)
{
- for (monitor_t *m = mon_head; m != NULL; m = m->next)
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
if (streq(m->name, name)) {
loc->monitor = m;
return true;
}
+ }
return false;
}
bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm)
{
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (mm != NULL && m != mm)
+ if (mm != NULL && m != mm) {
continue;
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--)
+ }
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
if (i == 1) {
loc->monitor = m;
loc->desktop = d;
loc->node = NULL;
return true;
}
+ }
}
return false;
}
bool monitor_from_index(int i, coordinates_t *loc)
{
- for (monitor_t *m = mon_head; m != NULL; m = m->next, i--)
+ for (monitor_t *m = mon_head; m != NULL; m = m->next, i--) {
if (i == 1) {
loc->monitor = m;
loc->desktop = NULL;
loc->node = NULL;
return true;
}
+ }
return false;
}
-bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel)
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
{
- if (loc->node == NULL)
- return false;
-
-#define WSTATE(prop) \
- if (sel.prop != OPTION_NONE && \
- !loc->node->client->prop \
- ? sel.prop == OPTION_TRUE \
- : sel.prop == OPTION_FALSE) { \
- return false; \
- }
- WSTATE(locked)
- WSTATE(sticky)
- WSTATE(private)
- WSTATE(urgent)
-#undef MATCHSTATE
-
- if (sel.tiled != OPTION_NONE &&
- loc->node->client->state != STATE_TILED
- ? sel.tiled == OPTION_TRUE
- : sel.tiled == OPTION_FALSE) {
- return false;
- }
-
- if (sel.pseudo_tiled != OPTION_NONE &&
- loc->node->client->state != STATE_PSEUDO_TILED
- ? sel.pseudo_tiled == OPTION_TRUE
- : sel.pseudo_tiled == OPTION_FALSE) {
+ if (loc->node == NULL) {
return false;
}
- if (sel.floating != OPTION_NONE &&
- loc->node->client->state != STATE_FLOATING
- ? sel.floating == OPTION_TRUE
- : sel.floating == OPTION_FALSE) {
- return false;
- }
-
- if (sel.fullscreen != OPTION_NONE &&
- loc->node->client->state != STATE_FULLSCREEN
- ? sel.fullscreen == OPTION_TRUE
- : sel.fullscreen == OPTION_FALSE) {
- return false;
- }
-
- if (sel.same_class != OPTION_NONE && ref->node != NULL &&
- streq(loc->node->client->class_name, ref->node->client->class_name)
- ? sel.same_class == OPTION_FALSE
- : sel.same_class == OPTION_TRUE) {
+ if (sel.focused != OPTION_NONE &&
+ loc->node != mon->desk->focus
+ ? sel.focused == OPTION_TRUE
+ : sel.focused == OPTION_FALSE) {
return false;
}
if (sel.automatic != OPTION_NONE &&
- loc->node->split_mode == MODE_MANUAL
+ loc->node->presel != NULL
? sel.automatic == OPTION_TRUE
: sel.automatic == OPTION_FALSE) {
return false;
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;
}
#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);
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);
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <fcntl.h>
#include "bspwm.h"
#include "desktop.h"
#include "ewmh.h"
#include "tree.h"
#include "settings.h"
#include "restore.h"
-#include "helpers.h"
-#include "common.h"
+#include "window.h"
#include "parse.h"
-#include "jsmn.h"
bool restore_tree(const char *file_path)
{
return false;
}
+ mon = NULL;
+
while (mon_head != NULL) {
remove_monitor(mon_head);
}
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;
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++;
}
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();
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)
{
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);
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)) {
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);
(*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;
} 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);
(*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)++;
}
}
}
-client_t *restore_client(jsmntok_t **t, char *json)
+presel_t *restore_presel(jsmntok_t **t, char *json)
{
if ((*t)->type == JSMN_PRIMITIVE) {
(*t)++;
} else {
int s = (*t)->size;
(*t)++;
- client_t *c = make_client(XCB_NONE, 0);
+ presel_t *p = make_presel();
for (int i = 0; i < s; i++) {
- if (keyeq("window", *t, json)) {
+ if (keyeq("splitRatio", *t, json)) {
(*t)++;
- sscanf(json + (*t)->start, "%u", &c->window);
- } else if (keyeq("className", *t, json)) {
+ sscanf(json + (*t)->start, "%lf", &p->split_ratio);
+ RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
+ }
+
+ (*t)++;
+ }
+
+ return p;
+ }
+}
+
+
+client_t *restore_client(jsmntok_t **t, char *json)
+{
+ if ((*t)->type == JSMN_PRIMITIVE) {
+ (*t)++;
+ return NULL;
+ } else {
+ int s = (*t)->size;
+ (*t)++;
+ client_t *c = make_client();
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("className", *t, json)) {
(*t)++;
snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
} else if (keyeq("instanceName", *t, json)) {
(*t)++;
snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
- RESTORE_ANY(c, state, state, parse_client_state)
- RESTORE_ANY(c, lastState, last_state, parse_client_state)
- RESTORE_ANY(c, layer, layer, parse_stack_layer)
- RESTORE_ANY(c, lastLayer, last_layer, parse_stack_layer)
- RESTORE_UINT(c, borderWidth, border_width)
- RESTORE_BOOL(c, locked, locked)
- RESTORE_BOOL(c, sticky, sticky)
- RESTORE_BOOL(c, urgent, urgent)
- RESTORE_BOOL(c, private, private)
- RESTORE_BOOL(c, icccmFocus, icccm_focus)
- RESTORE_BOOL(c, icccmInput, icccm_input)
- RESTORE_USINT(c, minWidth, min_width)
- RESTORE_USINT(c, maxWidth, max_width)
- RESTORE_USINT(c, minHeight, min_height)
- RESTORE_USINT(c, maxHeight, max_height)
- RESTORE_INT(c, numStates, num_states)
+ RESTORE_ANY(state, &c->state, parse_client_state)
+ RESTORE_ANY(lastState, &c->last_state, parse_client_state)
+ RESTORE_ANY(layer, &c->layer, parse_stack_layer)
+ RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
+ RESTORE_UINT(borderWidth, &c->border_width)
+ RESTORE_BOOL(urgent, &c->urgent)
+ RESTORE_BOOL(icccmFocus, &c->icccm_focus)
+ RESTORE_BOOL(icccmInput, &c->icccm_input)
+ RESTORE_USINT(minWidth, &c->min_width)
+ RESTORE_USINT(maxWidth, &c->max_width)
+ RESTORE_USINT(minHeight, &c->min_height)
+ RESTORE_USINT(maxHeight, &c->max_height)
+ RESTORE_INT(wmStatesCount, &c->wm_states_count)
} else if (keyeq("wmState", *t, json)) {
(*t)++;
restore_wm_state(c->wm_state, t, json);
(*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)++;
}
}
+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;
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;
-}
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
*/
#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "bspwm.h"
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;
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;
}
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 {
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);
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);
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) {
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);
}
}
-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);
}
}
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
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
#include <unistd.h>
+#include <sys/types.h>
#include "bspwm.h"
#include "settings.h"
void run_config(void)
{
if (fork() == 0) {
- if (dpy != NULL)
+ if (dpy != NULL) {
close(xcb_get_file_descriptor(dpy));
+ }
setsid();
execl(config_path, config_path, NULL);
err("Couldn't execute the configuration file.\n");
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;
#define EXTERNAL_RULES_COMMAND ""
#define STATUS_PREFIX "W"
-#define FOCUSED_BORDER_COLOR "#7E7F89"
-#define ACTIVE_BORDER_COLOR "#545350"
-#define NORMAL_BORDER_COLOR "#3F3E3B"
-#define PRESEL_BORDER_COLOR "#E8E8F4"
-#define FOCUSED_LOCKED_BORDER_COLOR "#C7B579"
-#define ACTIVE_LOCKED_BORDER_COLOR "#545350"
-#define NORMAL_LOCKED_BORDER_COLOR "#3F3E3B"
-#define FOCUSED_STICKY_BORDER_COLOR "#E3A5DA"
-#define ACTIVE_STICKY_BORDER_COLOR "#545350"
-#define NORMAL_STICKY_BORDER_COLOR "#3F3E3B"
-#define FOCUSED_PRIVATE_BORDER_COLOR "#42CAD9"
-#define ACTIVE_PRIVATE_BORDER_COLOR "#5C5955"
-#define NORMAL_PRIVATE_BORDER_COLOR "#34322E"
-#define URGENT_BORDER_COLOR "#EFA29A"
+#define NORMAL_BORDER_COLOR "#30302f"
+#define ACTIVE_BORDER_COLOR "#474645"
+#define FOCUSED_BORDER_COLOR "#817f7f"
+#define PRESEL_FEEDBACK_COLOR "#f4d775"
-#define SPLIT_RATIO 0.5
-#define WINDOW_GAP 6
-#define BORDER_WIDTH 1
+#define WINDOW_GAP 6
+#define BORDER_WIDTH 1
+#define SPLIT_RATIO 0.5
#define HISTORY_AWARE_FOCUS false
#define FOCUS_BY_DISTANCE false
#define BORDERLESS_MONOCLE false
#define GAPLESS_MONOCLE false
-#define LEAF_MONOCLE false
+#define SINGLE_MONOCLE false
#define FOCUS_FOLLOWS_POINTER false
#define POINTER_FOLLOWS_FOCUS false
#define POINTER_FOLLOWS_MONITOR false
char external_rules_command[MAXLEN];
char status_prefix[MAXLEN];
-char focused_border_color[MAXLEN];
-char active_border_color[MAXLEN];
char normal_border_color[MAXLEN];
-char presel_border_color[MAXLEN];
-char focused_locked_border_color[MAXLEN];
-char active_locked_border_color[MAXLEN];
-char normal_locked_border_color[MAXLEN];
-char focused_sticky_border_color[MAXLEN];
-char active_sticky_border_color[MAXLEN];
-char normal_sticky_border_color[MAXLEN];
-char focused_private_border_color[MAXLEN];
-char active_private_border_color[MAXLEN];
-char normal_private_border_color[MAXLEN];
-char urgent_border_color[MAXLEN];
+char active_border_color[MAXLEN];
+char focused_border_color[MAXLEN];
+char presel_feedback_color[MAXLEN];
-double split_ratio;
int window_gap;
unsigned int border_width;
+double split_ratio;
+
child_polarity_t initial_polarity;
bool borderless_monocle;
bool gapless_monocle;
-bool leaf_monocle;
+bool single_monocle;
bool focus_follows_pointer;
bool pointer_follows_focus;
bool pointer_follows_monitor;
#include <stdlib.h>
#include "bspwm.h"
#include "window.h"
+#include "subscribe.h"
+#include "ewmh.h"
+#include "tree.h"
#include "stack.h"
stacking_list_t *make_stack(node_t *n)
}
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;
+ }
}
}
}
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;
+ }
}
}
}
return s;
}
-void stack(node_t *n, bool focused)
+void stack(desktop_t *d, node_t *n, bool focused)
{
- if (IS_FLOATING(n->client) && !auto_raise) {
- return;
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (IS_FLOATING(f->client) && !auto_raise) {
+ continue;
+ }
+
+ if (stack_head == NULL) {
+ stack_insert_after(NULL, f);
+ } else {
+ stacking_list_t *s = (focused ? limit_above(f) : limit_below(f));
+ if (s == NULL) {
+ continue;
+ }
+ int i = stack_cmp(f->client, s->node->client);
+ if (i < 0 || (i == 0 && !focused)) {
+ stack_insert_before(s, f);
+ window_below(f->id, s->node->id);
+ put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%X below 0x%X\n", f->id, s->node->id);
+ } else {
+ stack_insert_after(s, f);
+ window_above(f->id, s->node->id);
+ put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%X above 0x%X\n", f->id, s->node->id);
+ }
+ }
}
- if (stack_head == NULL) {
- stack_insert_after(NULL, n);
+ ewmh_update_client_list(true);
+ restack_presel_feedback(d);
+}
+
+void restack_presel_feedback(desktop_t *d)
+{
+ stacking_list_t *s = stack_tail;
+ while (s != NULL && !IS_TILED(s->node->client)) {
+ s = s->prev;
+ }
+ if (s != NULL) {
+ restack_presel_feedback_in(d->root, s->node);
+ }
+}
+
+void restack_presel_feedback_in(node_t *r, node_t *n)
+{
+ if (r == NULL) {
+ return;
} else {
- stacking_list_t *s = (focused ? limit_above(n) : limit_below(n));
- if (s == NULL) {
- return;
- }
- int i = stack_cmp(n->client, s->node->client);
- if (i < 0 || (i == 0 && !focused)) {
- stack_insert_before(s, n);
- window_below(n->client->window, s->node->client->window);
- } else {
- stack_insert_after(s, n);
- window_above(n->client->window, s->node->client->window);
+ if (r->presel != NULL) {
+ window_above(r->presel->feedback, n->id);
}
+ restack_presel_feedback_in(r->first_child, n);
+ restack_presel_feedback_in(r->second_child, n);
}
}
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
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <stdbool.h>
#include <ctype.h>
#include <stdarg.h>
#include "bspwm.h"
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);
}
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);
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);
--- /dev/null
+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
--- /dev/null
+- Install *jshon*.
+- Run `make` once.
+- Run `./run`.
--- /dev/null
+#! /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"
--- /dev/null
+#! /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"
--- /dev/null
+#! /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"
--- /dev/null
+#! /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"
--- /dev/null
+#! /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"
--- /dev/null
+#! /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"
--- /dev/null
+#! /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"
--- /dev/null
+#! /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
+}
--- /dev/null
+#! /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."
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_icccm.h>
+
+#define TEST_WINDOW_IC "test\0Test"
+
+bool get_atom(xcb_connection_t *dpy, char *name, xcb_atom_t *atom)
+{
+ bool ret = true;
+ xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
+ if (reply != NULL) {
+ *atom = reply->atom;
+ } else {
+ ret = false;
+ }
+ free(reply);
+ return ret;
+}
+
+void check_request(xcb_connection_t *dpy, xcb_void_cookie_t cookie, char *msg)
+{
+ xcb_generic_error_t *err = xcb_request_check(dpy, cookie);
+ if (err != NULL) {
+ fprintf(stderr, "%s: error code: %u.\n", msg, err->error_code);
+ xcb_disconnect(dpy);
+ exit(-1);
+ }
+}
+
+xcb_gc_t get_font_gc(xcb_connection_t *dpy, xcb_window_t win, const char *font_name)
+{
+ xcb_void_cookie_t ck;
+ xcb_font_t font = xcb_generate_id(dpy);
+ ck = xcb_open_font_checked(dpy, font, strlen(font_name), font_name);
+ check_request(dpy, ck, "Can't open font");
+ xcb_gcontext_t gc = xcb_generate_id(dpy);
+ uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
+ uint32_t values[] = {0xffcccccc, 0xff111111, font};
+ xcb_create_gc(dpy, gc, win, mask, values);
+ xcb_close_font(dpy, font);
+ return gc;
+}
+
+void render_text(xcb_connection_t *dpy, xcb_window_t win, int16_t x, int16_t y)
+{
+ char id[8];
+ xcb_void_cookie_t ck;
+ snprintf(id, sizeof(id), "%07x", win);
+ xcb_gcontext_t gc = get_font_gc(dpy, win, "-*-fixed-medium-*-*-*-18-*-*-*-*-*-*-*");
+ /* Don't work with the _checked ! */
+ ck = xcb_image_text_8_checked(dpy, strlen(id), win, gc, x, y, id);
+ check_request(dpy, ck, "Can't draw text");
+ xcb_free_gc(dpy, gc);
+}
+
+
+int main(void)
+{
+ xcb_connection_t *dpy = xcb_connect(NULL, NULL);
+ if (dpy == NULL) {
+ fprintf(stderr, "Can't connect to X.\n");
+ return EXIT_FAILURE;
+ }
+ xcb_atom_t WM_PROTOCOLS, WM_DELETE_WINDOW;
+ if (!get_atom(dpy, "WM_PROTOCOLS", &WM_PROTOCOLS) ||
+ !get_atom(dpy, "WM_DELETE_WINDOW", &WM_DELETE_WINDOW)) {
+ fprintf(stderr, "Can't get required atoms.\n");
+ xcb_disconnect(dpy);
+ return EXIT_FAILURE;
+ }
+ xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
+ if (screen == NULL) {
+ fprintf(stderr, "Can't get current screen.\n");
+ xcb_disconnect(dpy);
+ return EXIT_FAILURE;
+ }
+ xcb_window_t win = xcb_generate_id(dpy);
+ uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+ uint32_t values[] = {0xff111111, XCB_EVENT_MASK_EXPOSURE};
+ xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, screen->root, 0, 0, 320, 240, 2,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
+ xcb_icccm_set_wm_class(dpy, win, sizeof(TEST_WINDOW_IC), TEST_WINDOW_IC);
+ xcb_map_window(dpy, win);
+ xcb_flush(dpy);
+ xcb_generic_event_t *evt;
+ bool running = true;
+ while (running && (evt = xcb_wait_for_event(dpy)) != NULL) {
+ uint8_t rt = XCB_EVENT_RESPONSE_TYPE(evt);
+ if (rt == XCB_CLIENT_MESSAGE) {
+ xcb_client_message_event_t *cme = (xcb_client_message_event_t *) evt;
+ if (cme->type == WM_PROTOCOLS && cme->data.data32[0] == WM_DELETE_WINDOW) {
+ running = false;
+ }
+ } else if (rt == XCB_EXPOSE) {
+ render_text(dpy, win, 12, 24);
+ }
+ free(evt);
+ }
+ xcb_destroy_window(dpy, win);
+ xcb_disconnect(dpy);
+ return EXIT_SUCCESS;
+}
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
#include <float.h>
#include <limits.h>
#include "bspwm.h"
void arrange(monitor_t *m, desktop_t *d)
{
- if (d->root == NULL)
+ if (d->root == NULL) {
return;
+ }
layout_t set_layout = d->layout;
- if (leaf_monocle && tiled_count(d) == 1) {
+
+ if (single_monocle && tiled_count(d->root) == 1) {
d->layout = LAYOUT_MONOCLE;
}
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;
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;
r = m->rectangle;
}
- window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
- window_border_width(n->client->window, bw);
- window_draw_border(n, d->focus == n, m == mon);
+ window_move_resize(n->id, r.x, r.y, r.width, r.height);
+ window_border_width(n->id, bw);
if (frozen_pointer->action == ACTION_NONE) {
- put_status(SBSC_MASK_WINDOW_GEOMETRY, "window_geometry %s %s 0x%X %ux%u+%i+%i\n", m->name, d->name, n->client->window, r.width, r.height, r.x, r.y);
- }
-
- if (pointer_follows_focus && mon->desk->focus == n && frozen_pointer->action == ACTION_NONE) {
- center_pointer(r);
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry %s %s 0x%X %ux%u+%i+%i\n", m->name, d->name, n->id, r.width, r.height, r.x, r.y);
}
-
} else {
xcb_rectangle_t first_rect;
xcb_rectangle_t second_rect;
}
}
-void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+presel_t *make_presel(void)
+{
+ presel_t *p = malloc(sizeof(presel_t));
+ p->split_dir = DIR_EAST;
+ p->split_ratio = split_ratio;
+ p->feedback = XCB_NONE;
+ return p;
+}
+
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)
+{
+ if (n->presel == NULL) {
+ n->presel = make_presel();
+ }
+
+ n->presel->split_dir = dir;
+
+ put_status(SBSC_MASK_NODE_PRESEL, "node_presel %s %s 0x%X dir %s\n", m->name, d->name, n->id, SPLIT_DIR_STR(dir));
+}
+
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)
+{
+ if (n->presel == NULL) {
+ n->presel = make_presel();
+ }
+
+ n->presel->split_ratio = ratio;
+
+ put_status(SBSC_MASK_NODE_PRESEL, "node_presel %s %s 0x%X ratio %lf\n", m->name, d->name, n->id, ratio);
+}
+
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n)
{
- if (d == NULL || n == NULL)
+ if (n->presel == NULL) {
return;
+ }
+
+ if (n->presel->feedback != XCB_NONE) {
+ xcb_destroy_window(dpy, n->presel->feedback);
+ }
+
+ free(n->presel);
+ n->presel = NULL;
- /* n: new leaf node */
- /* c: new container node */
+ put_status(SBSC_MASK_NODE_PRESEL, "node_presel %s %s 0x%X cancel\n", m->name, d->name, n->id);
+}
+
+node_t *find_public(desktop_t *d)
+{
+ unsigned int b_manual_area = 0;
+ unsigned int b_automatic_area = 0;
+ node_t *b_manual = NULL;
+ node_t *b_automatic = NULL;
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->vacant) {
+ continue;
+ }
+ unsigned int n_area = node_area(NULL, d, n);
+ if (n_area > b_manual_area && (n->presel != NULL || !n->private)) {
+ b_manual = n;
+ b_manual_area = n_area;
+ }
+ if (n_area > b_automatic_area &&
+ n->presel == NULL && !n->private && private_count(n->parent) == 0) {
+ b_automatic = n;
+ b_automatic_area = n_area;
+ }
+ }
+ if (b_automatic != NULL) {
+ return b_automatic;
+ } else {
+ return b_manual;
+ }
+}
+
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+{
+ if (d == NULL || n == NULL) {
+ return NULL;
+ }
+
+ /* n: inserted node */
+ /* c: new internal node */
/* f: focus or insertion anchor */
/* p: parent of focus */
/* g: grand parent of focus */
- if (f == NULL)
+ if (f == NULL) {
f = d->root;
+ }
if (f == NULL) {
d->root = n;
} else {
- node_t *c = make_node();
+ node_t *c = make_node(XCB_NONE);
node_t *p = f->parent;
- if ((f->client->private ||
- (p != NULL && p->privacy_level > 0)) &&
- f->split_mode == MODE_AUTOMATIC) {
- node_t *closest = NULL;
- node_t *public = NULL;
- closest_public(d, f, &closest, &public);
- if (public != NULL) {
- f = public;
+ if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+ node_t *k = find_public(d);
+ if (k != NULL) {
+ f = k;
p = f->parent;
- } else {
- if (closest != NULL) {
- f = closest;
- p = f->parent;
- }
- f->split_mode = MODE_MANUAL;
- xcb_rectangle_t rect = f->client->tiled_rectangle;
- f->split_dir = (rect.width >= rect.height ? DIR_LEFT : DIR_UP);
- if (f->client->private) {
- get_opposite(f->split_dir, &f->split_dir);
- update_privacy_level(f, false);
- }
+ }
+ if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+ xcb_rectangle_t rect = get_rectangle(m, d, f);
+ presel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));
}
}
- if (p != NULL && f->split_mode == MODE_AUTOMATIC &&
- (p->first_child->vacant || p->second_child->vacant)) {
+ if (f->presel == NULL && p != NULL && p->vacant) {
f = p;
p = f->parent;
}
n->parent = c;
c->birth_rotation = f->birth_rotation;
- switch (f->split_mode) {
- case MODE_AUTOMATIC:
- if (p == NULL) {
- if (initial_polarity == FIRST_CHILD) {
- c->first_child = n;
- c->second_child = f;
+ if (f->presel == NULL) {
+ if (p == NULL) {
+ if (initial_polarity == FIRST_CHILD) {
+ c->first_child = n;
+ c->second_child = f;
+ } else {
+ c->first_child = f;
+ c->second_child = n;
+ }
+ if (m->rectangle.width > m->rectangle.height) {
+ c->split_type = TYPE_VERTICAL;
+ } else {
+ c->split_type = TYPE_HORIZONTAL;
+ }
+ f->parent = c;
+ d->root = c;
+ } else {
+ node_t *g = p->parent;
+ c->parent = g;
+ if (g != NULL) {
+ if (is_first_child(p)) {
+ g->first_child = c;
} else {
- c->first_child = f;
- c->second_child = n;
+ g->second_child = c;
}
- if (m->rectangle.width > m->rectangle.height)
- c->split_type = TYPE_VERTICAL;
- else
- c->split_type = TYPE_HORIZONTAL;
- f->parent = c;
+ } else {
d->root = c;
+ }
+ c->split_type = p->split_type;
+ c->split_ratio = p->split_ratio;
+ p->parent = c;
+ int rot;
+ if (is_first_child(f)) {
+ c->first_child = n;
+ c->second_child = p;
+ rot = 90;
} else {
- node_t *g = p->parent;
- c->parent = g;
- if (g != NULL) {
- if (is_first_child(p))
- g->first_child = c;
- else
- g->second_child = c;
- } else {
- d->root = c;
- }
- c->split_type = p->split_type;
- c->split_ratio = p->split_ratio;
- p->parent = c;
- int rot;
- if (is_first_child(f)) {
- c->first_child = n;
- c->second_child = p;
- rot = 90;
- } else {
- c->first_child = p;
- c->second_child = n;
- rot = 270;
- }
- if (IS_TILED(n->client)) {
- rotate_tree(p, rot);
- }
- n->birth_rotation = rot;
+ c->first_child = p;
+ c->second_child = n;
+ rot = 270;
}
- break;
- case MODE_MANUAL:
- if (p != NULL) {
- if (is_first_child(f))
- p->first_child = c;
- else
- p->second_child = c;
+ n->birth_rotation = rot;
+ if (!n->vacant) {
+ rotate_tree(p, rot);
}
- c->split_ratio = f->split_ratio;
- c->parent = p;
- f->parent = c;
- f->birth_rotation = 0;
- switch (f->split_dir) {
- case DIR_LEFT:
- c->split_type = TYPE_VERTICAL;
- c->first_child = n;
- c->second_child = f;
- break;
- case DIR_RIGHT:
- c->split_type = TYPE_VERTICAL;
- c->first_child = f;
- c->second_child = n;
- break;
- case DIR_UP:
- c->split_type = TYPE_HORIZONTAL;
- c->first_child = n;
- c->second_child = f;
- break;
- case DIR_DOWN:
- c->split_type = TYPE_HORIZONTAL;
- c->first_child = f;
- c->second_child = n;
- break;
+ }
+ } else {
+ if (p != NULL) {
+ if (is_first_child(f)) {
+ p->first_child = c;
+ } else {
+ p->second_child = c;
}
- if (d->root == f)
- d->root = c;
- f->split_mode = MODE_AUTOMATIC;
- break;
+ }
+ c->split_ratio = f->presel->split_ratio;
+ c->parent = p;
+ f->parent = c;
+ f->birth_rotation = 0;
+ switch (f->presel->split_dir) {
+ case DIR_WEST:
+ c->split_type = TYPE_VERTICAL;
+ c->first_child = n;
+ c->second_child = f;
+ break;
+ case DIR_EAST:
+ c->split_type = TYPE_VERTICAL;
+ c->first_child = f;
+ c->second_child = n;
+ break;
+ case DIR_NORTH:
+ c->split_type = TYPE_HORIZONTAL;
+ c->first_child = n;
+ c->second_child = f;
+ break;
+ case DIR_SOUTH:
+ c->split_type = TYPE_HORIZONTAL;
+ c->first_child = f;
+ c->second_child = n;
+ break;
+ }
+ if (d->root == f) {
+ d->root = c;
+ }
+ cancel_presel(m, d, f);
}
if (f->vacant) {
- propagate_vacant_state(f);
- }
- if (f->client != NULL) {
- if (f->client->private) {
- update_privacy_level(f, true);
- }
+ propagate_vacant_state(m, d, f);
}
}
- if (n->client->private) {
- update_privacy_level(n, true);
- }
+
if (d->focus == NULL) {
d->focus = n;
}
- if (n->client->sticky) {
- m->num_sticky++;
- }
- put_status(SBSC_MASK_REPORT);
+
+ return f;
}
void activate_node(monitor_t *m, desktop_t *d, node_t *n)
{
+ if (n == NULL && d->root != NULL) {
+ n = history_last_node(d, NULL);
+ if (n == NULL) {
+ n = first_extrema(d->root);
+ }
+ }
+
if (n != NULL) {
- if (d->focus != NULL && n != d->focus && stack_cmp(n->client, d->focus->client) < 0) {
- neutralize_obscuring_windows(m, d, n);
+ if (d->focus != NULL && n != d->focus) {
+ neutralize_occluding_windows(m, d, n);
}
- stack(n, true);
+ stack(d, n, true);
if (d->focus != n) {
- window_draw_border(d->focus, false, m == mon);
- window_draw_border(n, true, m == mon);
+ for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+ if (!is_descendant(f, n)) {
+ window_draw_border(f->id, get_border_color(false, (m == mon)));
+ }
+ }
}
+ draw_border(n, true, (m == mon));
}
+
d->focus = n;
- put_status(SBSC_MASK_WINDOW_ACTIVATE, "window_activate %s %s 0x%X\n", m->name, d->name, n!=NULL?n->client->window:0);
+
+ put_status(SBSC_MASK_NODE_ACTIVATE, "node_activate %s %s 0x%X\n", m->name, d->name, n!=NULL?n->id:0);
+ put_status(SBSC_MASK_REPORT);
+}
+
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else if (n->sticky) {
+ transfer_node(m, ds, n, m, dd, dd->focus);
+ } else {
+ /* we need references to the children because n might be freed after
+ * the first recursive call */
+ node_t *first_child = n->first_child;
+ node_t *second_child = n->second_child;
+ transfer_sticky_nodes(m, ds, dd, first_child);
+ transfer_sticky_nodes(m, ds, dd, second_child);
+ }
}
void focus_node(monitor_t *m, desktop_t *d, node_t *n)
{
- if (mon->desk != d || n == NULL)
+ if (n == NULL && d->root != NULL) {
+ n = history_last_node(d, NULL);
+ if (n == NULL) {
+ n = first_extrema(d->root);
+ }
+ }
+
+ if (mon->desk != d || n == NULL || n->client == NULL) {
clear_input_focus();
+ }
- if (m->num_sticky > 0 && d != m->desk) {
- node_t *a = first_extrema(m->desk->root);
+ if (m->sticky_count > 0 && d != m->desk) {
sticky_still = false;
- while (a != NULL) {
- node_t *b = next_leaf(a, m->desk->root);
- if (a->client->sticky)
- transfer_node(m, m->desk, a, m, d, d->focus);
- a = b;
- }
+ transfer_sticky_nodes(m, m->desk, d, m->desk->root);
sticky_still = true;
- if (n == NULL && d->focus != NULL)
+ if (n == NULL && d->focus != NULL) {
n = d->focus;
+ }
}
- if (n != NULL) {
- if (n->client->urgent) {
- n->client->urgent = false;
- put_status(SBSC_MASK_REPORT);
- }
- if (d->focus != NULL && n != d->focus && stack_cmp(n->client, d->focus->client) < 0) {
- neutralize_obscuring_windows(m, d, n);
- }
+ if (d->focus != NULL && n != d->focus) {
+ neutralize_occluding_windows(m, d, n);
+ }
+
+ if (n != NULL && n->client != NULL && n->client->urgent) {
+ set_urgent(m, d, n, false);
}
if (mon != m) {
- for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next) {
- window_draw_border(cd->focus, true, false);
+ for (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {
+ draw_border(e->focus, true, false);
}
- for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next) {
- if (cd != d) {
- window_draw_border(cd->focus, true, true);
+ for (desktop_t *e = m->desk_head; e != NULL; e = e->next) {
+ if (e == d) {
+ continue;
}
- }
- if (d->focus == n) {
- window_draw_border(n, true, true);
+ draw_border(e->focus, true, true);
}
}
if (d->focus != n) {
- window_draw_border(d->focus, false, true);
- window_draw_border(n, true, true);
+ for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+ if (!is_descendant(f, n)) {
+ window_draw_border(f->id, get_border_color(false, true));
+ }
+ }
}
+ draw_border(n, true, true);
+
focus_desktop(m, d);
d->focus = n;
+ ewmh_update_active_window();
+ history_add(m, d, n);
+
+ put_status(SBSC_MASK_REPORT);
if (n == NULL) {
- history_add(m, d, NULL);
- ewmh_update_active_window();
return;
- } else {
- stack(n, true);
}
- put_status(SBSC_MASK_WINDOW_FOCUS, "window_focus %s %s 0x%X\n", m->name, d->name, n->client->window);
+ put_status(SBSC_MASK_NODE_FOCUS, "node_focus %s %s 0x%X\n", m->name, d->name, n->id);
- history_add(m, d, n);
+ stack(d, n, true);
set_input_focus(n);
if (focus_follows_pointer) {
xcb_window_t win = XCB_NONE;
query_pointer(&win, NULL);
- if (win != n->client->window) {
+ if (win != n->id) {
enable_motion_recorder();
} else {
disable_motion_recorder();
}
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)) {
}
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);
}
c->icccm_input = hints.input;
}
}
- return c;
}
bool is_leaf(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)
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;
}
{
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;
}
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);
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;
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);
}
}
}
-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) {
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;
}
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};
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;
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++;
}
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;
}
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);
void unrotate_tree(node_t *n, int rot)
{
- if (rot == 0)
+ if (rot == 0) {
return;
+ }
rotate_tree(n, 360 - rot);
}
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;
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;
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;
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;
bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
{
- if (ns == NULL || ns == nd || (sticky_still && ns->client->sticky)) {
+ if (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {
+ return false;
+ }
+
+ unsigned int sc = 0;
+
+ if (sticky_still && ms->sticky_count > 0 && ds != dd && (sc = sticky_count(ns)) > 0) {
return false;
}
- put_status(SBSC_MASK_WINDOW_TRANSFER, "window_transfer %s %s 0x%X %s %s 0x%X\n", ms->name, ds->name, ns->client->window, md->name, dd->name, nd!=NULL?nd->client->window:0);
+ put_status(SBSC_MASK_NODE_TRANSFER, "node_transfer %s %s 0x%X %s %s 0x%X\n", ms->name, ds->name, ns->id, md->name, dd->name, nd!=NULL?nd->id:0);
- bool focused = (ns == mon->desk->focus);
- bool active = (ns == ds->focus);
+ bool held_focus = is_descendant(ds->focus, ns);
+ /* avoid ending up with a dangling pointer (because of unlink_node) */
+ node_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;
- if (focused) {
+ if (held_focus && ds == mon->desk) {
clear_input_focus();
}
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));
}
}
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;
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;
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
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);
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);
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
#include "helpers.h"
#define MISSING_VALUE "N/A"
-#define MAX_STATE 4
+#define MAX_WM_STATES 4
typedef enum {
TYPE_HORIZONTAL,
} 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 {
} 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;
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;
} 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;
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;
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;
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;
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;
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include "bspwm.h"
#include "ewmh.h"
#include "settings.h"
#include "stack.h"
#include "tree.h"
-#include "subscribe.h"
#include "parse.h"
#include "window.h"
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);
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);
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;
} 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 */
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);
}
{
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) {
}
}
-void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
+void initialize_presel_feedback(node_t *n)
{
- if (n == NULL || n->client->border_width < 1) {
+ if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
return;
}
- xcb_window_t win = n->client->window;
- uint32_t border_color_pxl = get_border_color(n->client, focused_window, focused_monitor);
+ xcb_window_t win = xcb_generate_id(dpy);
+ xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL, (uint32_t []) {get_color_pixel(presel_feedback_color)});
- if (n->split_mode == MODE_AUTOMATIC) {
- xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
- } else {
- unsigned int border_width = n->client->border_width;
- uint32_t presel_border_color_pxl;
- get_color(presel_border_color, win, &presel_border_color_pxl);
-
- xcb_rectangle_t actual_rectangle = get_rectangle(NULL, n->client);
-
- uint16_t width = actual_rectangle.width;
- uint16_t height = actual_rectangle.height;
-
- uint16_t full_width = width + 2 * border_width;
- uint16_t full_height = height + 2 * border_width;
-
- xcb_rectangle_t border_rectangles[] =
- {
- { width, 0, 2 * border_width, height + 2 * border_width },
- { 0, height, width + 2 * border_width, 2 * border_width }
- };
-
- xcb_rectangle_t *presel_rectangles;
-
- uint8_t win_depth = root_depth;
- xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);
- if (geo != NULL)
- win_depth = geo->depth;
- free(geo);
-
- xcb_pixmap_t pixmap = xcb_generate_id(dpy);
- xcb_create_pixmap(dpy, win_depth, pixmap, win, full_width, full_height);
-
- xcb_gcontext_t gc = xcb_generate_id(dpy);
- xcb_create_gc(dpy, gc, pixmap, 0, NULL);
-
- xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &border_color_pxl);
- xcb_poly_fill_rectangle(dpy, pixmap, gc, LENGTH(border_rectangles), border_rectangles);
-
- uint16_t fence = (int16_t) (n->split_ratio * ((n->split_dir == DIR_UP || n->split_dir == DIR_DOWN) ? height : width));
- presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
- switch (n->split_dir) {
- case DIR_UP:
- presel_rectangles[0] = (xcb_rectangle_t) {width, 0, 2 * border_width, fence};
- presel_rectangles[1] = (xcb_rectangle_t) {0, height + border_width, full_width, border_width};
- break;
- case DIR_DOWN:
- presel_rectangles[0] = (xcb_rectangle_t) {width, fence + 1, 2 * border_width, height + border_width - (fence + 1)};
- presel_rectangles[1] = (xcb_rectangle_t) {0, height, full_width, border_width};
- break;
- case DIR_LEFT:
- presel_rectangles[0] = (xcb_rectangle_t) {0, height, fence, 2 * border_width};
- presel_rectangles[1] = (xcb_rectangle_t) {width + border_width, 0, border_width, full_height};
- break;
- case DIR_RIGHT:
- presel_rectangles[0] = (xcb_rectangle_t) {fence + 1, height, width + border_width - (fence + 1), 2 * border_width};
- presel_rectangles[1] = (xcb_rectangle_t) {width, 0, border_width, full_height};
- break;
- }
- xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
- xcb_poly_fill_rectangle(dpy, pixmap, gc, 2, presel_rectangles);
- xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXMAP, &pixmap);
- free(presel_rectangles);
- xcb_free_gc(dpy, gc);
- xcb_free_pixmap(dpy, pixmap);
+ xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
+ stacking_list_t *s = stack_tail;
+ while (s != NULL && !IS_TILED(s->node->client)) {
+ s = s->prev;
}
+ if (s != NULL) {
+ window_above(win, s->node->id);
+ }
+ n->presel->feedback = win;
}
-pointer_state_t *make_pointer_state(void)
-{
- pointer_state_t *p = malloc(sizeof(pointer_state_t));
- p->monitor = NULL;
- p->desktop = NULL;
- p->node = p->vertical_fence = p->horizontal_fence = NULL;
- p->client = NULL;
- p->window = XCB_NONE;
- p->action = ACTION_NONE;
- return p;
-}
-
-/* Returns true if a contains b */
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
{
- return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
- a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
-}
+ if (n == NULL || n->presel == NULL) {
+ return;
+ }
-xcb_rectangle_t get_rectangle(monitor_t *m, client_t *c)
-{
- switch (c->state) {
- case STATE_TILED:
- return c->tiled_rectangle;
- break;
- case STATE_PSEUDO_TILED:
- case STATE_FLOATING:
- return c->floating_rectangle;
- break;
- case STATE_FULLSCREEN:
- default:
- return m != NULL ? m->rectangle : (xcb_rectangle_t) {0, 0, screen_width, screen_height};
- break;
- }
-}
+ bool exists = (n->presel->feedback != XCB_NONE);
+ if (!exists) {
+ initialize_presel_feedback(n);
+ }
-void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt)
-{
- xcb_rectangle_t rect = get_rectangle(NULL, c);
+ presel_t *p = n->presel;
+ xcb_rectangle_t rect = n->rectangle;
+ rect.x = rect.y = 0;
+ rect.width -= d->window_gap;
+ rect.height -= d->window_gap;
+ xcb_rectangle_t presel_rect = rect;
- switch (dir) {
- case DIR_RIGHT:
- pt->x = rect.x + rect.width;
- pt->y = rect.y + (rect.height / 2);
+ switch (p->split_dir) {
+ case DIR_NORTH:
+ presel_rect.height = p->split_ratio * rect.height;
break;
- case DIR_DOWN:
- pt->x = rect.x + (rect.width / 2);
- pt->y = rect.y + rect.height;
+ case DIR_EAST:
+ presel_rect.width = (1 - p->split_ratio) * rect.width;
+ presel_rect.x = rect.width - presel_rect.width;
break;
- case DIR_LEFT:
- pt->x = rect.x;
- pt->y = rect.y + (rect.height / 2);
+ case DIR_SOUTH:
+ presel_rect.height = (1 - p->split_ratio) * rect.height;
+ presel_rect.y = rect.height - presel_rect.height;
break;
- case DIR_UP:
- pt->x = rect.x + (rect.width / 2);
- pt->y = rect.y;
+ case DIR_WEST:
+ presel_rect.width = p->split_ratio * rect.width;
break;
}
-}
-void adopt_orphans(void)
-{
- xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
- if (qtr == NULL)
- return;
+ window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
+ presel_rect.width, presel_rect.height);
- int len = xcb_query_tree_children_length(qtr);
- xcb_window_t *wins = xcb_query_tree_children(qtr);
- for (int i = 0; i < len; i++) {
- uint32_t idx;
- xcb_window_t win = wins[i];
- if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1)
- schedule_window(win);
+ if (!exists && m->desk == d) {
+ window_show(p->feedback);
}
-
- free(qtr);
-}
-
-void window_close(node_t *n)
-{
- if (n == NULL || n->client->locked)
- return;
-
- send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
}
-void window_kill(monitor_t *m, desktop_t *d, node_t *n)
+void refresh_presel_feebacks_in(node_t *n, desktop_t *d, monitor_t *m)
{
- if (n == NULL)
- return;
-
- xcb_window_t win = n->client->window;
-
- xcb_kill_client(dpy, win);
- remove_node(m, d, n);
-}
-
-void set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
-{
- if (n == NULL || n->client->layer == l) {
+ if (n == NULL) {
return;
+ } else {
+ if (n->presel != NULL) {
+ draw_presel_feedback(m, d, n);
+ }
+ refresh_presel_feebacks_in(n->first_child, d, m);
+ refresh_presel_feebacks_in(n->second_child, d, m);
}
-
- client_t *c = n->client;
-
- c->last_layer = c->layer;
- c->layer = l;
-
- put_status(SBSC_MASK_WINDOW_LAYER, "window_layer %s %s 0x%X %s\n", m->name, d->name, c->window, LAYER_STR(l));
-
- if (d->focus == n) {
- neutralize_obscuring_windows(m, d, n);
- }
-
- stack(n, (d->focus == n));
}
-void set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
+void update_colors(void)
{
- if (n == NULL || n->client->state == s) {
- return;
- }
-
- client_t *c = n->client;
-
- c->last_state = c->state;
- c->state = s;
-
- switch (c->last_state) {
- case STATE_TILED:
- case STATE_PSEUDO_TILED:
- break;
- case STATE_FLOATING:
- set_floating(m, d, n, false);
- break;
- case STATE_FULLSCREEN:
- set_fullscreen(m, d, n, false);
- break;
- }
-
- put_status(SBSC_MASK_WINDOW_STATE, "window_state %s %s 0x%X %s off\n", m->name, d->name, c->window, STATE_STR(c->last_state));
-
- switch (c->state) {
- case STATE_TILED:
- case STATE_PSEUDO_TILED:
- break;
- case STATE_FLOATING:
- set_floating(m, d, n, true);
- break;
- case STATE_FULLSCREEN:
- set_fullscreen(m, d, n, true);
- break;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ update_colors_in(d->root, d, m);
+ }
}
-
- put_status(SBSC_MASK_WINDOW_STATE, "window_state %s %s 0x%X %s on\n", m->name, d->name, c->window, STATE_STR(c->state));
}
-void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
{
if (n == NULL) {
return;
+ } else {
+ if (n->presel != NULL) {
+ uint32_t pxl = get_color_pixel(presel_feedback_color);
+ xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
+ if (d == m->desk) {
+ /* hack to induce back pixel refresh */
+ window_hide(n->presel->feedback);
+ window_show(n->presel->feedback);
+ }
+ }
+ if (n == d->focus) {
+ draw_border(n, true, (m == mon));
+ } else if (n->client != NULL) {
+ draw_border(n, false, (m == mon));
+ } else {
+ update_colors_in(n->first_child, d, m);
+ update_colors_in(n->second_child, d, m);
+ }
}
-
- n->split_mode = MODE_AUTOMATIC;
- set_vacant_state(n, value);
-
- if (!value && d->focus == n) {
- neutralize_obscuring_windows(m, d, n);
- }
-
- stack(n, (d->focus == n));
}
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void draw_border(node_t *n, bool focused_node, bool focused_monitor)
{
if (n == NULL) {
return;
}
- client_t *c = n->client;
-
- n->split_mode = MODE_AUTOMATIC;
- set_vacant_state(n, value);
-
- if (value) {
- ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_FULLSCREEN);
- c->last_layer = c->layer;
- c->layer = LAYER_ABOVE;
- } else {
- ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_FULLSCREEN);
- c->layer = c->last_layer;
- if (d->focus == n) {
- neutralize_obscuring_windows(m, d, n);
+ uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client->border_width > 0) {
+ window_draw_border(f->id, border_color_pxl);
}
}
+}
- stack(n, (d->focus == n));
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
+{
+ xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
}
-void neutralize_obscuring_windows(monitor_t *m, desktop_t *d, node_t *n)
+pointer_state_t *make_pointer_state(void)
{
- bool dirty = false;
- for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
- if (a != n) {
- if (IS_FULLSCREEN(a->client) && stack_cmp(n->client, a->client) < 0) {
- set_state(m, d, a, a->client->last_state);
- dirty = true;
- }
- }
- }
- if (dirty) {
- arrange(m, d);
- }
+ pointer_state_t *p = malloc(sizeof(pointer_state_t));
+ p->monitor = NULL;
+ p->desktop = NULL;
+ p->node = p->vertical_fence = p->horizontal_fence = NULL;
+ p->client = NULL;
+ p->window = XCB_NONE;
+ p->action = ACTION_NONE;
+ return p;
}
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void adopt_orphans(void)
{
- if (n == NULL || n->client->locked == value)
+ xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
+ if (qtr == NULL) {
return;
+ }
- client_t *c = n->client;
+ int len = xcb_query_tree_children_length(qtr);
+ xcb_window_t *wins = xcb_query_tree_children(qtr);
- put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X locked %s\n", m->name, d->name, c->window, ON_OFF_STR(value));
+ for (int i = 0; i < len; i++) {
+ uint32_t idx;
+ xcb_window_t win = wins[i];
+ if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
+ schedule_window(win);
+ }
+ }
- c->locked = value;
- window_draw_border(n, d->focus == n, m == mon);
+ free(qtr);
}
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void window_close(node_t *n)
{
- if (n == NULL || n->client->sticky == value)
+ if (n == NULL) {
return;
-
- client_t *c = n->client;
-
- put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X sticky %s\n", m->name, d->name, c->window, ON_OFF_STR(value));
-
- if (d != m->desk)
- transfer_node(m, d, n, m, m->desk, m->desk->focus);
-
- c->sticky = value;
- if (value) {
- ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_STICKY);
- m->num_sticky++;
+ } else if (n->client != NULL) {
+ send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
} else {
- ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_STICKY);
- m->num_sticky--;
+ window_close(n->first_child);
+ window_close(n->second_child);
}
-
- window_draw_border(n, d->focus == n, m == mon);
}
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
+void window_kill(monitor_t *m, desktop_t *d, node_t *n)
{
- if (n == NULL || n->client->private == value)
+ if (n == NULL) {
return;
+ }
- client_t *c = n->client;
-
- put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X private %s\n", m->name, d->name, c->window, ON_OFF_STR(value));
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ xcb_kill_client(dpy, f->id);
+ }
- c->private = value;
- update_privacy_level(n, value);
- window_draw_border(n, d->focus == n, m == mon);
+ remove_node(m, d, n);
}
-void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value)
+uint32_t get_border_color(bool focused_node, bool focused_monitor)
{
- if (value && mon->desk->focus == n)
- return;
- n->client->urgent = value;
- window_draw_border(n, d->focus == n, m == mon);
-
- put_status(SBSC_MASK_WINDOW_FLAG, "window_flag %s %s 0x%X urgent %s\n", m->name, d->name, n->client->window, ON_OFF_STR(value));
- put_status(SBSC_MASK_REPORT);
-}
-
-uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor)
-{
- if (c == NULL)
- return 0;
-
- uint32_t pxl = 0;
-
- if (focused_monitor && focused_window) {
- if (c->locked)
- get_color(focused_locked_border_color, c->window, &pxl);
- else if (c->sticky)
- get_color(focused_sticky_border_color, c->window, &pxl);
- else if (c->private)
- get_color(focused_private_border_color, c->window, &pxl);
- else
- get_color(focused_border_color, c->window, &pxl);
- } else if (focused_window) {
- if (c->urgent)
- get_color(urgent_border_color, c->window, &pxl);
- else if (c->locked)
- get_color(active_locked_border_color, c->window, &pxl);
- else if (c->sticky)
- get_color(active_sticky_border_color, c->window, &pxl);
- else if (c->private)
- get_color(active_private_border_color, c->window, &pxl);
- else
- get_color(active_border_color, c->window, &pxl);
+ if (focused_monitor && focused_node) {
+ return get_color_pixel(focused_border_color);
+ } else if (focused_node) {
+ return get_color_pixel(active_border_color);
} else {
- if (c->urgent)
- get_color(urgent_border_color, c->window, &pxl);
- else if (c->locked)
- get_color(normal_locked_border_color, c->window, &pxl);
- else if (c->sticky)
- get_color(normal_sticky_border_color, c->window, &pxl);
- else if (c->private)
- get_color(normal_private_border_color, c->window, &pxl);
- else
- get_color(normal_border_color, c->window, &pxl);
+ return get_color_pixel(normal_border_color);
}
-
- return pxl;
}
-void update_floating_rectangle(client_t *c)
+void update_floating_rectangle(node_t *n)
{
- xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), NULL);
+ client_t *c = n->client;
+
+ xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
- if (geo != NULL)
+ if (geo != NULL) {
c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
+ }
free(geo);
}
void restrain_floating_width(client_t *c, int *width)
{
- if (*width < 1)
+ if (*width < 1) {
*width = 1;
- if (c->min_width > 0 && *width < c->min_width)
+ }
+ if (c->min_width > 0 && *width < c->min_width) {
*width = c->min_width;
- else if (c->max_width > 0 && *width > c->max_width)
+ } else if (c->max_width > 0 && *width > c->max_width) {
*width = c->max_width;
+ }
}
void restrain_floating_height(client_t *c, int *height)
{
- if (*height < 1)
+ if (*height < 1) {
*height = 1;
- if (c->min_height > 0 && *height < c->min_height)
+ }
+ if (c->min_height > 0 && *height < c->min_height) {
*height = c->min_height;
- else if (c->max_height > 0 && *height > c->max_height)
+ } else if (c->max_height > 0 && *height > c->max_height) {
*height = c->max_height;
+ }
}
void restrain_floating_size(client_t *c, int *width, int *height)
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);
}
{
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);
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);
}
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);
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);
}
}
}
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);
}
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;
}
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);
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);