From 7e9b09b964a93a1cd376ba91509ef54a6427ed4e Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 24 Apr 2022 21:16:26 +0200 Subject: [PATCH] #1247 improved ffm autofocus when SIP is disabled --- CHANGELOG.md | 3 +- src/event.c | 9 ++-- src/message.c | 3 ++ src/osax/payload.m | 113 +++++++++++++++++++++++++++++++++++++++++++ src/sa.h | 1 + src/sa.m | 7 +++ src/window_manager.c | 7 ++- src/window_manager.h | 5 +- 8 files changed, 142 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c40707c1..c8e11908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - The scripting-addition will now also remove the space switch animation when using cmd+tab, clicking on an item in the Dock, and using the numeric macOS mission-control keyboard shortcuts [#1235](https://github.com/koekeishiya/yabai/issues/1235) - Improved logic used to determine the target window in a given direction [#1220](https://github.com/koekeishiya/yabai/issues/1220) -- Improve behaviour of *focus_follows_mouse autoraise*, preventing a window from being raised if it would occlude some other **floating window**. [#1246](https://github.com/koekeishiya/yabai/issues/1246) +- Improve behaviour of *focus_follows_mouse autoraise*, preventing a window from being raised if it would occlude some other **floating window** [#1246](https://github.com/koekeishiya/yabai/issues/1246) +- Added an improved implementation of *focus_follows_mouse autofocus* that uses the scripting-addition. prior autofocus has been renamed to **autofocus_sip_friendly** [#1247](https://github.com/koekeishiya/yabai/issues/1247) ## [4.0.0] - 2022-03-16 ### Added diff --git a/src/event.c b/src/event.c index 51f6fb31..64fcfe7c 100644 --- a/src/event.c +++ b/src/event.c @@ -1007,7 +1007,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_MOUSE_MOVED) } - if (g_window_manager.ffm_mode == FFM_AUTOFOCUS) { + if (g_window_manager.ffm_mode == FFM_AUTOFOCUS_SIP) { // // NOTE(koekeishiya): Look for a window with role AXSheet or AXDrawer @@ -1037,9 +1037,12 @@ static EVENT_CALLBACK(EVENT_HANDLER_MOUSE_MOVED) } CFRelease(window_list); - window_manager_focus_window_without_raise(&window->application->psn, window->id); + window_manager_focus_window_without_raise_sip_friendly(&window->application->psn, window->id); g_mouse_state.ffm_window_id = window->id; - } else { + } else if (g_window_manager.ffm_mode == FFM_AUTOFOCUS) { + window_manager_focus_window_without_raise(window->id); + g_mouse_state.ffm_window_id = window->id; + } else if (g_window_manager.ffm_mode == FFM_AUTORAISE) { // // NOTE(koekeishiya): If any **floating** window would be fully occluded by diff --git a/src/message.c b/src/message.c index c658bb57..d4f65ea1 100644 --- a/src/message.c +++ b/src/message.c @@ -52,6 +52,7 @@ extern bool g_verbose; #define SELECTOR_CONFIG_SPACE "--space" +#define ARGUMENT_CONFIG_FFM_AUTOFOCUS_SIP "autofocus_sip_friendly" #define ARGUMENT_CONFIG_FFM_AUTOFOCUS "autofocus" #define ARGUMENT_CONFIG_FFM_AUTORAISE "autoraise" #define ARGUMENT_CONFIG_WINDOW_ORIGIN_DEFAULT "default" @@ -997,6 +998,8 @@ static void handle_domain_config(FILE *rsp, struct token domain, char *message) fprintf(rsp, "%s\n", ffm_mode_str[g_window_manager.ffm_mode]); } else if (token_equals(value, ARGUMENT_COMMON_VAL_OFF)) { g_window_manager.ffm_mode = FFM_DISABLED; + } else if (token_equals(value, ARGUMENT_CONFIG_FFM_AUTOFOCUS_SIP)) { + g_window_manager.ffm_mode = FFM_AUTOFOCUS_SIP; } else if (token_equals(value, ARGUMENT_CONFIG_FFM_AUTOFOCUS)) { g_window_manager.ffm_mode = FFM_AUTOFOCUS; } else if (token_equals(value, ARGUMENT_CONFIG_FFM_AUTORAISE)) { diff --git a/src/osax/payload.m b/src/osax/payload.m index c64c162b..e276e1c2 100644 --- a/src/osax/payload.m +++ b/src/osax/payload.m @@ -40,6 +40,9 @@ #define kCGSOnAllWorkspacesTagBit (1 << 11) #define kCGSNoShadowTagBit (1 << 3) +#define CONNECTION_CALLBACK(name) void name(uint32_t type, void *data, size_t data_length, void *context, int cid) +typedef CONNECTION_CALLBACK(connection_callback); + extern int CGSMainConnectionID(void); extern CGError CGSGetConnectionPSN(int cid, ProcessSerialNumber *psn); extern CGError CGSSetWindowAlpha(int cid, uint32_t wid, float alpha); @@ -59,6 +62,11 @@ extern CFStringRef CGSCopyManagedDisplayForSpace(const int cid, uint64_t spid); extern void CGSShowSpaces(int cid, CFArrayRef spaces); extern void CGSHideSpaces(int cid, CFArrayRef spaces); +extern CFArrayRef CGSCopyWindowsWithOptionsAndTags(int cid, uint32_t owner, CFArrayRef spaces, uint32_t options, uint64_t *set_tags, uint64_t *clear_tags); +extern CFArrayRef CGSCopySpacesForWindows(int cid, int selector, CFArrayRef window_list); +extern CGError CGSOrderWindowList(int cid, const uint32_t *window_list, const int *window_order, const uint32_t *window_rel, int window_count); +extern CGError CGSRequestNotificationsForWindows(int cid, uint32_t *window_list, int window_count); +extern CGError CGSRegisterConnectionNotifyProc(int cid, connection_callback *handler, uint32_t event, void *context); static int _connection; static id dock_spaces; @@ -430,6 +438,45 @@ static Token get_token(const char **message) return token; } +static inline CFArrayRef cfarray_of_cfnumbers(void *values, size_t size, int count, CFNumberType type) +{ + CFNumberRef temp[count]; + + for (int i = 0; i < count; ++i) { + temp[i] = CFNumberCreate(NULL, type, ((char *)values) + (size * i)); + } + + CFArrayRef result = CFArrayCreate(NULL, (const void **)temp, count, &kCFTypeArrayCallBacks); + + for (int i = 0; i < count; ++i) { + CFRelease(temp[i]); + } + + return result; +} + +static uint64_t window_space(uint32_t wid) +{ + uint64_t sid = 0; + + CFArrayRef window_list_ref = cfarray_of_cfnumbers(&wid, sizeof(uint32_t), 1, kCFNumberSInt32Type); + CFArrayRef space_list_ref = CGSCopySpacesForWindows(_connection, 0x7, window_list_ref); + if (!space_list_ref) goto err; + + int count = CFArrayGetCount(space_list_ref); + if (!count) goto free; + + CFNumberRef id_ref = CFArrayGetValueAtIndex(space_list_ref, 0); + CFNumberGetValue(id_ref, CFNumberGetType(id_ref), &sid); + +free: + CFRelease(space_list_ref); +err: + CFRelease(window_list_ref); + + return sid; +} + static inline id get_ivar_value(id instance, const char *name) { id result = nil; @@ -739,6 +786,69 @@ static void do_window_focus(const char *message) ((focus_window_call) set_front_window_fp)(window_psn, window_id); } +static volatile bool g_sloppy_focus_spin = false; +static CONNECTION_CALLBACK(connection_handler) +{ + g_sloppy_focus_spin = false; +} + +static void do_window_sloppy_focus(const char *message) +{ + if (set_front_window_fp == 0) return; + + int window_connection; + ProcessSerialNumber window_psn; + + Token wid_token = get_token(&message); + uint32_t window_id = token_to_uint32t(wid_token); + + uint64_t set_tags = 0; + uint64_t clear_tags = 0; + uint32_t options = 0x2; + uint64_t window_sid = window_space(window_id); + + CFArrayRef space_list_ref = cfarray_of_cfnumbers(&window_sid, sizeof(uint64_t), 1, kCFNumberSInt64Type); + CFArrayRef window_list_ref = CGSCopyWindowsWithOptionsAndTags(_connection, 0, space_list_ref, options, &set_tags, &clear_tags); + CFRelease(space_list_ref); + + if (window_list_ref) { + g_sloppy_focus_spin = true; + CGSRequestNotificationsForWindows(_connection, &window_id, 1); + + int window_list_count = CFArrayGetCount(window_list_ref); + int window_count = 0; + + uint32_t window_list[window_list_count]; + uint32_t window_rel[window_list_count]; + int window_order[window_list_count]; + + for (int i = 0; i < window_list_count && i < 128; ++i) { + uint32_t value = 0; + CFNumberRef id_ref = CFArrayGetValueAtIndex(window_list_ref, i); + CFNumberGetValue(id_ref, CFNumberGetType(id_ref), &value); + + if (value != window_id) { + window_list[window_count] = value; + window_rel[window_count] = window_id; + window_order[window_count] = 1; + ++window_count; + } else { + break; + } + } + + CGSGetWindowOwner(_connection, window_id, &window_connection); + CGSGetConnectionPSN(window_connection, &window_psn); + ((focus_window_call) set_front_window_fp)(window_psn, window_id); + + for (;;) { if (!g_sloppy_focus_spin) break; } + + CGSOrderWindowList(_connection, window_list, window_order, window_rel, window_count); + CGSRequestNotificationsForWindows(_connection, &window_id, 0); + CFRelease(window_list_ref); + } +} + static void do_window_shadow(const char *message) { Token wid_token = get_token(&message); @@ -806,6 +916,8 @@ static void handle_message(int sockfd, const char *message) do_window_sticky(message); } else if (token_equals(token, "window_focus")) { do_window_focus(message); + } else if (token_equals(token, "window_sloppy_focus")) { + do_window_sloppy_focus(message); } else if (token_equals(token, "window_shadow")) { do_window_shadow(message); } @@ -862,6 +974,7 @@ static bool start_daemon(char *socket_path) } init_instances(); + CGSRegisterConnectionNotifyProc(_connection, connection_handler, 808, NULL); pthread_create(&daemon_thread, NULL, &handle_connection, NULL); return true; diff --git a/src/sa.h b/src/sa.h index eab4f6a5..bd76c841 100644 --- a/src/sa.h +++ b/src/sa.h @@ -22,6 +22,7 @@ bool scripting_addition_set_layer(uint32_t wid, int layer); bool scripting_addition_set_sticky(uint32_t wid, bool sticky); bool scripting_addition_set_shadow(uint32_t wid, bool shadow); bool scripting_addition_focus_window(uint32_t wid); +bool scripting_addition_sloppy_focus_window(uint32_t wid); bool scripting_addition_scale_window(uint32_t wid, float x, float y, float w, float h); extern bool workspace_is_macos_monterey(void); diff --git a/src/sa.m b/src/sa.m index 811b77bc..cdb39086 100644 --- a/src/sa.m +++ b/src/sa.m @@ -630,6 +630,13 @@ bool scripting_addition_focus_window(uint32_t wid) return scripting_addition_run_command(message); } +bool scripting_addition_sloppy_focus_window(uint32_t wid) +{ + char message[MAXLEN]; + snprintf(message, sizeof(message), "window_sloppy_focus %d", wid); + return scripting_addition_run_command(message); +} + bool scripting_addition_scale_window(uint32_t wid, float x, float y, float w, float h) { char message[MAXLEN]; diff --git a/src/window_manager.c b/src/window_manager.c index 9d8cab61..248be714 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -842,7 +842,7 @@ static void window_manager_make_key_window(ProcessSerialNumber *window_psn, uint SLPSPostEventRecordTo(window_psn, bytes2); } -void window_manager_focus_window_without_raise(ProcessSerialNumber *window_psn, uint32_t window_id) +void window_manager_focus_window_without_raise_sip_friendly(ProcessSerialNumber *window_psn, uint32_t window_id) { if (psn_equals(window_psn, &g_window_manager.focused_window_psn)) { uint8_t bytes1[0xf8] = { [0x04] = 0xf8, [0x08] = 0x0d, [0x8a] = 0x02 }; @@ -864,6 +864,11 @@ void window_manager_focus_window_without_raise(ProcessSerialNumber *window_psn, window_manager_make_key_window(window_psn, window_id); } +void window_manager_focus_window_without_raise(uint32_t window_id) +{ + scripting_addition_sloppy_focus_window(window_id); +} + void window_manager_focus_window_with_raise(ProcessSerialNumber *window_psn, uint32_t window_id, AXUIElementRef window_ref) { #if 1 diff --git a/src/window_manager.h b/src/window_manager.h index 7f55e024..87346ea7 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -40,6 +40,7 @@ static const char *purify_mode_str[] = enum ffm_mode { FFM_DISABLED, + FFM_AUTOFOCUS_SIP, FFM_AUTOFOCUS, FFM_AUTORAISE }; @@ -47,6 +48,7 @@ enum ffm_mode static const char *ffm_mode_str[] = { "disabled", + "autofocus_sip_friendly", "autofocus", "autoraise" }; @@ -125,7 +127,8 @@ struct window *window_manager_find_last_window_in_stack(struct space_manager *sm struct window *window_manager_find_recent_window_in_stack(struct space_manager *sm, struct window_manager *wm, struct window *window); struct window *window_manager_find_largest_managed_window(struct space_manager *sm, struct window_manager *wm); struct window *window_manager_find_smallest_managed_window(struct space_manager *sm, struct window_manager *wm); -void window_manager_focus_window_without_raise(ProcessSerialNumber *window_psn, uint32_t window_id); +void window_manager_focus_window_without_raise_sip_friendly(ProcessSerialNumber *window_psn, uint32_t window_id); +void window_manager_focus_window_without_raise(uint32_t window_id); void window_manager_focus_window_with_raise(ProcessSerialNumber *window_psn, uint32_t window_id, AXUIElementRef window_ref); struct window *window_manager_focused_window(struct window_manager *wm); struct application *window_manager_focused_application(struct window_manager *wm);