From 8f706688debfd0b6bb2389e9f0895b30a9783cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20R=C4=85czka?= <04_barista_pads@icloud.com> Date: Fri, 31 Oct 2025 22:33:38 +0100 Subject: [PATCH 1/2] feat: add smooth focus transition animation for opacity and border This adds animated transitions when switching focus between windows. Both window opacity and border color now fade smoothly using cubic bezier easing instead of changing instantly. Implementation: - Added animation_duration_focus config option (default 400ms) - Added animation_curve_focus for cubic bezier easing curve - Window opacity and border color animate together when focus changes - Uses existing animation infrastructure (baked bezier points) The feature is backwards compatible and can be disabled by setting animation_duration_focus=0 in config file. Changes affect 5 files with minimal additions to keep code clean. --- src/animation/client.h | 31 +++++++++++++++++++++++++++---- src/animation/common.h | 10 ++++++++++ src/config/parse_config.h | 19 +++++++++++++++++++ src/config/preset.h | 2 ++ src/mango.c | 31 ++++++++++++++++++++++++++++--- 5 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 249b794..1c4e567 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1053,12 +1053,35 @@ bool client_draw_frame(Client *c) { if (!c || !client_surface(c)->mapped) return false; + // Animate focus transitions (opacity + border color) if (c->isfullscreen) { client_set_opacity(c, 1); - } else if (c == selmon->sel && !c->animation.running) { - client_set_opacity(c, c->focused_opacity); - } else if (!c->animation.running) { - client_set_opacity(c, c->unfocused_opacity); + c->current_opacity = 1; + c->target_opacity = 1; + } else if (c->opacity_animation_frames > 0 && c->opacity_animation_passed < c->opacity_animation_frames) { + float linear_progress = (float)c->opacity_animation_passed / c->opacity_animation_frames; + float eased_progress = find_animation_curve_at(linear_progress, FOCUS); + + // Animate opacity + float opacity_start = (c->target_opacity == c->focused_opacity) ? c->unfocused_opacity : c->focused_opacity; + c->current_opacity = opacity_start + (c->target_opacity - opacity_start) * eased_progress; + client_set_opacity(c, c->current_opacity); + + // Animate border color + bool focusing = (c->target_border_color[0] == focuscolor[0]); + float *border_start = focusing ? bordercolor : focuscolor; + for (int i = 0; i < 4; i++) { + c->current_border_color[i] = border_start[i] + (c->target_border_color[i] - border_start[i]) * eased_progress; + } + client_set_border_color(c, c->current_border_color); + + c->opacity_animation_passed++; + } else { + // Animation complete or disabled - apply target values + c->current_opacity = c->target_opacity; + client_set_opacity(c, c->current_opacity); + memcpy(c->current_border_color, c->target_border_color, sizeof(c->current_border_color)); + client_set_border_color(c, c->current_border_color); } if (!c->need_output_flush) diff --git a/src/animation/common.h b/src/animation/common.h index 70a595c..2d28377 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -9,6 +9,8 @@ struct dvec2 calculate_animation_curve_at(double t, int type) { animation_curve = animation_curve_tag; } else if (type == CLOSE) { animation_curve = animation_curve_close; + } else if (type == FOCUS) { + animation_curve = animation_curve_focus; } else { animation_curve = animation_curve_move; } @@ -28,6 +30,8 @@ void init_baked_points(void) { baked_points_tag = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_tag)); baked_points_close = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_close)); + baked_points_focus = + calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_focus)); for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { baked_points_move[i] = calculate_animation_curve_at( @@ -45,6 +49,10 @@ void init_baked_points(void) { baked_points_close[i] = calculate_animation_curve_at( (double)i / (BAKED_POINTS_COUNT - 1), CLOSE); } + for (unsigned int i = 0; i < BAKED_POINTS_COUNT; i++) { + baked_points_focus[i] = calculate_animation_curve_at( + (double)i / (BAKED_POINTS_COUNT - 1), FOCUS); + } } double find_animation_curve_at(double t, int type) { @@ -61,6 +69,8 @@ double find_animation_curve_at(double t, int type) { baked_points = baked_points_tag; } else if (type == CLOSE) { baked_points = baked_points_close; + } else if (type == FOCUS) { + baked_points = baked_points_focus; } else { baked_points = baked_points_move; } diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 16b85e6..3c372ff 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -178,10 +178,12 @@ typedef struct { uint32_t animation_duration_open; uint32_t animation_duration_tag; uint32_t animation_duration_close; + uint32_t animation_duration_focus; double animation_curve_move[4]; double animation_curve_open[4]; double animation_curve_tag[4]; double animation_curve_close[4]; + double animation_curve_focus[4]; int scroller_structs; float scroller_default_proportion; @@ -1132,6 +1134,8 @@ void parse_option(Config *config, char *key, char *value) { config->animation_duration_tag = atoi(value); } else if (strcmp(key, "animation_duration_close") == 0) { config->animation_duration_close = atoi(value); + } else if (strcmp(key, "animation_duration_focus") == 0) { + config->animation_duration_focus = atoi(value); } else if (strcmp(key, "animation_curve_move") == 0) { int num = parse_double_array(value, config->animation_curve_move, 4); if (num != 4) { @@ -1157,6 +1161,13 @@ void parse_option(Config *config, char *key, char *value) { "Error: Failed to parse animation_curve_close: %s\n", value); } + } else if (strcmp(key, "animation_curve_focus") == 0) { + int num = parse_double_array(value, config->animation_curve_focus, 4); + if (num != 4) { + fprintf(stderr, + "Error: Failed to parse animation_curve_focus: %s\n", + value); + } } else if (strcmp(key, "scroller_structs") == 0) { config->scroller_structs = atoi(value); } else if (strcmp(key, "scroller_default_proportion") == 0) { @@ -2595,6 +2606,8 @@ void override_config(void) { animation_duration_tag = CLAMP_INT(config.animation_duration_tag, 1, 50000); animation_duration_close = CLAMP_INT(config.animation_duration_close, 1, 50000); + animation_duration_focus = + CLAMP_INT(config.animation_duration_focus, 1, 50000); // 滚动布局设置 scroller_default_proportion = @@ -2727,6 +2740,8 @@ void override_config(void) { sizeof(animation_curve_tag)); memcpy(animation_curve_close, config.animation_curve_close, sizeof(animation_curve_close)); + memcpy(animation_curve_focus, config.animation_curve_focus, + sizeof(animation_curve_focus)); } void set_value_default() { @@ -2749,6 +2764,8 @@ void set_value_default() { animation_duration_tag; // Animation tag speed config.animation_duration_close = animation_duration_close; // Animation tag speed + config.animation_duration_focus = + animation_duration_focus; // Animation focus opacity speed /* appearance */ config.axis_bind_apply_timeout = @@ -2866,6 +2883,8 @@ void set_value_default() { sizeof(animation_curve_tag)); memcpy(config.animation_curve_close, animation_curve_close, sizeof(animation_curve_close)); + memcpy(config.animation_curve_focus, animation_curve_focus, + sizeof(animation_curve_focus)); memcpy(config.rootcolor, rootcolor, sizeof(rootcolor)); memcpy(config.bordercolor, bordercolor, sizeof(bordercolor)); diff --git a/src/config/preset.h b/src/config/preset.h index d9425a7..b15860a 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -25,10 +25,12 @@ 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_focus = 400; // Animation focus opacity 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}; // 动画曲线 double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 +double animation_curve_focus[4] = {0.08, 0.82, 0.17, 1}; // 动画曲线 /* appearance */ unsigned int axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔 diff --git a/src/mango.c b/src/mango.c index 17a93a5..683b304 100644 --- a/src/mango.c +++ b/src/mango.c @@ -161,7 +161,7 @@ enum { }; /* EWMH atoms */ #endif enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */ -enum { NONE, OPEN, MOVE, CLOSE, TAG }; +enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS }; enum { UNFOLD, FOLD, INVALIDFOLD }; enum { PREV, NEXT }; @@ -339,6 +339,12 @@ struct Client { int isunglobal; float focused_opacity; float unfocused_opacity; + float current_opacity; + float target_opacity; + unsigned int opacity_animation_frames; + unsigned int opacity_animation_passed; + float current_border_color[4]; + float target_border_color[4]; char oldmonname[128]; int noblur; double master_mfact_per, master_inner_per, stack_innder_per; @@ -777,6 +783,7 @@ struct dvec2 *baked_points_move; struct dvec2 *baked_points_open; struct dvec2 *baked_points_tag; struct dvec2 *baked_points_close; +struct dvec2 *baked_points_focus; static struct wl_event_source *hide_source; static bool cursor_hidden = false; @@ -3100,7 +3107,13 @@ void focusclient(Client *c, int lift) { // change border color c->isurgent = 0; - setborder_color(c); + // Start border color animation to focused + memcpy(c->target_border_color, focuscolor, sizeof(c->target_border_color)); + + // Start opacity animation to focused + c->target_opacity = c->focused_opacity; + c->opacity_animation_frames = (animation_duration_focus * 60) / 1000; // 60fps + c->opacity_animation_passed = 0; } if (c && !c->iskilling && c->foreign_toplevel) @@ -3128,7 +3141,13 @@ void focusclient(Client *c, int lift) { * probably other clients */ } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { - setborder_color(w); + // Start border color animation to unfocused + memcpy(w->target_border_color, bordercolor, sizeof(w->target_border_color)); + + // Start opacity animation to unfocused + w->target_opacity = w->unfocused_opacity; + w->opacity_animation_frames = (animation_duration_focus * 60) / 1000; // 60fps + w->opacity_animation_passed = 0; client_activate_surface(old_keyboard_focus_surface, 0); } @@ -3512,6 +3531,12 @@ void init_client_properties(Client *c) { c->fake_no_border = false; c->focused_opacity = focused_opacity; c->unfocused_opacity = unfocused_opacity; + c->current_opacity = unfocused_opacity; + c->target_opacity = unfocused_opacity; + c->opacity_animation_frames = 0; + c->opacity_animation_passed = 0; + memcpy(c->current_border_color, bordercolor, sizeof(c->current_border_color)); + memcpy(c->target_border_color, bordercolor, sizeof(c->target_border_color)); c->nofadein = 0; c->nofadeout = 0; c->no_force_center = 0; From 6fdb9627861ac5617f7fe980a5eb72faa06775b0 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 1 Nov 2025 12:08:39 +0800 Subject: [PATCH 2/2] fix: fix focus animation error --- config.conf | 2 + src/animation/client.h | 171 ++++++++++++++++++++++++++++------------- src/config/preset.h | 4 +- src/fetch/client.h | 18 +++++ src/mango.c | 90 +++++++++------------- 5 files changed, 174 insertions(+), 111 deletions(-) diff --git a/config.conf b/config.conf index 353bdab..59e4631 100644 --- a/config.conf +++ b/config.conf @@ -42,10 +42,12 @@ animation_duration_move=500 animation_duration_open=400 animation_duration_tag=350 animation_duration_close=800 +animation_duration_focus=0 animation_curve_open=0.46,1.0,0.29,1 animation_curve_move=0.46,1.0,0.29,1 animation_curve_tag=0.46,1.0,0.29,1 animation_curve_close=0.08,0.92,0,1 +animation_curve_focus=0.46,1.0,0.29,1 # Scroller Layout Setting scroller_structs=20 diff --git a/src/animation/client.h b/src/animation/client.h index 1c4e567..671b899 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -225,12 +225,6 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, wlr_scene_buffer_set_corner_radius(buffer, border_radius, buffer_data->corner_location); - - float target_opacity = buffer_data->percent + fadein_begin_opacity; - if (target_opacity > buffer_data->opacity) { - target_opacity = buffer_data->opacity; - } - wlr_scene_buffer_set_opacity(buffer, target_opacity); } void buffer_set_effect(Client *c, BufferData data) { @@ -517,7 +511,6 @@ void client_apply_clip(Client *c, float factor) { bool should_render_client_surface = false; struct ivec2 offset; BufferData buffer_data; - float opacity, percent; enum corner_location current_corner_location = set_client_corner_location(c); @@ -537,29 +530,17 @@ void client_apply_clip(Client *c, float factor) { apply_border(c); client_draw_shadow(c); - opacity = c->isfullscreen ? 1 - : c == selmon->sel ? c->focused_opacity - : c->unfocused_opacity; - if (clip_box.width <= 0 || clip_box.height <= 0) { return; } wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); buffer_set_effect(c, (BufferData){1.0f, 1.0f, clip_box.width, - clip_box.height, opacity, opacity, + clip_box.height, current_corner_location, true}); return; } - percent = - c->animation.action == OPEN && animation_fade_in && !c->nofadein - ? (double)c->animation.passed_frames / c->animation.total_frames - : 1.0; - opacity = c->isfullscreen ? 1 - : c == selmon->sel ? c->focused_opacity - : c->unfocused_opacity; - // 获取窗口动画实时位置矩形 unsigned int width, height; client_actual_size(c, &width, &height); @@ -614,8 +595,6 @@ void client_apply_clip(Client *c, float factor) { buffer_data.width = clip_box.width; buffer_data.height = clip_box.height; buffer_data.corner_location = current_corner_location; - buffer_data.percent = percent; - buffer_data.opacity = opacity; if (factor == 1.0) { buffer_data.width_scale = 1.0; @@ -1048,45 +1027,128 @@ bool client_draw_fadeout_frame(Client *c) { return true; } +void client_set_focused_opacity_animation(Client *c) { + float *border_color = get_border_color(c); + 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.total_frames = + animation_duration_focus / all_output_frame_duration_ms(); + c->opacity_animation.passed_frames = 0; + if (c->opacity_animation.running) { + 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; + } else { + memcpy(c->opacity_animation.initial_border_color, border_color, + sizeof(c->opacity_animation.initial_border_color)); + c->opacity_animation.initial_opacity = c->unfocused_opacity; + } + c->opacity_animation.running = true; +} + +void cleint_set_unfocused_opacity_animation(Client *c) { + // Start border color animation to unfocused + float *border_color = get_border_color(c); + 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.total_frames = + animation_duration_focus / all_output_frame_duration_ms(); + c->opacity_animation.passed_frames = 0; + + if (c->opacity_animation.running) { + 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; + } else { + memcpy(c->opacity_animation.initial_border_color, border_color, + sizeof(c->opacity_animation.initial_border_color)); + c->opacity_animation.initial_opacity = c->focused_opacity; + } + + 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; + float percent = + animation_fade_in && !c->nofadein + ? (double)c->animation.passed_frames / c->animation.total_frames + : 1.0; + float opacity = c->isfullscreen ? 1 + : c == selmon->sel ? c->focused_opacity + : c->unfocused_opacity; + + float target_opacity = percent + fadein_begin_opacity; + if (target_opacity > opacity) { + target_opacity = opacity; + } + client_set_opacity(c, target_opacity); + } else if (animations && c->opacity_animation.running) { + float linear_progress = (float)c->opacity_animation.passed_frames / + c->opacity_animation.total_frames; + 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 (int i = 0; i < 4; i++) { + 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); + if (linear_progress == 1.0f) { + c->opacity_animation.running = false; + } else { + c->opacity_animation.passed_frames++; + 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; +} + bool client_draw_frame(Client *c) { if (!c || !client_surface(c)->mapped) return false; - // Animate focus transitions (opacity + border color) - if (c->isfullscreen) { - client_set_opacity(c, 1); - c->current_opacity = 1; - c->target_opacity = 1; - } else if (c->opacity_animation_frames > 0 && c->opacity_animation_passed < c->opacity_animation_frames) { - float linear_progress = (float)c->opacity_animation_passed / c->opacity_animation_frames; - float eased_progress = find_animation_curve_at(linear_progress, FOCUS); - - // Animate opacity - float opacity_start = (c->target_opacity == c->focused_opacity) ? c->unfocused_opacity : c->focused_opacity; - c->current_opacity = opacity_start + (c->target_opacity - opacity_start) * eased_progress; - client_set_opacity(c, c->current_opacity); - - // Animate border color - bool focusing = (c->target_border_color[0] == focuscolor[0]); - float *border_start = focusing ? bordercolor : focuscolor; - for (int i = 0; i < 4; i++) { - c->current_border_color[i] = border_start[i] + (c->target_border_color[i] - border_start[i]) * eased_progress; - } - client_set_border_color(c, c->current_border_color); - - c->opacity_animation_passed++; - } else { - // Animation complete or disabled - apply target values - c->current_opacity = c->target_opacity; - client_set_opacity(c, c->current_opacity); - memcpy(c->current_border_color, c->target_border_color, sizeof(c->current_border_color)); - client_set_border_color(c, c->current_border_color); + if (!c->need_output_flush) { + return client_apply_focus_opacity(c); } - if (!c->need_output_flush) - return false; - if (animations && c->animation.running) { client_animation_next_tick(c); } else { @@ -1097,5 +1159,6 @@ bool client_draw_frame(Client *c) { client_apply_clip(c, 1.0); c->need_output_flush = false; } + client_apply_focus_opacity(c); return true; } diff --git a/src/config/preset.h b/src/config/preset.h index b15860a..39cf509 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -25,12 +25,12 @@ 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_focus = 400; // Animation focus opacity speed +uint32_t animation_duration_focus = 0; // Animation focus opacity 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}; // 动画曲线 double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 -double animation_curve_focus[4] = {0.08, 0.82, 0.17, 1}; // 动画曲线 +double animation_curve_focus[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线 /* appearance */ unsigned int axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔 diff --git a/src/fetch/client.h b/src/fetch/client.h index f0a4868..cfc5232 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -360,3 +360,21 @@ Client *get_next_stack_client(Client *c, bool reverse) { } return NULL; } + +float *get_border_color(Client *c) { + if (c->isurgent) { + return urgentcolor; + } else if (c->is_in_scratchpad && selmon && c == selmon->sel) { + return scratchpadcolor; + } else if (c->isglobal && selmon && c == selmon->sel) { + return globalcolor; + } else if (c->isoverlay && selmon && c == selmon->sel) { + return overlaycolor; + } else if (c->ismaximizescreen && selmon && c == selmon->sel) { + return maximizescreencolor; + } else if (selmon && c == selmon->sel) { + return focuscolor; + } else { + return bordercolor; + } +} \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index 683b304..b125e61 100644 --- a/src/mango.c +++ b/src/mango.c @@ -104,7 +104,7 @@ (A && !(A)->isfloating && !(A)->isminied && !(A)->iskilling && \ !(A)->ismaximizescreen && !(A)->isfullscreen) #define VISIBLEON(C, M) \ - ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) + ((C) && (M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) @@ -239,13 +239,23 @@ struct dwl_animation { int action; }; +struct dwl_opacity_animation { + bool running; + float current_opacity; + float target_opacity; + float initial_opacity; + unsigned int total_frames; + unsigned int passed_frames; + float current_border_color[4]; + float target_border_color[4]; + float initial_border_color[4]; +}; + typedef struct { float width_scale; float height_scale; int width; int height; - double percent; - float opacity; enum corner_location corner_location; bool should_scale; } BufferData; @@ -324,6 +334,7 @@ struct Client { float scroller_proportion; bool need_output_flush; struct dwl_animation animation; + struct dwl_opacity_animation opacity_animation; int isterm, noswallow; int allow_csd; int force_maximize; @@ -339,12 +350,6 @@ struct Client { int isunglobal; float focused_opacity; float unfocused_opacity; - float current_opacity; - float target_opacity; - unsigned int opacity_animation_frames; - unsigned int opacity_animation_passed; - float current_border_color[4]; - float target_border_color[4]; char oldmonname[128]; int noblur; double master_mfact_per, master_inner_per, stack_innder_per; @@ -699,6 +704,7 @@ static void resize_tile_client(Client *grabc, bool isdrag, int offsetx, static void refresh_monitors_workspaces_status(Monitor *m); static void init_client_properties(Client *c); static bool check_keyboard_rules_validate(struct xkb_rule_names *rules); +static float *get_border_color(Client *c); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -3091,6 +3097,12 @@ void focusclient(Client *c, int lift) { selmon->prevsel = selmon->sel; selmon->sel = c; + if (selmon->prevsel && !selmon->prevsel->iskilling) { + cleint_set_unfocused_opacity_animation(selmon->prevsel); + } + + client_set_focused_opacity_animation(c); + // decide whether need to re-arrange if (c && selmon->prevsel && @@ -3107,13 +3119,6 @@ void focusclient(Client *c, int lift) { // change border color c->isurgent = 0; - // Start border color animation to focused - memcpy(c->target_border_color, focuscolor, sizeof(c->target_border_color)); - - // Start opacity animation to focused - c->target_opacity = c->focused_opacity; - c->opacity_animation_frames = (animation_duration_focus * 60) / 1000; // 60fps - c->opacity_animation_passed = 0; } if (c && !c->iskilling && c->foreign_toplevel) @@ -3141,24 +3146,17 @@ void focusclient(Client *c, int lift) { * probably other clients */ } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { - // Start border color animation to unfocused - memcpy(w->target_border_color, bordercolor, sizeof(w->target_border_color)); - - // Start opacity animation to unfocused - w->target_opacity = w->unfocused_opacity; - w->opacity_animation_frames = (animation_duration_focus * 60) / 1000; // 60fps - w->opacity_animation_passed = 0; - client_activate_surface(old_keyboard_focus_surface, 0); } } printstatus(); if (!c) { - /* With no client, all we have left is to clear focus */ - if (selmon && selmon->sel) - selmon->sel = - NULL; // 这个很关键,因为很多地方用到当前窗口做计算,不重置成NULL就会到处有野指针 + + if (selmon && selmon->sel && + (!VISIBLEON(selmon->sel, selmon) || selmon->sel->iskilling || + !client_surface(selmon->sel)->mapped)) + selmon->sel = NULL; // clear text input focus state dwl_im_relay_set_focus(dwl_input_method_relay, NULL); @@ -3531,12 +3529,6 @@ void init_client_properties(Client *c) { c->fake_no_border = false; c->focused_opacity = focused_opacity; c->unfocused_opacity = unfocused_opacity; - c->current_opacity = unfocused_opacity; - c->target_opacity = unfocused_opacity; - c->opacity_animation_frames = 0; - c->opacity_animation_passed = 0; - memcpy(c->current_border_color, bordercolor, sizeof(c->current_border_color)); - memcpy(c->target_border_color, bordercolor, sizeof(c->target_border_color)); c->nofadein = 0; c->nofadeout = 0; c->no_force_center = 0; @@ -4161,23 +4153,11 @@ void requeststartdrag(struct wl_listener *listener, void *data) { void setborder_color(Client *c) { if (!c || !c->mon) return; - if (c->isurgent) { - client_set_border_color(c, urgentcolor); - return; - } - if (c->is_in_scratchpad && selmon && c == selmon->sel) { - client_set_border_color(c, scratchpadcolor); - } else if (c->isglobal && selmon && c == selmon->sel) { - client_set_border_color(c, globalcolor); - } else if (c->isoverlay && selmon && c == selmon->sel) { - client_set_border_color(c, overlaycolor); - } else if (c->ismaximizescreen && selmon && c == selmon->sel) { - client_set_border_color(c, maximizescreencolor); - } else if (selmon && c == selmon->sel) { - client_set_border_color(c, focuscolor); - } else { - client_set_border_color(c, bordercolor); - } + + float *border_color = get_border_color(c); + memcpy(c->opacity_animation.target_border_color, border_color, + sizeof(c->opacity_animation.target_border_color)); + client_set_border_color(c, border_color); } void exchange_two_client(Client *c1, Client *c2) { @@ -5485,9 +5465,9 @@ urgent(struct wl_listener *listener, void *data) { view_in_mon(&(Arg){.ui = c->tags}, true, c->mon, true); focusclient(c, 1); } else if (c != focustop(selmon)) { - if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); c->isurgent = 1; + if (client_surface(c)->mapped) + setborder_color(c); printstatus(); } } @@ -5636,7 +5616,7 @@ void activatex11(struct wl_listener *listener, void *data) { } else if (c != focustop(selmon)) { c->isurgent = 1; if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); + setborder_color(c); } if (need_arrange) { @@ -5718,7 +5698,7 @@ void sethints(struct wl_listener *listener, void *data) { printstatus(); if (c->isurgent && surface && surface->mapped) - client_set_border_color(c, urgentcolor); + setborder_color(c); } void xwaylandready(struct wl_listener *listener, void *data) {