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 c53638e..56a40d5 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,22 +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; - 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); + 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 { @@ -1074,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/animation/common.h b/src/animation/common.h index edd7517..edd39b0 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 405ae93..97db321 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; @@ -1130,6 +1132,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) { @@ -1155,6 +1159,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) { @@ -2593,6 +2604,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 = @@ -2725,6 +2738,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() { @@ -2747,6 +2762,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 = @@ -2864,6 +2881,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..39cf509 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 = 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.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 ce93a3b..6e67522 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -372,3 +372,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 6602a8d..412a27f 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 }; @@ -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; @@ -692,6 +703,8 @@ static void resize_tile_client(Client *grabc, bool isdrag, int offsetx, int offsety, unsigned int time); 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); static void request_fresh_all_monitors(void); #include "data/static_keymap.h" @@ -777,6 +790,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; @@ -3075,6 +3089,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 && @@ -3091,7 +3111,6 @@ void focusclient(Client *c, int lift) { // change border color c->isurgent = 0; - setborder_color(c); } if (c && !c->iskilling && c->foreign_toplevel) @@ -3119,18 +3138,17 @@ void focusclient(Client *c, int lift) { * probably other clients */ } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { - setborder_color(w); - 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); @@ -4127,23 +4145,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) { @@ -5401,9 +5407,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(); } } @@ -5552,7 +5558,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) { @@ -5634,7 +5640,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) {