Allow controlling the easing curves used for the visual bell
This commit is contained in:
parent
3cf07e27a1
commit
43769bc3e0
12 changed files with 70 additions and 41 deletions
|
|
@ -96,6 +96,8 @@ Detailed list of changes
|
|||
|
||||
- Add support for in-band window resize notifications (:iss:`7642`)
|
||||
|
||||
- Allow controlling the easing curves used for :opt:`visual_bell_duration`
|
||||
|
||||
0.35.2 [2024-06-22]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ Glossary
|
|||
Commonly used easing functions are :code:`linear` for a constant rate
|
||||
animation and :code:`ease-in-out` for an animation that starts slow,
|
||||
becomes fast in the middle and ends slowly. These are used to control
|
||||
various animations in kitty, such as :opt:`cursor_blink_interval`.
|
||||
various animations in kitty, such as :opt:`cursor_blink_interval` and
|
||||
:opt:`visual_bell_duration`.
|
||||
|
||||
.. _env_vars:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ typedef enum {
|
|||
FONTCONFIG_CLEANUP_FUNC,
|
||||
FREETYPE_CLEANUP_FUNC,
|
||||
SYSTEMD_CLEANUP_FUNC,
|
||||
SHADERS_CLEANUP_FUNC,
|
||||
|
||||
NUM_CLEANUP_FUNCS
|
||||
} AtExitCleanupFunc;
|
||||
|
|
|
|||
|
|
@ -897,10 +897,16 @@
|
|||
)
|
||||
|
||||
opt('visual_bell_duration', '0.0',
|
||||
option_type='positive_float', ctype='time',
|
||||
option_type='visual_bell_duration', ctype='!visual_bell_duration',
|
||||
long_text='''
|
||||
The visual bell duration (in seconds). Flash the screen when a bell occurs for
|
||||
the specified number of seconds. Set to zero to disable.
|
||||
the specified number of seconds. Set to zero to disable. The flash is animated, fading
|
||||
in and out over the specified duration. The :term:`easing function` used for the fading can be controlled.
|
||||
For example, :code:`2.0 linear` will casuse the flash to fade in and out linearly. The default
|
||||
if unspecified is to use :code:`ease-in-out` which fades slowly at the start, middle and end.
|
||||
You can specify different easing functions for the fade-in and fade-out parts, like this:
|
||||
:code:`2.0 ease-in linear`. kitty
|
||||
supports all the :link:`CSS easing functions <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>`.
|
||||
'''
|
||||
)
|
||||
|
||||
|
|
|
|||
5
kitty/options/parse.py
generated
5
kitty/options/parse.py
generated
|
|
@ -19,7 +19,8 @@
|
|||
tab_activity_symbol, tab_bar_edge, tab_bar_margin_height, tab_bar_min_tabs, tab_fade,
|
||||
tab_font_style, tab_separator, tab_title_template, titlebar_color, to_cursor_shape,
|
||||
to_cursor_unfocused_shape, to_font_size, to_layout_names, to_modifiers, url_prefixes, url_style,
|
||||
visual_window_select_characters, window_border_width, window_logo_scale, window_size
|
||||
visual_bell_duration, visual_window_select_characters, window_border_width, window_logo_scale,
|
||||
window_size
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -1353,7 +1354,7 @@ def visual_bell_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None
|
|||
ans['visual_bell_color'] = to_color_or_none(val)
|
||||
|
||||
def visual_bell_duration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['visual_bell_duration'] = positive_float(val)
|
||||
ans['visual_bell_duration'] = visual_bell_duration(val)
|
||||
|
||||
def visual_window_select_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['visual_window_select_characters'] = visual_window_select_characters(val)
|
||||
|
|
|
|||
2
kitty/options/to-c-generated.h
generated
2
kitty/options/to-c-generated.h
generated
|
|
@ -488,7 +488,7 @@ convert_from_opts_enable_audio_bell(PyObject *py_opts, Options *opts) {
|
|||
|
||||
static void
|
||||
convert_from_python_visual_bell_duration(PyObject *val, Options *opts) {
|
||||
opts->visual_bell_duration = parse_s_double_to_monotonic_t(val);
|
||||
visual_bell_duration(val, opts);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -156,20 +156,31 @@ add_easing_function(Animation *a, PyObject *e, double y_at_start, double y_at_en
|
|||
#undef G
|
||||
}
|
||||
|
||||
#define parse_animation(duration, name, start, end) \
|
||||
opts->duration = parse_s_double_to_monotonic_t(PyTuple_GET_ITEM(src, 0)); \
|
||||
opts->animation.name = free_animation(opts->animation.name); \
|
||||
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 1)) && (opts->animation.name = alloc_animation()) != NULL) { \
|
||||
add_easing_function(opts->animation.name, PyTuple_GET_ITEM(src, 1), start, end); \
|
||||
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 2))) { \
|
||||
add_easing_function(opts->animation.name, PyTuple_GET_ITEM(src, 2), end, start); \
|
||||
} else { \
|
||||
add_easing_function(opts->animation.name, PyTuple_GET_ITEM(src, 1), end, start); \
|
||||
} \
|
||||
} \
|
||||
|
||||
static inline void
|
||||
cursor_blink_interval(PyObject *src, Options *opts) {
|
||||
opts->cursor_blink_interval = parse_s_double_to_monotonic_t(PyTuple_GET_ITEM(src, 0));
|
||||
opts->animation.cursor = free_animation(opts->animation.cursor);
|
||||
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 1)) && (opts->animation.cursor = alloc_animation()) != NULL) {
|
||||
add_easing_function(opts->animation.cursor, PyTuple_GET_ITEM(src, 1), 1, 0);
|
||||
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 2))) {
|
||||
add_easing_function(opts->animation.cursor, PyTuple_GET_ITEM(src, 2), 0, 1);
|
||||
} else {
|
||||
add_easing_function(opts->animation.cursor, PyTuple_GET_ITEM(src, 1), 0, 1);
|
||||
}
|
||||
}
|
||||
parse_animation(cursor_blink_interval, cursor, 1, 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
visual_bell_duration(PyObject *src, Options *opts) {
|
||||
parse_animation(visual_bell_duration, visual_bell, 0, 1);
|
||||
}
|
||||
|
||||
#undef parse_animation
|
||||
|
||||
|
||||
static void
|
||||
parse_font_mod_size(PyObject *val, float *sz, AdjustmentUnit *unit) {
|
||||
PyObject *mv = PyObject_GetAttrString(val, "mod_value");
|
||||
|
|
|
|||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
|
|
@ -612,7 +612,7 @@ class Options:
|
|||
url_prefixes: typing.Tuple[str, ...] = ('file', 'ftp', 'ftps', 'gemini', 'git', 'gopher', 'http', 'https', 'irc', 'ircs', 'kitty', 'mailto', 'news', 'sftp', 'ssh')
|
||||
url_style: int = 3
|
||||
visual_bell_color: typing.Optional[kitty.fast_data_types.Color] = None
|
||||
visual_bell_duration: float = 0
|
||||
visual_bell_duration: typing.Tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (0.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction())
|
||||
visual_window_select_characters: str = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
wayland_enable_ime: bool = True
|
||||
wayland_titlebar_color: int = 0
|
||||
|
|
|
|||
|
|
@ -1493,8 +1493,7 @@ def steps(cls, params: str) -> 'EasingFunction':
|
|||
return cls(type='steps', jump_type=jump_type, num_steps=n)
|
||||
|
||||
|
||||
def cursor_blink_interval(spec: str) -> Tuple[float, EasingFunction, EasingFunction]:
|
||||
interval: float = -1
|
||||
def parse_animation(spec: str, interval: float = -1.) -> Tuple[float, EasingFunction, EasingFunction]:
|
||||
with suppress(Exception):
|
||||
interval = float(spec)
|
||||
return interval, EasingFunction(), EasingFunction()
|
||||
|
|
@ -1540,6 +1539,14 @@ def parse_func(func_name: str, params: str) -> None:
|
|||
return interval, m[0], m[1]
|
||||
|
||||
|
||||
def cursor_blink_interval(spec: str) -> Tuple[float, EasingFunction, EasingFunction]:
|
||||
return parse_animation(spec)
|
||||
|
||||
|
||||
def visual_bell_duration(spec: str) -> Tuple[float, EasingFunction, EasingFunction]:
|
||||
return parse_animation(spec, interval=0.)
|
||||
|
||||
|
||||
def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||
if not hasattr(deprecated_hide_window_decorations_aliases, key):
|
||||
setattr(deprecated_hide_window_decorations_aliases, key, True)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "fonts.h"
|
||||
#include "gl.h"
|
||||
#include "cleanup.h"
|
||||
#include "colors.h"
|
||||
#include <stddef.h>
|
||||
#include "window_logo.h"
|
||||
|
|
@ -973,31 +974,23 @@ send_cell_data_to_gpu(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat d
|
|||
return changed;
|
||||
}
|
||||
|
||||
static float
|
||||
ease_out_cubic(float phase) {
|
||||
return 1.0f - powf(1.0f - phase, 3.0f);
|
||||
}
|
||||
|
||||
static float
|
||||
ease_in_out_cubic(float phase) {
|
||||
return phase < 0.5f ?
|
||||
4.0f * powf(phase, 3.0f) :
|
||||
1.0f - powf(-2.0f * phase + 2.0f, 3.0f) / 2.0f;
|
||||
}
|
||||
|
||||
static float
|
||||
visual_bell_intensity(float phase) {
|
||||
static const float peak = 0.2f;
|
||||
const float fade = 1.0f - peak;
|
||||
return phase < peak ? ease_out_cubic(phase / peak) : ease_in_out_cubic((1.0f - phase) / fade);
|
||||
}
|
||||
static Animation *default_visual_bell_animation = NULL;
|
||||
|
||||
static float
|
||||
get_visual_bell_intensity(Screen *screen) {
|
||||
if (screen->start_visual_bell_at > 0) {
|
||||
monotonic_t progress = monotonic() - screen->start_visual_bell_at;
|
||||
monotonic_t duration = OPT(visual_bell_duration);
|
||||
if (progress <= duration) return visual_bell_intensity((float)progress / duration);
|
||||
if (!default_visual_bell_animation) {
|
||||
default_visual_bell_animation = alloc_animation();
|
||||
if (!default_visual_bell_animation) fatal("Out of memory");
|
||||
add_cubic_bezier_animation(default_visual_bell_animation, 0, 1, 0.42, 0, 0.58, 1);
|
||||
add_cubic_bezier_animation(default_visual_bell_animation, 1, 0, 0.42, 0, 0.58, 1);
|
||||
}
|
||||
const monotonic_t progress = monotonic() - screen->start_visual_bell_at;
|
||||
const monotonic_t duration = OPT(visual_bell_duration) / 2;
|
||||
if (progress <= duration) {
|
||||
Animation *a = animation_is_valid(OPT(animation.visual_bell)) ? OPT(animation.visual_bell) : default_visual_bell_animation;
|
||||
return (float)apply_easing_curve(a, progress / (double)duration, duration);
|
||||
}
|
||||
screen->start_visual_bell_at = 0;
|
||||
}
|
||||
return 0.0f;
|
||||
|
|
@ -1244,6 +1237,11 @@ static PyMethodDef module_methods[] = {
|
|||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static void
|
||||
finalize(void) {
|
||||
default_visual_bell_animation = free_animation(default_visual_bell_animation);
|
||||
}
|
||||
|
||||
bool
|
||||
init_shaders(PyObject *module) {
|
||||
#define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; }
|
||||
|
|
@ -1277,6 +1275,7 @@ init_shaders(PyObject *module) {
|
|||
|
||||
#undef C
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||
register_at_exit_cleanup_func(SHADERS_CLEANUP_FUNC, finalize);
|
||||
return true;
|
||||
}
|
||||
// }}}
|
||||
|
|
|
|||
|
|
@ -1463,6 +1463,7 @@ finalize(void) {
|
|||
#undef F
|
||||
Py_CLEAR(options_object);
|
||||
free_animation(OPT(animation.cursor));
|
||||
free_animation(OPT(animation.visual_bell));
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ typedef struct {
|
|||
hb_feature_t *features;
|
||||
} *entries;
|
||||
} font_features;
|
||||
struct { Animation *cursor; } animation;
|
||||
struct { Animation *cursor, *visual_bell; } animation;
|
||||
} Options;
|
||||
|
||||
typedef struct WindowLogoRenderData {
|
||||
|
|
|
|||
Loading…
Reference in a new issue