From e142fc1e5e78430402a32584a1941d7592f382db Mon Sep 17 00:00:00 2001 From: pi66 Date: Tue, 29 Sep 2026 11:28:47 +0100 Subject: [PATCH] add: zoom and bezier animation support --- patches/animation/README.md | 10 ++ patches/animation/animation-0.9.patch | 210 ++++++++++++++++++++++ patches/animation/animation-d29259c.patch | 210 ++++++++++++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 patches/animation/README.md create mode 100644 patches/animation/animation-0.9.patch create mode 100644 patches/animation/animation-d29259c.patch diff --git a/patches/animation/README.md b/patches/animation/README.md new file mode 100644 index 0000000..8273535 --- /dev/null +++ b/patches/animation/README.md @@ -0,0 +1,10 @@ +### Description +Add simple animation support with a [cubic-bezier](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) curve. + +### Download +- [git branch](/pi66/dwl-patches/src/branch/animation) +- [animation-0.9.patch](/dwl/dwl-patches/raw/branch/main/patches/animation/animation-0.9.patch) +- [animation-d29259c.patch](/dwl/dwl-patches/raw/branch/main/patches/animation/animation-d29259c.patch) + +### Authors +- [Pi66](https://codeberg.org/pi66) diff --git a/patches/animation/animation-0.9.patch b/patches/animation/animation-0.9.patch new file mode 100644 index 0000000..db4f083 --- /dev/null +++ b/patches/animation/animation-0.9.patch @@ -0,0 +1,210 @@ +From 63675bf00dc9570424317f2ea87371d753ff5fae Mon Sep 17 00:00:00 2001 +From: pi66 +Date: Mon, 28 Sep 2026 18:03:22 +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 13c5322..b0c17e8 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 4cfd0fd..c2276bb 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 { +@@ -260,6 +266,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); +@@ -374,6 +384,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 void *exclusive_focus; +@@ -485,6 +496,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) + { +@@ -2365,7 +2457,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; +@@ -2375,21 +2466,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 +@@ -2615,6 +2713,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 + diff --git a/patches/animation/animation-d29259c.patch b/patches/animation/animation-d29259c.patch new file mode 100644 index 0000000..8732cdd --- /dev/null +++ b/patches/animation/animation-d29259c.patch @@ -0,0 +1,210 @@ +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 +