diff --git a/doc/yabai.1 b/doc/yabai.1 index b3572a44..0417c5fd 100644 --- a/doc/yabai.1 +++ b/doc/yabai.1 @@ -103,9 +103,11 @@ SIGNAL_SEL := | LABEL DIR_SEL := north | east | south | west +FARTHEST_DIR_SEL := farthest_north | farthest_east | farthest_south | farthest_west + STACK_SEL := stack.prev | stack.next | stack.first | stack.last | stack.recent -WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | +WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | FARTHEST_DIR_SEL | DISPLAY_SEL := prev | next | first | last | recent | mouse | DIR_SEL | diff --git a/doc/yabai.asciidoc b/doc/yabai.asciidoc index 959f1413..1852e17b 100644 --- a/doc/yabai.asciidoc +++ b/doc/yabai.asciidoc @@ -80,9 +80,11 @@ SIGNAL_SEL := | LABEL DIR_SEL := north | east | south | west +FARTHEST_DIR_SEL := farthest_north | farthest_east | farthest_south | farthest_west + STACK_SEL := stack.prev | stack.next | stack.first | stack.last | stack.recent -WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | +WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | FARTHEST_DIR_SEL | DISPLAY_SEL := prev | next | first | last | recent | mouse | DIR_SEL | diff --git a/src/message.c b/src/message.c index c658bb57..33fe75b2 100644 --- a/src/message.c +++ b/src/message.c @@ -204,24 +204,28 @@ extern bool g_verbose; /* ----------------------------------------------------------------------------- */ /* --------------------------------COMMON ARGUMENTS----------------------------- */ -#define ARGUMENT_COMMON_VAL_ON "on" -#define ARGUMENT_COMMON_VAL_OFF "off" -#define ARGUMENT_COMMON_SEL_PREV "prev" -#define ARGUMENT_COMMON_SEL_NEXT "next" -#define ARGUMENT_COMMON_SEL_FIRST "first" -#define ARGUMENT_COMMON_SEL_LAST "last" -#define ARGUMENT_COMMON_SEL_RECENT "recent" -#define ARGUMENT_COMMON_SEL_NORTH "north" -#define ARGUMENT_COMMON_SEL_EAST "east" -#define ARGUMENT_COMMON_SEL_SOUTH "south" -#define ARGUMENT_COMMON_SEL_WEST "west" -#define ARGUMENT_COMMON_SEL_MOUSE "mouse" -#define ARGUMENT_COMMON_SEL_STACK "stack" -#define ARGUMENT_COMMON_SEL_STACK_PREV "stack.prev" -#define ARGUMENT_COMMON_SEL_STACK_NEXT "stack.next" -#define ARGUMENT_COMMON_SEL_STACK_FIRST "stack.first" -#define ARGUMENT_COMMON_SEL_STACK_LAST "stack.last" -#define ARGUMENT_COMMON_SEL_STACK_RECENT "stack.recent" +#define ARGUMENT_COMMON_VAL_ON "on" +#define ARGUMENT_COMMON_VAL_OFF "off" +#define ARGUMENT_COMMON_SEL_PREV "prev" +#define ARGUMENT_COMMON_SEL_NEXT "next" +#define ARGUMENT_COMMON_SEL_FIRST "first" +#define ARGUMENT_COMMON_SEL_LAST "last" +#define ARGUMENT_COMMON_SEL_RECENT "recent" +#define ARGUMENT_COMMON_SEL_NORTH "north" +#define ARGUMENT_COMMON_SEL_EAST "east" +#define ARGUMENT_COMMON_SEL_SOUTH "south" +#define ARGUMENT_COMMON_SEL_WEST "west" +#define ARGUMENT_COMMON_SEL_FARTHEST_NORTH "farthest_north" +#define ARGUMENT_COMMON_SEL_FARTHEST_EAST "farthest_east" +#define ARGUMENT_COMMON_SEL_FARTHEST_SOUTH "farthest_south" +#define ARGUMENT_COMMON_SEL_FARTHEST_WEST "farthest_west" +#define ARGUMENT_COMMON_SEL_MOUSE "mouse" +#define ARGUMENT_COMMON_SEL_STACK "stack" +#define ARGUMENT_COMMON_SEL_STACK_PREV "stack.prev" +#define ARGUMENT_COMMON_SEL_STACK_NEXT "stack.next" +#define ARGUMENT_COMMON_SEL_STACK_FIRST "stack.first" +#define ARGUMENT_COMMON_SEL_STACK_LAST "stack.last" +#define ARGUMENT_COMMON_SEL_STACK_RECENT "stack.recent" /* ----------------------------------------------------------------------------- */ struct token @@ -798,6 +802,50 @@ 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_COMMON_SEL_FARTHEST_NORTH)) { + if (acting_window) { + struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_NORTH); + if (closest_window) { + result.window = closest_window; + } else { + daemon_fail(rsp, "could not locate a farthest northward managed window.\n"); + } + } else { + daemon_fail(rsp, "could not locate the selected window.\n"); + } + } else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_EAST)) { + if (acting_window) { + struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_EAST); + if (closest_window) { + result.window = closest_window; + } else { + daemon_fail(rsp, "could not locate a farthest eastward managed window.\n"); + } + } else { + daemon_fail(rsp, "could not locate the selected window.\n"); + } + } else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_SOUTH)) { + if (acting_window) { + struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_SOUTH); + if (closest_window) { + result.window = closest_window; + } else { + daemon_fail(rsp, "could not locate a farthest southward managed window.\n"); + } + } else { + daemon_fail(rsp, "could not locate the selected window.\n"); + } + } else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_WEST)) { + if (acting_window) { + struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_WEST); + if (closest_window) { + result.window = closest_window; + } else { + daemon_fail(rsp, "could not locate a farthest westward managed window.\n"); + } + } else { + daemon_fail(rsp, "could not locate the selected window.\n"); + } } else if (token_equals(result.token, ARGUMENT_COMMON_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 efe3b37a..6e42d200 100644 --- a/src/view.c +++ b/src/view.c @@ -538,6 +538,91 @@ struct window_node *view_find_window_node_in_direction(struct view *view, struct return best_node; } +struct window_node *view_find_farthest_window_node_in_direction(struct view *view, struct window_node *source, int direction) +{ + // Maximize parallel part of direction and minimize orthogonal part of direction + // measured from center of source window (source_point) to center of + // - west border of target for DIR_WEST + // - east border of target for DIR_EAST + // - north border of target for DIR_NORTH + // - south border of target for DIR_SOUTH + // Note coordinate origin: (x=0 (vertical),y=0 (horizontal)) is at top, left corner of screen. + + int best_distance_parallel = 0, best_distance_orthogonal = INT_MAX; + CGPoint source_point = area_center(source->area); + + struct window_node *best_node = NULL; + struct window_node *target = window_node_find_first_leaf(view->root); + + while (target) { + CGPoint target_point = area_center(target->area); + + int distance_parallel=0, distance_orthogonal=0; + switch (direction) { + case DIR_EAST: { + distance_parallel = (source_point.x-(target->area.x+target->area.w))*(source_point.x-(target->area.x+target->area.w)); + distance_orthogonal = (source_point.y-target_point.y)*(source_point.y-target_point.y); + } break; + case DIR_WEST: { + distance_parallel = (source_point.x-target->area.x)*(source_point.x-target->area.x); + distance_orthogonal = (source_point.y-target_point.y)*(source_point.y-target_point.y); + } break; + case DIR_NORTH: { + distance_parallel = (source_point.y-target->area.y)*(source_point.y-target->area.y); + distance_orthogonal = (source_point.x-target_point.x)*(source_point.x-target_point.x); + } break; + case DIR_SOUTH: { + distance_parallel = (source_point.y-(target->area.y+target->area.h))*(source_point.y-(target->area.y+target->area.h)); + distance_orthogonal = (source_point.x-target_point.x)*(source_point.x-target_point.x); + } break; + } + + // Skip if parallel dir is not farther and orthogonal dir is not closer + if (distance_parallel <= best_distance_parallel && + distance_orthogonal >= best_distance_orthogonal) goto next; + + switch (direction) { + case DIR_EAST: { + // Accept only if target is actually in the east: compare left of target with right of source + if (target->area.x >= source->area.x + source->area.w) { + best_node = target; + best_distance_parallel = distance_parallel; + best_distance_orthogonal = distance_orthogonal; + } + } break; + case DIR_SOUTH: { + // Accept only if target is actually in the south: compare top of target with bottom of source + if (target->area.y >= source->area.y + source->area.h) { + best_node = target; + best_distance_parallel = distance_parallel; + best_distance_orthogonal = distance_orthogonal; + } + } break; + case DIR_WEST: { + // Accept only if target is actually in the west: compare right of target with left of source + if (target->area.x + target->area.w <= source->area.x) { + best_node = target; + best_distance_parallel = distance_parallel; + best_distance_orthogonal = distance_orthogonal; + } + } break; + case DIR_NORTH: { + // Accept only if target is actually in the north: compare bottom of target with top of source + if (target->area.y + target->area.h <= source->area.y) { + best_node = target; + best_distance_parallel = distance_parallel; + best_distance_orthogonal = distance_orthogonal; + } + } break; + } + +next: + target = window_node_find_next_leaf(target); + } + + return best_node; +} + struct window_node *view_find_window_node(struct view *view, uint32_t window_id) { struct window_node *node = window_node_find_first_leaf(view->root); diff --git a/src/view.h b/src/view.h index 8317e7ca..0a89ea50 100644 --- a/src/view.h +++ b/src/view.h @@ -123,6 +123,7 @@ struct window_node *window_node_find_prev_leaf(struct window_node *node); struct window_node *window_node_find_next_leaf(struct window_node *node); struct window_node *view_find_window_node_in_direction(struct view *view, struct window_node *source, int direction); +struct window_node *view_find_farthest_window_node_in_direction(struct view *view, struct window_node *source, int direction); struct window_node *view_find_window_node(struct view *view, uint32_t window_id); void view_stack_window_node(struct view *view, struct window_node *node, struct window *window); void view_add_window_node(struct view *view, struct window *window); diff --git a/src/window_manager.c b/src/window_manager.c index d0e50fc9..91a7a9b6 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -644,6 +644,20 @@ struct window *window_manager_find_closest_managed_window_in_direction(struct wi return window_manager_find_window(wm, closest->window_order[0]); } +struct window *window_manager_find_farthest_managed_window_in_direction(struct window_manager *wm, struct window *window, int direction) +{ + struct view *view = window_manager_find_managed_window(wm, window); + if (!view) return NULL; + + struct window_node *node = view_find_window_node(view, window->id); + if (!node) return NULL; + + struct window_node *farthest = view_find_farthest_window_node_in_direction(view, node, direction); + if (!farthest) return NULL; + + return window_manager_find_window(wm, farthest->window_order[0]); +} + 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 653743a5..29a81d86 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -112,6 +112,7 @@ struct window *window_manager_find_window_at_point_filtering_window(struct windo struct window *window_manager_find_window_at_point(struct window_manager *wm, CGPoint point); 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_farthest_managed_window_in_direction(struct window_manager *wm, struct window *window, int direction); 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);