fuzzel animation
This commit is contained in:
parent
646b902683
commit
3e9e38ca08
5 changed files with 257 additions and 83 deletions
220
fuzzel-opening-animation/0001-fuzzel-animation.patch
Normal file
220
fuzzel-opening-animation/0001-fuzzel-animation.patch
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
From 669b92aa69ee4e102e84555b30cb4b589dbcf2da Mon Sep 17 00:00:00 2001
|
||||||
|
From: jrosh
|
||||||
|
Date: Sun, 10 Aug 2025 14:32:31 +0200
|
||||||
|
Subject: [PATCH] expand animation
|
||||||
|
|
||||||
|
---
|
||||||
|
main.c | 18 ++++++++++--
|
||||||
|
render.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
||||||
|
render.h | 3 ++
|
||||||
|
3 files changed, 105 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/main.c b/main.c
|
||||||
|
index 4562bda..738aa0e 100644
|
||||||
|
--- a/main.c
|
||||||
|
+++ b/main.c
|
||||||
|
@@ -673,6 +673,7 @@ process_event(struct context *ctx, enum event_type event)
|
||||||
|
wayl_resized(wayl);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ render_start_expand_animation(ctx->render);
|
||||||
|
wayl_ready_to_display(wayl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -714,6 +715,7 @@ process_event(struct context *ctx, enum event_type event)
|
||||||
|
(!(conf->dmenu.enabled && conf->minimal_lines) ||
|
||||||
|
apps->count >= conf->lines))
|
||||||
|
{
|
||||||
|
+ render_start_expand_animation(ctx->render);
|
||||||
|
wayl_ready_to_display(wayl);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
@@ -2218,6 +2220,7 @@ main(int argc, char *const *argv)
|
||||||
|
if (!conf.dmenu.exit_immediately_if_empty &&
|
||||||
|
!(conf.dmenu.enabled && conf.minimal_lines))
|
||||||
|
{
|
||||||
|
+ render_start_expand_animation(ctx.render);
|
||||||
|
wayl_ready_to_display(wayl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2225,8 +2228,19 @@ main(int argc, char *const *argv)
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
wayl_flush(wayl);
|
||||||
|
- if (!fdm_poll(fdm))
|
||||||
|
- break;
|
||||||
|
+
|
||||||
|
+ if (render_animation_active(ctx.render)) {
|
||||||
|
+ wayl_refresh(wayl);
|
||||||
|
+ // Force immediate poll return to keep animation smooth
|
||||||
|
+ // struct timespec timeout = {0, 16666666}; // 16ms for ~60fps
|
||||||
|
+ struct timespec timeout = {0, 6944444}; // 6.944ms for 144fps
|
||||||
|
+ nanosleep(&timeout, NULL);
|
||||||
|
+ if (!fdm_poll(fdm))
|
||||||
|
+ break;
|
||||||
|
+ } else {
|
||||||
|
+ if (!fdm_poll(fdm))
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wayl_update_cache(wayl))
|
||||||
|
diff --git a/render.c b/render.c
|
||||||
|
index 2d5645f..67439e3 100644
|
||||||
|
--- a/render.c
|
||||||
|
+++ b/render.c
|
||||||
|
@@ -51,6 +51,12 @@ struct thread_context {
|
||||||
|
int my_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
+struct animation_state {
|
||||||
|
+ bool active;
|
||||||
|
+ uint64_t start_time;
|
||||||
|
+ uint64_t duration;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
struct render {
|
||||||
|
const struct config *conf;
|
||||||
|
struct fcft_font *font;
|
||||||
|
@@ -60,7 +66,8 @@ struct render {
|
||||||
|
/* Cached fcft text runs */
|
||||||
|
struct fcft_text_run *prompt_text_run;
|
||||||
|
struct fcft_text_run *placeholder_text_run;
|
||||||
|
-
|
||||||
|
+ struct animation_state animation;
|
||||||
|
+
|
||||||
|
/* Cached selection corners */
|
||||||
|
pixman_image_t *selection_corners;
|
||||||
|
|
||||||
|
@@ -256,6 +263,44 @@ render_rounded_rectangle(pixman_image_t* dest, pixman_color_t* background,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+void
|
||||||
|
+render_start_expand_animation(struct render *render)
|
||||||
|
+{
|
||||||
|
+ struct timespec now;
|
||||||
|
+ clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
+ render->animation.start_time = now.tv_sec * 1000000000 + now.tv_nsec;
|
||||||
|
+ render->animation.active = true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static float
|
||||||
|
+get_animation_progress(struct render *render)
|
||||||
|
+{
|
||||||
|
+ if (!render->animation.active)
|
||||||
|
+ return 1.0f;
|
||||||
|
+
|
||||||
|
+ struct timespec now;
|
||||||
|
+ clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
+ uint64_t current_time = now.tv_sec * 1000000000 + now.tv_nsec;
|
||||||
|
+
|
||||||
|
+ float progress = (float)(current_time - render->animation.start_time) / render->animation.duration;
|
||||||
|
+ if (progress >= 1.0f) {
|
||||||
|
+ progress = 1.0f;
|
||||||
|
+ render->animation.active = false;
|
||||||
|
+ } else {
|
||||||
|
+ // Animation still active, need another frame
|
||||||
|
+ // This will be handled by the main loop checking render_animation_active
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Smoothstep easing
|
||||||
|
+ return progress * progress * (3.0f - 2.0f * progress);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+bool
|
||||||
|
+render_animation_active(const struct render *render)
|
||||||
|
+{
|
||||||
|
+ return render->animation.active;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void
|
||||||
|
render_background(const struct render *render, struct buffer *buf)
|
||||||
|
{
|
||||||
|
@@ -276,8 +321,33 @@ render_background(const struct render *render, struct buffer *buf)
|
||||||
|
max(render->x_margin,
|
||||||
|
render->y_margin));
|
||||||
|
|
||||||
|
- render_rounded_rectangle(buf->pix[0], &bg, &border_color, radius, bw,
|
||||||
|
- 0, 0, buf->width, buf->height);
|
||||||
|
+ float progress = get_animation_progress((struct render*)render);
|
||||||
|
+
|
||||||
|
+ if (progress < 1.0f) {
|
||||||
|
+ // Simple scaling by adjusting coordinates
|
||||||
|
+ int center_x = buf->width / 2;
|
||||||
|
+ int center_y = buf->height / 2;
|
||||||
|
+ int scaled_width = buf->width * progress;
|
||||||
|
+ int scaled_height = buf->height * progress;
|
||||||
|
+ int x = center_x - scaled_width / 2;
|
||||||
|
+ int y = center_y - scaled_height / 2;
|
||||||
|
+
|
||||||
|
+ // Clear background first
|
||||||
|
+ pixman_color_t clear = {0, 0, 0, 0};
|
||||||
|
+ pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &clear, 1,
|
||||||
|
+ &(pixman_rectangle16_t){0, 0, buf->width, buf->height});
|
||||||
|
+
|
||||||
|
+ if (scaled_width > 0 && scaled_height > 0) {
|
||||||
|
+ // Scale both border width and radius proportionally
|
||||||
|
+ render_rounded_rectangle(buf->pix[0], &bg, &border_color,
|
||||||
|
+ radius * progress,
|
||||||
|
+ bw * progress, // Scale border width too
|
||||||
|
+ x, y, scaled_width, scaled_height);
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ render_rounded_rectangle(buf->pix[0], &bg, &border_color, radius, bw,
|
||||||
|
+ 0, 0, buf->width, buf->height);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -548,6 +618,11 @@ void
|
||||||
|
render_prompt(struct render *render, struct buffer *buf,
|
||||||
|
const struct prompt *prompt, const struct matches *matches)
|
||||||
|
{
|
||||||
|
+ float progress = get_animation_progress((struct render*)render);
|
||||||
|
+ if (progress < 1.0f) {
|
||||||
|
+ // Skip rendering during animation to only show background scaling
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
struct fcft_font *font = render->font;
|
||||||
|
assert(font != NULL);
|
||||||
|
|
||||||
|
@@ -1485,6 +1560,11 @@ void
|
||||||
|
render_match_list(struct render *render, struct buffer *buf,
|
||||||
|
const struct prompt *prompt, const struct matches *matches)
|
||||||
|
{
|
||||||
|
+ float progress = get_animation_progress((struct render*)render);
|
||||||
|
+ if (progress < 1.0f) {
|
||||||
|
+ // Skip rendering during animation to only show background scaling
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
const size_t match_count = matches_get_count(matches);
|
||||||
|
const size_t selected = matches_get_match_index(matches);
|
||||||
|
|
||||||
|
@@ -1666,6 +1746,9 @@ render_init(const struct config *conf, mtx_t *icon_lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("using %hu render worker threads", render->workers.count);
|
||||||
|
+
|
||||||
|
+ render->animation.active = false;
|
||||||
|
+ render->animation.duration = 150000000;
|
||||||
|
|
||||||
|
return render;
|
||||||
|
|
||||||
|
diff --git a/render.h b/render.h
|
||||||
|
index 1e5922e..8259d05 100644
|
||||||
|
--- a/render.h
|
||||||
|
+++ b/render.h
|
||||||
|
@@ -14,6 +14,9 @@ struct render;
|
||||||
|
struct render *render_init(const struct config *conf, mtx_t *icon_lock);
|
||||||
|
void render_destroy(struct render *render);
|
||||||
|
|
||||||
|
+void render_start_expand_animation(struct render *render);
|
||||||
|
+bool render_animation_active(const struct render *render);
|
||||||
|
+
|
||||||
|
void render_initialize_colors(
|
||||||
|
struct render *render, const struct config *conf, bool gamma_correct);
|
||||||
|
|
||||||
|
--
|
||||||
|
2.50.1
|
||||||
|
|
||||||
12
fuzzel-opening-animation/README.md
Normal file
12
fuzzel-opening-animation/README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Fuzzel Expand Animation Patch
|
||||||
|
|
||||||
|
Adds a smooth expand animation when fuzzel opens.
|
||||||
|
|
||||||
|
[Fuzzel](https://codeberg.org/dnkl/fuzzel) is an app launcher and fuzzy finder for Wayland, inspired by rofi(1) and dmenu(1).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
1. Clone fuzzel: `git clone https://codeberg.org/dnkl/fuzzel.git`
|
||||||
|
2. Apply patch: `git apply 0001-fuzzel-animation.patch`
|
||||||
|
3. Build normally: [Fuzzel Installation](https://codeberg.org/dnkl/fuzzel#installation)
|
||||||
|
|
||||||
|
MIT license
|
||||||
100
niri/config.kdl
100
niri/config.kdl
|
|
@ -2,7 +2,7 @@
|
||||||
// "/-" comments out the following node.
|
// "/-" comments out the following node.
|
||||||
// Check the wiki for a full description of the configuration:
|
// Check the wiki for a full description of the configuration:
|
||||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction
|
// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction
|
||||||
|
// DOCS: https://variety4me.github.io/niri_docs/
|
||||||
// Input device configuration.
|
// Input device configuration.
|
||||||
// Find the full list of options on the wiki:
|
// Find the full list of options on the wiki:
|
||||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Input
|
// https://github.com/YaLTeR/niri/wiki/Configuration:-Input
|
||||||
|
|
@ -201,44 +201,6 @@ layout {
|
||||||
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
|
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
|
||||||
}
|
}
|
||||||
|
|
||||||
// You can enable drop shadows for windows.
|
|
||||||
shadow {
|
|
||||||
on
|
|
||||||
// Uncomment the next line to enable shadows.
|
|
||||||
// on
|
|
||||||
|
|
||||||
// By default, the shadow draws only around its window, and not behind it.
|
|
||||||
// Uncomment this setting to make the shadow draw behind its window.
|
|
||||||
//
|
|
||||||
// Note that niri has no way of knowing about the CSD window corner
|
|
||||||
// radius. It has to assume that windows have square corners, leading to
|
|
||||||
// shadow artifacts inside the CSD rounded corners. This setting fixes
|
|
||||||
// those artifacts.
|
|
||||||
//
|
|
||||||
// However, instead you may want to set prefer-no-csd and/or
|
|
||||||
// geometry-corner-radius. Then, niri will know the corner radius and
|
|
||||||
// draw the shadow correctly, without having to draw it behind the
|
|
||||||
// window. These will also remove client-side shadows if the window
|
|
||||||
// draws any.
|
|
||||||
//
|
|
||||||
// draw-behind-window true
|
|
||||||
|
|
||||||
// You can change how shadows look. The values below are in logical
|
|
||||||
// pixels and match the CSS box-shadow properties.
|
|
||||||
|
|
||||||
// Softness controls the shadow blur radius.
|
|
||||||
softness 30
|
|
||||||
|
|
||||||
// Spread expands the shadow.
|
|
||||||
spread 5
|
|
||||||
|
|
||||||
// Offset moves the shadow relative to the window.
|
|
||||||
offset x=0 y=5
|
|
||||||
|
|
||||||
// You can also change the shadow color and opacity.
|
|
||||||
color "#0007"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struts shrink the area occupied by windows, similarly to layer-shell panels.
|
// Struts shrink the area occupied by windows, similarly to layer-shell panels.
|
||||||
// You can think of them as a kind of outer gaps. They are set in logical pixels.
|
// You can think of them as a kind of outer gaps. They are set in logical pixels.
|
||||||
// Left and right struts will cause the next window to the side to always be visible.
|
// Left and right struts will cause the next window to the side to always be visible.
|
||||||
|
|
@ -271,7 +233,9 @@ spawn-at-startup "wl-clip-persist" "--clipboard" "regular"
|
||||||
|
|
||||||
// Clipboard history
|
// Clipboard history
|
||||||
spawn-at-startup "wl-paste" "--type" "text" "--watch" "cliphist" "store"
|
spawn-at-startup "wl-paste" "--type" "text" "--watch" "cliphist" "store"
|
||||||
//spawn-at-startup "wl-paste" "--type" "image" "--watch" "cliphist" "store"
|
spawn-at-startup "wl-paste" "--type" "image" "--watch" "cliphist" "store"
|
||||||
|
|
||||||
|
spawn-at-startup "arch_news.sh"
|
||||||
// Uncomment this line to ask the clients to omit their client-side decorations if possible.
|
// Uncomment this line to ask the clients to omit their client-side decorations if possible.
|
||||||
// If the client will specifically ask for CSD, the request will be honored.
|
// If the client will specifically ask for CSD, the request will be honored.
|
||||||
// Additionally, clients will be informed that they are tiled, removing some client-side rounded corners.
|
// Additionally, clients will be informed that they are tiled, removing some client-side rounded corners.
|
||||||
|
|
@ -298,59 +262,36 @@ animations {
|
||||||
// slowdown 3.0
|
// slowdown 3.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://variety4me.github.io/niri_docs/Configuration-Switch-Events/
|
||||||
|
switch-events {
|
||||||
|
lid-close { spawn "waylock"; }
|
||||||
|
lid-open { spawn "brightnessctl" "s" "50%"; }
|
||||||
|
}
|
||||||
|
|
||||||
// Window rules let you adjust behavior for individual windows.
|
// Window rules let you adjust behavior for individual windows.
|
||||||
// Find more information on the wiki:
|
// Find more information on the wiki:
|
||||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules
|
// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules
|
||||||
|
|
||||||
// Work around WezTerm's initial configure bug
|
|
||||||
// by setting an empty default-column-width.
|
|
||||||
window-rule {
|
|
||||||
// This regular expression is intentionally made as specific as possible,
|
|
||||||
// since this is the default config, and we want no false positives.
|
|
||||||
// You can get away with just app-id="wezterm" if you want.
|
|
||||||
match app-id=r#"^org\.wezfurlong\.wezterm$"#
|
|
||||||
default-column-width {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the Firefox picture-in-picture player as floating by default.
|
// Open the Firefox picture-in-picture player as floating by default.
|
||||||
window-rule {
|
window-rule {
|
||||||
// This app-id regular expression will work for both:
|
// This app-id regular expression will work for both:
|
||||||
// - host Firefox (app-id is "firefox")
|
// - host Firefox (app-id is "firefox")
|
||||||
// - Flatpak Firefox (app-id is "org.mozilla.firefox")
|
// - Flatpak Firefox (app-id is "org.mozilla.firefox")
|
||||||
match app-id=r#"firefox$"# title="^Picture-in-Picture$"
|
match app-id=r#"zen$"# title="^Picture-in-Picture$"
|
||||||
open-floating true
|
open-floating true
|
||||||
}
|
}
|
||||||
|
|
||||||
window-rule {
|
window-rule {
|
||||||
match app-id="zen"
|
// match app-id="zen"
|
||||||
|
// match app-id="obsidian"
|
||||||
|
// open-maximized true
|
||||||
|
}
|
||||||
|
|
||||||
|
window-rule {
|
||||||
open-maximized true
|
open-maximized true
|
||||||
}
|
match app-id=r#"^zen$"#
|
||||||
// set class to open floating windows "alacritty --class floating -e bluetuith"
|
match app-id=r#"^obsidian$"#
|
||||||
window-rule {
|
match app-id=r#"^org\.freedesktop\.Xwayland$"#
|
||||||
match app-id="floating"
|
|
||||||
default-column-width { proportion 0.5; }
|
|
||||||
open-on-output "current"
|
|
||||||
open-floating true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Example: block out two password managers from screen capture.
|
|
||||||
// (This example rule is commented out with a "/-" in front.)
|
|
||||||
/-window-rule {
|
|
||||||
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
|
|
||||||
match app-id=r#"^org\.gnome\.World\.Secrets$"#
|
|
||||||
|
|
||||||
block-out-from "screen-capture"
|
|
||||||
|
|
||||||
// Use this instead if you want them visible on third-party screenshot tools.
|
|
||||||
// block-out-from "screencast"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example: enable rounded corners for all windows.
|
|
||||||
// (This example rule is commented out with a "/-" in front.)
|
|
||||||
/-window-rule {
|
|
||||||
// geometry-corner-radius 12
|
|
||||||
clip-to-geometry true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binds {
|
binds {
|
||||||
|
|
@ -370,6 +311,7 @@ binds {
|
||||||
|
|
||||||
// Suggested binds for running programs: terminal, app launcher, screen locker.
|
// Suggested binds for running programs: terminal, app launcher, screen locker.
|
||||||
Mod+T hotkey-overlay-title="Open a Terminal: alacritty" { spawn "alacritty"; }
|
Mod+T hotkey-overlay-title="Open a Terminal: alacritty" { spawn "alacritty"; }
|
||||||
|
Mod+Return hotkey-overlay-title="Open a Terminal: alacritty" { spawn "alacritty"; }
|
||||||
Mod+D hotkey-overlay-title="Run an Application: fuzzel" { spawn "fuzzel"; }
|
Mod+D hotkey-overlay-title="Run an Application: fuzzel" { spawn "fuzzel"; }
|
||||||
Super+Alt+L hotkey-overlay-title="Lock the Screen: swaylock" { spawn "waylock"; }
|
Super+Alt+L hotkey-overlay-title="Lock the Screen: swaylock" { spawn "waylock"; }
|
||||||
|
|
||||||
|
|
|
||||||
1
waybar/.gitignore
vendored
Normal file
1
waybar/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/temp
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
],
|
],
|
||||||
"modules-center": ["niri/window"],
|
"modules-center": ["niri/window"],
|
||||||
"modules-right": [
|
"modules-right": [
|
||||||
"network",
|
|
||||||
"battery",
|
"battery",
|
||||||
"bluetooth",
|
"bluetooth",
|
||||||
"group/pulseaudio-wrapper",
|
"group/pulseaudio-wrapper",
|
||||||
|
|
@ -72,13 +71,13 @@
|
||||||
},
|
},
|
||||||
"custom/reboot": {
|
"custom/reboot": {
|
||||||
"format": "",
|
"format": "",
|
||||||
"on-click": "zenity --question --text='Are you sure you want to reboot?' --icon-name='system-reboot' --title='Reboot System' && sleep 1 && systemctl reboot",
|
"on-click": "zenity --question --text='Are you sure you want to reboot?' --icon-name='system-reboot' --title='Reboot System' && sleep 1 && systemctl reboot --force",
|
||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"tooltip-format": "Reboot"
|
"tooltip-format": "Reboot"
|
||||||
},
|
},
|
||||||
"custom/power": {
|
"custom/power": {
|
||||||
"format": "",
|
"format": "",
|
||||||
"on-click": "zenity --question --text='Are you sure you want to power off?' --icon-name='system-shutdown' --title='Power Off System' && sleep 1 && loginctl poweroff",
|
"on-click": "zenity --question --text='Are you sure you want to power off?' --icon-name='system-shutdown' --title='Power Off System' && sleep 1 && systemctl poweroff --force",
|
||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"tooltip-format": "Power Off"
|
"tooltip-format": "Power Off"
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +187,7 @@
|
||||||
},
|
},
|
||||||
"clock": {
|
"clock": {
|
||||||
"interval": 1,
|
"interval": 1,
|
||||||
"format": " {:%a %H:%M}",
|
"format": " {:%d.%m %a %H:%M}",
|
||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"tooltip-format": "{:L%A, %d-%m-%Y}"
|
"tooltip-format": "{:L%A, %d-%m-%Y}"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue