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_area_bound(LayerSurface *l, struct wlr_box *bound) { const struct wlr_layer_surface_v1_state *state = &l->layer_surface->current; if (state->exclusive_zone > 0 || state->exclusive_zone == -1) *bound = l->mon->m; else *bound = l->mon->w; } 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; } void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) { int slide_direction; int horizontal, horizontal_value; int vertical, vertical_value; int center_x, center_y; if (!l) return; struct wlr_box usable_area; get_layer_area_bound(l, &usable_area); geo->width = l->geom.width; geo->height = l->geom.height; center_x = l->geom.x + l->geom.width / 2; center_y = l->geom.y + l->geom.height / 2; horizontal = center_x > usable_area.x + usable_area.width / 2 ? RIGHT : LEFT; horizontal_value = horizontal == LEFT ? center_x - usable_area.x : usable_area.x + usable_area.width - center_x; vertical = center_y > usable_area.y + usable_area.height / 2 ? DOWN : UP; vertical_value = vertical == UP ? center_y - l->mon->w.y : usable_area.y + usable_area.height - center_y; slide_direction = horizontal_value < vertical_value ? horizontal : vertical; switch (slide_direction) { case UP: geo->x = l->geom.x; geo->y = usable_area.y - l->geom.height; break; case DOWN: geo->x = l->geom.x; geo->y = usable_area.y + usable_area.height; break; case LEFT: geo->x = usable_area.x - l->geom.width; geo->y = l->geom.y; break; case RIGHT: geo->x = usable_area.x + usable_area.width; geo->y = l->geom.y; break; default: geo->x = l->geom.x; geo->y = 0 - l->geom.height; } } void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, void *data) { BufferData *buffer_data = (BufferData *)data; struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer); if (scene_surface == NULL) return; struct wlr_surface *surface = scene_surface->surface; unsigned int surface_width = surface->current.width * buffer_data->width_scale; unsigned int surface_height = surface->current.height * buffer_data->height_scale; if (surface_height > 0 && surface_width > 0) { wlr_scene_buffer_set_dest_size(buffer, surface_width, surface_height); } } void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, void *data) { BufferData *buffer_data = (BufferData *)data; wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, buffer_data->height); } 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); BufferData buffer_data; buffer_data.width = width; buffer_data.height = height; if ((!l->animation_type_close && strcmp(layer_animation_type_close, "zoom") == 0) || (l->animation_type_close && strcmp(l->animation_type_close, "zoom") == 0)) { wlr_scene_node_for_each_buffer(&l->scene->node, layer_fadeout_scene_buffer_apply_effect, &buffer_data); } l->animation.current = (struct wlr_box){ .x = x, .y = y, .width = width, .height = height, }; double opacity = MAX(fadeout_begin_opacity - animation_passed, 0.0f); 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); wlr_scene_node_set_position(&l->scene->node, x, y); BufferData buffer_data; if (factor == 1.0) { buffer_data.width_scale = 1.0f; buffer_data.height_scale = 1.0f; } else { buffer_data.width_scale = (float)width / (float)l->current.width; buffer_data.height_scale = (float)height / (float)l->current.height; } if ((!l->animation_type_open && strcmp(layer_animation_type_open, "zoom") == 0) || (l->animation_type_open && strcmp(l->animation_type_open, "zoom") == 0)) { wlr_scene_node_for_each_buffer( &l->scene->node, layer_scene_buffer_apply_effect, &buffer_data); } 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 (!animations || !layer_animations || l->noanim) { return; } if (!l->mon || !l->scene) return; if ((l->animation_type_close && strcmp(l->animation_type_close, "none") == 0) || (!l->animation_type_close && strcmp(layer_animation_type_close, "none") == 0)) { 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)); struct wlr_box usable_area; get_layer_area_bound(l, &usable_area); 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; fadeout_layer->animation_type_close = l->animation_type_close; fadeout_layer->animation_type_open = l->animation_type_open; // 这里snap节点的坐标设置是使用的相对坐标,不能用绝对坐标 // 这跟普通node有区别 fadeout_layer->animation.initial.x = 0; fadeout_layer->animation.initial.y = 0; if ((!l->animation_type_close && strcmp(layer_animation_type_close, "zoom") == 0) || (l->animation_type_close && strcmp(l->animation_type_close, "zoom") == 0)) { // 算出要设置的绝对坐标和大小 fadeout_layer->current.width = (float)l->animation.current.width * zoom_end_ratio; fadeout_layer->current.height = (float)l->animation.current.height * zoom_end_ratio; fadeout_layer->current.x = usable_area.x + usable_area.width / 2 - fadeout_layer->current.width / 2; fadeout_layer->current.y = usable_area.y + usable_area.height / 2 - fadeout_layer->current.height / 2; // 算出偏差坐标,大小不用因为后续不使用他的大小偏差去设置,而是直接缩放buffer fadeout_layer->current.x = fadeout_layer->current.x - l->animation.current.x; fadeout_layer->current.y = fadeout_layer->current.y - l->animation.current.y; } else if ((!l->animation_type_close && strcmp(layer_animation_type_close, "slide") == 0) || (l->animation_type_close && strcmp(l->animation_type_close, "slide") == 0)) { // 获取slide动画的结束绝对坐标和大小 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; fadeout_layer->current.width = fadeout_layer->current.width - l->geom.width; fadeout_layer->current.height = fadeout_layer->current.height - l->geom.height; } else { // fade动画坐标大小不用变 fadeout_layer->current.x = 0; fadeout_layer->current.y = 0; fadeout_layer->current.width = 0; fadeout_layer->current.height = 0; } // 计算动画帧数 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); // 请求刷新屏幕 wlr_output_schedule_frame(l->mon->wlr_output); } void layer_set_pending_state(LayerSurface *l) { if (!l || !l->mapped) return; struct wlr_box usable_area; get_layer_area_bound(l, &usable_area); l->pending = l->geom; if (l->animation.action == OPEN && !l->animation.running) { if ((!l->animation_type_open && strcmp(layer_animation_type_open, "zoom") == 0) || (l->animation_type_open && strcmp(l->animation_type_open, "zoom") == 0)) { l->animainit_geom.width = l->geom.width * zoom_initial_ratio; l->animainit_geom.height = l->geom.height * zoom_initial_ratio; l->animainit_geom.x = usable_area.x + usable_area.width / 2 - l->animainit_geom.width / 2; l->animainit_geom.y = usable_area.y + usable_area.height / 2 - l->animainit_geom.height / 2; } else 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; l->animainit_geom.width = l->geom.width; l->animainit_geom.height = l->geom.height; } } else { l->animainit_geom = l->animation.current; } // 判断是否需要动画 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; } if (((l->animation_type_open && strcmp(l->animation_type_open, "none") == 0) || (!l->animation_type_open && strcmp(layer_animation_type_open, "none") == 0)) && l->animation.action == OPEN) { l->animation.should_animate = false; } // 开始动画 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; } else { // 如果动画没有开始,且被判定为不应该动画, // 则设置总帧数为1,不然其他地方一旦获取动画 // 进度,总帧数作为分母会造成除零 // 比如动画类型为none的时候 if (!l->animation.running) { l->animation.passed_frames = 1; l->animation.total_frames = 1; } } // 请求刷新屏幕 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; }