From 76f523026156ffdd478e304342b846f4ed64804e Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Wed, 28 Dec 2022 19:25:03 +0100 Subject: [PATCH] #148 make frame rate configurable --- CHANGELOG.md | 1 + doc/yabai.1 | 7 +++++ doc/yabai.asciidoc | 4 +++ src/manifest.m | 1 + src/message.c | 10 ++++++ src/misc/helpers.h | 74 +++++++++++++++++++++++++++++++------------- src/view.h | 1 + src/window_manager.c | 4 ++- src/window_manager.h | 1 + 9 files changed, 81 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6433c97f..00bbd3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fixed issue with window warping across displays with only a single window tiled at both displays [#1577](https://github.com/koekeishiya/yabai/issues/1577) - Fixed issue preventing window split from being toggled for windwos on an inactive space/display [#1557](https://github.com/koekeishiya/yabai/issues/1557) - Make window zoom persistence configurable (*config window_zoom_persist*) [#1481](https://github.com/koekeishiya/yabai/issues/1481) +- Make frame rate of window animations configurable (*config window_animation_frame_rate*) [#148](https://github.com/koekeishiya/yabai/issues/148) ## [5.0.2] - 2022-12-16 ### Changed diff --git a/doc/yabai.1 b/doc/yabai.1 index f1a872b2..3c70c9d5 100644 --- a/doc/yabai.1 +++ b/doc/yabai.1 @@ -208,6 +208,13 @@ Requires Screen Recording permissions. System Integrity Protection must be partially disabled. .RE .sp +\fBwindow_animation_frame_rate\fP [\fI\fP] +.RS 4 +Frame rate (expressed in frames per second) of window frame animation. +.br +This setting does nothing if \fBwindow_animation_duration\fP is set to 0.0. +.RE +.sp \fBactive_window_opacity\fP [\fI\fP] .RS 4 Opacity of the focused window. diff --git a/doc/yabai.asciidoc b/doc/yabai.asciidoc index 0f0b13b4..4ff8fa50 100644 --- a/doc/yabai.asciidoc +++ b/doc/yabai.asciidoc @@ -152,6 +152,10 @@ Global Settings Requires Screen Recording permissions. + System Integrity Protection must be partially disabled. +*window_animation_frame_rate* ['']:: + Frame rate (expressed in frames per second) of window frame animation. + + This setting does nothing if *window_animation_duration* is set to 0.0. + *active_window_opacity* ['']:: Opacity of the focused window. + System Integrity Protection must be partially disabled. diff --git a/src/manifest.m b/src/manifest.m index cbbabb4d..613bf495 100644 --- a/src/manifest.m +++ b/src/manifest.m @@ -1,6 +1,7 @@ #include #include #include +#include #include #include diff --git a/src/message.c b/src/message.c index a9071d2a..d54630a9 100644 --- a/src/message.c +++ b/src/message.c @@ -30,6 +30,7 @@ extern bool g_verbose; #define COMMAND_CONFIG_OPACITY "window_opacity" #define COMMAND_CONFIG_OPACITY_DURATION "window_opacity_duration" #define COMMAND_CONFIG_ANIMATION_DURATION "window_animation_duration" +#define COMMAND_CONFIG_ANIMATION_FRAME_RATE "window_animation_frame_rate" #define COMMAND_CONFIG_BORDER "window_border" #define COMMAND_CONFIG_BORDER_HIDPI "window_border_hidpi" #define COMMAND_CONFIG_BORDER_BLUR "window_border_blur" @@ -1170,6 +1171,15 @@ 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.token.length, value.token.text, command.length, command.text, domain.length, domain.text); } + } else if (token_equals(command, COMMAND_CONFIG_ANIMATION_FRAME_RATE)) { + struct token_value value = token_to_value(get_token(&message), false); + if (value.type == TOKEN_TYPE_INVALID) { + fprintf(rsp, "%d\n", g_window_manager.window_animation_frame_rate); + } else if (value.type == TOKEN_TYPE_INT && value.int_value) { + g_window_manager.window_animation_frame_rate = value.int_value; + } else { + daemon_fail(rsp, "unknown value '%.*s' given to command '%.*s' for domain '%.*s'\n", value.token.length, value.token.text, command.length, command.text, domain.length, domain.text); + } } else if (token_equals(command, COMMAND_CONFIG_BORDER)) { struct token value = get_token(&message); if (!token_is_valid(value)) { diff --git a/src/misc/helpers.h b/src/misc/helpers.h index 7fde6e3a..1397359d 100644 --- a/src/misc/helpers.h +++ b/src/misc/helpers.h @@ -22,32 +22,64 @@ static const char *layer_str[] = [LAYER_ABOVE] = "above" }; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +static inline uint64_t get_wall_clock(void) +{ + uint64_t absolute = mach_absolute_time(); + Nanoseconds result = AbsoluteToNanoseconds(*(AbsoluteTime *) &absolute); + return *(uint64_t *) &result; +} +#pragma clang diagnostic pop + +static inline float get_seconds_elapsed(uint64_t start, uint64_t end) +{ + float result = ((float)(end - start) / 1000.0f) / 1000000.0f; + return result; +} + static inline float ease_out_cubic(float t) { return 1.0f - powf(1.0f - t, 3.0f); } -#define ANIMATE(animation_connection, animation_duration, easing_function, code_block) \ -{ \ - int frame_duration = 4; \ - int total_duration = (int)(animation_duration * 1000.0f); \ - int frame_count = (int)(((float) total_duration / (float) frame_duration) + 1.0f); \ - \ - for (int frame_index = 1; frame_index <= frame_count; ++frame_index) { \ - float t = (float) frame_index / (float) frame_count; \ - if (t < 0.0f) t = 0.0f; \ - if (t > 1.0f) t = 1.0f; \ - \ - float mt = easing_function(t); \ - CFTypeRef transaction = SLSTransactionCreate(animation_connection); \ - \ - code_block \ - \ - SLSTransactionCommit(transaction, 0); \ - CFRelease(transaction); \ - \ - usleep(frame_duration*1000); \ - } \ +#define ANIMATE_DELAY(current_frame_duration) \ + while (frame_elapsed < current_frame_duration) { \ + uint32_t sleep_ms = (uint32_t)(1000.0f * (current_frame_duration - frame_elapsed)); \ + usleep(sleep_ms * 700.0f); \ + frame_elapsed = get_seconds_elapsed(last_counter, get_wall_clock()); \ + } + +#define ANIMATE(connection, frame_rate, duration, easing_function, code_block) \ +{ \ + float frame_duration = 1.0f / (float)frame_rate; \ + int frame_count = (int)((duration / frame_duration) + 1.0f); \ + uint64_t last_counter = get_wall_clock(); \ + \ + for (int frame_index = 1; frame_index <= frame_count; ++frame_index) { \ + float t = (float) frame_index / (float) frame_count; \ + if (t < 0.0f) t = 0.0f; \ + if (t > 1.0f) t = 1.0f; \ + \ + float mt = easing_function(t); \ + CFTypeRef transaction = SLSTransactionCreate(connection); \ + \ + code_block \ + \ + SLSTransactionCommit(transaction, 0); \ + CFRelease(transaction); \ + \ + float frame_elapsed = get_seconds_elapsed(last_counter, get_wall_clock()); \ + if (frame_elapsed < frame_duration) { \ + ANIMATE_DELAY(frame_duration); \ + } else { \ + int frame_skip = (int)((frame_elapsed / frame_duration) + 0.5f); \ + frame_index += frame_skip; \ + ANIMATE_DELAY(frame_duration * frame_skip); \ + } \ + \ + last_counter = get_wall_clock(); \ + } \ } static inline bool socket_open(int *sockfd) diff --git a/src/view.h b/src/view.h index f52f8186..95340b82 100644 --- a/src/view.h +++ b/src/view.h @@ -39,6 +39,7 @@ struct window_animation_context { int animation_connection; float animation_duration; + int animation_frame_rate; struct window_animation *animation_list; }; diff --git a/src/window_manager.c b/src/window_manager.c index bd62eb6f..b07a88be 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -560,7 +560,7 @@ void *window_manager_animate_window_list_thread_proc(void *data) struct window_animation_context *context = data; int animation_count = buf_len(context->animation_list); - ANIMATE(context->animation_connection, context->animation_duration, ease_out_cubic, { + ANIMATE(context->animation_connection, context->animation_frame_rate, context->animation_duration, ease_out_cubic, { for (int i = 0; i < animation_count; ++i) { if (context->animation_list[i].skip) continue; @@ -600,6 +600,7 @@ void window_manager_animate_window_list_async(struct window_capture *window_list SLSNewConnection(0, &context->animation_connection); context->animation_duration = g_window_manager.window_animation_duration; + context->animation_frame_rate = g_window_manager.window_animation_frame_rate; context->animation_list = NULL; for (int i = 0; i < window_count; ++i) { @@ -2286,6 +2287,7 @@ void window_manager_init(struct window_manager *wm) wm->normal_window_opacity = 1.0f; wm->window_opacity_duration = 0.0f; wm->window_animation_duration = 0.0f; + wm->window_animation_frame_rate = 120; wm->insert_feedback_windows = NULL; wm->insert_feedback_color = rgba_color_from_hex(0xffd75f5f); wm->active_border_color = rgba_color_from_hex(0xff775759); diff --git a/src/window_manager.h b/src/window_manager.h index 1ba116ee..6663cd7d 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -91,6 +91,7 @@ struct window_manager float normal_window_opacity; float window_opacity_duration; float window_animation_duration; + int window_animation_frame_rate; uint32_t *insert_feedback_windows; float border_resolution; bool border_blur;