From cd320e05c14196598664f30995dc8e52fa420810 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 16 Jul 2024 19:11:00 +0530 Subject: [PATCH] Implement simple easing curves --- kitty/animation.c | 37 +++++++++++++++++++++++++++++++++++++ kitty/animation.h | 28 ++++++++++++++++++++++++++++ kitty/child-monitor.c | 25 ++++++++++++++----------- kitty/state.h | 3 ++- 4 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 kitty/animation.c create mode 100644 kitty/animation.h diff --git a/kitty/animation.c b/kitty/animation.c new file mode 100644 index 000000000..af0cbff80 --- /dev/null +++ b/kitty/animation.c @@ -0,0 +1,37 @@ +/* + * animation.c + * Copyright (C) 2024 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "animation.h" +#include +#include "data-types.h" + +static double +unit_value(double x) { return MAX(0., MIN(x, 1.)); } + +double +linear_easing_curve(easing_curve_parameters p, double val) { + for (size_t i = p.count - 1; i-- > 0;) if (p.positions[i] <= val) return p.params[i]; + return p.params[0]; +} + +double +cubic_bezier_easing_curve(easing_curve_parameters p, double t) { + const double u = 1. - t, uu = u * u, uuu = uu * u, tt = t * t, ttt = tt * t; + // p0 is start, p3 is end. p1, p2 are control points + return uuu * p.params[0] + 3 * uu * t * p.params[1] + 3 * u * tt * p.params[2] + ttt * p.params[3]; +} + +double +apply_easing_curve(const Animation *a, double val) { + if (a->first_half.curve) { + if (a->second_half.curve) { + if (val <= 0.5) return unit_value(a->first_half.curve(a->first_half.params, 2 * val)); + return unit_value(a->second_half.curve(a->second_half.params, 2 * (val - 0.5))); + } else return unit_value(a->first_half.curve(a->first_half.params, val)); + } else return (val <= 0.5) ? 1. : 0.; +} + diff --git a/kitty/animation.h b/kitty/animation.h new file mode 100644 index 000000000..d21261dd7 --- /dev/null +++ b/kitty/animation.h @@ -0,0 +1,28 @@ +/* + * animation.h + * Copyright (C) 2024 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#pragma once + +#include + +typedef struct easing_curve_parameters { + size_t count; + const double *params, *positions; +} easing_curve_parameters; + +typedef double(*easing_curve)(easing_curve_parameters, double); + +typedef struct Animation { + struct { + easing_curve_parameters params; + easing_curve curve; + } first_half, second_half; +} Animation; + +double linear_easing_curve(easing_curve_parameters, double); +double cubic_bezier_easing_curve(easing_curve_parameters, double); +double apply_easing_curve(const Animation *a, double t /* must be between 0 and 1*/); diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 039ad93c9..e214d8de3 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -12,6 +12,7 @@ #include "screen.h" #include "fonts.h" #include "monotonic.h" +#include "animation.h" #include #include #include @@ -673,18 +674,20 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return cursor_needs_render(w); monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time; bool cursor_blinking = OPT(cursor_blink_interval) > 0 && !cursor->non_blinking && os_window->is_focused && (OPT(cursor_stop_blinking_after) == 0 || time_since_start_blink <= OPT(cursor_stop_blinking_after)); - bool do_draw_cursor = true; - if (cursor_blinking) { - int t = monotonic_t_to_ms(time_since_start_blink); - int d = monotonic_t_to_ms(OPT(cursor_blink_interval)); - int n = t / d; - do_draw_cursor = n % 2 == 0 ? true : false; - monotonic_t bucket = ms_to_monotonic_t((monotonic_t)(n + 1) * d); - monotonic_t delay = bucket - time_since_start_blink; - set_maximum_wait(delay); - } - if (!do_draw_cursor) { ans->opacity = 0; return cursor_needs_render(w); } ans->opacity = 1; + if (cursor_blinking) { + if (OPT(animation.cursor).first_half.curve) { + monotonic_t den = OPT(cursor_blink_interval) * 2; + monotonic_t time_into_cycle = time_since_start_blink % den; + double frac_into_cycle = (double)time_into_cycle / (double)den; + ans->opacity = (float)apply_easing_curve(&OPT(animation.cursor), frac_into_cycle); + set_maximum_wait(ms_to_monotonic_t(75)); + } else { + monotonic_t n = time_since_start_blink / OPT(cursor_blink_interval); + ans->opacity = n % 2 == 0 ? 1 : 0; + set_maximum_wait((n + 1) * OPT(cursor_blink_interval) - time_since_start_blink); + } + } ans->shape = cursor->shape ? cursor->shape : OPT(cursor_shape); ans->is_focused = os_window->is_focused; return cursor_needs_render(w); diff --git a/kitty/state.h b/kitty/state.h index b633f10c9..8039c7227 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -5,7 +5,7 @@ */ #pragma once -#include "data-types.h" +#include "animation.h" #include "screen.h" #include "monotonic.h" #include "window_logo.h" @@ -118,6 +118,7 @@ typedef struct { hb_feature_t *features; } *entries; } font_features; + struct { Animation cursor; } animation; } Options; typedef struct WindowLogoRenderData {