maomaowm/src/animation/layer.h

382 lines
10 KiB
C
Raw Normal View History

2025-07-06 19:10:35 +08:00
void layer_actual_size(LayerSurface *l, unsigned int *width,
unsigned int *height) {
struct wlr_box box;
if (l->animation.running) {
*width = l->animation.current.width;
*height = l->animation.current.height;
} else {
get_layer_target_geometry(l, &box);
*width = box.width;
*height = box.height;
}
}
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;
// 限制区域
// waybar一般都是大于0,表示要占用多少区域,所以计算位置也要用全部区域作为基准
// 如果是-1可能表示独占所有可用空间
// 如果是0应该是表示使用exclusive_zone外的可用区域
struct wlr_box bounds;
if (state->exclusive_zone > 0 || 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 & 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 & 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;
}
}
target_box->x = box.x;
target_box->y = box.y;
target_box->width = box.width;
target_box->height = box.height;
}
2025-07-11 22:10:31 +08:00
void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) {
2025-07-06 19:10:35 +08:00
int slide_direction;
int horizontal, horizontal_value;
int vertical, vertical_value;
int center_x, center_y;
2025-07-11 22:10:31 +08:00
if (!l)
2025-07-06 19:10:35 +08:00
return;
center_x = l->geom.x + l->geom.width / 2;
center_y = l->geom.y + l->geom.height / 2;
2025-07-11 22:10:31 +08:00
horizontal = center_x > l->mon->m.x + l->mon->m.width / 2 ? RIGHT : LEFT;
2025-07-06 19:10:35 +08:00
horizontal_value = horizontal == LEFT
2025-07-11 22:10:31 +08:00
? center_x - l->mon->m.x
: l->mon->m.x + l->mon->m.width - center_x;
vertical = center_y > l->mon->m.y + l->mon->m.height / 2 ? DOWN : UP;
2025-07-06 19:10:35 +08:00
vertical_value = vertical == UP ? center_y - l->mon->w.y
2025-07-11 22:10:31 +08:00
: l->mon->m.y + l->mon->m.height - center_y;
2025-07-06 19:10:35 +08:00
slide_direction = horizontal_value < vertical_value ? horizontal : vertical;
switch (slide_direction) {
case UP:
2025-07-11 22:10:31 +08:00
geo->x = l->geom.x;
geo->y = l->mon->m.y - l->geom.height;
2025-07-06 19:10:35 +08:00
break;
case DOWN:
2025-07-11 22:10:31 +08:00
geo->x = l->geom.x;
geo->y = l->mon->m.y + l->mon->m.height;
2025-07-06 19:10:35 +08:00
break;
case LEFT:
2025-07-11 22:10:31 +08:00
geo->x = l->mon->m.x - l->geom.width;
geo->y = l->geom.y;
2025-07-06 19:10:35 +08:00
break;
case RIGHT:
2025-07-11 22:10:31 +08:00
geo->x = l->mon->m.x + l->mon->m.width;
geo->y = l->geom.y;
2025-07-06 19:10:35 +08:00
break;
default:
2025-07-11 22:10:31 +08:00
geo->x = l->geom.x;
geo->y = 0 - l->geom.height;
2025-07-06 19:10:35 +08:00
}
}
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.0f);
2025-07-06 19:10:35 +08:00
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 layer_animation_next_tick(LayerSurface *l) {
if (!l || !l->mapped)
return;
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;
double opacity = MIN(fadein_begin_opacity + animation_passed, 1.0f);
if (animation_fade_in)
wlr_scene_node_for_each_buffer(&l->scene->node,
scene_buffer_apply_opacity, &opacity);
2025-07-06 19:10:35 +08:00
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;
l->animation.action = MOVE;
} else {
l->animation.passed_frames++;
}
}
void init_fadeout_layers(LayerSurface *l) {
if (!layer_animations || l->noanim) {
return;
}
if (!l->mon)
return;
if (!l->scene) {
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);
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;
if ((!l->animation_type_close &&
strcmp(layer_animation_type_close, "slide") == 0) ||
(l->animation_type_close &&
strcmp(l->animation_type_close, "slide") == 0)) {
set_layer_dir_animaiton(l, &fadeout_layer->current);
fadeout_layer->current.x = fadeout_layer->current.x - l->geom.x;
fadeout_layer->current.y = fadeout_layer->current.y - l->geom.y;
} else {
fadeout_layer->current.x = 0;
fadeout_layer->current.y = 0;
}
2025-07-06 19:10:35 +08:00
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 layer_set_pending_state(LayerSurface *l) {
if (!l || !l->mapped)
return;
l->pending = l->geom;
if (l->animation.action == OPEN) {
if ((!l->animation_type_open &&
strcmp(layer_animation_type_open, "slide") == 0) ||
(l->animation_type_open &&
strcmp(l->animation_type_open, "slide") == 0)) {
set_layer_dir_animaiton(l, &l->animainit_geom);
} else {
l->animainit_geom.x = l->geom.x;
l->animainit_geom.y = l->geom.y;
}
} else {
2025-07-06 19:10:35 +08:00
l->animainit_geom = l->animation.current;
}
2025-07-06 19:10:35 +08:00
// 判断是否需要动画
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) {
l->animation.should_animate = false;
} else {
l->animation.should_animate = true;
}
l->animation.duration = animation_duration_open;
l->animation.action = OPEN;
// 开始动画
layer_commit(l);
l->dirty = true;
}
void layer_commit(LayerSurface *l) {
if (!l || !l->mapped)
return;
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);
}
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 && layer_animations && l->animation.running && !l->noanim) {
layer_animation_next_tick(l);
} else {
l->need_output_flush = false;
}
return true;
}
bool layer_draw_fadeout_frame(LayerSurface *l) {
if (!l)
return false;
fadeout_layer_animation_next_tick(l);
return true;
}