maomaowm/src/animation/client.h

972 lines
28 KiB
C
Raw Normal View History

void client_actual_size(Client *c, int32_t *width, int32_t *height) {
*width = c->animation.current.width - 2 * (int32_t)c->bw;
2025-07-06 19:10:35 +08:00
*height = c->animation.current.height - 2 * (int32_t)c->bw;
2025-07-06 19:10:35 +08:00
}
void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) {
2025-07-06 19:10:35 +08:00
wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height));
}
bool is_horizontal_stack_layout(Monitor *m) {
if (!m->pertag->curtag &&
(strcmp(m->pertag->ltidxs[m->pertag->prevtag]->name, "tile") == 0 ||
strcmp(m->pertag->ltidxs[m->pertag->prevtag]->name, "spiral") == 0 ||
strcmp(m->pertag->ltidxs[m->pertag->prevtag]->name, "dwindle") == 0 ||
strcmp(m->pertag->ltidxs[m->pertag->prevtag]->name, "deck") == 0))
return true;
2025-07-06 19:10:35 +08:00
if (m->pertag->curtag &&
2025-10-09 13:09:40 +08:00
(m->pertag->ltidxs[m->pertag->curtag]->id == TILE ||
m->pertag->ltidxs[m->pertag->curtag]->id == DECK))
return true;
return false;
}
2025-10-15 13:22:41 +08:00
bool is_horizontal_right_stack_layout(Monitor *m) {
if (m->pertag->curtag &&
(m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE))
return true;
return false;
}
2026-02-06 15:18:31 +05:45
int32_t is_special_animation_rule(Client *c) {
if (is_scroller_layout(c->mon) && !c->isfloating) {
2025-07-06 19:10:35 +08:00
return DOWN;
} else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) {
2025-07-06 19:10:35 +08:00
return DOWN;
} else if (c->mon->visible_tiling_clients == 2 && !c->isfloating &&
!new_is_master && is_horizontal_stack_layout(c->mon)) {
2025-07-06 19:10:35 +08:00
return RIGHT;
} else if (!c->isfloating && new_is_master &&
is_horizontal_stack_layout(c->mon)) {
2025-07-06 19:10:35 +08:00
return LEFT;
2025-10-15 13:22:41 +08:00
} else if (c->mon->visible_tiling_clients == 2 && !c->isfloating &&
!new_is_master && is_horizontal_right_stack_layout(c->mon)) {
return LEFT;
} else if (!c->isfloating && new_is_master &&
is_horizontal_right_stack_layout(c->mon)) {
return RIGHT;
2025-07-06 19:10:35 +08:00
} else {
return UNDIR;
}
}
2026-02-06 15:18:31 +05:45
void set_client_open_animation(Client *c, struct wlr_box geo) {
int32_t slide_direction;
int32_t horizontal, horizontal_value;
int32_t vertical, vertical_value;
int32_t special_direction;
int32_t center_x, center_y;
if ((!c->animation_type_open && strcmp(animation_type_open, "fade") == 0) ||
(c->animation_type_open &&
strcmp(c->animation_type_open, "fade") == 0)) {
c->animainit_geom.width = geo.width;
c->animainit_geom.height = geo.height;
c->animainit_geom.x = geo.x;
c->animainit_geom.y = geo.y;
return;
} else if ((!c->animation_type_open &&
strcmp(animation_type_open, "zoom") == 0) ||
(c->animation_type_open &&
strcmp(c->animation_type_open, "zoom") == 0)) {
2025-07-06 19:10:35 +08:00
c->animainit_geom.width = geo.width * zoom_initial_ratio;
c->animainit_geom.height = geo.height * zoom_initial_ratio;
c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width) / 2;
c->animainit_geom.y =
geo.y + (geo.height - c->animainit_geom.height) / 2;
return;
} else {
2026-02-06 15:18:31 +05:45
special_direction = is_special_animation_rule(c);
2025-07-06 19:10:35 +08:00
center_x = c->geom.x + c->geom.width / 2;
center_y = c->geom.y + c->geom.height / 2;
if (special_direction == UNDIR) {
horizontal = c->mon->w.x + c->mon->w.width - center_x <
center_x - c->mon->w.x
? RIGHT
: LEFT;
horizontal_value = horizontal == LEFT
? center_x - c->mon->w.x
: c->mon->w.x + c->mon->w.width - center_x;
vertical = c->mon->w.y + c->mon->w.height - center_y <
center_y - c->mon->w.y
? DOWN
: UP;
vertical_value = vertical == UP
? center_y - c->mon->w.y
: c->mon->w.y + c->mon->w.height - center_y;
slide_direction =
horizontal_value < vertical_value ? horizontal : vertical;
} else {
slide_direction = special_direction;
}
c->animainit_geom.width = c->geom.width;
c->animainit_geom.height = c->geom.height;
switch (slide_direction) {
case UP:
c->animainit_geom.x = c->geom.x;
c->animainit_geom.y = c->mon->m.y - c->geom.height;
break;
case DOWN:
c->animainit_geom.x = c->geom.x;
c->animainit_geom.y =
c->geom.y + c->mon->m.height - (c->geom.y - c->mon->m.y);
break;
case LEFT:
c->animainit_geom.x = c->mon->m.x - c->geom.width;
c->animainit_geom.y = c->geom.y;
break;
case RIGHT:
c->animainit_geom.x =
c->geom.x + c->mon->m.width - (c->geom.x - c->mon->m.x);
c->animainit_geom.y = c->geom.y;
break;
default:
c->animainit_geom.x = c->geom.x;
c->animainit_geom.y = 0 - c->geom.height;
}
}
}
void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data;
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
buffer_data->height);
2025-07-06 19:10:35 +08:00
}
void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data;
2025-07-06 19:10:35 +08:00
if (buffer_data->should_scale && buffer_data->height_scale < 1 &&
buffer_data->width_scale < 1) {
buffer_data->should_scale = false;
2025-07-06 19:10:35 +08:00
}
if (buffer_data->should_scale && buffer_data->height_scale == 1 &&
buffer_data->width_scale < 1) {
buffer_data->should_scale = false;
2025-07-06 19:10:35 +08:00
}
if (buffer_data->should_scale && buffer_data->height_scale < 1 &&
buffer_data->width_scale == 1) {
buffer_data->should_scale = false;
2025-07-06 19:10:35 +08:00
}
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;
if (buffer_data->should_scale) {
2025-07-06 19:10:35 +08:00
int32_t surface_width = surface->current.width;
int32_t surface_height = surface->current.height;
2025-07-06 19:10:35 +08:00
surface_width = buffer_data->width_scale < 1
2025-07-06 19:10:35 +08:00
? surface_width
: buffer_data->width_scale * surface_width;
surface_height = buffer_data->height_scale < 1
2025-07-06 19:10:35 +08:00
? surface_height
: buffer_data->height_scale * surface_height;
2025-07-06 19:10:35 +08:00
if (surface_width > buffer_data->width &&
2025-07-06 19:10:35 +08:00
wlr_subsurface_try_from_wlr_surface(surface) == NULL) {
surface_width = buffer_data->width;
2025-07-06 19:10:35 +08:00
}
if (surface_height > buffer_data->height &&
2025-07-06 19:10:35 +08:00
wlr_subsurface_try_from_wlr_surface(surface) == NULL) {
surface_height = buffer_data->height;
2025-07-06 19:10:35 +08:00
}
if (surface_width > buffer_data->width &&
2025-07-06 19:10:35 +08:00
wlr_subsurface_try_from_wlr_surface(surface) != NULL) {
return;
}
if (surface_height > buffer_data->height &&
2025-07-06 19:10:35 +08:00
wlr_subsurface_try_from_wlr_surface(surface) != NULL) {
return;
}
if (surface_height > 0 && surface_width > 0) {
wlr_scene_buffer_set_dest_size(buffer, surface_width,
surface_height);
}
}
// TODO: blur set, opacity set
if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL)
return;
}
void buffer_set_effect(Client *c, BufferData data) {
2025-07-06 19:10:35 +08:00
if (!c || c->iskilling)
return;
if (c->animation.tagouting || c->animation.tagouted ||
c->animation.tagining) {
data.should_scale = false;
}
if (c == grabc)
data.should_scale = false;
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
scene_buffer_apply_effect, &data);
}
2026-02-27 09:05:00 +08:00
void apply_shield(Client *c) {
if (active_capture_count > 0 && c->shield_when_capture) {
wlr_scene_node_raise_to_top(&c->shield->node);
2026-02-27 09:05:00 +08:00
wlr_scene_rect_set_size(c->shield, c->animation.current.width,
c->animation.current.height);
wlr_scene_node_set_enabled(&c->shield->node, true);
} else {
if (c->shield->node.enabled) {
wlr_scene_node_lower_to_bottom(&c->shield->node);
wlr_scene_rect_set_size(c->shield, c->animation.current.width,
c->animation.current.height);
wlr_scene_node_set_enabled(&c->shield->node, false);
}
}
}
2025-07-09 11:44:04 +08:00
void apply_border(Client *c) {
bool hit_no_border = false;
2025-07-06 19:10:35 +08:00
if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow)
2025-07-06 19:10:35 +08:00
return;
2025-07-09 11:44:04 +08:00
hit_no_border = check_hit_no_border(c);
2025-07-06 19:10:35 +08:00
if (hit_no_border && smartgaps) {
c->bw = 0;
c->fake_no_border = true;
} else if (hit_no_border && !smartgaps) {
2025-07-09 11:44:04 +08:00
set_rect_size(c->border[0], 0, 0);
set_rect_size(c->border[1], 0, 0);
set_rect_size(c->border[2], 0, 0);
set_rect_size(c->border[3], 0, 0);
2025-07-06 19:10:35 +08:00
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
c->fake_no_border = true;
return;
} else if (!c->isfullscreen && VISIBLEON(c, c->mon)) {
c->bw = c->isnoborder ? 0 : borderpx;
c->fake_no_border = false;
}
2025-07-09 11:44:04 +08:00
struct wlr_box fullgeom = c->animation.current;
int32_t bw = (int32_t)c->bw; // 使用有符号类型避免负数问题
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
// 设置场景表面的位置(缩进边框宽度)
wlr_scene_node_set_position(&c->scene_surface->node, bw, bw);
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
// 上边框:位于窗口顶部,宽度为窗口宽,高度为边框宽
set_rect_size(c->border[0], fullgeom.width, bw);
wlr_scene_node_set_position(&c->border[0]->node, 0, 0);
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
// 下边框:位于窗口底部
set_rect_size(c->border[1], fullgeom.width, bw);
wlr_scene_node_set_position(&c->border[1]->node, 0, fullgeom.height - bw);
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
// 左边框:位于窗口左侧,高度为窗口高,宽度为边框宽
set_rect_size(c->border[2], bw, fullgeom.height);
wlr_scene_node_set_position(&c->border[2]->node, 0, 0);
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
// 右边框:位于窗口右侧
set_rect_size(c->border[3], bw, fullgeom.height);
wlr_scene_node_set_position(&c->border[3]->node, fullgeom.width - bw, 0);
2025-07-06 19:10:35 +08:00
}
void monitor_clip_scene_tree(Monitor *m) {
int i;
for (i = 0; i < NUM_LAYERS; i++) {
wlr_scene_tree_set_clip(m->layers_scene_tree[i], &m->m);
}
}
2025-07-13 09:11:43 +08:00
void client_apply_clip(Client *c, float factor) {
2025-07-06 19:10:35 +08:00
if (c->iskilling || !client_surface(c)->mapped)
return;
struct wlr_box clip_box;
BufferData buffer_data;
2025-07-06 19:10:35 +08:00
2025-07-15 16:10:06 +08:00
if (!animations) {
2026-02-27 09:05:00 +08:00
// 非动画模式:直接固定几何,应用边框,设置剪切
2025-07-06 19:10:35 +08:00
c->animation.running = false;
c->need_output_flush = false;
c->animainit_geom = c->current = c->pending = c->animation.current =
c->geom;
2026-02-27 09:05:00 +08:00
client_get_clip(c, &clip_box); // 获取相对于父级的初始剪切区域
2025-07-06 19:10:35 +08:00
monitor_clip_scene_tree(c->mon);
2025-07-06 19:10:35 +08:00
apply_border(c);
2026-02-27 09:05:00 +08:00
apply_shield(c);
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
if (clip_box.width <= 0 || clip_box.height <= 0)
2025-07-06 19:10:35 +08:00
return;
wlr_scene_tree_set_clip(c->scene_surface, &clip_box);
2025-11-01 12:08:39 +08:00
buffer_set_effect(
c, (BufferData){1.0f, 1.0f, clip_box.width, clip_box.height, true});
2025-07-06 19:10:35 +08:00
return;
}
2026-02-27 09:05:00 +08:00
// 动画模式:获取窗口当前动画尺寸和几何
int32_t width, height;
2025-07-06 19:10:35 +08:00
client_actual_size(c, &width, &height);
struct wlr_box geometry;
client_get_geometry(c, &geometry);
2026-02-27 09:05:00 +08:00
// 剪切框 = 窗口当前动画矩形(相对于父级)
2025-07-06 19:10:35 +08:00
clip_box = (struct wlr_box){
.x = geometry.x,
.y = geometry.y,
.width = width,
.height = height,
2025-07-06 19:10:35 +08:00
};
if (client_is_x11(c)) {
clip_box.x = 0;
clip_box.y = 0;
}
monitor_clip_scene_tree(c->mon);
2025-07-06 19:10:35 +08:00
apply_border(c);
2026-02-27 09:05:00 +08:00
apply_shield(c);
2025-07-06 19:10:35 +08:00
if (clip_box.width <= 0 || clip_box.height <= 0) {
wlr_scene_node_set_enabled(&c->scene_surface->node, false);
2026-02-27 09:05:00 +08:00
return;
2025-07-06 19:10:35 +08:00
} else {
wlr_scene_node_set_enabled(&c->scene_surface->node, true);
}
wlr_scene_tree_set_clip(c->scene_surface, &clip_box);
2025-07-06 19:10:35 +08:00
2026-02-27 09:05:00 +08:00
// 计算缩放因子(动画尺寸 / 原始表面尺寸)
buffer_data.should_scale = true;
buffer_data.width = clip_box.width;
buffer_data.height = clip_box.height;
2026-02-27 09:05:00 +08:00
buffer_data.width_scale = (float)buffer_data.width / geometry.width;
buffer_data.height_scale = (float)buffer_data.height / geometry.height;
2025-07-13 09:11:43 +08:00
if (factor == 1.0) {
buffer_data.width_scale = 1.0;
buffer_data.height_scale = 1.0;
2025-07-13 09:11:43 +08:00
}
buffer_set_effect(c, buffer_data);
2025-07-06 19:10:35 +08:00
}
void fadeout_client_animation_next_tick(Client *c) {
if (!c)
return;
BufferData buffer_data;
2025-07-06 19:10:35 +08:00
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
2025-07-06 19:10:35 +08:00
double animation_passed =
c->animation.duration
? (double)passed_time / (double)c->animation.duration
: 1.0;
int32_t type = c->animation.action = c->animation.action;
2025-07-06 19:10:35 +08:00
double factor = find_animation_curve_at(animation_passed, type);
2025-12-12 11:08:53 +08:00
int32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
int32_t height = c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
2025-07-06 19:10:35 +08:00
int32_t x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor;
int32_t y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor;
2025-07-06 19:10:35 +08:00
wlr_scene_node_set_position(&c->scene->node, x, y);
c->animation.current = (struct wlr_box){
.x = x,
.y = y,
.width = width,
.height = height,
};
2025-12-12 11:08:53 +08:00
double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEOUT);
double percent = fadeout_begin_opacity -
(opacity_eased_progress * fadeout_begin_opacity);
double opacity = MAX(percent, 0);
2025-07-06 19:10:35 +08:00
if (animation_fade_out && !c->nofadeout)
wlr_scene_node_for_each_buffer(&c->scene->node,
scene_buffer_apply_opacity, &opacity);
if ((c->animation_type_close &&
strcmp(c->animation_type_close, "zoom") == 0) ||
(!c->animation_type_close &&
strcmp(animation_type_close, "zoom") == 0)) {
buffer_data.width = width;
buffer_data.height = height;
buffer_data.width_scale = animation_passed;
buffer_data.height_scale = animation_passed;
2025-07-06 19:10:35 +08:00
wlr_scene_node_for_each_buffer(
&c->scene->node, snap_scene_buffer_apply_effect, &buffer_data);
2025-07-06 19:10:35 +08:00
}
if (animation_passed >= 1.0) {
2025-07-06 19:10:35 +08:00
wl_list_remove(&c->fadeout_link);
wlr_scene_node_destroy(&c->scene->node);
free(c);
c = NULL;
}
}
void client_animation_next_tick(Client *c) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
2025-07-06 19:10:35 +08:00
double animation_passed =
c->animation.duration
? (double)passed_time / (double)c->animation.duration
: 1.0;
2025-07-06 19:10:35 +08:00
int32_t type = c->animation.action == NONE ? MOVE : c->animation.action;
2025-07-06 19:10:35 +08:00
double factor = find_animation_curve_at(animation_passed, type);
Client *pointer_c = NULL;
double sx = 0, sy = 0;
struct wlr_surface *surface = NULL;
int32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor;
int32_t height = c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor;
2025-07-06 19:10:35 +08:00
int32_t x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor;
int32_t y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor;
2025-07-06 19:10:35 +08:00
wlr_scene_node_set_position(&c->scene->node, x, y);
c->animation.current = (struct wlr_box){
.x = x,
.y = y,
.width = width,
.height = height,
};
2025-07-15 16:18:16 +08:00
c->is_pending_open_animation = false;
2025-07-06 19:10:35 +08:00
if (animation_passed >= 1.0) {
2025-07-06 19:10:35 +08:00
// clear the open action state
// To prevent him from being mistaken that
// it's still in the opening animation in resize
c->animation.action = MOVE;
c->animation.tagining = false;
c->animation.running = false;
if (c->animation.tagouting) {
c->animation.tagouting = false;
wlr_scene_node_set_enabled(&c->scene->node, false);
client_set_suspended(c, true);
c->animation.tagouted = true;
c->animation.current = c->geom;
}
xytonode(cursor->x, cursor->y, NULL, &pointer_c, NULL, &sx, &sy);
surface =
pointer_c && pointer_c == c ? client_surface(pointer_c) : NULL;
if (surface && pointer_c == selmon->sel) {
wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
}
client_apply_node_layer(c);
2025-07-06 19:10:35 +08:00
// end flush in next frame, not the current frame
c->need_output_flush = false;
}
2025-07-13 09:11:43 +08:00
client_apply_clip(c, factor);
2025-07-06 19:10:35 +08:00
}
void init_fadeout_client(Client *c) {
if (!c->mon || client_is_unmanaged(c))
return;
if (!c->scene) {
return;
}
if (c->shield_when_capture && active_capture_count > 0) {
return;
}
2025-07-12 10:03:52 +08:00
if ((c->animation_type_close &&
strcmp(c->animation_type_close, "none") == 0) ||
(!c->animation_type_close &&
strcmp(animation_type_close, "none") == 0)) {
2025-07-06 19:10:35 +08:00
return;
}
2026-02-06 15:18:31 +05:45
Client *fadeout_client = ecalloc(1, sizeof(*fadeout_client));
2025-07-06 19:10:35 +08:00
wlr_scene_node_set_enabled(&c->scene->node, true);
client_set_border_color(c, bordercolor);
2026-02-06 15:18:31 +05:45
fadeout_client->scene =
2025-07-06 19:10:35 +08:00
wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]);
wlr_scene_node_set_enabled(&c->scene->node, false);
2026-02-06 15:18:31 +05:45
if (!fadeout_client->scene) {
free(fadeout_client);
2025-07-06 19:10:35 +08:00
return;
}
2026-02-06 15:18:31 +05:45
fadeout_client->animation.duration = animation_duration_close;
fadeout_client->geom = fadeout_client->current =
fadeout_client->animainit_geom = fadeout_client->animation.initial =
2025-07-06 19:10:35 +08:00
c->animation.current;
2026-02-06 15:18:31 +05:45
fadeout_client->mon = c->mon;
fadeout_client->animation_type_close = c->animation_type_close;
fadeout_client->animation.action = CLOSE;
fadeout_client->bw = c->bw;
fadeout_client->nofadeout = c->nofadeout;
2025-07-06 19:10:35 +08:00
// 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标
// 这跟普通node有区别
2026-02-06 15:18:31 +05:45
fadeout_client->animation.initial.x = 0;
fadeout_client->animation.initial.y = 0;
if ((!c->animation_type_close &&
strcmp(animation_type_close, "fade") == 0) ||
(c->animation_type_close &&
strcmp(c->animation_type_close, "fade") == 0)) {
2026-02-06 15:18:31 +05:45
fadeout_client->current.x = 0;
fadeout_client->current.y = 0;
fadeout_client->current.width = 0;
fadeout_client->current.height = 0;
} else if ((c->animation_type_close &&
strcmp(c->animation_type_close, "slide") == 0) ||
(!c->animation_type_close &&
strcmp(animation_type_close, "slide") == 0)) {
2026-02-06 15:18:31 +05:45
fadeout_client->current.y =
2025-07-06 19:10:35 +08:00
c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2
? c->mon->m.height -
(c->animation.current.y - c->mon->m.y) // down out
: c->mon->m.y - c->geom.height; // up out
2026-02-06 15:18:31 +05:45
fadeout_client->current.x = 0; // x无偏差垂直划出
2025-07-06 19:10:35 +08:00
} else {
2026-02-06 15:18:31 +05:45
fadeout_client->current.y =
(fadeout_client->geom.height -
fadeout_client->geom.height * zoom_end_ratio) /
2025-07-06 19:10:35 +08:00
2;
2026-02-06 15:18:31 +05:45
fadeout_client->current.x =
(fadeout_client->geom.width -
fadeout_client->geom.width * zoom_end_ratio) /
2025-07-06 19:10:35 +08:00
2;
2026-02-06 15:18:31 +05:45
fadeout_client->current.width =
fadeout_client->geom.width * zoom_end_ratio;
fadeout_client->current.height =
fadeout_client->geom.height * zoom_end_ratio;
2025-07-06 19:10:35 +08:00
}
2026-02-06 15:18:31 +05:45
fadeout_client->animation.time_started = get_now_in_ms();
wlr_scene_node_set_enabled(&fadeout_client->scene->node, true);
wl_list_insert(&fadeout_clients, &fadeout_client->fadeout_link);
// 请求刷新屏幕
request_fresh_all_monitors();
2025-07-06 19:10:35 +08:00
}
void client_commit(Client *c) {
c->current = c->pending; // 设置动画的结束位置
if (c->animation.should_animate) {
if (!c->animation.running) {
c->animation.current = c->animainit_geom;
}
c->animation.initial = c->animainit_geom;
c->animation.time_started = get_now_in_ms();
2025-07-06 19:10:35 +08:00
// 标记动画开始
c->animation.running = true;
c->animation.should_animate = false;
}
// 请求刷新屏幕
request_fresh_all_monitors();
2025-07-06 19:10:35 +08:00
}
void client_set_pending_state(Client *c) {
2025-11-02 12:04:15 +08:00
if (!c || c->iskilling)
return;
2025-07-06 19:10:35 +08:00
// 判断是否需要动画
if (!animations) {
c->animation.should_animate = false;
} else if (animations && c->animation.tagining) {
c->animation.should_animate = true;
} else if (!animations || c == grabc ||
2025-07-15 16:18:16 +08:00
(!c->is_pending_open_animation &&
2025-07-06 19:10:35 +08:00
wlr_box_equal(&c->current, &c->pending))) {
c->animation.should_animate = false;
} else {
c->animation.should_animate = true;
}
2025-07-12 10:03:52 +08:00
if (((c->animation_type_open &&
strcmp(c->animation_type_open, "none") == 0) ||
(!c->animation_type_open &&
strcmp(animation_type_open, "none") == 0)) &&
c->animation.action == OPEN) {
2025-09-08 16:26:53 +08:00
c->animation.duration = 0;
2025-07-12 10:03:52 +08:00
}
if (c->istagswitching) {
c->animation.duration = 0;
c->istagswitching = 0;
}
2025-10-09 13:09:40 +08:00
if (start_drag_window) {
c->animation.should_animate = false;
c->animation.duration = 0;
}
if (c->isnoanimation) {
c->animation.should_animate = false;
c->animation.duration = 0;
}
2025-07-06 19:10:35 +08:00
// 开始动画
client_commit(c);
c->dirty = true;
}
void resize(Client *c, struct wlr_box geo, int32_t interact) {
2025-07-06 19:10:35 +08:00
// 动画设置的起始函数,这里用来计算一些动画的起始值
// 动画起始位置大小是由于c->animainit_geom确定的
if (!c || !c->mon || !client_surface(c)->mapped)
return;
struct wlr_box *bbox;
struct wlr_box clip;
if (!c->mon)
return;
c->need_output_flush = true;
2025-08-02 23:02:21 +08:00
c->dirty = true;
2025-07-06 19:10:35 +08:00
2025-09-09 11:02:47 +08:00
// float_geom = c->geom;
2025-07-06 19:10:35 +08:00
bbox = (interact || c->isfloating || c->isfullscreen) ? &sgeom : &c->mon->w;
if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) {
c->geom = geo;
c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width);
c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height);
2025-07-06 19:10:35 +08:00
} else { // 这里会限制不允许窗口划出屏幕
c->geom = geo;
applybounds(
c,
bbox); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常
}
if (!c->isnosizehint && !c->ismaximizescreen && !c->isfullscreen &&
c->isfloating) {
client_set_size_bound(c);
}
2025-07-15 16:18:16 +08:00
if (!c->is_pending_open_animation) {
2025-07-06 19:10:35 +08:00
c->animation.begin_fade_in = false;
}
if (c->animation.action == OPEN && !c->animation.tagining &&
!c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) {
c->animation.action = c->animation.action;
} else if (c->animation.tagouting) {
c->animation.duration = animation_duration_tag;
c->animation.action = TAG;
} else if (c->animation.tagining) {
c->animation.duration = animation_duration_tag;
c->animation.action = TAG;
2025-07-15 16:18:16 +08:00
} else if (c->is_pending_open_animation) {
2025-07-06 19:10:35 +08:00
c->animation.duration = animation_duration_open;
c->animation.action = OPEN;
} else {
c->animation.duration = animation_duration_move;
c->animation.action = MOVE;
}
// 动画起始位置大小设置
if (c->animation.tagouting) {
c->animainit_geom = c->animation.current;
} else if (c->animation.tagining) {
c->animainit_geom.height = c->animation.current.height;
c->animainit_geom.width = c->animation.current.width;
2025-07-15 16:18:16 +08:00
} else if (c->is_pending_open_animation) {
2026-02-06 15:18:31 +05:45
set_client_open_animation(c, c->geom);
2025-07-06 19:10:35 +08:00
} else {
c->animainit_geom = c->animation.current;
}
if (c->isnoborder || c->iskilling) {
c->bw = 0;
}
2026-01-24 23:04:59 +08:00
bool hit_no_border = check_hit_no_border(c);
if (hit_no_border && smartgaps) {
c->bw = 0;
c->fake_no_border = true;
}
2025-07-06 19:10:35 +08:00
// c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局
c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw);
2026-02-23 08:01:45 +08:00
if (c->configure_serial != 0) {
c->mon->resizing_count_pending++;
}
2025-07-06 19:10:35 +08:00
if (c == grabc) {
c->animation.running = false;
c->need_output_flush = false;
2025-07-06 19:10:35 +08:00
c->animainit_geom = c->current = c->pending = c->animation.current =
c->geom;
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
2025-07-06 19:10:35 +08:00
client_get_clip(c, &clip);
2026-02-27 09:05:00 +08:00
apply_border(c);
apply_shield(c);
wlr_scene_tree_set_clip(c->scene_surface, &clip);
2025-07-06 19:10:35 +08:00
return;
}
// 如果不是工作区切换时划出去的窗口,就让动画的结束位置,就是上面的真实位置和大小
// c->pending 决定动画的终点一般在其他调用resize的函数的附近设置了
if (!c->animation.tagouting && !c->iskilling) {
c->pending = c->geom;
}
if (c->swallowedby && c->animation.action == OPEN) {
c->animainit_geom = c->swallowedby->animation.current;
}
if (c->swallowing) {
c->animainit_geom = c->geom;
}
if ((c->isglobal || c->isunglobal) && c->isfloating &&
c->animation.action == TAG) {
c->animainit_geom = c->geom;
}
if (c->scratchpad_switching_mon && c->isfloating) {
c->animainit_geom = c->geom;
}
client_apply_node_layer(c);
2025-07-06 19:10:35 +08:00
// 开始应用动画设置
client_set_pending_state(c);
setborder_color(c);
}
bool client_draw_fadeout_frame(Client *c) {
if (!c)
return false;
fadeout_client_animation_next_tick(c);
return true;
}
2025-11-01 12:08:39 +08:00
void client_set_focused_opacity_animation(Client *c) {
float *border_color = get_border_color(c);
if (!animations) {
setborder_color(c);
return;
}
c->opacity_animation.duration = animation_duration_focus;
2025-11-01 12:08:39 +08:00
memcpy(c->opacity_animation.target_border_color, border_color,
sizeof(c->opacity_animation.target_border_color));
c->opacity_animation.target_opacity = c->focused_opacity;
c->opacity_animation.time_started = get_now_in_ms();
memcpy(c->opacity_animation.initial_border_color,
c->opacity_animation.current_border_color,
sizeof(c->opacity_animation.initial_border_color));
c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
2025-11-01 12:08:39 +08:00
c->opacity_animation.running = true;
}
void client_set_unfocused_opacity_animation(Client *c) {
2025-11-01 12:08:39 +08:00
// Start border color animation to unfocused
float *border_color = get_border_color(c);
if (!animations) {
setborder_color(c);
return;
}
c->opacity_animation.duration = animation_duration_focus;
2025-11-01 12:08:39 +08:00
memcpy(c->opacity_animation.target_border_color, border_color,
sizeof(c->opacity_animation.target_border_color));
// Start opacity animation to unfocused
c->opacity_animation.target_opacity = c->unfocused_opacity;
c->opacity_animation.time_started = get_now_in_ms();
2025-11-01 12:08:39 +08:00
memcpy(c->opacity_animation.initial_border_color,
c->opacity_animation.current_border_color,
sizeof(c->opacity_animation.initial_border_color));
c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
2025-11-01 12:08:39 +08:00
c->opacity_animation.running = true;
}
bool client_apply_focus_opacity(Client *c) {
// Animate focus transitions (opacity + border color)
float *border_color = get_border_color(c);
if (c->isfullscreen) {
c->opacity_animation.running = false;
client_set_opacity(c, 1);
} else if (c->animation.running && c->animation.action == OPEN) {
c->opacity_animation.running = false;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double linear_progress =
c->animation.duration
? (double)passed_time / (double)c->animation.duration
: 1.0;
2025-11-01 12:08:39 +08:00
2025-12-12 11:08:53 +08:00
double opacity_eased_progress =
find_animation_curve_at(linear_progress, OPAFADEIN);
2025-11-02 09:24:59 +08:00
float percent =
2025-12-12 11:08:53 +08:00
animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0;
2025-11-02 09:24:59 +08:00
float opacity =
c == selmon->sel ? c->focused_opacity : c->unfocused_opacity;
float target_opacity =
percent * (1.0 - fadein_begin_opacity) + fadein_begin_opacity;
2025-11-01 12:08:39 +08:00
if (target_opacity > opacity) {
target_opacity = opacity;
}
memcpy(c->opacity_animation.current_border_color,
c->opacity_animation.target_border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.current_opacity = target_opacity;
2025-11-01 12:08:39 +08:00
client_set_opacity(c, target_opacity);
client_set_border_color(c, c->opacity_animation.target_border_color);
2025-11-01 12:08:39 +08:00
} else if (animations && c->opacity_animation.running) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t passed_time =
timespec_to_ms(&now) - c->opacity_animation.time_started;
double linear_progress =
c->opacity_animation.duration
? (double)passed_time / (double)c->opacity_animation.duration
: 1.0;
2025-11-01 12:08:39 +08:00
float eased_progress = find_animation_curve_at(linear_progress, FOCUS);
c->opacity_animation.current_opacity =
c->opacity_animation.initial_opacity +
(c->opacity_animation.target_opacity -
c->opacity_animation.initial_opacity) *
eased_progress;
client_set_opacity(c, c->opacity_animation.current_opacity);
// Animate border color
for (int32_t i = 0; i < 4; i++) {
2025-11-01 12:08:39 +08:00
c->opacity_animation.current_border_color[i] =
c->opacity_animation.initial_border_color[i] +
(c->opacity_animation.target_border_color[i] -
c->opacity_animation.initial_border_color[i]) *
eased_progress;
}
client_set_border_color(c, c->opacity_animation.current_border_color);
2026-01-28 14:50:17 +08:00
if (linear_progress >= 1.0f) {
2025-11-01 12:08:39 +08:00
c->opacity_animation.running = false;
} else {
return true;
}
} else if (c == selmon->sel) {
c->opacity_animation.running = false;
c->opacity_animation.current_opacity = c->focused_opacity;
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
client_set_opacity(c, c->focused_opacity);
} else {
c->opacity_animation.running = false;
c->opacity_animation.current_opacity = c->unfocused_opacity;
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
client_set_opacity(c, c->unfocused_opacity);
}
return false;
}
2025-07-06 19:10:35 +08:00
bool client_draw_frame(Client *c) {
if (!c || !client_surface(c)->mapped)
return false;
2025-11-01 12:08:39 +08:00
if (!c->need_output_flush) {
return client_apply_focus_opacity(c);
2025-07-06 19:10:35 +08:00
}
if (animations && c->animation.running) {
client_animation_next_tick(c);
} else {
wlr_scene_node_set_position(&c->scene->node, c->pending.x,
c->pending.y);
c->animation.current = c->animainit_geom = c->animation.initial =
c->pending = c->current = c->geom;
2025-07-13 09:11:43 +08:00
client_apply_clip(c, 1.0);
2025-07-06 19:10:35 +08:00
c->need_output_flush = false;
}
2025-11-01 12:08:39 +08:00
client_apply_focus_opacity(c);
2025-07-06 19:10:35 +08:00
return true;
}