Skip to content

Commit

Permalink
#2431 workaround for breaking issue caused by other third-party softw…
Browse files Browse the repository at this point in the history
…are on macOS Sequoia
  • Loading branch information
koekeishiya committed Sep 26, 2024
1 parent 61bbb9b commit 6f9006d
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Changed
- Implement fallback method for macOS Sequoia to detect when a window is destroyed, because of weird system problems when other third-party software is in use [#2431](https://github.com/koekeishiya/yabai/issues/2431)

## [7.1.3] - 2024-09-17
### Changed
Expand Down
52 changes: 52 additions & 0 deletions src/event_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ extern int g_connection;
extern void *g_workspace_context;
extern int g_layer_below_window_level;

static void update_window_notifications(void)
{
int window_count = 0;
uint32_t window_list[1024] = {0};

if (workspace_is_macos_sequoia()) {
// NOTE(koekeishiya): Subscribe to all windows because of window_destroyed (and ordered) notifications
table_for (struct window *window, g_window_manager.window, {
window_list[window_count++] = window->id;
})
} else {
// NOTE(koekeishiya): Subscribe to windows that have a feedback_border because of window_ordered notifications
table_for (struct window_node *node, g_window_manager.insert_feedback, {
window_list[window_count++] = node->window_order[0];
})
}

SLSRequestNotificationsForWindows(g_connection, window_list, window_count);

This comment has been minimized.

Copy link
@lwouis

lwouis Oct 18, 2024

Hi @koekeishiya,

People have reported windows not disappearing from AltTab on Sequoia. Someone pointed here saying that you found a fix.

Looking at this PR, I see private API calls:

 SLSRequestNotificationsForWindows(g_connection, window_list, window_count);
SLSRegisterConnectionNotifyProc(g_connection, connection_handler, 804, NULL);

Could you please share with me what these do? From a distance, it seems that you register to get more notifications than usual.

Are these APIs usable without the Dock injection?

In AltTab we suscribe to events from the Apps and Windows using public APIs such as AXObserverCreate(pid, axObserverCallback, &axObserver). We listen for the kAXUIElementDestroyedNotification event for window destruction. Somehow on Sequoia, we don't get notified some these, sometimes. That seems like a clear macOS bug to me. Do you know more perhaps?

Thank you 🙇

This comment has been minimized.

Copy link
@koekeishiya

koekeishiya Oct 18, 2024

Author Owner

Hey @lwouis ,

These functions are usable without having to disable SIP. They allow you to listen for window events for specific windows through your own application' connection to the WindowServer, basically making them equivalent in functionality to the notifications you observe through the AX API.

The difference is that you have to explicitly tell the system which windows you want notifications for, using SLSRequestNotificationsForWindows(int cid, uint32_t *window_list, int window_count);. That means that you need to keep a list of which window-ids you want to observe, that is updated when a window is created or destroyed. Every call to this function must include the full collection of windows to be notified for. There is a maximum limit of 1024 windows hardcoded by Apple.

The event-type "804" is basically "Window Destroyed" notification, and it passes along the window-id (uint32_t) of the destroyed window. There are other events for window_moved,resized,created,ordered etc.

I agree that AXUIElementDestroyedNotification not triggering under certain circumstances on Sequoia should be treated as an Apple bug, but I figured it would take them forever to fix it, hence I implemented this workaround.

This comment has been minimized.

Copy link
@lwouis

lwouis Oct 18, 2024

Thank you very much @koekeishiya! It's super clear. I'm really impressed how you keep finding incredible workarounds like this!

If I understand correct, a call to SLSRegisterConnectionNotifyProc(g_connection, connection_handler, 804, NULL) is first required, to be able to get the events 804 (window destroyed. Then we need to send a call to SLSRequestNotificationsForWindows(g_connection, window_list, window_count) every time our list of observed windows changes.

At this point, we'll get callbacks on connection_handler with events 804 any time a window is destroyed.

Is there any extra initial setup or clean-up setup at the end?

Thank you!

This comment has been minimized.

Copy link
@koekeishiya

koekeishiya Oct 18, 2024

Author Owner

@lwouis

That sounds right yeah. I don't do any additonal steps for now, and I haven't noticed any issues so far.

}

static void window_did_receive_focus(struct window_manager *wm, struct mouse_state *ms, struct window *window)
{
struct window *focused_window = window_manager_find_window(wm, wm->focused_window_id);
Expand Down Expand Up @@ -200,6 +220,10 @@ static EVENT_HANDLER(APPLICATION_LAUNCHED)
window_node_flush(view->root);
view_clear_flag(view, VIEW_IS_DIRTY);
}

if (workspace_is_macos_sequoia()) {
update_window_notifications();
}
}

static EVENT_HANDLER(APPLICATION_TERMINATED)
Expand Down Expand Up @@ -290,6 +314,10 @@ static EVENT_HANDLER(APPLICATION_TERMINATED)
view_clear_flag(view, VIEW_IS_DIRTY);
}

if (workspace_is_macos_sequoia()) {
update_window_notifications();
}

out:
process_destroy(process);
}
Expand Down Expand Up @@ -525,6 +553,10 @@ static EVENT_HANDLER(WINDOW_CREATED)
if (window_manager_is_window_eligible(window)) {
event_signal_push(SIGNAL_WINDOW_CREATED, window);
}

if (workspace_is_macos_sequoia()) {
update_window_notifications();
}
}

static EVENT_HANDLER(WINDOW_DESTROYED)
Expand Down Expand Up @@ -552,6 +584,10 @@ static EVENT_HANDLER(WINDOW_DESTROYED)
window_manager_remove_scratchpad_for_window(&g_window_manager, window, false);
window_manager_remove_window(&g_window_manager, window->id);
window_destroy(window);

if (workspace_is_macos_sequoia()) {
update_window_notifications();
}
}

static EVENT_HANDLER(WINDOW_FOCUSED)
Expand Down Expand Up @@ -799,6 +835,22 @@ static EVENT_HANDLER(SLS_WINDOW_ORDERED)
if (node) SLSOrderWindow(g_connection, node->feedback_window.id, 1, node->window_order[0]);
}

static EVENT_HANDLER(SLS_WINDOW_DESTROYED)
{
uint32_t wid = (uint64_t)(intptr_t) context;
debug("%s: %d\n", __FUNCTION__, wid);

struct window *window = window_manager_find_window(&g_window_manager, wid);
if (!window) return;

if (!__sync_bool_compare_and_swap(&window->id_ptr, &window->id, &window->id)) {
debug("%s: %d has been marked invalid by the system, ignoring event..\n", __FUNCTION__, wid);
return;
}

EVENT_HANDLER_WINDOW_DESTROYED(window, 0);
}

static EVENT_HANDLER(SLS_SPACE_CREATED)
{
uint64_t sid = (uint64_t)(intptr_t) context;
Expand Down
1 change: 1 addition & 0 deletions src/event_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
EVENT_TYPE_ENTRY(WINDOW_DEMINIMIZED) \
EVENT_TYPE_ENTRY(WINDOW_TITLE_CHANGED) \
EVENT_TYPE_ENTRY(SLS_WINDOW_ORDERED) \
EVENT_TYPE_ENTRY(SLS_WINDOW_DESTROYED) \
EVENT_TYPE_ENTRY(SLS_SPACE_CREATED) \
EVENT_TYPE_ENTRY(SLS_SPACE_DESTROYED) \
EVENT_TYPE_ENTRY(SPACE_CHANGED) \
Expand Down
2 changes: 2 additions & 0 deletions src/mission_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ static CONNECTION_CALLBACK(connection_handler)
event_loop_post(&g_event_loop, SLS_SPACE_DESTROYED, (void *) (intptr_t) (* (uint64_t *) data), 0);
} else if (type == 808) {
event_loop_post(&g_event_loop, SLS_WINDOW_ORDERED, (void *) (intptr_t) (* (uint32_t *) data), 0);
} else if (type == 804) {
event_loop_post(&g_event_loop, SLS_WINDOW_DESTROYED, (void *) (intptr_t) (* (uint32_t *) data), 0);
}
}
#pragma clang diagnostic pop
Expand Down
21 changes: 7 additions & 14 deletions src/view.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,6 @@ extern struct display_manager g_display_manager;
extern struct space_manager g_space_manager;
extern struct window_manager g_window_manager;

void insert_feedback_update_notifications(void)
{
int window_count = 0;
uint32_t window_list[1024] = {0};

table_for (struct window_node *node, g_window_manager.insert_feedback, {
window_list[window_count++] = node->window_order[0];
})

SLSRequestNotificationsForWindows(g_connection, window_list, window_count);
}

#define INSERT_FEEDBACK_WIDTH 2
#define INSERT_FEEDBACK_RADIUS 9
void insert_feedback_show(struct window_node *node)
Expand Down Expand Up @@ -53,7 +41,9 @@ void insert_feedback_show(struct window_node *node)
SLSReenableUpdate(g_connection);
SLSOrderWindow(g_connection, node->feedback_window.id, 1, node->window_order[0]);
table_add(&g_window_manager.insert_feedback, &node->window_order[0], node);
insert_feedback_update_notifications();
if (!workspace_is_macos_sequoia()) {
update_window_notifications();
}
}

CGFloat clip_x, clip_y, clip_w, clip_h;
Expand Down Expand Up @@ -116,7 +106,10 @@ void insert_feedback_destroy(struct window_node *node)
{
if (node->feedback_window.id) {
table_remove(&g_window_manager.insert_feedback, &node->window_order[0]);
insert_feedback_update_notifications();

if (!workspace_is_macos_sequoia()) {
update_window_notifications();
}

SLSOrderWindow(g_connection, node->feedback_window.id, 0, 0);
CGContextRelease(node->feedback_window.context);
Expand Down
8 changes: 8 additions & 0 deletions src/yabai.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,18 @@ int main(int argc, char **argv)

SLSRegisterConnectionNotifyProc(g_connection, connection_handler, 808, NULL);

if (workspace_is_macos_sequoia()) {
SLSRegisterConnectionNotifyProc(g_connection, connection_handler, 804, NULL);
}

window_manager_init(&g_window_manager);
space_manager_begin(&g_space_manager);
window_manager_begin(&g_space_manager, &g_window_manager);

if (workspace_is_macos_sequoia()) {
update_window_notifications();
}

if (!message_loop_begin(g_socket_file)) {
error("yabai: could not start message loop! abort..\n");
}
Expand Down

0 comments on commit 6f9006d

Please sign in to comment.