diff --git a/kitty/animation.c b/kitty/animation.c index af0cbff80..264d43da7 100644 --- a/kitty/animation.c +++ b/kitty/animation.c @@ -5,33 +5,143 @@ * Distributed under terms of the GPL3 license. */ -#include "animation.h" -#include #include "data-types.h" +#define ANIMATION_INTERNAL_API + +typedef struct easing_curve_parameters { + size_t count; + double extra0, extra1, extra2, extra3; + const double *params, *positions; +} easing_curve_parameters; + +typedef double(*easing_curve)(easing_curve_parameters*, double); + +typedef struct animation_function { + easing_curve_parameters params; + easing_curve curve; + double y_at_start, y_size; +} animation_function; + + +typedef struct Animation { + animation_function *functions; + size_t count, capacity; +} Animation; + + +#include "animation.h" + +Animation* +alloc_animation(void) { + return calloc(1, sizeof(Animation)); +} + +bool +animation_is_valid(const Animation* a) { return a != NULL && a->count > 0; } + +Animation* +free_animation(Animation *a) { + if (a) { + for (size_t i = 0; i < a->count; i++) free((void*)a->functions[i].params.params); + free(a->functions); + free(a); + } + return NULL; +} 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]; +static double +linear_easing_curve(easing_curve_parameters *p, double val) { + double start_pos = 0, stop_pos = 1, start_val = 0, stop_val = 1; + for (size_t i = 0; i < p->count; i++) { + if (p->positions[i] >= val) { + stop_pos = p->positions[i]; + stop_val = p->params[i]; + if (i > 0) { + start_val = p->params[i-1]; + start_pos = p->positions[i-1]; + } + break; + } + } + double frac = (val - start_pos) / (stop_pos - start_pos); + return start_val + frac * (stop_val - start_val); } -double -cubic_bezier_easing_curve(easing_curve_parameters p, double t) { +static 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]; + return uuu * p->extra0 + 3 * uu * t * p->extra1 + 3 * u * tt * p->extra2 + ttt * p->extra3; +} + +static double +step_easing_curve(easing_curve_parameters *p, double t) { + size_t val_bucket = (size_t)(t * p->count); + return p->params[MIN(val_bucket, p->count - 1)]; } 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.; + val = unit_value(val); + if (!a->count) return val; + size_t idx = MIN((size_t)(val * a->count), a->count - 1); + animation_function *f = a->functions + idx; + double ans = f->curve(&f->params, val); + return f->y_at_start + unit_value(ans) * f->y_size; } +static animation_function* +init_function(Animation *a, double y_at_start, double y_at_end, easing_curve curve, size_t count) { + ensure_space_for(a, functions, animation_function, a->count + 1, capacity, 4, false); + animation_function *f = a->functions + a->count++; + zero_at_ptr(f); + f->y_at_start = y_at_start; f->y_size = y_at_end - y_at_start; f->curve = curve; + if (count) { + double *p = calloc(count*2, sizeof(double)); + if (!p) fatal("Out of memory"); + f->params.params = p; + f->params.positions = p + count; + f->params.count = 0; + } + return f; +} + +void +add_cubic_bezier_animation(Animation *a, double y_at_start, double y_at_end, double start, double p1, double p2, double end) { + animation_function *f = init_function(a, y_at_start, y_at_end, cubic_bezier_easing_curve, 4); + f->params.extra0 = start; f->params.extra1 = p1; f->params.extra2 = p2; f->params.extra3 = end; +} + +void +add_linear_animation(Animation *a, double y_at_start, double y_at_end, size_t count, const double *params, const double *positions) { + animation_function *f = init_function(a, y_at_start, y_at_end, linear_easing_curve, count); + const size_t sz = count * sizeof(double); + memcpy((void*)f->params.params, params, sz); memcpy((void*)f->params.positions, positions, sz); +} + +void +add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t count, EasingStep step) { + animation_function *f = init_function(a, y_at_start, y_at_end, step_easing_curve, count + 2); + double jump_size = 1. / count, val = 0.; + f->params.count = count; + double *params = (double*)f->params.params; + switch (step) { + case EASING_STEP_START: + val = jump_size; + f->params.count = count - 1; + break; + case EASING_STEP_END: break; + case EASING_STEP_NONE: + f->params.count = count - 1; + break; + case EASING_STEP_BOTH: + jump_size = 1. / (count + 1); + val = jump_size; + f->params.count = count + 1; + break; + } + for (size_t i = 0; i < f->params.count; i++, val += jump_size) params[i] = val; +} diff --git a/kitty/animation.h b/kitty/animation.h index d21261dd7..9d831fa7e 100644 --- a/kitty/animation.h +++ b/kitty/animation.h @@ -8,21 +8,16 @@ #pragma once #include +#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); +typedef enum { EASING_STEP_START, EASING_STEP_END, EASING_STEP_NONE, EASING_STEP_BOTH } EasingStep; +#ifndef ANIMATION_INTERNAL_API +typedef struct {int x;} *Animation; +#endif +Animation* alloc_animation(void); double apply_easing_curve(const Animation *a, double t /* must be between 0 and 1*/); +bool animation_is_valid(const Animation *a); +void add_cubic_bezier_animation(Animation *a, double y_at_start, double y_at_end, double start, double p1, double p2, double end); +void add_linear_animation(Animation *a, double y_at_start, double y_at_end, size_t count, const double *params, const double *positions); +void add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t count, EasingStep step); +Animation* free_animation(Animation *a); diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index e214d8de3..7b09eda32 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -12,7 +12,6 @@ #include "screen.h" #include "fonts.h" #include "monotonic.h" -#include "animation.h" #include #include #include @@ -676,11 +675,11 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow 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)); ans->opacity = 1; if (cursor_blinking) { - if (OPT(animation.cursor).first_half.curve) { + if (animation_is_valid(OPT(animation.cursor))) { 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); + 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); diff --git a/kitty/data-types.h b/kitty/data-types.h index ce6176575..3b88d18fc 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -31,11 +31,11 @@ #define LIKELY(x) __builtin_expect (!!(x), 1) #define UNLIKELY(x) __builtin_expect (!!(x), 0) #define MAX(x, y) __extension__ ({ \ - __typeof__ (x) a = (x); __typeof__ (y) b = (y); \ - a > b ? a : b;}) + __typeof__ (x) __a__ = (x); __typeof__ (y) __b__ = (y); \ + __a__ > __b__ ? __a__ : __b__;}) #define MIN(x, y) __extension__ ({ \ - __typeof__ (x) a = (x); __typeof__ (y) b = (y); \ - a < b ? a : b;}) + __typeof__ (x) __a__ = (x); __typeof__ (y) __b__ = (y); \ + __a__ < __b__ ? __a__ : __b__;}) #define SWAP(x, y) do { __typeof__(x) _sw_ = y; y = x; x = _sw_; } while(0) #define xstr(s) str(s) #define str(s) #s diff --git a/kitty/state.c b/kitty/state.c index ebcc58b00..c8f379c5b 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -1462,6 +1462,7 @@ finalize(void) { F(background_image); F(bell_path); F(bell_theme); F(default_window_logo); #undef F Py_CLEAR(options_object); + free_animation(OPT(animation.cursor)); // we leak the texture here since it is not guaranteed // that freeing the texture will work during shutdown and // the GPU driver should take care of it when the OpenGL context is diff --git a/kitty/state.h b/kitty/state.h index 8039c7227..010ccaa44 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -118,7 +118,7 @@ typedef struct { hb_feature_t *features; } *entries; } font_features; - struct { Animation cursor; } animation; + struct { Animation *cursor; } animation; } Options; typedef struct WindowLogoRenderData {