From 00dab4cdb0c74378da06fb96e2e474f1d767c674 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Fri, 27 Jun 2025 10:48:06 +0800 Subject: [PATCH 1/7] feat: layershell open animaiton support --- src/config/preset.h | 1 + src/maomao.c | 262 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 257 insertions(+), 6 deletions(-) diff --git a/src/config/preset.h b/src/config/preset.h index 49bad8b..f486a3d 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -21,6 +21,7 @@ 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 +uint32_t animation_duration_layer = 300; // Animation layer speed double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 diff --git a/src/maomao.c b/src/maomao.c index 9dac2f9..7940755 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -344,7 +344,7 @@ typedef struct { typedef struct { /* Must keep these three elements in this order */ unsigned int type; /* LayerShell */ - struct wlr_box geom; + struct wlr_box geom, current, pending, animainit_geom; Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_tree *popups; @@ -357,6 +357,10 @@ typedef struct { struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; + + struct dwl_animation animation; + bool dirty; + bool need_output_flush; } LayerSurface; typedef struct { @@ -611,6 +615,7 @@ static struct wlr_box setclient_coordinate_center(Client *c, static unsigned int get_tags_first_tag(unsigned int tags); static void client_commit(Client *c); +static void layer_commit(LayerSurface *l); static void apply_border(Client *c); static void client_set_opacity(Client *c, double opacity); static void init_baked_points(void); @@ -624,6 +629,7 @@ static void buffer_set_effect(Client *c, animationScale scale_data); static void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, void *data); static void client_set_pending_state(Client *c); +static void layer_set_pending_state(LayerSurface *l); static void set_rect_size(struct wlr_scene_rect *rect, int width, int height); static Client *center_select(Monitor *m); static void handlecursoractivity(void); @@ -632,6 +638,7 @@ static bool check_hit_no_border(Client *c); static void reset_keyboard_layout(void); static void client_update_oldmonname_record(Client *c, Monitor *m); static void pending_kill_client(Client *c); +static void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo); #include "data/static_keymap.h" #include "dispatch/dispatch.h" @@ -944,6 +951,42 @@ void fadeout_client_animation_next_tick(Client *c) { } } +void layer_animation_next_tick(LayerSurface *l) { + double animation_passed = + (double)l->animation.passed_frames / l->animation.total_frames; + + int type = l->animation.action == NONE ? MOVE : l->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + + unsigned int width = + l->animation.initial.width + + (l->current.width - l->animation.initial.width) * factor; + unsigned int height = + l->animation.initial.height + + (l->current.height - l->animation.initial.height) * factor; + + unsigned int x = l->animation.initial.x + + (l->current.x - l->animation.initial.x) * factor; + unsigned int y = l->animation.initial.y + + (l->current.y - l->animation.initial.y) * factor; + + wlr_scene_node_set_position(&l->scene->node, x, y); + + l->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + if (animation_passed == 1.0) { + l->animation.running = false; + l->need_output_flush = false; + } else { + l->animation.passed_frames++; + } +} + void client_animation_next_tick(Client *c) { double animation_passed = (double)c->animation.passed_frames / c->animation.total_frames; @@ -1379,6 +1422,30 @@ void client_apply_clip(Client *c) { buffer_set_effect(c, scale_data); } +bool layer_draw_frame(LayerSurface *l) { + + if (!l || !l->mapped) + return false; + + if (!l->need_output_flush) + return false; + + if (l->layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_TOP && + l->layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + return false; + } + + if (animations && l->animation.running) { + layer_animation_next_tick(l); + } else { + wlr_scene_node_set_position(&l->scene->node, l->geom.x, l->geom.y); + l->animainit_geom = l->animation.initial = l->pending = l->current = + l->geom; + l->need_output_flush = false; + } + return true; +} + bool client_draw_frame(Client *c) { if (!c || !client_surface(c)->mapped) @@ -3052,11 +3119,86 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { if (!l->mon) return; struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; + const struct wlr_layer_surface_v1_state *state = &layer_surface->current; strncpy(l->mon->last_surface_ws_name, layer_surface->namespace, sizeof(l->mon->last_surface_ws_name) - 1); // 最多拷贝255个字符 l->mon->last_surface_ws_name[sizeof(l->mon->last_surface_ws_name) - 1] = '\0'; // 确保字符串以null结尾 + + // 计算几何位置 + struct wlr_box bounds; + if (state->exclusive_zone == -1) + bounds = l->mon->m; + else + bounds = l->mon->w; + + // 初始化几何位置 + struct wlr_box box = {.width = state->desired_width, + .height = state->desired_height}; + + // 水平方向定位 + const uint32_t both_horiz = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if (box.width == 0) { + box.x = bounds.x; + } else if ((state->anchor & both_horiz) == both_horiz) { + box.x = bounds.x + ((bounds.width - box.width) / 2); + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x = bounds.x; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width - box.width) / 2); + } + + // 垂直方向定位 + const uint32_t both_vert = + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + if (box.height == 0) { + box.y = bounds.y; + } else if ((state->anchor & both_vert) == both_vert) { + box.y = bounds.y + ((bounds.height - box.height) / 2); + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y = bounds.y; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height - box.height) / 2); + } + + // 应用边距 + if (box.width == 0) { + box.x += state->margin.left; + box.width = bounds.width - (state->margin.left + state->margin.right); + } else if (!(state->anchor & both_horiz)) { + if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x += state->margin.left; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x -= state->margin.right; + } + } + + if (box.height == 0) { + box.y += state->margin.top; + box.height = bounds.height - (state->margin.top + state->margin.bottom); + } else if (!(state->anchor & both_vert)) { + if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y += state->margin.top; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y -= state->margin.bottom; + } + } + + // 更新几何位置 + l->geom = box; + + l->need_output_flush = true; + l->mapped = 1; + layer_set_pending_state(l); + // 刷新布局,让窗口能感应到exclude_zone变化 + arrangelayers(l->mon); } + void commitlayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *l = wl_container_of(listener, l, surface_commit); struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; @@ -3076,8 +3218,11 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { l->layer_surface->current = l->layer_surface->pending; arrangelayers(l->mon); l->layer_surface->current = old_state; + return; } + // wlr_scene_node_set_position(&l->scene->node, 10, 10); + // wlr_output_schedule_frame(l->mon->wlr_output); if (blur && blur_layer) { // 设置非背景layer模糊 @@ -3092,6 +3237,11 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { } } + if (l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && + l->layer_surface->current.layer == + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + } + if (!exclude_blur && wlr_layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && @@ -3137,6 +3287,23 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { arrangelayers(l->mon); } +void layer_set_pending_state(LayerSurface *l) { + + l->pending = l->geom; + set_layer_open_animaiton(l, l->geom); + // 判断是否需要动画 + if (!animations) { + l->animation.should_animate = false; + } else { + l->animation.should_animate = true; + } + + l->animation.duration = animation_duration_layer; + // 开始动画 + layer_commit(l); + l->dirty = true; +} + void client_set_pending_state(Client *c) { // 判断是否需要动画 @@ -3157,7 +3324,7 @@ void client_set_pending_state(Client *c) { c->dirty = true; } -double output_frame_duration_ms(Client *c) { +double output_frame_duration_ms() { int32_t refresh_total = 0; Monitor *m; wl_list_for_each(m, &mons, link) { @@ -3169,6 +3336,28 @@ double output_frame_duration_ms(Client *c) { return 1000000.0 / refresh_total; } +void layer_commit(LayerSurface *l) { + l->current = l->pending; // 设置动画的结束位置 + + if (l->animation.should_animate) { + if (!l->animation.running) { + l->animation.current = l->animainit_geom; + } + + l->animation.initial = l->animainit_geom; + // 设置动画速度 + l->animation.passed_frames = 0; + l->animation.total_frames = + l->animation.duration / output_frame_duration_ms(); + + // 标记动画开始 + l->animation.running = true; + l->animation.should_animate = false; + } + // 请求刷新屏幕 + wlr_output_schedule_frame(l->mon->wlr_output); +} + void client_commit(Client *c) { c->current = c->pending; // 设置动画的结束位置 @@ -3181,7 +3370,7 @@ void client_commit(Client *c) { // 设置动画速度 c->animation.passed_frames = 0; c->animation.total_frames = - c->animation.duration / output_frame_duration_ms(c); + c->animation.duration / output_frame_duration_ms(); // 标记动画开始 c->animation.running = true; @@ -5171,10 +5360,21 @@ void rendermon(struct wl_listener *listener, void *data) { Monitor *m = wl_container_of(listener, m, frame); Client *c, *tmp; struct wlr_output_state pending = {0}; + LayerSurface *l, *tmpl; + int i; + struct wl_list *layer_list; struct timespec now; bool need_more_frames = false; + for (i = 0; i < LENGTH(m->layers); i++) { + layer_list = &m->layers[i]; + // Draw frames for all layer + wl_list_for_each_safe(l, tmpl, layer_list, link) { + need_more_frames = layer_draw_frame(l) || need_more_frames; + } + } + // Draw frames for all clients wl_list_for_each(c, &clients, link) { need_more_frames = client_draw_frame(c) || need_more_frames; @@ -5315,7 +5515,57 @@ int is_special_animaiton_rule(Client *c) { } } -void set_open_animaiton(Client *c, struct wlr_box geo) { +void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo) { + int slide_direction; + int horizontal, horizontal_value; + int vertical, vertical_value; + int center_x, center_y; + + center_x = l->geom.x + l->geom.width / 2; + center_y = l->geom.y + l->geom.height / 2; + horizontal = + l->mon->w.x + l->mon->w.width - center_x < center_x - l->mon->w.x + ? RIGHT + : LEFT; + horizontal_value = horizontal == LEFT + ? center_x - l->mon->w.x + : l->mon->w.x + l->mon->w.width - center_x; + vertical = + l->mon->w.y + l->mon->w.height - center_y < center_y - l->mon->w.y + ? DOWN + : UP; + vertical_value = vertical == UP ? center_y - l->mon->w.y + : l->mon->w.y + l->mon->w.height - center_y; + slide_direction = horizontal_value < vertical_value ? horizontal : vertical; + + l->animainit_geom.width = l->geom.width; + l->animainit_geom.height = l->geom.height; + switch (slide_direction) { + case UP: + l->animainit_geom.x = l->geom.x; + l->animainit_geom.y = l->mon->m.y - l->geom.height; + break; + case DOWN: + l->animainit_geom.x = l->geom.x; + l->animainit_geom.y = + l->geom.y + l->mon->m.height - (l->geom.y - l->mon->m.y); + break; + case LEFT: + l->animainit_geom.x = l->mon->m.x - l->geom.width; + l->animainit_geom.y = l->geom.y; + break; + case RIGHT: + l->animainit_geom.x = + l->geom.x + l->mon->m.width - (l->geom.x - l->mon->m.x); + l->animainit_geom.y = l->geom.y; + break; + default: + l->animainit_geom.x = l->geom.x; + l->animainit_geom.y = 0 - l->geom.height; + } +} + +void set_client_open_animaiton(Client *c, struct wlr_box geo) { int slide_direction; int horizontal, horizontal_value; int vertical, vertical_value; @@ -5445,7 +5695,7 @@ void resize(Client *c, struct wlr_box geo, int interact) { c->animainit_geom.height = c->animation.current.height; c->animainit_geom.width = c->animation.current.width; } else if (c->is_open_animation) { - set_open_animaiton(c, c->geom); + set_client_open_animaiton(c, c->geom); } else { c->animainit_geom = c->animation.current; } @@ -7092,7 +7342,7 @@ void init_fadeout_client(Client *c) { fadeout_cient->animation.passed_frames = 0; fadeout_cient->animation.total_frames = - fadeout_cient->animation.duration / output_frame_duration_ms(c); + fadeout_cient->animation.duration / output_frame_duration_ms(); wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true); wl_list_insert(&fadeout_clients, &fadeout_cient->fadeout_link); } From 11eef5ea2dd53c070c1b5ac9aa0b9f1cf08e58c9 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 29 Jun 2025 13:11:49 +0800 Subject: [PATCH 2/7] feat: support layershell close animation --- src/config/parse_config.h | 4 + src/config/preset.h | 2 +- src/maomao.c | 167 +++++++++++++++++++++++++++++++++----- 3 files changed, 153 insertions(+), 20 deletions(-) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 1cfa2c8..ebc967e 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -125,6 +125,7 @@ typedef struct { typedef struct { char *layer_name; // 布局名称 int noblur; + int noanim; } ConfigLayerRule; typedef struct { @@ -1294,6 +1295,7 @@ void parse_config_line(Config *config, const char *line) { // 设置默认值 rule->layer_name = NULL; rule->noblur = 0; + rule->noanim = 0; char *token = strtok(value, ","); while (token != NULL) { @@ -1310,6 +1312,8 @@ void parse_config_line(Config *config, const char *line) { rule->layer_name = strdup(val); } else if (strcmp(key, "noblur") == 0) { rule->noblur = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "noanim") == 0) { + rule->noanim = CLAMP_INT(atoi(val), 0, 1); } } token = strtok(NULL, ","); diff --git a/src/config/preset.h b/src/config/preset.h index f486a3d..00fb739 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -11,6 +11,7 @@ char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom int animations = 1; // 是否启用动画 +int layer_animaitons = 1; // 是否启用layer动画 int tag_animation_direction = HORIZONTAL; // 标签动画方向 int animation_fade_in = 1; // Enable animation fade in int animation_fade_out = 1; // Enable animation fade out @@ -21,7 +22,6 @@ 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 -uint32_t animation_duration_layer = 300; // Animation layer speed double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 diff --git a/src/maomao.c b/src/maomao.c index 7940755..56d9e1a 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -350,6 +350,7 @@ typedef struct { struct wlr_scene_tree *popups; struct wlr_scene_layer_surface_v1 *scene_layer; struct wl_list link; + struct wl_list fadeout_link; int mapped; struct wlr_layer_surface_v1 *layer_surface; @@ -360,6 +361,8 @@ typedef struct { struct dwl_animation animation; bool dirty; + int noblur; + int noanim; bool need_output_flush; } LayerSurface; @@ -639,6 +642,7 @@ static void reset_keyboard_layout(void); static void client_update_oldmonname_record(Client *c, Monitor *m); static void pending_kill_client(Client *c); static void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo); +static void init_fadeout_layers(LayerSurface *l); #include "data/static_keymap.h" #include "dispatch/dispatch.h" @@ -666,6 +670,7 @@ 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 wl_list fadeout_layers; static struct wlr_idle_notifier_v1 *idle_notifier; static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; static struct wlr_layer_shell_v1 *layer_shell; @@ -888,6 +893,51 @@ void apply_opacity_to_rect_nodes(Client *c, struct wlr_scene_node *node, } } +void fadeout_layer_animation_next_tick(LayerSurface *l) { + if (!l) + return; + + double animation_passed = + (double)l->animation.passed_frames / l->animation.total_frames; + int type = l->animation.action = l->animation.action; + double factor = find_animation_curve_at(animation_passed, type); + unsigned int width = + l->animation.initial.width + + (l->current.width - l->animation.initial.width) * factor; + unsigned int height = + l->animation.initial.height + + (l->current.height - l->animation.initial.height) * factor; + + unsigned int x = l->animation.initial.x + + (l->current.x - l->animation.initial.x) * factor; + unsigned int y = l->animation.initial.y + + (l->current.y - l->animation.initial.y) * factor; + + wlr_scene_node_set_position(&l->scene->node, x, y); + + l->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + double opacity = MAX(fadeout_begin_opacity - animation_passed, 0); + + if (animation_fade_out) + wlr_scene_node_for_each_buffer(&l->scene->node, + scene_buffer_apply_opacity, &opacity); + + if (animation_passed == 1.0) { + wl_list_remove(&l->fadeout_link); + wlr_scene_node_destroy(&l->scene->node); + free(l); + l = NULL; + } else { + l->animation.passed_frames++; + } +} + void fadeout_client_animation_next_tick(Client *c) { if (!c) return; @@ -952,6 +1002,14 @@ void fadeout_client_animation_next_tick(Client *c) { } void layer_animation_next_tick(LayerSurface *l) { + + if (!animations || l->noanim) { + wlr_scene_node_set_position(&l->scene->node, l->geom.x, l->geom.y); + l->animation.passed_frames = 0; + l->animation.running = false; + l->need_output_flush = false; + } + double animation_passed = (double)l->animation.passed_frames / l->animation.total_frames; @@ -1484,6 +1542,14 @@ bool client_draw_fadeout_frame(Client *c) { return true; } +bool layer_draw_fadeout_frame(LayerSurface *l) { + if (!l) + return false; + + fadeout_layer_animation_next_tick(l); + return true; +} + void applybounds(Client *c, struct wlr_box *bbox) { /* set minimum possible */ c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); @@ -3115,6 +3181,7 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx, } void maplayersurfacenotify(struct wl_listener *listener, void *data) { + int ji; LayerSurface *l = wl_container_of(listener, l, map); if (!l->mon) return; @@ -3191,6 +3258,24 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { // 更新几何位置 l->geom = box; + l->noanim = 0; + l->dirty = false; + l->noblur = 0; + + // 应用layer规则 + for (ji = 0; ji < config.layer_rules_count; ji++) { + if (config.layer_rules_count < 1) + break; + if (strcmp(config.layer_rules[ji].layer_name, + l->layer_surface->namespace) == 0) { + if (config.layer_rules[ji].noblur > 0) { + l->noblur = 1; + } + if (config.layer_rules[ji].noanim > 0) { + l->noanim = 1; + } + } + } l->need_output_flush = true; l->mapped = 1; @@ -3206,8 +3291,6 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { layers[layermap[layer_surface->current.layer]]; struct wlr_layer_surface_v1_state old_state; struct wlr_layer_surface_v1 *wlr_layer_surface = l->layer_surface; - int ji; - bool exclude_blur = false; if (l->layer_surface->initial_commit) { client_set_scale(layer_surface->surface, l->mon->wlr_output->scale); @@ -3226,23 +3309,8 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { if (blur && blur_layer) { // 设置非背景layer模糊 - for (ji = 0; ji < config.layer_rules_count; ji++) { - if (config.layer_rules_count < 1) - break; - if (strcmp(config.layer_rules[ji].layer_name, - l->layer_surface->namespace) == 0 && - config.layer_rules[ji].noblur) { - exclude_blur = true; - break; - } - } - if (l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && - l->layer_surface->current.layer == - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { - } - - if (!exclude_blur && + if (!l->noblur && wlr_layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && wlr_layer_surface->current.layer != @@ -3298,7 +3366,8 @@ void layer_set_pending_state(LayerSurface *l) { l->animation.should_animate = true; } - l->animation.duration = animation_duration_layer; + l->animation.duration = animation_duration_open; + l->animation.action = OPEN; // 开始动画 layer_commit(l); l->dirty = true; @@ -5384,6 +5453,10 @@ void rendermon(struct wl_listener *listener, void *data) { need_more_frames = client_draw_fadeout_frame(c) || need_more_frames; } + wl_list_for_each_safe(l, tmpl, &fadeout_layers, fadeout_link) { + need_more_frames = layer_draw_fadeout_frame(l) || need_more_frames; + } + wlr_scene_output_commit(m->scene_output, NULL); // Send frame done notification @@ -6644,6 +6717,7 @@ void setup(void) { wl_list_init(&clients); wl_list_init(&fstack); wl_list_init(&fadeout_clients); + wl_list_init(&fadeout_layers); idle_notifier = wlr_idle_notifier_v1_create(dpy); @@ -7263,6 +7337,9 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *l = wl_container_of(listener, l, unmap); l->mapped = 0; + + init_fadeout_layers(l); + wlr_scene_node_set_enabled(&l->scene->node, false); if (l == exclusive_focus) exclusive_focus = NULL; @@ -7273,6 +7350,58 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { motionnotify(0, NULL, 0, 0, 0, 0); } +void init_fadeout_layers(LayerSurface *l) { + + if (!layer_animaitons || l->noanim) { + return; + } + + if (!l->mon) + return; + + if (!l->scene) { + return; + } + + LayerSurface *fadeout_layer = ecalloc(1, sizeof(*fadeout_layer)); + + wlr_scene_node_set_enabled(&l->scene->node, true); + fadeout_layer->scene = + wlr_scene_tree_snapshot(&l->scene->node, layers[LyrFadeOut]); + wlr_scene_node_set_enabled(&l->scene->node, false); + + if (!fadeout_layer->scene) { + free(fadeout_layer); + return; + } + + fadeout_layer->animation.duration = animation_duration_close; + fadeout_layer->geom = fadeout_layer->current = + fadeout_layer->animainit_geom = fadeout_layer->animation.initial = + l->animation.current; + fadeout_layer->mon = l->mon; + fadeout_layer->animation.action = CLOSE; + + // 这里snap节点的坐标设置是使用的相对坐标,所以不能加上原来坐标 + // 这跟普通node有区别 + + fadeout_layer->animation.initial.x = 0; + fadeout_layer->animation.initial.y = 0; + + fadeout_layer->current.y = + l->geom.y + l->geom.height / 2 > l->mon->m.y + l->mon->m.height / 2 + ? l->mon->m.height - + (l->animation.current.y - l->mon->m.y) // down out + : l->mon->m.y - l->geom.height; // up out + fadeout_layer->current.x = 0; // x无偏差,垂直划出 + + fadeout_layer->animation.passed_frames = 0; + fadeout_layer->animation.total_frames = + fadeout_layer->animation.duration / output_frame_duration_ms(); + wlr_scene_node_set_enabled(&fadeout_layer->scene->node, true); + wl_list_insert(&fadeout_layers, &fadeout_layer->fadeout_link); +} + void init_fadeout_client(Client *c) { if (!c->mon || client_is_unmanaged(c)) From 65d2642064a0f0f82e02d737dda4f9284bb4adfb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 29 Jun 2025 13:28:00 +0800 Subject: [PATCH 3/7] fix: noaime for layershell and not use animation for bottom and background layer --- src/maomao.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/maomao.c b/src/maomao.c index 56d9e1a..4e7ebc5 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -1003,13 +1003,6 @@ void fadeout_client_animation_next_tick(Client *c) { void layer_animation_next_tick(LayerSurface *l) { - if (!animations || l->noanim) { - wlr_scene_node_set_position(&l->scene->node, l->geom.x, l->geom.y); - l->animation.passed_frames = 0; - l->animation.running = false; - l->need_output_flush = false; - } - double animation_passed = (double)l->animation.passed_frames / l->animation.total_frames; @@ -1493,7 +1486,7 @@ bool layer_draw_frame(LayerSurface *l) { return false; } - if (animations && l->animation.running) { + if (animations && l->animation.running && !l->noanim) { layer_animation_next_tick(l); } else { wlr_scene_node_set_position(&l->scene->node, l->geom.x, l->geom.y); @@ -7363,6 +7356,10 @@ void init_fadeout_layers(LayerSurface *l) { return; } + if (l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || + l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) + return; + LayerSurface *fadeout_layer = ecalloc(1, sizeof(*fadeout_layer)); wlr_scene_node_set_enabled(&l->scene->node, true); From 58e0227a1f81ec9dce387acac11ba7befba89df4 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 29 Jun 2025 13:37:05 +0800 Subject: [PATCH 4/7] opt: avoid Unnecessary calculation --- src/maomao.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/maomao.c b/src/maomao.c index 4e7ebc5..523a6c7 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -3353,7 +3353,10 @@ void layer_set_pending_state(LayerSurface *l) { l->pending = l->geom; set_layer_open_animaiton(l, l->geom); // 判断是否需要动画 - if (!animations) { + if (!animations || l->noanim || + l->layer_surface->current.layer == + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || + l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { l->animation.should_animate = false; } else { l->animation.should_animate = true; From 9319ff547868f02054c4619cbc61563b1c866ee6 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 29 Jun 2025 14:11:05 +0800 Subject: [PATCH 5/7] opt code struct --- src/config/parse_config.h | 5 +++++ src/config/preset.h | 2 +- src/maomao.c | 40 +++++++++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index ebc967e..10bbf44 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -130,6 +130,7 @@ typedef struct { typedef struct { int animations; + int layer_animations; char animation_type_open[10]; char animation_type_close[10]; int animation_fade_in; @@ -832,6 +833,8 @@ void parse_config_line(Config *config, const char *line) { if (strcmp(key, "animations") == 0) { config->animations = atoi(value); + } else if (strcmp(key, "layer_animations") == 0) { + config->layer_animations = atoi(value); } else if (strcmp(key, "animation_type_open") == 0) { snprintf(config->animation_type_open, sizeof(config->animation_type_open), "%.9s", @@ -2057,6 +2060,7 @@ void free_config(void) { void override_config(void) { // 动画启用 animations = CLAMP_INT(config.animations, 0, 1); + layer_animations = CLAMP_INT(config.layer_animations, 0, 1); // 标签动画方向 tag_animation_direction = CLAMP_INT(config.tag_animation_direction, 0, 1); @@ -2199,6 +2203,7 @@ void override_config(void) { void set_value_default() { /* animaion */ config.animations = animations; // 是否启用动画 + config.layer_animations = layer_animations; // 是否启用layer动画 config.animation_fade_in = animation_fade_in; // Enable animation fade in config.animation_fade_out = animation_fade_out; // Enable animation fade out config.tag_animation_direction = tag_animation_direction; // 标签动画方向 diff --git a/src/config/preset.h b/src/config/preset.h index 00fb739..07f672a 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -11,7 +11,7 @@ char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom int animations = 1; // 是否启用动画 -int layer_animaitons = 1; // 是否启用layer动画 +int layer_animations = 1; // 是否启用layer动画 int tag_animation_direction = HORIZONTAL; // 标签动画方向 int animation_fade_in = 1; // Enable animation fade in int animation_fade_out = 1; // Enable animation fade out diff --git a/src/maomao.c b/src/maomao.c index 523a6c7..32e46be 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -1033,6 +1033,7 @@ void layer_animation_next_tick(LayerSurface *l) { if (animation_passed == 1.0) { l->animation.running = false; l->need_output_flush = false; + l->animation.action = MOVE; } else { l->animation.passed_frames++; } @@ -1486,7 +1487,7 @@ bool layer_draw_frame(LayerSurface *l) { return false; } - if (animations && l->animation.running && !l->noanim) { + if (animations && layer_animations && l->animation.running && !l->noanim) { layer_animation_next_tick(l); } else { wlr_scene_node_set_position(&l->scene->node, l->geom.x, l->geom.y); @@ -3173,17 +3174,8 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx, } } -void maplayersurfacenotify(struct wl_listener *listener, void *data) { - int ji; - LayerSurface *l = wl_container_of(listener, l, map); - if (!l->mon) - return; - struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; - const struct wlr_layer_surface_v1_state *state = &layer_surface->current; - strncpy(l->mon->last_surface_ws_name, layer_surface->namespace, - sizeof(l->mon->last_surface_ws_name) - 1); // 最多拷贝255个字符 - l->mon->last_surface_ws_name[sizeof(l->mon->last_surface_ws_name) - 1] = - '\0'; // 确保字符串以null结尾 +void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { + const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; // 计算几何位置 struct wlr_box bounds; @@ -3249,6 +3241,26 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { } } + target_box->x = box.x; + target_box->y = box.y; + target_box->width = box.width; + target_box->height = box.height; +} + +void maplayersurfacenotify(struct wl_listener *listener, void *data) { + int ji; + LayerSurface *l = wl_container_of(listener, l, map); + if (!l->mon) + return; + struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; + strncpy(l->mon->last_surface_ws_name, layer_surface->namespace, + sizeof(l->mon->last_surface_ws_name) - 1); // 最多拷贝255个字符 + l->mon->last_surface_ws_name[sizeof(l->mon->last_surface_ws_name) - 1] = + '\0'; // 确保字符串以null结尾 + + struct wlr_box box; + get_layer_target_geometry(l, &box); + // 更新几何位置 l->geom = box; l->noanim = 0; @@ -3353,7 +3365,7 @@ void layer_set_pending_state(LayerSurface *l) { l->pending = l->geom; set_layer_open_animaiton(l, l->geom); // 判断是否需要动画 - if (!animations || l->noanim || + if (!animations || !layer_animations || l->noanim || l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { @@ -7348,7 +7360,7 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { void init_fadeout_layers(LayerSurface *l) { - if (!layer_animaitons || l->noanim) { + if (!layer_animations || l->noanim) { return; } From fed194ffd17d8e5340b0354b819d4af6b22d5196 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 29 Jun 2025 14:48:19 +0800 Subject: [PATCH 6/7] feat: support layershell move animaiton --- src/maomao.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/maomao.c b/src/maomao.c index 32e46be..4e4dece 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -3177,9 +3177,12 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx, void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; - // 计算几何位置 + // 限制区域 + // waybar一般都是大于0,表示要占用多少区域,所以计算位置也要用全部区域作为基准 + // 如果是-1可能表示独占所有可用空间 + // 如果是0,应该是表示使用exclusive_zone外的可用区域 struct wlr_box bounds; - if (state->exclusive_zone == -1) + if (state->exclusive_zone > 0 || state->exclusive_zone == -1) bounds = l->mon->m; else bounds = l->mon->w; @@ -3284,6 +3287,7 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { l->need_output_flush = true; l->mapped = 1; + l->animation.action = OPEN; layer_set_pending_state(l); // 刷新布局,让窗口能感应到exclude_zone变化 arrangelayers(l->mon); @@ -3296,6 +3300,7 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { layers[layermap[layer_surface->current.layer]]; struct wlr_layer_surface_v1_state old_state; struct wlr_layer_surface_v1 *wlr_layer_surface = l->layer_surface; + struct wlr_box box; if (l->layer_surface->initial_commit) { client_set_scale(layer_surface->surface, l->mon->wlr_output->scale); @@ -3309,6 +3314,20 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { return; } + + get_layer_target_geometry(l, &box); + + if (wlr_layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && + wlr_layer_surface->current.layer != + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND && + !wlr_box_equal(&box, &l->geom)) { + + l->geom = box; + l->animation.action = MOVE; + l->need_output_flush = true; + layer_set_pending_state(l); + } + // wlr_scene_node_set_position(&l->scene->node, 10, 10); // wlr_output_schedule_frame(l->mon->wlr_output); @@ -3363,7 +3382,10 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { void layer_set_pending_state(LayerSurface *l) { l->pending = l->geom; - set_layer_open_animaiton(l, l->geom); + if (l->animation.action == OPEN) + set_layer_open_animaiton(l, l->geom); + else + l->animainit_geom = l->animation.current; // 判断是否需要动画 if (!animations || !layer_animations || l->noanim || l->layer_surface->current.layer == From 238ecda316ee1de93de651aea3a16305f1fa914f Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 29 Jun 2025 15:17:47 +0800 Subject: [PATCH 7/7] opt: Increase defensive conditions for layer animation --- src/maomao.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/maomao.c b/src/maomao.c index 4e4dece..1ec47be 100644 --- a/src/maomao.c +++ b/src/maomao.c @@ -1003,6 +1003,9 @@ void fadeout_client_animation_next_tick(Client *c) { void layer_animation_next_tick(LayerSurface *l) { + if (!l || !l->mapped) + return; + double animation_passed = (double)l->animation.passed_frames / l->animation.total_frames; @@ -3175,6 +3178,10 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx, } void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { + + if (!l || !l->mapped) + return; + const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; // 限制区域 @@ -3253,6 +3260,8 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) { void maplayersurfacenotify(struct wl_listener *listener, void *data) { int ji; LayerSurface *l = wl_container_of(listener, l, map); + l->mapped = 1; + if (!l->mon) return; struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; @@ -3286,7 +3295,6 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { } l->need_output_flush = true; - l->mapped = 1; l->animation.action = OPEN; layer_set_pending_state(l); // 刷新布局,让窗口能感应到exclude_zone变化 @@ -3317,7 +3325,8 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { get_layer_target_geometry(l, &box); - if (wlr_layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && + if (l->mapped && + wlr_layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && wlr_layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND && !wlr_box_equal(&box, &l->geom)) { @@ -3381,6 +3390,9 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { void layer_set_pending_state(LayerSurface *l) { + if (!l || !l->mapped) + return; + l->pending = l->geom; if (l->animation.action == OPEN) set_layer_open_animaiton(l, l->geom); @@ -3436,6 +3448,10 @@ double output_frame_duration_ms() { } void layer_commit(LayerSurface *l) { + + if (!l || !l->mapped) + return; + l->current = l->pending; // 设置动画的结束位置 if (l->animation.should_animate) { @@ -5624,6 +5640,9 @@ void set_layer_open_animaiton(LayerSurface *l, struct wlr_box geo) { int vertical, vertical_value; int center_x, center_y; + if (!l || !l->mapped) + return; + center_x = l->geom.x + l->geom.width / 2; center_y = l->geom.y + l->geom.height / 2; horizontal =