mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-02-01 11:34:59 +01:00
177 lines
6.4 KiB
C
177 lines
6.4 KiB
C
#include <float.h>
|
|
#include "state.h"
|
|
|
|
inline static float
|
|
norm(float x, float y) {
|
|
return sqrtf(x * x + y * y);
|
|
}
|
|
|
|
static void
|
|
update_cursor_trail_target(CursorTrail *ct, Window *w) {
|
|
#define EDGE(axis, index) ct->cursor_edge_##axis[index]
|
|
#define WD w->render_data
|
|
float left = FLT_MAX, right = FLT_MAX, top = FLT_MAX, bottom = FLT_MAX;
|
|
switch (WD.screen->cursor_render_info.shape) {
|
|
case CURSOR_BLOCK:
|
|
case CURSOR_HOLLOW:
|
|
case CURSOR_BEAM:
|
|
case CURSOR_UNDERLINE:
|
|
left = WD.xstart + WD.screen->cursor_render_info.x * WD.dx;
|
|
bottom = WD.ystart - (WD.screen->cursor_render_info.y + 1) * WD.dy;
|
|
default:
|
|
break;
|
|
}
|
|
switch (WD.screen->cursor_render_info.shape) {
|
|
case CURSOR_BLOCK:
|
|
case CURSOR_HOLLOW:
|
|
right = left + WD.dx;
|
|
top = bottom + WD.dy;
|
|
break;
|
|
case CURSOR_BEAM:
|
|
right = left + WD.dx / WD.screen->cell_size.width * OPT(cursor_beam_thickness);
|
|
top = bottom + WD.dy;
|
|
break;
|
|
case CURSOR_UNDERLINE:
|
|
right = left + WD.dx;
|
|
top = bottom + WD.dy / WD.screen->cell_size.height * OPT(cursor_underline_thickness);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (left != FLT_MAX) {
|
|
EDGE(x, 0) = left;
|
|
EDGE(x, 1) = right;
|
|
EDGE(y, 0) = top;
|
|
EDGE(y, 1) = bottom;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
should_skip_cursor_trail_update(CursorTrail *ct, Window *w, OSWindow *os_window) {
|
|
if (os_window->live_resize.in_progress) {
|
|
return true;
|
|
}
|
|
|
|
if (OPT(cursor_trail_start_threshold) > 0 && !ct->needs_render) {
|
|
int dx = (int)round((ct->corner_x[0] - EDGE(x, 1)) / WD.dx);
|
|
int dy = (int)round((ct->corner_y[0] - EDGE(y, 0)) / WD.dy);
|
|
if (abs(dx) + abs(dy) <= OPT(cursor_trail_start_threshold)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
update_cursor_trail_corners(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window) {
|
|
// the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner.
|
|
// equivalent to exponential ease out animation.
|
|
static const int corner_index[2][4] = {{1, 1, 0, 0}, {0, 1, 1, 0}};
|
|
|
|
// the decay time for the trail to reach 1/1024 of its distance from the cursor corner
|
|
float decay_fast = OPT(cursor_trail_decay_fast);
|
|
float decay_slow = OPT(cursor_trail_decay_slow);
|
|
|
|
if (should_skip_cursor_trail_update(ct, w, os_window)) {
|
|
for (int i = 0; i < 4; ++i) {
|
|
ct->corner_x[i] = EDGE(x, corner_index[0][i]);
|
|
ct->corner_y[i] = EDGE(y, corner_index[1][i]);
|
|
}
|
|
}
|
|
else if (ct->updated_at < now) {
|
|
float cursor_center_x = (EDGE(x, 0) + EDGE(x, 1)) * 0.5f;
|
|
float cursor_center_y = (EDGE(y, 0) + EDGE(y, 1)) * 0.5f;
|
|
float cursor_diag_2 = norm(EDGE(x, 1) - EDGE(x, 0), EDGE(y, 1) - EDGE(y, 0)) * 0.5f;
|
|
float dt = (float)monotonic_t_to_s_double(now - ct->updated_at);
|
|
|
|
// dot product here is used to dynamically adjust the decay speed of
|
|
// each corner. The closer the corner is to the cursor, the faster it
|
|
// moves.
|
|
float dx[4], dy[4];
|
|
float dot[4]; // dot product of "direction vector" and "cursor center to corner vector"
|
|
for (int i = 0; i < 4; ++i) {
|
|
dx[i] = EDGE(x, corner_index[0][i]) - ct->corner_x[i];
|
|
dy[i] = EDGE(y, corner_index[1][i]) - ct->corner_y[i];
|
|
if (fabsf(dx[i]) < 1e-6 && fabsf(dy[i]) < 1e-6) {
|
|
dx[i] = dy[i] = 0.0f;
|
|
dot[i] = 0.0f;
|
|
continue;
|
|
}
|
|
dot[i] = (dx[i] * (EDGE(x, corner_index[0][i]) - cursor_center_x) +
|
|
dy[i] * (EDGE(y, corner_index[1][i]) - cursor_center_y)) /
|
|
cursor_diag_2 / norm(dx[i], dy[i]);
|
|
}
|
|
float min_dot = FLT_MAX, max_dot = -FLT_MAX;
|
|
for (int i = 0; i < 4; ++i) {
|
|
min_dot = fminf(min_dot, dot[i]);
|
|
max_dot = fmaxf(max_dot, dot[i]);
|
|
}
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
if ((dx[i] == 0 && dy[i] == 0) || min_dot == FLT_MAX) {
|
|
continue;
|
|
}
|
|
|
|
float decay = (min_dot == max_dot)
|
|
? decay_slow
|
|
: decay_slow + (decay_fast - decay_slow) * (dot[i] - min_dot) / (max_dot - min_dot);
|
|
float step = 1.0f - exp2f(-10.0f * dt / decay);
|
|
ct->corner_x[i] += dx[i] * step;
|
|
ct->corner_y[i] += dy[i] * step;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_cursor_trail_opacity(CursorTrail *ct, Window *w, monotonic_t now) {
|
|
const bool cursor_trail_always_visible = false;
|
|
if (cursor_trail_always_visible) {
|
|
ct->opacity = 1.0f;
|
|
} else if (WD.screen->modes.mDECTCEM) {
|
|
ct->opacity += (float)monotonic_t_to_s_double(now - ct->updated_at) / OPT(cursor_trail_decay_slow);
|
|
ct->opacity = fminf(ct->opacity, 1.0f);
|
|
} else {
|
|
ct->opacity -= (float)monotonic_t_to_s_double(now - ct->updated_at) / OPT(cursor_trail_decay_slow);
|
|
ct->opacity = fmaxf(ct->opacity, 0.0f);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_cursor_trail_needs_render(CursorTrail *ct, Window *w) {
|
|
static const int corner_index[2][4] = {{1, 1, 0, 0}, {0, 1, 1, 0}};
|
|
ct->needs_render = false;
|
|
|
|
// check if any corner is still far from the cursor corner, so it should be rendered
|
|
const float dx_threshold = WD.dx / WD.screen->cell_size.width * 0.5f;
|
|
const float dy_threshold = WD.dy / WD.screen->cell_size.height * 0.5f;
|
|
for (int i = 0; i < 4; ++i) {
|
|
float dx = fabsf(EDGE(x, corner_index[0][i]) - ct->corner_x[i]);
|
|
float dy = fabsf(EDGE(y, corner_index[1][i]) - ct->corner_y[i]);
|
|
if (dx_threshold <= dx || dy_threshold <= dy) {
|
|
ct->needs_render = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window) {
|
|
if (!WD.screen->paused_rendering.expires_at && OPT(cursor_trail) <= now - WD.screen->cursor->position_changed_by_client_at) {
|
|
update_cursor_trail_target(ct, w);
|
|
}
|
|
|
|
update_cursor_trail_corners(ct, w, now, os_window);
|
|
update_cursor_trail_opacity(ct, w, now);
|
|
|
|
bool needs_render_prev = ct->needs_render;
|
|
update_cursor_trail_needs_render(ct, w);
|
|
|
|
ct->updated_at = now;
|
|
|
|
// returning true here will cause the cells to be drawn
|
|
return ct->needs_render || needs_render_prev;
|
|
}
|
|
|
|
#undef WD
|
|
#undef EDGE
|