From 45e18e0d7b65b4dab403f79986c7558ee1ea8f2f Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Mon, 12 Sep 2022 18:38:12 +0200 Subject: [PATCH] #148 transactional animations --- examples/yabairc | 2 +- makefile | 2 +- src/osax/payload.m | 101 ++++++++++++++++++++++++++++++++++++++++++- src/sa.h | 3 ++ src/sa.m | 23 ++++++++++ src/view.c | 18 +++++--- src/view.h | 6 +++ src/window_manager.c | 31 +++++++++++++ src/window_manager.h | 1 + 9 files changed, 178 insertions(+), 9 deletions(-) diff --git a/examples/yabairc b/examples/yabairc index 11049f02..c5aaa399 100755 --- a/examples/yabairc +++ b/examples/yabairc @@ -24,7 +24,7 @@ yabai -m config \ window_shadow on \ window_opacity off \ window_opacity_duration 0.0 \ - window_animation_duration 0.10 \ + window_animation_duration 0.35 \ active_window_opacity 1.0 \ normal_window_opacity 0.90 \ window_border off \ diff --git a/makefile b/makefile index 1f40d5b1..6b22bf63 100644 --- a/makefile +++ b/makefile @@ -21,7 +21,7 @@ install: clean-build $(BINS) $(OSAX_SRC): $(OSAX_PATH)/loader.m $(OSAX_PATH)/payload.m xcrun clang $(OSAX_PATH)/loader.m -shared -O2 -mmacosx-version-min=10.13 -arch x86_64 -o $(OSAX_PATH)/loader -framework Foundation - xcrun clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -arch x86_64 -arch arm64e -o $(OSAX_PATH)/payload -framework Foundation -framework Carbon + xcrun clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -arch x86_64 -arch arm64e -o $(OSAX_PATH)/payload -framework Foundation -framework Carbon -F/System/Library/PrivateFrameworks -framework SkyLight xcrun clang $(OSAX_PATH)/mach_loader.m -O2 -mmacosx-version-min=10.13 -arch x86_64 -arch arm64e -o $(OSAX_PATH)/mach_loader -framework Cocoa xxd -i -a $(OSAX_PATH)/loader $(OSAX_PATH)/loader_bin.c xxd -i -a $(OSAX_PATH)/payload $(OSAX_PATH)/payload_bin.c diff --git a/src/osax/payload.m b/src/osax/payload.m index 1abb1fa3..1e8854ad 100644 --- a/src/osax/payload.m +++ b/src/osax/payload.m @@ -67,6 +67,18 @@ extern void CGSShowSpaces(int cid, CFArrayRef spaces); extern void CGSHideSpaces(int cid, CFArrayRef spaces); +extern CFTypeRef SLSTransactionCreate(int cid); +extern CGError SLSTransactionCommit(CFTypeRef transaction, int unknown); // passed 0 as unknown +extern CGError SLSTransactionSetWindowTransform(CFTypeRef transaction, uint32_t wid, int unknown, int unknown2, CGAffineTransform t); +extern CGError SLSTransactionMoveWindowWithGroup(CFTypeRef transaction, uint32_t wid, CGPoint point); + +struct window_animation_context +{ + uint32_t wid; + float x, y, w, h; + CGRect frame; +}; + struct window_fade_context { pthread_t thread; @@ -614,8 +626,7 @@ static void do_window_animate_frame(char *message) CGRect frame = {}; CGSGetWindowBounds(_connection, wid, &frame); - CGAffineTransform original_transform = CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y); - CGSSetWindowTransform(_connection, wid, original_transform); + CGSSetWindowTransform(_connection, wid, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y)); int frame_duration = 4; int total_duration = (int)(animation_duration * 1000.0f); @@ -646,12 +657,94 @@ static void do_window_animate_frame(char *message) CGSSetWindowTransform(_connection, wid, CGAffineTransformConcat(transform, scale)); } +static void do_window_list_animate(char *message) +{ + int window_count; + unpack(message, window_count); + if (!window_count) return; + + struct window_animation_context *window_list = malloc(sizeof(struct window_animation_context) * window_count); + for (int i = 0; i < window_count; ++i) { + unpack(message, window_list[i].wid); + unpack(message, window_list[i].x); + unpack(message, window_list[i].y); + unpack(message, window_list[i].w); + unpack(message, window_list[i].h); + } + + float animation_duration; + unpack(message, animation_duration); + + CFTypeRef transaction1 = SLSTransactionCreate(_connection); + for (int i = 0; i < window_count; ++i) { + CGSGetWindowBounds(_connection, window_list[i].wid, &window_list[i].frame); + SLSTransactionSetWindowTransform(transaction1, window_list[i].wid, 0, 0, CGAffineTransformMakeTranslation(-window_list[i].frame.origin.x, -window_list[i].frame.origin.y)); + } + SLSTransactionCommit(transaction1, 0); + CFRelease(transaction1); + + int frame_duration = 4; + int total_duration = (int)(animation_duration * 1000.0f); + int frame_count = (int)(((float) total_duration / (float) frame_duration) + 0.5f); + + int duration = 0; + for (int i = 0; i < frame_count; ++i) { + float t = (float) duration / (float) total_duration; + if (t < 0.0f) t = 0.0f; + if (t > 1.0f) t = 1.0f; + + float mt = ease_in_out_back(t); + + CFTypeRef transaction2 = SLSTransactionCreate(_connection); + for (int j = 0; j < window_count; ++j) { + float target_x = lerp(window_list[j].frame.origin.x, mt, window_list[j].x); + float target_y = lerp(window_list[j].frame.origin.y, mt, window_list[j].y); + float target_w = lerp(window_list[j].frame.size.width, mt, window_list[j].w); + float target_h = lerp(window_list[j].frame.size.height, mt, window_list[j].h); + + CGAffineTransform transform = CGAffineTransformMakeTranslation(-target_x, -target_y); + CGAffineTransform scale = CGAffineTransformMakeScale(window_list[j].frame.size.width / target_w, window_list[j].frame.size.height / target_h); + SLSTransactionSetWindowTransform(transaction2, window_list[j].wid, 0, 0, CGAffineTransformConcat(transform, scale)); + } + SLSTransactionCommit(transaction2, 0); + CFRelease(transaction2); + + duration += frame_duration; + usleep(frame_duration*1000); + } + + CFTypeRef transaction3 = SLSTransactionCreate(_connection); + for (int i = 0; i < window_count; ++i) { + CGAffineTransform transform = CGAffineTransformMakeTranslation(-window_list[i].x, -window_list[i].y); + CGAffineTransform scale = CGAffineTransformMakeScale(window_list[i].frame.size.width / window_list[i].w, window_list[i].frame.size.height / window_list[i].h); + SLSTransactionSetWindowTransform(transaction3, window_list[i].wid, 0, 0, CGAffineTransformConcat(transform, scale)); + } + SLSTransactionCommit(transaction3, 0); + CFRelease(transaction3); + + free(window_list); +} + static void do_window_transform_reset(char *message) { uint32_t wid; unpack(message, wid); if (!wid) return; +#if 0 + float x, y; + unpack(message, x); + unpack(message, y); + + CGRect frame = {}; + CGSGetWindowBounds(_connection, wid, &frame); + + CFTypeRef transaction = SLSTransactionCreate(_connection); + SLSTransactionSetWindowTransform(transaction, wid, 0, 0, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y)); + SLSTransactionMoveWindowWithGroup(transaction, wid, CGPointMake(x, y)); + SLSTransactionCommit(transaction, 0); + CFRelease(transaction); +#else float x, y; unpack(message, x); unpack(message, y); @@ -667,6 +760,7 @@ static void do_window_transform_reset(char *message) CGAffineTransform original_transform = CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y); CGSSetWindowTransform(_connection, wid, original_transform); +#endif } static void do_window_move(char *message) @@ -912,6 +1006,9 @@ static void handle_message(int sockfd, char *message) case 0x0F: { do_window_transform_reset(message); } break; + case 0x10: { + do_window_list_animate(message); + } break; } } diff --git a/src/sa.h b/src/sa.h index c77ff57d..372341fc 100644 --- a/src/sa.h +++ b/src/sa.h @@ -6,6 +6,8 @@ #define PAYLOAD_STATUS_NO_ATTRIB 2 #define PAYLOAD_STATUS_CON_ERROR 3 +struct window_animation; + int scripting_addition_check(void); int scripting_addition_load(void); bool scripting_addition_is_installed(void); @@ -25,6 +27,7 @@ bool scripting_addition_focus_window(uint32_t wid); bool scripting_addition_scale_window(uint32_t wid, float x, float y, float w, float h); bool scripting_addition_animate_frame(uint32_t wid, float x, float y, float w, float h, float duration); bool scripting_addition_reset_transform(uint32_t wid, float x, float y); +bool scripting_addition_animate_window_list(struct window_animation *window_list, int window_count, float duration); extern bool workspace_is_macos_monterey(void); extern bool workspace_is_macos_bigsur(void); diff --git a/src/sa.m b/src/sa.m index accf21d8..f4f48e27 100644 --- a/src/sa.m +++ b/src/sa.m @@ -738,3 +738,26 @@ bool scripting_addition_reset_transform(uint32_t wid, float x, float y) return scripting_addition_send_bytes(bytes, length); } + +bool scripting_addition_animate_window_list(struct window_animation *window_list, int window_count, float duration) +{ + char bytes[0x100]; + + char length = 2; + pack(bytes, window_count, length); + + // NOTE(koekeishiya): We can only send bytes to animate 12 windows for now. + for (int i = 0; i < window_count && i < 12; ++i) { + pack(bytes, window_list[i].window->id, length); + pack(bytes, window_list[i].area.x, length); + pack(bytes, window_list[i].area.y, length); + pack(bytes, window_list[i].area.w, length); + pack(bytes, window_list[i].area.h, length); + } + + pack(bytes, duration, length); + bytes[1] = 0x10; + bytes[0] = length-1; + + return scripting_addition_send_bytes(bytes, length); +} diff --git a/src/view.c b/src/view.c index 26166e93..2f162ba6 100644 --- a/src/view.c +++ b/src/view.c @@ -303,27 +303,35 @@ static void window_node_clear_zoom(struct window_node *node) } } -void window_node_flush(struct window_node *node) +void window_node_capture_windows(struct window_node *node, struct window_animation **window_list) { if (window_node_is_occupied(node)) { for (int i = 0; i < node->window_count; ++i) { struct window *window = window_manager_find_window(&g_window_manager, node->window_list[i]); if (window) { if (node->zoom) { - window_manager_animate_window_frame(window, node->zoom->area.x, node->zoom->area.y, node->zoom->area.w, node->zoom->area.h); + buf_push(*window_list, ((struct window_animation) { .window = window, .area = node->zoom->area })); } else { - window_manager_animate_window_frame(window, node->area.x, node->area.y, node->area.w, node->area.h); + buf_push(*window_list, ((struct window_animation) { .window = window, .area = node->area })); } } } } if (!window_node_is_leaf(node)) { - window_node_flush(node->left); - window_node_flush(node->right); + window_node_capture_windows(node->left, window_list); + window_node_capture_windows(node->right, window_list); } } +void window_node_flush(struct window_node *node) +{ + struct window_animation *window_list = NULL; + window_node_capture_windows(node, &window_list); + window_manager_animate_window_list(window_list, buf_len(window_list)); + buf_free(window_list); +} + bool window_node_contains_window(struct window_node *node, uint32_t window_id) { for (int i = 0; i < node->window_count; ++i) { diff --git a/src/view.h b/src/view.h index d75ec8f8..6bb02ba6 100644 --- a/src/view.h +++ b/src/view.h @@ -11,6 +11,12 @@ struct area float h; }; +struct window_animation +{ + struct window *window; + struct area area; +}; + struct equalize_node { int y_count; diff --git a/src/window_manager.c b/src/window_manager.c index da56bb56..9ec3c6d8 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -450,6 +450,21 @@ void window_manager_resize_window(struct window *window, float width, float heig CFRelease(size_ref); } +void window_manager_animate_window_list(struct window_animation *window_list, int window_count) +{ + if (g_window_manager.window_animation_duration) { + scripting_addition_animate_window_list(window_list, window_count, g_window_manager.window_animation_duration); + for (int i = 0; i < window_count; ++i) { + window_manager_set_window_frame(window_list[i].window, window_list[i].area.x, window_list[i].area.y, window_list[i].area.w, window_list[i].area.h); + scripting_addition_reset_transform(window_list[i].window->id, window_list[i].area.x, window_list[i].area.y); + } + } else { + for (int i = 0; i < window_count; ++i) { + window_manager_set_window_frame(window_list[i].window, window_list[i].area.x, window_list[i].area.y, window_list[i].area.w, window_list[i].area.h); + } + } +} + void window_manager_animate_window_frame(struct window *window, float x, float y, float width, float height) { if (g_window_manager.window_animation_duration) { @@ -1313,8 +1328,16 @@ enum window_op_error window_manager_warp_window(struct space_manager *sm, struct window_node_swap_window_list(a_node, b_node); +#if 0 window_node_flush(a_node); window_node_flush(b_node); +#else + struct window_animation *window_list = NULL; + window_node_capture_windows(a_node, &window_list); + window_node_capture_windows(b_node, &window_list); + window_manager_animate_window_list(window_list, buf_len(window_list)); + buf_free(window_list); +#endif } } else { space_manager_untile_window(sm, a_view, a); @@ -1391,8 +1414,16 @@ enum window_op_error window_manager_swap_window(struct space_manager *sm, struct } } +#if 0 window_node_flush(a_node); window_node_flush(b_node); +#else + struct window_animation *window_list = NULL; + window_node_capture_windows(a_node, &window_list); + window_node_capture_windows(b_node, &window_list); + window_manager_animate_window_list(window_list, buf_len(window_list)); + buf_free(window_list); +#endif return WINDOW_OP_ERROR_SUCCESS; } diff --git a/src/window_manager.h b/src/window_manager.h index 4a82f594..ee86caeb 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -109,6 +109,7 @@ void window_manager_move_window(struct window *window, float x, float y); void window_manager_resize_window(struct window *window, float width, float height); enum window_op_error window_manager_adjust_window_ratio(struct window_manager *wm, struct window *window, int action, float ratio); void window_manager_animate_window_frame(struct window *window, float x, float y, float width, float height); +void window_manager_animate_window_list(struct window_animation *window_list, int window_count); void window_manager_set_window_frame(struct window *window, float x, float y, float width, float height); int window_manager_find_rank_of_window_in_list(uint32_t wid, uint32_t *window_list, int window_count); struct window *window_manager_find_window_on_space_by_rank_filtering_window(struct window_manager *wm, uint64_t sid, int rank, uint32_t filter_wid);