From e02addf7792d79b8d48d0e3bb4f5ee8e96a45127 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 12 Mar 2024 01:10:24 +0100 Subject: [PATCH] #550 config to change how yabai should interpret display arrangement indices --- CHANGELOG.md | 1 + doc/yabai.1 | 15 ++++++-- doc/yabai.asciidoc | 6 ++++ examples/yabairc | 1 + src/display.c | 27 +------------- src/display.h | 1 - src/display_manager.c | 83 +++++++++++++++++++++++++++++++++++++------ src/display_manager.h | 17 +++++++++ src/event_signal.c | 6 ++-- src/message.c | 17 +++++++++ src/rule.c | 2 +- src/view.c | 2 +- src/window.c | 4 +-- 13 files changed, 136 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce13bccc..837a4af8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Added new command `config window_animation_easing ..` to select easing function [#2131](https://github.com/koekeishiya/yabai/issues/2131) - Added new command `space --equalize .. ` to reset split ratios of all nodes within a space to default value [#2133](https://github.com/koekeishiya/yabai/issues/2133) - Added new command `space --switch ..` to focus a space (substitute with current focus) regardless of its display [#549](https://github.com/koekeishiya/yabai/issues/549) +- Added new command `config display_arrangement_order ..` to change yabai interprets arrangement indexes used to select displays [#550](https://github.com/koekeishiya/yabai/issues/550) ### Changed - Preserve relative space ordering when moving spaces to other displays [#2114](https://github.com/koekeishiya/yabai/issues/2114) diff --git a/doc/yabai.1 b/doc/yabai.1 index 24027fc6..c3de4aed 100644 --- a/doc/yabai.1 +++ b/doc/yabai.1 @@ -2,12 +2,12 @@ .\" Title: yabai .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2024-03-11 +.\" Date: 2024-03-12 .\" Manual: Yabai Manual .\" Source: Yabai .\" Language: English .\" -.TH "YABAI" "1" "2024-03-11" "Yabai" "Yabai Manual" +.TH "YABAI" "1" "2024-03-12" "Yabai" "Yabai Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -202,6 +202,17 @@ When focusing a window, put the mouse at its center. Automatically focus the window under the mouse. .RE .sp +\fBdisplay_arrangement_order\fP [\fIdefault|vertical|horizontal\fP] +.RS 4 +Specify how displays are ordered (determined by center point). +.br +\fIdefault\fP: Native macOS ordering. +.br +\fIvertical\fP: Order by y\-coordinate (followed by x\-coordinate when equal). +.br +\fIhorizontal\fP: Order by x\-coordinate (followed by y\-coordinate when equal). +.RE +.sp \fBwindow_origin_display\fP [\fIdefault|focused|cursor\fP] .RS 4 Specify which display a newly created window should be managed in. diff --git a/doc/yabai.asciidoc b/doc/yabai.asciidoc index b9f8e208..94f8e78a 100644 --- a/doc/yabai.asciidoc +++ b/doc/yabai.asciidoc @@ -152,6 +152,12 @@ Global Settings *focus_follows_mouse* ['autofocus|autoraise|off']:: Automatically focus the window under the mouse. +*display_arrangement_order* ['default|vertical|horizontal']:: + Specify how displays are ordered (determined by center point). + + 'default': Native macOS ordering. + + 'vertical': Order by y-coordinate (followed by x-coordinate when equal). + + 'horizontal': Order by x-coordinate (followed by y-coordinate when equal). + *window_origin_display* ['default|focused|cursor']:: Specify which display a newly created window should be managed in. + 'default': The display in which the window is created (standard macOS behaviour). + diff --git a/examples/yabairc b/examples/yabairc index a365f531..d523e9f8 100755 --- a/examples/yabairc +++ b/examples/yabairc @@ -17,6 +17,7 @@ yabai -m config \ menubar_opacity 1.0 \ mouse_follows_focus off \ focus_follows_mouse off \ + display_arrangement_order default \ window_origin_display default \ window_placement second_child \ window_zoom_persist on \ diff --git a/src/display.c b/src/display.c index 59169ca1..071a31e6 100644 --- a/src/display.c +++ b/src/display.c @@ -59,7 +59,7 @@ void display_serialize(FILE *rsp, uint32_t did) "}", did, uuid ? uuid : "", - display_arrangement(did), + display_manager_display_id_arrangement(did), frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, buffer); } @@ -214,28 +214,3 @@ uint64_t *display_space_list(uint32_t did, int *count) out: return space_list; } - -int display_arrangement(uint32_t did) -{ - int result = 0; - - CFStringRef uuid = display_uuid(did); - if (!uuid) goto out; - - CFArrayRef displays = SLSCopyManagedDisplays(g_connection); - if (!displays) goto err; - - int displays_count = CFArrayGetCount(displays); - for (int i = 0; i < displays_count; ++i) { - if (CFEqual(CFArrayGetValueAtIndex(displays, i), uuid)) { - result = i + 1; - break; - } - } - - CFRelease(displays); -err: - CFRelease(uuid); -out: - return result; -} diff --git a/src/display.h b/src/display.h index 23918114..3b37fd1d 100644 --- a/src/display.h +++ b/src/display.h @@ -12,6 +12,5 @@ CGPoint display_center(uint32_t did); uint64_t display_space_id(uint32_t did); int display_space_count(uint32_t did); uint64_t *display_space_list(uint32_t did, int *count); -int display_arrangement(uint32_t did); #endif diff --git a/src/display_manager.c b/src/display_manager.c index 2e73d6f0..25b3ac06 100644 --- a/src/display_manager.c +++ b/src/display_manager.c @@ -80,38 +80,100 @@ uint32_t display_manager_point_display_id(CGPoint point) return result; } -CFStringRef display_manager_arrangement_display_uuid(int arrangement) +static CFComparisonResult display_manager_coordinate_comparator(CFTypeRef a, CFTypeRef b, void *context) { - CFStringRef result = NULL; + enum display_arrangement_order axis = (enum display_arrangement_order)(uintptr_t) context; + if (axis == DISPLAY_ARRANGEMENT_ORDER_DEFAULT) goto out; + + uint32_t a_did = display_id(a); + uint32_t b_did = display_id(b); + + CGPoint a_center = display_center(a_did); + CGPoint b_center = display_center(b_did); + + float a_coord = axis == DISPLAY_ARRANGEMENT_ORDER_Y ? a_center.y : a_center.x; + float b_coord = axis == DISPLAY_ARRANGEMENT_ORDER_Y ? b_center.y : b_center.x; + + if (a_coord < b_coord) return kCFCompareLessThan; + if (a_coord > b_coord) return kCFCompareGreaterThan; + + a_coord = axis == DISPLAY_ARRANGEMENT_ORDER_Y ? a_center.x : a_center.y; + b_coord = axis == DISPLAY_ARRANGEMENT_ORDER_Y ? b_center.x : b_center.y; + + if (a_coord < b_coord) return kCFCompareLessThan; + if (a_coord > b_coord) return kCFCompareGreaterThan; + +out: + return kCFCompareEqualTo; +} + +int display_manager_display_id_arrangement(uint32_t did) +{ + int result = 0; + + CFStringRef uuid = display_uuid(did); + if (!uuid) goto out; + CFArrayRef displays = SLSCopyManagedDisplays(g_connection); + if (!displays) goto err; int count = CFArrayGetCount(displays); - int index = arrangement - 1; + if (!count) goto empty; - if (in_range_ie(index, 0, count)) { - result = CFRetain(CFArrayGetValueAtIndex(displays, index)); + if (g_display_manager.order != DISPLAY_ARRANGEMENT_ORDER_DEFAULT) { + CFMutableArrayRef mut_displays = CFArrayCreateMutableCopy(NULL, count, displays); + CFArraySortValues(mut_displays, CFRangeMake(0, count), &display_manager_coordinate_comparator, (void *)(uintptr_t) g_display_manager.order); + CFRelease(displays); displays = mut_displays; } + for (int i = 0; i < count; ++i) { + if (CFEqual(CFArrayGetValueAtIndex(displays, i), uuid)) { + result = i + 1; + break; + } + } + +empty: CFRelease(displays); +err: + CFRelease(uuid); +out: return result; } -uint32_t display_manager_arrangement_display_id(int arrangement) +CFStringRef display_manager_arrangement_display_uuid(int arrangement) { - uint32_t result = 0; + CFStringRef result = NULL; CFArrayRef displays = SLSCopyManagedDisplays(g_connection); int count = CFArrayGetCount(displays); int index = arrangement - 1; if (in_range_ie(index, 0, count)) { - result = display_id(CFArrayGetValueAtIndex(displays, index)); + if (g_display_manager.order != DISPLAY_ARRANGEMENT_ORDER_DEFAULT) { + CFMutableArrayRef mut_displays = CFArrayCreateMutableCopy(NULL, count, displays); + CFArraySortValues(mut_displays, CFRangeMake(0, count), &display_manager_coordinate_comparator, (void *)(uintptr_t) g_display_manager.order); + CFRelease(displays); displays = mut_displays; + } + + result = CFRetain(CFArrayGetValueAtIndex(displays, index)); } CFRelease(displays); return result; } +uint32_t display_manager_arrangement_display_id(int arrangement) +{ + CFStringRef uuid = display_manager_arrangement_display_uuid(arrangement); + if (!uuid) return 0; + + uint32_t result = display_id(uuid); + CFRelease(uuid); + + return result; +} + uint32_t display_manager_cursor_display_id(void) { CGPoint cursor; @@ -121,7 +183,7 @@ uint32_t display_manager_cursor_display_id(void) uint32_t display_manager_prev_display_id(uint32_t did) { - int arrangement = display_arrangement(did); + int arrangement = display_manager_display_id_arrangement(did); if (arrangement <= 1) return 0; return display_manager_arrangement_display_id(arrangement - 1); @@ -129,7 +191,7 @@ uint32_t display_manager_prev_display_id(uint32_t did) uint32_t display_manager_next_display_id(uint32_t did) { - int arrangement = display_arrangement(did); + int arrangement = display_manager_display_id_arrangement(did); if (arrangement >= display_manager_active_display_count()) return 0; return display_manager_arrangement_display_id(arrangement + 1); @@ -350,6 +412,7 @@ bool display_manager_begin(struct display_manager *dm) { dm->current_display_id = display_manager_active_display_id(); dm->last_display_id = dm->current_display_id; + dm->order = DISPLAY_ARRANGEMENT_ORDER_DEFAULT; dm->mode = EXTERNAL_BAR_OFF; dm->top_padding = 0; dm->bottom_padding = 0; diff --git a/src/display_manager.h b/src/display_manager.h index 59b16458..ca77f10f 100644 --- a/src/display_manager.h +++ b/src/display_manager.h @@ -5,6 +5,20 @@ #define DOCK_ORIENTATION_LEFT 3 #define DOCK_ORIENTATION_RIGHT 4 +enum display_arrangement_order +{ + DISPLAY_ARRANGEMENT_ORDER_DEFAULT, + DISPLAY_ARRANGEMENT_ORDER_X, + DISPLAY_ARRANGEMENT_ORDER_Y +}; + +static const char *display_arrangement_order_str[] = +{ + "default", + "horizontal", + "vertical" +}; + enum external_bar_mode { EXTERNAL_BAR_OFF, @@ -26,6 +40,8 @@ struct display_manager int top_padding; int bottom_padding; + + enum display_arrangement_order order; enum external_bar_mode mode; }; @@ -39,6 +55,7 @@ uint32_t display_manager_dock_display_id(void); CFStringRef display_manager_point_display_uuid(CGPoint point); uint32_t display_manager_point_display_id(CGPoint point); uint32_t display_manager_cursor_display_id(void); +int display_manager_display_id_arrangement(uint32_t did); CFStringRef display_manager_arrangement_display_uuid(int arrangement); uint32_t display_manager_arrangement_display_id(int arrangement); uint32_t display_manager_prev_display_id(uint32_t did); diff --git a/src/event_signal.c b/src/event_signal.c index ab62ef5b..b56b2409 100644 --- a/src/event_signal.c +++ b/src/event_signal.c @@ -279,7 +279,7 @@ void event_signal_push(enum signal_type type, void *context) case SIGNAL_DISPLAY_MOVED: case SIGNAL_DISPLAY_RESIZED: { uint32_t did = (uint32_t)(uintptr_t) context; - int index = display_arrangement(did); + int index = display_manager_display_id_arrangement(did); es->arg_name[0] = ts_alloc_unaligned(arg_size); es->arg_value[0] = ts_alloc_unaligned(arg_size); @@ -304,8 +304,8 @@ void event_signal_push(enum signal_type type, void *context) uint32_t did = g_display_manager.current_display_id; uint32_t recent_did = g_display_manager.last_display_id; - int index = display_arrangement(did); - int recent_index = display_arrangement(recent_did); + int index = display_manager_display_id_arrangement(did); + int recent_index = display_manager_display_id_arrangement(recent_did); es->arg_name[0] = ts_alloc_unaligned(arg_size); es->arg_value[0] = ts_alloc_unaligned(arg_size); diff --git a/src/message.c b/src/message.c index 699c2fa1..9609dc91 100644 --- a/src/message.c +++ b/src/message.c @@ -23,6 +23,7 @@ extern bool g_verbose; #define COMMAND_CONFIG_DEBUG_OUTPUT "debug_output" #define COMMAND_CONFIG_MFF "mouse_follows_focus" #define COMMAND_CONFIG_FFM "focus_follows_mouse" +#define COMMAND_CONFIG_DISPLAY_ORDER "display_arrangement_order" #define COMMAND_CONFIG_WINDOW_ORIGIN "window_origin_display" #define COMMAND_CONFIG_WINDOW_PLACEMENT "window_placement" #define COMMAND_CONFIG_WINDOW_ZOOM_PERSIST "window_zoom_persist" @@ -54,6 +55,9 @@ extern bool g_verbose; #define ARGUMENT_CONFIG_FFM_AUTOFOCUS "autofocus" #define ARGUMENT_CONFIG_FFM_AUTORAISE "autoraise" +#define ARGUMENT_CONFIG_DISPLAY_ORDER_DEFAULT "default" +#define ARGUMENT_CONFIG_DISPLAY_ORDER_X "horizontal" +#define ARGUMENT_CONFIG_DISPLAY_ORDER_Y "vertical" #define ARGUMENT_CONFIG_WINDOW_ORIGIN_DEFAULT "default" #define ARGUMENT_CONFIG_WINDOW_ORIGIN_FOCUSED "focused" #define ARGUMENT_CONFIG_WINDOW_ORIGIN_CURSOR "cursor" @@ -1079,6 +1083,19 @@ static void handle_domain_config(FILE *rsp, struct token domain, char *message) } else { daemon_fail(rsp, "unknown value '%.*s' given to command '%.*s' for domain '%.*s'\n", value.length, value.text, command.length, command.text, domain.length, domain.text); } + } else if (token_equals(command, COMMAND_CONFIG_DISPLAY_ORDER)) { + struct token value = get_token(&message); + if (!token_is_valid(value)) { + fprintf(rsp, "%s\n", display_arrangement_order_str[g_display_manager.order]); + } else if (token_equals(value, ARGUMENT_CONFIG_DISPLAY_ORDER_DEFAULT)) { + g_display_manager.order = DISPLAY_ARRANGEMENT_ORDER_DEFAULT; + } else if (token_equals(value, ARGUMENT_CONFIG_DISPLAY_ORDER_X)) { + g_display_manager.order = DISPLAY_ARRANGEMENT_ORDER_X; + } else if (token_equals(value, ARGUMENT_CONFIG_DISPLAY_ORDER_Y)) { + g_display_manager.order = DISPLAY_ARRANGEMENT_ORDER_Y; + } else { + daemon_fail(rsp, "unknown value '%.*s' given to command '%.*s' for domain '%.*s'\n", value.length, value.text, command.length, command.text, domain.length, domain.text); + } } else if (token_equals(command, COMMAND_CONFIG_WINDOW_ORIGIN)) { struct token value = get_token(&message); if (!token_is_valid(value)) { diff --git a/src/rule.c b/src/rule.c index 576668eb..2c214d1d 100644 --- a/src/rule.c +++ b/src/rule.c @@ -32,7 +32,7 @@ void rule_serialize(FILE *rsp, struct rule *rule, int index) rule->title ? rule->title : "", rule->role ? rule->role : "", rule->subrole ? rule->subrole : "", - display_arrangement(rule->effects.did), + display_manager_display_id_arrangement(rule->effects.did), space_manager_mission_control_index(rule->effects.sid), json_bool(rule_effects_check_flag(&rule->effects, RULE_FOLLOW_SPACE)), rule->effects.opacity, diff --git a/src/view.c b/src/view.c index ba23e84d..4852d049 100644 --- a/src/view.c +++ b/src/view.c @@ -875,7 +875,7 @@ void view_serialize(FILE *rsp, struct view *view) space_manager_mission_control_index(view->sid), space_label ? space_label->label : "", view_type_str[view->layout], - display_arrangement(space_display_id(view->sid)), + display_manager_display_id_arrangement(space_display_id(view->sid)), buffer, first_leaf ? first_leaf->window_order[0] : 0, last_leaf ? last_leaf->window_order[0] : 0, diff --git a/src/window.c b/src/window.c index c7afb274..93184c9a 100644 --- a/src/window.c +++ b/src/window.c @@ -161,7 +161,7 @@ void window_nonax_serialize(FILE *rsp, uint32_t wid) bool is_sticky = window_space_count(wid) > 1; int space = space_manager_mission_control_index(sid); - int display = display_arrangement(space_display_id(sid)); + int display = display_manager_display_id_arrangement(space_display_id(sid)); int level = window_level(wid); int sub_level = window_sub_level(wid); const char *layer = window_layer(level); @@ -249,7 +249,7 @@ void window_serialize(FILE *rsp, struct window *window) char *escaped_title = ts_string_escape(title); uint64_t sid = window_space(window->id); int space = space_manager_mission_control_index(sid); - int display = display_arrangement(space_display_id(sid)); + int display = display_manager_display_id_arrangement(space_display_id(sid)); int level = window_level(window->id); int sub_level = window_sub_level(window->id); const char *layer = window_layer(level);