From 5823a2663210b0b9bd8b1842b8dfc3b11f1151cd Mon Sep 17 00:00:00 2001 From: unrevre Date: Wed, 9 Oct 2019 16:57:58 -0400 Subject: [PATCH 1/8] view: additional split mode - orthogonal to screen additional split mode, allowing new windows to be inserted above the selected window. --- src/message.c | 3 +++ src/misc/macros.h | 1 + src/view.c | 5 ++++- src/view.h | 2 ++ src/window_manager.c | 6 ++++++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/message.c b/src/message.c index 7245cd41..0e3fc7dc 100644 --- a/src/message.c +++ b/src/message.c @@ -130,6 +130,7 @@ extern bool g_verbose; #define ARGUMENT_WINDOW_DIR_EAST "east" #define ARGUMENT_WINDOW_DIR_SOUTH "south" #define ARGUMENT_WINDOW_DIR_WEST "west" +#define ARGUMENT_WINDOW_DIR_ABOVE "above" #define ARGUMENT_WINDOW_SEL_MOUSE "mouse" #define ARGUMENT_WINDOW_SEL_LARGEST "largest" #define ARGUMENT_WINDOW_SEL_SMALLEST "smallest" @@ -1233,6 +1234,8 @@ static struct selector parse_dir_selector(FILE *rsp, char **message) result.dir = DIR_SOUTH; } else if (token_equals(result.token, ARGUMENT_WINDOW_DIR_WEST)) { result.dir = DIR_WEST; + } else if (token_equals(result.token, ARGUMENT_WINDOW_DIR_ABOVE)) { + result.dir = DIR_ABOVE; } else { result.did_parse = false; daemon_fail(rsp, "value '%.*s' is not a valid option for DIR_SEL\n", result.token.length, result.token.text); diff --git a/src/misc/macros.h b/src/misc/macros.h index e5985f2d..e3d9cac6 100644 --- a/src/misc/macros.h +++ b/src/misc/macros.h @@ -16,6 +16,7 @@ #define DIR_EAST 90 #define DIR_SOUTH 180 #define DIR_WEST 270 +#define DIR_ABOVE 1 #define TYPE_ABS 0x1 #define TYPE_REL 0x2 diff --git a/src/view.c b/src/view.c index 27281e43..07bb1ced 100644 --- a/src/view.c +++ b/src/view.c @@ -44,7 +44,10 @@ static void area_make_pair(struct view *view, struct window_node *node) float ratio = window_node_get_ratio(node); float gap = window_node_get_gap(view); - if (split == SPLIT_Y) { + if (split == SPLIT_Z) { + node->left->area = node->area; + node->right->area = node->area; + } else if (split == SPLIT_Y) { node->left->area = node->area; node->left->area.w *= ratio; node->left->area.w -= gap; diff --git a/src/view.h b/src/view.h index 15de29d5..d74dd9a7 100644 --- a/src/view.h +++ b/src/view.h @@ -34,6 +34,7 @@ static const char *window_node_child_str[] = enum window_node_split { SPLIT_NONE, + SPLIT_Z, SPLIT_Y, SPLIT_X }; @@ -41,6 +42,7 @@ enum window_node_split static const char *window_node_split_str[] = { "none", + "normal", "vertical", "horizontal" }; diff --git a/src/window_manager.c b/src/window_manager.c index 807f5d48..db60e4d4 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -1134,6 +1134,12 @@ void window_manager_set_window_insertion(struct space_manager *sm, struct window window->border.insert_active = true; window->border.insert_dir = direction; view->insertion_point = node->window_id; + } else if (direction == DIR_ABOVE) { + node->split = SPLIT_Z; + node->child = CHILD_SECOND; + window->border.insert_active = true; + window->border.insert_dir = direction; + view->insertion_point = node->window_id; } border_window_refresh(window); From f608b7cd13f038bd578403bd6f4ed1ebb022c1b0 Mon Sep 17 00:00:00 2001 From: unrevre Date: Thu, 10 Oct 2019 13:07:04 -0400 Subject: [PATCH 2/8] view: inherit orthogonal split style by default subsequent splittings take on the same window size. --- src/view.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/view.c b/src/view.c index 07bb1ced..e49d4bef 100644 --- a/src/view.c +++ b/src/view.c @@ -24,6 +24,7 @@ static enum window_node_child window_node_get_child(struct window_node *node) static enum window_node_split window_node_get_split(struct window_node *node) { if (node->split != SPLIT_NONE) return node->split; + if (node->parent != NULL && node->parent->split == SPLIT_Z) return SPLIT_Z; return node->area.w / node->area.h >= 1.1618f ? SPLIT_Y : SPLIT_X; } From f2ee9faad730f13ac155ff904f056e0ef1b1a9ee Mon Sep 17 00:00:00 2001 From: unrevre Date: Thu, 10 Oct 2019 13:20:10 -0400 Subject: [PATCH 3/8] border: extend line directly to endpoint --- src/border.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/border.c b/src/border.c index 36948b6d..c2d1d43f 100644 --- a/src/border.c +++ b/src/border.c @@ -65,25 +65,25 @@ static CGMutablePathRef border_insert_shape(struct border *border, CGRect frame, CGPathMoveToPoint(insert, NULL, maxx, midy); CGPathAddArcToPoint(insert, NULL, maxx, maxy, midx, maxy, radius); CGPathAddArcToPoint(insert, NULL, minx, maxy, minx, midy, radius); - CGPathAddArcToPoint(insert, NULL, minx, midy, minx, miny, radius); + CGPathAddLineToPoint(insert, NULL, minx, midy); } break; case DIR_EAST: { CGPathMoveToPoint(insert, NULL, midx, miny); CGPathAddArcToPoint(insert, NULL, maxx, miny, maxx, midy, radius); CGPathAddArcToPoint(insert, NULL, maxx, maxy, midx, maxy, radius); - CGPathAddArcToPoint(insert, NULL, midx, maxy, maxx, maxy, radius); + CGPathAddLineToPoint(insert, NULL, midx, maxy); } break; case DIR_SOUTH: { CGPathMoveToPoint(insert, NULL, minx, midy); CGPathAddArcToPoint(insert, NULL, minx, miny, midx, miny, radius); CGPathAddArcToPoint(insert, NULL, maxx, miny, maxx, midy, radius); - CGPathAddArcToPoint(insert, NULL, maxx, midy, maxx, maxy, radius); + CGPathAddLineToPoint(insert, NULL, maxx, midy); } break; case DIR_WEST: { CGPathMoveToPoint(insert, NULL, midx, miny); CGPathAddArcToPoint(insert, NULL, minx, miny, minx, midy, radius); CGPathAddArcToPoint(insert, NULL, minx, maxy, midx, maxy, radius); - CGPathAddArcToPoint(insert, NULL, midx, maxy, midx, maxy, radius); + CGPathAddLineToPoint(insert, NULL, midx, maxy); } break; } From 08604e0e21849246d0d6795aa81564a1be6bcbbc Mon Sep 17 00:00:00 2001 From: unrevre Date: Thu, 10 Oct 2019 13:58:25 -0400 Subject: [PATCH 4/8] border: highlight border for '--insert above' --- src/border.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/border.c b/src/border.c index c2d1d43f..0bbc0545 100644 --- a/src/border.c +++ b/src/border.c @@ -85,6 +85,14 @@ static CGMutablePathRef border_insert_shape(struct border *border, CGRect frame, CGPathAddArcToPoint(insert, NULL, minx, maxy, midx, maxy, radius); CGPathAddLineToPoint(insert, NULL, midx, maxy); } break; + case DIR_ABOVE: { + CGPathMoveToPoint(insert, NULL, maxx, midy); + CGPathAddArcToPoint(insert, NULL, maxx, maxy, midx, maxy, radius); + CGPathAddArcToPoint(insert, NULL, minx, maxy, minx, midy, radius); + CGPathAddArcToPoint(insert, NULL, minx, miny, midx, miny, radius); + CGPathAddArcToPoint(insert, NULL, maxx, miny, maxx, midy, radius); + CGPathAddLineToPoint(insert, NULL, maxx, midy); + } break; } return insert; From e2644b4031e0f14b61068619c8a54937fddbfbd9 Mon Sep 17 00:00:00 2001 From: unrevre Date: Thu, 10 Oct 2019 14:22:57 -0400 Subject: [PATCH 5/8] space_manager: toggle amongst all split modes --- src/space_manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/space_manager.c b/src/space_manager.c index 3712df1e..1eeb11bf 100644 --- a/src/space_manager.c +++ b/src/space_manager.c @@ -412,7 +412,8 @@ void space_manager_toggle_window_split(struct space_manager *sm, struct window * struct window_node *node = view_find_window_node(view, window->id); if (node && window_node_is_intermediate(node)) { - node->parent->split = node->parent->split == SPLIT_Y ? SPLIT_X : SPLIT_Y; + enum window_node_split split = node->parent->split; + node->parent->split = split == SPLIT_Z ? SPLIT_Y : split == SPLIT_Y ? SPLIT_X : SPLIT_Z; if (g_space_manager.auto_balance) { window_node_equalize(view->root); From 4dce85f539df32a3ba635f283c6e9af5d05b5d53 Mon Sep 17 00:00:00 2001 From: unrevre Date: Sun, 13 Oct 2019 11:38:52 -0400 Subject: [PATCH 6/8] view: add other z direction 'below', in addition to 'above'. represents stacking order. --- src/border.c | 2 +- src/message.c | 3 +++ src/misc/macros.h | 1 + src/window_manager.c | 6 ++++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/border.c b/src/border.c index 0bbc0545..ec7c1f74 100644 --- a/src/border.c +++ b/src/border.c @@ -85,7 +85,7 @@ static CGMutablePathRef border_insert_shape(struct border *border, CGRect frame, CGPathAddArcToPoint(insert, NULL, minx, maxy, midx, maxy, radius); CGPathAddLineToPoint(insert, NULL, midx, maxy); } break; - case DIR_ABOVE: { + case DIR_ABOVE: case DIR_BELOW: { CGPathMoveToPoint(insert, NULL, maxx, midy); CGPathAddArcToPoint(insert, NULL, maxx, maxy, midx, maxy, radius); CGPathAddArcToPoint(insert, NULL, minx, maxy, minx, midy, radius); diff --git a/src/message.c b/src/message.c index 0e3fc7dc..0b8c88f7 100644 --- a/src/message.c +++ b/src/message.c @@ -131,6 +131,7 @@ extern bool g_verbose; #define ARGUMENT_WINDOW_DIR_SOUTH "south" #define ARGUMENT_WINDOW_DIR_WEST "west" #define ARGUMENT_WINDOW_DIR_ABOVE "above" +#define ARGUMENT_WINDOW_DIR_BELOW "below" #define ARGUMENT_WINDOW_SEL_MOUSE "mouse" #define ARGUMENT_WINDOW_SEL_LARGEST "largest" #define ARGUMENT_WINDOW_SEL_SMALLEST "smallest" @@ -1236,6 +1237,8 @@ static struct selector parse_dir_selector(FILE *rsp, char **message) result.dir = DIR_WEST; } else if (token_equals(result.token, ARGUMENT_WINDOW_DIR_ABOVE)) { result.dir = DIR_ABOVE; + } else if (token_equals(result.token, ARGUMENT_WINDOW_DIR_BELOW)) { + result.dir = DIR_BELOW; } else { result.did_parse = false; daemon_fail(rsp, "value '%.*s' is not a valid option for DIR_SEL\n", result.token.length, result.token.text); diff --git a/src/misc/macros.h b/src/misc/macros.h index e3d9cac6..7c8a70ef 100644 --- a/src/misc/macros.h +++ b/src/misc/macros.h @@ -17,6 +17,7 @@ #define DIR_SOUTH 180 #define DIR_WEST 270 #define DIR_ABOVE 1 +#define DIR_BELOW -1 #define TYPE_ABS 0x1 #define TYPE_REL 0x2 diff --git a/src/window_manager.c b/src/window_manager.c index db60e4d4..bfa43719 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -1140,6 +1140,12 @@ void window_manager_set_window_insertion(struct space_manager *sm, struct window window->border.insert_active = true; window->border.insert_dir = direction; view->insertion_point = node->window_id; + } else if (direction == DIR_BELOW) { + node->split = SPLIT_Z; + node->child = CHILD_FIRST; + window->border.insert_active = true; + window->border.insert_dir = direction; + view->insertion_point = node->window_id; } border_window_refresh(window); From 2462fd9e6f3b5d2264d536f7523cd1b4d6c15652 Mon Sep 17 00:00:00 2001 From: unrevre Date: Sun, 13 Oct 2019 15:12:23 -0400 Subject: [PATCH 7/8] window_manager: direction selectors along z axis require all nodes on paths to lowest common ancestor of current + prev/next nodes to be z-split nodes. --- src/message.c | 22 ++++++++++++++++++++++ src/view.c | 23 +++++++++++++++++++++++ src/window_manager.c | 36 ++++++++++++++++++++++++++++++++++++ src/window_manager.h | 2 ++ 4 files changed, 83 insertions(+) diff --git a/src/message.c b/src/message.c index 0b8c88f7..57c98f8f 100644 --- a/src/message.c +++ b/src/message.c @@ -1131,6 +1131,28 @@ static struct selector parse_window_selector(FILE *rsp, char **message, struct w } else { daemon_fail(rsp, "could not locate the selected window.\n"); } + } else if (token_equals(result.token, ARGUMENT_WINDOW_DIR_ABOVE)) { + if (acting_window) { + struct window *closest_window = window_manager_find_managed_window_above(&g_space_manager, &g_window_manager, acting_window); + if (closest_window) { + result.window = closest_window; + } else { + daemon_fail(rsp, "could not locate a managed window above.\n"); + } + } else { + daemon_fail(rsp, "could not locate the selected window.\n"); + } + } else if (token_equals(result.token, ARGUMENT_WINDOW_DIR_BELOW)) { + if (acting_window) { + struct window *closest_window = window_manager_find_managed_window_below(&g_space_manager, &g_window_manager, acting_window); + if (closest_window) { + result.window = closest_window; + } else { + daemon_fail(rsp, "could not locate a managed window below.\n"); + } + } else { + daemon_fail(rsp, "could not locate the selected window.\n"); + } } else if (token_equals(result.token, ARGUMENT_WINDOW_SEL_MOUSE)) { struct window *mouse_window = window_manager_find_window_below_cursor(&g_window_manager); if (mouse_window) { diff --git a/src/view.c b/src/view.c index e49d4bef..7738739b 100644 --- a/src/view.c +++ b/src/view.c @@ -271,6 +271,29 @@ struct window_node *window_node_find_next_leaf(struct window_node *node) return window_node_find_first_leaf(node->parent->right->left); } +struct window_node *window_node_find_common_ancestor(struct window_node *node, struct window_node *a, struct window_node *b) { + if (!node) return NULL; + if (node == a || node == b) return node; + + struct window_node *left = window_node_find_common_ancestor(node->left, a, b); + struct window_node *right = window_node_find_common_ancestor(node->right, a, b); + + if (left && right) return node; + + return left ? left : right; +} + +bool window_nodes_are_stacked(struct window_node *ancestor, struct window_node *node) { + do { + node = node->parent; + + if (node->split != SPLIT_Z) + return false; + } while (node != ancestor); + + return true; +} + void window_node_rotate(struct window_node *node, int degrees) { if ((degrees == 90 && node->split == SPLIT_Y) || diff --git a/src/window_manager.c b/src/window_manager.c index bfa43719..e289d0e3 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -707,6 +707,42 @@ struct window *window_manager_find_closest_window_in_direction(struct window_man return result; } +struct window *window_manager_find_managed_window_below(struct space_manager *sm, struct window_manager *wm, struct window *window) +{ + struct view *view = space_manager_find_view(sm, space_manager_active_space()); + if (!view) return NULL; + + struct window_node *node = view_find_window_node(view, window->id); + if (!node) return NULL; + + struct window_node *prev = window_node_find_prev_leaf(node); + if (!prev) return NULL; + + struct window_node *ancestor = window_node_find_common_ancestor(view->root, node, prev); + if (!window_nodes_are_stacked(ancestor, node) || !window_nodes_are_stacked(ancestor, prev)) + return NULL; + + return window_manager_find_window(wm, prev->window_id); +} + +struct window *window_manager_find_managed_window_above(struct space_manager *sm, struct window_manager *wm, struct window *window) +{ + struct view *view = space_manager_find_view(sm, space_manager_active_space()); + if (!view) return NULL; + + struct window_node *node = view_find_window_node(view, window->id); + if (!node) return NULL; + + struct window_node *next = window_node_find_next_leaf(node); + if (!next) return NULL; + + struct window_node *ancestor = window_node_find_common_ancestor(view->root, node, next); + if (!window_nodes_are_stacked(ancestor, node) || !window_nodes_are_stacked(ancestor, next)) + return NULL; + + return window_manager_find_window(wm, next->window_id); +} + struct window *window_manager_find_prev_managed_window(struct space_manager *sm, struct window_manager *wm, struct window *window) { struct view *view = space_manager_find_view(sm, space_manager_active_space()); diff --git a/src/window_manager.h b/src/window_manager.h index d4be8fd9..2f72543a 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -95,6 +95,8 @@ struct window *window_manager_find_window_at_point(struct window_manager *wm, CG struct window *window_manager_find_window_below_cursor(struct window_manager *wm); struct window *window_manager_find_closest_managed_window_in_direction(struct window_manager *wm, struct window *window, int direction); struct window *window_manager_find_closest_window_in_direction(struct window_manager *wm, struct window *window, int direction); +struct window *window_manager_find_managed_window_below(struct space_manager *sm, struct window_manager *wm, struct window *window); +struct window *window_manager_find_managed_window_above(struct space_manager *sm, struct window_manager *wm, struct window *window); struct window *window_manager_find_prev_managed_window(struct space_manager *sm, struct window_manager *wm, struct window *window); struct window *window_manager_find_next_managed_window(struct space_manager *sm, struct window_manager *wm, struct window *window); struct window *window_manager_find_first_managed_window(struct space_manager *sm, struct window_manager *wm); From ec9e38521a4a39436df882812870c2d5b65fb340 Mon Sep 17 00:00:00 2001 From: unrevre Date: Sun, 13 Oct 2019 16:56:27 -0400 Subject: [PATCH 8/8] doc: update documentation for changes --- doc/yabai.1 | 2 +- doc/yabai.asciidoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/yabai.1 b/doc/yabai.1 index 5749cc0d..e9a6053c 100644 --- a/doc/yabai.1 +++ b/doc/yabai.1 @@ -81,7 +81,7 @@ Loads the scripting\-addition into Dock.app. .sp .if n .RS 4 .nf -DIR_SEL := north | east | south | west +DIR_SEL := north | east | south | west | above | below WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | DIR_SEL | window id diff --git a/doc/yabai.asciidoc b/doc/yabai.asciidoc index 2ff05def..d5050afb 100644 --- a/doc/yabai.asciidoc +++ b/doc/yabai.asciidoc @@ -62,7 +62,7 @@ Definitions [subs=+macros] ---- -DIR_SEL := north | east | south | west +DIR_SEL := north | east | south | west | above | below WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | DIR_SEL | window id