From 91ca9657e49f501f2765eb2afa01785bbe88a2c2 Mon Sep 17 00:00:00 2001 From: pi66 Date: Mon, 28 Sep 2026 18:10:19 +0100 Subject: [PATCH] add: zoom and bezier animation support --- config.def.h | 4 ++ dwl.c | 127 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 14 deletions(-) diff --git a/config.def.h b/config.def.h index a4019d3..d37aa10 100644 --- a/config.def.h +++ b/config.def.h @@ -15,6 +15,10 @@ static const float urgentcolor[] = COLOR(0xff0000ff); /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ +/* animation */ +static const unsigned int animation_duration = 300; /* ms */ +static const float animation_curve[4] = {0.0f, 0.85f, 0.2f, 1.0f}; /* cubic-bezier x1, y1, x2, y2 */ + /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) diff --git a/dwl.c b/dwl.c index e07a269..570f862 100644 --- a/dwl.c +++ b/dwl.c @@ -159,6 +159,12 @@ typedef struct { unsigned int bw; uint32_t tags; int isfloating, isurgent, isfullscreen; + + /* Animation */ + struct wlr_box anim_from, anim_to, anim_cur; + uint32_t anim_start; + int animating; + int anim_zoom; } Client; typedef struct { @@ -261,6 +267,10 @@ typedef struct { } SessionLock; /* function declarations */ +static uint32_t animation_now_ms(void); +static float animation_bezier(float t); +static int animation_tick(void *data); +static void animation_apply(Client *c, struct wlr_box g); static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); static void arrange(Monitor *m); @@ -376,6 +386,7 @@ static void xytonode(double x, double y, struct wlr_surface **psurface, static void zoom(const Arg *arg); /* variables */ +static struct wl_event_source *animation_timer; static pid_t child_pid = -1; static int locked; static Client *focused_client; @@ -486,6 +497,87 @@ static struct wlr_xwayland *xwayland; #include "ime.h" /* function implementations */ +uint32_t +animation_now_ms(void) +{ + struct timespec t; + + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_sec * 1000 + t.tv_nsec / 1000000; +} + +float +animation_bezier(float t) +{ + float x1 = animation_curve[0], y1 = animation_curve[1]; + float x2 = animation_curve[2], y2 = animation_curve[3]; + float lo = 0, hi = 1, s = t, x; + int i; + + for (i = 0; i < 20; i++) { + x = 3*(1-s)*(1-s)*s*x1 + 3*(1-s)*s*s*x2 + s*s*s; + if (x < t) + lo = s; + else + hi = s; + s = (lo + hi) / 2; + } + return 3*(1-s)*(1-s)*s*y1 + 3*(1-s)*s*s*y2 + s*s*s; +} + +int +animation_tick(void *data) +{ + Client *c; + float t, p; + int more = 0; + + wl_list_for_each(c, &clients, link) { + if (!c->animating) + continue; + t = (animation_now_ms() - c->anim_start) / (float)animation_duration; + if (t >= 1) { + t = 1; + c->animating = 0; + c->anim_zoom = 0; + } + p = animation_bezier(t); + c->anim_cur.x = (int)(c->anim_from.x + (c->anim_to.x - c->anim_from.x) * p); + c->anim_cur.y = (int)(c->anim_from.y + (c->anim_to.y - c->anim_from.y) * p); + c->anim_cur.width = (int)(c->anim_from.width + (c->anim_to.width - c->anim_from.width) * p); + c->anim_cur.height = (int)(c->anim_from.height + (c->anim_to.height - c->anim_from.height) * p); + + animation_apply(c, c->anim_cur); + more |= c->animating; + } + if (more) + wl_event_source_timer_update(animation_timer, 16); + return 0; +} + +void +animation_apply(Client *c, struct wlr_box g) +{ + struct wlr_box clip, save = c->geom; + + c->geom = g; + + wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); + wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); + wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); + wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); + wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); + wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); + wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); + + client_get_clip(c, &clip); + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); + + c->geom = save; +} + void applybounds(Client *c, struct wlr_box *bbox) { @@ -2378,7 +2470,6 @@ void resize(Client *c, struct wlr_box geo, int interact) { struct wlr_box *bbox; - struct wlr_box clip; if (!c->mon || !client_surface(c)->mapped) return; @@ -2388,21 +2479,28 @@ resize(Client *c, struct wlr_box geo, int interact) client_set_bounds(c, geo.width, geo.height); c->geom = geo; applybounds(c, bbox); + client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); - /* Update scene-graph, including borders */ - wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); - wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); - wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); - wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); - wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); - wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); - wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); - wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); - wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); + if (interact) { + c->animating = 0; + c->anim_cur = c->geom; + animation_apply(c, c->geom); + return; + } - client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); - client_get_clip(c, &clip); - wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); + if (!c->animating && c->anim_cur.width == 0) { + c->anim_cur.width = c->geom.width / 2; + c->anim_cur.height = c->geom.height / 2; + c->anim_cur.x = c->geom.x + (c->geom.width - c->anim_cur.width) / 2; + c->anim_cur.y = c->geom.y + (c->geom.height - c->anim_cur.height) / 2; + c->anim_zoom = 1; + } + + c->anim_from = c->anim_cur; + c->anim_to = c->geom; + c->anim_start = animation_now_ms(); + c->animating = 1; + wl_event_source_timer_update(animation_timer, 1); } void @@ -2628,6 +2726,7 @@ setup(void) * clients from the Unix socket, managing Wayland globals, and so on. */ dpy = wl_display_create(); event_loop = wl_display_get_event_loop(dpy); + animation_timer = wl_event_loop_add_timer(event_loop, animation_tick, NULL); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable -- 2.55.0