feat: support fadeout animation

This commit is contained in:
DreamMaoMao 2025-02-24 18:19:42 +08:00
parent 88b78a6679
commit 9df9ada7d9
5 changed files with 229 additions and 2 deletions

View file

@ -58,6 +58,7 @@ See below for more features.
- fade in animation
- alt-tab switch window like gnome
- niri like scroller layout
- fadeout animation
## suggest tools
```

View file

@ -7,6 +7,7 @@ fadein_begin_opacity=0.5
animation_duration_move=500
animation_duration_open=400
animation_duration_tag=350
animation_duration_close=350
animation_curve=0.46,1.0,0.29,0.99
# Scroller Layout Setting

223
maomao.c
View file

@ -109,6 +109,7 @@ enum {
#ifdef IM
LyrIMPopup,
#endif
LyrFadeOut,
LyrBlock,
NUM_LAYERS
}; /* scene layers */
@ -177,8 +178,10 @@ typedef struct {
struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
struct wlr_scene_tree *scene_surface;
struct wlr_scene_tree *snapshot_scene;
struct wl_list link;
struct wl_list flink;
struct wl_list fadeout_link;
union {
struct wlr_xdg_surface *xdg;
struct wlr_xwayland_surface *xwayland;
@ -235,6 +238,7 @@ typedef struct {
float scroller_proportion;
bool need_output_flush;
struct dwl_animation animation;
bool is_fadeout_client;
// struct wl_event_source *timer_tick;
} Client;
@ -537,6 +541,8 @@ void client_commit(Client *c);
void apply_border(Client *c, struct wlr_box clip_box, int offset);
void client_set_opacity(Client *c, double opacity);
void init_baked_points(void);
void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, int sx, int sy,
void *data);
Client *direction_select(const Arg *arg);
void bind_to_view(const Arg *arg);
@ -577,6 +583,7 @@ static struct wlr_xdg_activation_v1 *activation;
static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
static struct wl_list clients; /* tiling order */
static struct wl_list fstack; /* focus order */
static struct wl_list fadeout_clients;
// static struct wlr_idle *idle;
static struct wlr_idle_notifier_v1 *idle_notifier;
static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr;
@ -714,6 +721,48 @@ double find_animation_curve_at(double t) {
return baked_points[up].y;
}
void fadeout_client_animation_next_tick(Client *c) {
if (!c)
return;
double animation_passed =
(double)c->animation.passed_frames / c->animation.total_frames;
double factor = find_animation_curve_at(animation_passed);
uint32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
uint32_t height = c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
uint32_t x =
c->animation.initial.x + (c->current.x - c->animation.initial.x) * factor;
uint32_t y =
c->animation.initial.y + (c->current.y - c->animation.initial.y) * factor;
wlr_scene_node_set_position(&c->snapshot_scene->node, 0, y);
c->animation.current = (struct wlr_box){
.x = x,
.y = y,
.width = width,
.height = height,
};
double opacity = MAX(1 - 0.4 - animation_passed, 0);
wlr_scene_node_for_each_buffer(&c->snapshot_scene->node,
scene_buffer_apply_opacity, &opacity);
if (animation_passed == 1.0) {
wl_list_remove(&c->fadeout_link);
wlr_scene_node_destroy(&c->snapshot_scene->node);
free(c);
c = NULL;
} else {
c->animation.passed_frames++;
}
}
void client_animation_next_tick(Client *c) {
double animation_passed =
(double)c->animation.passed_frames / c->animation.total_frames;
@ -894,6 +943,7 @@ void client_apply_clip(Client *c) {
}
bool client_draw_frame(Client *c) {
if (!c || !client_surface(c)->mapped)
return false;
@ -913,6 +963,16 @@ bool client_draw_frame(Client *c) {
return true;
}
bool client_draw_fadeout_frame(Client *c) {
if(!c)
return false;
fadeout_client_animation_next_tick(c);
return true;
}
void // 0.5
applybounds(Client *c, struct wlr_box *bbox) {
/* set minimum possible */
@ -3093,7 +3153,119 @@ keypressmod(struct wl_listener *listener, void *data) {
wlr_seat_keyboard_notify_modifiers(seat, &kb->wlr_keyboard->modifiers);
}
static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly,
struct wlr_scene_tree *snapshot_tree) {
if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) {
return true;
}
lx += node->x;
ly += node->y;
struct wlr_scene_node *snapshot_node = NULL;
switch (node->type) {
case WLR_SCENE_NODE_TREE:;
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
scene_node_snapshot(child, lx, ly, snapshot_tree);
}
break;
case WLR_SCENE_NODE_RECT:;
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
struct wlr_scene_rect *snapshot_rect =
wlr_scene_rect_create(snapshot_tree, scene_rect->width,
scene_rect->height, scene_rect->color);
snapshot_rect->node.data = scene_rect->node.data;
if (snapshot_rect == NULL) {
return false;
}
snapshot_node = &snapshot_rect->node;
break;
case WLR_SCENE_NODE_BUFFER:;
struct wlr_scene_buffer *scene_buffer =
wlr_scene_buffer_from_node(node);
struct wlr_scene_buffer *snapshot_buffer =
wlr_scene_buffer_create(snapshot_tree, NULL);
if (snapshot_buffer == NULL) {
return false;
}
snapshot_node = &snapshot_buffer->node;
snapshot_buffer->node.data = scene_buffer->node.data;
wlr_scene_buffer_set_dest_size(snapshot_buffer, scene_buffer->dst_width,
scene_buffer->dst_height);
wlr_scene_buffer_set_opaque_region(snapshot_buffer,
&scene_buffer->opaque_region);
wlr_scene_buffer_set_source_box(snapshot_buffer,
&scene_buffer->src_box);
wlr_scene_buffer_set_transform(snapshot_buffer,
scene_buffer->transform);
wlr_scene_buffer_set_filter_mode(snapshot_buffer,
scene_buffer->filter_mode);
// Effects
wlr_scene_buffer_set_opacity(snapshot_buffer, scene_buffer->opacity);
wlr_scene_buffer_set_opacity(snapshot_buffer, scene_buffer->opacity);
snapshot_buffer->node.data = scene_buffer->node.data;
struct wlr_scene_surface *scene_surface =
wlr_scene_surface_try_from_buffer(scene_buffer);
if (scene_surface != NULL && scene_surface->surface->buffer != NULL) {
wlr_scene_buffer_set_buffer(snapshot_buffer,
&scene_surface->surface->buffer->base);
} else {
wlr_scene_buffer_set_buffer(snapshot_buffer, scene_buffer->buffer);
}
break;
}
if (snapshot_node != NULL) {
wlr_scene_node_set_position(snapshot_node, lx, ly);
}
return true;
}
struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node,
struct wlr_scene_tree *parent) {
struct wlr_scene_tree *snapshot = wlr_scene_tree_create(parent);
if (snapshot == NULL) {
return NULL;
}
// Disable and enable the snapshot tree like so to atomically update
// the scene-graph. This will prevent over-damaging or other weirdness.
wlr_scene_node_set_enabled(&snapshot->node, false);
if (!scene_node_snapshot(node, 0, 0, snapshot)) {
wlr_scene_node_destroy(&snapshot->node);
return NULL;
}
wlr_scene_node_set_enabled(&snapshot->node, true);
return snapshot;
}
void pending_kill_client(Client *c) {
if(!c->snapshot_scene) {
wlr_scene_node_destroy(&c->snapshot_scene->node);
}
if(c->mon) {
c->snapshot_scene = wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]);
wlr_scene_node_set_enabled(&c->snapshot_scene->node, false);
}
// c->iskilling = 1; //不可以提前标记已经杀掉,因为有些客户端可能拒绝
client_send_close(c);
}
@ -3708,7 +3880,7 @@ void client_handle_opacity(Client *c) {
void rendermon(struct wl_listener *listener, void *data) {
Monitor *m = wl_container_of(listener, m, frame);
Client *c;
Client *c,*tmp;
struct wlr_output_state pending = {0};
struct timespec now;
@ -3719,6 +3891,9 @@ void rendermon(struct wl_listener *listener, void *data) {
need_more_frames = client_draw_frame(c) || need_more_frames;
}
wl_list_for_each_safe(c, tmp, &fadeout_clients, fadeout_link) {
need_more_frames = client_draw_fadeout_frame(c) || need_more_frames;
}
wlr_scene_output_commit(m->scene_output, NULL);
@ -4544,6 +4719,7 @@ void setup(void) {
*/
wl_list_init(&clients);
wl_list_init(&fstack);
wl_list_init(&fadeout_clients);
idle_notifier = wlr_idle_notifier_v1_create(dpy);
@ -5419,11 +5595,49 @@ unmaplayersurfacenotify(struct wl_listener *listener, void *data) {
motionnotify(0, NULL, 0, 0, 0, 0);
}
void init_fadeout_client(Client *c) {
if(!c->mon || client_is_unmanaged(c))
return;
if(!c->snapshot_scene) {
wlr_scene_node_destroy(&c->snapshot_scene->node);
}
Client *fadeout_cient = ecalloc(1, sizeof(*fadeout_cient));
wlr_scene_node_set_enabled(&c->scene->node, true);
fadeout_cient->snapshot_scene = wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]);
wlr_scene_node_set_enabled(&c->scene->node, false);
if(!fadeout_cient->snapshot_scene) {
free(fadeout_cient);
return;
}
fadeout_cient->animation.duration = animation_duration_close;
fadeout_cient->current = fadeout_cient->animainit_geom = c->animation.initial = c->animation.current;
fadeout_cient->mon = c->mon;
// 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标
// 这根普通node有区别
fadeout_cient->current.y = c->mon->m.height - (c->animation.current.y - c->mon->m.y);
fadeout_cient->current.x = 0; //x无偏差垂直划出
fadeout_cient->animation.passed_frames = 0;
fadeout_cient->animation.total_frames =
fadeout_cient->animation.duration / output_frame_duration_ms(c);
fadeout_cient->is_fadeout_client =true;
wlr_scene_node_set_enabled(&fadeout_cient->snapshot_scene->node,true);
wl_list_insert(&fadeout_clients, &fadeout_cient->fadeout_link);
}
void unmapnotify(struct wl_listener *listener, void *data) {
/* Called when the surface is unmapped, and should no longer be shown. */
Client *c = wl_container_of(listener, c, unmap);
c->iskilling = 1;
if (c->is_fadeout_client)
return;
init_fadeout_client(c);
if (c == grabc) {
cursor_mode = CurNormal;
@ -5462,10 +5676,12 @@ void unmapnotify(struct wl_listener *listener, void *data) {
wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel);
c->foreign_toplevel = NULL;
}
// wl_event_source_remove(c->timer_tick);
wlr_scene_node_destroy(&c->scene->node);
printstatus();
motionnotify(0, NULL, 0, 0, 0, 0);
}
void // 0.5
@ -5855,6 +6071,9 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
if (layer == LyrIMPopup)
continue;
#endif
if(layer == LyrFadeOut)
continue;
if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
continue;

View file

@ -66,6 +66,7 @@ typedef struct {
uint32_t animation_duration_move;
uint32_t animation_duration_open;
uint32_t animation_duration_tag;
uint32_t animation_duration_close;
double animation_curve[4];
int scroller_structs;
@ -452,6 +453,8 @@ void parse_config_line(Config *config, const char *line) {
config->animation_duration_open = atoi(value);
} else if (strcmp(key, "animation_duration_tag") == 0) {
config->animation_duration_tag = atoi(value);
} else if (strcmp(key, "animation_duration_close") == 0) {
config->animation_duration_close = atoi(value);
} else if (strcmp(key, "animation_curve") == 0) {
if (sscanf(value, "%lf,%lf,%lf,%lf", &config->animation_curve[0],
&config->animation_curve[1], &config->animation_curve[2],
@ -876,6 +879,7 @@ void override_config(void) {
animation_duration_move = config.animation_duration_move;
animation_duration_open = config.animation_duration_open;
animation_duration_tag = config.animation_duration_tag;
animation_duration_close = config.animation_duration_close;
// 复制数组类型的变量
memcpy(animation_curve, config.animation_curve, sizeof(animation_curve));
@ -927,6 +931,7 @@ config.fadein_begin_opacity = 0; // Begin opac window ratio for animations
config.animation_duration_move = 500; // Animation move speed
config.animation_duration_open = 400; // Animation open speed
config.animation_duration_tag = 300; // Animation tag speed
config.animation_duration_close = 300; // Animation tag speed
/* appearance */
config.axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔

View file

@ -13,6 +13,7 @@ float fadein_begin_opacity = 0; // Begin opac window ratio for animations
uint32_t animation_duration_move = 500; // Animation move speed
uint32_t animation_duration_open = 400; // Animation open speed
uint32_t animation_duration_tag = 300; // Animation tag speed
uint32_t animation_duration_close = 300; // Animation close speed
double animation_curve[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
/* appearance */