diff --git a/src/application.c b/src/application.c index c6b612c7..0c2710ce 100644 --- a/src/application.c +++ b/src/application.c @@ -25,6 +25,13 @@ static OBSERVER_CALLBACK(application_notification_handler) struct event *event; event_create(event, WINDOW_FOCUSED, (void *)(intptr_t) window_id); event_loop_post(&g_event_loop, event); + } else if (CFEqual(notification, kAXMainWindowChangedNotification)) { + uint32_t window_id = ax_window_id(element); + if (!window_id) return; + + struct event *event; + event_create(event, WINDOW_MAIN_CHANGED, (void *)(intptr_t) window_id); + event_loop_post(&g_event_loop, event); } else if (CFEqual(notification, kAXWindowMovedNotification)) { uint32_t window_id = ax_window_id(element); if (!window_id) return; diff --git a/src/application.h b/src/application.h index 04c6f7b8..f7e4abda 100644 --- a/src/application.h +++ b/src/application.h @@ -6,28 +6,30 @@ typedef OBSERVER_CALLBACK(observer_callback); #define AX_APPLICATION_WINDOW_CREATED_INDEX 0 #define AX_APPLICATION_WINDOW_FOCUSED_INDEX 1 -#define AX_APPLICATION_WINDOW_MOVED_INDEX 2 -#define AX_APPLICATION_WINDOW_RESIZED_INDEX 3 -#define AX_APPLICATION_WINDOW_TITLE_CHANGED_INDEX 4 -#define AX_APPLICATION_WINDOW_MENU_OPENED_INDEX 5 +#define AX_APPLICATION_WINDOW_MAIN_CHANGED_INDEX 2 +#define AX_APPLICATION_WINDOW_MOVED_INDEX 3 +#define AX_APPLICATION_WINDOW_RESIZED_INDEX 4 +#define AX_APPLICATION_WINDOW_TITLE_CHANGED_INDEX 5 +#define AX_APPLICATION_WINDOW_MENU_OPENED_INDEX 6 #define AX_APPLICATION_WINDOW_CREATED (1 << AX_APPLICATION_WINDOW_CREATED_INDEX) #define AX_APPLICATION_WINDOW_FOCUSED (1 << AX_APPLICATION_WINDOW_FOCUSED_INDEX) +#define AX_APPLICATION_WINDOW_MAIN_CHANGED (1 << AX_APPLICATION_WINDOW_MAIN_CHANGED_INDEX) #define AX_APPLICATION_WINDOW_MOVED (1 << AX_APPLICATION_WINDOW_MOVED_INDEX) #define AX_APPLICATION_WINDOW_RESIZED (1 << AX_APPLICATION_WINDOW_RESIZED_INDEX) #define AX_APPLICATION_WINDOW_TITLE_CHANGED (1 << AX_APPLICATION_WINDOW_TITLE_CHANGED_INDEX) #define AX_APPLICATION_ALL (AX_APPLICATION_WINDOW_CREATED |\ AX_APPLICATION_WINDOW_FOCUSED |\ + AX_APPLICATION_WINDOW_MAIN_CHANGED |\ AX_APPLICATION_WINDOW_MOVED |\ AX_APPLICATION_WINDOW_RESIZED |\ AX_APPLICATION_WINDOW_TITLE_CHANGED) -static const CFStringRef kAXFocusedTabChangedNotification = CFSTR("AXFocusedTabChanged"); - static CFStringRef ax_application_notification[] = { [AX_APPLICATION_WINDOW_CREATED_INDEX] = kAXCreatedNotification, [AX_APPLICATION_WINDOW_FOCUSED_INDEX] = kAXFocusedWindowChangedNotification, + [AX_APPLICATION_WINDOW_MAIN_CHANGED_INDEX] = kAXMainWindowChangedNotification, [AX_APPLICATION_WINDOW_MOVED_INDEX] = kAXWindowMovedNotification, [AX_APPLICATION_WINDOW_RESIZED_INDEX] = kAXWindowResizedNotification, [AX_APPLICATION_WINDOW_TITLE_CHANGED_INDEX] = kAXTitleChangedNotification, diff --git a/src/event.c b/src/event.c index 7d40472b..906e6abd 100644 --- a/src/event.c +++ b/src/event.c @@ -182,6 +182,9 @@ static EVENT_CALLBACK(EVENT_HANDLER_APPLICATION_LAUNCHED) for (int i = 0; i < window_count; ++i) { struct ax_window *window = window_list[i]; if (window) { + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window->id); + if (group && group->active_window_id != window->id) continue; + if (window_manager_should_manage_window(window)) { struct view *view = space_manager_tile_window_on_space_with_insertion_point(&g_space_manager, window, window_space(window), prev_window_id); window_manager_add_managed_window(&g_window_manager, window, view); @@ -236,7 +239,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_APPLICATION_TERMINATED) struct ax_window *window = window_list[i]; if (!window) continue; - struct view *view = window_manager_find_managed_window(&g_window_manager, window); + struct view *view = window_manager_find_managed_window(&g_window_manager, window->id); if (view) { space_manager_untile_window(&g_space_manager, view, window); window_manager_remove_managed_window(&g_window_manager, window->id); @@ -363,6 +366,9 @@ static EVENT_CALLBACK(EVENT_HANDLER_APPLICATION_VISIBLE) for (int i = 0; i < window_count; ++i) { struct ax_window *window = window_list[i]; if (window) { + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window->id); + if (group && group->active_window_id != window->id) continue; + if (window_manager_should_manage_window(window)) { struct view *view = space_manager_tile_window_on_space_with_insertion_point(&g_space_manager, window, window_space(window), prev_window_id); window_manager_add_managed_window(&g_window_manager, window, view); @@ -396,7 +402,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_APPLICATION_HIDDEN) border_window_hide(window); - struct view *view = window_manager_find_managed_window(&g_window_manager, window); + struct view *view = window_manager_find_managed_window(&g_window_manager, window->id); if (view) { space_manager_untile_window(&g_space_manager, view, window); window_manager_remove_managed_window(&g_window_manager, window->id); @@ -423,6 +429,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_CREATED) if (!application) return EVENT_FAILURE; struct ax_window *window = window_create(application, CFRetain(context), window_id); + window_manager_populate_window_group(&g_window_manager, window); window_manager_apply_rules_to_window(&g_space_manager, &g_window_manager, window); window_manager_set_window_opacity(&g_window_manager, window, g_window_manager.normal_window_opacity); window_manager_purify_window(&g_window_manager, window); @@ -445,16 +452,23 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_CREATED) } } - if (window_manager_should_manage_window(window)) { - struct view *view = space_manager_tile_window_on_space(&g_space_manager, window, window_space(window)); - window_manager_add_managed_window(&g_window_manager, window, view); - } - - if (window_manager_find_lost_focused_event(&g_window_manager, window->id)) { + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window->id); + if (group) { struct event *event; - event_create(event, WINDOW_FOCUSED, (void *)(intptr_t) window->id); + event_create(event, WINDOW_MAIN_CHANGED, (void *)(intptr_t) window->id); event_loop_post(&g_event_loop, event); - window_manager_remove_lost_focused_event(&g_window_manager, window->id); + } else { + if (window_manager_should_manage_window(window)) { + struct view *view = space_manager_tile_window_on_space(&g_space_manager, window, window_space(window)); + window_manager_add_managed_window(&g_window_manager, window, view); + } + + if (window_manager_find_lost_focused_event(&g_window_manager, window->id)) { + struct event *event; + event_create(event, WINDOW_FOCUSED, (void *)(intptr_t) window->id); + event_loop_post(&g_event_loop, event); + window_manager_remove_lost_focused_event(&g_window_manager, window->id); + } } } else { debug("%s: could not observe %s %d\n", __FUNCTION__, window->application->name, window->id); @@ -480,11 +494,37 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_DESTROYED) if (g_mouse_state.window == window) g_mouse_state.window = NULL; - struct view *view = window_manager_find_managed_window(&g_window_manager, window); - if (view) { - space_manager_untile_window(&g_space_manager, view, window); - window_manager_remove_managed_window(&g_window_manager, window->id); - window_manager_purify_window(&g_window_manager, window); + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window->id); + if (group) { + window_group_remove_window(group, window->id); + window_manager_remove_grouped_window(&g_window_manager, window->id); + + uint32_t new_wid = (buf_len(group->window_list) == 1) ? group->window_list[0] : window_group_find_active_window(group); + struct ax_window *new_window = window_manager_find_window(&g_window_manager, new_wid); + assert(new_window); + + struct view *view = window_manager_find_managed_window(&g_window_manager, group->active_window_id); + if (view) { + window_manager_remove_managed_window(&g_window_manager, group->active_window_id); + window_manager_add_managed_window(&g_window_manager, new_window, view); + struct window_node *node = view_find_window_node(view->root, group->active_window_id); + node->window_id = new_window->id; + } + + if (buf_len(group->window_list) == 1) { + window_group_remove_window(group, new_window->id); + window_manager_remove_grouped_window(&g_window_manager, new_window->id); + window_group_destroy(group); + } else { + group->active_window_id = new_window->id; + } + } else { + struct view *view = window_manager_find_managed_window(&g_window_manager, window->id); + if (view) { + space_manager_untile_window(&g_space_manager, view, window); + window_manager_remove_managed_window(&g_window_manager, window->id); + window_manager_purify_window(&g_window_manager, window); + } } return EVENT_SUCCESS; @@ -543,6 +583,65 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_FOCUSED) return EVENT_SUCCESS; } +static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MAIN_CHANGED) +{ + uint32_t window_id = (uint32_t)(intptr_t) context; + struct ax_window *window = window_manager_find_window(&g_window_manager, window_id); + if (!window) return EVENT_FAILURE; + + 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__, window_id); + return EVENT_SUCCESS; + } + + if (window_manager_find_lost_focused_event(&g_window_manager, window->id)) { + return EVENT_FAILURE; + } + + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window->id); + if (!group) return EVENT_FAILURE; + + printf("%s: %s %d\n", __FUNCTION__, window->application->name, window->id); + struct ax_window *active_window = window_manager_find_window(&g_window_manager, group->active_window_id); + assert(active_window); + + if (active_window == window) { + struct ax_window *focused_window = window_manager_find_window(&g_window_manager, g_window_manager.focused_window_id); + if (focused_window && focused_window != window) { + border_window_deactivate(focused_window); + window_manager_set_window_opacity(&g_window_manager, focused_window, g_window_manager.normal_window_opacity); + } + } else { + border_window_deactivate(active_window); + border_window_hide(active_window); + window_manager_set_window_opacity(&g_window_manager, active_window, g_window_manager.normal_window_opacity); + + struct view *view = window_manager_find_managed_window(&g_window_manager, group->active_window_id); + if (view) { + window_manager_remove_managed_window(&g_window_manager, group->active_window_id); + window_manager_add_managed_window(&g_window_manager, window, view); + struct window_node *node = view_find_window_node(view->root, group->active_window_id); + node->window_id = window->id; + } + + group->active_window_id = window->id; + } + + g_window_manager.focused_window_id = window->id; + g_window_manager.focused_window_psn = window->application->psn; + + border_window_activate(window); + window_manager_set_window_opacity(&g_window_manager, window, g_window_manager.active_window_opacity); + + if (g_mouse_state.ffm_window_id != window->id) { + window_manager_center_mouse(&g_window_manager, window); + } else { + g_mouse_state.ffm_window_id = 0; + } + + return EVENT_SUCCESS; +} + static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MOVED) { uint32_t window_id = (uint32_t)(intptr_t) context; @@ -559,7 +658,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MOVED) debug("%s: %s %d\n", __FUNCTION__, window->application->name, window->id); #if 0 - struct view *view = window_manager_find_managed_window(&g_window_manager, window); + struct view *view = window_manager_find_managed_window(&g_window_manager, window->id); if (view) view_flush(view); #endif @@ -584,7 +683,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_RESIZED) debug("%s: %s %d\n", __FUNCTION__, window->application->name, window->id); #if 0 - struct view *view = window_manager_find_managed_window(&g_window_manager, window); + struct view *view = window_manager_find_managed_window(&g_window_manager, window->id); if (view) view_flush(view); #endif @@ -612,7 +711,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MINIMIZED) g_window_manager.last_window_id = g_window_manager.focused_window_id; } - struct view *view = window_manager_find_managed_window(&g_window_manager, window); + struct view *view = window_manager_find_managed_window(&g_window_manager, window->id); if (view) { space_manager_untile_window(&g_space_manager, view, window); window_manager_remove_managed_window(&g_window_manager, window->id); @@ -635,6 +734,10 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_DEMINIMIZED) } window->is_minimized = false; + + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window_id); + if (group && group->active_window_id != window_id) return EVENT_FAILURE; + border_window_show(window); if (space_manager_is_window_on_active_space(window)) { @@ -831,7 +934,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_MOUSE_UP) return EVENT_SUCCESS; } - struct view *view = window_manager_find_managed_window(&g_window_manager, g_mouse_state.window); + struct view *view = window_manager_find_managed_window(&g_window_manager, g_mouse_state.window->id); if ((g_mouse_state.current_action != MOUSE_MODE_RESIZE) && (view && view->layout == VIEW_BSP)) { CGRect frame = window_ax_frame(g_mouse_state.window); @@ -1049,12 +1152,14 @@ static EVENT_CALLBACK(EVENT_HANDLER_MISSION_CONTROL_EXIT) while (bucket) { if (bucket->value) { struct ax_window *window = bucket->value; - if ((!window->application->is_hidden) && - (!window->is_minimized)) { - border_window_show(window); - } - } + if (window->application->is_hidden || window->is_minimized) goto next; + + struct window_group *group = window_manager_find_grouped_window(&g_window_manager, window->id); + if (group && group->active_window_id != window->id) goto next; + border_window_show(window); + } +next: bucket = bucket->next; } } diff --git a/src/event.h b/src/event.h index ec9536e0..078e5aa2 100644 --- a/src/event.h +++ b/src/event.h @@ -14,6 +14,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_APPLICATION_HIDDEN); static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_CREATED); static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_DESTROYED); static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_FOCUSED); +static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MAIN_CHANGED); static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MOVED); static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_RESIZED); static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_MINIMIZED); @@ -61,6 +62,7 @@ enum event_type WINDOW_CREATED, WINDOW_DESTROYED, WINDOW_FOCUSED, + WINDOW_MAIN_CHANGED, WINDOW_MOVED, WINDOW_RESIZED, WINDOW_MINIMIZED, @@ -103,6 +105,7 @@ static const char *event_type_str[] = [WINDOW_CREATED] = "window_created", [WINDOW_DESTROYED] = "window_destroyed", [WINDOW_FOCUSED] = "window_focused", + [WINDOW_MAIN_CHANGED] = "window_main_changed", [WINDOW_MOVED] = "window_moved", [WINDOW_RESIZED] = "window_resized", [WINDOW_MINIMIZED] = "window_minimized", @@ -143,6 +146,7 @@ static event_callback *event_handler[] = [WINDOW_CREATED] = EVENT_HANDLER_WINDOW_CREATED, [WINDOW_DESTROYED] = EVENT_HANDLER_WINDOW_DESTROYED, [WINDOW_FOCUSED] = EVENT_HANDLER_WINDOW_FOCUSED, + [WINDOW_MAIN_CHANGED] = EVENT_HANDLER_WINDOW_MAIN_CHANGED, [WINDOW_MOVED] = EVENT_HANDLER_WINDOW_MOVED, [WINDOW_RESIZED] = EVENT_HANDLER_WINDOW_RESIZED, [WINDOW_MINIMIZED] = EVENT_HANDLER_WINDOW_MINIMIZED, diff --git a/src/manifest.m b/src/manifest.m index 1ae5447e..44e21a32 100644 --- a/src/manifest.m +++ b/src/manifest.m @@ -49,6 +49,7 @@ #include "view.h" #include "border.h" #include "window.h" +#include "window_group.h" #include "application.h" #include "process_manager.h" #include "display_manager.h" @@ -68,6 +69,7 @@ #include "view.c" #include "border.c" #include "window.c" +#include "window_group.c" #include "application.c" #include "process_manager.c" #include "display_manager.c" diff --git a/src/window_group.c b/src/window_group.c new file mode 100644 index 00000000..791626df --- /dev/null +++ b/src/window_group.c @@ -0,0 +1,65 @@ +#include "window_group.h" + +extern uint32_t g_group_id_seed; + +uint32_t window_group_find_active_window(struct window_group *group) +{ + uint32_t window_id = 0; + CFTypeRef window_ref = NULL; + + AXUIElementCopyAttributeValue(group->ref, kAXWindowAttribute, &window_ref); + if (!window_ref) goto out; + + window_id = ax_window_id(window_ref); + CFRelease(window_ref); + +out: + return window_id; +} + +bool window_group_remove_window(struct window_group *group, uint32_t wid) +{ + for (int i = 0; i < buf_len(group->window_list); ++i) { + if (group->window_list[i] == wid) { + buf_del(group->window_list, i); + return true; + } + } + + return false; +} + +bool window_group_find_window(struct window_group *group, uint32_t wid) +{ + for (int i = 0; i < buf_len(group->window_list); ++i) { + if (group->window_list[i] == wid) { + return true; + } + } + + return false; +} + +void window_group_add_window(struct window_group *group, uint32_t wid) +{ + buf_push(group->window_list, wid); +} + +struct window_group *window_group_create(AXUIElementRef ref) +{ + struct window_group *group = malloc(sizeof(struct window_group)); + memset(group, 0, sizeof(struct window_group)); + + group->id = ++g_group_id_seed; + group->window_list = NULL; + group->ref = ref; + + return group; +} + +void window_group_destroy(struct window_group *group) +{ + CFRelease(group->ref); + buf_free(group->window_list); + free(group); +} diff --git a/src/window_group.h b/src/window_group.h new file mode 100644 index 00000000..1966dea3 --- /dev/null +++ b/src/window_group.h @@ -0,0 +1,21 @@ +#ifndef WINDOW_GROUP_H +#define WINDOW_GROUP_H + +struct window_group +{ + AXUIElementRef ref; + uint32_t id; + uint32_t *window_list; + uint32_t active_window_id; +}; + +static uint32_t g_group_id_seed; + +uint32_t window_group_find_active_window(struct window_group *group); +bool window_group_remove_window(struct window_group *group, uint32_t wid); +bool window_group_find_window(struct window_group *group, uint32_t wid); +void window_group_add_window(struct window_group *group, uint32_t wid); +struct window_group *window_group_create(AXUIElementRef ref); +void window_group_destroy(struct window_group *group); + +#endif diff --git a/src/window_manager.c b/src/window_manager.c index f9cad61a..a306fca0 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -208,6 +208,21 @@ void window_manager_center_mouse(struct window_manager *wm, struct ax_window *wi CGWarpMouseCursorPosition(center); } +struct window_group *window_manager_find_grouped_window(struct window_manager *wm, uint32_t wid) +{ + return table_find(&wm->grouped_window, &wid); +} + +void window_manager_remove_grouped_window(struct window_manager *wm, uint32_t wid) +{ + table_remove(&wm->grouped_window, &wid); +} + +void window_manager_add_grouped_window(struct window_manager *wm, uint32_t wid, struct window_group *group) +{ + table_add(&wm->grouped_window, &wid, group); +} + bool window_manager_should_manage_window(struct ax_window *window) { if (window->rule_manage) return true; @@ -219,9 +234,9 @@ bool window_manager_should_manage_window(struct ax_window *window) (!window->is_floating)); } -struct view *window_manager_find_managed_window(struct window_manager *wm, struct ax_window *window) +struct view *window_manager_find_managed_window(struct window_manager *wm, uint32_t wid) { - return table_find(&wm->managed_window, &window->id); + return table_find(&wm->managed_window, &wid); } void window_manager_remove_managed_window(struct window_manager *wm, uint32_t wid) @@ -238,7 +253,7 @@ void window_manager_add_managed_window(struct window_manager *wm, struct ax_wind void window_manager_move_window_relative(struct window_manager *wm, struct ax_window *window, int type, float dx, float dy) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) return; if (type == TYPE_REL) { @@ -252,7 +267,7 @@ void window_manager_move_window_relative(struct window_manager *wm, struct ax_wi void window_manager_resize_window_relative(struct window_manager *wm, struct ax_window *window, int direction, float dx, float dy) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) { struct window_node *x_fence = NULL; struct window_node *y_fence = NULL; @@ -461,7 +476,7 @@ void window_manager_purify_window(struct window_manager *wm, struct ax_window *w if (wm->purify_mode == PURIFY_DISABLED) { value = 1; } else if (wm->purify_mode == PURIFY_MANAGED) { - value = window_manager_find_managed_window(wm, window) ? 0 : 1; + value = window_manager_find_managed_window(wm, window->id) ? 0 : 1; } else if (wm->purify_mode == PURIFY_ALWAYS) { value = 0; } @@ -554,7 +569,7 @@ static struct ax_window *window_manager_find_closest_window_for_direction_in_win struct ax_window *window_manager_find_closest_managed_window_in_direction(struct window_manager *wm, struct ax_window *window, int direction) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (!view) return NULL; uint32_t *view_window_list = view_find_window_list(view); @@ -858,6 +873,7 @@ void window_manager_add_application_windows(struct space_manager *sm, struct win continue; } + window_manager_populate_window_group(wm, window); window_manager_apply_rules_to_window(sm, wm, window); window_manager_set_window_opacity(wm, window, wm->normal_window_opacity); window_manager_purify_window(wm, window); @@ -889,6 +905,74 @@ void window_manager_add_application_windows(struct space_manager *sm, struct win free(window_list); } +void window_manager_populate_window_group(struct window_manager *wm, struct ax_window *window) +{ + CFTypeRef tab_group_ref = NULL; + CFTypeRef window_children = NULL; + CFTypeRef tab_list_ref = NULL; + struct window_group *group = NULL; + + printf("populate window group %d\n", window->id); + AXUIElementCopyAttributeValue(window->ref, kAXChildrenAttribute, &window_children); + if (!window_children) { + printf("no children found for %d\n", window->id); + goto out; + } + + for (int i = 0; i < CFArrayGetCount(window_children); ++i) { + AXUIElementRef window_child = CFArrayGetValueAtIndex(window_children, i); + + CFTypeRef window_child_role = NULL; + AXUIElementCopyAttributeValue(window_child, kAXRoleAttribute, &window_child_role); + if (!window_child_role) continue; + + bool match = CFEqual(kAXTabGroupRole, window_child_role); + CFRelease(window_child_role); + + if (match) { + tab_group_ref = window_child; + break; + } + } + + if (!tab_group_ref) { + printf("no tab group found for %d\n", window->id); + goto err; + } + + AXUIElementCopyAttributeValue(tab_group_ref, kAXTabsAttribute, &tab_list_ref); + if (!tab_list_ref) { + printf("no tab list reference found for %d\n", window->id); + goto err; + } + + int tab_count = CFArrayGetCount(tab_list_ref); + if (tab_count < 2) { + printf("tab count less than 2 found for %d\n", window->id); + goto err; + } + + assert(psn_equals(&wm->focused_window_psn, &window->application->psn)); + group = window_manager_find_grouped_window(wm, wm->focused_window_id); + + if (!group) { + group = window_group_create(CFRetain(tab_group_ref)); + group->active_window_id = wm->focused_window_id; + window_group_add_window(group, wm->focused_window_id); + window_manager_add_grouped_window(wm, wm->focused_window_id, group); + } + + window_group_add_window(group, window->id); + window_manager_add_grouped_window(wm, window->id, group); + + CFRelease(tab_list_ref); + +err: + CFRelease(window_children); + +out:; +} + void window_manager_set_window_insertion(struct space_manager *sm, struct window_manager *wm, struct ax_window *window, int direction) { struct view *view = space_manager_find_view(sm, space_manager_active_space()); @@ -995,7 +1079,7 @@ void window_manager_send_window_to_display(struct space_manager *sm, struct wind uint64_t dst_sid = display_space_id(did); if (src_sid == dst_sid) return; - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) { space_manager_untile_window(sm, view, window); window_manager_remove_managed_window(wm, window->id); @@ -1024,7 +1108,7 @@ void window_manager_send_window_to_space(struct space_manager *sm, struct window uint64_t src_sid = window_space(window); if (src_sid == dst_sid) return; - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) { space_manager_untile_window(sm, view, window); window_manager_remove_managed_window(wm, window->id); @@ -1052,7 +1136,7 @@ void window_manager_send_window_to_space(struct space_manager *sm, struct window void window_manager_apply_grid(struct space_manager *sm, struct window_manager *wm, struct ax_window *window, unsigned r, unsigned c, unsigned x, unsigned y, unsigned w, unsigned h) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) return; uint32_t did = window_display_id(window); @@ -1126,7 +1210,7 @@ void window_manager_toggle_window_float(struct space_manager *sm, struct window_ window_manager_add_managed_window(wm, window, view); } } else { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) { space_manager_untile_window(sm, view, window); window_manager_remove_managed_window(wm, window->id); @@ -1147,7 +1231,7 @@ void window_manager_toggle_window_sticky(struct space_manager *sm, struct window window_manager_add_managed_window(wm, window, view); } } else { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) { space_manager_untile_window(sm, view, window); window_manager_remove_managed_window(wm, window->id); @@ -1164,7 +1248,7 @@ void window_manager_toggle_window_native_fullscreen(struct space_manager *sm, st return; } - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (view) { space_manager_untile_window(sm, view, window); window_manager_remove_managed_window(wm, window->id); @@ -1175,7 +1259,7 @@ void window_manager_toggle_window_native_fullscreen(struct space_manager *sm, st void window_manager_toggle_window_parent(struct space_manager *sm, struct window_manager *wm, struct ax_window *window) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (!view || view->layout != VIEW_BSP) return; struct window_node *node = view_find_window_node(view->root, window->id); @@ -1194,7 +1278,7 @@ void window_manager_toggle_window_parent(struct space_manager *sm, struct window void window_manager_toggle_window_fullscreen(struct space_manager *sm, struct window_manager *wm, struct ax_window *window) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); if (!view || view->layout != VIEW_BSP) return; struct window_node *node = view_find_window_node(view->root, window->id); @@ -1213,7 +1297,7 @@ void window_manager_toggle_window_fullscreen(struct space_manager *sm, struct wi void window_manager_toggle_window_border(struct window_manager *wm, struct ax_window *window) { - struct view *view = window_manager_find_managed_window(wm, window); + struct view *view = window_manager_find_managed_window(wm, window->id); struct window_node *node = view ? view_find_window_node(view->root, window->id) : NULL; if (window->border.enabled) { @@ -1271,7 +1355,7 @@ void window_manager_check_for_windows_on_space(struct space_manager *sm, struct if (!window || !window_manager_should_manage_window(window)) continue; if (window->is_minimized || window->application->is_hidden) continue; - struct view *existing_view = window_manager_find_managed_window(wm, window); + struct view *existing_view = window_manager_find_managed_window(wm, window->id); if (existing_view && existing_view->sid != sid) { space_manager_untile_window(sm, existing_view, window); window_manager_remove_managed_window(wm, window->id); @@ -1301,7 +1385,7 @@ void window_manager_handle_display_add_and_remove(struct space_manager *sm, stru struct ax_window *window = window_manager_find_window(wm, window_list[i]); if (!window || !window_manager_should_manage_window(window)) continue; - struct view *existing_view = window_manager_find_managed_window(wm, window); + struct view *existing_view = window_manager_find_managed_window(wm, window->id); if (existing_view && existing_view->layout == VIEW_BSP && existing_view->sid != space_list[0]) { space_manager_untile_window(sm, existing_view, window); window_manager_remove_managed_window(wm, window->id); @@ -1348,6 +1432,7 @@ void window_manager_init(struct window_manager *wm) table_init(&wm->application, 150, hash_wm, compare_wm); table_init(&wm->window, 150, hash_wm, compare_wm); + table_init(&wm->grouped_window, 150, hash_wm, compare_wm); table_init(&wm->managed_window, 150, hash_wm, compare_wm); table_init(&wm->window_lost_focused_event, 150, hash_wm, compare_wm); table_init(&wm->application_lost_front_switched_event, 150, hash_wm, compare_wm); diff --git a/src/window_manager.h b/src/window_manager.h index ea8d4090..b965f6e1 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -52,6 +52,7 @@ struct window_manager AXUIElementRef system_element; struct table application; struct table window; + struct table grouped_window; struct table managed_window; struct table window_lost_focused_event; struct table application_lost_front_switched_event; @@ -97,7 +98,7 @@ void window_manager_focus_window_without_raise(uint32_t window_id); void window_manager_focus_window_with_raise(uint32_t window_id); struct ax_window *window_manager_focused_window(struct window_manager *wm); struct ax_application *window_manager_focused_application(struct window_manager *wm); -struct view *window_manager_find_managed_window(struct window_manager *wm, struct ax_window *window); +struct view *window_manager_find_managed_window(struct window_manager *wm, uint32_t wid); void window_manager_remove_managed_window(struct window_manager *wm, uint32_t wid); void window_manager_add_managed_window(struct window_manager *wm, struct ax_window *window, struct view *view); bool window_manager_find_lost_front_switched_event(struct window_manager *wm, pid_t pid); @@ -106,6 +107,9 @@ void window_manager_add_lost_front_switched_event(struct window_manager *wm, pid bool window_manager_find_lost_focused_event(struct window_manager *wm, uint32_t window_id); void window_manager_remove_lost_focused_event(struct window_manager *wm, uint32_t window_id); void window_manager_add_lost_focused_event(struct window_manager *wm, uint32_t window_id); +struct window_group *window_manager_find_grouped_window(struct window_manager *wm, uint32_t wid); +void window_manager_remove_grouped_window(struct window_manager *wm, uint32_t wid); +void window_manager_add_grouped_window(struct window_manager *wm, uint32_t wid, struct window_group *group); struct ax_window *window_manager_find_window(struct window_manager *wm, uint32_t window_id); void window_manager_remove_window(struct window_manager *wm, uint32_t window_id); void window_manager_add_window(struct window_manager *wm, struct ax_window *window); @@ -113,6 +117,7 @@ struct ax_application *window_manager_find_application(struct window_manager *wm void window_manager_remove_application(struct window_manager *wm, pid_t pid); void window_manager_add_application(struct window_manager *wm, struct ax_application *application); struct ax_window **window_manager_find_application_windows(struct window_manager *wm, struct ax_application *application, int *count); +void window_manager_populate_window_group(struct window_manager *wm, struct ax_window *window); void window_manager_move_window_relative(struct window_manager *wm, struct ax_window *window, int type, float dx, float dy); void window_manager_resize_window_relative(struct window_manager *wm, struct ax_window *window, int direction, float dx, float dy); void window_manager_set_purify_mode(struct window_manager *wm, enum purify_mode mode);