From 0fc7559c3c3f229ab4e5afa9a115988c6be5d824 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Fri, 19 Jun 2026 19:32:47 +0800 Subject: [PATCH 01/70] version: chase wlroots 0.20 --- docs/configuration/miscellaneous.md | 2 +- docs/window-management/rules.md | 2 + meson.build | 6 +- src/animation/client.h | 128 +++- src/animation/common.h | 9 +- src/animation/layer.h | 14 +- src/config/parse_config.h | 7 +- src/ext-protocol/ext-workspace.h | 2 +- src/ext-protocol/text-input.h | 11 +- src/ext-protocol/wlr_ext_workspace_v1.c | 975 ------------------------ src/ext-protocol/wlr_ext_workspace_v1.h | 150 ---- src/fetch/monitor.h | 2 +- src/mango.c | 230 +++++- 13 files changed, 330 insertions(+), 1208 deletions(-) delete mode 100644 src/ext-protocol/wlr_ext_workspace_v1.c delete mode 100644 src/ext-protocol/wlr_ext_workspace_v1.h diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index cd8d167d..6fb5dc8f 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -8,7 +8,7 @@ description: Advanced settings for XWayland, focus behavior, and system integrat | Setting | Default | Description | | :--- | :--- | :--- | | `xwayland_persistence` | `1` | Keep XWayland running even when no X11 apps are open (reduces startup lag). | -| `syncobj_enable` | `0` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** | +| `syncobj_enable` | `1` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** | | `allow_lock_transparent` | `0` | Allow the lock screen to be transparent. | | `allow_shortcuts_inhibit` | `1` | Allow shortcuts to be inhibited by clients. | | `vrr` | - | Set via [monitor rule](/docs/configuration/monitors#monitor-rules). | diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md index b5ae81b1..5c97961c 100644 --- a/docs/window-management/rules.md +++ b/docs/window-management/rules.md @@ -35,6 +35,8 @@ windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values | `single_scratchpad` | integer | `0` / `1` (default 1) | Only show one out of named scratchpads or the normal scratchpad | | `allow_shortcuts_inhibit` | integer | `0` / `1` (default 1) | Allow shortcuts to be inhibited by clients | | `idleinhibit_when_focus` | integer | `0` / `1` (default 0) | Automatically keep idle inhibit active when this window is focused | +| `shield_when_capture` | integer | `0` / `1` | Shield window when captured | + ### Geometry & Position diff --git a/meson.build b/meson.build index c0c22202..938371e5 100644 --- a/meson.build +++ b/meson.build @@ -30,12 +30,12 @@ libdrm = dependency('libdrm') xcb = dependency('xcb', required : get_option('xwayland')) xlibs = dependency('xcb-icccm', required : get_option('xwayland')) wayland_server_dep = dependency('wayland-server',version: '>=1.23.1') -wlroots_dep = dependency('wlroots-0.19',version: '>=0.19.0') +wlroots_dep = dependency('wlroots-0.20',version: '>=0.20.0') xkbcommon_dep = dependency('xkbcommon') libinput_dep = dependency('libinput',version: '>=1.27.1') libwayland_client_dep = dependency('wayland-client') pcre2_dep = dependency('libpcre2-8') -libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1') +libscenefx_dep = dependency('scenefx-0.5',version: '>=0.5.0') pixman_dep = dependency('pixman-1') cjson_dep = dependency('libcjson') pangocairo_dep = dependency('pangocairo') @@ -97,7 +97,7 @@ executable('mango', 'src/mango.c', 'src/common/util.c', 'src/draw/text-node.c', - 'src/ext-protocol/wlr_ext_workspace_v1.c', + wayland_sources, dependencies : [ libm, diff --git a/src/animation/client.h b/src/animation/client.h index 79544cd2..fb771da9 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -9,23 +9,28 @@ void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) { wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height)); } -enum corner_location set_client_corner_location(Client *c) { - enum corner_location current_corner_location = CORNER_LOCATION_ALL; +struct fx_corner_radii set_client_corner_location(Client *c) { + struct fx_corner_radii current_corner_location = + corner_radii_all(config.border_radius); struct wlr_box target_geom = config.animations ? c->animation.current : c->geom; if (target_geom.x + config.border_radius <= c->mon->m.x) { - current_corner_location &= ~CORNER_LOCATION_LEFT; + current_corner_location.top_left = 0; // 清除左标志位 + current_corner_location.bottom_left = 0; // 清除左标志位 } if (target_geom.x + target_geom.width - config.border_radius >= c->mon->m.x + c->mon->m.width) { - current_corner_location &= ~CORNER_LOCATION_RIGHT; + current_corner_location.top_right = 0; // 清除右标志位 + current_corner_location.bottom_right = 0; // 清除右标志位 } if (target_geom.y + config.border_radius <= c->mon->m.y) { - current_corner_location &= ~CORNER_LOCATION_TOP; + current_corner_location.top_left = 0; // 清除上标志位 + current_corner_location.top_right = 0; // 清除上标志位 } if (target_geom.y + target_geom.height - config.border_radius >= c->mon->m.y + c->mon->m.height) { - current_corner_location &= ~CORNER_LOCATION_BOTTOM; + current_corner_location.bottom_left = 0; // 清除下标志位 + current_corner_location.bottom_right = 0; // 清除下标志位 } return current_corner_location; } @@ -235,8 +240,7 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL) return; - wlr_scene_buffer_set_corner_radius(buffer, config.border_radius, - buffer_data->corner_location); + wlr_scene_buffer_set_corner_radii(buffer, buffer_data->corner_location); } void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer, @@ -268,8 +272,7 @@ void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer, if (is_subsurface) return; - wlr_scene_buffer_set_corner_radius(buffer, config.border_radius, - buffer_data->corner_location); + wlr_scene_buffer_set_corner_radii(buffer, buffer_data->corner_location); } void buffer_set_effect(Client *c, BufferData data) { @@ -285,10 +288,13 @@ void buffer_set_effect(Client *c, BufferData data) { if (c == grabc) data.should_scale = false; - if (c->isnoradius || c->isfullscreen || - (config.no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1)) { - data.corner_location = CORNER_LOCATION_NONE; + if (c->isfullscreen || (config.no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1)) { + data.corner_location = corner_radii_none(); + } + + if (config.blur && !c->noblur) { + wlr_scene_blur_set_corner_radii(c->blur, data.corner_location); } if (c->overview_scene_surface) { @@ -316,11 +322,11 @@ void client_draw_shadow(Client *c) { } bool hit_no_border = check_hit_no_border(c); - enum corner_location current_corner_location = + struct fx_corner_radii current_corner_location = c->isfullscreen || (config.no_radius_when_single && c->mon && c->mon->visible_tiling_clients == 1) - ? CORNER_LOCATION_NONE - : CORNER_LOCATION_ALL; + ? corner_radii_none() + : set_client_corner_location(c); int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0; @@ -350,7 +356,6 @@ void client_draw_shadow(Client *c) { struct clipped_region clipped_region = { .area = intersection_box, - .corner_radius = config.border_radius, .corners = current_corner_location, }; @@ -398,6 +403,46 @@ void client_draw_shadow(Client *c) { wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } +void apply_shield(Client *c, struct wlr_box clip_box) { + + if (clip_box.width <= 0 || clip_box.height <= 0) { + return; + } + + if (active_capture_count > 0 && c->shield_when_capture) { + wlr_scene_node_raise_to_top(&c->shield->node); + wlr_scene_node_set_position(&c->shield->node, clip_box.x, clip_box.y); + wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.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); + } + } +} + +void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { + + if (c->isfullscreen) { + if (c->blur->node.enabled) { + wlr_scene_node_set_enabled(&c->blur->node, false); + } + return; + } else { + if (config.blur && !c->noblur) { + wlr_scene_node_set_enabled(&c->blur->node, true); + wlr_scene_node_set_position(&c->blur->node, offset.x, offset.y); + wlr_scene_blur_set_size(c->blur, clip_box.width - c->bw, + clip_box.height - c->bw); + } else { + wlr_scene_node_set_enabled(&c->blur->node, false); + } + } +} + void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, int32_t height) { if (!c->tab_bar_node) @@ -509,7 +554,8 @@ void apply_border(Client *c) { if (c->isfullscreen) { if (c->border->node.enabled) { - wlr_scene_node_set_position(&c->scene_surface->node, 0, 0); + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); wlr_scene_node_set_enabled(&c->border->node, false); } return; @@ -520,16 +566,13 @@ void apply_border(Client *c) { } bool hit_no_border = check_hit_no_border(c); - apply_split_border(c, hit_no_border); - enum corner_location current_corner_location; - if (c->isfullscreen || (config.no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1)) { - current_corner_location = CORNER_LOCATION_NONE; - } else { - current_corner_location = set_client_corner_location(c); - } + struct fx_corner_radii current_corner_location = + c->isfullscreen || (config.no_radius_when_single && c->mon && + c->mon->visible_tiling_clients == 1) + ? corner_radii_none() + : set_client_corner_location(c); if (hit_no_border && config.smartgaps) { c->bw = 0; @@ -602,15 +645,13 @@ void apply_border(Client *c) { struct clipped_region clipped_region = { .area = {inner_surface_x, inner_surface_y, inner_surface_width, inner_surface_height}, - .corner_radius = config.border_radius, .corners = current_corner_location, }; wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); wlr_scene_rect_set_size(c->border, rect_width, rect_height); wlr_scene_node_set_position(&c->border->node, rect_x, rect_y); - wlr_scene_rect_set_corner_radius(c->border, config.border_radius, - current_corner_location); + wlr_scene_rect_set_corner_radii(c->border, current_corner_location); wlr_scene_rect_set_clipped_region(c->border, clipped_region); } @@ -882,7 +923,7 @@ void client_apply_clip(Client *c, float factor) { struct ivec2 offset; BufferData buffer_data; - enum corner_location current_corner_location = + struct fx_corner_radii current_corner_location = set_client_corner_location(c); if (!config.animations && !c->overview_scene_surface) { @@ -897,6 +938,8 @@ void client_apply_clip(Client *c, float factor) { apply_border(c); client_draw_shadow(c); + client_draw_blur(c, clip_box, offset); + apply_shield(c, clip_box); if (clip_box.width <= 0 || clip_box.height <= 0) { return; @@ -938,6 +981,8 @@ void client_apply_clip(Client *c, float factor) { // 应用窗口装饰 apply_border(c); client_draw_shadow(c); + apply_shield(c, clip_box); + client_draw_blur(c, clip_box, offset); // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 if (clip_box.width <= 0 || clip_box.height <= 0) { @@ -1135,6 +1180,10 @@ void init_fadeout_client(Client *c) { return; } + if (c->shield_when_capture && active_capture_count > 0) { + return; + } + if ((c->animation_type_close && strcmp(c->animation_type_close, "none") == 0) || (!c->animation_type_close && @@ -1379,10 +1428,15 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->geom; wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); - client_draw_shadow(c); apply_border(c); client_get_clip(c, &clip); + apply_shield(c, clip); + client_draw_shadow(c); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); + if (config.blur && !c->noblur) + wlr_scene_blur_set_size(c->blur, + c->animation.current.width - 2 * c->bw, + c->animation.current.height - 2 * c->bw); return; } // 如果不是工作区切换时划出去的窗口,就让动画的结束位置,就是上面的真实位置和大小 @@ -1512,6 +1566,16 @@ bool client_apply_focus_opacity(Client *c) { sizeof(c->opacity_animation.current_border_color)); c->opacity_animation.current_opacity = target_opacity; client_set_opacity(c, target_opacity); + if (config.blur && !c->noblur && !config.blur_optimized) { + wlr_scene_blur_set_strength( + c->blur, MIN(percent * (1.0 - config.fadein_begin_opacity) + + config.fadein_begin_opacity, + 1.0)); + wlr_scene_blur_set_alpha( + c->blur, MIN(percent * (1.0 - config.fadein_begin_opacity) + + config.fadein_begin_opacity, + 1.0)); + } client_set_border_color(c, c->opacity_animation.target_border_color); } else if (config.animations && c->opacity_animation.running) { diff --git a/src/animation/common.h b/src/animation/common.h index 6120d79c..cfd50045 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -212,11 +212,8 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, // Effects wlr_scene_buffer_set_opacity(snapshot_buffer, scene_buffer->opacity); - wlr_scene_buffer_set_corner_radius(snapshot_buffer, - scene_buffer->corner_radius, - scene_buffer->corners); - - wlr_scene_buffer_set_backdrop_blur(snapshot_buffer, false); + wlr_scene_buffer_set_corner_radii(snapshot_buffer, + scene_buffer->corners); if (scene_surface != NULL && scene_surface->surface->buffer != NULL) { wlr_scene_buffer_set_buffer(snapshot_buffer, @@ -248,6 +245,8 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, break; } + case WLR_SCENE_NODE_BLUR: + break; case WLR_SCENE_NODE_OPTIMIZED_BLUR: return true; } diff --git a/src/animation/layer.h b/src/animation/layer.h index 7ccabe33..80d7e6ca 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -187,8 +187,7 @@ void layer_draw_shadow(LayerSurface *l) { struct clipped_region clipped_region = { .area = intersection_box, - .corner_radius = config.border_radius, - .corners = config.border_radius_location_default, + .corners = corner_radii_all(config.border_radius), }; wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y); @@ -328,9 +327,14 @@ void layer_animation_next_tick(LayerSurface *l) { (1.0 - config.fadein_begin_opacity), 1.0f); - if (config.animation_fade_in) + if (config.animation_fade_in) { + if (config.blur && !l->noblur && !config.blur_optimized) { + wlr_scene_blur_set_strength(l->blur, opacity); + wlr_scene_blur_set_alpha(l->blur, opacity); + } wlr_scene_node_for_each_buffer(&l->scene->node, scene_buffer_apply_opacity, &opacity); + } wlr_scene_node_set_position(&l->scene->node, x, y); @@ -358,6 +362,10 @@ void layer_animation_next_tick(LayerSurface *l) { .height = height, }; + if (config.blur && config.blur_layer && !l->noblur && l->blur) + wlr_scene_blur_set_size(l->blur, l->animation.current.width, + l->animation.current.height); + if (animation_passed >= 1.0) { l->animation.running = false; l->need_output_flush = false; diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 61af421b..61a6da69 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -74,6 +74,7 @@ typedef struct { int32_t isunglobal; int32_t isglobal; int32_t isoverlay; + int32_t shield_when_capture; int32_t allow_shortcuts_inhibit; int32_t ignore_maximize; int32_t ignore_minimize; @@ -2349,6 +2350,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->isunglobal = -1; rule->isglobal = -1; rule->isoverlay = -1; + rule->shield_when_capture = -1; rule->allow_shortcuts_inhibit = -1; rule->ignore_maximize = -1; rule->ignore_minimize = -1; @@ -2456,6 +2458,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->focused_opacity = atof(val); } else if (strcmp(key, "isoverlay") == 0) { rule->isoverlay = atoi(val); + } else if (strcmp(key, "shield_when_capture") == 0) { + rule->shield_when_capture = atoi(val); } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { rule->allow_shortcuts_inhibit = atoi(val); } else if (strcmp(key, "ignore_maximize") == 0) { @@ -3683,7 +3687,7 @@ void set_value_default() { config.view_current_to_back = 0; config.single_scratchpad = 1; config.xwayland_persistence = 1; - config.syncobj_enable = 0; + config.syncobj_enable = 1; config.tag_carousel = 0; config.drag_tile_refresh_interval = 8.0f; config.drag_floating_refresh_interval = 8.0f; @@ -3738,7 +3742,6 @@ void set_value_default() { config.blur_layer = 0; config.blur_optimized = 1; config.border_radius = 0; - config.border_radius_location_default = CORNER_LOCATION_ALL; config.blur_params.num_passes = 1; config.blur_params.radius = 5; config.blur_params.noise = 0.02f; diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index eea0f455..82b7ef1f 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -1,4 +1,4 @@ -#include "wlr_ext_workspace_v1.h" +#include #define EXT_WORKSPACE_ENABLE_CAPS \ EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | \ diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index e9f221a4..7b4e1a1f 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -292,9 +292,8 @@ static void handle_input_method_commit(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); - struct wlr_input_method_v2 *input_method = data; struct text_input *text_input; - assert(relay->input_method == input_method); + struct wlr_input_method_v2 *input_method = relay->input_method; text_input = relay->active_text_input; if (!text_input) { @@ -324,7 +323,8 @@ static void handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, keyboard_grab_destroy); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = + relay->input_method->keyboard_grab; wl_list_remove(&relay->keyboard_grab_destroy.link); if (keyboard_grab->keyboard) { @@ -356,7 +356,6 @@ static void handle_input_method_destroy(struct wl_listener *listener, void *data) { struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy); - assert(relay->input_method == data); wl_list_remove(&relay->input_method_commit.link); wl_list_remove(&relay->input_method_grab_keyboard.link); wl_list_remove(&relay->input_method_new_popup_surface.link); @@ -562,11 +561,11 @@ struct dwl_input_method_relay *dwl_im_relay_create() { relay->popup_tree = wlr_scene_tree_create(&scene->tree); relay->new_text_input.notify = handle_new_text_input; - wl_signal_add(&text_input_manager->events.text_input, + wl_signal_add(&text_input_manager->events.new_text_input, &relay->new_text_input); relay->new_input_method.notify = handle_new_input_method; - wl_signal_add(&input_method_manager->events.input_method, + wl_signal_add(&input_method_manager->events.new_input_method, &relay->new_input_method); relay->focused_surface_destroy.notify = handle_focused_surface_destroy; diff --git a/src/ext-protocol/wlr_ext_workspace_v1.c b/src/ext-protocol/wlr_ext_workspace_v1.c deleted file mode 100644 index dca93e93..00000000 --- a/src/ext-protocol/wlr_ext_workspace_v1.c +++ /dev/null @@ -1,975 +0,0 @@ -#include "wlr_ext_workspace_v1.h" -#include "ext-workspace-v1-protocol.h" -#include -#include -#include -#include - -#define EXT_WORKSPACE_V1_VERSION 1 - -struct wlr_ext_workspace_v1_group_output { - struct wlr_output *output; - struct wlr_ext_workspace_group_handle_v1 *group; - struct wl_listener output_bind; - struct wl_listener output_destroy; - struct wl_list link; -}; - -// These structs wrap wl_resource of each interface to access the request queue -// (wlr_ext_workspace_manager_v1_resource.requests) assigned per manager -// resource - -struct wlr_ext_workspace_manager_v1_resource { - struct wl_resource *resource; - struct wlr_ext_workspace_manager_v1 *manager; - struct wl_list requests; // wlr_ext_workspace_v1_request.link - struct wl_list workspace_resources; // wlr_ext_workspace_v1_resource.link - struct wl_list group_resources; // wlr_ext_workspace_group_v1_resource.link - struct wl_list link; // wlr_ext_workspace_manager_v1.resources -}; - -struct wlr_ext_workspace_group_v1_resource { - struct wl_resource *resource; - struct wlr_ext_workspace_group_handle_v1 *group; - struct wlr_ext_workspace_manager_v1_resource *manager; - struct wl_list link; // wlr_ext_workspace_group_v1.resources - struct wl_list - manager_resource_link; // wlr_ext_workspace_manager_v1_resource.group_resources -}; - -struct wlr_ext_workspace_v1_resource { - struct wl_resource *resource; - struct wlr_ext_workspace_handle_v1 *workspace; - struct wlr_ext_workspace_manager_v1_resource *manager; - struct wl_list link; // wlr_ext_workspace_v1.resources - struct wl_list - manager_resource_link; // wlr_ext_workspace_manager_v1_resource.workspace_resources -}; - -static const struct ext_workspace_group_handle_v1_interface group_impl; - -static struct wlr_ext_workspace_group_v1_resource * -group_resource_from_resource(struct wl_resource *resource) { - assert(wl_resource_instance_of( - resource, &ext_workspace_group_handle_v1_interface, &group_impl)); - return wl_resource_get_user_data(resource); -} - -static const struct ext_workspace_handle_v1_interface workspace_impl; - -static struct wlr_ext_workspace_v1_resource * -workspace_resource_from_resource(struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &ext_workspace_handle_v1_interface, - &workspace_impl)); - return wl_resource_get_user_data(resource); -} - -static const struct ext_workspace_manager_v1_interface manager_impl; - -static struct wlr_ext_workspace_manager_v1_resource * -manager_resource_from_resource(struct wl_resource *resource) { - assert(wl_resource_instance_of( - resource, &ext_workspace_manager_v1_interface, &manager_impl)); - return wl_resource_get_user_data(resource); -} - -static void workspace_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void workspace_handle_activate(struct wl_client *client, - struct wl_resource *workspace_resource) { - struct wlr_ext_workspace_v1_resource *workspace_res = - workspace_resource_from_resource(workspace_resource); - if (!workspace_res) { - return; - } - - struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); - if (!req) { - wl_resource_post_no_memory(workspace_resource); - return; - } - req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE; - req->activate.workspace = workspace_res->workspace; - wl_list_insert(workspace_res->manager->requests.prev, &req->link); -} - -static void -workspace_handle_deactivate(struct wl_client *client, - struct wl_resource *workspace_resource) { - struct wlr_ext_workspace_v1_resource *workspace_res = - workspace_resource_from_resource(workspace_resource); - if (!workspace_res) { - return; - } - - struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); - if (!req) { - wl_resource_post_no_memory(workspace_resource); - return; - } - req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE; - req->deactivate.workspace = workspace_res->workspace; - wl_list_insert(workspace_res->manager->requests.prev, &req->link); -} - -static void workspace_handle_assign(struct wl_client *client, - struct wl_resource *workspace_resource, - struct wl_resource *group_resource) { - struct wlr_ext_workspace_v1_resource *workspace_res = - workspace_resource_from_resource(workspace_resource); - struct wlr_ext_workspace_group_v1_resource *group_res = - group_resource_from_resource(group_resource); - if (!workspace_res || !group_res) { - return; - } - - struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); - if (!req) { - wl_resource_post_no_memory(workspace_resource); - return; - } - req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN; - req->assign.group = group_res->group; - req->assign.workspace = workspace_res->workspace; - wl_list_insert(workspace_res->manager->requests.prev, &req->link); -} - -static void workspace_handle_remove(struct wl_client *client, - struct wl_resource *workspace_resource) { - struct wlr_ext_workspace_v1_resource *workspace_res = - workspace_resource_from_resource(workspace_resource); - if (!workspace_res) { - return; - } - - struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); - if (!req) { - wl_resource_post_no_memory(workspace_resource); - return; - } - req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE; - req->remove.workspace = workspace_res->workspace; - wl_list_insert(workspace_res->manager->requests.prev, &req->link); -} - -static const struct ext_workspace_handle_v1_interface workspace_impl = { - .destroy = workspace_handle_destroy, - .activate = workspace_handle_activate, - .deactivate = workspace_handle_deactivate, - .assign = workspace_handle_assign, - .remove = workspace_handle_remove, -}; - -static void group_handle_create_workspace(struct wl_client *client, - struct wl_resource *group_resource, - const char *name) { - struct wlr_ext_workspace_group_v1_resource *group_res = - group_resource_from_resource(group_resource); - if (!group_res) { - return; - } - - struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); - if (!req) { - wl_resource_post_no_memory(group_resource); - return; - } - req->create_workspace.name = strdup(name); - if (!req->create_workspace.name) { - free(req); - wl_resource_post_no_memory(group_resource); - return; - } - req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE; - req->create_workspace.group = group_res->group; - wl_list_insert(group_res->manager->requests.prev, &req->link); -} - -static void group_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static const struct ext_workspace_group_handle_v1_interface group_impl = { - .create_workspace = group_handle_create_workspace, - .destroy = group_handle_destroy, -}; - -static void destroy_workspace_resource( - struct wlr_ext_workspace_v1_resource *workspace_res) { - wl_list_remove(&workspace_res->link); - wl_list_remove(&workspace_res->manager_resource_link); - wl_resource_set_user_data(workspace_res->resource, NULL); - free(workspace_res); -} - -static void workspace_resource_destroy(struct wl_resource *resource) { - struct wlr_ext_workspace_v1_resource *workspace_res = - workspace_resource_from_resource(resource); - if (workspace_res) { - destroy_workspace_resource(workspace_res); - } -} - -static struct wlr_ext_workspace_v1_resource *create_workspace_resource( - struct wlr_ext_workspace_handle_v1 *workspace, - struct wlr_ext_workspace_manager_v1_resource *manager_res) { - struct wlr_ext_workspace_v1_resource *workspace_res = - calloc(1, sizeof(*workspace_res)); - if (!workspace_res) { - return NULL; - } - - struct wl_client *client = wl_resource_get_client(manager_res->resource); - workspace_res->resource = - wl_resource_create(client, &ext_workspace_handle_v1_interface, - wl_resource_get_version(manager_res->resource), 0); - if (!workspace_res->resource) { - free(workspace_res); - return NULL; - } - wl_resource_set_implementation(workspace_res->resource, &workspace_impl, - workspace_res, workspace_resource_destroy); - - workspace_res->workspace = workspace; - workspace_res->manager = manager_res; - wl_list_insert(&workspace->resources, &workspace_res->link); - wl_list_insert(&manager_res->workspace_resources, - &workspace_res->manager_resource_link); - - return workspace_res; -} - -static void -destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) { - wl_list_remove(&group_res->link); - wl_list_remove(&group_res->manager_resource_link); - wl_resource_set_user_data(group_res->resource, NULL); - free(group_res); -} - -static void group_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_ext_workspace_group_v1_resource *group_res = - group_resource_from_resource(resource); - if (group_res) { - destroy_group_resource(group_res); - } -} - -static struct wlr_ext_workspace_group_v1_resource *create_group_resource( - struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_ext_workspace_manager_v1_resource *manager_res) { - struct wlr_ext_workspace_group_v1_resource *group_res = - calloc(1, sizeof(*group_res)); - if (!group_res) { - return NULL; - } - - struct wl_client *client = wl_resource_get_client(manager_res->resource); - uint32_t version = wl_resource_get_version(manager_res->resource); - group_res->resource = wl_resource_create( - client, &ext_workspace_group_handle_v1_interface, version, 0); - if (group_res->resource == NULL) { - free(group_res); - return NULL; - } - wl_resource_set_implementation(group_res->resource, &group_impl, group_res, - group_handle_resource_destroy); - - group_res->group = group; - group_res->manager = manager_res; - wl_list_insert(&group->resources, &group_res->link); - wl_list_insert(&manager_res->group_resources, - &group_res->manager_resource_link); - - return group_res; -} - -static void -destroy_requests(struct wlr_ext_workspace_manager_v1_resource *manager_res) { - struct wlr_ext_workspace_v1_request *req, *tmp; - wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { - if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE) { - free(req->create_workspace.name); - } - wl_list_remove(&req->link); - free(req); - } -} - -static void -clear_requests_by(struct wlr_ext_workspace_manager_v1_resource *manager_res, - struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_ext_workspace_handle_v1 *workspace) { - struct wlr_ext_workspace_v1_request *req, *tmp; - wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { - switch (req->type) { - case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE: - if (group && req->create_workspace.group == group) { - req->create_workspace.group = NULL; - } - break; - case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: - if (workspace && req->activate.workspace == workspace) { - req->activate.workspace = NULL; - } - break; - case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: - if (workspace && req->deactivate.workspace == workspace) { - req->deactivate.workspace = NULL; - } - break; - case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN: - if (workspace && req->assign.workspace == workspace) { - req->assign.workspace = NULL; - } - if (group && req->assign.group == group) { - req->assign.group = NULL; - } - break; - case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE: - if (workspace && req->remove.workspace == workspace) { - req->remove.workspace = NULL; - } - break; - } - } -} - -static void manager_handle_commit(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_ext_workspace_manager_v1_resource *manager_res = - manager_resource_from_resource(resource); - if (!manager_res) { - return; - } - - struct wlr_ext_workspace_v1_commit_event commit_event = { - .requests = &manager_res->requests, - }; - wl_signal_emit_mutable(&manager_res->manager->events.commit, &commit_event); - destroy_requests(manager_res); -} - -static void handle_idle(void *data) { - struct wlr_ext_workspace_manager_v1 *manager = data; - - struct wlr_ext_workspace_manager_v1_resource *manager_res; - wl_list_for_each(manager_res, &manager->resources, link) { - ext_workspace_manager_v1_send_done(manager_res->resource); - } - manager->idle_source = NULL; -} - -static void -manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) { - if (!manager->idle_source) { - manager->idle_source = - wl_event_loop_add_idle(manager->event_loop, handle_idle, manager); - } -} - -static void -workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) { - struct wlr_ext_workspace_handle_v1 *workspace = workspace_res->workspace; - struct wl_resource *resource = workspace_res->resource; - - ext_workspace_handle_v1_send_capabilities(resource, workspace->caps); - if (workspace->coordinates.size > 0) { - ext_workspace_handle_v1_send_coordinates(resource, - &workspace->coordinates); - } - if (workspace->name) { - ext_workspace_handle_v1_send_name(resource, workspace->name); - } - if (workspace->id) { - ext_workspace_handle_v1_send_id(resource, workspace->id); - } - ext_workspace_handle_v1_send_state(resource, workspace->state); - manager_schedule_done(workspace->manager); -} - -static void manager_handle_stop(struct wl_client *client, - struct wl_resource *resource) { - ext_workspace_manager_v1_send_finished(resource); - wl_resource_destroy(resource); -} - -static const struct ext_workspace_manager_v1_interface manager_impl = { - .commit = manager_handle_commit, - .stop = manager_handle_stop, -}; - -static void destroy_manager_resource( - struct wlr_ext_workspace_manager_v1_resource *manager_res) { - destroy_requests(manager_res); - - struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2; - wl_list_for_each_safe(workspace_res, tmp2, - &manager_res->workspace_resources, - manager_resource_link) { - destroy_workspace_resource(workspace_res); - } - struct wlr_ext_workspace_group_v1_resource *group_res, *tmp3; - wl_list_for_each_safe(group_res, tmp3, &manager_res->group_resources, - manager_resource_link) { - destroy_group_resource(group_res); - } - - wl_list_remove(&manager_res->link); - wl_resource_set_user_data(manager_res->resource, NULL); - free(manager_res); -} - -static void manager_resource_destroy(struct wl_resource *resource) { - struct wlr_ext_workspace_manager_v1_resource *manager_res = - manager_resource_from_resource(resource); - if (manager_res) { - destroy_manager_resource(manager_res); - } -} - -static void -group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) { - struct wlr_ext_workspace_group_handle_v1 *group = group_res->group; - struct wl_resource *resource = group_res->resource; - struct wl_client *client = wl_resource_get_client(resource); - - ext_workspace_group_handle_v1_send_capabilities(resource, group->caps); - - struct wlr_ext_workspace_v1_group_output *group_output; - wl_list_for_each(group_output, &group->outputs, link) { - struct wl_resource *output_resource; - wl_resource_for_each(output_resource, - &group_output->output->resources) { - if (wl_resource_get_client(output_resource) == client) { - ext_workspace_group_handle_v1_send_output_enter( - resource, output_resource); - } - } - } - - manager_schedule_done(group->manager); -} - -static void manager_bind(struct wl_client *client, void *data, uint32_t version, - uint32_t id) { - struct wlr_ext_workspace_manager_v1 *manager = data; - - struct wlr_ext_workspace_manager_v1_resource *manager_res = - calloc(1, sizeof(*manager_res)); - if (!manager_res) { - wl_client_post_no_memory(client); - return; - } - - manager_res->manager = manager; - wl_list_init(&manager_res->requests); - wl_list_init(&manager_res->workspace_resources); - wl_list_init(&manager_res->group_resources); - - manager_res->resource = wl_resource_create( - client, &ext_workspace_manager_v1_interface, version, id); - if (!manager_res->resource) { - free(manager_res); - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(manager_res->resource, &manager_impl, - manager_res, manager_resource_destroy); - wl_list_insert(&manager->resources, &manager_res->link); - - struct wlr_ext_workspace_group_handle_v1 *group; - wl_list_for_each(group, &manager->groups, link) { - struct wlr_ext_workspace_group_v1_resource *group_res = - create_group_resource(group, manager_res); - if (!group_res) { - wl_resource_post_no_memory(manager_res->resource); - continue; - } - ext_workspace_manager_v1_send_workspace_group(manager_res->resource, - group_res->resource); - group_send_details(group_res); - } - - struct wlr_ext_workspace_handle_v1 *workspace; - wl_list_for_each(workspace, &manager->workspaces, link) { - struct wlr_ext_workspace_v1_resource *workspace_res = - create_workspace_resource(workspace, manager_res); - if (!workspace_res) { - wl_resource_post_no_memory(manager_res->resource); - continue; - } - ext_workspace_manager_v1_send_workspace(manager_res->resource, - workspace_res->resource); - workspace_send_details(workspace_res); - - if (!workspace->group) { - continue; - } - struct wlr_ext_workspace_group_v1_resource *group_res; - wl_list_for_each(group_res, &workspace->group->resources, link) { - if (group_res->manager == manager_res) { - ext_workspace_group_handle_v1_send_workspace_enter( - group_res->resource, workspace_res->resource); - } - } - } - - ext_workspace_manager_v1_send_done(manager_res->resource); -} - -static void manager_handle_display_destroy(struct wl_listener *listener, - void *data) { - struct wlr_ext_workspace_manager_v1 *manager = - wl_container_of(listener, manager, display_destroy); - - wl_signal_emit_mutable(&manager->events.destroy, NULL); - assert(wl_list_empty(&manager->events.commit.listener_list)); - assert(wl_list_empty(&manager->events.destroy.listener_list)); - - struct wlr_ext_workspace_group_handle_v1 *group, *tmp; - wl_list_for_each_safe(group, tmp, &manager->groups, link) { - wlr_ext_workspace_group_handle_v1_destroy(group); - } - - struct wlr_ext_workspace_handle_v1 *workspace, *tmp2; - wl_list_for_each_safe(workspace, tmp2, &manager->workspaces, link) { - wlr_ext_workspace_handle_v1_destroy(workspace); - } - - struct wlr_ext_workspace_manager_v1_resource *manager_res, *tmp3; - wl_list_for_each_safe(manager_res, tmp3, &manager->resources, link) { - destroy_manager_resource(manager_res); - } - - if (manager->idle_source) { - wl_event_source_remove(manager->idle_source); - } - - wl_list_remove(&manager->display_destroy.link); - wl_global_destroy(manager->global); - free(manager); -} - -struct wlr_ext_workspace_manager_v1 * -wlr_ext_workspace_manager_v1_create(struct wl_display *display, - uint32_t version) { - assert(version <= EXT_WORKSPACE_V1_VERSION); - - struct wlr_ext_workspace_manager_v1 *manager = calloc(1, sizeof(*manager)); - if (!manager) { - return NULL; - } - - manager->global = - wl_global_create(display, &ext_workspace_manager_v1_interface, version, - manager, manager_bind); - if (!manager->global) { - free(manager); - return NULL; - } - - manager->event_loop = wl_display_get_event_loop(display); - - manager->display_destroy.notify = manager_handle_display_destroy; - wl_display_add_destroy_listener(display, &manager->display_destroy); - - wl_list_init(&manager->groups); - wl_list_init(&manager->workspaces); - wl_list_init(&manager->resources); - wl_signal_init(&manager->events.commit); - wl_signal_init(&manager->events.destroy); - - return manager; -} - -struct wlr_ext_workspace_group_handle_v1 * -wlr_ext_workspace_group_handle_v1_create( - struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps) { - struct wlr_ext_workspace_group_handle_v1 *group = calloc(1, sizeof(*group)); - if (!group) { - return NULL; - } - - group->manager = manager; - group->caps = caps; - - wl_list_init(&group->outputs); - wl_list_init(&group->resources); - wl_signal_init(&group->events.destroy); - - wl_list_insert(manager->groups.prev, &group->link); - - struct wlr_ext_workspace_manager_v1_resource *manager_res; - wl_list_for_each(manager_res, &manager->resources, link) { - struct wlr_ext_workspace_group_v1_resource *group_res = - create_group_resource(group, manager_res); - if (!group_res) { - continue; - } - ext_workspace_manager_v1_send_workspace_group(manager_res->resource, - group_res->resource); - group_send_details(group_res); - } - - manager_schedule_done(manager); - - return group; -} - -static void -workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace, - struct wlr_ext_workspace_group_handle_v1 *group, - bool enter) { - - struct wlr_ext_workspace_v1_resource *workspace_res; - wl_list_for_each(workspace_res, &workspace->resources, link) { - struct wlr_ext_workspace_group_v1_resource *group_res; - wl_list_for_each(group_res, &group->resources, link) { - if (group_res->manager != workspace_res->manager) { - continue; - } - if (enter) { - ext_workspace_group_handle_v1_send_workspace_enter( - group_res->resource, workspace_res->resource); - } else { - ext_workspace_group_handle_v1_send_workspace_leave( - group_res->resource, workspace_res->resource); - } - } - } - - manager_schedule_done(workspace->manager); -} - -static void -destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) { - wl_list_remove(&group_output->output_bind.link); - wl_list_remove(&group_output->output_destroy.link); - wl_list_remove(&group_output->link); - free(group_output); -} - -static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_output *output, bool enter) { - - struct wlr_ext_workspace_group_v1_resource *group_res; - wl_list_for_each(group_res, &group->resources, link) { - struct wl_client *client = wl_resource_get_client(group_res->resource); - - struct wl_resource *output_resource; - wl_resource_for_each(output_resource, &output->resources) { - if (wl_resource_get_client(output_resource) != client) { - continue; - } - if (enter) { - ext_workspace_group_handle_v1_send_output_enter( - group_res->resource, output_resource); - } else { - ext_workspace_group_handle_v1_send_output_leave( - group_res->resource, output_resource); - } - } - } - - manager_schedule_done(group->manager); -} - -void wlr_ext_workspace_group_handle_v1_destroy( - struct wlr_ext_workspace_group_handle_v1 *group) { - if (!group) { - return; - } - - wl_signal_emit_mutable(&group->events.destroy, NULL); - - assert(wl_list_empty(&group->events.destroy.listener_list)); - - struct wlr_ext_workspace_handle_v1 *workspace; - wl_list_for_each(workspace, &group->manager->workspaces, link) { - if (workspace->group == group) { - workspace_send_group(workspace, group, false); - workspace->group = NULL; - } - } - - struct wlr_ext_workspace_group_v1_resource *group_res, *tmp; - wl_list_for_each_safe(group_res, tmp, &group->resources, link) { - ext_workspace_group_handle_v1_send_removed(group_res->resource); - destroy_group_resource(group_res); - } - - struct wlr_ext_workspace_manager_v1_resource *manager_res; - wl_list_for_each(manager_res, &group->manager->resources, link) { - clear_requests_by(manager_res, group, NULL); - } - - struct wlr_ext_workspace_v1_group_output *group_output, *tmp3; - wl_list_for_each_safe(group_output, tmp3, &group->outputs, link) { - group_send_output(group, group_output->output, false); - destroy_group_output(group_output); - } - - manager_schedule_done(group->manager); - - wl_list_remove(&group->link); - free(group); -} - -static void handle_output_bind(struct wl_listener *listener, void *data) { - struct wlr_ext_workspace_v1_group_output *group_output = - wl_container_of(listener, group_output, output_bind); - struct wlr_output_event_bind *event = data; - struct wl_client *client = wl_resource_get_client(event->resource); - - struct wlr_ext_workspace_group_v1_resource *group_res; - wl_list_for_each(group_res, &group_output->group->resources, link) { - if (wl_resource_get_client(group_res->resource) == client) { - ext_workspace_group_handle_v1_send_output_enter(group_res->resource, - event->resource); - } - } - - manager_schedule_done(group_output->group->manager); -} - -static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct wlr_ext_workspace_v1_group_output *group_output = - wl_container_of(listener, group_output, output_destroy); - group_send_output(group_output->group, group_output->output, false); - destroy_group_output(group_output); -} - -static struct wlr_ext_workspace_v1_group_output * -get_group_output(struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_output *output) { - struct wlr_ext_workspace_v1_group_output *group_output; - wl_list_for_each(group_output, &group->outputs, link) { - if (group_output->output == output) { - return group_output; - } - } - return NULL; -} - -void wlr_ext_workspace_group_handle_v1_output_enter( - struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_output *output) { - if (get_group_output(group, output)) { - return; - } - struct wlr_ext_workspace_v1_group_output *group_output = - calloc(1, sizeof(*group_output)); - if (!group_output) { - return; - } - group_output->output = output; - group_output->group = group; - wl_list_insert(&group->outputs, &group_output->link); - - group_output->output_bind.notify = handle_output_bind; - wl_signal_add(&output->events.bind, &group_output->output_bind); - group_output->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->events.destroy, &group_output->output_destroy); - - group_send_output(group, output, true); -} - -void wlr_ext_workspace_group_handle_v1_output_leave( - struct wlr_ext_workspace_group_handle_v1 *group, - struct wlr_output *output) { - struct wlr_ext_workspace_v1_group_output *group_output = - get_group_output(group, output); - if (!group_output) { - return; - } - - group_send_output(group, output, false); - destroy_group_output(group_output); -} - -struct wlr_ext_workspace_handle_v1 * -wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, - const char *id, uint32_t caps) { - struct wlr_ext_workspace_handle_v1 *workspace = - calloc(1, sizeof(*workspace)); - if (!workspace) { - return NULL; - } - - workspace->manager = manager; - workspace->caps = caps; - - if (id) { - workspace->id = strdup(id); - if (!workspace->id) { - free(workspace); - return NULL; - } - } - - wl_list_init(&workspace->resources); - wl_array_init(&workspace->coordinates); - wl_signal_init(&workspace->events.destroy); - - wl_list_insert(manager->workspaces.prev, &workspace->link); - - struct wlr_ext_workspace_manager_v1_resource *manager_res; - wl_list_for_each(manager_res, &manager->resources, link) { - struct wlr_ext_workspace_v1_resource *workspace_res = - create_workspace_resource(workspace, manager_res); - if (!workspace_res) { - continue; - } - ext_workspace_manager_v1_send_workspace(manager_res->resource, - workspace_res->resource); - workspace_send_details(workspace_res); - } - - manager_schedule_done(manager); - - return workspace; -} - -void wlr_ext_workspace_handle_v1_destroy( - struct wlr_ext_workspace_handle_v1 *workspace) { - if (!workspace) { - return; - } - - wl_signal_emit_mutable(&workspace->events.destroy, NULL); - - assert(wl_list_empty(&workspace->events.destroy.listener_list)); - - if (workspace->group) { - workspace_send_group(workspace, workspace->group, false); - } - - struct wlr_ext_workspace_v1_resource *workspace_res, *tmp; - wl_list_for_each_safe(workspace_res, tmp, &workspace->resources, link) { - ext_workspace_handle_v1_send_removed(workspace_res->resource); - destroy_workspace_resource(workspace_res); - } - - struct wlr_ext_workspace_manager_v1_resource *manager_res; - wl_list_for_each(manager_res, &workspace->manager->resources, link) { - clear_requests_by(manager_res, NULL, workspace); - } - - manager_schedule_done(workspace->manager); - - wl_list_remove(&workspace->link); - wl_array_release(&workspace->coordinates); - free(workspace->id); - free(workspace->name); - free(workspace); -} - -void wlr_ext_workspace_handle_v1_set_group( - struct wlr_ext_workspace_handle_v1 *workspace, - struct wlr_ext_workspace_group_handle_v1 *group) { - if (workspace->group == group) { - return; - } - - if (workspace->group) { - workspace_send_group(workspace, workspace->group, false); - } - workspace->group = group; - if (group) { - workspace_send_group(workspace, group, true); - } -} - -void wlr_ext_workspace_handle_v1_set_name( - struct wlr_ext_workspace_handle_v1 *workspace, const char *name) { - assert(name); - - if (workspace->name && strcmp(workspace->name, name) == 0) { - return; - } - - free(workspace->name); - workspace->name = strdup(name); - if (workspace->name == NULL) { - return; - } - - struct wlr_ext_workspace_v1_resource *workspace_res; - wl_list_for_each(workspace_res, &workspace->resources, link) { - ext_workspace_handle_v1_send_name(workspace_res->resource, - workspace->name); - } - - manager_schedule_done(workspace->manager); -} - -void wlr_ext_workspace_handle_v1_set_coordinates( - struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords, - size_t coords_len) { - size_t size = coords_len * sizeof(coords[0]); - if (size == workspace->coordinates.size && - (size == 0 || memcmp(workspace->coordinates.data, coords, size) == 0)) { - return; - } - - wl_array_release(&workspace->coordinates); - wl_array_init(&workspace->coordinates); - struct wl_array arr = { - .data = (void *)coords, - .size = size, - }; - wl_array_copy(&workspace->coordinates, &arr); - - struct wlr_ext_workspace_v1_resource *workspace_res; - wl_list_for_each(workspace_res, &workspace->resources, link) { - ext_workspace_handle_v1_send_coordinates(workspace_res->resource, - &workspace->coordinates); - } - - manager_schedule_done(workspace->manager); -} - -static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace, - enum ext_workspace_handle_v1_state state, - bool enabled) { - uint32_t old_state = workspace->state; - if (enabled) { - workspace->state |= state; - } else { - workspace->state &= ~state; - } - if (old_state == workspace->state) { - return; - } - - struct wlr_ext_workspace_v1_resource *workspace_res; - wl_list_for_each(workspace_res, &workspace->resources, link) { - ext_workspace_handle_v1_send_state(workspace_res->resource, - workspace->state); - } - - manager_schedule_done(workspace->manager); -} - -void wlr_ext_workspace_handle_v1_set_active( - struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) { - workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE, - enabled); -} - -void wlr_ext_workspace_handle_v1_set_urgent( - struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) { - workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT, - enabled); -} - -void wlr_ext_workspace_handle_v1_set_hidden( - struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) { - workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN, - enabled); -} diff --git a/src/ext-protocol/wlr_ext_workspace_v1.h b/src/ext-protocol/wlr_ext_workspace_v1.h deleted file mode 100644 index 183d5801..00000000 --- a/src/ext-protocol/wlr_ext_workspace_v1.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This an unstable interface of wlroots. No guarantees are made regarding the - * future consistency of this API. - */ -#ifndef WLR_USE_UNSTABLE -#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" -#endif - -#ifndef WLR_TYPES_WLR_EXT_WORKSPACE_V1_H -#define WLR_TYPES_WLR_EXT_WORKSPACE_V1_H - -#include -#include - -struct wlr_output; - -enum wlr_ext_workspace_v1_request_type { - WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE, - WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE, - WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE, - WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN, - WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE, -}; - -struct wlr_ext_workspace_v1_request { - enum wlr_ext_workspace_v1_request_type type; - struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests - union { - struct { - char *name; - struct wlr_ext_workspace_group_handle_v1 - *group; // NULL if destroyed - } create_workspace; - struct { - struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed - } activate; - struct { - struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed - } deactivate; - struct { - struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed - struct wlr_ext_workspace_group_handle_v1 - *group; // NULL if destroyed - } assign; - struct { - struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed - } remove; - }; -}; - -struct wlr_ext_workspace_v1_commit_event { - struct wl_list *requests; // wlr_ext_workspace_v1_request.link -}; - -struct wlr_ext_workspace_manager_v1 { - struct wl_global *global; - struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link - struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link - - struct { - struct wl_signal commit; // wlr_ext_workspace_v1_commit_event - struct wl_signal destroy; - } events; - - void *data; - - struct { - struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link - struct wl_event_source *idle_source; - struct wl_event_loop *event_loop; - struct wl_listener display_destroy; - }; -}; - -struct wlr_ext_workspace_group_handle_v1 { - struct wlr_ext_workspace_manager_v1 *manager; - uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities - struct { - struct wl_signal destroy; - } events; - - struct wl_list link; // wlr_ext_workspace_manager_v1.groups - - void *data; - - struct { - struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link - struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link - }; -}; - -struct wlr_ext_workspace_handle_v1 { - struct wlr_ext_workspace_manager_v1 *manager; - struct wlr_ext_workspace_group_handle_v1 *group; // May be NULL - char *id; - char *name; - struct wl_array coordinates; - uint32_t caps; // ext_workspace_handle_v1_workspace_capabilities - uint32_t state; // ext_workspace_handle_v1_state - - struct { - struct wl_signal destroy; - } events; - - struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces - - void *data; - - struct { - struct wl_list resources; // wlr_ext_workspace_v1_resource.link - }; -}; - -struct wlr_ext_workspace_manager_v1 * -wlr_ext_workspace_manager_v1_create(struct wl_display *display, - uint32_t version); - -struct wlr_ext_workspace_group_handle_v1 * -wlr_ext_workspace_group_handle_v1_create( - struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps); -void wlr_ext_workspace_group_handle_v1_destroy( - struct wlr_ext_workspace_group_handle_v1 *group); - -void wlr_ext_workspace_group_handle_v1_output_enter( - struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output); -void wlr_ext_workspace_group_handle_v1_output_leave( - struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output); - -struct wlr_ext_workspace_handle_v1 * -wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, - const char *id, uint32_t caps); -void wlr_ext_workspace_handle_v1_destroy( - struct wlr_ext_workspace_handle_v1 *workspace); - -void wlr_ext_workspace_handle_v1_set_group( - struct wlr_ext_workspace_handle_v1 *workspace, - struct wlr_ext_workspace_group_handle_v1 *group); -void wlr_ext_workspace_handle_v1_set_name( - struct wlr_ext_workspace_handle_v1 *workspace, const char *name); -void wlr_ext_workspace_handle_v1_set_coordinates( - struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords, - size_t coords_len); -void wlr_ext_workspace_handle_v1_set_active( - struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); -void wlr_ext_workspace_handle_v1_set_urgent( - struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); -void wlr_ext_workspace_handle_v1_set_hidden( - struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); - -#endif diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index c5a5869a..0bff62f5 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -181,4 +181,4 @@ bool match_monitor_spec(char *spec, Monitor *m) { free(serial_rule); return match; -} \ No newline at end of file +} diff --git a/src/mango.c b/src/mango.c index 26eb0769..1684f9fc 100644 --- a/src/mango.c +++ b/src/mango.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -41,8 +40,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -319,7 +320,7 @@ typedef struct { float height_scale; int32_t width; int32_t height; - enum corner_location corner_location; + struct fx_corner_radii corner_location; bool should_scale; } BufferData; @@ -335,7 +336,13 @@ struct Client { struct wlr_scene_rect *droparea; struct wlr_scene_rect *splitindicator[4]; struct wlr_scene_shadow *shadow; + struct wlr_scene_rect *shield; + struct wlr_scene_blur *blur; struct wlr_scene_tree *scene_surface; + struct wlr_scene_tree *image_capture_tree; + struct wlr_scene *image_capture_scene; + struct wlr_ext_image_capture_source_v1 *image_capture_source; + struct wlr_scene_surface *image_capture_scene_surface; struct wlr_scene_tree *overview_scene_surface; struct mango_jump_label_node *jump_label_node; struct mango_tab_bar_node *tab_bar_node; @@ -406,6 +413,7 @@ struct Client { int32_t iskilling; int32_t istagswitching; int32_t isnamedscratchpad; + int32_t shield_when_capture; bool is_monocle_hide; bool is_pending_open_animation; bool is_restoring_from_ov; @@ -434,6 +442,7 @@ struct Client { float unfocused_opacity; char oldmonname[128]; int32_t noblur; + struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; double master_mfact_per, master_inner_per, stack_inner_per; double old_master_mfact_per, old_master_inner_per, old_stack_inner_per; double old_scroller_pproportion; @@ -502,6 +511,7 @@ typedef struct { struct wlr_scene_tree *scene; struct wlr_scene_tree *popups; struct wlr_scene_shadow *shadow; + struct wlr_scene_blur *blur; struct wlr_scene_layer_surface_v1 *scene_layer; struct wl_list link; struct wl_list fadeout_link; @@ -596,6 +606,11 @@ typedef struct { struct wl_listener destroy; } SessionLock; +struct capture_session_tracker { + struct wl_listener session_destroy; + struct wlr_ext_image_copy_capture_session_v1 *session; +}; + typedef struct DwindleNode DwindleNode; struct DwindleNode { bool is_split; @@ -780,6 +795,9 @@ static void virtualkeyboard(struct wl_listener *listener, void *data); static void virtualpointer(struct wl_listener *listener, void *data); static void warp_cursor(const Client *c); static Monitor *xytomon(double x, double y); +static Monitor *get_monitor_nearest_to(int32_t x, int32_t y); +static void handle_iamge_copy_capture_new_session(struct wl_listener *listener, + void *data); static void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); static void clear_fullscreen_flag(Client *c); @@ -846,7 +864,7 @@ static double find_animation_curve_at(double t, int32_t type); static void apply_opacity_to_rect_nodes(Client *c, struct wlr_scene_node *node, double animation_passed); -static enum corner_location set_client_corner_location(Client *c); +static struct fx_corner_radii set_client_corner_location(Client *c); static double all_output_frame_duration_ms(); static struct wlr_scene_tree * wlr_scene_tree_snapshot(struct wlr_scene_node *node, @@ -955,6 +973,7 @@ static struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; static struct wlr_output_power_manager_v1 *power_mgr; +static struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_mgr; static struct wlr_pointer_gestures_v1 *pointer_gestures; static struct wlr_drm_lease_v1_manager *drm_lease_manager; struct mango_print_status_manager *print_status_manager; @@ -981,7 +1000,13 @@ static struct wl_list keyboard_shortcut_inhibitors; static uint32_t cursor_mode; static Client *grabc, *dropc; static int32_t rzcorner; -static int32_t grabcx, grabcy; /* client-relative */ +static int32_t grabcx, grabcy; /* client-relative */ + +static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 + *ext_foreign_toplevel_image_capture_source_manager_v1; +static struct wl_listener new_foreign_toplevel_capture_request; +static struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list; + static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */ static bool start_drag_window = false; static int32_t last_apply_drap_time = 0; @@ -1019,6 +1044,7 @@ static struct wl_event_source *keep_idle_inhibit_source; static bool cursor_hidden = false; static bool tag_combo = false; static const char *cli_config_path = NULL; +static int active_capture_count = 0; static bool cli_debug_log = false; static KeyMode keymode = { .mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'}, @@ -1084,6 +1110,8 @@ static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode}; +static struct wl_listener ext_image_copy_capture_mgr_new_session = { + .notify = handle_iamge_copy_capture_new_session}; static struct wl_listener request_activate = {.notify = urgent}; static struct wl_listener request_cursor = {.notify = setcursor}; static struct wl_listener request_set_psel = {.notify = setpsel}; @@ -1548,6 +1576,7 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, isnamedscratchpad); APPLY_INT_PROP(c, r, isglobal); APPLY_INT_PROP(c, r, isoverlay); + APPLY_INT_PROP(c, r, shield_when_capture); APPLY_INT_PROP(c, r, ignore_maximize); APPLY_INT_PROP(c, r, ignore_minimize); APPLY_INT_PROP(c, r, isnosizehint); @@ -2560,6 +2589,7 @@ void cleanuplisteners(void) { wl_list_remove(&request_start_drag.link); wl_list_remove(&start_drag.link); wl_list_remove(&new_session_lock.link); + wl_list_remove(&new_foreign_toplevel_capture_request.link); wl_list_remove(&tearing_new_object.link); wl_list_remove(&keyboard_shortcuts_inhibit_new_inhibitor.link); if (drm_lease_manager) { @@ -2700,12 +2730,16 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, return; } - wlr_scene_buffer_set_backdrop_blur(buffer, true); - wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true); + LayerSurface *l = (LayerSurface *)user_data; + + wlr_scene_node_set_enabled(&l->blur->node, true); + wlr_scene_blur_set_transparency_mask_source(l->blur, buffer); + wlr_scene_blur_set_size(l->blur, l->geom.width, l->geom.height); + if (config.blur_optimized) { - wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true); + wlr_scene_blur_set_should_only_blur_bottom_layer(l->blur, true); } else { - wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false); + wlr_scene_blur_set_should_only_blur_bottom_layer(l->blur, false); } } @@ -2763,14 +2797,18 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { } // 初始化阴影 - if (layer_surface->current.exclusive_zone == 0 && - layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && + if (layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { - l->shadow = - wlr_scene_shadow_create(l->scene, 0, 0, config.border_radius, - config.shadows_blur, config.shadowscolor); - wlr_scene_node_lower_to_bottom(&l->shadow->node); - wlr_scene_node_set_enabled(&l->shadow->node, true); + if (layer_surface->current.exclusive_zone == 0) { + l->shadow = wlr_scene_shadow_create( + l->scene, 0, 0, config.border_radius, config.shadows_blur, + config.shadowscolor); + wlr_scene_node_lower_to_bottom(&l->shadow->node); + wlr_scene_node_set_enabled(&l->shadow->node, true); + } + + l->blur = wlr_scene_blur_create(l->scene, 0, 0); + wlr_scene_node_lower_to_bottom(&l->blur->node); } // 初始化动画 @@ -2842,7 +2880,8 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { if (!l->noblur && layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && layer_surface->current.layer != - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND && + l->blur) { wlr_scene_node_for_each_buffer(&l->scene->node, iter_layer_scene_buffers, l); @@ -4408,15 +4447,11 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, return; if (config.blur && c && !c->noblur) { - wlr_scene_buffer_set_backdrop_blur(buffer, true); - wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false); if (config.blur_optimized) { - wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true); + wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, true); } else { - wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false); + wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, false); } - } else { - wlr_scene_buffer_set_backdrop_blur(buffer, false); } } @@ -4512,6 +4547,11 @@ void init_client_properties(Client *c) { sizeof(c->opacity_animation.current_border_color)); c->opacity_animation.initial_opacity = c->unfocused_opacity; c->opacity_animation.current_opacity = c->unfocused_opacity; + c->animation.tagining = false; + c->animation.running = false; + c->animation.overining = false; + c->animation.tagouting = false; + c->animation.tagouted = false; } void // old fix to 0.5 @@ -4554,6 +4594,18 @@ mapnotify(struct wl_listener *listener, void *data) { c->geom.height += 2 * c->bw; c->overview_backup_geom = c->geom; + struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { + .app_id = client_get_appid(c), + .title = client_get_title(c), + }; + + c->image_capture_scene = wlr_scene_create(); + c->ext_foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create( + foreign_toplevel_list, &foreign_toplevel_state); + c->ext_foreign_toplevel->data = c; + c->image_capture_scene_surface = wlr_scene_surface_create( + &c->image_capture_scene->tree, client_surface(c)); + /* Handle unmanaged clients first so we can return prior create borders */ #ifdef XWAYLAND @@ -4594,17 +4646,26 @@ mapnotify(struct wl_listener *listener, void *data) { c->scene, 0, 0, c->isurgent ? config.urgentcolor : config.bordercolor); wlr_scene_node_lower_to_bottom(&c->border->node); wlr_scene_node_set_position(&c->border->node, 0, 0); - wlr_scene_rect_set_corner_radius(c->border, config.border_radius, - config.border_radius_location_default); + wlr_scene_rect_set_corner_radii(c->border, + corner_radii_all(config.border_radius)); wlr_scene_node_set_enabled(&c->border->node, true); c->shadow = wlr_scene_shadow_create(c->scene, 0, 0, config.border_radius, config.shadows_blur, config.shadowscolor); + c->blur = wlr_scene_blur_create(c->scene_surface, 0, 0); + wlr_scene_node_lower_to_bottom(&c->blur->node); + wlr_scene_node_lower_to_bottom(&c->shadow->node); wlr_scene_node_set_enabled(&c->shadow->node, true); + c->shield = wlr_scene_rect_create(c->scene_surface, 0, 0, + (float[4]){0, 0, 0, 0xff}); + c->shield->node.data = c; + wlr_scene_node_lower_to_bottom(&c->shield->node); + wlr_scene_node_set_enabled(&c->shield->node, false); + if (config.new_is_master && selmon && !is_scroller_layout(selmon)) // tile at the top wl_list_insert(&clients, &c->link); // 新窗口是master,头部入栈 @@ -4950,6 +5011,29 @@ void outputmgrapply(struct wl_listener *listener, void *data) { outputmgrapplyortest(config, 0); } +static void +handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, + void *data) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request + *request = data; + Client *c = request->toplevel_handle->data; + + if (c->shield_when_capture) + return; + + if (c->image_capture_source == NULL) { + c->image_capture_source = + wlr_ext_image_capture_source_v1_create_with_scene_node( + &c->image_capture_scene->tree.node, event_loop, alloc, drw); + if (c->image_capture_source == NULL) { + return; + } + } + + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( + request, c->image_capture_source); +} + void // 0.7 custom outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test) { /* @@ -5058,6 +5142,53 @@ void printstatus(enum ipc_watch_type type) { wl_signal_emit(&mango_print_status, &type); } +// 会话销毁时的回调 +void handle_session_destroy(struct wl_listener *listener, void *data) { + struct capture_session_tracker *tracker = + wl_container_of(listener, tracker, session_destroy); + active_capture_count--; + wl_list_remove(&tracker->session_destroy.link); + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if (c->shield_when_capture && !c->iskilling && VISIBLEON(c, c->mon)) { + arrange(c->mon, false, false); + } + } + + wlr_log(WLR_DEBUG, "Capture session ended, active count: %d", + active_capture_count); + free(tracker); +} + +// 新会话创建时的回调 +void handle_iamge_copy_capture_new_session(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = data; + + struct capture_session_tracker *tracker = calloc(1, sizeof(*tracker)); + if (!tracker) { + wlr_log(WLR_ERROR, "Failed to allocate capture session tracker"); + return; + } + tracker->session = session; + tracker->session_destroy.notify = handle_session_destroy; + // 监听会话的 destroy 信号,以便在会话结束时减少计数 + wl_signal_add(&session->events.destroy, &tracker->session_destroy); + + active_capture_count++; + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if (c->shield_when_capture && !c->iskilling && VISIBLEON(c, c->mon)) { + arrange(c->mon, false, false); + } + } + + wlr_log(WLR_DEBUG, "New capture session started, active count: %d", + active_capture_count); +} + void powermgrsetmode(struct wl_listener *listener, void *data) { struct wlr_output_power_v1_set_mode_event *event = data; struct wlr_output_state state = {0}; @@ -6000,11 +6131,17 @@ void setup(void) { wlr_subcompositor_create(dpy); wlr_alpha_modifier_v1_create(dpy); wlr_ext_data_control_manager_v1_create(dpy, 1); + wlr_fixes_create(dpy, 1); // 在 setup 函数中 wl_signal_init(&mango_print_status); wl_signal_add(&mango_print_status, &print_status_listener); + ext_image_copy_capture_mgr = + wlr_ext_image_copy_capture_manager_v1_create(dpy, 1); + wl_signal_add(&ext_image_copy_capture_mgr->events.new_session, + &ext_image_copy_capture_mgr_new_session); + /* Initializes the interface used to implement urgency hints */ activation = wlr_xdg_activation_v1_create(dpy); wl_signal_add(&activation->events.request_activate, &request_activate); @@ -6015,6 +6152,15 @@ void setup(void) { power_mgr = wlr_output_power_manager_v1_create(dpy); wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode); + foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(dpy, 1); + ext_foreign_toplevel_image_capture_source_manager_v1 = + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1); + new_foreign_toplevel_capture_request.notify = + handle_new_foreign_toplevel_capture_request; + wl_signal_add(&ext_foreign_toplevel_image_capture_source_manager_v1->events + .new_request, + &new_foreign_toplevel_capture_request); + tearing_control = wlr_tearing_control_manager_v1_create(dpy, 1); tearing_new_object.notify = handle_tearing_new_object; wl_signal_add(&tearing_control->events.new_object, &tearing_new_object); @@ -6303,6 +6449,11 @@ void overview_backup_surface(Client *c) { wlr_scene_tree_snapshot(&c->scene_surface->node, c->scene); wlr_scene_node_set_enabled(&c->overview_scene_surface->node, false); wlr_scene_node_set_enabled(&c->scene_surface->node, true); + + wlr_scene_node_reparent(&c->blur->node, c->scene_surface); + wlr_scene_node_lower_to_bottom(&c->blur->node); + wlr_scene_node_reparent(&c->shield->node, c->scene_surface); + wlr_scene_node_raise_to_top(&c->shield->node); } // 普通视图切换到overview时保存窗口的旧状态 @@ -6348,6 +6499,10 @@ void overview_restore(Client *c, const Arg *arg) { c->is_restoring_from_ov = (arg->ui & c->tags & TAGMASK) == 0 ? true : false; if (c->overview_scene_surface) { + wlr_scene_node_reparent(&c->blur->node, c->overview_scene_surface); + wlr_scene_node_lower_to_bottom(&c->blur->node); + wlr_scene_node_reparent(&c->shield->node, c->overview_scene_surface); + wlr_scene_node_raise_to_top(&c->shield->node); wlr_scene_node_destroy(&c->scene_surface->node); c->scene_surface = c->overview_scene_surface; c->overview_scene_surface = NULL; @@ -6486,6 +6641,11 @@ void unmapnotify(struct wl_listener *listener, void *data) { (!c->mon || VISIBLEON(c, c->mon))) init_fadeout_client(c); + if (c->ext_foreign_toplevel) { + wlr_ext_foreign_toplevel_handle_v1_destroy(c->ext_foreign_toplevel); + c->ext_foreign_toplevel = NULL; + } + // If the client is in a stack, remove it from the stack if (c->swallowedby) { @@ -6582,6 +6742,8 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->tab_bar_node = NULL; } + wlr_scene_node_destroy(&c->image_capture_scene_surface->buffer->node); + wlr_scene_node_destroy(&c->image_capture_scene->tree.node); wlr_scene_node_destroy(&c->scene->node); printstatus(IPC_WATCH_ARRANGGE); motionnotify(0, NULL, 0, 0, 0, 0); @@ -6737,6 +6899,14 @@ void updatetitle(struct wl_listener *listener, void *data) { mango_tab_bar_node_update(c->tab_bar_node, title, 1.0); if (title && c->foreign_toplevel) wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); + if (title && c->ext_foreign_toplevel) { + wlr_ext_foreign_toplevel_handle_v1_update_state( + c->ext_foreign_toplevel, + &(struct wlr_ext_foreign_toplevel_handle_v1_state){ + .title = title, + .app_id = c->ext_foreign_toplevel->app_id, + }); + } if (c == focustop(c->mon)) printstatus(IPC_WATCH_ARRANGGE); } @@ -7107,11 +7277,13 @@ void xwaylandready(struct wl_listener *listener, void *data) { wlr_xwayland_set_seat(xwayland, seat); /* Set the default XWayland cursor to match the rest of dwl. */ - if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) - wlr_xwayland_set_cursor( - xwayland, xcursor->images[0]->buffer, xcursor->images[0]->width * 4, - xcursor->images[0]->width, xcursor->images[0]->height, - xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); + if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) { + struct wlr_xcursor_image *image = xcursor->images[0]; + struct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(image); + wlr_xwayland_set_cursor(xwayland, buffer, xcursor->images[0]->hotspot_x, + xcursor->images[0]->hotspot_y); + } + /* xwayland can't auto sync the keymap, so we do it manually and we need to wait the xwayland completely inited */ From d1cab2c4b7bc5636b0a431ca2781602f47af4cac Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 20 Jun 2026 18:44:28 +0800 Subject: [PATCH 02/70] feat: support group --- src/action/client.h | 73 +++++++++++++- src/animation/client.h | 81 ++++++++++++++- src/animation/common.h | 2 +- src/animation/tag.h | 4 +- src/config/parse_config.h | 6 ++ src/dispatch/bind_declare.h | 2 + src/dispatch/bind_define.h | 67 +++++++++++++ src/fetch/client.h | 24 ++--- src/layout/arrange.h | 25 ----- src/layout/dwindle.h | 2 +- src/layout/horizontal.h | 79 +++------------ src/layout/overview.h | 138 ++++++++++++-------------- src/layout/scroll.h | 23 +++-- src/mango.c | 190 ++++++++++++++++++++++++++++-------- 14 files changed, 483 insertions(+), 233 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 876284cd..7a6863d3 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -57,7 +57,18 @@ void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) { if (!ISFAKETILED(c)) return; - if (!c->isfullscreen && !c->ismaximizescreen) { + if (c->isfullscreen && c->tab_bar_node) { + wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + } + + if (!c->mon->isoverview && c->tab_bar_node && !c->isfullscreen && + (c->group_next || c->group_prev)) { + geo.y = geo.y + config.tab_bar_height; + geo.height -= config.tab_bar_height; + } + + if ((!c->isfullscreen && !c->ismaximizescreen) || + is_scroller_layout(c->mon)) { resize(c, geo, interact); } } @@ -120,3 +131,63 @@ void client_add_tab_bar_node(Client *c) { wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); mango_tab_bar_node_update(c->tab_bar_node, client_get_title(c), 1.0); } + +void client_focus_group_member(Client *c) { + if (!c->group_prev && !c->group_next) + return; + + if (c->isgroupfocusing) + return; + + if (c->mon->isoverview) + return; + + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + Client *cur_focusing = NULL; + while (head) { + if (head->isgroupfocusing) { + cur_focusing = head; + break; + } + head = head->group_next; + } + + if (cur_focusing) { + cur_focusing->isgroupfocusing = false; + client_replace(c, cur_focusing, true); + mango_tab_bar_node_set_focus(cur_focusing->tab_bar_node, false); + } + + c->isgroupfocusing = true; + mango_tab_bar_node_set_focus(c->tab_bar_node, true); + focusclient(c, 1); + + arrange(c->mon, false, false); +} + +void client_set_tab_node_visible(Client *c) { + + if (!c || c->iskilling) + return; + + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + Client *cur = head; + while (cur) { + if (!c->mon->isoverview && cur->tab_bar_node && + (cur->group_next || cur->group_prev) && VISIBLEON(c, c->mon) && + ISSCROLLTILED(c) && !c->isfullscreen) { + wlr_scene_node_set_enabled(&cur->tab_bar_node->scene_buffer->node, + true); + } else { + wlr_scene_node_set_enabled(&cur->tab_bar_node->scene_buffer->node, + false); + } + cur = cur->group_next; + } +} \ No newline at end of file diff --git a/src/animation/client.h b/src/animation/client.h index fb771da9..76370e2f 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -403,6 +403,79 @@ void client_draw_shadow(Client *c) { wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } +void client_draw_title(Client *c) { + + if (!c || !c->tab_bar_node) + return; + + if (!c->group_next && !c->group_prev && c->tab_bar_node && + c->tab_bar_node->scene_buffer->node.enabled) { + wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + return; + } + + if (!c->group_next && !c->group_prev) + return; + + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + int count = 0; + Client *cur = head; + while (cur) { + count++; + cur = cur->group_next; + } + + int32_t tab_x = c->animation.current.x; + int32_t tab_y = c->animation.current.y - config.tab_bar_height; + int32_t tw = c->animation.current.width; + int32_t th = config.tab_bar_height; + + int32_t left_over = c->mon->m.x - tab_x; + int32_t right_over = tab_x + tw - c->mon->m.x - c->mon->m.width; + int32_t top_over = c->mon->m.y - tab_y; + int32_t bottom_over = + tab_y + config.tab_bar_height - c->mon->m.y - c->mon->m.height; + + if (top_over > 0) + th = config.tab_bar_height - top_over; + if (bottom_over > 0) + th = th - bottom_over; + if (right_over > 0) + tw = tw - right_over; + if (left_over > 0) { + tab_x = c->mon->m.x; + tw = tw - left_over; + } + + if (tw <= 0 || th <= 0) { + cur = head; + while (cur) { + if (cur->tab_bar_node) + wlr_scene_node_set_enabled( + &cur->tab_bar_node->scene_buffer->node, false); + cur = cur->group_next; + } + return; + } else { + client_set_tab_node_visible(c); + } + + int32_t bar_w = tw / count; + int32_t rem = tw % count; + int32_t x = tab_x; + cur = head; + + for (int i = 0; i < count && cur; i++) { + int32_t w = bar_w + (i < rem ? 1 : 0); + global_draw_tab_bar(cur, x, tab_y, w, th); + x += w; + cur = cur->group_next; + } +} + void apply_shield(Client *c, struct wlr_box clip_box) { if (clip_box.width <= 0 || clip_box.height <= 0) { @@ -453,7 +526,6 @@ void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, } wlr_scene_node_set_position(&c->tab_bar_node->scene_buffer->node, x, y); - wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, true); mango_tab_bar_node_set_size(c->tab_bar_node, width, height); } @@ -711,10 +783,8 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { c->is_clip_to_hide = true; wlr_scene_node_set_enabled(&c->scene->node, false); - } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon) && - (!c->is_monocle_hide || !is_monocle_layout(c->mon))) { + } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { c->is_clip_to_hide = false; - c->is_monocle_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); } @@ -938,6 +1008,7 @@ void client_apply_clip(Client *c, float factor) { apply_border(c); client_draw_shadow(c); + client_draw_title(c); client_draw_blur(c, clip_box, offset); apply_shield(c, clip_box); @@ -981,6 +1052,7 @@ void client_apply_clip(Client *c, float factor) { // 应用窗口装饰 apply_border(c); client_draw_shadow(c); + client_draw_title(c); apply_shield(c, clip_box); client_draw_blur(c, clip_box, offset); @@ -1432,6 +1504,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { client_get_clip(c, &clip); apply_shield(c, clip); client_draw_shadow(c); + client_draw_title(c); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); if (config.blur && !c->noblur) wlr_scene_blur_set_size(c->blur, diff --git a/src/animation/common.h b/src/animation/common.h index cfd50045..f77e73e9 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -30,7 +30,7 @@ struct dvec2 calculate_animation_curve_at(double t, int32_t type) { void handle_snapshot_meta_destroy(struct wl_listener *listener, void *data) { SnapshotMetadata *meta = wl_container_of(listener, meta, destroy); - wl_list_remove(&meta->destroy.link); // 安全移除监听器 + wl_list_remove(&meta->destroy.link); free(meta); } diff --git a/src/animation/tag.h b/src/animation/tag.h index 859ee1b8..9ff5908f 100644 --- a/src/animation/tag.h +++ b/src/animation/tag.h @@ -45,10 +45,8 @@ void set_tagin_animation(Monitor *m, Client *c) { void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { - if (!ISTILED(c) || ((!c->is_clip_to_hide || !is_scroller_layout(c->mon)) && - (!c->is_monocle_hide || !is_monocle_layout(c->mon)))) { + if (!ISTILED(c) || (!c->is_clip_to_hide || !is_scroller_layout(c->mon))) { c->is_clip_to_hide = false; - c->is_monocle_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); wlr_scene_node_set_enabled(&c->scene_surface->node, true); } diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 61a6da69..bdca5389 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1011,9 +1011,15 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, if (strcmp(func_name, "focusstack") == 0) { func = focusstack; (*arg).i = parse_circle_direction(arg_value); + } else if (strcmp(func_name, "groupfocus") == 0) { + func = groupfocus; + (*arg).i = parse_circle_direction(arg_value); } else if (strcmp(func_name, "focusdir") == 0) { func = focusdir; (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "groupjoin") == 0) { + func = groupjoin; + (*arg).i = parse_direction(arg_value); } else if (strcmp(func_name, "focusid") == 0) { func = focusid; } else if (strcmp(func_name, "incnmaster") == 0) { diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 6960b001..ee59d604 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -2,6 +2,7 @@ int32_t minimized(const Arg *arg); int32_t restore_minimized(const Arg *arg); int32_t toggle_scratchpad(const Arg *arg); int32_t focusdir(const Arg *arg); +int32_t groupjoin(const Arg *arg); int32_t toggleoverview(const Arg *arg); int32_t togglejump(const Arg *arg); int32_t set_proportion(const Arg *arg); @@ -38,6 +39,7 @@ int32_t toggleglobal(const Arg *arg); int32_t incnmaster(const Arg *arg); int32_t focusmon(const Arg *arg); int32_t focusstack(const Arg *arg); +int32_t groupfocus(const Arg *arg); int32_t chvt(const Arg *arg); int32_t reload_config(const Arg *arg); int32_t smartmovewin(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index e7cfd020..44282b94 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -156,6 +156,48 @@ int32_t focusdir(const Arg *arg) { return 0; } +int32_t groupjoin(const Arg *arg) { + + if (!selmon) + return 0; + + Client *need_join_client = arg->tc ? arg->tc : selmon->sel; + if (!need_join_client) + return 0; + + if (need_join_client->group_next || need_join_client->group_prev) { + return 0; + } + + Client *need_replace_client = NULL; + need_replace_client = direction_select(arg); + + if (!need_replace_client || + need_replace_client->mon != need_join_client->mon) + return 0; + + if (!need_join_client->group_next && !need_join_client->group_prev) { + + if (!need_replace_client->group_prev && + !need_replace_client->group_next) { + need_replace_client->isgroupfocusing = true; + } + + need_join_client->group_next = need_replace_client; + if (need_replace_client->group_prev) { + need_replace_client->group_prev->group_next = need_join_client; + } + need_join_client->group_prev = need_replace_client->group_prev; + need_replace_client->group_prev = need_join_client; + + client_focus_group_member(need_join_client); + arrange(need_join_client->mon, false, false); + return 0; + } + + return 0; +} + int32_t focuslast(const Arg *arg) { Client *c = NULL; Client *tc = NULL; @@ -261,6 +303,31 @@ int32_t focusstack(const Arg *arg) { return 0; } +int32_t groupfocus(const Arg *arg) { + Client *c = arg->tc ? arg->tc : selmon->sel; + if (!c) + return 0; + + if (!c->group_prev && !c->group_next) { + return 0; + } + + Client *tc = NULL; + + if (arg->i == NEXT) { + tc = c->group_next; + } else { + tc = c->group_prev; + } + + if (!tc) + return 0; + + client_focus_group_member(tc); + arrange(tc->mon, false, false); + return 0; +} + int32_t incnmaster(const Arg *arg) { if (!arg || !selmon) return 0; diff --git a/src/fetch/client.h b/src/fetch/client.h index a44f4ca7..e7d8e673 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -188,8 +188,6 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, continue; if (!findfloating && c->isfloating) continue; - if (c->is_monocle_hide) - continue; if (c->isunglobal) continue; if (!config.focus_cross_monitor && c->mon != tc->mon) @@ -197,11 +195,6 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, if (!(c->tags & c->mon->tagset[c->mon->seltags])) continue; - if (step == 0 && ((!tc->mon->isoverview && - !client_is_in_same_stack(tc, c, NULL)) || - c->mon != tc->mon)) - continue; - int32_t c_l = c->geom.x; int32_t c_r = c->geom.x + c->geom.width; int32_t c_t = c->geom.y; @@ -257,17 +250,24 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, if (!match_dir) continue; + if (step == 0) { + if (c->mon != tc->mon) + continue; + if (!tc->mon->isoverview && + !client_is_in_same_stack(tc, c, NULL)) + continue; + if (orth_dist != 0) + continue; + } + int64_t penalty = 0; if (main_dist < 0) { - penalty = 10000000000LL; // 主方向重叠(反方向)的极大惩罚 + penalty = 10000000000LL; main_dist = -main_dist; } - // 正交方向无重叠惩罚,优先选择在同一行/列的窗口 int64_t no_overlap_penalty = 0; if (orth_dist > 0) { - // LEFT/RIGHT 时 orth_dist 是垂直间距,>0 表示垂直无重叠 - // UP/DOWN 时 orth_dist 是水平间距,>0 表示水平无重叠 no_overlap_penalty = 10000000LL; } @@ -442,7 +442,7 @@ Client *get_focused_stack_client(Client *sc, Client *custom_focus_client) { return sc; wl_list_for_each(tc, &fstack, flink) { - if (tc->iskilling || tc->isunglobal || tc->is_monocle_hide) + if (tc->iskilling || tc->isunglobal) continue; if (!VISIBLEON(tc, sc->mon)) continue; diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 4782b3ea..0f67c77b 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -37,21 +37,6 @@ void set_size_per(Monitor *m, Client *c) { } } -void monocle_set_focus(Client *c, bool focused) { - - if (!c || !c->mon) - return; - - c->is_monocle_hide = !focused; - mango_tab_bar_node_set_focus(c->tab_bar_node, focused); - wlr_scene_node_set_enabled(&c->scene->node, focused); - - if (!focused) { - c->animation.current = c->animainit_geom = c->animation.initial = - c->pending = c->current = c->geom; - } -} - void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time, int32_t type) { @@ -1147,16 +1132,6 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, client_add_jump_label_node(c); } - if (m->pertag->ltidxs[m->pertag->curtag]->id == MONOCLE && - !c->tab_bar_node) { - client_add_tab_bar_node(c); - } - - if (c->tab_bar_node && c->mon == m) { - wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, - false); - } - if (c->mon == m && (c->isglobal || c->isunglobal)) { c->tags = m->tagset[m->seltags]; } diff --git a/src/layout/dwindle.h b/src/layout/dwindle.h index 5e56a6bf..e291c69b 100644 --- a/src/layout/dwindle.h +++ b/src/layout/dwindle.h @@ -261,7 +261,7 @@ static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay, !node->client->ismaximizescreen) { struct wlr_box box = {ax, ay, MANGO_MAX(1, aw), MANGO_MAX(1, ah)}; - resize(node->client, box, 0); + client_tile_resize(node->client, box, 0); } } return; diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 96c0ed54..8788c210 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -545,83 +545,32 @@ void deck(Monitor *m) { } } -void monocle(Monitor *m) { - Client *c = NULL, *fc = NULL; +void // 17 +monocle(Monitor *m) { + Client *c = NULL; struct wlr_box geom; + int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - int32_t cur_gapiv = enablegaps ? m->gappiv : 0; - int32_t cur_gapih = enablegaps ? m->gappih : 0; - if (config.smartgaps && m->visible_fake_tiling_clients == 1) { - cur_gappov = cur_gappoh = cur_gapiv = cur_gapih = 0; - } - - int n = m->visible_fake_tiling_clients; - if (n == 0) - return; - - wl_list_for_each(c, &fstack, flink) { - if (c->iskilling || c->isunglobal || !ISFAKETILED(c)) - continue; - if (VISIBLEON(c, m)) { - fc = c; - break; - } - } - - if (n == 1) { - geom.x = m->w.x + cur_gappoh; - geom.y = m->w.y + cur_gappov; - geom.width = m->w.width - 2 * cur_gappoh; - geom.height = m->w.height - 2 * cur_gappov; - client_tile_resize(fc, geom, 0); - monocle_set_focus(fc, true); - return; - } - - int tab_bar_height = config.tab_bar_height; - - int tab_bar_inner_gap_height = - config.tab_bar_height > 0 ? 2 * cur_gapiv : 0; - int tab_bar_y_offset = config.tab_bar_height > 0 ? cur_gapiv : 0; - - int tab_y = m->w.y + cur_gappov; - int main_y = tab_y + tab_bar_height + tab_bar_y_offset; - int main_height = m->w.height - 2 * cur_gappov - tab_bar_inner_gap_height - - tab_bar_height; - - int tab_area_width = m->w.width - 2 * cur_gappoh; - - int total_gaps = (n - 1) * cur_gapih; - int base_width = (tab_area_width - total_gaps) / n; - int remainder = (tab_area_width - total_gaps) % n; - - int tab_x = m->w.x + cur_gappoh; - int idx = 0; + cur_gappoh = config.smartgaps && m->visible_fake_tiling_clients == 1 + ? 0 + : cur_gappoh; + cur_gappov = config.smartgaps && m->visible_fake_tiling_clients == 1 + ? 0 + : cur_gappov; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || !ISFAKETILED(c)) continue; - - if (c == fc) { - monocle_set_focus(c, true); - } else { - monocle_set_focus(c, false); - } - geom.x = m->w.x + cur_gappoh; - geom.y = main_y; + geom.y = m->w.y + cur_gappov; geom.width = m->w.width - 2 * cur_gappoh; - geom.height = main_height; + geom.height = m->w.height - 2 * cur_gappov; client_tile_resize(c, geom, 0); - - int tw = base_width + (idx < remainder ? 1 : 0); - global_draw_tab_bar(c, tab_x, tab_y, tw, tab_bar_height); - - tab_x += tw + cur_gapih; - idx++; } + if ((c = focustop(m))) + wlr_scene_node_raise_to_top(&c->scene->node); } // 网格布局窗口大小和位置计算 diff --git a/src/layout/overview.h b/src/layout/overview.h index 1ae9e7a0..80b2e31d 100644 --- a/src/layout/overview.h +++ b/src/layout/overview.h @@ -1,4 +1,3 @@ - typedef struct { float x, y, w, h; } OvPlacedRect; @@ -232,16 +231,20 @@ void overview_scale(Monitor *m) { float base_x = m->w.x + target_gappo + dx; float base_y = m->w.y + target_gappo + dy; + // 收集所有客户端的目标几何,最后统一调用 client_tile_resize + struct wlr_box overview_boxes[n]; // C99 VLA,n > 0 时有效 for (int k = 0; k < n; k++) { - Client *cl = items[k].c; - struct wlr_box geom; - geom.x = (int)(base_x + placed[k].x + 0.5f); - geom.y = (int)(base_y + placed[k].y + 0.5f); float w = items[k].orig_w * best_s; float h = items[k].orig_h * best_s; - geom.width = (int)(geom.x + w + 0.5f) - geom.x; - geom.height = (int)(geom.y + h + 0.5f) - geom.y; - resize(cl, geom, 0); + int ix = (int)(base_x + placed[k].x + 0.5f); + int iy = (int)(base_y + placed[k].y + 0.5f); + int iw = (int)(ix + w + 0.5f) - ix; + int ih = (int)(iy + h + 0.5f) - iy; + overview_boxes[k] = (struct wlr_box){ix, iy, iw, ih}; + } + + for (int k = 0; k < n; k++) { + client_tile_resize(items[k].c, overview_boxes[k], 0); } } @@ -280,74 +283,69 @@ void overview_resize(Monitor *m) { return; } + // 临时存储每个客户端的目标几何 + struct wlr_box boxes[n]; // C99 VLA + if (n == 1) { int32_t cw = (m->w.width - 2 * target_gappo) * single_width_ratio; int32_t ch = (m->w.height - 2 * target_gappo) * single_height_ratio; - c_arr[0]->geom.x = m->w.x + (m->w.width - cw) / 2; - c_arr[0]->geom.y = m->w.y + (m->w.height - ch) / 2; - c_arr[0]->geom.width = cw; - c_arr[0]->geom.height = ch; - resize(c_arr[0], c_arr[0]->geom, 0); - free(c_arr); - return; - } - - if (n == 2) { + boxes[0].x = m->w.x + (m->w.width - cw) / 2; + boxes[0].y = m->w.y + (m->w.height - ch) / 2; + boxes[0].width = cw; + boxes[0].height = ch; + } else if (n == 2) { int32_t cw = (m->w.width - 2 * target_gappo - target_gappi) / 2; int32_t ch = (m->w.height - 2 * target_gappo) * 0.65f; - c_arr[0]->geom.x = m->w.x + target_gappo; - c_arr[0]->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo; - c_arr[0]->geom.width = cw; - c_arr[0]->geom.height = ch; - resize(c_arr[0], c_arr[0]->geom, 0); + boxes[0].x = m->w.x + target_gappo; + boxes[0].y = m->w.y + (m->w.height - ch) / 2 + target_gappo; + boxes[0].width = cw; + boxes[0].height = ch; - c_arr[1]->geom.x = m->w.x + cw + target_gappo + target_gappi; - c_arr[1]->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo; - c_arr[1]->geom.width = cw; - c_arr[1]->geom.height = ch; - resize(c_arr[1], c_arr[1]->geom, 0); + boxes[1].x = m->w.x + cw + target_gappo + target_gappi; + boxes[1].y = m->w.y + (m->w.height - ch) / 2 + target_gappo; + boxes[1].width = cw; + boxes[1].height = ch; + } else { + int32_t cols = 1; + while (cols * cols < n) + cols++; + int32_t rows = (n + cols - 1) / cols; - free(c_arr); - return; - } + int32_t ch = + (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows; + int32_t cw = + (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols; - int32_t cols = 1; - while (cols * cols < n) { - cols++; - } - int32_t rows = (n + cols - 1) / cols; + if (ch < 1) + ch = 1; + if (cw < 1) + cw = 1; - int32_t ch = - (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows; - int32_t cw = - (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols; - - if (ch < 1) - ch = 1; - if (cw < 1) - cw = 1; - - int32_t overcols = n % cols; - int32_t dx = 0; - if (overcols) { - dx = (m->w.width - overcols * cw - (overcols - 1) * target_gappi) / 2 - - target_gappo; - } - - for (int i = 0; i < n; i++) { - int32_t cx = m->w.x + (i % cols) * (cw + target_gappi); - int32_t cy = m->w.y + (i / cols) * (ch + target_gappi); - - if (overcols && i >= n - overcols) { - cx += dx; + int32_t overcols = n % cols; + int32_t dx = 0; + if (overcols) { + dx = (m->w.width - overcols * cw - (overcols - 1) * target_gappi) / + 2 - + target_gappo; } - c_arr[i]->geom.x = cx + target_gappo; - c_arr[i]->geom.y = cy + target_gappo; - c_arr[i]->geom.width = cw; - c_arr[i]->geom.height = ch; - resize(c_arr[i], c_arr[i]->geom, 0); + for (int i = 0; i < n; i++) { + int32_t cx = m->w.x + (i % cols) * (cw + target_gappi); + int32_t cy = m->w.y + (i / cols) * (ch + target_gappi); + if (overcols && i >= n - overcols) + cx += dx; + + boxes[i].x = cx + target_gappo; + boxes[i].y = cy + target_gappo; + boxes[i].width = cw; + boxes[i].height = ch; + } + } + + // 统一应用所有几何变更,使用 client_tile_resize + for (int k = 0; k < n; k++) { + client_tile_resize(c_arr[k], boxes[k], 0); } free(c_arr); @@ -365,9 +363,7 @@ void create_jump_hints(Monitor *m) { char c_char = jump_labels[label_idx]; c->jump_char = c_char; - // 把字符变成字符串 char label_text[2] = {c_char, '\0'}; - mango_jump_label_node_update(c->jump_label_node, label_text, 1.0f); wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, true); @@ -385,12 +381,10 @@ void create_jump_hints(Monitor *m) { void begin_jump_mode(Monitor *m) { m->is_jump_mode = 1; } void finish_jump_mode(Monitor *m) { - Client *c = NULL; - - if (!m->is_jump_mode) { + if (!m->is_jump_mode) return; - } + Client *c; wl_list_for_each(c, &clients, link) { if (VISIBLEON(c, m)) { if (c->jump_label_node->scene_buffer->node.enabled) { @@ -400,12 +394,10 @@ void finish_jump_mode(Monitor *m) { } } } - m->is_jump_mode = 0; } void overview(Monitor *m) { - if (config.ov_no_resize) { overview_scale(m); } else { @@ -415,4 +407,4 @@ void overview(Monitor *m) { if (m->is_jump_mode) { create_jump_hints(m); } -} \ No newline at end of file +} diff --git a/src/layout/scroll.h b/src/layout/scroll.h index b800c5cf..63e52c99 100644 --- a/src/layout/scroll.h +++ b/src/layout/scroll.h @@ -36,7 +36,6 @@ scroller_node_create(struct TagScrollerState *st, Client *c) { return n; } -/* 从 tag 状态中移除一个节点并释放 */ static void scroller_node_remove(struct TagScrollerState *st, struct ScrollerStackNode *target) { if (!st || !target) @@ -223,7 +222,7 @@ void arrange_stack_node(struct ScrollerStackNode *head, struct wlr_box geometry, .y = current_y, .width = geometry.width, .height = client_height}; - resize(iter->client, client_geom, 0); + client_tile_resize(iter->client, client_geom, 0); remain_proportion -= iter->stack_proportion; remain_client_height -= client_height; current_y += client_height + gappiv; @@ -272,7 +271,7 @@ void arrange_stack_vertical_node(struct ScrollerStackNode *head, .x = current_x, .height = geometry.height, .width = client_width}; - resize(iter->client, client_geom, 0); + client_tile_resize(iter->client, client_geom, 0); remain_proportion -= iter->stack_proportion; remain_client_width -= client_width; current_x += client_width + gappih; @@ -508,6 +507,7 @@ void scroller(Monitor *m) { void vertical_scroller(Monitor *m) { uint32_t tag = m->pertag->curtag; + int32_t bar_height = 0; struct TagScrollerState *st = ensure_scroller_state(m, tag); Client *c = NULL; float scroller_default_proportion_single = @@ -696,7 +696,12 @@ void vertical_scroller(Monitor *m) { arrange_stack_vertical_node(heads[focus_index], target_geom, cur_gappih); } else { - target_geom.y = root_client->geom.y; + bar_height = !root_client->isfullscreen && (root_client->group_prev || + root_client->group_next) + ? config.tab_bar_height + : 0; + + target_geom.y = root_client->geom.y - bar_height; vertical_check_scroller_root_inside_mon(heads[focus_index]->client, &target_geom); arrange_stack_vertical_node(heads[focus_index], target_geom, @@ -709,8 +714,15 @@ void vertical_scroller(Monitor *m) { up_geom.width = m->w.width - 2 * cur_gappoh; up_geom.height = max_client_height * cur->scroller_proportion; vertical_scroll_adjust_fullandmax(cur->client, &up_geom); + + bar_height = !heads[focus_index - i + 1]->client->isfullscreen && + (heads[focus_index - i + 1]->client->group_prev || + heads[focus_index - i + 1]->client->group_next) + ? config.tab_bar_height + : 0; + up_geom.y = heads[focus_index - i + 1]->client->geom.y - cur_gappiv - - up_geom.height; + up_geom.height - bar_height; arrange_stack_vertical_node(cur, up_geom, cur_gappih); } @@ -902,7 +914,6 @@ static void update_scroller_state(Monitor *m) { break; } - /* 移除不再可见的节点 */ struct ScrollerStackNode *n = st->all_first; while (n) { bool found = false; diff --git a/src/mango.c b/src/mango.c index 1684f9fc..d718a9c7 100644 --- a/src/mango.c +++ b/src/mango.c @@ -414,7 +414,6 @@ struct Client { int32_t istagswitching; int32_t isnamedscratchpad; int32_t shield_when_capture; - bool is_monocle_hide; bool is_pending_open_animation; bool is_restoring_from_ov; float scroller_proportion; @@ -466,6 +465,9 @@ struct Client { int32_t grid_col_idx; int32_t grid_row_idx; uint32_t id; + Client *group_prev; + Client *group_next; + bool isgroupfocusing; }; typedef struct { @@ -804,7 +806,7 @@ static void clear_fullscreen_flag(Client *c); static pid_t getparentprocess(pid_t p); static int32_t isdescprocess(pid_t p, pid_t c); static Client *termforwin(Client *w); -static void swallow(Client *c, Client *w); +static void client_replace(Client *c, Client *w, bool isgroupaction); static void warp_cursor_to_selmon(Monitor *m); uint32_t want_restore_fullscreen(Client *target_client); @@ -936,6 +938,8 @@ static void overview_backup_surface(Client *c); static void create_jump_hints(Monitor *m); static void finish_jump_mode(Monitor *m); static void begin_jump_mode(Monitor *m); +static void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, + int32_t height); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -1273,7 +1277,7 @@ void client_update_oldmonname_record(Client *c, Monitor *m) { c->oldmonname[sizeof(c->oldmonname) - 1] = '\0'; } -void swallow(Client *c, Client *w) { +void client_replace(Client *c, Client *w, bool isgroupaction) { c->bw = w->bw; c->isfloating = w->isfloating; c->isurgent = w->isurgent; @@ -1288,10 +1292,43 @@ void swallow(Client *c, Client *w) { c->scroller_proportion = w->scroller_proportion; c->isglobal = w->isglobal; c->overview_backup_geom = w->overview_backup_geom; - - /* 调整 w 的邻居指针,让它们指向 c */ + c->animation.current = w->animation.current; c->stack_proportion = w->stack_proportion; + if (!isgroupaction) { + + if (w->group_prev == c) { + c->group_next = w->group_next; + if (w->group_next) { + w->group_next->group_prev = c; + } + } else if (w->group_next == c) { + c->group_prev = w->group_prev; + if (w->group_prev) { + w->group_prev->group_next = c; + } + } else { + c->group_prev = w->group_prev; + c->group_next = w->group_next; + if (w->group_prev) { + w->group_prev->group_next = c; + } + + if (w->group_next) { + w->group_next->group_prev = c; + } + } + + if (!c->group_prev && !c->group_next) { + c->isgroupfocusing = false; + } else { + c->isgroupfocusing = w->isgroupfocusing; + } + + if (c->isgroupfocusing) + mango_tab_bar_node_set_focus(c->tab_bar_node, true); + } + if (w->overview_scene_surface) { wlr_scene_node_destroy(&w->scene_surface->node); w->scene_surface = w->overview_scene_surface; @@ -1302,13 +1339,36 @@ void swallow(Client *c, Client *w) { overview_backup_surface(c); } - if (w->tab_bar_node) { + if (w->tab_bar_node && !isgroupaction) { wlr_scene_node_set_enabled(&w->tab_bar_node->scene_buffer->node, false); } - /* 全局链表替换 */ - wl_list_insert(&w->link, &c->link); - wl_list_insert(&w->flink, &c->flink); + if (c->link.prev && c->link.next && c->link.prev != &c->link) { + wl_list_remove(&c->link); + } + wl_list_init(&c->link); + + if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) { + wl_list_remove(&c->flink); + } + wl_list_init(&c->flink); + + if (w->link.prev && w->link.next && w->link.prev != &w->link) { + wl_list_insert(w->link.prev, &c->link); + wl_list_remove(&w->link); + wl_list_init(&w->link); + } + + if (w->flink.prev && w->flink.next && w->flink.prev != &w->flink) { + if (selmon && c == selmon->sel) { + wl_list_insert(&fstack, &c->flink); + } else { + wl_list_insert(w->flink.prev, &c->flink); + } + wl_list_remove(&w->flink); + wl_list_init(&w->flink); + } + /* --------------------------------------------------------------------- */ if (w->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_leave(w->foreign_toplevel, @@ -1357,12 +1417,10 @@ void swallow(Client *c, Client *w) { struct TagScrollerState *st = w->mon->pertag->scroller_state[t]; if (!st) continue; - /* 先移除 c 在任意 tag 中的旧节点 */ struct ScrollerStackNode *cn = find_scroller_node(st, c); if (cn) scroller_node_remove(st, cn); - /* 将 w 的节点(如果存在)转给 c */ struct ScrollerStackNode *wn = find_scroller_node(st, w); if (wn) wn->client = c; @@ -1780,11 +1838,9 @@ void applyrules(Client *c) { if (p && !p->isminimized) { c->swallowedby = p; p->swallowing = c; - wl_list_remove(&c->link); - wl_list_remove(&c->flink); - swallow(c, p); - wl_list_remove(&p->link); - wl_list_remove(&p->flink); + + client_replace(c, p, false); + mon = p->mon; newtags = p->tags; } @@ -1799,7 +1855,10 @@ void applyrules(Client *c) { (!c->istagsilent || !newtags || newtags & mon->tagset[mon->seltags]); if (!should_init_get_focus) { - wl_list_remove(&c->flink); + if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) { + wl_list_remove(&c->flink); + wl_list_init(&c->flink); + } wl_list_insert(fstack.prev, &c->flink); } @@ -1836,7 +1895,10 @@ void applyrules(Client *c) { } if (c->isfloating && !c->iscustompos && !c->isnamedscratchpad) { - wl_list_remove(&c->link); + if (c->link.prev && c->link.next && c->link.prev != &c->link) { + wl_list_remove(&c->link); + wl_list_init(&c->link); + } wl_list_insert(clients.prev, &c->link); set_float_malposition(c); } @@ -2425,7 +2487,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { MangoNodeData *mangonodedata = (MangoNodeData *)node->data; if (mangonodedata->type == MANGO_TITLE_NODE) { Client *c = mangonodedata->node_data; - focusclient(c, 1); + client_focus_group_member(c); } } @@ -2592,6 +2654,7 @@ void cleanuplisteners(void) { wl_list_remove(&new_foreign_toplevel_capture_request.link); wl_list_remove(&tearing_new_object.link); wl_list_remove(&keyboard_shortcuts_inhibit_new_inhibitor.link); + wl_list_remove(&ext_image_copy_capture_mgr_new_session.link); if (drm_lease_manager) { wl_list_remove(&drm_lease_request.link); } @@ -2602,6 +2665,8 @@ void cleanuplisteners(void) { } void cleanup(void) { + allow_frame_scheduling = false; + ipc_cleanup(); cleanuplisteners(); #ifdef XWAYLAND @@ -3519,30 +3584,22 @@ void destroyinputdevice(struct wl_listener *listener, void *data) { InputDevice *input_dev = wl_container_of(listener, input_dev, destroy_listener); - // 清理设备特定数据 if (input_dev->device_data) { - // 根据设备类型进行特定清理 switch (input_dev->wlr_device->type) { case WLR_INPUT_DEVICE_SWITCH: { Switch *sw = (Switch *)input_dev->device_data; - // 移除 toggle 监听器 wl_list_remove(&sw->toggle.link); - // 释放 Switch 内存 free(sw); break; } - // 可以添加其他设备类型的清理代码 default: break; } input_dev->device_data = NULL; } - // 从设备列表中移除 wl_list_remove(&input_dev->link); - // 移除 destroy 监听器 wl_list_remove(&input_dev->destroy_listener.link); - // 释放内存 free(input_dev); } @@ -4456,9 +4513,11 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, } void init_client_properties(Client *c) { + c->isgroupfocusing = false; + c->group_prev = NULL; + c->group_next = NULL; c->grid_col_per = 1.0f; c->grid_row_per = 1.0f; - c->is_monocle_hide = false; c->jump_label_node = NULL; c->tab_bar_node = NULL; c->overview_scene_surface = NULL; @@ -4637,6 +4696,8 @@ mapnotify(struct wl_listener *listener, void *data) { wlr_scene_node_set_enabled(&c->splitindicator[i]->node, false); } + client_add_tab_bar_node(c); + c->droparea = wlr_scene_rect_create(c->scene, 0, 0, config.dropcolor); wlr_scene_node_lower_to_bottom(&c->droparea->node); wlr_scene_node_set_position(&c->droparea->node, 0, 0); @@ -5641,11 +5702,18 @@ setfloating(Client *c, int32_t floating) { } void reset_maximizescreen_size(Client *c) { - c->geom.x = c->mon->w.x + config.gappoh; - c->geom.y = c->mon->w.y + config.gappov; - c->geom.width = c->mon->w.width - 2 * config.gappoh; - c->geom.height = c->mon->w.height - 2 * config.gappov; - resize(c, c->geom, 0); + struct wlr_box geom; + geom.x = c->mon->w.x + config.gappoh; + geom.y = c->mon->w.y + config.gappov; + geom.width = c->mon->w.width - 2 * config.gappoh; + geom.height = c->mon->w.height - 2 * config.gappov; + + if ((c->group_next || c->group_prev) && c->tab_bar_node) { + geom.height -= config.tab_bar_height; + geom.y += config.tab_bar_height; + } + + resize(c, geom, 0); } void exit_scroller_stack(Client *c) { @@ -5658,7 +5726,7 @@ void exit_scroller_stack(Client *c) { struct ScrollerStackNode *n = find_scroller_node(st, c); if (n) { scroller_node_remove(st, n); - return; /* 节点已移除,客户端指针已在函数内清空 */ + return; } } } @@ -5686,6 +5754,12 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { maximizescreen_box.y = c->mon->w.y + config.gappov; maximizescreen_box.width = c->mon->w.width - 2 * config.gappoh; maximizescreen_box.height = c->mon->w.height - 2 * config.gappov; + + if ((c->group_next || c->group_prev) && c->tab_bar_node) { + maximizescreen_box.height -= config.tab_bar_height; + maximizescreen_box.y += config.tab_bar_height; + } + wlr_scene_node_raise_to_top(&c->scene->node); if (!is_scroller_layout(c->mon) || c->isfloating) resize(c, maximizescreen_box, 0); @@ -6650,7 +6724,12 @@ void unmapnotify(struct wl_listener *listener, void *data) { if (c->swallowedby) { c->swallowedby->mon = c->mon; - swallow(c->swallowedby, c); + client_replace(c->swallowedby, c, false); + } else if ((c->group_next || c->group_prev) && c->isgroupfocusing) { + Client *group_replacement = + c->group_next ? c->group_next : c->group_prev; + group_replacement->mon = c->mon; + client_replace(group_replacement, c, false); } else { scroller_remove_client(c); dwindle_remove_client(c); @@ -6699,7 +6778,11 @@ void unmapnotify(struct wl_listener *listener, void *data) { if (client_is_unmanaged(c)) { #ifdef XWAYLAND if (client_is_x11(c)) { - wl_list_remove(&c->set_geometry.link); + if (c->set_geometry.link.prev && c->set_geometry.link.next && + c->set_geometry.link.prev != &c->set_geometry.link) { + wl_list_remove(&c->set_geometry.link); + wl_list_init(&c->set_geometry.link); + } } #endif if (c == exclusive_focus) @@ -6707,11 +6790,33 @@ void unmapnotify(struct wl_listener *listener, void *data) { if (client_surface(c) == seat->keyboard_state.focused_surface) focusclient(focustop(selmon), 1); } else { - if (!c->swallowing) - wl_list_remove(&c->link); + + bool is_in_group = c->group_next || c->group_prev; + + if (c->group_next && !c->isgroupfocusing) { + c->group_next->group_prev = c->group_prev; + } + + if (c->group_prev && !c->isgroupfocusing) { + c->group_prev->group_next = c->group_next; + } + + c->group_next = NULL; + c->group_prev = NULL; + + if (!c->swallowing && (!is_in_group || c->isgroupfocusing)) { + if (c->link.prev && c->link.next && c->link.prev != &c->link) { + wl_list_remove(&c->link); + wl_list_init(&c->link); + } + } setmon(c, NULL, 0, true); - if (!c->swallowing) - wl_list_remove(&c->flink); + if (!c->swallowing && (!is_in_group || c->isgroupfocusing)) { + if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) { + wl_list_remove(&c->flink); + wl_list_init(&c->flink); + } + } } if (c->foreign_toplevel) { @@ -6731,8 +6836,6 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->swallowing = NULL; } - c->stack_proportion = 0.0f; - if (c->jump_label_node) { mango_jump_label_node_destroy(c->jump_label_node); c->jump_label_node = NULL; @@ -6744,6 +6847,9 @@ void unmapnotify(struct wl_listener *listener, void *data) { wlr_scene_node_destroy(&c->image_capture_scene_surface->buffer->node); wlr_scene_node_destroy(&c->image_capture_scene->tree.node); + + init_client_properties(c); + wlr_scene_node_destroy(&c->scene->node); printstatus(IPC_WATCH_ARRANGGE); motionnotify(0, NULL, 0, 0, 0, 0); From 90c50e664d2b4039d938f6323cf13fbca4301e11 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 15:28:04 +0800 Subject: [PATCH 03/70] feat: add dispatch groupleave --- src/config/parse_config.h | 2 ++ src/dispatch/bind_declare.h | 1 + src/dispatch/bind_define.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index bdca5389..0bc26a7f 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1020,6 +1020,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "groupjoin") == 0) { func = groupjoin; (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "groupleave") == 0) { + func = groupleave; } else if (strcmp(func_name, "focusid") == 0) { func = focusid; } else if (strcmp(func_name, "incnmaster") == 0) { diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index ee59d604..056ae82c 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -3,6 +3,7 @@ int32_t restore_minimized(const Arg *arg); int32_t toggle_scratchpad(const Arg *arg); int32_t focusdir(const Arg *arg); int32_t groupjoin(const Arg *arg); +int32_t groupleave(const Arg *arg); int32_t toggleoverview(const Arg *arg); int32_t togglejump(const Arg *arg); int32_t set_proportion(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 44282b94..34195530 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -198,6 +198,40 @@ int32_t groupjoin(const Arg *arg) { return 0; } +int32_t groupleave(const Arg *arg) { + + if (!selmon) + return 0; + Client *tc = arg->tc ? arg->tc : selmon->sel; + if (!tc || !tc->isgroupfocusing) + return 0; + if (!tc->group_next && !tc->group_prev) { + return 0; + } + Client *rc = tc->group_next ? tc->group_next : tc->group_prev; + + client_focus_group_member(rc); + + if (tc->group_prev) { + tc->group_prev->group_next = tc->group_next; + } + + if (tc->group_next) { + tc->group_next->group_prev = tc->group_prev; + } + + tc->group_prev = NULL; + tc->group_next = NULL; + tc->isgroupfocusing = false; + + wl_list_insert(&rc->link, &tc->link); + wl_list_insert(&rc->flink, &tc->flink); + + arrange(tc->mon, false, false); + + return 0; +} + int32_t focuslast(const Arg *arg) { Client *c = NULL; Client *tc = NULL; From e05bea855e6dff4eae87e01b6dc35bf7330e08d5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 19:36:38 +0800 Subject: [PATCH 04/70] opt: optimize layer cover --- src/action/client.h | 23 ++++++++++++++++++++++- src/mango.c | 8 ++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 7a6863d3..23fd70a4 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -181,7 +181,8 @@ void client_set_tab_node_visible(Client *c) { while (cur) { if (!c->mon->isoverview && cur->tab_bar_node && (cur->group_next || cur->group_prev) && VISIBLEON(c, c->mon) && - ISSCROLLTILED(c) && !c->isfullscreen) { + ISSCROLLTILED(c) && !c->isfullscreen && + (!is_monocle_layout(c->mon) || c == c->mon->sel)) { wlr_scene_node_set_enabled(&cur->tab_bar_node->scene_buffer->node, true); } else { @@ -190,4 +191,24 @@ void client_set_tab_node_visible(Client *c) { } cur = cur->group_next; } +} + +void client_raise_group_tab_bar(Client *c) { + if (!c || !c->mon) + return; + + if (!c->group_prev && !c->group_next) + return; + + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + Client *cur = head; + while (cur) { + if (cur->tab_bar_node) { + wlr_scene_node_raise_to_top(&cur->tab_bar_node->scene_buffer->node); + } + cur = cur->group_next; + } } \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index d718a9c7..381065d6 100644 --- a/src/mango.c +++ b/src/mango.c @@ -170,8 +170,9 @@ enum { LyrBg, LyrBlur, LyrBottom, - LyrDecorate, LyrTile, + LyrDecorate, + LyrMaximize, LyrTop, LyrFadeOut, LyrOverlay, @@ -5761,6 +5762,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { } wlr_scene_node_raise_to_top(&c->scene->node); + client_raise_group_tab_bar(c); if (!is_scroller_layout(c->mon) || c->isfloating) resize(c, maximizescreen_box, 0); } else { @@ -5770,7 +5772,9 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { } wlr_scene_node_reparent(&c->scene->node, - layers[c->isfloating ? LyrTop : LyrTile]); + layers[c->ismaximizescreen ? LyrMaximize + : c->isfloating ? LyrTop + : LyrTile]); if (!c->force_fakemaximize && !c->ismaximizescreen) { client_set_maximized(c, false); From a0feccc6ae3c9918fcdfc58844b73f523dc0cb8c Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 19:59:24 +0800 Subject: [PATCH 05/70] fix: miss set client isgroupfocusing to false when it no group member --- src/dispatch/bind_define.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 34195530..ddbd9484 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -227,6 +227,10 @@ int32_t groupleave(const Arg *arg) { wl_list_insert(&rc->link, &tc->link); wl_list_insert(&rc->flink, &tc->flink); + if (!rc->group_prev && !rc->group_next) { + rc->isgroupfocusing = false; + } + arrange(tc->mon, false, false); return 0; From 0e3c64d22157fbe344951ba26b5b4369f1b400a2 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 22:17:34 +0800 Subject: [PATCH 06/70] fix: fix miss hide bar node when disable animaitons --- src/action/client.h | 2 +- src/animation/client.h | 2 +- src/layout/arrange.h | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 23fd70a4..1f5b3b6c 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -168,7 +168,7 @@ void client_focus_group_member(Client *c) { arrange(c->mon, false, false); } -void client_set_tab_node_visible(Client *c) { +void client_check_tab_node_visible(Client *c) { if (!c || c->iskilling) return; diff --git a/src/animation/client.h b/src/animation/client.h index 76370e2f..a1457cc5 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -460,7 +460,7 @@ void client_draw_title(Client *c) { } return; } else { - client_set_tab_node_visible(c); + client_check_tab_node_visible(c); } int32_t bar_w = tw / count; diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 0f67c77b..df703b85 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -1132,6 +1132,10 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, client_add_jump_label_node(c); } + if (c->tab_bar_node && c->tab_bar_node->scene_buffer->node.enabled) { + client_check_tab_node_visible(c); + } + if (c->mon == m && (c->isglobal || c->isunglobal)) { c->tags = m->tagset[m->seltags]; } From 5e9a28ec796e988cc40dc5b3d630e015cd37cfb7 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 22:26:58 +0800 Subject: [PATCH 07/70] opt: allow floating window show group bar --- src/action/client.h | 2 +- src/mango.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/action/client.h b/src/action/client.h index 1f5b3b6c..4309321e 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -181,7 +181,7 @@ void client_check_tab_node_visible(Client *c) { while (cur) { if (!c->mon->isoverview && cur->tab_bar_node && (cur->group_next || cur->group_prev) && VISIBLEON(c, c->mon) && - ISSCROLLTILED(c) && !c->isfullscreen && + ISNORMAL(c) && !c->isfullscreen && (!is_monocle_layout(c->mon) || c == c->mon->sel)) { wlr_scene_node_set_enabled(&cur->tab_bar_node->scene_buffer->node, true); diff --git a/src/mango.c b/src/mango.c index 381065d6..bc182cb1 100644 --- a/src/mango.c +++ b/src/mango.c @@ -115,6 +115,8 @@ #define ISTILED(A) \ (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ !(A)->ismaximizescreen && !(A)->isfullscreen && !(A)->isunglobal) +#define ISNORMAL(A) \ + (A && !(A)->isminimized && !(A)->iskilling && !(A)->isunglobal) #define ISSCROLLTILED(A) \ (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ !(A)->isunglobal) From 38f2ecf30b499752482c4d0c07ac6379fda2c8cc Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 22:32:53 +0800 Subject: [PATCH 08/70] opt: optimize layer cover when setfloating --- src/mango.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mango.c b/src/mango.c index bc182cb1..b7b131b9 100644 --- a/src/mango.c +++ b/src/mango.c @@ -5683,6 +5683,10 @@ setfloating(Client *c, int32_t floating) { layers[c->isfloating ? LyrTop : LyrTile]); } + if (c->isfloating) { + client_raise_group_tab_bar(c); + } + if (!c->force_fakemaximize) client_set_maximized(c, false); From 5a57198c1d6dc1e968f0329e75e40ea034597922 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 22:37:09 +0800 Subject: [PATCH 09/70] opt: optimize size per set when setfloating --- src/layout/arrange.h | 2 +- src/mango.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/layout/arrange.h b/src/layout/arrange.h index df703b85..4a441f14 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -20,7 +20,7 @@ void set_size_per(Monitor *m, Client *c) { } } - if (!found) { + if (!found || c->isfloating) { c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag]; c->master_inner_per = 1.0f; c->stack_inner_per = 1.0f; diff --git a/src/mango.c b/src/mango.c index b7b131b9..ef680077 100644 --- a/src/mango.c +++ b/src/mango.c @@ -5684,6 +5684,7 @@ setfloating(Client *c, int32_t floating) { } if (c->isfloating) { + set_size_per(c->mon, c); client_raise_group_tab_bar(c); } From 060b0a0c7d99ce1cc263cb6c57711f2457157508 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 21 Jun 2026 23:24:35 +0800 Subject: [PATCH 10/70] opt: optimize layer cover of floating group bar --- src/action/client.h | 52 ++++++++++++++++++++++++++++++++++++++++++++- src/mango.c | 24 ++++++++++----------- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 4309321e..c609e35e 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -163,6 +163,9 @@ void client_focus_group_member(Client *c) { c->isgroupfocusing = true; mango_tab_bar_node_set_focus(c->tab_bar_node, true); + + client_reparent_group(c); + focusclient(c, 1); arrange(c->mon, false, false); @@ -193,7 +196,7 @@ void client_check_tab_node_visible(Client *c) { } } -void client_raise_group_tab_bar(Client *c) { +void client_raise_group(Client *c) { if (!c || !c->mon) return; @@ -208,7 +211,54 @@ void client_raise_group_tab_bar(Client *c) { while (cur) { if (cur->tab_bar_node) { wlr_scene_node_raise_to_top(&cur->tab_bar_node->scene_buffer->node); + wlr_scene_node_raise_to_top(&cur->scene->node); } cur = cur->group_next; } +} + +void client_reparent_group(Client *c) { + if (!c || !c->tab_bar_node) + return; + + if (!c->group_prev && !c->group_next) + return; + + int32_t bar_layer = c->isfloating ? LyrDecorateTop : LyrDecorate; + int32_t client_layer = c->isfloating || c->isfullscreen ? LyrTop + : c->ismaximizescreen ? LyrMaximize + : LyrTile; + + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + Client *cur = head; + while (cur) { + if (cur->tab_bar_node) { + wlr_scene_node_reparent(&cur->tab_bar_node->scene_buffer->node, + layers[bar_layer]); + wlr_scene_node_reparent(&cur->scene->node, layers[client_layer]); + } + cur = cur->group_next; + } +} + +void client_handle_decorate_click(int32_t x, int32_t y) { + struct wlr_scene_node *node = + wlr_scene_node_at(&layers[LyrDecorateTop]->node, x, y, NULL, NULL); + + if (!node || !node->data) { + node = wlr_scene_node_at(&layers[LyrDecorate]->node, x, y, NULL, NULL); + } + + if (!node || !node->data) { + return; + } + + MangoNodeData *mangonodedata = (MangoNodeData *)node->data; + if (mangonodedata->type == MANGO_TITLE_NODE) { + Client *c = mangonodedata->node_data; + client_focus_group_member(c); + } } \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index ef680077..00513e13 100644 --- a/src/mango.c +++ b/src/mango.c @@ -175,6 +175,7 @@ enum { LyrTile, LyrDecorate, LyrMaximize, + LyrDecorateTop, LyrTop, LyrFadeOut, LyrOverlay, @@ -944,6 +945,8 @@ static void begin_jump_mode(Monitor *m); static void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, int32_t height); +static void client_reparent_group(Client *c); + #include "data/static_keymap.h" #include "dispatch/bind_declare.h" #include "layout/layout.h" @@ -2484,15 +2487,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { } // handle click on tile node - struct wlr_scene_node *node = wlr_scene_node_at( - &layers[LyrDecorate]->node, cursor->x, cursor->y, NULL, NULL); - if (node && node->data) { - MangoNodeData *mangonodedata = (MangoNodeData *)node->data; - if (mangonodedata->type == MANGO_TITLE_NODE) { - Client *c = mangonodedata->node_data; - client_focus_group_member(c); - } - } + client_handle_decorate_click(cursor->x, cursor->y); // 当鼠标焦点在layer上的时候,不检测虚拟键盘的mod状态, // 避免layer虚拟键盘锁死mod按键状态 @@ -3952,8 +3947,10 @@ void focusclient(Client *c, int32_t lift) { return; /* Raise client in stacking order if requested */ - if (c && lift) + if (c && lift) { + client_raise_group(c); wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 + } if (c && client_surface(c) == old_keyboard_focus_surface && selmon && selmon->sel) @@ -5683,9 +5680,10 @@ setfloating(Client *c, int32_t floating) { layers[c->isfloating ? LyrTop : LyrTile]); } + client_reparent_group(c); + if (c->isfloating) { set_size_per(c->mon, c); - client_raise_group_tab_bar(c); } if (!c->force_fakemaximize) @@ -5769,7 +5767,6 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { } wlr_scene_node_raise_to_top(&c->scene->node); - client_raise_group_tab_bar(c); if (!is_scroller_layout(c->mon) || c->isfloating) resize(c, maximizescreen_box, 0); } else { @@ -5782,6 +5779,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { layers[c->ismaximizescreen ? LyrMaximize : c->isfloating ? LyrTop : LyrTile]); + client_reparent_group(c); if (!c->force_fakemaximize && !c->ismaximizescreen) { client_set_maximized(c, false); @@ -5851,6 +5849,8 @@ void setfullscreen(Client *c, int32_t fullscreen, layers[fullscreen || c->isfloating ? LyrTop : LyrTile]); } + client_reparent_group(c); + if (rearrange) arrange(c->mon, false, false); } From 06eed8cd2ec4feb3ff53c52c78253ecae7a17b11 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 09:31:23 +0800 Subject: [PATCH 11/70] opt: make groupbar same layer with its client --- src/action/client.h | 40 +++++++++++++++++--------------------- src/animation/client.h | 8 +++++--- src/animation/common.h | 1 + src/dispatch/bind_define.h | 2 +- src/ext-protocol/tablet.h | 2 +- src/fetch/common.h | 11 ++++++++--- src/mango.c | 26 +++++++++++++------------ 7 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index c609e35e..61dc8c9e 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -121,12 +121,17 @@ void client_add_tab_bar_node(Client *c) { return; } - MangoNodeData *mangonodedata = ecalloc(1, sizeof(MangoNodeData)); - mangonodedata->node_data = c; - mangonodedata->type = MANGO_TITLE_NODE; + MangoCustomDecorate *MangoCustomDecorate = + ecalloc(1, sizeof(MangoCustomDecorate)); + MangoCustomDecorate->node_data = c; + MangoCustomDecorate->node_type = MANGO_TITLE_NODE; + MangoCustomDecorate->type = CustomDecorate; + uint32_t layer = c->isfloating || c->isfullscreen ? LyrTop + : c->ismaximizescreen ? LyrMaximize + : LyrTile; c->tab_bar_node = mango_tab_bar_node_create( - mangonodedata, layers[LyrDecorate], config.tabdata, 0, 0); + MangoCustomDecorate, layers[layer], config.tabdata, 0, 0); wlr_scene_node_lower_to_bottom(&c->tab_bar_node->scene_buffer->node); wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); mango_tab_bar_node_update(c->tab_bar_node, client_get_title(c), 1.0); @@ -224,10 +229,9 @@ void client_reparent_group(Client *c) { if (!c->group_prev && !c->group_next) return; - int32_t bar_layer = c->isfloating ? LyrDecorateTop : LyrDecorate; - int32_t client_layer = c->isfloating || c->isfullscreen ? LyrTop - : c->ismaximizescreen ? LyrMaximize - : LyrTile; + int32_t layer = c->isfloating || c->isfullscreen ? LyrTop + : c->ismaximizescreen ? LyrMaximize + : LyrTile; Client *head = c; while (head->group_prev) @@ -237,28 +241,20 @@ void client_reparent_group(Client *c) { while (cur) { if (cur->tab_bar_node) { wlr_scene_node_reparent(&cur->tab_bar_node->scene_buffer->node, - layers[bar_layer]); - wlr_scene_node_reparent(&cur->scene->node, layers[client_layer]); + layers[layer]); + wlr_scene_node_reparent(&cur->scene->node, layers[layer]); } cur = cur->group_next; } } -void client_handle_decorate_click(int32_t x, int32_t y) { - struct wlr_scene_node *node = - wlr_scene_node_at(&layers[LyrDecorateTop]->node, x, y, NULL, NULL); +void client_handle_decorate_click(MangoCustomDecorate *md) { - if (!node || !node->data) { - node = wlr_scene_node_at(&layers[LyrDecorate]->node, x, y, NULL, NULL); - } - - if (!node || !node->data) { + if (!md) return; - } - MangoNodeData *mangonodedata = (MangoNodeData *)node->data; - if (mangonodedata->type == MANGO_TITLE_NODE) { - Client *c = mangonodedata->node_data; + if (md->node_type == MANGO_TITLE_NODE) { + Client *c = md->node_data; client_focus_group_member(c); } } \ No newline at end of file diff --git a/src/animation/client.h b/src/animation/client.h index a1457cc5..1ea4bab2 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -252,11 +252,13 @@ void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer, bool is_subsurface = false; struct wlr_scene_tree *parent_tree = buffer->node.parent; - if (parent_tree->node.data != NULL) { - SnapshotMetadata *meta = (SnapshotMetadata *)parent_tree->node.data; + SnapshotMetadata *meta = (SnapshotMetadata *)parent_tree->node.data; + if (parent_tree->node.data != NULL && meta->type == Snapshot) { surface_width = meta->orig_width; surface_height = meta->orig_height; is_subsurface = meta->is_subsurface; + } else { + return; } surface_height = surface_height * buffer_data->height_scale; @@ -1228,7 +1230,7 @@ void client_animation_next_tick(Client *c) { c->animation.current = c->geom; } - xytonode(cursor->x, cursor->y, NULL, &pointer_c, NULL, &sx, &sy); + xytonode(cursor->x, cursor->y, NULL, &pointer_c, NULL, NULL, &sx, &sy); surface = pointer_c && pointer_c == c ? client_surface(pointer_c) : NULL; diff --git a/src/animation/common.h b/src/animation/common.h index f77e73e9..00a23b9e 100644 --- a/src/animation/common.h +++ b/src/animation/common.h @@ -175,6 +175,7 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx, } meta->orig_width = scene_buffer->dst_width; meta->orig_height = scene_buffer->dst_height; + meta->type = Snapshot; struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index ddbd9484..424cf7f6 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -471,7 +471,7 @@ int32_t moveresize(const Arg *arg) { if (cursor_mode != CurNormal && cursor_mode != CurPressed) return 0; - xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); + xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL, NULL); if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen || grabc->ismaximizescreen) { grabc = NULL; diff --git a/src/ext-protocol/tablet.h b/src/ext-protocol/tablet.h index 34c82dbf..afa0ebc1 100644 --- a/src/ext-protocol/tablet.h +++ b/src/ext-protocol/tablet.h @@ -281,7 +281,7 @@ void tablettoolmotion(struct TabletTool *tool, bool change_x, bool change_y, if (config.sloppyfocus) selmon = xytomon(cursor->x, cursor->y); - xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); + xytonode(cursor->x, cursor->y, &surface, &c, NULL, NULL, &sx, &sy); if (cursor_mode == CurPressed && !seat->drag && surface != seat->pointer_state.focused_surface && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, diff --git a/src/fetch/common.h b/src/fetch/common.h index acb5d0e8..1627ea6a 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -100,11 +100,13 @@ static bool layer_ignores_focus(LayerSurface *l) { } void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, - LayerSurface **pl, double *nx, double *ny) { + LayerSurface **pl, MangoCustomDecorate **pd, double *nx, + double *ny) { struct wlr_scene_node *node, *pnode; struct wlr_surface *surface = NULL; Client *c = NULL; LayerSurface *l = NULL; + MangoCustomDecorate *mangocustomdecorate = NULL; int32_t layer; Client *ovc = NULL; @@ -125,8 +127,6 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, wlr_scene_buffer_from_node(node)); if (scene_surface) { surface = scene_surface->surface; - } else { - continue; } } @@ -142,6 +142,9 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, if (c && c->type == LayerShell) { l = (LayerSurface *)c; c = NULL; + } else if (c && c->type == CustomDecorate) { + mangocustomdecorate = (MangoCustomDecorate *)c; + c = NULL; } } @@ -159,6 +162,8 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, *pc = c; if (pl) *pl = l; + if (pd) + *pd = mangocustomdecorate; if (selmon && selmon->isoverview && config.ov_no_resize) { ovc = xytoclient(x, y); diff --git a/src/mango.c b/src/mango.c index 00513e13..d69e74d7 100644 --- a/src/mango.c +++ b/src/mango.c @@ -165,17 +165,15 @@ enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; enum { VERTICAL, HORIZONTAL }; enum { SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT }; -enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -enum { XDGShell, LayerShell, X11 }; /* client types */ -enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 +enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ +enum { XDGShell, LayerShell, X11, CustomDecorate, Snapshot }; /* client types */ +enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 enum { LyrBg, LyrBlur, LyrBottom, LyrTile, - LyrDecorate, LyrMaximize, - LyrDecorateTop, LyrTop, LyrFadeOut, LyrOverlay, @@ -184,7 +182,7 @@ enum { NUM_LAYERS }; /* scene layers */ -enum mango_node_type { MANGO_TITLE_NODE, MANGO_jump_label_node }; +enum mango_node_type { MANGO_TITLE_NODE, MANGO_JUMP_NODE }; #ifdef XWAYLAND enum { @@ -253,9 +251,10 @@ typedef struct { } Arg; typedef struct { - enum mango_node_type type; + uint32_t type; + enum mango_node_type node_type; void *node_data; -} MangoNodeData; +} MangoCustomDecorate; typedef struct { uint32_t mod; @@ -652,6 +651,7 @@ struct TagScrollerState { }; typedef struct { + uint32_t type; int32_t orig_width; int32_t orig_height; bool is_subsurface; @@ -805,7 +805,8 @@ static Monitor *get_monitor_nearest_to(int32_t x, int32_t y); static void handle_iamge_copy_capture_new_session(struct wl_listener *listener, void *data); static void xytonode(double x, double y, struct wlr_surface **psurface, - Client **pc, LayerSurface **pl, double *nx, double *ny); + Client **pc, LayerSurface **pl, MangoCustomDecorate **pd, + double *nx, double *ny); static void clear_fullscreen_flag(Client *c); static pid_t getparentprocess(pid_t p); static int32_t isdescprocess(pid_t p, pid_t c); @@ -2435,6 +2436,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { uint32_t hard_mods, mods; Client *c = NULL; LayerSurface *l = NULL; + MangoCustomDecorate *md = NULL; struct wlr_surface *surface; Client *tmpc = NULL; int32_t ji; @@ -2456,7 +2458,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { if (locked) break; - xytonode(cursor->x, cursor->y, &surface, NULL, NULL, NULL, NULL); + xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &md, NULL, NULL); if (toplevel_from_wlr_surface(surface, &c, &l) >= 0) { if (c && c->scene->node.enabled && (!client_is_unmanaged(c) || client_wants_focus(c))) @@ -2487,7 +2489,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { } // handle click on tile node - client_handle_decorate_click(cursor->x, cursor->y); + client_handle_decorate_click(md); // 当鼠标焦点在layer上的时候,不检测虚拟键盘的mod状态, // 避免layer虚拟键盘锁死mod按键状态 @@ -4951,7 +4953,7 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, } /* Find the client under the pointer and send the event along. */ - xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); + xytonode(cursor->x, cursor->y, &surface, &c, NULL, NULL, &sx, &sy); if (cursor_mode == CurPressed && !seat->drag && surface != seat->pointer_state.focused_surface && From 230993ba00fee1ff1585f1b94690529bc6d14639 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 09:37:03 +0800 Subject: [PATCH 12/70] opt: optimize groupbar animation clip --- src/animation/client.h | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 1ea4bab2..5e300547 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -441,15 +441,22 @@ void client_draw_title(Client *c) { int32_t bottom_over = tab_y + config.tab_bar_height - c->mon->m.y - c->mon->m.height; - if (top_over > 0) - th = config.tab_bar_height - top_over; - if (bottom_over > 0) - th = th - bottom_over; - if (right_over > 0) - tw = tw - right_over; - if (left_over > 0) { - tab_x = c->mon->m.x; - tw = tw - left_over; + if (c != grabc && + (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouting)) { + if (top_over > 0) { + tab_y = c->mon->m.y; + th = config.tab_bar_height - top_over; + } + if (bottom_over > 0) { + th = th - bottom_over; + } + if (right_over > 0) { + tw = tw - right_over; + } + if (left_over > 0) { + tab_x = c->mon->m.x; + tw = tw - left_over; + } } if (tw <= 0 || th <= 0) { From ee5ff22e58058c03b9134f64b408972dd6f75cde Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 09:41:59 +0800 Subject: [PATCH 13/70] fix: fix cant focus group membar when change mon --- src/action/client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/action/client.h b/src/action/client.h index 61dc8c9e..5b0be9d8 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -162,6 +162,7 @@ void client_focus_group_member(Client *c) { if (cur_focusing) { cur_focusing->isgroupfocusing = false; + c->mon = cur_focusing->mon; client_replace(c, cur_focusing, true); mango_tab_bar_node_set_focus(cur_focusing->tab_bar_node, false); } From a2f7c4b23574dedf436c4eb6c766973a28b749eb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 09:54:08 +0800 Subject: [PATCH 14/70] opt: optimize shadow and border drap when floating cross monitor --- src/animation/client.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 5e300547..10c3d836 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -12,6 +12,12 @@ void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) { struct fx_corner_radii set_client_corner_location(Client *c) { struct fx_corner_radii current_corner_location = corner_radii_all(config.border_radius); + + if (c == grabc || + (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { + return current_corner_location; + } + struct wlr_box target_geom = config.animations ? c->animation.current : c->geom; if (target_geom.x + config.border_radius <= c->mon->m.x) { @@ -370,7 +376,8 @@ void client_draw_shadow(Client *c) { int32_t right_offset, bottom_offset, left_offset, top_offset; - if (c == grabc) { + if (c == grabc || + (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { right_offset = 0; bottom_offset = 0; left_offset = 0; @@ -674,7 +681,8 @@ void apply_border(Client *c) { int32_t right_offset, bottom_offset, left_offset, top_offset; - if (c == grabc) { + if (c == grabc || + (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { right_offset = 0; bottom_offset = 0; left_offset = 0; From 69e994c37a8a691385ced80d6afebd0230ce9ff3 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 09:59:21 +0800 Subject: [PATCH 15/70] opt: optimize overlay layer set for group --- src/action/client.h | 14 ++++++++------ src/dispatch/bind_define.h | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 5b0be9d8..41ca0663 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -126,9 +126,10 @@ void client_add_tab_bar_node(Client *c) { MangoCustomDecorate->node_data = c; MangoCustomDecorate->node_type = MANGO_TITLE_NODE; MangoCustomDecorate->type = CustomDecorate; - uint32_t layer = c->isfloating || c->isfullscreen ? LyrTop - : c->ismaximizescreen ? LyrMaximize - : LyrTile; + uint32_t layer = c->isoverlay ? LyrOverlay + : c->isfloating || c->isfullscreen ? LyrTop + : c->ismaximizescreen ? LyrMaximize + : LyrTile; c->tab_bar_node = mango_tab_bar_node_create( MangoCustomDecorate, layers[layer], config.tabdata, 0, 0); @@ -230,9 +231,10 @@ void client_reparent_group(Client *c) { if (!c->group_prev && !c->group_next) return; - int32_t layer = c->isfloating || c->isfullscreen ? LyrTop - : c->ismaximizescreen ? LyrMaximize - : LyrTile; + int32_t layer = c->isoverlay ? LyrOverlay + : c->isfloating || c->isfullscreen ? LyrTop + : c->ismaximizescreen ? LyrMaximize + : LyrTile; Client *head = c; while (head->group_prev) diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 424cf7f6..b8b142ae 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1541,6 +1541,8 @@ int32_t toggleoverlay(const Arg *arg) { wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrTop : LyrTile]); } + + client_reparent_group(c); setborder_color(c); return 0; } From afd7135fa609b4a373b557a47deb28219cf55c7c Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 11:43:03 +0800 Subject: [PATCH 16/70] fix: fix xytonode not exclue snapbuffer for client --- src/fetch/common.h | 55 ++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/src/fetch/common.h b/src/fetch/common.h index 1627ea6a..fdd85be5 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -102,7 +102,7 @@ static bool layer_ignores_focus(LayerSurface *l) { void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, MangoCustomDecorate **pd, double *nx, double *ny) { - struct wlr_scene_node *node, *pnode; + struct wlr_scene_node *node = NULL, *pnode = NULL; struct wlr_surface *surface = NULL; Client *c = NULL; LayerSurface *l = NULL; @@ -110,15 +110,21 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, int32_t layer; Client *ovc = NULL; - for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { + if (psurface) + *psurface = NULL; + if (pc) + *pc = NULL; + if (pl) + *pl = NULL; + if (pd) + *pd = NULL; + for (layer = NUM_LAYERS - 1; layer >= 0; layer--) { if (layer == LyrFadeOut) continue; - if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) - continue; - - if (!node->enabled) + node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny); + if (!node) continue; if (node->type == WLR_SCENE_NODE_BUFFER) { @@ -130,21 +136,27 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, } } - /* start from the topmost layer, - find a sureface that can be focused by pointer, - impopup neither a client nor a layer surface.*/ if (layer == LyrIMPopup) { c = NULL; l = NULL; } else { - for (pnode = node; pnode && !c; pnode = &pnode->parent->node) - c = pnode->data; - if (c && c->type == LayerShell) { - l = (LayerSurface *)c; - c = NULL; - } else if (c && c->type == CustomDecorate) { - mangocustomdecorate = (MangoCustomDecorate *)c; - c = NULL; + void *data = NULL; + for (pnode = node; pnode; pnode = &pnode->parent->node) { + if (pnode->data) { + data = pnode->data; + break; + } + } + + if (data) { + Client *temp_c = (Client *)data; + if (temp_c->type == LayerShell) { + l = (LayerSurface *)temp_c; + } else if (temp_c->type == CustomDecorate) { + mangocustomdecorate = (MangoCustomDecorate *)temp_c; + } else if (temp_c->type == XDGShell || temp_c->type == X11) { + c = temp_c; + } } } @@ -152,8 +164,9 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, if (c) { surface = client_surface(c); } - break; } + + break; } if (psurface) @@ -179,12 +192,12 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, if (ovc && (!l || layer_ignores_focus(l) || is_below)) { if (pc) *pc = ovc; - if (psurface) *psurface = ovc ? client_surface(ovc) : NULL; - - if (pl && ovc) + if (pl) *pl = NULL; + if (pd) + *pd = NULL; } } } \ No newline at end of file From b8ca3a3c3474eb86c940ac909421e28adb449c88 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 14:18:56 +0800 Subject: [PATCH 17/70] opt: optmize structruing\ --- docs/visuals/theming.md | 22 ++--- src/action/client.h | 57 ++++++------- src/animation/client.h | 36 ++++----- src/config/parse_config.h | 42 +++++----- src/draw/text-node.c | 146 +++++++++++++++++----------------- src/draw/text-node.h | 68 ++++++++-------- src/ext-protocol/text-input.h | 3 + src/fetch/common.h | 21 +++-- src/layout/arrange.h | 2 +- src/layout/scroll.h | 4 +- src/mango.c | 72 +++++++++-------- 11 files changed, 234 insertions(+), 239 deletions(-) diff --git a/docs/visuals/theming.md b/docs/visuals/theming.md index 2f9993be..75c90f37 100644 --- a/docs/visuals/theming.md +++ b/docs/visuals/theming.md @@ -69,17 +69,17 @@ You can also color-code windows based on their state: ### Tab Bar For Monocle Layout | Setting | Default | Description | | :--- | :--- | :--- | -| `tab_bar_height` | `50` | Height of the tab bar for monocle layout. | -| `tab_bar_decorate_fg_color` | `0xc4939dff` | text color. -| `tab_bar_decorate_bg_color` | `0x201b14ff` | background color.| -| `tab_bar_decorate_focus_fg_color` | `0x201b14ff` | text color for focus. | -| `tab_bar_decorate_focus_bg_color` | `0xc4939dff` | background color for focus.| -| `tab_bar_decorate_border_color` | `0x8BAA9Bff` | border color.| -| `tab_bar_decorate_border_width` | `4` | border width.| -| `tab_bar_decorate_corner_radius` | `5` | corner radius.| -| `tab_bar_decorate_padding_x` | `0` | horizontal padding.| -| `tab_bar_decorate_padding_y` | `0` | vertical padding.| -| `tab_bar_decorate_font_desc` | `monospace Bold 16` | font set.| +| `group_bar_height` | `50` | Height of the tab bar for monocle layout. | +| `group_bar_decorate_fg_color` | `0xc4939dff` | text color. +| `group_bar_decorate_bg_color` | `0x201b14ff` | background color.| +| `group_bar_decorate_focus_fg_color` | `0x201b14ff` | text color for focus. | +| `group_bar_decorate_focus_bg_color` | `0xc4939dff` | background color for focus.| +| `group_bar_decorate_border_color` | `0x8BAA9Bff` | border color.| +| `group_bar_decorate_border_width` | `4` | border width.| +| `group_bar_decorate_corner_radius` | `5` | corner radius.| +| `group_bar_decorate_padding_x` | `0` | horizontal padding.| +| `group_bar_decorate_padding_y` | `0` | vertical padding.| +| `group_bar_decorate_font_desc` | `monospace Bold 16` | font set.| ## Borders diff --git a/src/action/client.h b/src/action/client.h index 41ca0663..aca5d2ef 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -57,14 +57,14 @@ void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) { if (!ISFAKETILED(c)) return; - if (c->isfullscreen && c->tab_bar_node) { - wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + if (c->isfullscreen && c->group_bar) { + wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); } - if (!c->mon->isoverview && c->tab_bar_node && !c->isfullscreen && + if (!c->mon->isoverview && c->group_bar && !c->isfullscreen && (c->group_next || c->group_prev)) { - geo.y = geo.y + config.tab_bar_height; - geo.height -= config.tab_bar_height; + geo.y = geo.y + config.group_bar_height; + geo.height -= config.group_bar_height; } if ((!c->isfullscreen && !c->ismaximizescreen) || @@ -115,27 +115,22 @@ void client_add_jump_label_node(Client *c) { wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, false); } -void client_add_tab_bar_node(Client *c) { +void client_add_group_bar(Client *c) { - if (config.tab_bar_height <= 0) { + if (config.group_bar_height <= 0) { return; } - MangoCustomDecorate *MangoCustomDecorate = - ecalloc(1, sizeof(MangoCustomDecorate)); - MangoCustomDecorate->node_data = c; - MangoCustomDecorate->node_type = MANGO_TITLE_NODE; - MangoCustomDecorate->type = CustomDecorate; uint32_t layer = c->isoverlay ? LyrOverlay : c->isfloating || c->isfullscreen ? LyrTop : c->ismaximizescreen ? LyrMaximize : LyrTile; - c->tab_bar_node = mango_tab_bar_node_create( - MangoCustomDecorate, layers[layer], config.tabdata, 0, 0); - wlr_scene_node_lower_to_bottom(&c->tab_bar_node->scene_buffer->node); - wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); - mango_tab_bar_node_update(c->tab_bar_node, client_get_title(c), 1.0); + c->group_bar = mango_group_bar_create(c, GroupBar, layers[layer], + config.tabdata, 0, 0); + wlr_scene_node_lower_to_bottom(&c->group_bar->scene_buffer->node); + wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); + mango_group_bar_update(c->group_bar, client_get_title(c), 1.0); } void client_focus_group_member(Client *c) { @@ -165,11 +160,11 @@ void client_focus_group_member(Client *c) { cur_focusing->isgroupfocusing = false; c->mon = cur_focusing->mon; client_replace(c, cur_focusing, true); - mango_tab_bar_node_set_focus(cur_focusing->tab_bar_node, false); + mango_group_bar_set_focus(cur_focusing->group_bar, false); } c->isgroupfocusing = true; - mango_tab_bar_node_set_focus(c->tab_bar_node, true); + mango_group_bar_set_focus(c->group_bar, true); client_reparent_group(c); @@ -189,14 +184,14 @@ void client_check_tab_node_visible(Client *c) { Client *cur = head; while (cur) { - if (!c->mon->isoverview && cur->tab_bar_node && + if (!c->mon->isoverview && cur->group_bar && (cur->group_next || cur->group_prev) && VISIBLEON(c, c->mon) && ISNORMAL(c) && !c->isfullscreen && (!is_monocle_layout(c->mon) || c == c->mon->sel)) { - wlr_scene_node_set_enabled(&cur->tab_bar_node->scene_buffer->node, + wlr_scene_node_set_enabled(&cur->group_bar->scene_buffer->node, true); } else { - wlr_scene_node_set_enabled(&cur->tab_bar_node->scene_buffer->node, + wlr_scene_node_set_enabled(&cur->group_bar->scene_buffer->node, false); } cur = cur->group_next; @@ -216,8 +211,8 @@ void client_raise_group(Client *c) { Client *cur = head; while (cur) { - if (cur->tab_bar_node) { - wlr_scene_node_raise_to_top(&cur->tab_bar_node->scene_buffer->node); + if (cur->group_bar) { + wlr_scene_node_raise_to_top(&cur->group_bar->scene_buffer->node); wlr_scene_node_raise_to_top(&cur->scene->node); } cur = cur->group_next; @@ -225,7 +220,7 @@ void client_raise_group(Client *c) { } void client_reparent_group(Client *c) { - if (!c || !c->tab_bar_node) + if (!c || !c->group_bar) return; if (!c->group_prev && !c->group_next) @@ -242,8 +237,8 @@ void client_reparent_group(Client *c) { Client *cur = head; while (cur) { - if (cur->tab_bar_node) { - wlr_scene_node_reparent(&cur->tab_bar_node->scene_buffer->node, + if (cur->group_bar) { + wlr_scene_node_reparent(&cur->group_bar->scene_buffer->node, layers[layer]); wlr_scene_node_reparent(&cur->scene->node, layers[layer]); } @@ -251,13 +246,13 @@ void client_reparent_group(Client *c) { } } -void client_handle_decorate_click(MangoCustomDecorate *md) { +void client_handle_decorate_click(MangoGroupBar *gb) { - if (!md) + if (!gb) return; - if (md->node_type == MANGO_TITLE_NODE) { - Client *c = md->node_data; + if (gb->node_data) { + Client *c = gb->node_data; client_focus_group_member(c); } } \ No newline at end of file diff --git a/src/animation/client.h b/src/animation/client.h index 10c3d836..eb0eb328 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -414,12 +414,12 @@ void client_draw_shadow(Client *c) { void client_draw_title(Client *c) { - if (!c || !c->tab_bar_node) + if (!c || !c->group_bar) return; - if (!c->group_next && !c->group_prev && c->tab_bar_node && - c->tab_bar_node->scene_buffer->node.enabled) { - wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + if (!c->group_next && !c->group_prev && c->group_bar && + c->group_bar->scene_buffer->node.enabled) { + wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); return; } @@ -438,21 +438,21 @@ void client_draw_title(Client *c) { } int32_t tab_x = c->animation.current.x; - int32_t tab_y = c->animation.current.y - config.tab_bar_height; + int32_t tab_y = c->animation.current.y - config.group_bar_height; int32_t tw = c->animation.current.width; - int32_t th = config.tab_bar_height; + int32_t th = config.group_bar_height; int32_t left_over = c->mon->m.x - tab_x; int32_t right_over = tab_x + tw - c->mon->m.x - c->mon->m.width; int32_t top_over = c->mon->m.y - tab_y; int32_t bottom_over = - tab_y + config.tab_bar_height - c->mon->m.y - c->mon->m.height; + tab_y + config.group_bar_height - c->mon->m.y - c->mon->m.height; if (c != grabc && (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouting)) { if (top_over > 0) { tab_y = c->mon->m.y; - th = config.tab_bar_height - top_over; + th = config.group_bar_height - top_over; } if (bottom_over > 0) { th = th - bottom_over; @@ -469,9 +469,9 @@ void client_draw_title(Client *c) { if (tw <= 0 || th <= 0) { cur = head; while (cur) { - if (cur->tab_bar_node) - wlr_scene_node_set_enabled( - &cur->tab_bar_node->scene_buffer->node, false); + if (cur->group_bar) + wlr_scene_node_set_enabled(&cur->group_bar->scene_buffer->node, + false); cur = cur->group_next; } return; @@ -486,7 +486,7 @@ void client_draw_title(Client *c) { for (int i = 0; i < count && cur; i++) { int32_t w = bar_w + (i < rem ? 1 : 0); - global_draw_tab_bar(cur, x, tab_y, w, th); + global_draw_group_bar(cur, x, tab_y, w, th); x += w; cur = cur->group_next; } @@ -532,17 +532,17 @@ void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { } } -void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, - int32_t height) { - if (!c->tab_bar_node) +void global_draw_group_bar(Client *c, int32_t x, int32_t y, int32_t width, + int32_t height) { + if (!c->group_bar) return; if (height <= 0) { - wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); } - wlr_scene_node_set_position(&c->tab_bar_node->scene_buffer->node, x, y); - mango_tab_bar_node_set_size(c->tab_bar_node, width, height); + wlr_scene_node_set_position(&c->group_bar->scene_buffer->node, x, y); + mango_group_bar_set_size(c->group_bar, width, height); } void apply_split_border(Client *c, bool hit_no_border) { diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 0bc26a7f..875ce081 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -333,7 +333,7 @@ typedef struct { uint32_t gappoh; uint32_t gappov; uint32_t borderpx; - uint32_t tab_bar_height; + uint32_t group_bar_height; float scratchpad_width_ratio; float scratchpad_height_ratio; float rootcolor[4]; @@ -1774,75 +1774,75 @@ bool parse_option(Config *config, char *key, char *value) { config->cursor_size = atoi(value); } else if (strcmp(key, "cursor_theme") == 0) { config->cursor_theme = strdup(value); - } else if (strcmp(key, "tab_bar_decorate_font_desc") == 0) { + } else if (strcmp(key, "group_bar_decorate_font_desc") == 0) { config->tabdata.font_desc = strdup(value); - } else if (strcmp(key, "tab_bar_decorate_fg_color") == 0) { + } else if (strcmp(key, "group_bar_decorate_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "tab_bar_decorate_fg_color " + "group_bar_decorate_fg_color " "format: %s\n", value); return false; } else { convert_hex_to_rgba(config->tabdata.fg_color, color); } - } else if (strcmp(key, "tab_bar_decorate_bg_color") == 0) { + } else if (strcmp(key, "group_bar_decorate_bg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "tab_bar_decorate_bg_color " + "group_bar_decorate_bg_color " "format: %s\n", value); return false; } else { convert_hex_to_rgba(config->tabdata.bg_color, color); } - } else if (strcmp(key, "tab_bar_decorate_focus_fg_color") == 0) { + } else if (strcmp(key, "group_bar_decorate_focus_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "tab_bar_decorate_focus_fg_color " + "group_bar_decorate_focus_fg_color " "format: %s\n", value); return false; } else { convert_hex_to_rgba(config->tabdata.focus_fg_color, color); } - } else if (strcmp(key, "tab_bar_decorate_focus_bg_color") == 0) { + } else if (strcmp(key, "group_bar_decorate_focus_bg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "tab_bar_decorate_focus_bg_color " + "group_bar_decorate_focus_bg_color " "format: %s\n", value); return false; } else { convert_hex_to_rgba(config->tabdata.focus_bg_color, color); } - } else if (strcmp(key, "tab_bar_decorate_border_color") == 0) { + } else if (strcmp(key, "group_bar_decorate_border_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "tab_bar_decorate_border_color " + "group_bar_decorate_border_color " "format: %s\n", value); return false; } else { convert_hex_to_rgba(config->tabdata.border_color, color); } - } else if (strcmp(key, "tab_bar_decorate_border_width") == 0) { + } else if (strcmp(key, "group_bar_decorate_border_width") == 0) { config->tabdata.border_width = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "tab_bar_decorate_corner_radius") == 0) { + } else if (strcmp(key, "group_bar_decorate_corner_radius") == 0) { config->tabdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "tab_bar_decorate_padding_x") == 0) { + } else if (strcmp(key, "group_bar_decorate_padding_x") == 0) { config->tabdata.padding_x = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "tab_bar_decorate_padding_y") == 0) { + } else if (strcmp(key, "group_bar_decorate_padding_y") == 0) { config->tabdata.padding_y = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "jump_label_decorate_font_desc") == 0) { config->jumplabeldata.font_desc = strdup(value); @@ -1960,8 +1960,8 @@ bool parse_option(Config *config, char *key, char *value) { config->scratchpad_height_ratio = atof(value); } else if (strcmp(key, "borderpx") == 0) { config->borderpx = atoi(value); - } else if (strcmp(key, "tab_bar_height") == 0) { - config->tab_bar_height = atoi(value); + } else if (strcmp(key, "group_bar_height") == 0) { + config->group_bar_height = atoi(value); } else if (strcmp(key, "rootcolor") == 0) { int64_t color = parse_color(value); if (color == -1) { @@ -3581,7 +3581,7 @@ void override_config(void) { config.scratchpad_height_ratio = CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f); config.borderpx = CLAMP_INT(config.borderpx, 0, 200); - config.tab_bar_height = CLAMP_INT(config.tab_bar_height, 0, 500); + config.group_bar_height = CLAMP_INT(config.group_bar_height, 0, 500); config.smartgaps = CLAMP_INT(config.smartgaps, 0, 1); config.blur = CLAMP_INT(config.blur, 0, 1); config.blur_layer = CLAMP_INT(config.blur_layer, 0, 1); @@ -3713,7 +3713,7 @@ void set_value_default() { config.idleinhibit_ignore_visible = 0; config.borderpx = 4; - config.tab_bar_height = 50; + config.group_bar_height = 50; config.overviewgappi = 5; config.overviewgappo = 30; config.cursor_hide_timeout = 0; @@ -4130,7 +4130,7 @@ void reapply_property(void) { mango_jump_label_node_apply_config(c->jump_label_node, &config.jumplabeldata); - mango_tab_bar_node_apply_config(c->tab_bar_node, &config.tabdata); + mango_group_bar_apply_config(c->group_bar, &config.tabdata); wlr_scene_rect_set_color(c->droparea, config.dropcolor); wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor); diff --git a/src/draw/text-node.c b/src/draw/text-node.c index c85b25dc..2b6c21d7 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -59,10 +59,9 @@ static const struct wlr_buffer_impl text_buffer_impl = { .end_data_ptr_access = text_buffer_end_data_ptr_access, }; -struct mango_jump_label_node * -mango_jump_label_node_create(struct wlr_scene_tree *parent, - DecorateDrawData data) { - struct mango_jump_label_node *node = calloc(1, sizeof(*node)); +MangoJumpLabel *mango_jump_label_node_create(struct wlr_scene_tree *parent, + DecorateDrawData data) { + MangoJumpLabel *node = calloc(1, sizeof(*node)); if (!node) return NULL; @@ -104,7 +103,7 @@ mango_jump_label_node_create(struct wlr_scene_tree *parent, return node; } -void mango_jump_label_node_destroy(struct mango_jump_label_node *node) { +void mango_jump_label_node_destroy(MangoJumpLabel *node) { if (!node) return; @@ -136,8 +135,8 @@ void mango_jump_label_node_destroy(struct mango_jump_label_node *node) { free(node); } -void mango_jump_label_node_set_background(struct mango_jump_label_node *node, - float r, float g, float b, float a) { +void mango_jump_label_node_set_background(MangoJumpLabel *node, float r, + float g, float b, float a) { if (!node) return; node->bg_color[0] = r; @@ -146,9 +145,9 @@ void mango_jump_label_node_set_background(struct mango_jump_label_node *node, node->bg_color[3] = a; } -void mango_jump_label_node_set_border(struct mango_jump_label_node *node, - float r, float g, float b, float a, - int32_t width, int32_t radius) { +void mango_jump_label_node_set_border(MangoJumpLabel *node, float r, float g, + float b, float a, int32_t width, + int32_t radius) { if (!node) return; node->border_color[0] = r; @@ -159,17 +158,16 @@ void mango_jump_label_node_set_border(struct mango_jump_label_node *node, node->corner_radius = radius; } -void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, - int32_t pad_x, int32_t pad_y) { +void mango_jump_label_node_set_padding(MangoJumpLabel *node, int32_t pad_x, + int32_t pad_y) { if (!node) return; node->padding_x = pad_x >= 0 ? pad_x : 0; node->padding_y = pad_y >= 0 ? pad_y : 0; } -static void get_text_pixel_size(struct mango_jump_label_node *node, - const char *text, float scale, int32_t *out_w, - int32_t *out_h) { +static void get_text_pixel_size(MangoJumpLabel *node, const char *text, + float scale, int32_t *out_w, int32_t *out_h) { if (node->measure_scale != scale) { pango_cairo_context_set_resolution(node->measure_context, 96.0 * scale); node->measure_scale = scale; @@ -193,8 +191,8 @@ static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, cairo_close_path(cr); } -void mango_jump_label_node_update(struct mango_jump_label_node *node, - const char *text, float scale) { +void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, + float scale) { if (!node || !text) return; if (scale <= 0.0f) @@ -407,8 +405,7 @@ void mango_jump_label_node_update(struct mango_jump_label_node *node, node->logical_height); } -void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, - bool focused) { +void mango_jump_label_node_set_focus(MangoJumpLabel *node, bool focused) { if (!node || node->focused == focused) return; node->focused = focused; @@ -419,55 +416,61 @@ void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, } } -struct mango_tab_bar_node * -mango_tab_bar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, - DecorateDrawData data, int32_t width, - int32_t height) { - struct mango_tab_bar_node *node = calloc(1, sizeof(*node)); - if (!node) +MangoGroupBar *mango_group_bar_create(void *cdata, uint32_t type, + struct wlr_scene_tree *parent, + DecorateDrawData data, int32_t width, + int32_t height) { + MangoGroupBar *mangobar = calloc(1, sizeof(*mangobar)); + if (!mangobar) return NULL; - node->scene_buffer = wlr_scene_buffer_create(parent, NULL); - if (!node->scene_buffer) { - free(node); + mangobar->scene_buffer = wlr_scene_buffer_create(parent, NULL); + if (!mangobar->scene_buffer) { + free(mangobar); return NULL; } - memcpy(node->fg_color, data.fg_color, sizeof(node->fg_color)); - memcpy(node->bg_color, data.bg_color, sizeof(node->bg_color)); - memcpy(node->focus_fg_color, data.focus_fg_color, - sizeof(node->focus_fg_color)); - memcpy(node->focus_bg_color, data.focus_bg_color, - sizeof(node->focus_bg_color)); - memcpy(node->border_color, data.border_color, sizeof(node->border_color)); - node->border_width = data.border_width; - node->corner_radius = data.corner_radius; - node->padding_x = data.padding_x; - node->padding_y = data.padding_y; - node->font_desc = + memcpy(mangobar->fg_color, data.fg_color, sizeof(mangobar->fg_color)); + memcpy(mangobar->bg_color, data.bg_color, sizeof(mangobar->bg_color)); + memcpy(mangobar->focus_fg_color, data.focus_fg_color, + sizeof(mangobar->focus_fg_color)); + memcpy(mangobar->focus_bg_color, data.focus_bg_color, + sizeof(mangobar->focus_bg_color)); + memcpy(mangobar->border_color, data.border_color, + sizeof(mangobar->border_color)); + mangobar->border_width = data.border_width; + mangobar->corner_radius = data.corner_radius; + mangobar->padding_x = data.padding_x; + mangobar->padding_y = data.padding_y; + mangobar->font_desc = g_strdup(data.font_desc ? data.font_desc : "monospace Bold 16"); - node->target_width = width; - node->target_height = height; - node->focused = false; - node->cached_focused = false; + mangobar->target_width = width; + mangobar->target_height = height; + mangobar->focused = false; + mangobar->cached_focused = false; - node->measure_surface = + mangobar->measure_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); - node->measure_cr = cairo_create(node->measure_surface); - node->measure_context = pango_cairo_create_context(node->measure_cr); - node->measure_layout = pango_layout_new(node->measure_context); - node->measure_scale = 1.0f; + mangobar->measure_cr = cairo_create(mangobar->measure_surface); + mangobar->measure_context = + pango_cairo_create_context(mangobar->measure_cr); + mangobar->measure_layout = pango_layout_new(mangobar->measure_context); + mangobar->measure_scale = 1.0f; - node->cached_scale = -1.0f; - node->last_text = NULL; - node->last_scale = 0.0f; - node->scene_buffer->node.data = mango_node_data; + mangobar->cached_scale = -1.0f; + mangobar->last_text = NULL; + mangobar->last_scale = 0.0f; - return node; + mangobar->type = type; + mangobar->node_data = cdata; + + mangobar->scene_buffer->node.data = mangobar; + + return mangobar; } -void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node) { +void mango_group_bar_destroy(MangoGroupBar *node) { if (!node) return; @@ -492,19 +495,15 @@ void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node) { if (node->measure_cr) cairo_destroy(node->measure_cr); - void *data = node->scene_buffer->node.data; - wlr_scene_node_destroy(&node->scene_buffer->node); - g_free(node->font_desc); g_free(node->cached_text); g_free(node->cached_font_desc); g_free(node->last_text); - free(data); free(node); } -void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, - int32_t height) { +void mango_group_bar_set_size(MangoGroupBar *node, int32_t width, + int32_t height) { if (!node) return; @@ -522,11 +521,11 @@ void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, const char *redraw_text = node->last_text ? node->last_text : ""; float redraw_scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_tab_bar_node_update(node, redraw_text, redraw_scale); + mango_group_bar_update(node, redraw_text, redraw_scale); } -void mango_tab_bar_node_update(struct mango_tab_bar_node *node, - const char *text, float scale) { +void mango_group_bar_update(MangoGroupBar *node, const char *text, + float scale) { if (!node || !text) return; if (scale <= 0.0f) @@ -765,19 +764,18 @@ void mango_tab_bar_node_update(struct mango_tab_bar_node *node, node->logical_height); } -void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, - bool focused) { +void mango_group_bar_set_focus(MangoGroupBar *node, bool focused) { if (!node || node->focused == focused) return; node->focused = focused; if (node->last_text) { float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_tab_bar_node_update(node, node->last_text, scale); + mango_group_bar_update(node, node->last_text, scale); } } -void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, - const float fg[4], const float bg[4]) { +void mango_group_bar_set_colors(MangoGroupBar *node, const float fg[4], + const float bg[4]) { if (!node) return; @@ -786,11 +784,11 @@ void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, if (!node->focused && node->last_text) { float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_tab_bar_node_update(node, node->last_text, scale); + mango_group_bar_update(node, node->last_text, scale); } } -void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, +void mango_jump_label_node_apply_config(MangoJumpLabel *node, const DecorateDrawData *data) { if (!node || !data) return; @@ -817,8 +815,8 @@ void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, } } -void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, - const DecorateDrawData *data) { +void mango_group_bar_apply_config(MangoGroupBar *node, + const DecorateDrawData *data) { if (!node || !data) return; @@ -840,6 +838,6 @@ void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, if (node->last_text) { float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_tab_bar_node_update(node, node->last_text, scale); + mango_group_bar_update(node, node->last_text, scale); } } \ No newline at end of file diff --git a/src/draw/text-node.h b/src/draw/text-node.h index 079af908..2eb3c4e8 100644 --- a/src/draw/text-node.h +++ b/src/draw/text-node.h @@ -26,8 +26,7 @@ struct mango_text_buffer { struct wlr_buffer base; cairo_surface_t *surface; }; - -struct mango_jump_label_node { +typedef struct { struct wlr_scene_buffer *scene_buffer; struct mango_text_buffer *buffer; cairo_surface_t *surface; @@ -70,13 +69,15 @@ struct mango_jump_label_node { int32_t logical_width; int32_t logical_height; -}; +} MangoJumpLabel; -struct mango_tab_bar_node { +typedef struct { + uint32_t type; struct wlr_scene_buffer *scene_buffer; struct mango_text_buffer *buffer; cairo_surface_t *surface; int surface_pixel_w, surface_pixel_h; + void *node_data; // 存储窗口指针 // 初始配置 float fg_color[4]; @@ -126,41 +127,38 @@ struct mango_tab_bar_node { int32_t logical_width; int32_t logical_height; -}; +} MangoGroupBar; void mango_text_global_finish(void); -struct mango_jump_label_node * -mango_jump_label_node_create(struct wlr_scene_tree *parent, - DecorateDrawData data); -void mango_jump_label_node_destroy(struct mango_jump_label_node *node); -void mango_jump_label_node_set_background(struct mango_jump_label_node *node, - float r, float g, float b, float a); -void mango_jump_label_node_set_border(struct mango_jump_label_node *node, - float r, float g, float b, float a, - int32_t width, int32_t radius); -void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, - int32_t pad_x, int32_t pad_y); -void mango_jump_label_node_update(struct mango_jump_label_node *node, - const char *text, float scale); +MangoJumpLabel *mango_jump_label_node_create(struct wlr_scene_tree *parent, + DecorateDrawData data); +void mango_jump_label_node_destroy(MangoJumpLabel *node); +void mango_jump_label_node_set_background(MangoJumpLabel *node, float r, + float g, float b, float a); +void mango_jump_label_node_set_border(MangoJumpLabel *node, float r, float g, + float b, float a, int32_t width, + int32_t radius); +void mango_jump_label_node_set_padding(MangoJumpLabel *node, int32_t pad_x, + int32_t pad_y); +void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, + float scale); -struct mango_tab_bar_node * -mango_tab_bar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, - DecorateDrawData data, int32_t width, int32_t height); -void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node); -void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, - int32_t height); -void mango_tab_bar_node_update(struct mango_tab_bar_node *node, - const char *text, float scale); +MangoGroupBar *mango_group_bar_create(void *cdata, uint32_t type, + struct wlr_scene_tree *parent, + DecorateDrawData data, int32_t width, + int32_t height); +void mango_group_bar_destroy(MangoGroupBar *node); +void mango_group_bar_set_size(MangoGroupBar *node, int32_t width, + int32_t height); +void mango_group_bar_update(MangoGroupBar *node, const char *text, float scale); -void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, - bool focused); -void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, - bool focused); +void mango_jump_label_node_set_focus(MangoJumpLabel *node, bool focused); +void mango_group_bar_set_focus(MangoGroupBar *node, bool focused); -void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, - const float fg[4], const float bg[4]); -void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, +void mango_group_bar_set_colors(MangoGroupBar *node, const float fg[4], + const float bg[4]); +void mango_jump_label_node_apply_config(MangoJumpLabel *node, const DecorateDrawData *data); -void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, - const DecorateDrawData *data); +void mango_group_bar_apply_config(MangoGroupBar *node, + const DecorateDrawData *data); #endif // jump_label_node_H \ No newline at end of file diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index 7b4e1a1f..ebe8c7b1 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -27,6 +27,7 @@ struct dwl_input_method_relay { }; struct dwl_input_method_popup { + uint32_t type; struct wlr_input_popup_surface_v2 *popup_surface; struct wlr_scene_tree *tree; struct wlr_scene_tree *scene_surface; @@ -404,6 +405,8 @@ static void handle_input_method_new_popup_surface(struct wl_listener *listener, popup->tree = wlr_scene_tree_create(layers[LyrIMPopup]); popup->scene_surface = wlr_scene_subsurface_tree_create( popup->tree, popup->popup_surface->surface); + + popup->type = XdgImPopup; popup->scene_surface->node.data = popup; wl_list_insert(&relay->popups, &popup->link); diff --git a/src/fetch/common.h b/src/fetch/common.h index fdd85be5..d017dff3 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -100,13 +100,12 @@ static bool layer_ignores_focus(LayerSurface *l) { } void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, - LayerSurface **pl, MangoCustomDecorate **pd, double *nx, - double *ny) { + LayerSurface **pl, MangoGroupBar **gb, double *nx, double *ny) { struct wlr_scene_node *node = NULL, *pnode = NULL; struct wlr_surface *surface = NULL; Client *c = NULL; LayerSurface *l = NULL; - MangoCustomDecorate *mangocustomdecorate = NULL; + MangoGroupBar *mangogroupbar = NULL; int32_t layer; Client *ovc = NULL; @@ -116,8 +115,8 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, *pc = NULL; if (pl) *pl = NULL; - if (pd) - *pd = NULL; + if (gb) + *gb = NULL; for (layer = NUM_LAYERS - 1; layer >= 0; layer--) { if (layer == LyrFadeOut) @@ -152,8 +151,8 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, Client *temp_c = (Client *)data; if (temp_c->type == LayerShell) { l = (LayerSurface *)temp_c; - } else if (temp_c->type == CustomDecorate) { - mangocustomdecorate = (MangoCustomDecorate *)temp_c; + } else if (temp_c->type == GroupBar) { + mangogroupbar = (MangoGroupBar *)temp_c; } else if (temp_c->type == XDGShell || temp_c->type == X11) { c = temp_c; } @@ -175,8 +174,8 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, *pc = c; if (pl) *pl = l; - if (pd) - *pd = mangocustomdecorate; + if (gb) + *gb = mangogroupbar; if (selmon && selmon->isoverview && config.ov_no_resize) { ovc = xytoclient(x, y); @@ -196,8 +195,8 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, *psurface = ovc ? client_surface(ovc) : NULL; if (pl) *pl = NULL; - if (pd) - *pd = NULL; + if (gb) + *gb = NULL; } } } \ No newline at end of file diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 4a441f14..803970da 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -1132,7 +1132,7 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, client_add_jump_label_node(c); } - if (c->tab_bar_node && c->tab_bar_node->scene_buffer->node.enabled) { + if (c->group_bar && c->group_bar->scene_buffer->node.enabled) { client_check_tab_node_visible(c); } diff --git a/src/layout/scroll.h b/src/layout/scroll.h index 63e52c99..dbdfe919 100644 --- a/src/layout/scroll.h +++ b/src/layout/scroll.h @@ -698,7 +698,7 @@ void vertical_scroller(Monitor *m) { } else { bar_height = !root_client->isfullscreen && (root_client->group_prev || root_client->group_next) - ? config.tab_bar_height + ? config.group_bar_height : 0; target_geom.y = root_client->geom.y - bar_height; @@ -718,7 +718,7 @@ void vertical_scroller(Monitor *m) { bar_height = !heads[focus_index - i + 1]->client->isfullscreen && (heads[focus_index - i + 1]->client->group_prev || heads[focus_index - i + 1]->client->group_next) - ? config.tab_bar_height + ? config.group_bar_height : 0; up_geom.y = heads[focus_index - i + 1]->client->geom.y - cur_gappiv - diff --git a/src/mango.c b/src/mango.c index d69e74d7..95ca4e91 100644 --- a/src/mango.c +++ b/src/mango.c @@ -165,9 +165,17 @@ enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; enum { VERTICAL, HORIZONTAL }; enum { SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT }; -enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -enum { XDGShell, LayerShell, X11, CustomDecorate, Snapshot }; /* client types */ -enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 +enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ +enum { + XDGShell, + LayerShell, + X11, + Snapshot, + XdgPopup, + XdgImPopup, + GroupBar +}; /* client types */ +enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 enum { LyrBg, LyrBlur, @@ -182,8 +190,6 @@ enum { NUM_LAYERS }; /* scene layers */ -enum mango_node_type { MANGO_TITLE_NODE, MANGO_JUMP_NODE }; - #ifdef XWAYLAND enum { NetWMWindowTypeDialog, @@ -249,13 +255,6 @@ typedef struct { uint32_t ui2; Client *tc; } Arg; - -typedef struct { - uint32_t type; - enum mango_node_type node_type; - void *node_data; -} MangoCustomDecorate; - typedef struct { uint32_t mod; uint32_t button; @@ -347,8 +346,8 @@ struct Client { struct wlr_ext_image_capture_source_v1 *image_capture_source; struct wlr_scene_surface *image_capture_scene_surface; struct wlr_scene_tree *overview_scene_surface; - struct mango_jump_label_node *jump_label_node; - struct mango_tab_bar_node *tab_bar_node; + MangoJumpLabel *jump_label_node; + MangoGroupBar *group_bar; struct wl_list link; struct wl_list flink; struct wl_list fadeout_link; @@ -540,6 +539,7 @@ typedef struct { } LayerSurface; typedef struct { + uint32_t type; struct wlr_xdg_popup *wlr_popup; struct wl_listener destroy; struct wl_listener commit; @@ -805,7 +805,7 @@ static Monitor *get_monitor_nearest_to(int32_t x, int32_t y); static void handle_iamge_copy_capture_new_session(struct wl_listener *listener, void *data); static void xytonode(double x, double y, struct wlr_surface **psurface, - Client **pc, LayerSurface **pl, MangoCustomDecorate **pd, + Client **pc, LayerSurface **pl, MangoGroupBar **tb, double *nx, double *ny); static void clear_fullscreen_flag(Client *c); static pid_t getparentprocess(pid_t p); @@ -943,8 +943,8 @@ static void overview_backup_surface(Client *c); static void create_jump_hints(Monitor *m); static void finish_jump_mode(Monitor *m); static void begin_jump_mode(Monitor *m); -static void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, - int32_t height); +static void global_draw_group_bar(Client *c, int32_t x, int32_t y, + int32_t width, int32_t height); static void client_reparent_group(Client *c); @@ -1333,7 +1333,7 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { } if (c->isgroupfocusing) - mango_tab_bar_node_set_focus(c->tab_bar_node, true); + mango_group_bar_set_focus(c->group_bar, true); } if (w->overview_scene_surface) { @@ -1346,8 +1346,8 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { overview_backup_surface(c); } - if (w->tab_bar_node && !isgroupaction) { - wlr_scene_node_set_enabled(&w->tab_bar_node->scene_buffer->node, false); + if (w->group_bar && !isgroupaction) { + wlr_scene_node_set_enabled(&w->group_bar->scene_buffer->node, false); } if (c->link.prev && c->link.next && c->link.prev != &c->link) { @@ -2436,7 +2436,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { uint32_t hard_mods, mods; Client *c = NULL; LayerSurface *l = NULL; - MangoCustomDecorate *md = NULL; + MangoGroupBar *gb = NULL; struct wlr_surface *surface; Client *tmpc = NULL; int32_t ji; @@ -2458,7 +2458,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { if (locked) break; - xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &md, NULL, NULL); + xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &gb, NULL, NULL); if (toplevel_from_wlr_surface(surface, &c, &l) >= 0) { if (c && c->scene->node.enabled && (!client_is_unmanaged(c) || client_wants_focus(c))) @@ -2489,7 +2489,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { } // handle click on tile node - client_handle_decorate_click(md); + client_handle_decorate_click(gb); // 当鼠标焦点在layer上的时候,不检测虚拟键盘的mod状态, // 避免layer虚拟键盘锁死mod按键状态 @@ -3154,6 +3154,8 @@ static void createpopup(struct wl_listener *listener, void *data) { if (!popup) return; + popup->type = XdgPopup; + popup->destroy.notify = destroypopup; wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); @@ -4521,7 +4523,7 @@ void init_client_properties(Client *c) { c->grid_col_per = 1.0f; c->grid_row_per = 1.0f; c->jump_label_node = NULL; - c->tab_bar_node = NULL; + c->group_bar = NULL; c->overview_scene_surface = NULL; c->drop_direction = UNDIR; c->enable_drop_area_draw = false; @@ -4698,7 +4700,7 @@ mapnotify(struct wl_listener *listener, void *data) { wlr_scene_node_set_enabled(&c->splitindicator[i]->node, false); } - client_add_tab_bar_node(c); + client_add_group_bar(c); c->droparea = wlr_scene_rect_create(c->scene, 0, 0, config.dropcolor); wlr_scene_node_lower_to_bottom(&c->droparea->node); @@ -5716,9 +5718,9 @@ void reset_maximizescreen_size(Client *c) { geom.width = c->mon->w.width - 2 * config.gappoh; geom.height = c->mon->w.height - 2 * config.gappov; - if ((c->group_next || c->group_prev) && c->tab_bar_node) { - geom.height -= config.tab_bar_height; - geom.y += config.tab_bar_height; + if ((c->group_next || c->group_prev) && c->group_bar) { + geom.height -= config.group_bar_height; + geom.y += config.group_bar_height; } resize(c, geom, 0); @@ -5763,9 +5765,9 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { maximizescreen_box.width = c->mon->w.width - 2 * config.gappoh; maximizescreen_box.height = c->mon->w.height - 2 * config.gappov; - if ((c->group_next || c->group_prev) && c->tab_bar_node) { - maximizescreen_box.height -= config.tab_bar_height; - maximizescreen_box.y += config.tab_bar_height; + if ((c->group_next || c->group_prev) && c->group_bar) { + maximizescreen_box.height -= config.group_bar_height; + maximizescreen_box.y += config.group_bar_height; } wlr_scene_node_raise_to_top(&c->scene->node); @@ -6853,9 +6855,9 @@ void unmapnotify(struct wl_listener *listener, void *data) { mango_jump_label_node_destroy(c->jump_label_node); c->jump_label_node = NULL; } - if (c->tab_bar_node) { - mango_tab_bar_node_destroy(c->tab_bar_node); - c->tab_bar_node = NULL; + if (c->group_bar) { + mango_group_bar_destroy(c->group_bar); + c->group_bar = NULL; } wlr_scene_node_destroy(&c->image_capture_scene_surface->buffer->node); @@ -7015,7 +7017,7 @@ void updatetitle(struct wl_listener *listener, void *data) { const char *title; title = client_get_title(c); - mango_tab_bar_node_update(c->tab_bar_node, title, 1.0); + mango_group_bar_update(c->group_bar, title, 1.0); if (title && c->foreign_toplevel) wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); if (title && c->ext_foreign_toplevel) { From 95b0724cff864a5866554da1362597e915456ebb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 14:31:43 +0800 Subject: [PATCH 18/70] opt: add common for struct type It must be placed first; otherwise, after the xytonode's null pointer is forcibly converted, the reading type will encounter an incorrect address --- src/draw/text-node.h | 2 +- src/ext-protocol/text-input.h | 2 +- src/mango.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/draw/text-node.h b/src/draw/text-node.h index 2eb3c4e8..23ac4f9a 100644 --- a/src/draw/text-node.h +++ b/src/draw/text-node.h @@ -72,7 +72,7 @@ typedef struct { } MangoJumpLabel; typedef struct { - uint32_t type; + uint32_t type; // must at first in struct struct wlr_scene_buffer *scene_buffer; struct mango_text_buffer *buffer; cairo_surface_t *surface; diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index ebe8c7b1..0eb89881 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -27,7 +27,7 @@ struct dwl_input_method_relay { }; struct dwl_input_method_popup { - uint32_t type; + uint32_t type; // must at first in struct struct wlr_input_popup_surface_v2 *popup_surface; struct wlr_scene_tree *tree; struct wlr_scene_tree *scene_surface; diff --git a/src/mango.c b/src/mango.c index 95ca4e91..b0dcbc80 100644 --- a/src/mango.c +++ b/src/mango.c @@ -328,7 +328,7 @@ typedef struct { struct Client { /* Must keep these three elements in this order */ - uint32_t type; /* XDGShell or X11* */ + uint32_t type; // must at first in struct struct wlr_box geom, pending, float_geom, animainit_geom, overview_backup_geom, current, drag_begin_geom; /* layout-relative, includes border */ @@ -509,7 +509,7 @@ typedef struct { typedef struct { /* Must keep these three elements in this order */ - uint32_t type; /* LayerShell */ + uint32_t type; // must at first in struct struct wlr_box geom, current, pending, animainit_geom; Monitor *mon; struct wlr_scene_tree *scene; @@ -539,7 +539,7 @@ typedef struct { } LayerSurface; typedef struct { - uint32_t type; + uint32_t type; // must at first in struct struct wlr_xdg_popup *wlr_popup; struct wl_listener destroy; struct wl_listener commit; @@ -651,7 +651,7 @@ struct TagScrollerState { }; typedef struct { - uint32_t type; + uint32_t type; // must at first in struct int32_t orig_width; int32_t orig_height; bool is_subsurface; From 1b2e1cec68f7a9f211531f229722060b06691fdd Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 16:50:22 +0800 Subject: [PATCH 19/70] fix: clip error when cross monitor --- src/animation/client.h | 57 ++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index eb0eb328..83df14b1 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -13,8 +13,8 @@ struct fx_corner_radii set_client_corner_location(Client *c) { struct fx_corner_radii current_corner_location = corner_radii_all(config.border_radius); - if (c == grabc || - (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting)) { return current_corner_location; } @@ -376,8 +376,8 @@ void client_draw_shadow(Client *c) { int32_t right_offset, bottom_offset, left_offset, top_offset; - if (c == grabc || - (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting)) { right_offset = 0; bottom_offset = 0; left_offset = 0; @@ -448,22 +448,27 @@ void client_draw_title(Client *c) { int32_t bottom_over = tab_y + config.group_bar_height - c->mon->m.y - c->mon->m.height; - if (c != grabc && - (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouting)) { - if (top_over > 0) { - tab_y = c->mon->m.y; - th = config.group_bar_height - top_over; - } - if (bottom_over > 0) { - th = th - bottom_over; - } - if (right_over > 0) { - tw = tw - right_over; - } - if (left_over > 0) { - tab_x = c->mon->m.x; - tw = tw - left_over; - } + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting)) { + top_over = 0; + bottom_over = 0; + left_over = 0; + right_over = 0; + } + + if (top_over > 0) { + tab_y = c->mon->m.y; + th = config.group_bar_height - top_over; + } + if (bottom_over > 0) { + th = th - bottom_over; + } + if (right_over > 0) { + tw = tw - right_over; + } + if (left_over > 0) { + tab_x = c->mon->m.x; + tw = tw - left_over; } if (tw <= 0 || th <= 0) { @@ -498,6 +503,14 @@ void apply_shield(Client *c, struct wlr_box clip_box) { return; } + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting)) { + clip_box.x = 0; + clip_box.y = 0; + clip_box.width = c->animation.current.width - 2 * (int32_t)c->bw; + clip_box.height = c->animation.current.height - 2 * (int32_t)c->bw; + } + if (active_capture_count > 0 && c->shield_when_capture) { wlr_scene_node_raise_to_top(&c->shield->node); wlr_scene_node_set_position(&c->shield->node, clip_box.x, clip_box.y); @@ -681,8 +694,8 @@ void apply_border(Client *c) { int32_t right_offset, bottom_offset, left_offset, top_offset; - if (c == grabc || - (!ISTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting)) { right_offset = 0; bottom_offset = 0; left_offset = 0; From e1c1bf77a7ffd9adadbb8a37e549cf0b0c886943 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 17:32:28 +0800 Subject: [PATCH 20/70] fix: miss check c->mon when switch tty --- src/action/client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/action/client.h b/src/action/client.h index aca5d2ef..4a1f72c2 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -175,7 +175,7 @@ void client_focus_group_member(Client *c) { void client_check_tab_node_visible(Client *c) { - if (!c || c->iskilling) + if (!c || c->iskilling || !c->mon) return; Client *head = c; From c385106370acc17c72bd698b78cd041366b48ad4 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 22 Jun 2026 20:58:10 +0800 Subject: [PATCH 21/70] fix: fix group cross monitor --- src/action/client.h | 31 ++++++++++++++------ src/dispatch/bind_define.h | 58 +++++++++++++++++++++++--------------- src/mango.c | 2 ++ 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 4a1f72c2..11ec5866 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -140,9 +140,6 @@ void client_focus_group_member(Client *c) { if (c->isgroupfocusing) return; - if (c->mon->isoverview) - return; - Client *head = c; while (head->group_prev) head = head->group_prev; @@ -156,12 +153,16 @@ void client_focus_group_member(Client *c) { head = head->group_next; } - if (cur_focusing) { - cur_focusing->isgroupfocusing = false; - c->mon = cur_focusing->mon; - client_replace(c, cur_focusing, true); - mango_group_bar_set_focus(cur_focusing->group_bar, false); - } + if (!cur_focusing || !cur_focusing->mon) + return; + + if (cur_focusing && cur_focusing->mon->isoverview) + return; + + cur_focusing->isgroupfocusing = false; + c->mon = cur_focusing->mon; + client_replace(c, cur_focusing, true); + mango_group_bar_set_focus(cur_focusing->group_bar, false); c->isgroupfocusing = true; mango_group_bar_set_focus(c->group_bar, true); @@ -255,4 +256,16 @@ void client_handle_decorate_click(MangoGroupBar *gb) { Client *c = gb->node_data; client_focus_group_member(c); } +} + +void client_set_group_mon(Client *c, Monitor *m) { + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + Client *cur = head; + while (cur) { + cur->mon = m; + cur = cur->group_next; + } } \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index b8b142ae..57c8228d 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -161,38 +161,50 @@ int32_t groupjoin(const Arg *arg) { if (!selmon) return 0; - Client *need_join_client = arg->tc ? arg->tc : selmon->sel; - if (!need_join_client) - return 0; + Monitor *oldmon = NULL; - if (need_join_client->group_next || need_join_client->group_prev) { + Client *need_join_client = arg->tc ? arg->tc : selmon->sel; + if (!need_join_client || !need_join_client->mon) return 0; - } Client *need_replace_client = NULL; need_replace_client = direction_select(arg); - if (!need_replace_client || - need_replace_client->mon != need_join_client->mon) + if (!need_replace_client || !need_replace_client->mon) return 0; - if (!need_join_client->group_next && !need_join_client->group_prev) { - - if (!need_replace_client->group_prev && - !need_replace_client->group_next) { - need_replace_client->isgroupfocusing = true; - } - - need_join_client->group_next = need_replace_client; - if (need_replace_client->group_prev) { - need_replace_client->group_prev->group_next = need_join_client; - } - need_join_client->group_prev = need_replace_client->group_prev; - need_replace_client->group_prev = need_join_client; - - client_focus_group_member(need_join_client); - arrange(need_join_client->mon, false, false); + if (need_join_client == need_replace_client) return 0; + + if (need_join_client->group_next || need_join_client->group_prev) { + groupleave(&(Arg){.tc = need_join_client}); + } + + if (need_join_client->mon != need_replace_client->mon) { + oldmon = need_join_client->mon; + need_join_client->mon = need_replace_client->mon; + } + + if (!need_replace_client->group_prev && !need_replace_client->group_next) { + need_replace_client->isgroupfocusing = true; + } + + need_join_client->group_next = need_replace_client; + + if (need_replace_client->group_prev) { + need_replace_client->group_prev->group_next = need_join_client; + } + + need_join_client->group_prev = need_replace_client->group_prev; + + need_replace_client->group_prev = need_join_client; + + client_focus_group_member(need_join_client); + arrange(need_join_client->mon, false, false); + + // oldmon可能已经死掉了 + if (oldmon) { + arrange(oldmon, false, false); } return 0; diff --git a/src/mango.c b/src/mango.c index b0dcbc80..0bee885c 100644 --- a/src/mango.c +++ b/src/mango.c @@ -2772,8 +2772,10 @@ void closemon(Monitor *m) { } c->mon = NULL; + client_set_group_mon(c, NULL); } else { client_change_mon(c, selmon); + client_set_group_mon(c, selmon); } // record the oldmonname which is used to restore if (c->oldmonname[0] == '\0') { From 0824961b8145614c1002953c98d38b9ea3375bb3 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 00:24:28 +0800 Subject: [PATCH 22/70] fix: disable group action in ov mode --- src/dispatch/bind_define.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 57c8228d..7af80ed9 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -167,6 +167,9 @@ int32_t groupjoin(const Arg *arg) { if (!need_join_client || !need_join_client->mon) return 0; + if (need_join_client->mon->isoverview) + return 0; + Client *need_replace_client = NULL; need_replace_client = direction_select(arg); @@ -215,11 +218,15 @@ int32_t groupleave(const Arg *arg) { if (!selmon) return 0; Client *tc = arg->tc ? arg->tc : selmon->sel; - if (!tc || !tc->isgroupfocusing) + if (!tc || !tc->mon || !tc->isgroupfocusing) return 0; if (!tc->group_next && !tc->group_prev) { return 0; } + + if (tc->mon->isoverview) + return 0; + Client *rc = tc->group_next ? tc->group_next : tc->group_prev; client_focus_group_member(rc); @@ -355,13 +362,16 @@ int32_t focusstack(const Arg *arg) { int32_t groupfocus(const Arg *arg) { Client *c = arg->tc ? arg->tc : selmon->sel; - if (!c) + if (!c || !c->mon) return 0; if (!c->group_prev && !c->group_next) { return 0; } + if (c->mon->isoverview) + return 0; + Client *tc = NULL; if (arg->i == NEXT) { From 38393873f34fef6b18810728f5b08272a5317e23 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 09:30:28 +0800 Subject: [PATCH 23/70] opt: optimize text draw for hidpi --- src/action/client.h | 5 ++++- src/layout/overview.h | 3 ++- src/mango.c | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 11ec5866..c86f67cf 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -130,7 +130,10 @@ void client_add_group_bar(Client *c) { config.tabdata, 0, 0); wlr_scene_node_lower_to_bottom(&c->group_bar->scene_buffer->node); wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); - mango_group_bar_update(c->group_bar, client_get_title(c), 1.0); + mango_group_bar_update(c->group_bar, client_get_title(c), + c->mon ? c->mon->wlr_output->scale + : selmon ? selmon->wlr_output->scale + : 1.0f); } void client_focus_group_member(Client *c) { diff --git a/src/layout/overview.h b/src/layout/overview.h index 80b2e31d..e1054531 100644 --- a/src/layout/overview.h +++ b/src/layout/overview.h @@ -364,7 +364,8 @@ void create_jump_hints(Monitor *m) { c->jump_char = c_char; char label_text[2] = {c_char, '\0'}; - mango_jump_label_node_update(c->jump_label_node, label_text, 1.0f); + mango_jump_label_node_update(c->jump_label_node, label_text, + m->wlr_output->scale); wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, true); wlr_scene_node_raise_to_top( diff --git a/src/mango.c b/src/mango.c index 0bee885c..e57bca61 100644 --- a/src/mango.c +++ b/src/mango.c @@ -7019,7 +7019,8 @@ void updatetitle(struct wl_listener *listener, void *data) { const char *title; title = client_get_title(c); - mango_group_bar_update(c->group_bar, title, 1.0); + mango_group_bar_update(c->group_bar, title, + c->mon ? c->mon->wlr_output->scale : 1.0f); if (title && c->foreign_toplevel) wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); if (title && c->ext_foreign_toplevel) { From d0645e5c308afea9280aaf0418decdc69fb4bd37 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 10:43:16 +0800 Subject: [PATCH 24/70] opt: Remove redundant code --- src/action/client.h | 16 +++++----------- src/dispatch/bind_define.h | 10 ---------- src/mango.c | 27 +-------------------------- 3 files changed, 6 insertions(+), 47 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index c86f67cf..85ab1aaf 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -179,7 +179,7 @@ void client_focus_group_member(Client *c) { void client_check_tab_node_visible(Client *c) { - if (!c || c->iskilling || !c->mon) + if (!c || !c->mon) return; Client *head = c; @@ -206,9 +206,6 @@ void client_raise_group(Client *c) { if (!c || !c->mon) return; - if (!c->group_prev && !c->group_next) - return; - Client *head = c; while (head->group_prev) head = head->group_prev; @@ -217,17 +214,14 @@ void client_raise_group(Client *c) { while (cur) { if (cur->group_bar) { wlr_scene_node_raise_to_top(&cur->group_bar->scene_buffer->node); - wlr_scene_node_raise_to_top(&cur->scene->node); } + wlr_scene_node_raise_to_top(&cur->scene->node); cur = cur->group_next; } } void client_reparent_group(Client *c) { - if (!c || !c->group_bar) - return; - - if (!c->group_prev && !c->group_next) + if (!c || !c->mon) return; int32_t layer = c->isoverlay ? LyrOverlay @@ -244,8 +238,8 @@ void client_reparent_group(Client *c) { if (cur->group_bar) { wlr_scene_node_reparent(&cur->group_bar->scene_buffer->node, layers[layer]); - wlr_scene_node_reparent(&cur->scene->node, layers[layer]); } + wlr_scene_node_reparent(&cur->scene->node, layers[layer]); cur = cur->group_next; } } @@ -268,7 +262,7 @@ void client_set_group_mon(Client *c, Monitor *m) { Client *cur = head; while (cur) { - cur->mon = m; + client_change_mon(cur, m); cur = cur->group_next; } } \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 7af80ed9..c70d6213 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1554,16 +1554,6 @@ int32_t toggleoverlay(const Arg *arg) { c->isoverlay ^= 1; - if (c->isoverlay) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); - wlr_scene_node_raise_to_top(&c->scene->node); - } else if (client_should_overtop(c) && c->isfloating) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]); - } else { - wlr_scene_node_reparent(&c->scene->node, - layers[c->isfloating ? LyrTop : LyrTile]); - } - client_reparent_group(c); setborder_color(c); return 0; diff --git a/src/mango.c b/src/mango.c index e57bca61..33755895 100644 --- a/src/mango.c +++ b/src/mango.c @@ -947,6 +947,7 @@ static void global_draw_group_bar(Client *c, int32_t x, int32_t y, int32_t width, int32_t height); static void client_reparent_group(Client *c); +static void client_change_mon(Client *c, Monitor *m); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -2771,10 +2772,8 @@ void closemon(Monitor *m) { c->foreign_toplevel = NULL; } - c->mon = NULL; client_set_group_mon(c, NULL); } else { - client_change_mon(c, selmon); client_set_group_mon(c, selmon); } // record the oldmonname which is used to restore @@ -3955,7 +3954,6 @@ void focusclient(Client *c, int32_t lift) { /* Raise client in stacking order if requested */ if (c && lift) { client_raise_group(c); - wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 } if (c && client_surface(c) == old_keyboard_focus_surface && selmon && @@ -5677,15 +5675,6 @@ setfloating(Client *c, int32_t floating) { } } - if (c->isoverlay) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); - } else if (client_should_overtop(c) && c->isfloating) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]); - } else { - wlr_scene_node_reparent(&c->scene->node, - layers[c->isfloating ? LyrTop : LyrTile]); - } - client_reparent_group(c); if (c->isfloating) { @@ -5781,10 +5770,6 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { setfloating(c, 1); } - wlr_scene_node_reparent(&c->scene->node, - layers[c->ismaximizescreen ? LyrMaximize - : c->isfloating ? LyrTop - : LyrTile]); client_reparent_group(c); if (!c->force_fakemaximize && !c->ismaximizescreen) { @@ -5845,16 +5830,6 @@ void setfullscreen(Client *c, int32_t fullscreen, setfloating(c, 1); } - if (c->isoverlay) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); - } else if (client_should_overtop(c) && c->isfloating) { - wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]); - } else { - wlr_scene_node_reparent( - &c->scene->node, - layers[fullscreen || c->isfloating ? LyrTop : LyrTile]); - } - client_reparent_group(c); if (rearrange) From 68ac770281f2b1b7fba5af831e46e3a2c3ac54ea Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 14:01:46 +0800 Subject: [PATCH 25/70] fix: miss reparent client node backto scene_surface node after replace client --- src/mango.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mango.c b/src/mango.c index 33755895..52ecdfbc 100644 --- a/src/mango.c +++ b/src/mango.c @@ -1338,6 +1338,12 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { } if (w->overview_scene_surface) { + + wlr_scene_node_reparent(&w->blur->node, w->overview_scene_surface); + wlr_scene_node_lower_to_bottom(&w->blur->node); + wlr_scene_node_reparent(&w->shield->node, w->overview_scene_surface); + wlr_scene_node_raise_to_top(&w->shield->node); + wlr_scene_node_destroy(&w->scene_surface->node); w->scene_surface = w->overview_scene_surface; w->overview_scene_surface = NULL; From b472fa11d19a612157c8a75cdba8b7fb349af54a Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 14:45:06 +0800 Subject: [PATCH 26/70] feat: add windowrule option vrr_only_fullscreen --- docs/window-management/rules.md | 1 + src/config/parse_config.h | 4 ++++ src/mango.c | 42 +++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md index 5c97961c..27ff8ab0 100644 --- a/docs/window-management/rules.md +++ b/docs/window-management/rules.md @@ -35,6 +35,7 @@ windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values | `single_scratchpad` | integer | `0` / `1` (default 1) | Only show one out of named scratchpads or the normal scratchpad | | `allow_shortcuts_inhibit` | integer | `0` / `1` (default 1) | Allow shortcuts to be inhibited by clients | | `idleinhibit_when_focus` | integer | `0` / `1` (default 0) | Automatically keep idle inhibit active when this window is focused | +| `vrr_only_fullscreen` | integer | `0` / `1` (default 0) | VRR only fullscreen,you need to turn `vrr` to `0` in monitor rule first | | `shield_when_capture` | integer | `0` / `1` | Shield window when captured | diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 875ce081..0103e8d3 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -80,6 +80,7 @@ typedef struct { int32_t ignore_minimize; int32_t isnosizehint; int32_t idleinhibit_when_focus; + int32_t vrr_only_fullscreen; char *monitor; int32_t offsetx; int32_t offsety; @@ -2364,6 +2365,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->ignore_minimize = -1; rule->isnosizehint = -1; rule->idleinhibit_when_focus = -1; + rule->vrr_only_fullscreen = -1; rule->isterm = -1; rule->allow_csd = -1; rule->force_fakemaximize = -1; @@ -2478,6 +2480,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->isnosizehint = atoi(val); } else if (strcmp(key, "idleinhibit_when_focus") == 0) { rule->idleinhibit_when_focus = atoi(val); + } else if (strcmp(key, "vrr_only_fullscreen") == 0) { + rule->vrr_only_fullscreen = atoi(val); } else if (strcmp(key, "isterm") == 0) { rule->isterm = atoi(val); } else if (strcmp(key, "allow_csd") == 0) { diff --git a/src/mango.c b/src/mango.c index 52ecdfbc..b90c06b2 100644 --- a/src/mango.c +++ b/src/mango.c @@ -379,7 +379,8 @@ struct Client { struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; int32_t isfloating, isurgent, isfullscreen, isfakefullscreen, need_float_size_reduce, isminimized, isoverlay, isnosizehint, - ignore_maximize, ignore_minimize, idleinhibit_when_focus; + ignore_maximize, ignore_minimize, idleinhibit_when_focus, + vrr_only_fullscreen; int32_t ismaximizescreen; int32_t overview_backup_bw; int32_t fullscreen_backup_x, fullscreen_backup_y, fullscreen_backup_w, @@ -595,6 +596,8 @@ struct Monitor { struct wlr_ext_workspace_group_handle_v1 *ext_group; bool iscleanuping; int8_t carousel_anim_dir; + bool vrr_global_enable; + bool is_vrr_opening; }; typedef struct { @@ -1653,6 +1656,7 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, ignore_minimize); APPLY_INT_PROP(c, r, isnosizehint); APPLY_INT_PROP(c, r, idleinhibit_when_focus); + APPLY_INT_PROP(c, r, vrr_only_fullscreen); APPLY_INT_PROP(c, r, isunglobal); APPLY_INT_PROP(c, r, noblur); APPLY_INT_PROP(c, r, allow_shortcuts_inhibit); @@ -3353,17 +3357,24 @@ struct wlr_output_mode *get_nearest_output_mode(struct wlr_output *output, } void enable_adaptive_sync(Monitor *m, struct wlr_output_state *state) { + wlr_output_state_set_adaptive_sync_enabled(state, true); if (!wlr_output_test_state(m->wlr_output, state)) { wlr_output_state_set_adaptive_sync_enabled(state, false); wlr_log(WLR_DEBUG, "failed to enable adaptive sync for output %s", m->wlr_output->name); } else { + m->is_vrr_opening = true; wlr_log(WLR_INFO, "adaptive sync enabled for output %s", m->wlr_output->name); } } +void disable_adaptive_sync(Monitor *m, struct wlr_output_state *state) { + wlr_output_state_set_adaptive_sync_enabled(state, false); + m->is_vrr_opening = false; +} + bool monitor_matches_rule(Monitor *m, const ConfigMonitorRule *rule) { if (rule->name != NULL && !regex_match(rule->name, m->wlr_output->name)) return false; @@ -3384,6 +3395,8 @@ bool monitor_matches_rule(Monitor *m, const ConfigMonitorRule *rule) { bool apply_rule_to_state(Monitor *m, const ConfigMonitorRule *rule, struct wlr_output_state *state, int vrr, int custom) { bool mode_set = false; + m->vrr_global_enable = vrr; + if (rule->width > 0 && rule->height > 0 && rule->refresh > 0) { struct wlr_output_mode *internal_mode = get_nearest_output_mode( m->wlr_output, rule->width, rule->height, rule->refresh); @@ -3400,7 +3413,7 @@ bool apply_rule_to_state(Monitor *m, const ConfigMonitorRule *rule, if (vrr) { enable_adaptive_sync(m, state); } else { - wlr_output_state_set_adaptive_sync_enabled(state, false); + disable_adaptive_sync(m, state); } wlr_output_state_set_scale(state, rule->scale); wlr_output_state_set_transform(state, rule->rr); @@ -3439,6 +3452,8 @@ void createmon(struct wl_listener *listener, void *data) { m->resizing_count_pending = 0; m->resizing_count_current = 0; m->carousel_anim_dir = 0; + m->vrr_global_enable = false; + m->is_vrr_opening = false; m->wlr_output = wlr_output; m->wlr_output->data = m; @@ -4603,6 +4618,7 @@ void init_client_properties(Client *c) { c->force_tearing = 0; c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; c->idleinhibit_when_focus = 0; + c->vrr_only_fullscreen = 0; c->scroller_proportion_single = 0.0f; c->float_geom.width = 0; c->float_geom.height = 0; @@ -5140,8 +5156,12 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test) { wlr_output_state_set_transform(&state, config_head->state.transform); wlr_output_state_set_scale(&state, config_head->state.scale); - wlr_output_state_set_adaptive_sync_enabled( - &state, config_head->state.adaptive_sync_enabled); + + if (config_head->state.adaptive_sync_enabled) { + enable_adaptive_sync(m, &state); + } else { + disable_adaptive_sync(m, &state); + } apply_or_test: ok &= test ? wlr_output_test_state(wlr_output, &state) @@ -6637,6 +6657,20 @@ void check_keep_idle_inhibit(Client *c) { if (c && c->idleinhibit_when_focus && keep_idle_inhibit_source) { wl_event_source_timer_update(keep_idle_inhibit_source, 1000); } + + if (c && c->mon && c->vrr_only_fullscreen && c->isfullscreen && + !c->mon->is_vrr_opening) { + struct wlr_output_state state = {0}; + enable_adaptive_sync(c->mon, &state); + } else if (c && c->mon && (!c->vrr_only_fullscreen || !c->isfullscreen)) { + if (!c->mon->is_vrr_opening && c->mon->vrr_global_enable) { + struct wlr_output_state state = {0}; + enable_adaptive_sync(c->mon, &state); + } else if (c->mon->is_vrr_opening && !c->mon->vrr_global_enable) { + struct wlr_output_state state = {0}; + disable_adaptive_sync(c->mon, &state); + } + } } int32_t keep_idle_inhibit(void *data) { From 124756f14a57c69d7518883b1dec636f1d557f31 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 14:45:36 +0800 Subject: [PATCH 27/70] update docs --- docs/bindings/keys.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index 6c8a8c58..53a4f81d 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -114,6 +114,13 @@ bindr=Super,Super_L,spawn,rofi -show run | `exchange_stack_client` | `next/prev` | Exchange window position in stack. | | `zoom` | - | Swap focused window with Master. | +### Group +| Command | Param | Description | +| :--- | :--- | :--- | +| `groupjoin` | `left/right/up/down` | Join group by direction. | +| `groupfocus` | `prev/next` | Focus group member by direction. | +| `groupleave` | - | Leave group. | + ### Tags & Monitors | Command | Param | Description | From cc35b136d1e221849a724b6c39765b728799119d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 23 Jun 2026 15:20:33 +0800 Subject: [PATCH 28/70] fix: fix center_tile gap --- src/layout/horizontal.h | 73 +++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 8788c210..aa60100b 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -290,18 +290,37 @@ void center_tile(Monitor *m) { int32_t should_overspread = config.center_master_overspread && (n <= nmasters); + int32_t left_num = 0; + int32_t right_num = 0; + for (int j = 0; j < stack_num; j++) { + if ((j % 2) ^ (n % 2 == 0)) { + right_num++; + } else { + left_num++; + } + } + int32_t master_surplus_height = - (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); + (m->w.height - 2 * cur_gappov - + cur_gappiv * ie * (master_num > 0 ? master_num - 1 : 0)); float master_surplus_ratio = 1.0; + int32_t init_master_surplus = master_surplus_height; int32_t slave_left_surplus_height = - (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num / 2 - 1)); + (m->w.height - 2 * cur_gappov - + cur_gappiv * ie * (left_num > 0 ? left_num - 1 : 0)); float slave_left_surplus_ratio = 1.0; + int32_t init_slave_left_surplus = slave_left_surplus_height; int32_t slave_right_surplus_height = (m->w.height - 2 * cur_gappov - - cur_gappiv * ie * ((stack_num + 1) / 2 - 1)); + cur_gappiv * ie * (right_num > 0 ? right_num - 1 : 0)); float slave_right_surplus_ratio = 1.0; + int32_t init_slave_right_surplus = slave_right_surplus_height; + + int32_t init_single_stack_surplus = + (m->w.height - 2 * cur_gappov - + cur_gappiv * ie * (stack_num > 0 ? stack_num - 1 : 0)); if (n > nmasters || !should_overspread) { // 计算主区域宽度(居中模式) @@ -349,6 +368,8 @@ void center_tile(Monitor *m) { if (c->master_inner_per > 0.0f) { h = master_surplus_height * c->master_inner_per / master_surplus_ratio; + if (r == 1) + h = m->w.height - my - cur_gappov; master_surplus_height = master_surplus_height - h; master_surplus_ratio = master_surplus_ratio - c->master_inner_per; @@ -357,8 +378,12 @@ void center_tile(Monitor *m) { h = (m->w.height - my - cur_gappov - cur_gappiv * ie * (r - 1)) / r; - c->master_inner_per = h / (m->w.height - my - cur_gappov - - cur_gappiv * ie * (r - 1)); + if (r == 1) + h = m->w.height - my - cur_gappov; + c->master_inner_per = + init_master_surplus > 0 + ? ((float)h / (float)init_master_surplus) + : 0; c->master_mfact_per = mfact; } @@ -377,16 +402,20 @@ void center_tile(Monitor *m) { // 单个堆叠窗口 r = n - i; if (c->stack_inner_per > 0.0f) { - h = (m->w.height - 2 * cur_gappov - - cur_gappiv * ie * (stack_num - 1)) * - c->stack_inner_per; + h = init_single_stack_surplus * c->stack_inner_per; + if (r == 1) + h = m->w.height - ety - cur_gappov; c->master_mfact_per = mfact; } else { h = (m->w.height - ety - cur_gappov - cur_gappiv * ie * (r - 1)) / r; - c->stack_inner_per = h / (m->w.height - ety - cur_gappov - - cur_gappiv * ie * (r - 1)); + if (r == 1) + h = m->w.height - ety - cur_gappov; + c->stack_inner_per = + init_single_stack_surplus > 0 + ? ((float)h / (float)init_single_stack_surplus) + : 0; c->master_mfact_per = mfact; } @@ -403,7 +432,7 @@ void center_tile(Monitor *m) { .width = tw, .height = h}, 0); - ety += h + cur_gappiv * ie; // 使用理论高度累加 + ety += h + cur_gappiv * ie; } else { // 多个堆叠窗口:交替放在左右两侧 r = (n - i + 1) / 2; @@ -413,6 +442,8 @@ void center_tile(Monitor *m) { if (c->stack_inner_per > 0.0f) { h = slave_right_surplus_height * c->stack_inner_per / slave_right_surplus_ratio; + if (r == 1) + h = m->w.height - ety - cur_gappov; slave_right_surplus_height = slave_right_surplus_height - h; slave_right_surplus_ratio = @@ -422,9 +453,12 @@ void center_tile(Monitor *m) { h = (m->w.height - ety - cur_gappov - cur_gappiv * ie * (r - 1)) / r; + if (r == 1) + h = m->w.height - ety - cur_gappov; c->stack_inner_per = - h / (m->w.height - ety - cur_gappov - - cur_gappiv * ie * (r - 1)); + init_slave_right_surplus > 0 + ? ((float)h / (float)init_slave_right_surplus) + : 0; c->master_mfact_per = mfact; } @@ -436,12 +470,14 @@ void center_tile(Monitor *m) { .width = tw, .height = h}, 0); - ety += h + cur_gappiv * ie; // 使用理论高度累加 + ety += h + cur_gappiv * ie; } else { // 左侧堆叠窗口 if (c->stack_inner_per > 0.0f) { h = slave_left_surplus_height * c->stack_inner_per / slave_left_surplus_ratio; + if (r == 1) + h = m->w.height - oty - cur_gappov; slave_left_surplus_height = slave_left_surplus_height - h; slave_left_surplus_ratio = @@ -451,9 +487,12 @@ void center_tile(Monitor *m) { h = (m->w.height - oty - cur_gappov - cur_gappiv * ie * (r - 1)) / r; + if (r == 1) + h = m->w.height - oty - cur_gappov; c->stack_inner_per = - h / (m->w.height - oty - cur_gappov - - cur_gappiv * ie * (r - 1)); + init_slave_left_surplus > 0 + ? ((float)h / (float)init_slave_left_surplus) + : 0; c->master_mfact_per = mfact; } @@ -464,7 +503,7 @@ void center_tile(Monitor *m) { .width = tw, .height = h}, 0); - oty += h + cur_gappiv * ie; // 使用理论高度累加 + oty += h + cur_gappiv * ie; } } } From 94d89bdefcf5666b21b1f5f262ad9bf3a9e94254 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 24 Jun 2026 07:47:46 +0800 Subject: [PATCH 29/70] fix: fix fullscreen tearing only --- src/mango.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/mango.c b/src/mango.c index b90c06b2..bfe21445 100644 --- a/src/mango.c +++ b/src/mango.c @@ -951,6 +951,7 @@ static void global_draw_group_bar(Client *c, int32_t x, int32_t y, static void client_reparent_group(Client *c); static void client_change_mon(Client *c, Monitor *m); +static void check_vrr_enable(Client *c); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -3996,6 +3997,7 @@ void focusclient(Client *c, int32_t lift) { c->isfocusing = true; check_keep_idle_inhibit(c); + check_vrr_enable(c); if (last_focus_client && !last_focus_client->iskilling && last_focus_client != c) { @@ -5857,6 +5859,7 @@ void setfullscreen(Client *c, int32_t fullscreen, } client_reparent_group(c); + check_vrr_enable(c); if (rearrange) arrange(c->mon, false, false); @@ -6657,19 +6660,27 @@ void check_keep_idle_inhibit(Client *c) { if (c && c->idleinhibit_when_focus && keep_idle_inhibit_source) { wl_event_source_timer_update(keep_idle_inhibit_source, 1000); } +} - if (c && c->mon && c->vrr_only_fullscreen && c->isfullscreen && - !c->mon->is_vrr_opening) { - struct wlr_output_state state = {0}; +void check_vrr_enable(Client *c) { + + if (!c || !c->mon) + return; + + struct wlr_output_state state = {0}; + + if (c->vrr_only_fullscreen && c->isfullscreen && !c->mon->is_vrr_opening) { enable_adaptive_sync(c->mon, &state); - } else if (c && c->mon && (!c->vrr_only_fullscreen || !c->isfullscreen)) { - if (!c->mon->is_vrr_opening && c->mon->vrr_global_enable) { - struct wlr_output_state state = {0}; - enable_adaptive_sync(c->mon, &state); - } else if (c->mon->is_vrr_opening && !c->mon->vrr_global_enable) { - struct wlr_output_state state = {0}; - disable_adaptive_sync(c->mon, &state); - } + wlr_output_commit_state(c->mon->wlr_output, &state); + return; + } + + if (!c->mon->is_vrr_opening && c->mon->vrr_global_enable) { + enable_adaptive_sync(c->mon, &state); + wlr_output_commit_state(c->mon->wlr_output, &state); + } else if (c->mon->is_vrr_opening && !c->mon->vrr_global_enable) { + disable_adaptive_sync(c->mon, &state); + wlr_output_commit_state(c->mon->wlr_output, &state); } } From fc2c5ae49fc24eba0ec5046aa4aba4628807b51d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 24 Jun 2026 07:52:20 +0800 Subject: [PATCH 30/70] feat: hdr support --- docs/configuration/miscellaneous.md | 1 + docs/configuration/monitors.md | 1 + src/action/client.h | 5 ++ src/config/parse_config.h | 39 +++++++-- src/ext-protocol/all.h | 1 + src/ext-protocol/hdr.h | 128 ++++++++++++++++++++++++++++ src/ext-protocol/tearing.h | 65 ++++---------- src/mango.c | 34 +++++--- 8 files changed, 211 insertions(+), 63 deletions(-) create mode 100644 src/ext-protocol/hdr.h diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index 6fb5dc8f..d8871885 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -12,6 +12,7 @@ description: Advanced settings for XWayland, focus behavior, and system integrat | `allow_lock_transparent` | `0` | Allow the lock screen to be transparent. | | `allow_shortcuts_inhibit` | `1` | Allow shortcuts to be inhibited by clients. | | `vrr` | - | Set via [monitor rule](/docs/configuration/monitors#monitor-rules). | +| `hdr_hdr_depth` | `0`/`1`/`2` | Set the hdr depth for the current display. `0` is Default, `1` is HDR8, `2` is HDR10. | ## Focus & Input diff --git a/docs/configuration/monitors.md b/docs/configuration/monitors.md index a8001ae0..ff64bc10 100644 --- a/docs/configuration/monitors.md +++ b/docs/configuration/monitors.md @@ -30,6 +30,7 @@ monitorrule=name:Values,Parameter:Values,Parameter:Values | `y` | integer | 0-99999 | Y position | | `scale` | float | 0.01-100.0 | Monitor scale | | `vrr` | integer | 0, 1 | Enable variable refresh rate | +| `hdr` | integer | 0, 1 | Enable hdr support | | `rr` | integer | 0-7 | Monitor transform | | `custom` | integer | 0, 1 | Enable custom mode (not supported on all displays — may cause black screen) | diff --git a/src/action/client.h b/src/action/client.h index 85ab1aaf..22072a09 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -265,4 +265,9 @@ void client_set_group_mon(Client *c, Monitor *m) { client_change_mon(cur, m); cur = cur->group_next; } +} + +void handle_client_focus_change(Client *c) { + check_keep_idle_inhibit(c); + check_vrr_enable(c); } \ No newline at end of file diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 0103e8d3..306f23bf 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -22,6 +22,11 @@ enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT }; enum { KEY_TYPE_CODE, KEY_TYPE_SYM }; +enum render_bit_depth { + MANGO_RENDER_BIT_DEPTH_DEFAULT = 0, + MANGO_RENDER_BIT_DEPTH_8, + MANGO_RENDER_BIT_DEPTH_10, +}; typedef struct { uint32_t keycode1; uint32_t keycode2; @@ -115,6 +120,7 @@ typedef struct { float refresh; // Refresh rate int32_t vrr; // variable refresh rate int32_t custom; // enable custom mode + int32_t hdr; // enable hdr mode } ConfigMonitorRule; // 修改后的宏定义 @@ -413,6 +419,8 @@ typedef struct { struct xkb_keymap *keymap; DecorateDrawData jumplabeldata; DecorateDrawData tabdata; + + int32_t hdr_depth; } Config; typedef int32_t (*FuncType)(const Arg *); @@ -1520,6 +1528,8 @@ bool parse_option(Config *config, char *key, char *value) { config->drag_floating_refresh_interval = atof(value); } else if (strcmp(key, "allow_tearing") == 0) { config->allow_tearing = atoi(value); + } else if (strcmp(key, "hdr_depth") == 0) { + config->hdr_depth = atoi(value); } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { config->allow_shortcuts_inhibit = atoi(value); } else if (strcmp(key, "allow_lock_transparent") == 0) { @@ -2116,6 +2126,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->height = -1; rule->refresh = 0.0f; rule->vrr = 0; + rule->hdr = 0; rule->custom = 0; bool parse_error = false; @@ -2154,6 +2165,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->refresh = CLAMP_FLOAT(atof(val), 0.001f, 1000.0f); } else if (strcmp(key, "vrr") == 0) { rule->vrr = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "hdr") == 0) { + rule->hdr = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "custom") == 0) { rule->custom = CLAMP_INT(atoi(val), 0, 1); } else { @@ -3512,6 +3525,7 @@ void override_config(void) { config.drag_tile_to_tile = CLAMP_INT(config.drag_tile_to_tile, 0, 1); config.drag_tile_small = CLAMP_INT(config.drag_tile_small, 0, 1); config.allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2); + config.hdr_depth = CLAMP_INT(config.hdr_depth, 0, 2); config.allow_shortcuts_inhibit = CLAMP_INT(config.allow_shortcuts_inhibit, 0, 1); config.allow_lock_transparent = @@ -3704,6 +3718,7 @@ void set_value_default() { config.drag_tile_refresh_interval = 8.0f; config.drag_floating_refresh_interval = 8.0f; config.allow_tearing = TEARING_DISABLED; + config.hdr_depth = MANGO_RENDER_BIT_DEPTH_DEFAULT; config.allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; config.allow_lock_transparent = 0; config.no_border_when_single = 0; @@ -4034,13 +4049,12 @@ void reapply_monitor_rules(void) { Monitor *m = NULL; int32_t ji, vrr, custom; int32_t mx, my; - struct wlr_output_state state; wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) continue; - wlr_output_state_init(&state); + wlr_output_state_init(&m->pending); for (ji = 0; ji < config.monitor_rules_count; ji++) { if (config.monitor_rules_count < 1) @@ -4053,16 +4067,31 @@ void reapply_monitor_rules(void) { my = mr->y == INT32_MAX ? m->m.y : mr->y; vrr = mr->vrr >= 0 ? mr->vrr : 0; custom = mr->custom >= 0 ? mr->custom : 0; + m->hdr_enable = mr->hdr >= 0 ? mr->hdr : 0; - (void)apply_rule_to_state(m, mr, &state, vrr, custom); + (void)apply_rule_to_state(m, mr, &m->pending, vrr, custom); wlr_output_layout_add(output_layout, m->wlr_output, mx, my); - wlr_output_commit_state(m->wlr_output, &state); break; } } - wlr_output_state_finish(&state); + wlr_output_state_set_enabled(&m->pending, true); + + if (m->hdr_enable) { + output_state_setup_hdr(m, false); + bool success = wlr_output_commit_state(m->wlr_output, &m->pending); + if (!success) { // 多尝试一次 + output_state_setup_hdr(m, true); + } + } else { + wlr_output_commit_state(m->wlr_output, &m->pending); + } + + wlr_output_state_finish(&m->pending); + wlr_output_effective_resolution(m->wlr_output, &m->m.width, + &m->m.height); } + updatemons(NULL, NULL); } diff --git a/src/ext-protocol/all.h b/src/ext-protocol/all.h index fac2779d..e9923c35 100644 --- a/src/ext-protocol/all.h +++ b/src/ext-protocol/all.h @@ -1,6 +1,7 @@ #include "dwl-ipc.h" #include "ext-workspace.h" #include "foreign-toplevel.h" +#include "hdr.h" #include "tablet.h" #include "tearing.h" #include "text-input.h" diff --git a/src/ext-protocol/hdr.h b/src/ext-protocol/hdr.h new file mode 100644 index 00000000..2f2ff9a3 --- /dev/null +++ b/src/ext-protocol/hdr.h @@ -0,0 +1,128 @@ +#include + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static uint32_t output_formats_8bit[] = { + DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, +}; + +static uint32_t output_formats_10bit[] = { + DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_RGBX1010102, + DRM_FORMAT_BGRX1010102, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, DRM_FORMAT_BGRA1010102, +}; + +static bool output_set_render_format(Monitor *m, uint32_t candidates[], + size_t count) { + for (size_t i = 0; i < count; i++) { + wlr_output_state_set_render_format(&m->pending, candidates[i]); + if (wlr_output_test_state(m->wlr_output, &m->pending)) + return true; + } + return false; +} + +static bool output_format_in_candidates(uint32_t format, uint32_t candidates[], + size_t count) { + for (size_t i = 0; i < count; i++) + if (candidates[i] == format) + return true; + return false; +} + +static enum render_bit_depth bit_depth_from_format(uint32_t render_format) { + if (output_format_in_candidates(render_format, output_formats_10bit, + ARRAY_SIZE(output_formats_10bit))) + return MANGO_RENDER_BIT_DEPTH_10; + if (output_format_in_candidates(render_format, output_formats_8bit, + ARRAY_SIZE(output_formats_8bit))) + return MANGO_RENDER_BIT_DEPTH_8; + return MANGO_RENDER_BIT_DEPTH_DEFAULT; +} + +static bool output_supports_hdr(const struct wlr_output *output, + const char **reason) { + const char *r = NULL; + if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) + r = "BT2020 primaries not supported"; + else if (!(output->supported_transfer_functions & + WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) + r = "PQ transfer function not supported"; + else if (!drw->features.output_color_transform) + r = "renderer doesn't support output color transforms"; + if (reason) + *reason = r; + return !r; +} + +void output_enable_hdr(Monitor *m, struct wlr_output_state *os, bool enabled, + bool silent) { + if (enabled && !output_supports_hdr(m->wlr_output, NULL)) + enabled = false; + + if (!enabled) { + if (m->wlr_output->supported_primaries || + m->wlr_output->supported_transfer_functions) { + if (!silent) + wlr_log(WLR_DEBUG, "Disabling HDR on output %s", + m->wlr_output->name); + wlr_output_state_set_image_description(os, NULL); + } + return; + } + + if (!silent) + wlr_log(WLR_DEBUG, "Enabling HDR on output %s", m->wlr_output->name); + struct wlr_output_image_description desc = { + .primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020, + .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ, + }; + wlr_output_state_set_image_description(os, &desc); +} + +void output_state_setup_hdr(Monitor *m, bool silent) { + uint32_t render_format = m->wlr_output->render_format; + const char *unsupported_reason = NULL; + bool hdr_supported = + output_supports_hdr(m->wlr_output, &unsupported_reason); + bool hdr_succeeded = false; + + enum render_bit_depth depth = config.hdr_depth; + if (depth == MANGO_RENDER_BIT_DEPTH_DEFAULT) + depth = bit_depth_from_format(render_format); + + if (!hdr_supported && depth == MANGO_RENDER_BIT_DEPTH_10) { + if (!silent) + wlr_log(WLR_INFO, "Cannot enable HDR on output %s: %s", + m->wlr_output->name, unsupported_reason); + depth = MANGO_RENDER_BIT_DEPTH_8; + } + + if (depth == MANGO_RENDER_BIT_DEPTH_10 && + bit_depth_from_format(render_format) == depth) { + hdr_succeeded = true; // 上次已经成功设置10位,直接复用 + } else if (depth == MANGO_RENDER_BIT_DEPTH_10) { + hdr_succeeded = output_set_render_format( + m, output_formats_10bit, ARRAY_SIZE(output_formats_10bit)); + if (!hdr_succeeded) { + if (!silent) + wlr_log(WLR_INFO, + "No 10 bit color formats supported, HDR disabled."); + if (!output_set_render_format(m, output_formats_8bit, + ARRAY_SIZE(output_formats_8bit))) + if (!silent) + wlr_log(WLR_ERROR, "No 8 bit color formats either!"); + } + } else { + // 明确要求8位或自动降级 + if (!output_set_render_format(m, output_formats_8bit, + ARRAY_SIZE(output_formats_8bit))) + if (!silent) + wlr_log(WLR_ERROR, "No 8 bit color formats supported!"); + } + + output_enable_hdr(m, &m->pending, hdr_succeeded, silent); +} \ No newline at end of file diff --git a/src/ext-protocol/tearing.h b/src/ext-protocol/tearing.h index 5a9851e4..02394eaa 100644 --- a/src/ext-protocol/tearing.h +++ b/src/ext-protocol/tearing.h @@ -100,73 +100,44 @@ bool custom_wlr_scene_output_commit(struct wlr_scene_output *scene_output, struct wlr_output *wlr_output = scene_output->output; Monitor *m = wlr_output->data; - // 检查是否需要帧 - if (!wlr_scene_output_needs_frame(scene_output)) { - wlr_log(WLR_DEBUG, "No frame needed for output %s", wlr_output->name); + if (!wlr_scene_output_needs_frame(scene_output)) return true; - } - // 构建输出状态 - if (!wlr_scene_output_build_state(scene_output, state, NULL)) { - wlr_log(WLR_ERROR, "Failed to build output state for %s", - wlr_output->name); + // 构建状态,将场景的 Buffer 附着到 state 上 + if (!wlr_scene_output_build_state(scene_output, state, NULL)) return false; + + // 测试是否支持撕裂 + if (!wlr_output_test_state(wlr_output, state)) { + // 如果 DRM 拒绝(例如当前输出/驱动不支持撕裂),降级关闭撕裂 + state->tearing_page_flip = false; } - // 测试撕裂翻页 - if (state->tearing_page_flip) { - if (!wlr_output_test_state(wlr_output, state)) { - state->tearing_page_flip = false; - } - } - - // 尝试提交 + // 提交状态 bool committed = wlr_output_commit_state(wlr_output, state); - - // 如果启用撕裂翻页但提交失败,重试禁用撕裂翻页 if (!committed && state->tearing_page_flip) { - wlr_log(WLR_DEBUG, "Retrying commit without tearing for %s", - wlr_output->name); + // 重试一次 state->tearing_page_flip = false; committed = wlr_output_commit_state(wlr_output, state); } - // 处理状态清理 if (committed) { - wlr_log(WLR_DEBUG, "Successfully committed output %s", - wlr_output->name); if (state == &m->pending) { wlr_output_state_finish(&m->pending); wlr_output_state_init(&m->pending); } - } else { - wlr_log(WLR_ERROR, "Failed to commit output %s", wlr_output->name); - // 即使提交失败,也清理状态避免积累 - if (state == &m->pending) { - wlr_output_state_finish(&m->pending); - wlr_output_state_init(&m->pending); - } - return false; } - - return true; + return committed; } void apply_tear_state(Monitor *m) { - if (wlr_scene_output_needs_frame(m->scene_output)) { - wlr_output_state_init(&m->pending); - if (wlr_scene_output_build_state(m->scene_output, &m->pending, NULL)) { - struct wlr_output_state *pending = &m->pending; - pending->tearing_page_flip = true; + if (!wlr_scene_output_needs_frame(m->scene_output)) + return; - if (!custom_wlr_scene_output_commit(m->scene_output, pending)) { - wlr_log(WLR_ERROR, "Failed to commit output %s", - m->scene_output->output->name); - } - } else { - wlr_log(WLR_ERROR, "Failed to build state for output %s", - m->scene_output->output->name); - wlr_output_state_finish(&m->pending); - } + wlr_output_state_init(&m->pending); + m->pending.tearing_page_flip = true; + if (!custom_wlr_scene_output_commit(m->scene_output, &m->pending)) { + wlr_log(WLR_ERROR, "Failed to commit output %s", + m->scene_output->output->name); } } \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index bfe21445..e45d179e 100644 --- a/src/mango.c +++ b/src/mango.c @@ -598,6 +598,7 @@ struct Monitor { int8_t carousel_anim_dir; bool vrr_global_enable; bool is_vrr_opening; + bool hdr_enable; }; typedef struct { @@ -952,6 +953,9 @@ static void global_draw_group_bar(Client *c, int32_t x, int32_t y, static void client_reparent_group(Client *c); static void client_change_mon(Client *c, Monitor *m); static void check_vrr_enable(Client *c); +static void output_state_setup_hdr(Monitor *m, bool silent); +static void output_enable_hdr(Monitor *m, struct wlr_output_state *os, + bool enabled, bool silent); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -3428,7 +3432,6 @@ void createmon(struct wl_listener *listener, void *data) { const ConfigMonitorRule *r; uint32_t i; int32_t ji, vrr, custom; - struct wlr_output_state state; Monitor *m = NULL; bool custom_monitor_mode = false; @@ -3456,6 +3459,8 @@ void createmon(struct wl_listener *listener, void *data) { m->vrr_global_enable = false; m->is_vrr_opening = false; + m->hdr_enable = false; + m->wlr_output = wlr_output; m->wlr_output->data = m; @@ -3476,9 +3481,9 @@ void createmon(struct wl_listener *listener, void *data) { float scale = 1; enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL; - wlr_output_state_init(&state); - wlr_output_state_set_scale(&state, scale); - wlr_output_state_set_transform(&state, rr); + wlr_output_state_init(&m->pending); + wlr_output_state_set_scale(&m->pending, scale); + wlr_output_state_set_transform(&m->pending, rr); for (ji = 0; ji < config.monitor_rules_count; ji++) { if (config.monitor_rules_count < 1) @@ -3493,8 +3498,9 @@ void createmon(struct wl_listener *listener, void *data) { custom = r->custom >= 0 ? r->custom : 0; scale = r->scale; rr = r->rr; + m->hdr_enable = r->hdr; - if (apply_rule_to_state(m, r, &state, vrr, custom)) { + if (apply_rule_to_state(m, r, &m->pending, vrr, custom)) { custom_monitor_mode = true; } break; // 只应用第一个匹配规则 @@ -3502,7 +3508,7 @@ void createmon(struct wl_listener *listener, void *data) { } if (!custom_monitor_mode) - wlr_output_state_set_mode(&state, + wlr_output_state_set_mode(&m->pending, wlr_output_preferred_mode(wlr_output)); /* Set up event listeners */ @@ -3511,9 +3517,16 @@ void createmon(struct wl_listener *listener, void *data) { LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); - wlr_output_state_set_enabled(&state, 1); - wlr_output_commit_state(wlr_output, &state); - wlr_output_state_finish(&state); + wlr_output_state_set_enabled(&m->pending, 1); + + if (m->hdr_enable) { + output_state_setup_hdr(m, false); + } + + wlr_output_commit_state(wlr_output, &m->pending); + wlr_output_state_finish(&m->pending); + + wlr_output_effective_resolution(m->wlr_output, &m->m.width, &m->m.height); wl_list_insert(&mons, &m->link); @@ -3996,8 +4009,7 @@ void focusclient(Client *c, int32_t lift) { selmon->sel = c; c->isfocusing = true; - check_keep_idle_inhibit(c); - check_vrr_enable(c); + handle_client_focus_change(c); if (last_focus_client && !last_focus_client->iskilling && last_focus_client != c) { From 041a06e659efbf24b80be65cf5449c17fc97a88c Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 24 Jun 2026 11:18:18 +0800 Subject: [PATCH 31/70] opt: blur and shield node mount in root scene of client --- src/animation/client.h | 9 +++++++-- src/mango.c | 15 +++------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 83df14b1..54a58284 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -512,13 +512,16 @@ void apply_shield(Client *c, struct wlr_box clip_box) { } if (active_capture_count > 0 && c->shield_when_capture) { + int32_t shield_x = clip_box.x + (int32_t)c->bw; + int32_t shield_y = clip_box.y + (int32_t)c->bw; wlr_scene_node_raise_to_top(&c->shield->node); - wlr_scene_node_set_position(&c->shield->node, clip_box.x, clip_box.y); + wlr_scene_node_set_position(&c->shield->node, shield_x, shield_y); wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.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_node_set_position(&c->shield->node, 0, 0); wlr_scene_rect_set_size(c->shield, c->animation.current.width, c->animation.current.height); wlr_scene_node_set_enabled(&c->shield->node, false); @@ -535,8 +538,10 @@ void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { return; } else { if (config.blur && !c->noblur) { + int32_t blur_x = (int32_t)c->bw + offset.x; + int32_t blur_y = (int32_t)c->bw + offset.y; wlr_scene_node_set_enabled(&c->blur->node, true); - wlr_scene_node_set_position(&c->blur->node, offset.x, offset.y); + wlr_scene_node_set_position(&c->blur->node, blur_x, blur_y); wlr_scene_blur_set_size(c->blur, clip_box.width - c->bw, clip_box.height - c->bw); } else { diff --git a/src/mango.c b/src/mango.c index e45d179e..4df5fadb 100644 --- a/src/mango.c +++ b/src/mango.c @@ -4755,14 +4755,14 @@ mapnotify(struct wl_listener *listener, void *data) { wlr_scene_shadow_create(c->scene, 0, 0, config.border_radius, config.shadows_blur, config.shadowscolor); - c->blur = wlr_scene_blur_create(c->scene_surface, 0, 0); + c->blur = wlr_scene_blur_create(c->scene, 0, 0); wlr_scene_node_lower_to_bottom(&c->blur->node); wlr_scene_node_lower_to_bottom(&c->shadow->node); wlr_scene_node_set_enabled(&c->shadow->node, true); - c->shield = wlr_scene_rect_create(c->scene_surface, 0, 0, - (float[4]){0, 0, 0, 0xff}); + c->shield = + wlr_scene_rect_create(c->scene, 0, 0, (float[4]){0, 0, 0, 0xff}); c->shield->node.data = c; wlr_scene_node_lower_to_bottom(&c->shield->node); wlr_scene_node_set_enabled(&c->shield->node, false); @@ -6556,11 +6556,6 @@ void overview_backup_surface(Client *c) { wlr_scene_tree_snapshot(&c->scene_surface->node, c->scene); wlr_scene_node_set_enabled(&c->overview_scene_surface->node, false); wlr_scene_node_set_enabled(&c->scene_surface->node, true); - - wlr_scene_node_reparent(&c->blur->node, c->scene_surface); - wlr_scene_node_lower_to_bottom(&c->blur->node); - wlr_scene_node_reparent(&c->shield->node, c->scene_surface); - wlr_scene_node_raise_to_top(&c->shield->node); } // 普通视图切换到overview时保存窗口的旧状态 @@ -6606,10 +6601,6 @@ void overview_restore(Client *c, const Arg *arg) { c->is_restoring_from_ov = (arg->ui & c->tags & TAGMASK) == 0 ? true : false; if (c->overview_scene_surface) { - wlr_scene_node_reparent(&c->blur->node, c->overview_scene_surface); - wlr_scene_node_lower_to_bottom(&c->blur->node); - wlr_scene_node_reparent(&c->shield->node, c->overview_scene_surface); - wlr_scene_node_raise_to_top(&c->shield->node); wlr_scene_node_destroy(&c->scene_surface->node); c->scene_surface = c->overview_scene_surface; c->overview_scene_surface = NULL; From 5639b0728a4b1d890d7f321fc31601929a620b7b Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 24 Jun 2026 12:16:42 +0800 Subject: [PATCH 32/70] opt: disable vrr when focus empty tag if not global enable vrr --- src/action/client.h | 5 ----- src/mango.c | 22 ++++++++++++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 22072a09..85ab1aaf 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -265,9 +265,4 @@ void client_set_group_mon(Client *c, Monitor *m) { client_change_mon(cur, m); cur = cur->group_next; } -} - -void handle_client_focus_change(Client *c) { - check_keep_idle_inhibit(c); - check_vrr_enable(c); } \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index 4df5fadb..aa77e910 100644 --- a/src/mango.c +++ b/src/mango.c @@ -4009,7 +4009,8 @@ void focusclient(Client *c, int32_t lift) { selmon->sel = c; c->isfocusing = true; - handle_client_focus_change(c); + check_keep_idle_inhibit(c); + check_vrr_enable(c); if (last_focus_client && !last_focus_client->iskilling && last_focus_client != c) { @@ -4091,6 +4092,7 @@ void focusclient(Client *c, int32_t lift) { // clear text input focus state dwl_im_relay_set_focus(dwl_input_method_relay, NULL); wlr_seat_keyboard_notify_clear_focus(seat); + check_vrr_enable(c); if (active_constraint) { cursorconstrain(NULL); } @@ -6667,12 +6669,24 @@ void check_keep_idle_inhibit(Client *c) { void check_vrr_enable(Client *c) { - if (!c || !c->mon) + struct wlr_output_state state = {0}; + Monitor *m = c && c->mon ? c->mon : selmon; + + if (!m) return; - struct wlr_output_state state = {0}; + if (!c && m && !m->iscleanuping && m->is_vrr_opening && + !m->vrr_global_enable) { + disable_adaptive_sync(m, &state); + wlr_output_commit_state(m->wlr_output, &state); + return; + } - if (c->vrr_only_fullscreen && c->isfullscreen && !c->mon->is_vrr_opening) { + if (!c) + return; + + if (VISIBLEON(c, c->mon) && c->vrr_only_fullscreen && c->isfullscreen && + !c->mon->is_vrr_opening) { enable_adaptive_sync(c->mon, &state); wlr_output_commit_state(c->mon->wlr_output, &state); return; From d4907d63afef5e5a350ee30d3688efe47190f050 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 24 Jun 2026 17:50:18 +0800 Subject: [PATCH 33/70] opt: avoid invalid decorate draw --- src/draw/text-node.c | 201 ++++++++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 69 deletions(-) diff --git a/src/draw/text-node.c b/src/draw/text-node.c index 2b6c21d7..c57272f1 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -182,6 +182,10 @@ static void get_text_pixel_size(MangoJumpLabel *node, const char *text, static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, double h, double r) { + // 宽高非正时不绘制任何东西 + if (w <= 0.0 || h <= 0.0) + return; + double degrees = G_PI / 180.0; cairo_new_sub_path(cr); cairo_arc(cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); @@ -198,7 +202,7 @@ void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, if (scale <= 0.0f) scale = 1.0f; - /* 脏检查,加入 focused 状态 */ + // 脏检查 if (node->cached_scale == scale && node->cached_font_desc && strcmp(node->cached_font_desc, node->font_desc) == 0 && node->cached_text && strcmp(node->cached_text, text) == 0 && @@ -220,7 +224,7 @@ void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, return; } - /* 更新缓存 */ + // 更新缓存 g_free(node->cached_text); node->cached_text = g_strdup(text); g_free(node->cached_font_desc); @@ -264,6 +268,7 @@ void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, int32_t box_logical_w = logical_text_w + 2 * node->padding_x; int32_t box_logical_h = logical_text_h + 2 * node->padding_y; + // 物理像素尺寸包含边框,避免边框越界 int32_t required_pixel_w = (int32_t)((box_logical_w + 2 * node->border_width) * scale + 0.5f); int32_t required_pixel_h = @@ -316,13 +321,15 @@ void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, radius = bg_w / 2.0; if (radius > bg_h / 2.0) radius = bg_h / 2.0; + if (radius < 0.0) + radius = 0.0; const float *active_bg = node->focused ? node->focus_bg_color : node->bg_color; const float *active_fg = node->focused ? node->focus_fg_color : node->fg_color; - bool draw_bg = (active_bg[3] > 0.0f); // 使用 active_bg + bool draw_bg = (active_bg[3] > 0.0f) && (bg_w > 0.0 && bg_h > 0.0); bool draw_border = (node->border_width > 0) && (node->border_color[3] > 0.0f); @@ -338,25 +345,28 @@ void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, } } - cairo_save(cr); - double text_x = (node->border_width + node->padding_x) * scale; - double text_y = (node->border_width + node->padding_y) * scale; - cairo_translate(cr, text_x, text_y); + // 文本绘制仅在背景区域有有效空间时进行 + if (bg_w > 0.0 && bg_h > 0.0) { + cairo_save(cr); + double text_x = (node->border_width + node->padding_x) * scale; + double text_y = (node->border_width + node->padding_y) * scale; + cairo_translate(cr, text_x, text_y); - PangoContext *ctx = pango_cairo_create_context(cr); - pango_cairo_context_set_resolution(ctx, 96.0 * scale); - PangoLayout *layout = pango_layout_new(ctx); - PangoFontDescription *desc = get_cached_font_desc(node->font_desc); - pango_layout_set_font_description(layout, desc); - pango_layout_set_text(layout, text, -1); + PangoContext *ctx = pango_cairo_create_context(cr); + pango_cairo_context_set_resolution(ctx, 96.0 * scale); + PangoLayout *layout = pango_layout_new(ctx); + PangoFontDescription *desc = get_cached_font_desc(node->font_desc); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, text, -1); - cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2], - active_fg[3]); - pango_cairo_show_layout(cr, layout); + cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2], + active_fg[3]); + pango_cairo_show_layout(cr, layout); - g_object_unref(layout); - g_object_unref(ctx); - cairo_restore(cr); + g_object_unref(layout); + g_object_unref(ctx); + cairo_restore(cr); + } if (draw_border) { cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1], @@ -369,15 +379,35 @@ void mango_jump_label_node_update(MangoJumpLabel *node, const char *text, double bw = bg_w + border; double bh = bg_h + border; - if (radius > 0.0) { - double outer_radius = radius + half_lw; - if (outer_radius < 0.0) - outer_radius = 0.0; - draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); - } else { - cairo_rectangle(cr, bx, by, bw, bh); + // 确保边框矩形不越界且宽高为正 + if (bx < 0.0) { + bw += bx; // bx 为负,bw 缩小 + bx = 0.0; + } + if (by < 0.0) { + bh += by; + by = 0.0; + } + if (bx + bw > (double)node->surface_pixel_w) + bw = (double)node->surface_pixel_w - bx; + if (by + bh > (double)node->surface_pixel_h) + bh = (double)node->surface_pixel_h - by; + if (bw < 0.0) + bw = 0.0; + if (bh < 0.0) + bh = 0.0; + + if (bw > 0.0 && bh > 0.0) { + if (radius > 0.0) { + double outer_radius = radius + half_lw; + if (outer_radius < 0.0) + outer_radius = 0.0; + draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); + } else { + cairo_rectangle(cr, bx, by, bw, bh); + } + cairo_stroke(cr); } - cairo_stroke(cr); } cairo_surface_flush(node->surface); @@ -409,7 +439,6 @@ void mango_jump_label_node_set_focus(MangoJumpLabel *node, bool focused) { if (!node || node->focused == focused) return; node->focused = focused; - // 使用缓存的文本和缩放触发重绘(如果无文本则不重绘) if (node->cached_text && node->cached_scale > 0.0f) { mango_jump_label_node_update(node, node->cached_text, node->cached_scale); @@ -537,7 +566,7 @@ void mango_group_bar_update(MangoGroupBar *node, const char *text, node->last_text = safe_text; // 所有权转移 node->last_scale = scale; - // 脏检查加入 focused + // 脏检查 if (node->cached_scale == scale && node->cached_font_desc && strcmp(node->cached_font_desc, node->font_desc) == 0 && node->cached_text && strcmp(node->cached_text, safe_text) == 0 && @@ -607,8 +636,18 @@ void mango_group_bar_update(MangoGroupBar *node, const char *text, if (box_logical_h < 0) box_logical_h = 0; + // surface 物理尺寸包含边框,避免边框绘制越界 int32_t required_pixel_w = (int32_t)(node->target_width * scale + 0.5f); int32_t required_pixel_h = (int32_t)(node->target_height * scale + 0.5f); + // 如果边框会伸出 target 区域,则扩展 surface 尺寸以完全容纳 + double border_phys = node->border_width * scale; + if (border_phys > 0.0) { + // 边框描边会向外延伸 half line width,所以需要额外空间 + int extra_w = (int32_t)ceil(border_phys * 0.5); + int extra_h = (int32_t)ceil(border_phys * 0.5); + required_pixel_w += extra_w; + required_pixel_h += extra_h; + } if (required_pixel_w < 1) required_pixel_w = 1; if (required_pixel_h < 1) @@ -640,7 +679,6 @@ void mango_group_bar_update(MangoGroupBar *node, const char *text, cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - double border_phys = node->border_width * scale; double bg_x = border_phys; double bg_y = border_phys; double bg_w = box_logical_w * scale; @@ -656,13 +694,15 @@ void mango_group_bar_update(MangoGroupBar *node, const char *text, radius = bg_w / 2.0; if (radius > bg_h / 2.0) radius = bg_h / 2.0; + if (radius < 0.0) + radius = 0.0; const float *active_bg = node->focused ? node->focus_bg_color : node->bg_color; const float *active_fg = node->focused ? node->focus_fg_color : node->fg_color; - bool draw_bg = (active_bg[3] > 0.0f); + bool draw_bg = (active_bg[3] > 0.0f) && (bg_w > 0.0 && bg_h > 0.0); bool draw_border = (node->border_width > 0) && (node->border_color[3] > 0.0f); @@ -678,43 +718,46 @@ void mango_group_bar_update(MangoGroupBar *node, const char *text, } } - int32_t text_area_logical_w = box_logical_w - 2 * node->padding_x; - int32_t text_area_logical_h = box_logical_h - 2 * node->padding_y; - if (text_area_logical_w > 0 && text_area_logical_h > 0) { - cairo_save(cr); + // 仅在背景区域有空间时进行 + if (bg_w > 0.0 && bg_h > 0.0) { + int32_t text_area_logical_w = box_logical_w - 2 * node->padding_x; + int32_t text_area_logical_h = box_logical_h - 2 * node->padding_y; + if (text_area_logical_w > 0 && text_area_logical_h > 0) { + cairo_save(cr); - double text_x = (node->border_width + node->padding_x) * scale; - double text_y = (node->border_width + node->padding_y) * scale; - double text_area_w = text_area_logical_w * scale; - double text_area_h = text_area_logical_h * scale; + double text_x = (node->border_width + node->padding_x) * scale; + double text_y = (node->border_width + node->padding_y) * scale; + double text_area_w = text_area_logical_w * scale; + double text_area_h = text_area_logical_h * scale; - PangoContext *ctx = pango_cairo_create_context(cr); - pango_cairo_context_set_resolution(ctx, 96.0 * scale); - PangoLayout *layout = pango_layout_new(ctx); - PangoFontDescription *desc = get_cached_font_desc(node->font_desc); - pango_layout_set_font_description(layout, desc); - pango_layout_set_text(layout, safe_text, -1); + PangoContext *ctx = pango_cairo_create_context(cr); + pango_cairo_context_set_resolution(ctx, 96.0 * scale); + PangoLayout *layout = pango_layout_new(ctx); + PangoFontDescription *desc = get_cached_font_desc(node->font_desc); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, safe_text, -1); - pango_layout_set_wrap(layout, PANGO_WRAP_NONE); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); - pango_layout_set_width(layout, (int)(text_area_w * PANGO_SCALE)); + pango_layout_set_wrap(layout, PANGO_WRAP_NONE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + pango_layout_set_width(layout, (int)(text_area_w * PANGO_SCALE)); - int text_pixel_w, text_pixel_h; - pango_layout_get_pixel_size(layout, &text_pixel_w, &text_pixel_h); - double y_offset = (text_area_h - text_pixel_h) / 2.0; - if (y_offset < 0) - y_offset = 0; + int text_pixel_w, text_pixel_h; + pango_layout_get_pixel_size(layout, &text_pixel_w, &text_pixel_h); + double y_offset = (text_area_h - text_pixel_h) / 2.0; + if (y_offset < 0) + y_offset = 0; - cairo_translate(cr, text_x, text_y + y_offset); + cairo_translate(cr, text_x, text_y + y_offset); - cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2], - active_fg[3]); - pango_cairo_show_layout(cr, layout); + cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2], + active_fg[3]); + pango_cairo_show_layout(cr, layout); - g_object_unref(layout); - g_object_unref(ctx); - cairo_restore(cr); + g_object_unref(layout); + g_object_unref(ctx); + cairo_restore(cr); + } } if (draw_border) { @@ -728,15 +771,35 @@ void mango_group_bar_update(MangoGroupBar *node, const char *text, double bw = bg_w + border_phys; double bh = bg_h + border_phys; - if (radius > 0.0) { - double outer_radius = radius + half_lw; - if (outer_radius < 0.0) - outer_radius = 0.0; - draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); - } else { - cairo_rectangle(cr, bx, by, bw, bh); + // 确保边框矩形不越界且宽高为正 + if (bx < 0.0) { + bw += bx; + bx = 0.0; + } + if (by < 0.0) { + bh += by; + by = 0.0; + } + if (bx + bw > (double)node->surface_pixel_w) + bw = (double)node->surface_pixel_w - bx; + if (by + bh > (double)node->surface_pixel_h) + bh = (double)node->surface_pixel_h - by; + if (bw < 0.0) + bw = 0.0; + if (bh < 0.0) + bh = 0.0; + + if (bw > 0.0 && bh > 0.0) { + if (radius > 0.0) { + double outer_radius = radius + half_lw; + if (outer_radius < 0.0) + outer_radius = 0.0; + draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); + } else { + cairo_rectangle(cr, bx, by, bw, bh); + } + cairo_stroke(cr); } - cairo_stroke(cr); } cairo_surface_flush(node->surface); From fde0eee0a564f63851d81add88951403fc040685 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 24 Jun 2026 18:15:26 +0800 Subject: [PATCH 34/70] opt: set surface_scene to 0 when client is fullscreen It is noted that the foot terminal cannot enter the scan-out, but other clients do not have this problem --- src/animation/client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/animation/client.h b/src/animation/client.h index 54a58284..09e9cc5f 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -660,6 +660,7 @@ void apply_border(Client *c) { if (c->isfullscreen) { if (c->border->node.enabled) { + wlr_scene_node_set_position(&c->scene_surface->node, 0, 0); wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); wlr_scene_node_set_enabled(&c->border->node, false); From 09e241121e22740aa9237835cd4b3935665fc5e5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 25 Jun 2026 10:37:52 +0800 Subject: [PATCH 35/70] opt: Reconstruct the output commit logic --- src/action/monitor.h | 61 ++++++++++++++++++++++++++++++++++++++ src/config/parse_config.h | 16 ++++------ src/dispatch/bind_define.h | 16 +++++----- src/ext-protocol/hdr.h | 19 +++++++----- src/ext-protocol/tearing.h | 51 +++---------------------------- src/mango.c | 52 ++++++++++++++------------------ 6 files changed, 112 insertions(+), 103 deletions(-) create mode 100644 src/action/monitor.h diff --git a/src/action/monitor.h b/src/action/monitor.h new file mode 100644 index 00000000..434fb159 --- /dev/null +++ b/src/action/monitor.h @@ -0,0 +1,61 @@ +bool mango_scene_output_commit(struct wlr_scene_output *scene_output, + struct wlr_output_state *state) { + struct wlr_output *wlr_output = scene_output->output; + Monitor *m = wlr_output->data; + bool committed = false; + + bool frame_allow_tearing = check_tearing_frame_allow(m); + + if (!wlr_scene_output_needs_frame(scene_output)) + return true; + + // 构建状态,将场景的 Buffer 附着到 state 上 + if (!wlr_scene_output_build_state(scene_output, state, NULL)) + return false; + + if (frame_allow_tearing) { + state->tearing_page_flip = true; + } else { + state->tearing_page_flip = false; + } + + // 测试是否支持撕裂 + if (state->tearing_page_flip == true) { + if (!wlr_output_test_state(wlr_output, state)) { + // 如果 DRM 拒绝(例如当前输出/驱动不支持撕裂),降级关闭撕裂 + state->tearing_page_flip = false; + } + } + + // 提交状态 + committed = wlr_output_commit_state(wlr_output, state); + if (!committed && state->tearing_page_flip) { + // 重试一次 + state->tearing_page_flip = false; + committed = wlr_output_commit_state(wlr_output, state); + } + + if (committed) { + if (state == &m->pending) { + wlr_output_state_finish(&m->pending); + wlr_output_state_init(&m->pending); + } + } else { + wlr_log(WLR_INFO, "Failed to commit output %s", m->wlr_output->name); + return false; + } + + return committed; +} + +bool mango_output_commit(Monitor *m) { + + bool committed = wlr_output_commit_state(m->wlr_output, &m->pending); + if (committed) { + wlr_output_state_finish(&m->pending); + wlr_output_state_init(&m->pending); + } else { + wlr_log(WLR_ERROR, "Failed to commit frame"); + } + return committed; +} \ No newline at end of file diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 306f23bf..959ff095 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -4054,8 +4054,6 @@ void reapply_monitor_rules(void) { if (!m->wlr_output->enabled) continue; - wlr_output_state_init(&m->pending); - for (ji = 0; ji < config.monitor_rules_count; ji++) { if (config.monitor_rules_count < 1) break; @@ -4078,16 +4076,14 @@ void reapply_monitor_rules(void) { wlr_output_state_set_enabled(&m->pending, true); if (m->hdr_enable) { - output_state_setup_hdr(m, false); - bool success = wlr_output_commit_state(m->wlr_output, &m->pending); - if (!success) { // 多尝试一次 - output_state_setup_hdr(m, true); - } - } else { - wlr_output_commit_state(m->wlr_output, &m->pending); + output_state_setup_hdr(m, false, &m->pending); } - wlr_output_state_finish(&m->pending); + if (!(mango_scene_output_commit(m->scene_output, &m->pending))) { + if (m->hdr_enable) { + output_state_setup_hdr(m, true, &m->pending); + } + } wlr_output_effective_resolution(m->wlr_output, &m->m.width, &m->m.height); } diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index c70d6213..9312b12c 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1944,11 +1944,11 @@ int32_t togglejump(const Arg *arg) { int32_t disable_monitor(const Arg *arg) { Monitor *m = NULL; - struct wlr_output_state state = {0}; + wl_list_for_each(m, &mons, link) { if (match_monitor_spec(arg->v, m)) { - wlr_output_state_set_enabled(&state, false); - wlr_output_commit_state(m->wlr_output, &state); + wlr_output_state_set_enabled(&m->pending, false); + mango_output_commit(m); m->asleep = 1; updatemons(NULL, NULL); break; @@ -1959,11 +1959,10 @@ int32_t disable_monitor(const Arg *arg) { int32_t enable_monitor(const Arg *arg) { Monitor *m = NULL; - struct wlr_output_state state = {0}; wl_list_for_each(m, &mons, link) { if (match_monitor_spec(arg->v, m)) { - wlr_output_state_set_enabled(&state, true); - wlr_output_commit_state(m->wlr_output, &state); + wlr_output_state_set_enabled(&m->pending, true); + mango_output_commit(m); m->asleep = 0; updatemons(NULL, NULL); break; @@ -1974,11 +1973,10 @@ int32_t enable_monitor(const Arg *arg) { int32_t toggle_monitor(const Arg *arg) { Monitor *m = NULL; - struct wlr_output_state state = {0}; wl_list_for_each(m, &mons, link) { if (match_monitor_spec(arg->v, m)) { - wlr_output_state_set_enabled(&state, !m->wlr_output->enabled); - wlr_output_commit_state(m->wlr_output, &state); + wlr_output_state_set_enabled(&m->pending, !m->wlr_output->enabled); + mango_output_commit(m); m->asleep = !m->wlr_output->enabled; updatemons(NULL, NULL); break; diff --git a/src/ext-protocol/hdr.h b/src/ext-protocol/hdr.h index 2f2ff9a3..7b82a983 100644 --- a/src/ext-protocol/hdr.h +++ b/src/ext-protocol/hdr.h @@ -16,10 +16,11 @@ static uint32_t output_formats_10bit[] = { }; static bool output_set_render_format(Monitor *m, uint32_t candidates[], - size_t count) { + size_t count, + struct wlr_output_state *state) { for (size_t i = 0; i < count; i++) { - wlr_output_state_set_render_format(&m->pending, candidates[i]); - if (wlr_output_test_state(m->wlr_output, &m->pending)) + wlr_output_state_set_render_format(state, candidates[i]); + if (wlr_output_test_state(m->wlr_output, state)) return true; } return false; @@ -83,7 +84,8 @@ void output_enable_hdr(Monitor *m, struct wlr_output_state *os, bool enabled, wlr_output_state_set_image_description(os, &desc); } -void output_state_setup_hdr(Monitor *m, bool silent) { +void output_state_setup_hdr(Monitor *m, bool silent, + struct wlr_output_state *state) { uint32_t render_format = m->wlr_output->render_format; const char *unsupported_reason = NULL; bool hdr_supported = @@ -106,23 +108,24 @@ void output_state_setup_hdr(Monitor *m, bool silent) { hdr_succeeded = true; // 上次已经成功设置10位,直接复用 } else if (depth == MANGO_RENDER_BIT_DEPTH_10) { hdr_succeeded = output_set_render_format( - m, output_formats_10bit, ARRAY_SIZE(output_formats_10bit)); + m, output_formats_10bit, ARRAY_SIZE(output_formats_10bit), state); if (!hdr_succeeded) { if (!silent) wlr_log(WLR_INFO, "No 10 bit color formats supported, HDR disabled."); if (!output_set_render_format(m, output_formats_8bit, - ARRAY_SIZE(output_formats_8bit))) + ARRAY_SIZE(output_formats_8bit), + state)) if (!silent) wlr_log(WLR_ERROR, "No 8 bit color formats either!"); } } else { // 明确要求8位或自动降级 if (!output_set_render_format(m, output_formats_8bit, - ARRAY_SIZE(output_formats_8bit))) + ARRAY_SIZE(output_formats_8bit), state)) if (!silent) wlr_log(WLR_ERROR, "No 8 bit color formats supported!"); } - output_enable_hdr(m, &m->pending, hdr_succeeded, silent); + output_enable_hdr(m, state, hdr_succeeded, silent); } \ No newline at end of file diff --git a/src/ext-protocol/tearing.h b/src/ext-protocol/tearing.h index 02394eaa..90143423 100644 --- a/src/ext-protocol/tearing.h +++ b/src/ext-protocol/tearing.h @@ -1,3 +1,4 @@ +#include #include struct tearing_controller { @@ -64,6 +65,9 @@ bool check_tearing_frame_allow(Monitor *m) { return false; } + if (!selmon) + return false; + Client *c = selmon->sel; /* tearing is only allowed for the output with the active client */ @@ -94,50 +98,3 @@ bool check_tearing_frame_allow(Monitor *m) { /* honor tearing as requested by action */ return c->force_tearing == STATE_ENABLED; } - -bool custom_wlr_scene_output_commit(struct wlr_scene_output *scene_output, - struct wlr_output_state *state) { - struct wlr_output *wlr_output = scene_output->output; - Monitor *m = wlr_output->data; - - if (!wlr_scene_output_needs_frame(scene_output)) - return true; - - // 构建状态,将场景的 Buffer 附着到 state 上 - if (!wlr_scene_output_build_state(scene_output, state, NULL)) - return false; - - // 测试是否支持撕裂 - if (!wlr_output_test_state(wlr_output, state)) { - // 如果 DRM 拒绝(例如当前输出/驱动不支持撕裂),降级关闭撕裂 - state->tearing_page_flip = false; - } - - // 提交状态 - bool committed = wlr_output_commit_state(wlr_output, state); - if (!committed && state->tearing_page_flip) { - // 重试一次 - state->tearing_page_flip = false; - committed = wlr_output_commit_state(wlr_output, state); - } - - if (committed) { - if (state == &m->pending) { - wlr_output_state_finish(&m->pending); - wlr_output_state_init(&m->pending); - } - } - return committed; -} - -void apply_tear_state(Monitor *m) { - if (!wlr_scene_output_needs_frame(m->scene_output)) - return; - - wlr_output_state_init(&m->pending); - m->pending.tearing_page_flip = true; - if (!custom_wlr_scene_output_commit(m->scene_output, &m->pending)) { - wlr_log(WLR_ERROR, "Failed to commit output %s", - m->scene_output->output->name); - } -} \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index aa77e910..476b4e94 100644 --- a/src/mango.c +++ b/src/mango.c @@ -953,9 +953,14 @@ static void global_draw_group_bar(Client *c, int32_t x, int32_t y, static void client_reparent_group(Client *c); static void client_change_mon(Client *c, Monitor *m); static void check_vrr_enable(Client *c); -static void output_state_setup_hdr(Monitor *m, bool silent); +static void output_state_setup_hdr(Monitor *m, bool silent, + struct wlr_output_state *state); static void output_enable_hdr(Monitor *m, struct wlr_output_state *os, bool enabled, bool silent); +static bool mango_scene_output_commit(struct wlr_scene_output *scene_output, + struct wlr_output_state *state); +static bool mango_output_commit(Monitor *m); +static bool check_tearing_frame_allow(Monitor *m); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -1165,6 +1170,7 @@ static struct wl_event_source *sync_keymap; #endif #include "action/client.h" +#include "action/monitor.h" #include "animation/client.h" #include "animation/common.h" #include "animation/layer.h" @@ -3520,14 +3526,9 @@ void createmon(struct wl_listener *listener, void *data) { wlr_output_state_set_enabled(&m->pending, 1); if (m->hdr_enable) { - output_state_setup_hdr(m, false); + output_state_setup_hdr(m, false, &m->pending); } - wlr_output_commit_state(wlr_output, &m->pending); - wlr_output_state_finish(&m->pending); - - wlr_output_effective_resolution(m->wlr_output, &m->m.width, &m->m.height); - wl_list_insert(&mons, &m->link); m->pertag = calloc(1, sizeof(Pertag)); @@ -3575,6 +3576,9 @@ void createmon(struct wl_listener *listener, void *data) { else wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); + mango_scene_output_commit(m->scene_output, &m->pending); + wlr_output_effective_resolution(m->wlr_output, &m->m.width, &m->m.height); + if (config.blur) { m->blur = wlr_scene_optimized_blur_create(&scene->tree, 0, 0); wlr_scene_node_set_position(&m->blur->node, m->m.x, m->m.y); @@ -4142,6 +4146,7 @@ void requestmonstate(struct wl_listener *listener, void *data) { const struct wlr_output_event_request_state *event = data; if (event->state->committed == WLR_OUTPUT_STATE_MODE) { + switch (event->state->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED: wlr_output_state_set_mode(&m->pending, event->state->mode); @@ -5298,15 +5303,13 @@ void handle_iamge_copy_capture_new_session(struct wl_listener *listener, void powermgrsetmode(struct wl_listener *listener, void *data) { struct wlr_output_power_v1_set_mode_event *event = data; - struct wlr_output_state state = {0}; Monitor *m = event->output->data; if (!m) return; - wlr_output_state_set_enabled(&state, event->mode); - wlr_output_commit_state(m->wlr_output, &state); - + wlr_output_state_set_enabled(&m->pending, event->mode); + mango_output_commit(m); m->asleep = !event->mode; updatemons(NULL, NULL); } @@ -5363,7 +5366,6 @@ void rendermon(struct wl_listener *listener, void *data) { LayerSurface *l = NULL, *tmpl = NULL; int32_t i; struct wl_list *layer_list; - bool frame_allow_tearing = false; struct timespec now; bool need_more_frames = false; @@ -5374,8 +5376,6 @@ void rendermon(struct wl_listener *listener, void *data) { if (!m->wlr_output->enabled || !allow_frame_scheduling) return; - frame_allow_tearing = check_tearing_frame_allow(m); - // 绘制层和淡出效果 for (i = 0; i < LENGTH(m->layers); i++) { layer_list = &m->layers[i]; @@ -5406,12 +5406,7 @@ void rendermon(struct wl_listener *listener, void *data) { monitor_stop_skip_frame_timer(m); } - // 只有在需要帧时才构建和提交状态 - if (config.allow_tearing && frame_allow_tearing) { - apply_tear_state(m); - } else { - wlr_scene_output_commit(m->scene_output, NULL); - } + mango_scene_output_commit(m->scene_output, &m->pending); skip: // 发送帧完成通知 @@ -6669,7 +6664,6 @@ void check_keep_idle_inhibit(Client *c) { void check_vrr_enable(Client *c) { - struct wlr_output_state state = {0}; Monitor *m = c && c->mon ? c->mon : selmon; if (!m) @@ -6677,8 +6671,8 @@ void check_vrr_enable(Client *c) { if (!c && m && !m->iscleanuping && m->is_vrr_opening && !m->vrr_global_enable) { - disable_adaptive_sync(m, &state); - wlr_output_commit_state(m->wlr_output, &state); + disable_adaptive_sync(m, &m->pending); + mango_output_commit(m); return; } @@ -6687,17 +6681,17 @@ void check_vrr_enable(Client *c) { if (VISIBLEON(c, c->mon) && c->vrr_only_fullscreen && c->isfullscreen && !c->mon->is_vrr_opening) { - enable_adaptive_sync(c->mon, &state); - wlr_output_commit_state(c->mon->wlr_output, &state); + enable_adaptive_sync(c->mon, &m->pending); + mango_output_commit(m); return; } if (!c->mon->is_vrr_opening && c->mon->vrr_global_enable) { - enable_adaptive_sync(c->mon, &state); - wlr_output_commit_state(c->mon->wlr_output, &state); + enable_adaptive_sync(c->mon, &m->pending); + mango_output_commit(m); } else if (c->mon->is_vrr_opening && !c->mon->vrr_global_enable) { - disable_adaptive_sync(c->mon, &state); - wlr_output_commit_state(c->mon->wlr_output, &state); + disable_adaptive_sync(c->mon, &m->pending); + mango_output_commit(m); } } From ed16b1da555097e92fbe83181a8de767e52ddaed Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 25 Jun 2026 13:18:48 +0800 Subject: [PATCH 36/70] opt: make env can set for wlroots --- src/mango.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mango.c b/src/mango.c index 476b4e94..b9e5b00e 100644 --- a/src/mango.c +++ b/src/mango.c @@ -5538,8 +5538,6 @@ cleanup: void // 17 run(char *startup_cmd) { - set_env(); - /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); if (!socket) @@ -6137,6 +6135,8 @@ void setup(void) { } init_baked_points(); + set_env(); + int32_t drm_fd, i; int32_t sig[] = {SIGCHLD, SIGINT, SIGTERM}; // 不设置SIGPIPE,因为ipc发送失败不应该影响主程序 From 15032151c8e4ec066fee30501f6937753d5b640d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 25 Jun 2026 13:33:00 +0800 Subject: [PATCH 37/70] update docs --- docs/configuration/miscellaneous.md | 2 -- docs/configuration/monitors.md | 28 ++++++++++++++++++---------- docs/faq.md | 3 ++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index d8871885..32dc5b42 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -11,8 +11,6 @@ description: Advanced settings for XWayland, focus behavior, and system integrat | `syncobj_enable` | `1` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** | | `allow_lock_transparent` | `0` | Allow the lock screen to be transparent. | | `allow_shortcuts_inhibit` | `1` | Allow shortcuts to be inhibited by clients. | -| `vrr` | - | Set via [monitor rule](/docs/configuration/monitors#monitor-rules). | -| `hdr_hdr_depth` | `0`/`1`/`2` | Set the hdr depth for the current display. `0` is Default, `1` is HDR8, `2` is HDR10. | ## Focus & Input diff --git a/docs/configuration/monitors.md b/docs/configuration/monitors.md index ff64bc10..ff6cb8fa 100644 --- a/docs/configuration/monitors.md +++ b/docs/configuration/monitors.md @@ -103,6 +103,21 @@ Tearing allows games to bypass the compositor's VSync for lower latency. | :--- | :--- | :--- | | `allow_tearing` | `0` | Global tearing control: `0` (Disable), `1` (Enable), `2` (Fullscreen only). | +## HDR +| Setting | Default | Description | +| :--- | :--- | :--- | +| `hdr_hdr_depth` | `0`/`1`/`2` | Set the hdr depth for the current display. `0` is Default, `1` is HDR8, `2` is HDR10. | + +- you should enable HDR in monitorrule first, refer to [Monitors — Monitor Rules](/docs/configuration/monitors#monitor-rules) +- you must set `env=WLR_RENDERER,vulkan` before mango starts. + +#### for example(must relogin once after setting): +```conf +env=WLR_RENDERER,vulkan +monitorrule=name:eDP-1,model:0x15F5,width:1920,height:1080,refresh:60,x:0,y:0,scale:1,vrr:0,rr:0:hdr:1 +``` + + ### Configuration **Enable Globally:** @@ -131,16 +146,9 @@ windowrule=force_tearing:1,title:vkcube > **Warning:** Some graphics cards require setting the `WLR_DRM_NO_ATOMIC` environment variable before mango starts to successfully enable tearing. -Add this to `/etc/environment` and reboot: - -```bash -WLR_DRM_NO_ATOMIC=1 +Add this to config and relogin mango: ``` - -Or run mango with the environment variable: - -```bash -WLR_DRM_NO_ATOMIC=1 mango +env=WLR_DRM_NO_ATOMIC,1 ``` --- @@ -157,7 +165,7 @@ WLR_DRM_DEVICES=/dev/dri/card1 mango WLR_DRM_DEVICES=/dev/dri/card0:/dev/dri/card1 mango ``` -Some GPUs have compatibility issues with `syncobj_enable=1` — it may crash apps like `kitty` that use syncobj. Set `WLR_DRM_NO_ATOMIC=1` in `/etc/environment` and reboot to resolve this. +Some GPUs have compatibility issues with `syncobj_enable=1` — it may crash apps like `kitty` that use syncobj. Set `env=WLR_DRM_NO_ATOMIC,1` in `config.conf` and relogin to resolve this. --- diff --git a/docs/faq.md b/docs/faq.md index 9c9288de..85c9f09e 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -65,7 +65,8 @@ Then force it for your specific game: windowrule=force_tearing:1,title:Counter-Strike 2 ``` -> **Warning:** Some graphics cards require setting `WLR_DRM_NO_ATOMIC=1` before mango starts for tearing to work. Add it to `/etc/environment` and reboot, or launch mango with `WLR_DRM_NO_ATOMIC=1 mango`. See [Monitors — Tearing](/docs/configuration/monitors#tearing-game-mode) for details. +> **Warning:** Some graphics cards require setting `env=WLR_DRM_NO_ATOMIC,1` in config before mango starts for tearing to work. +> See [Monitors — Tearing](/docs/configuration/monitors#tearing-game-mode) for details. --- From 9e5a5f3a42f5769241d32473b0a63dcd51645be1 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 25 Jun 2026 14:08:45 +0800 Subject: [PATCH 38/70] fix: headless output must config before add to output layout --- src/mango.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mango.c b/src/mango.c index b9e5b00e..35f9266e 100644 --- a/src/mango.c +++ b/src/mango.c @@ -3529,6 +3529,11 @@ void createmon(struct wl_listener *listener, void *data) { output_state_setup_hdr(m, false, &m->pending); } + // 虚拟显示器在加入显示器链前必须先配置,否则会崩溃 + if (wlr_output_is_headless(m->wlr_output)) { + mango_output_commit(m); + } + wl_list_insert(&mons, &m->link); m->pertag = calloc(1, sizeof(Pertag)); @@ -3576,7 +3581,11 @@ void createmon(struct wl_listener *listener, void *data) { else wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); - mango_scene_output_commit(m->scene_output, &m->pending); + // 无头显示器不不要支持hdr + if (!wlr_output_is_headless(m->wlr_output)) { + mango_scene_output_commit(m->scene_output, &m->pending); + } + wlr_output_effective_resolution(m->wlr_output, &m->m.width, &m->m.height); if (config.blur) { From 73abc2802d163dfa3301592b086a6e089fa150fb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 25 Jun 2026 23:14:07 +0800 Subject: [PATCH 39/70] opt: hdr_depth default to hdr10 --- docs/configuration/monitors.md | 2 +- src/config/parse_config.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/monitors.md b/docs/configuration/monitors.md index ff6cb8fa..15ddfbaa 100644 --- a/docs/configuration/monitors.md +++ b/docs/configuration/monitors.md @@ -106,7 +106,7 @@ Tearing allows games to bypass the compositor's VSync for lower latency. ## HDR | Setting | Default | Description | | :--- | :--- | :--- | -| `hdr_hdr_depth` | `0`/`1`/`2` | Set the hdr depth for the current display. `0` is Default, `1` is HDR8, `2` is HDR10. | +| `hdr_depth` | `2`| Set the hdr depth for the current display. `0` is Default, `1` is HDR8, `2` is HDR10. | - you should enable HDR in monitorrule first, refer to [Monitors — Monitor Rules](/docs/configuration/monitors#monitor-rules) - you must set `env=WLR_RENDERER,vulkan` before mango starts. diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 959ff095..09847014 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -3718,7 +3718,7 @@ void set_value_default() { config.drag_tile_refresh_interval = 8.0f; config.drag_floating_refresh_interval = 8.0f; config.allow_tearing = TEARING_DISABLED; - config.hdr_depth = MANGO_RENDER_BIT_DEPTH_DEFAULT; + config.hdr_depth = MANGO_RENDER_BIT_DEPTH_10; config.allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; config.allow_lock_transparent = 0; config.no_border_when_single = 0; From 1a667b22b3607129628c330fcc26bd0339f7eec5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Fri, 26 Jun 2026 10:14:54 +0800 Subject: [PATCH 40/70] fix: hdr miss add color mangaer mgr --- src/mango.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/mango.c b/src/mango.c index 35f9266e..29859377 100644 --- a/src/mango.c +++ b/src/mango.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -6200,6 +6202,48 @@ void setup(void) { if (!(drw = fx_renderer_create(backend))) die("couldn't create renderer"); + if (drw->features.input_color_transform) { + const enum wp_color_manager_v1_render_intent render_intents[] = { + WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL, + }; + size_t transfer_functions_len = 0; + enum wp_color_manager_v1_transfer_function *transfer_functions = + wlr_color_manager_v1_transfer_function_list_from_renderer( + drw, &transfer_functions_len); + + size_t primaries_len = 0; + enum wp_color_manager_v1_primaries *primaries = + wlr_color_manager_v1_primaries_list_from_renderer(drw, + &primaries_len); + + struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( + dpy, 2, + &(struct wlr_color_manager_v1_options){ + .features = + { + .parametric = true, + .set_mastering_display_primaries = true, + }, + .render_intents = render_intents, + .render_intents_len = ARRAY_SIZE(render_intents), + .transfer_functions = transfer_functions, + .transfer_functions_len = transfer_functions_len, + .primaries = primaries, + .primaries_len = primaries_len, + }); + + free(transfer_functions); + free(primaries); + + if (cm) { + wlr_scene_set_color_manager_v1(scene, cm); + } else { + wlr_log(WLR_ERROR, "unable to create color manager"); + } + } + + wlr_color_representation_manager_v1_create_with_renderer(dpy, 1, drw); + wl_signal_add(&drw->events.lost, &gpu_reset); /* Create shm, drm and linux_dmabuf interfaces by ourselves. From c071c6f019c968e7d5c9740533428d2c089a0553 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Fri, 26 Jun 2026 12:38:05 +0800 Subject: [PATCH 41/70] opt: remove useless code --- src/action/client.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 85ab1aaf..aa41e9c8 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -190,8 +190,7 @@ void client_check_tab_node_visible(Client *c) { while (cur) { if (!c->mon->isoverview && cur->group_bar && (cur->group_next || cur->group_prev) && VISIBLEON(c, c->mon) && - ISNORMAL(c) && !c->isfullscreen && - (!is_monocle_layout(c->mon) || c == c->mon->sel)) { + ISNORMAL(c) && !c->isfullscreen) { wlr_scene_node_set_enabled(&cur->group_bar->scene_buffer->node, true); } else { From 2f77be7e69e74f84db51b02bcb6e3acc69ae052b Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 27 Jun 2026 11:30:18 +0800 Subject: [PATCH 42/70] opt: opt group data set when reload config --- src/action/client.h | 19 ++++++- src/config/parse_config.h | 101 ++++++++++++++++++-------------------- src/mango.c | 1 + 3 files changed, 67 insertions(+), 54 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index aa41e9c8..b12f8cef 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -127,7 +127,7 @@ void client_add_group_bar(Client *c) { : LyrTile; c->group_bar = mango_group_bar_create(c, GroupBar, layers[layer], - config.tabdata, 0, 0); + config.groupbardata, 0, 0); wlr_scene_node_lower_to_bottom(&c->group_bar->scene_buffer->node); wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); mango_group_bar_update(c->group_bar, client_get_title(c), @@ -264,4 +264,21 @@ void client_set_group_mon(Client *c, Monitor *m) { client_change_mon(cur, m); cur = cur->group_next; } +} + +void client_set_group_config(Client *c) { + Client *head = c; + while (head->group_prev) + head = head->group_prev; + + Client *cur = head; + while (cur) { + mango_jump_label_node_apply_config(cur->jump_label_node, + &config.jumplabeldata); + wlr_scene_rect_set_color(cur->droparea, config.dropcolor); + wlr_scene_rect_set_color(cur->splitindicator[0], config.splitcolor); + wlr_scene_rect_set_color(cur->splitindicator[1], config.splitcolor); + mango_group_bar_apply_config(cur->group_bar, &config.groupbardata); + cur = cur->group_next; + } } \ No newline at end of file diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 09847014..3b7384a3 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -418,7 +418,7 @@ typedef struct { struct xkb_context *ctx; struct xkb_keymap *keymap; DecorateDrawData jumplabeldata; - DecorateDrawData tabdata; + DecorateDrawData groupbardata; int32_t hdr_depth; } Config; @@ -1786,7 +1786,7 @@ bool parse_option(Config *config, char *key, char *value) { } else if (strcmp(key, "cursor_theme") == 0) { config->cursor_theme = strdup(value); } else if (strcmp(key, "group_bar_decorate_font_desc") == 0) { - config->tabdata.font_desc = strdup(value); + config->groupbardata.font_desc = strdup(value); } else if (strcmp(key, "group_bar_decorate_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { @@ -1797,7 +1797,7 @@ bool parse_option(Config *config, char *key, char *value) { value); return false; } else { - convert_hex_to_rgba(config->tabdata.fg_color, color); + convert_hex_to_rgba(config->groupbardata.fg_color, color); } } else if (strcmp(key, "group_bar_decorate_bg_color") == 0) { int64_t color = parse_color(value); @@ -1809,7 +1809,7 @@ bool parse_option(Config *config, char *key, char *value) { value); return false; } else { - convert_hex_to_rgba(config->tabdata.bg_color, color); + convert_hex_to_rgba(config->groupbardata.bg_color, color); } } else if (strcmp(key, "group_bar_decorate_focus_fg_color") == 0) { int64_t color = parse_color(value); @@ -1821,7 +1821,7 @@ bool parse_option(Config *config, char *key, char *value) { value); return false; } else { - convert_hex_to_rgba(config->tabdata.focus_fg_color, color); + convert_hex_to_rgba(config->groupbardata.focus_fg_color, color); } } else if (strcmp(key, "group_bar_decorate_focus_bg_color") == 0) { int64_t color = parse_color(value); @@ -1833,7 +1833,7 @@ bool parse_option(Config *config, char *key, char *value) { value); return false; } else { - convert_hex_to_rgba(config->tabdata.focus_bg_color, color); + convert_hex_to_rgba(config->groupbardata.focus_bg_color, color); } } else if (strcmp(key, "group_bar_decorate_border_color") == 0) { int64_t color = parse_color(value); @@ -1845,16 +1845,16 @@ bool parse_option(Config *config, char *key, char *value) { value); return false; } else { - convert_hex_to_rgba(config->tabdata.border_color, color); + convert_hex_to_rgba(config->groupbardata.border_color, color); } } else if (strcmp(key, "group_bar_decorate_border_width") == 0) { - config->tabdata.border_width = CLAMP_INT(atoi(value), 0, 100); + config->groupbardata.border_width = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "group_bar_decorate_corner_radius") == 0) { - config->tabdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); + config->groupbardata.corner_radius = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "group_bar_decorate_padding_x") == 0) { - config->tabdata.padding_x = CLAMP_INT(atoi(value), 0, 100); + config->groupbardata.padding_x = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "group_bar_decorate_padding_y") == 0) { - config->tabdata.padding_y = CLAMP_INT(atoi(value), 0, 100); + config->groupbardata.padding_y = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "jump_label_decorate_font_desc") == 0) { config->jumplabeldata.font_desc = strdup(value); } else if (strcmp(key, "jump_label_decorate_fg_color") == 0) { @@ -3430,9 +3430,9 @@ void free_config(void) { config.jumplabeldata.font_desc = NULL; } - if (config.tabdata.font_desc) { - free((void *)config.tabdata.font_desc); - config.tabdata.font_desc = NULL; + if (config.groupbardata.font_desc) { + free((void *)config.groupbardata.font_desc); + config.groupbardata.font_desc = NULL; } if (config.tablet_map_to_mon) { @@ -3628,12 +3628,14 @@ void override_config(void) { config.unfocused_opacity = CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); - config.tabdata.border_width = - CLAMP_INT(config.tabdata.border_width, 0, 100); - config.tabdata.corner_radius = - CLAMP_INT(config.tabdata.corner_radius, 0, 100); - config.tabdata.padding_x = CLAMP_INT(config.tabdata.padding_x, 0, 100); - config.tabdata.padding_y = CLAMP_INT(config.tabdata.padding_y, 0, 100); + config.groupbardata.border_width = + CLAMP_INT(config.groupbardata.border_width, 0, 100); + config.groupbardata.corner_radius = + CLAMP_INT(config.groupbardata.corner_radius, 0, 100); + config.groupbardata.padding_x = + CLAMP_INT(config.groupbardata.padding_x, 0, 100); + config.groupbardata.padding_y = + CLAMP_INT(config.groupbardata.padding_y, 0, 100); config.jumplabeldata.border_width = CLAMP_INT(config.jumplabeldata.border_width, 0, 100); @@ -3819,30 +3821,30 @@ void set_value_default() { config.animation_curve_opafadeout[2] = 0.5; config.animation_curve_opafadeout[3] = 0.5; - config.tabdata.fg_color[0] = 0xc4 / 255.0f; - config.tabdata.fg_color[1] = 0x93 / 255.0f; - config.tabdata.fg_color[2] = 0x9d / 255.0f; - config.tabdata.fg_color[3] = 1.0f; - config.tabdata.bg_color[0] = 0x32 / 255.0f; - config.tabdata.bg_color[1] = 0x32 / 255.0f; - config.tabdata.bg_color[2] = 0x32 / 255.0f; - config.tabdata.bg_color[3] = 1.0f; - config.tabdata.focus_fg_color[0] = 0xed / 255.0f; - config.tabdata.focus_fg_color[1] = 0xa6 / 255.0f; - config.tabdata.focus_fg_color[2] = 0xb4 / 255.0f; - config.tabdata.focus_fg_color[3] = 1.0f; - config.tabdata.focus_bg_color[0] = 0x4e / 255.0f; - config.tabdata.focus_bg_color[1] = 0x45 / 255.0f; - config.tabdata.focus_bg_color[2] = 0x3c / 255.0f; - config.tabdata.focus_bg_color[3] = 1.0f; - config.tabdata.border_color[0] = 0x8b / 255.0f; - config.tabdata.border_color[1] = 0xaa / 255.0f; - config.tabdata.border_color[2] = 0x9b / 255.0f; - config.tabdata.border_color[3] = 1.0f; - config.tabdata.border_width = 4; - config.tabdata.corner_radius = 5; - config.tabdata.padding_x = 0; - config.tabdata.padding_y = 0; + config.groupbardata.fg_color[0] = 0xc4 / 255.0f; + config.groupbardata.fg_color[1] = 0x93 / 255.0f; + config.groupbardata.fg_color[2] = 0x9d / 255.0f; + config.groupbardata.fg_color[3] = 1.0f; + config.groupbardata.bg_color[0] = 0x32 / 255.0f; + config.groupbardata.bg_color[1] = 0x32 / 255.0f; + config.groupbardata.bg_color[2] = 0x32 / 255.0f; + config.groupbardata.bg_color[3] = 1.0f; + config.groupbardata.focus_fg_color[0] = 0xed / 255.0f; + config.groupbardata.focus_fg_color[1] = 0xa6 / 255.0f; + config.groupbardata.focus_fg_color[2] = 0xb4 / 255.0f; + config.groupbardata.focus_fg_color[3] = 1.0f; + config.groupbardata.focus_bg_color[0] = 0x4e / 255.0f; + config.groupbardata.focus_bg_color[1] = 0x45 / 255.0f; + config.groupbardata.focus_bg_color[2] = 0x3c / 255.0f; + config.groupbardata.focus_bg_color[3] = 1.0f; + config.groupbardata.border_color[0] = 0x8b / 255.0f; + config.groupbardata.border_color[1] = 0xaa / 255.0f; + config.groupbardata.border_color[2] = 0x9b / 255.0f; + config.groupbardata.border_color[3] = 1.0f; + config.groupbardata.border_width = 4; + config.groupbardata.corner_radius = 5; + config.groupbardata.padding_x = 0; + config.groupbardata.padding_y = 0; config.jumplabeldata.fg_color[0] = 0xc4 / 255.0f; config.jumplabeldata.fg_color[1] = 0x93 / 255.0f; @@ -3982,7 +3984,7 @@ bool parse_config(void) { config.tag_rules_count = 0; config.cursor_theme = NULL; config.jumplabeldata.font_desc = NULL; - config.tabdata.font_desc = NULL; + config.groupbardata.font_desc = NULL; config.tablet_map_to_mon = NULL; strcpy(config.keymode, "default"); @@ -4156,14 +4158,7 @@ void reapply_property(void) { if (!c->isnoborder && !c->isfullscreen) { c->bw = config.borderpx; } - - mango_jump_label_node_apply_config(c->jump_label_node, - &config.jumplabeldata); - mango_group_bar_apply_config(c->group_bar, &config.tabdata); - - wlr_scene_rect_set_color(c->droparea, config.dropcolor); - wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor); - wlr_scene_rect_set_color(c->splitindicator[1], config.splitcolor); + client_set_group_config(c); } } } diff --git a/src/mango.c b/src/mango.c index 29859377..4bd01ba5 100644 --- a/src/mango.c +++ b/src/mango.c @@ -963,6 +963,7 @@ static bool mango_scene_output_commit(struct wlr_scene_output *scene_output, struct wlr_output_state *state); static bool mango_output_commit(Monitor *m); static bool check_tearing_frame_allow(Monitor *m); +static void client_set_group_config(Client *c); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" From 171d859cb570c190f114110cd3ed556067797875 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 27 Jun 2026 16:45:57 +0800 Subject: [PATCH 43/70] feat: add disable option to monitor rule --- docs/configuration/monitors.md | 1 + src/config/parse_config.h | 11 ++++++++++- src/mango.c | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/configuration/monitors.md b/docs/configuration/monitors.md index 15ddfbaa..fba8f407 100644 --- a/docs/configuration/monitors.md +++ b/docs/configuration/monitors.md @@ -33,6 +33,7 @@ monitorrule=name:Values,Parameter:Values,Parameter:Values | `hdr` | integer | 0, 1 | Enable hdr support | | `rr` | integer | 0-7 | Monitor transform | | `custom` | integer | 0, 1 | Enable custom mode (not supported on all displays — may cause black screen) | +| `disable` | integer | 0, 1 | Disable the monitor | ### Transform Values diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 3b7384a3..c1166fc7 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -121,6 +121,7 @@ typedef struct { int32_t vrr; // variable refresh rate int32_t custom; // enable custom mode int32_t hdr; // enable hdr mode + int32_t disable; // prefer disable } ConfigMonitorRule; // 修改后的宏定义 @@ -2128,6 +2129,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->vrr = 0; rule->hdr = 0; rule->custom = 0; + rule->disable = 0; bool parse_error = false; char *token = strtok(value, ","); @@ -2167,6 +2169,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->vrr = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "hdr") == 0) { rule->hdr = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "disable") == 0) { + rule->disable = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "custom") == 0) { rule->custom = CLAMP_INT(atoi(val), 0, 1); } else { @@ -4068,6 +4072,7 @@ void reapply_monitor_rules(void) { vrr = mr->vrr >= 0 ? mr->vrr : 0; custom = mr->custom >= 0 ? mr->custom : 0; m->hdr_enable = mr->hdr >= 0 ? mr->hdr : 0; + m->prefer_disable = mr->disable >= 0 ? mr->disable : 0; (void)apply_rule_to_state(m, mr, &m->pending, vrr, custom); wlr_output_layout_add(output_layout, m->wlr_output, mx, my); @@ -4075,7 +4080,11 @@ void reapply_monitor_rules(void) { } } - wlr_output_state_set_enabled(&m->pending, true); + if (m->prefer_disable) { + wlr_output_state_set_enabled(&m->pending, false); + } else { + wlr_output_state_set_enabled(&m->pending, true); + } if (m->hdr_enable) { output_state_setup_hdr(m, false, &m->pending); diff --git a/src/mango.c b/src/mango.c index 4bd01ba5..5bdcbc2e 100644 --- a/src/mango.c +++ b/src/mango.c @@ -601,6 +601,7 @@ struct Monitor { bool vrr_global_enable; bool is_vrr_opening; bool hdr_enable; + bool prefer_disable; }; typedef struct { @@ -3469,6 +3470,7 @@ void createmon(struct wl_listener *listener, void *data) { m->is_vrr_opening = false; m->hdr_enable = false; + m->prefer_disable = false; m->wlr_output = wlr_output; m->wlr_output->data = m; @@ -3508,6 +3510,7 @@ void createmon(struct wl_listener *listener, void *data) { scale = r->scale; rr = r->rr; m->hdr_enable = r->hdr; + m->prefer_disable = r->disable >= 0 ? r->disable : 0; if (apply_rule_to_state(m, r, &m->pending, vrr, custom)) { custom_monitor_mode = true; @@ -3526,7 +3529,11 @@ void createmon(struct wl_listener *listener, void *data) { LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); - wlr_output_state_set_enabled(&m->pending, 1); + if (m->prefer_disable) { + wlr_output_state_set_enabled(&m->pending, false); + } else { + wlr_output_state_set_enabled(&m->pending, true); + } if (m->hdr_enable) { output_state_setup_hdr(m, false, &m->pending); From b0326d710cf06eee86dfe7ee26d4fb18de445573 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 27 Jun 2026 16:59:29 +0800 Subject: [PATCH 44/70] feat: distinguish dpms dispatch and disable dispatch in monitor --- docs/bindings/keys.md | 11 +++++---- src/config/parse_config.h | 9 +++++++ src/dispatch/bind_declare.h | 3 +++ src/dispatch/bind_define.h | 49 ++++++++++++++++++++++++++++++++++--- src/mango.c | 8 +++--- 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index 53a4f81d..f9dbc4d5 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -177,11 +177,12 @@ bindr=Super,Super_L,spawn,rofi -show run | `setkeymode` | `mode` | Set keymode. | | `switch_keyboard_layout` | `[index]` | Switch keyboard layout. Optional index (0, 1, 2...) to switch to specific layout. | | `setoption` | `key,value` | Set config option temporarily. | -| `disable_monitor` | `monitor_spec` | Shutdown monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | -| `enable_monitor` | `monitor_spec` | Power on monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | -| `toggle_monitor` | `monitor_spec` | Toggle monitor power. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | -| `chvt` | `1-9` | Change virtual terminal (tty, equivalent to using ctrl+alt+Fkeys) | - +| `dpms_off_monitor` | `monitor_spec` | Shutdown monitor power but not remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | +| `dpms_on_monitor` | `monitor_spec` | Turn on monitor power. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | +| `dpms_toggle_monitor` | `monitor_spec` | Toggle monitor power but not remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). +| `disable_monitor` | `monitor_spec` | remove monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | +| `enable_monitor` | `monitor_spec` | add monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | +| `toggle_monitor` | `monitor_spec` | Toggle monitor add/remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | ### Media Controls diff --git a/src/config/parse_config.h b/src/config/parse_config.h index c1166fc7..bf90379b 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1296,6 +1296,15 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "toggle_monitor") == 0) { func = toggle_monitor; (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "dpms_off_monitor") == 0) { + func = dpms_off_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "dpms_on_monitor") == 0) { + func = dpms_on_monitor; + (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "dpms_toggle_monitor") == 0) { + func = dpms_toggle_monitor; + (*arg).v = strdup(arg_value); } else if (strcmp(func_name, "scroller_stack") == 0) { func = scroller_stack; (*arg).i = parse_direction(arg_value); diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 056ae82c..4cebd213 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -73,6 +73,9 @@ int32_t setoption(const Arg *arg); int32_t disable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg); int32_t toggle_monitor(const Arg *arg); +int32_t dpms_off_monitor(const Arg *arg); +int32_t dpms_on_monitor(const Arg *arg); +int32_t dpms_toggle_monitor(const Arg *arg); int32_t scroller_stack(const Arg *arg); int32_t toggle_all_floating(const Arg *arg); int32_t dwindle_toggle_split_direction(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 9312b12c..3a719302 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1949,7 +1949,7 @@ int32_t disable_monitor(const Arg *arg) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, false); mango_output_commit(m); - m->asleep = 1; + m->only_dpms_off = 0; updatemons(NULL, NULL); break; } @@ -1963,7 +1963,7 @@ int32_t enable_monitor(const Arg *arg) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, true); mango_output_commit(m); - m->asleep = 0; + m->only_dpms_off = 0; updatemons(NULL, NULL); break; } @@ -1977,7 +1977,50 @@ int32_t toggle_monitor(const Arg *arg) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, !m->wlr_output->enabled); mango_output_commit(m); - m->asleep = !m->wlr_output->enabled; + m->only_dpms_off = 0; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t dpms_off_monitor(const Arg *arg) { + Monitor *m = NULL; + + wl_list_for_each(m, &mons, link) { + if (match_monitor_spec(arg->v, m)) { + wlr_output_state_set_enabled(&m->pending, false); + mango_output_commit(m); + m->only_dpms_off = 1; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t dpms_on_monitor(const Arg *arg) { + Monitor *m = NULL; + wl_list_for_each(m, &mons, link) { + if (match_monitor_spec(arg->v, m)) { + wlr_output_state_set_enabled(&m->pending, true); + mango_output_commit(m); + m->only_dpms_off = 0; + updatemons(NULL, NULL); + break; + } + } + return 0; +} + +int32_t dpms_toggle_monitor(const Arg *arg) { + Monitor *m = NULL; + wl_list_for_each(m, &mons, link) { + if (match_monitor_spec(arg->v, m)) { + wlr_output_state_set_enabled(&m->pending, !m->wlr_output->enabled); + mango_output_commit(m); + m->only_dpms_off = !m->wlr_output->enabled; updatemons(NULL, NULL); break; } diff --git a/src/mango.c b/src/mango.c index 5bdcbc2e..b35cd43c 100644 --- a/src/mango.c +++ b/src/mango.c @@ -588,7 +588,7 @@ struct Monitor { int32_t isoverview; int32_t is_jump_mode; int32_t is_in_hotarea; - int32_t asleep; + int32_t only_dpms_off; uint32_t visible_clients; uint32_t visible_tiling_clients; uint32_t visible_scroll_tiling_clients; @@ -5179,7 +5179,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test) { /* Ensure displays previously disabled by * wlr-output-power-management-v1 are properly handled*/ - m->asleep = 0; + m->only_dpms_off = 0; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, config_head->state.enabled); @@ -5329,7 +5329,7 @@ void powermgrsetmode(struct wl_listener *listener, void *data) { wlr_output_state_set_enabled(&m->pending, event->mode); mango_output_commit(m); - m->asleep = !event->mode; + m->only_dpms_off = !event->mode; updatemons(NULL, NULL); } @@ -6987,7 +6987,7 @@ void updatemons(struct wl_listener *listener, void *data) { /* First remove from the layout the disabled monitors */ wl_list_for_each(m, &mons, link) { - if (m->wlr_output->enabled || m->asleep) + if (m->wlr_output->enabled || m->only_dpms_off) continue; config_head = wlr_output_configuration_head_v1_create(output_config, m->wlr_output); From 43f3678eb60c7df68e6b0f6599bcb6aaf530e5dd Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 27 Jun 2026 17:28:42 +0800 Subject: [PATCH 45/70] opt: optimize monitorrule setting --- src/config/parse_config.h | 11 ++++------- src/mango.c | 20 ++++++++------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index bf90379b..fe8b7a0e 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -429,7 +429,7 @@ Config config; bool parse_config_file(Config *config, const char *file_path, bool must_exist); bool apply_rule_to_state(Monitor *m, const ConfigMonitorRule *rule, - struct wlr_output_state *state, int vrr, int custom); + struct wlr_output_state *state); bool monitor_matches_rule(Monitor *m, const ConfigMonitorRule *rule); // Helper function to trim whitespace from start and end of a string @@ -4062,7 +4062,7 @@ void reset_blur_params(void) { void reapply_monitor_rules(void) { ConfigMonitorRule *mr; Monitor *m = NULL; - int32_t ji, vrr, custom; + int32_t ji; int32_t mx, my; wl_list_for_each(m, &mons, link) { @@ -4078,12 +4078,9 @@ void reapply_monitor_rules(void) { if (monitor_matches_rule(m, mr)) { mx = mr->x == INT32_MAX ? m->m.x : mr->x; my = mr->y == INT32_MAX ? m->m.y : mr->y; - vrr = mr->vrr >= 0 ? mr->vrr : 0; - custom = mr->custom >= 0 ? mr->custom : 0; - m->hdr_enable = mr->hdr >= 0 ? mr->hdr : 0; - m->prefer_disable = mr->disable >= 0 ? mr->disable : 0; - (void)apply_rule_to_state(m, mr, &m->pending, vrr, custom); + apply_rule_to_state(m, mr, &m->pending); + wlr_output_layout_add(output_layout, m->wlr_output, mx, my); break; } diff --git a/src/mango.c b/src/mango.c index b35cd43c..1c4a6caa 100644 --- a/src/mango.c +++ b/src/mango.c @@ -3408,9 +3408,11 @@ bool monitor_matches_rule(Monitor *m, const ConfigMonitorRule *rule) { /* 将规则中的显示参数应用到 wlr_output_state 中,返回是否设置了自定义模式 */ bool apply_rule_to_state(Monitor *m, const ConfigMonitorRule *rule, - struct wlr_output_state *state, int vrr, int custom) { + struct wlr_output_state *state) { bool mode_set = false; - m->vrr_global_enable = vrr; + m->vrr_global_enable = rule->vrr >= 0 ? rule->vrr : 0; + m->hdr_enable = rule->hdr >= 0 ? rule->hdr : 0; + m->prefer_disable = rule->disable >= 0 ? rule->disable : 0; if (rule->width > 0 && rule->height > 0 && rule->refresh > 0) { struct wlr_output_mode *internal_mode = get_nearest_output_mode( @@ -3418,14 +3420,14 @@ bool apply_rule_to_state(Monitor *m, const ConfigMonitorRule *rule, if (internal_mode) { wlr_output_state_set_mode(state, internal_mode); mode_set = true; - } else if (custom || wlr_output_is_headless(m->wlr_output)) { + } else if (rule->custom || wlr_output_is_headless(m->wlr_output)) { wlr_output_state_set_custom_mode( state, rule->width, rule->height, (int32_t)roundf(rule->refresh * 1000)); mode_set = true; } } - if (vrr) { + if (m->vrr_global_enable) { enable_adaptive_sync(m, state); } else { disable_adaptive_sync(m, state); @@ -3441,7 +3443,7 @@ void createmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; const ConfigMonitorRule *r; uint32_t i; - int32_t ji, vrr, custom; + int32_t ji; Monitor *m = NULL; bool custom_monitor_mode = false; @@ -3505,14 +3507,8 @@ void createmon(struct wl_listener *listener, void *data) { if (monitor_matches_rule(m, r)) { m->m.x = r->x == INT32_MAX ? INT32_MAX : r->x; m->m.y = r->y == INT32_MAX ? INT32_MAX : r->y; - vrr = r->vrr >= 0 ? r->vrr : 0; - custom = r->custom >= 0 ? r->custom : 0; - scale = r->scale; - rr = r->rr; - m->hdr_enable = r->hdr; - m->prefer_disable = r->disable >= 0 ? r->disable : 0; - if (apply_rule_to_state(m, r, &m->pending, vrr, custom)) { + if (apply_rule_to_state(m, r, &m->pending)) { custom_monitor_mode = true; } break; // 只应用第一个匹配规则 From 5686bdc95cbe4e120e444a1ed753727466d6af79 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 27 Jun 2026 19:07:59 +0800 Subject: [PATCH 46/70] opt: optimize dmps state --- src/mango.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mango.c b/src/mango.c index 1c4a6caa..94ae0eb3 100644 --- a/src/mango.c +++ b/src/mango.c @@ -6983,14 +6983,19 @@ void updatemons(struct wl_listener *listener, void *data) { /* First remove from the layout the disabled monitors */ wl_list_for_each(m, &mons, link) { - if (m->wlr_output->enabled || m->only_dpms_off) + if (m->wlr_output->enabled) continue; config_head = wlr_output_configuration_head_v1_create(output_config, m->wlr_output); config_head->state.enabled = 0; + + if (m->only_dpms_off) { + continue; + } /* Remove this output from the layout to avoid cursor enter inside * it */ wlr_output_layout_remove(output_layout, m->wlr_output); + closemon(m); m->m = m->w = (struct wlr_box){0}; } From b73cd4ef906ac5f3167cd6220e7b7463fc472386 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 09:07:13 +0800 Subject: [PATCH 47/70] opt: Adopt a more flexible way to control window hiding --- src/animation/client.h | 4 ++- src/animation/tag.h | 4 ++- src/dispatch/bind_define.h | 4 +-- src/fetch/client.h | 4 ++- src/mango.c | 50 +++++++++----------------------------- 5 files changed, 21 insertions(+), 45 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 09e9cc5f..5898576c 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -819,8 +819,10 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { c->is_clip_to_hide = true; wlr_scene_node_set_enabled(&c->scene->node, false); - } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { + } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon) && + !c->is_logic_hide) { c->is_clip_to_hide = false; + c->is_logic_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); } diff --git a/src/animation/tag.h b/src/animation/tag.h index 9ff5908f..d61429b8 100644 --- a/src/animation/tag.h +++ b/src/animation/tag.h @@ -45,8 +45,10 @@ void set_tagin_animation(Monitor *m, Client *c) { void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { - if (!ISTILED(c) || (!c->is_clip_to_hide || !is_scroller_layout(c->mon))) { + if ((!ISTILED(c) || (!c->is_clip_to_hide || !is_scroller_layout(c->mon))) && + !c->is_logic_hide) { c->is_clip_to_hide = false; + c->is_logic_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); wlr_scene_node_set_enabled(&c->scene_surface->node, true); } diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 3a719302..b0b04e1d 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -242,9 +242,7 @@ int32_t groupleave(const Arg *arg) { tc->group_prev = NULL; tc->group_next = NULL; tc->isgroupfocusing = false; - - wl_list_insert(&rc->link, &tc->link); - wl_list_insert(&rc->flink, &tc->flink); + tc->is_logic_hide = false; if (!rc->group_prev && !rc->group_next) { rc->isgroupfocusing = false; diff --git a/src/fetch/client.h b/src/fetch/client.h index e7d8e673..20be6596 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -188,6 +188,8 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, continue; if (!findfloating && c->isfloating) continue; + if (c->is_logic_hide) + continue; if (c->isunglobal) continue; if (!config.focus_cross_monitor && c->mon != tc->mon) @@ -442,7 +444,7 @@ Client *get_focused_stack_client(Client *sc, Client *custom_focus_client) { return sc; wl_list_for_each(tc, &fstack, flink) { - if (tc->iskilling || tc->isunglobal) + if (tc->iskilling || tc->isunglobal || tc->is_logic_hide) continue; if (!VISIBLEON(tc, sc->mon)) continue; diff --git a/src/mango.c b/src/mango.c index 94ae0eb3..679fa9d3 100644 --- a/src/mango.c +++ b/src/mango.c @@ -126,7 +126,7 @@ (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ !(A)->isunglobal) #define VISIBLEON(C, M) \ - ((C) && (M) && (C)->mon == (M) && \ + ((C) && (M) && (C)->mon == (M) && !(C)->is_logic_hide && \ (((C)->tags & (M)->tagset[(M)->seltags] || (C)->isglobal || \ (C)->isunglobal))) #define LENGTH(X) (sizeof X / sizeof X[0]) @@ -473,6 +473,7 @@ struct Client { Client *group_prev; Client *group_next; bool isgroupfocusing; + bool is_logic_hide; }; typedef struct { @@ -1375,32 +1376,14 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { wlr_scene_node_set_enabled(&w->group_bar->scene_buffer->node, false); } - if (c->link.prev && c->link.next && c->link.prev != &c->link) { - wl_list_remove(&c->link); - } - wl_list_init(&c->link); + wl_list_remove(&c->link); + wl_list_insert(&w->link, &c->link); - if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) { - wl_list_remove(&c->flink); - } - wl_list_init(&c->flink); + wl_list_remove(&c->flink); + wl_list_insert(&w->flink, &c->flink); - if (w->link.prev && w->link.next && w->link.prev != &w->link) { - wl_list_insert(w->link.prev, &c->link); - wl_list_remove(&w->link); - wl_list_init(&w->link); - } - - if (w->flink.prev && w->flink.next && w->flink.prev != &w->flink) { - if (selmon && c == selmon->sel) { - wl_list_insert(&fstack, &c->flink); - } else { - wl_list_insert(w->flink.prev, &c->flink); - } - wl_list_remove(&w->flink); - wl_list_init(&w->flink); - } - /* --------------------------------------------------------------------- */ + w->is_logic_hide = true; + c->is_logic_hide = false; if (w->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_leave(w->foreign_toplevel, @@ -4574,6 +4557,7 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, } void init_client_properties(Client *c) { + c->is_logic_hide = false; c->isgroupfocusing = false; c->group_prev = NULL; c->group_next = NULL; @@ -6902,8 +6886,6 @@ void unmapnotify(struct wl_listener *listener, void *data) { focusclient(focustop(selmon), 1); } else { - bool is_in_group = c->group_next || c->group_prev; - if (c->group_next && !c->isgroupfocusing) { c->group_next->group_prev = c->group_prev; } @@ -6915,19 +6897,9 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->group_next = NULL; c->group_prev = NULL; - if (!c->swallowing && (!is_in_group || c->isgroupfocusing)) { - if (c->link.prev && c->link.next && c->link.prev != &c->link) { - wl_list_remove(&c->link); - wl_list_init(&c->link); - } - } + wl_list_remove(&c->link); setmon(c, NULL, 0, true); - if (!c->swallowing && (!is_in_group || c->isgroupfocusing)) { - if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) { - wl_list_remove(&c->flink); - wl_list_init(&c->flink); - } - } + wl_list_remove(&c->flink); } if (c->foreign_toplevel) { From 3b3170ef7d94e46f6c25198f7499a5ea6df1eeb0 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 09:58:13 +0800 Subject: [PATCH 48/70] opt: Ensure that the client remains in the linked list until it is destroyed --- src/action/client.h | 4 ++-- src/common/util.c | 26 ++++++++++++++++++++++++++ src/common/util.h | 4 +++- src/dispatch/bind_define.h | 6 ++---- src/layout/scroll.h | 18 ++++++------------ src/mango.c | 32 ++++++++++---------------------- 6 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index b12f8cef..6a180c8c 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -46,8 +46,8 @@ static void finish_exchange_arrange_and_focus(Client *c1, Client *c2, } else { arrange(c1->mon, false, false); } - wl_list_remove(&c2->flink); - wl_list_insert(&c1->flink, &c2->flink); + + wl_list_safe_reinsert_next(&c1->flink, &c2->flink); if (config.warpcursor) warp_cursor(c1); diff --git a/src/common/util.c b/src/common/util.c index 8e562b19..fb5cb93c 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -205,4 +205,30 @@ void wl_list_swap(struct wl_list *l1, struct wl_list *l2) { tmp2_prev->next = l1; tmp2_next->prev = l1; } +} + +void wl_list_safe_reinsert_prev(struct wl_list *l1, struct wl_list *l2) { + if (!l1 || !l2) + return; + if (l1 == l2) + return; + if (l1->prev == l2) + return; + + wl_list_remove(l2); + wl_list_init(l2); + wl_list_insert(l1->prev, l2); +} + +void wl_list_safe_reinsert_next(struct wl_list *l1, struct wl_list *l2) { + if (!l1 || !l2) + return; + if (l1 == l2) + return; + if (l1->next == l2) + return; + + wl_list_remove(l2); + wl_list_init(l2); + wl_list_insert(l1, l2); } \ No newline at end of file diff --git a/src/common/util.h b/src/common/util.h index c7f83f2b..52b509a7 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -12,4 +12,6 @@ char *join_strings(char *arr[], const char *sep); char *join_strings_with_suffix(char *arr[], const char *suffix, const char *sep); char *string_printf(const char *fmt, ...); -void wl_list_swap(struct wl_list *l1, struct wl_list *l2); \ No newline at end of file +void wl_list_swap(struct wl_list *l1, struct wl_list *l2); +void wl_list_safe_reinsert_prev(struct wl_list *l1, struct wl_list *l2); +void wl_list_safe_reinsert_next(struct wl_list *l1, struct wl_list *l2); \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index b0b04e1d..6215b962 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -2070,13 +2070,11 @@ int32_t scroller_apply_stack(Client *c, Client *target_client, if (direction == LEFT || direction == UP) { if (c != stack_head) { - wl_list_remove(&c->link); - wl_list_insert(stack_head->link.prev, &c->link); + wl_list_safe_reinsert_prev(&stack_head->link, &c->link); } } else if (direction == RIGHT || direction == DOWN) { if (c != stack_tail) { - wl_list_remove(&c->link); - wl_list_insert(&stack_tail->link, &c->link); + wl_list_safe_reinsert_next(&stack_head->link, &c->link); } } sync_scroller_state_to_clients(m, tag); diff --git a/src/layout/scroll.h b/src/layout/scroll.h index dbdfe919..6d290607 100644 --- a/src/layout/scroll.h +++ b/src/layout/scroll.h @@ -785,16 +785,14 @@ void scroller_insert_stack(Client *c, Client *target_client, if (tnode->prev_in_stack) tnode->prev_in_stack->next_in_stack = newnode; tnode->prev_in_stack = newnode; - wl_list_remove(&c->link); - wl_list_insert(tnode->client->link.prev, &c->link); + wl_list_safe_reinsert_prev(&tnode->client->link, &c->link); } else { newnode->prev_in_stack = tnode; newnode->next_in_stack = tnode->next_in_stack; if (tnode->next_in_stack) tnode->next_in_stack->prev_in_stack = newnode; tnode->next_in_stack = newnode; - wl_list_remove(&c->link); - wl_list_insert(&tnode->client->link, &c->link); + wl_list_safe_reinsert_next(&tnode->client->link, &c->link); } /* 处理堆叠头部的全屏/最大化状态*/ @@ -833,13 +831,11 @@ void scroller_drop_tile(Client *c, Client *closest, int vertical) { return; } else if (closest->drop_direction == UP) { if (c != stack_head) { - wl_list_remove(&c->link); - wl_list_insert(stack_head->link.prev, &c->link); + wl_list_safe_reinsert_prev(&stack_head->link, &c->link); } } else if (closest->drop_direction == DOWN) { if (c != stack_tail) { - wl_list_remove(&c->link); - wl_list_insert(&stack_tail->link, &c->link); + wl_list_safe_reinsert_next(&stack_head->link, &c->link); } } } else { @@ -853,13 +849,11 @@ void scroller_drop_tile(Client *c, Client *closest, int vertical) { return; } else if (closest->drop_direction == LEFT) { if (c != stack_head) { - wl_list_remove(&c->link); - wl_list_insert(stack_head->link.prev, &c->link); + wl_list_safe_reinsert_prev(&stack_head->link, &c->link); } } else if (closest->drop_direction == RIGHT) { if (c != stack_tail) { - wl_list_remove(&c->link); - wl_list_insert(&stack_tail->link, &c->link); + wl_list_safe_reinsert_next(&stack_head->link, &c->link); } } } diff --git a/src/mango.c b/src/mango.c index 679fa9d3..47aa9497 100644 --- a/src/mango.c +++ b/src/mango.c @@ -1290,8 +1290,7 @@ void show_scratchpad(Client *c) { } c->oldtags = c->mon->tagset[c->mon->seltags]; - wl_list_remove(&c->link); // 从原来位置移除 - wl_list_insert(clients.prev->next, &c->link); // 插入开头 + wl_list_safe_reinsert_next(&clients, &c->link); show_hide_client(c); setborder_color(c); } @@ -1376,11 +1375,9 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { wlr_scene_node_set_enabled(&w->group_bar->scene_buffer->node, false); } - wl_list_remove(&c->link); - wl_list_insert(&w->link, &c->link); + wl_list_safe_reinsert_next(&w->link, &c->link); - wl_list_remove(&c->flink); - wl_list_insert(&w->flink, &c->flink); + wl_list_safe_reinsert_prev(&w->flink, &c->flink); w->is_logic_hide = true; c->is_logic_hide = false; @@ -1871,11 +1868,7 @@ void applyrules(Client *c) { (!c->istagsilent || !newtags || newtags & mon->tagset[mon->seltags]); if (!should_init_get_focus) { - if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) { - wl_list_remove(&c->flink); - wl_list_init(&c->flink); - } - wl_list_insert(fstack.prev, &c->flink); + wl_list_safe_reinsert_prev(&fstack, &c->flink); } setmon(c, mon, newtags, should_init_get_focus); @@ -1911,11 +1904,7 @@ void applyrules(Client *c) { } if (c->isfloating && !c->iscustompos && !c->isnamedscratchpad) { - if (c->link.prev && c->link.next && c->link.prev != &c->link) { - wl_list_remove(&c->link); - wl_list_init(&c->link); - } - wl_list_insert(clients.prev, &c->link); + wl_list_safe_reinsert_prev(&clients, &c->link); set_float_malposition(c); } @@ -2384,8 +2373,7 @@ void place_drag_tile_client(Client *c) { if (closest->drop_direction == UNDIR) { setfloating(c, 0); - wl_list_remove(&c->link); - wl_list_insert(closest->link.prev, &c->link); + wl_list_safe_reinsert_prev(&closest->link, &c->link); arrange(closest->mon, false, false); return; } @@ -2412,11 +2400,9 @@ void place_drag_tile_client(Client *c) { } if (closest->drop_direction == LEFT || closest->drop_direction == UP) { - wl_list_remove(&c->link); - wl_list_insert(closest->link.prev, &c->link); + wl_list_safe_reinsert_prev(&closest->link, &c->link); } else { - wl_list_remove(&c->link); - wl_list_insert(&closest->link, &c->link); + wl_list_safe_reinsert_next(&closest->link, &c->link); } } @@ -4657,6 +4643,8 @@ void init_client_properties(Client *c) { c->animation.overining = false; c->animation.tagouting = false; c->animation.tagouted = false; + wl_list_init(&c->link); + wl_list_init(&c->flink); } void // old fix to 0.5 From 76de8206dd5dfaf6034481538dbe213e1bda1443 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 10:06:28 +0800 Subject: [PATCH 49/70] fix: Fix ignoring hidden clients --- src/animation/client.h | 3 +-- src/animation/tag.h | 3 +-- src/fetch/client.h | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 5898576c..555bbf51 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -819,8 +819,7 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { c->is_clip_to_hide = true; wlr_scene_node_set_enabled(&c->scene->node, false); - } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon) && - !c->is_logic_hide) { + } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { c->is_clip_to_hide = false; c->is_logic_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); diff --git a/src/animation/tag.h b/src/animation/tag.h index d61429b8..4395fa67 100644 --- a/src/animation/tag.h +++ b/src/animation/tag.h @@ -45,8 +45,7 @@ void set_tagin_animation(Monitor *m, Client *c) { void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { - if ((!ISTILED(c) || (!c->is_clip_to_hide || !is_scroller_layout(c->mon))) && - !c->is_logic_hide) { + if (!ISTILED(c) || (!c->is_clip_to_hide || !is_scroller_layout(c->mon))) { c->is_clip_to_hide = false; c->is_logic_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); diff --git a/src/fetch/client.h b/src/fetch/client.h index 20be6596..c67b9022 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -184,11 +184,11 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, break; wl_list_for_each(c, &clients, link) { - if (!c || c == tc) + if (!c || !c->mon || c == tc) continue; if (!findfloating && c->isfloating) continue; - if (c->is_logic_hide) + if (!VISIBLEON(c, c->mon)) continue; if (c->isunglobal) continue; @@ -444,7 +444,7 @@ Client *get_focused_stack_client(Client *sc, Client *custom_focus_client) { return sc; wl_list_for_each(tc, &fstack, flink) { - if (tc->iskilling || tc->isunglobal || tc->is_logic_hide) + if (tc->iskilling || tc->isunglobal) continue; if (!VISIBLEON(tc, sc->mon)) continue; From e914d76acd7e9fc38bbbff83f4c1405d7bf5fed4 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 12:07:45 +0800 Subject: [PATCH 50/70] opt: optimize swallow in ipc action --- docs/window-management/rules.md | 2 +- src/action/client.h | 2 +- src/animation/client.h | 6 ++-- src/dispatch/bind_define.h | 7 ++++ src/ext-protocol/foreign-toplevel.h | 6 ++-- src/fetch/client.h | 8 ++--- src/ipc/ipc.h | 4 +++ src/mango.c | 56 ++++++++++++++++------------- 8 files changed, 55 insertions(+), 36 deletions(-) diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md index 27ff8ab0..27216942 100644 --- a/docs/window-management/rules.md +++ b/docs/window-management/rules.md @@ -141,7 +141,7 @@ windowrule=offsetx:20,offsety:-30,width:800,height:600,appid:alacritty # Send to specific tag and monitor windowrule=tags:9,monitor:HDMI-A-1,appid:discord -# Terminal swallowing setup +# Terminal swallowdby setup windowrule=isterm:1,appid:st windowrule=noswallow:1,appid:foot diff --git a/src/action/client.h b/src/action/client.h index 6a180c8c..dce78d1e 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -84,7 +84,7 @@ void client_active(Client *c) { return; } - if (c->swallowing || !c->mon) + if (c->swallowdby || !c->mon) return; if (c->isminimized) { diff --git a/src/animation/client.h b/src/animation/client.h index 555bbf51..937ce3b6 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1555,11 +1555,11 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->pending = c->geom; } - if (c->swallowedby && c->animation.action == OPEN) { - c->animainit_geom = c->swallowedby->animation.current; + if (c->swallowing && c->animation.action == OPEN) { + c->animainit_geom = c->swallowing->animation.current; } - if (c->swallowing) { + if (c->swallowdby) { c->animainit_geom = c->geom; } diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 6215b962..4899808d 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -2196,6 +2196,13 @@ int32_t focusid(const Arg *arg) { return 0; Client *c = arg->tc; + + if (c->swallowdby) + return 0; + + if (c->group_next || c->group_prev) + client_focus_group_member(c); + client_active(c); return 0; } \ No newline at end of file diff --git a/src/ext-protocol/foreign-toplevel.h b/src/ext-protocol/foreign-toplevel.h index a8c49c77..eb5e7f0b 100644 --- a/src/ext-protocol/foreign-toplevel.h +++ b/src/ext-protocol/foreign-toplevel.h @@ -12,7 +12,7 @@ void handle_foreign_maximize_request(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, foreign_maximize_request); struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; - if (c->swallowing || !c->mon) + if (c->swallowdby || !c->mon) return; if (c->ismaximizescreen && !event->maximized) { @@ -30,7 +30,7 @@ void handle_foreign_minimize_request(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, foreign_minimize_request); struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; - if (c->swallowing || !c->mon) + if (c->swallowdby || !c->mon) return; if (!c->isminimized && event->minimized) { @@ -55,7 +55,7 @@ void handle_foreign_fullscreen_request(struct wl_listener *listener, Client *c = wl_container_of(listener, c, foreign_fullscreen_request); struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; - if (c->swallowing || !c->mon) + if (c->swallowdby || !c->mon) return; if (c->isfullscreen && !event->fullscreen) { diff --git a/src/fetch/client.h b/src/fetch/client.h index c67b9022..869f3da3 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -31,7 +31,7 @@ Client *termforwin(Client *w) { return NULL; wl_list_for_each(c, &fstack, flink) { - if (c->isterm && !c->swallowing && c->pid && + if (c->isterm && !c->swallowdby && c->pid && isdescprocess(c->pid, w->pid)) { return c; } @@ -48,9 +48,9 @@ Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title) { continue; } - if (c->swallowedby) { - appid = client_get_appid(c->swallowedby); - title = client_get_title(c->swallowedby); + if (c->swallowing) { + appid = client_get_appid(c->swallowing); + title = client_get_title(c->swallowing); } else { appid = client_get_appid(c); title = client_get_title(c); diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h index 9ac1a7b2..ee93ed23 100644 --- a/src/ipc/ipc.h +++ b/src/ipc/ipc.h @@ -148,6 +148,10 @@ static cJSON *build_client_json(Client *c) { cJSON_AddStringToObject(obj, "monitor", c->mon ? c->mon->wlr_output->name : ""); cJSON_AddItemToObject(obj, "tags", tags_mask_to_array(c->tags)); + cJSON_AddBoolToObject(obj, "is_swallowing", c->swallowing ? true : false); + cJSON_AddBoolToObject(obj, "is_swallowedby", c->swallowdby ? true : false); + cJSON_AddBoolToObject(obj, "is_group", c->group_prev || c->group_next); + cJSON_AddBoolToObject(obj, "is_visible", c->mon && VISIBLEON(c, c->mon)); cJSON_AddBoolToObject(obj, "is_focused", c->isfocusing); cJSON_AddBoolToObject(obj, "is_fullscreen", c->isfullscreen); cJSON_AddBoolToObject(obj, "is_floating", c->isfloating); diff --git a/src/mango.c b/src/mango.c index 47aa9497..12511672 100644 --- a/src/mango.c +++ b/src/mango.c @@ -432,7 +432,7 @@ struct Client { int32_t force_fakemaximize; int32_t force_tiled_state; pid_t pid; - Client *swallowing, *swallowedby; + Client *swallowdby, *swallowing; bool is_clip_to_hide; bool drag_to_tile; bool scratchpad_switching_mon; @@ -1719,7 +1719,7 @@ void client_reset_mon_tags(Client *c, Monitor *mon, uint32_t newtags) { } void check_match_tag_floating_rule(Client *c, Monitor *mon) { - if (c->tags && !c->isfloating && mon && !c->swallowedby && + if (c->tags && !c->isfloating && mon && !c->swallowing && mon->pertag->open_as_floating[get_tags_first_tag_num(c->tags)]) { c->isfloating = 1; } @@ -1849,8 +1849,8 @@ void applyrules(Client *c) { !c->surface.xdg->initial_commit) { Client *p = termforwin(c); if (p && !p->isminimized) { - c->swallowedby = p; - p->swallowing = c; + c->swallowing = p; + p->swallowdby = c; client_replace(c, p, false); @@ -4568,8 +4568,8 @@ void init_client_properties(Client *c) { c->overview_ismaximizescreenbak = 0; c->overview_isfloatingbak = 0; c->pid = 0; + c->swallowdby = NULL; c->swallowing = NULL; - c->swallowedby = NULL; c->ismaster = 0; c->old_ismaster = 0; c->isleftstack = 0; @@ -6798,16 +6798,11 @@ void unmapnotify(struct wl_listener *listener, void *data) { (!c->mon || VISIBLEON(c, c->mon))) init_fadeout_client(c); - if (c->ext_foreign_toplevel) { - wlr_ext_foreign_toplevel_handle_v1_destroy(c->ext_foreign_toplevel); - c->ext_foreign_toplevel = NULL; - } - // If the client is in a stack, remove it from the stack - if (c->swallowedby) { - c->swallowedby->mon = c->mon; - client_replace(c->swallowedby, c, false); + if (c->swallowing) { + c->swallowing->mon = c->mon; + client_replace(c->swallowing, c, false); } else if ((c->group_next || c->group_prev) && c->isgroupfocusing) { Client *group_replacement = c->group_next ? c->group_next : c->group_prev; @@ -6840,9 +6835,9 @@ void unmapnotify(struct wl_listener *listener, void *data) { } if (c->mon && c->mon == selmon) { - if (next_node && !c->swallowedby) { + if (next_node && !c->swallowing) { nextfocus = next_node->client; - } else if (prev_node && !c->swallowedby) { + } else if (prev_node && !c->swallowing) { nextfocus = prev_node->client; } else { nextfocus = focustop(selmon); @@ -6895,30 +6890,43 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->foreign_toplevel = NULL; } - if (c->swallowedby) { - setmaximizescreen(c->swallowedby, c->ismaximizescreen, true); - setfullscreen(c->swallowedby, c->isfullscreen, true); - c->swallowedby->swallowing = NULL; - c->swallowedby = NULL; + if (c->ext_foreign_toplevel) { + wlr_ext_foreign_toplevel_handle_v1_destroy(c->ext_foreign_toplevel); + c->ext_foreign_toplevel = NULL; } if (c->swallowing) { - c->swallowing->swallowedby = NULL; + setmaximizescreen(c->swallowing, c->ismaximizescreen, true); + setfullscreen(c->swallowing, c->isfullscreen, true); + c->swallowing->swallowdby = NULL; c->swallowing = NULL; } + if (c->swallowdby) { + c->swallowdby->swallowing = NULL; + c->swallowdby = NULL; + } + if (c->jump_label_node) { mango_jump_label_node_destroy(c->jump_label_node); c->jump_label_node = NULL; } + if (c->group_bar) { mango_group_bar_destroy(c->group_bar); c->group_bar = NULL; } - wlr_scene_node_destroy(&c->image_capture_scene_surface->buffer->node); - wlr_scene_node_destroy(&c->image_capture_scene->tree.node); + if (c->image_capture_tree) { + wlr_scene_node_destroy(&c->image_capture_tree->node); + c->image_capture_tree = NULL; + } + if (c->image_capture_scene) { + wlr_scene_node_destroy(&c->image_capture_scene->tree.node); + c->image_capture_scene = NULL; + } + c->image_capture_source = NULL; init_client_properties(c); wlr_scene_node_destroy(&c->scene->node); @@ -7310,7 +7318,7 @@ void activatex11(struct wl_listener *listener, void *data) { if (!c || c->iskilling || !c->foreign_toplevel || client_is_unmanaged(c)) return; - if (c && c->swallowing) + if (c && c->swallowdby) return; if (c->isminimized) { From 53c634dcb4a473d5c8a1cdfcf1bd5ab5245aecae Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 13:33:18 +0800 Subject: [PATCH 51/70] fix: groupbar hide judgement --- src/action/client.h | 8 ++------ src/animation/client.h | 9 ++++----- src/layout/arrange.h | 2 +- src/mango.c | 8 ++++++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index dce78d1e..b4eaaf27 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -57,11 +57,7 @@ void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) { if (!ISFAKETILED(c)) return; - if (c->isfullscreen && c->group_bar) { - wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); - } - - if (!c->mon->isoverview && c->group_bar && !c->isfullscreen && + if (!c->mon->isoverview && !c->isfullscreen && (c->group_next || c->group_prev)) { geo.y = geo.y + config.group_bar_height; geo.height -= config.group_bar_height; @@ -189,7 +185,7 @@ void client_check_tab_node_visible(Client *c) { Client *cur = head; while (cur) { if (!c->mon->isoverview && cur->group_bar && - (cur->group_next || cur->group_prev) && VISIBLEON(c, c->mon) && + (cur->group_next || cur->group_prev) && TAGMATCH(c, c->mon) && ISNORMAL(c) && !c->isfullscreen) { wlr_scene_node_set_enabled(&cur->group_bar->scene_buffer->node, true); diff --git a/src/animation/client.h b/src/animation/client.h index 937ce3b6..ef75ca68 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -417,12 +417,15 @@ void client_draw_title(Client *c) { if (!c || !c->group_bar) return; - if (!c->group_next && !c->group_prev && c->group_bar && + if (!c->group_next && !c->group_prev && c->group_bar->scene_buffer->node.enabled) { wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); return; } + if (c->is_logic_hide) + return; + if (!c->group_next && !c->group_prev) return; @@ -555,10 +558,6 @@ void global_draw_group_bar(Client *c, int32_t x, int32_t y, int32_t width, if (!c->group_bar) return; - if (height <= 0) { - wlr_scene_node_set_enabled(&c->group_bar->scene_buffer->node, false); - } - wlr_scene_node_set_position(&c->group_bar->scene_buffer->node, x, y); mango_group_bar_set_size(c->group_bar, width, height); } diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 803970da..7f41f378 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -1132,7 +1132,7 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, client_add_jump_label_node(c); } - if (c->group_bar && c->group_bar->scene_buffer->node.enabled) { + if (c->group_bar->scene_buffer->node.enabled) { client_check_tab_node_visible(c); } diff --git a/src/mango.c b/src/mango.c index 12511672..4d1363d0 100644 --- a/src/mango.c +++ b/src/mango.c @@ -129,6 +129,10 @@ ((C) && (M) && (C)->mon == (M) && !(C)->is_logic_hide && \ (((C)->tags & (M)->tagset[(M)->seltags] || (C)->isglobal || \ (C)->isunglobal))) + +#define TAGMATCH(C, M) \ + ((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) @@ -5731,7 +5735,7 @@ void reset_maximizescreen_size(Client *c) { geom.width = c->mon->w.width - 2 * config.gappoh; geom.height = c->mon->w.height - 2 * config.gappov; - if ((c->group_next || c->group_prev) && c->group_bar) { + if (c->group_next || c->group_prev) { geom.height -= config.group_bar_height; geom.y += config.group_bar_height; } @@ -5778,7 +5782,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) { maximizescreen_box.width = c->mon->w.width - 2 * config.gappoh; maximizescreen_box.height = c->mon->w.height - 2 * config.gappov; - if ((c->group_next || c->group_prev) && c->group_bar) { + if (c->group_next || c->group_prev) { maximizescreen_box.height -= config.group_bar_height; maximizescreen_box.y += config.group_bar_height; } From eafe63815c832835932a0631ad53216e2267942d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 13:51:40 +0800 Subject: [PATCH 52/70] opt: logic hide client not need draw frame --- src/mango.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mango.c b/src/mango.c index 4d1363d0..c8b6c474 100644 --- a/src/mango.c +++ b/src/mango.c @@ -5376,6 +5376,9 @@ void rendermon(struct wl_listener *listener, void *data) { } wl_list_for_each_safe(c, tmp, &fadeout_clients, fadeout_link) { + if (c->is_logic_hide) + continue; + need_more_frames = client_draw_fadeout_frame(c) || need_more_frames; } @@ -5385,6 +5388,9 @@ void rendermon(struct wl_listener *listener, void *data) { // 绘制客户端 wl_list_for_each(c, &clients, link) { + if (c->is_logic_hide) + continue; + need_more_frames = client_draw_frame(c) || need_more_frames; if (!config.animations && !grabc && c->configure_serial && client_is_rendered_on_mon(c, m)) { From 676f82860a5c1e8abf48432f190255d87edd5d08 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 15:26:08 +0800 Subject: [PATCH 53/70] fix: miss destroy groupbar node when it not focusing --- src/draw/text-node.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/draw/text-node.c b/src/draw/text-node.c index c57272f1..7e65b17e 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -524,6 +524,8 @@ void mango_group_bar_destroy(MangoGroupBar *node) { if (node->measure_cr) cairo_destroy(node->measure_cr); + wlr_scene_node_destroy(&node->scene_buffer->node); + g_free(node->font_desc); g_free(node->cached_text); g_free(node->cached_font_desc); From 3aa2206821d9d0f87091bfecb9309108017508ee Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 29 Jun 2026 16:41:23 +0800 Subject: [PATCH 54/70] fix: fix swallow conflict group --- src/action/client.h | 2 +- src/mango.c | 100 ++++++++++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index b4eaaf27..00580173 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -160,7 +160,7 @@ void client_focus_group_member(Client *c) { cur_focusing->isgroupfocusing = false; c->mon = cur_focusing->mon; - client_replace(c, cur_focusing, true); + client_replace(c, cur_focusing, true, false); mango_group_bar_set_focus(cur_focusing->group_bar, false); c->isgroupfocusing = true; diff --git a/src/mango.c b/src/mango.c index c8b6c474..62819e55 100644 --- a/src/mango.c +++ b/src/mango.c @@ -823,7 +823,8 @@ static void clear_fullscreen_flag(Client *c); static pid_t getparentprocess(pid_t p); static int32_t isdescprocess(pid_t p, pid_t c); static Client *termforwin(Client *w); -static void client_replace(Client *c, Client *w, bool isgroupaction); +static void client_replace(Client *c, Client *w, bool is_group_change_member, + bool is_swallow); static void warp_cursor_to_selmon(Monitor *m); uint32_t want_restore_fullscreen(Client *target_client); @@ -1307,7 +1308,8 @@ void client_update_oldmonname_record(Client *c, Monitor *m) { c->oldmonname[sizeof(c->oldmonname) - 1] = '\0'; } -void client_replace(Client *c, Client *w, bool isgroupaction) { +void client_replace(Client *c, Client *w, bool is_group_change_member, + bool is_swallow) { c->bw = w->bw; c->isfloating = w->isfloating; c->isurgent = w->isurgent; @@ -1324,41 +1326,61 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { c->overview_backup_geom = w->overview_backup_geom; c->animation.current = w->animation.current; c->stack_proportion = w->stack_proportion; + c->is_logic_hide = w->is_logic_hide; - if (!isgroupaction) { - - if (w->group_prev == c) { - c->group_next = w->group_next; - if (w->group_next) { - w->group_next->group_prev = c; - } - } else if (w->group_next == c) { - c->group_prev = w->group_prev; - if (w->group_prev) { - w->group_prev->group_next = c; - } - } else { - c->group_prev = w->group_prev; - c->group_next = w->group_next; - if (w->group_prev) { - w->group_prev->group_next = c; - } - - if (w->group_next) { - w->group_next->group_prev = c; - } + if (is_swallow) { + c->group_prev = w->group_prev; + c->group_next = w->group_next; + if (w->group_prev) { + w->group_prev->group_next = c; } + if (w->group_next) { + w->group_next->group_prev = c; + } + w->group_next = NULL; + w->group_prev = NULL; - if (!c->group_prev && !c->group_next) { - c->isgroupfocusing = false; - } else { + if (!w->is_logic_hide) { c->isgroupfocusing = w->isgroupfocusing; + } else { + c->isgroupfocusing = false; } - if (c->isgroupfocusing) - mango_group_bar_set_focus(c->group_bar, true); + } else { + if (!is_group_change_member) { + if (w->group_prev == c) { + c->group_next = w->group_next; + if (w->group_next) { + w->group_next->group_prev = c; + } + } else if (w->group_next == c) { + c->group_prev = w->group_prev; + if (w->group_prev) { + w->group_prev->group_next = c; + } + } else { + c->group_prev = w->group_prev; + c->group_next = w->group_next; + if (w->group_prev) { + w->group_prev->group_next = c; + } + + if (w->group_next) { + w->group_next->group_prev = c; + } + } + + if (!c->group_prev && !c->group_next) { + c->isgroupfocusing = false; + } else { + c->isgroupfocusing = w->isgroupfocusing; + } + } } + w->is_logic_hide = true; + mango_group_bar_set_focus(c->group_bar, c->isgroupfocusing); + if (w->overview_scene_surface) { wlr_scene_node_reparent(&w->blur->node, w->overview_scene_surface); @@ -1375,16 +1397,18 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { overview_backup_surface(c); } - if (w->group_bar && !isgroupaction) { + if (w->group_bar && !is_group_change_member) { wlr_scene_node_set_enabled(&w->group_bar->scene_buffer->node, false); } wl_list_safe_reinsert_next(&w->link, &c->link); - wl_list_safe_reinsert_prev(&w->flink, &c->flink); + wlr_scene_node_set_enabled(&w->scene->node, false); - w->is_logic_hide = true; - c->is_logic_hide = false; + if (!c->is_logic_hide) { + wlr_scene_node_set_enabled(&c->scene->node, true); + wlr_scene_node_set_enabled(&c->scene_surface->node, true); + } if (w->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_leave(w->foreign_toplevel, @@ -1393,10 +1417,6 @@ void client_replace(Client *c, Client *w, bool isgroupaction) { w->foreign_toplevel = NULL; } - wlr_scene_node_set_enabled(&w->scene->node, false); - wlr_scene_node_set_enabled(&c->scene->node, true); - wlr_scene_node_set_enabled(&c->scene_surface->node, true); - if (!c->foreign_toplevel && c->mon) add_foreign_toplevel(c); else if (c->foreign_toplevel && c->mon) { @@ -1856,7 +1876,7 @@ void applyrules(Client *c) { c->swallowing = p; p->swallowdby = c; - client_replace(c, p, false); + client_replace(c, p, false, true); mon = p->mon; newtags = p->tags; @@ -6812,12 +6832,12 @@ void unmapnotify(struct wl_listener *listener, void *data) { if (c->swallowing) { c->swallowing->mon = c->mon; - client_replace(c->swallowing, c, false); + client_replace(c->swallowing, c, false, true); } else if ((c->group_next || c->group_prev) && c->isgroupfocusing) { Client *group_replacement = c->group_next ? c->group_next : c->group_prev; group_replacement->mon = c->mon; - client_replace(group_replacement, c, false); + client_replace(group_replacement, c, false, false); } else { scroller_remove_client(c); dwindle_remove_client(c); From 4ddc713881b18473db9f29a0a45a9707ab718ceb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 1 Jul 2026 07:45:33 +0800 Subject: [PATCH 55/70] opt: simplified group code --- src/action/client.h | 29 ++++++++++++++++++ src/dispatch/bind_define.h | 11 +------ src/mango.c | 61 ++------------------------------------ 3 files changed, 33 insertions(+), 68 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 00580173..98f986ad 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -277,4 +277,33 @@ void client_set_group_config(Client *c) { mango_group_bar_apply_config(cur->group_bar, &config.groupbardata); cur = cur->group_next; } +} + +void client_group_detach(Client *c) { + if (c->group_prev) + c->group_prev->group_next = c->group_next; + if (c->group_next) + c->group_next->group_prev = c->group_prev; + c->group_prev = NULL; + c->group_next = NULL; + c->isgroupfocusing = false; +} + +void client_group_replace(Client *old, Client *new) { + client_group_detach(new); + + new->group_prev = old->group_prev; + new->group_next = old->group_next; + if (old->group_prev) + old->group_prev->group_next = new; + if (old->group_next) + old->group_next->group_prev = new; + old->group_prev = NULL; + old->group_next = NULL; + + if (old->is_logic_hide || (!new->group_prev && !new->group_next)) { + new->isgroupfocusing = false; + } else { + new->isgroupfocusing = old->isgroupfocusing; + } } \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 4899808d..63f6206e 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -230,17 +230,8 @@ int32_t groupleave(const Arg *arg) { Client *rc = tc->group_next ? tc->group_next : tc->group_prev; client_focus_group_member(rc); + client_group_detach(tc); - if (tc->group_prev) { - tc->group_prev->group_next = tc->group_next; - } - - if (tc->group_next) { - tc->group_next->group_prev = tc->group_prev; - } - - tc->group_prev = NULL; - tc->group_next = NULL; tc->isgroupfocusing = false; tc->is_logic_hide = false; diff --git a/src/mango.c b/src/mango.c index 62819e55..ac660940 100644 --- a/src/mango.c +++ b/src/mango.c @@ -1328,54 +1328,8 @@ void client_replace(Client *c, Client *w, bool is_group_change_member, c->stack_proportion = w->stack_proportion; c->is_logic_hide = w->is_logic_hide; - if (is_swallow) { - c->group_prev = w->group_prev; - c->group_next = w->group_next; - if (w->group_prev) { - w->group_prev->group_next = c; - } - if (w->group_next) { - w->group_next->group_prev = c; - } - w->group_next = NULL; - w->group_prev = NULL; - - if (!w->is_logic_hide) { - c->isgroupfocusing = w->isgroupfocusing; - } else { - c->isgroupfocusing = false; - } - - } else { - if (!is_group_change_member) { - if (w->group_prev == c) { - c->group_next = w->group_next; - if (w->group_next) { - w->group_next->group_prev = c; - } - } else if (w->group_next == c) { - c->group_prev = w->group_prev; - if (w->group_prev) { - w->group_prev->group_next = c; - } - } else { - c->group_prev = w->group_prev; - c->group_next = w->group_next; - if (w->group_prev) { - w->group_prev->group_next = c; - } - - if (w->group_next) { - w->group_next->group_prev = c; - } - } - - if (!c->group_prev && !c->group_next) { - c->isgroupfocusing = false; - } else { - c->isgroupfocusing = w->isgroupfocusing; - } - } + if (is_swallow || !is_group_change_member) { + client_group_replace(w, c); } w->is_logic_hide = true; @@ -6899,16 +6853,7 @@ void unmapnotify(struct wl_listener *listener, void *data) { focusclient(focustop(selmon), 1); } else { - if (c->group_next && !c->isgroupfocusing) { - c->group_next->group_prev = c->group_prev; - } - - if (c->group_prev && !c->isgroupfocusing) { - c->group_prev->group_next = c->group_next; - } - - c->group_next = NULL; - c->group_prev = NULL; + client_group_detach(c); wl_list_remove(&c->link); setmon(c, NULL, 0, true); From d3cf4319cd8a0d7b90ae8c5038dccbc8cf91fbaf Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 1 Jul 2026 08:30:55 +0800 Subject: [PATCH 56/70] fix: fix shield and blur node offset --- src/animation/client.h | 27 ++++++++++++++++++++++----- src/mango.c | 6 ------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index ef75ca68..769846af 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -503,6 +503,7 @@ void client_draw_title(Client *c) { void apply_shield(Client *c, struct wlr_box clip_box) { if (clip_box.width <= 0 || clip_box.height <= 0) { + wlr_scene_node_set_enabled(&c->shield->node, false); return; } @@ -533,7 +534,6 @@ void apply_shield(Client *c, struct wlr_box clip_box) { } void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { - if (c->isfullscreen) { if (c->blur->node.enabled) { wlr_scene_node_set_enabled(&c->blur->node, false); @@ -541,12 +541,21 @@ void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { return; } else { if (config.blur && !c->noblur) { - int32_t blur_x = (int32_t)c->bw + offset.x; - int32_t blur_y = (int32_t)c->bw + offset.y; + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting)) { + clip_box.x = 0; + clip_box.y = 0; + clip_box.width = + c->animation.current.width - 2 * (int32_t)c->bw; + clip_box.height = + c->animation.current.height - 2 * (int32_t)c->bw; + } + + int32_t blur_x = clip_box.x + (int32_t)c->bw; + int32_t blur_y = clip_box.y + (int32_t)c->bw; wlr_scene_node_set_enabled(&c->blur->node, true); wlr_scene_node_set_position(&c->blur->node, blur_x, blur_y); - wlr_scene_blur_set_size(c->blur, clip_box.width - c->bw, - clip_box.height - c->bw); + wlr_scene_blur_set_size(c->blur, clip_box.width, clip_box.height); } else { wlr_scene_node_set_enabled(&c->blur->node, false); } @@ -1049,6 +1058,14 @@ void client_apply_clip(Client *c, float factor) { apply_shield(c, clip_box); if (clip_box.width <= 0 || clip_box.height <= 0) { + should_render_client_surface = false; + wlr_scene_node_set_enabled(&c->scene_surface->node, false); + } else { + should_render_client_surface = true; + wlr_scene_node_set_enabled(&c->scene_surface->node, true); + } + + if (!should_render_client_surface) { return; } diff --git a/src/mango.c b/src/mango.c index ac660940..0f1c8987 100644 --- a/src/mango.c +++ b/src/mango.c @@ -1336,12 +1336,6 @@ void client_replace(Client *c, Client *w, bool is_group_change_member, mango_group_bar_set_focus(c->group_bar, c->isgroupfocusing); if (w->overview_scene_surface) { - - wlr_scene_node_reparent(&w->blur->node, w->overview_scene_surface); - wlr_scene_node_lower_to_bottom(&w->blur->node); - wlr_scene_node_reparent(&w->shield->node, w->overview_scene_surface); - wlr_scene_node_raise_to_top(&w->shield->node); - wlr_scene_node_destroy(&w->scene_surface->node); w->scene_surface = w->overview_scene_surface; w->overview_scene_surface = NULL; From 186259cf44ea0261c6e3bf30b82eb211e576cc09 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 1 Jul 2026 11:58:03 +0800 Subject: [PATCH 57/70] opt: Optimize some function names --- src/animation/client.h | 28 ++++++++++++++-------------- src/dispatch/bind_define.h | 2 +- src/mango.c | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 769846af..1e4dfe3d 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -412,7 +412,7 @@ void client_draw_shadow(Client *c) { wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } -void client_draw_title(Client *c) { +void client_draw_groupbar(Client *c) { if (!c || !c->group_bar) return; @@ -500,7 +500,7 @@ void client_draw_title(Client *c) { } } -void apply_shield(Client *c, struct wlr_box clip_box) { +void client_draw_shield(Client *c, struct wlr_box clip_box) { if (clip_box.width <= 0 || clip_box.height <= 0) { wlr_scene_node_set_enabled(&c->shield->node, false); @@ -571,7 +571,7 @@ void global_draw_group_bar(Client *c, int32_t x, int32_t y, int32_t width, mango_group_bar_set_size(c->group_bar, width, height); } -void apply_split_border(Client *c, bool hit_no_border) { +void client_draw_split_border(Client *c, bool hit_no_border) { if (c->iskilling || !c->mon || !client_surface(c)->mapped) return; @@ -662,7 +662,7 @@ void apply_split_border(Client *c, bool hit_no_border) { border_right_y); } -void apply_border(Client *c) { +void client_draw_border(Client *c) { if (!c || c->iskilling || !client_surface(c)->mapped) return; @@ -681,7 +681,7 @@ void apply_border(Client *c) { } bool hit_no_border = check_hit_no_border(c); - apply_split_border(c, hit_no_border); + client_draw_split_border(c, hit_no_border); struct fx_corner_radii current_corner_location = c->isfullscreen || (config.no_radius_when_single && c->mon && @@ -1051,11 +1051,11 @@ void client_apply_clip(Client *c, float factor) { offset = clip_to_hide(c, &clip_box); - apply_border(c); + client_draw_border(c); client_draw_shadow(c); - client_draw_title(c); + client_draw_groupbar(c); client_draw_blur(c, clip_box, offset); - apply_shield(c, clip_box); + client_draw_shield(c, clip_box); if (clip_box.width <= 0 || clip_box.height <= 0) { should_render_client_surface = false; @@ -1103,10 +1103,10 @@ void client_apply_clip(Client *c, float factor) { offset = clip_to_hide(c, &clip_box); // 应用窗口装饰 - apply_border(c); + client_draw_border(c); client_draw_shadow(c); - client_draw_title(c); - apply_shield(c, clip_box); + client_draw_groupbar(c); + client_draw_shield(c, clip_box); client_draw_blur(c, clip_box, offset); // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 @@ -1553,11 +1553,11 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->geom; wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); - apply_border(c); + client_draw_border(c); client_get_clip(c, &clip); - apply_shield(c, clip); + client_draw_shield(c, clip); client_draw_shadow(c); - client_draw_title(c); + client_draw_groupbar(c); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); if (config.blur && !c->noblur) wlr_scene_blur_set_size(c->blur, diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 63f6206e..c449f53d 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -2148,7 +2148,7 @@ int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) { leaf->custom_leaf_split_h = false; } bool hit_no_border = check_hit_no_border(c); - apply_split_border(c, hit_no_border); + client_draw_split_border(c, hit_no_border); return 0; } diff --git a/src/mango.c b/src/mango.c index 0f1c8987..056d7b6d 100644 --- a/src/mango.c +++ b/src/mango.c @@ -848,7 +848,7 @@ get_nearest_output_mode(struct wlr_output *output, int32_t width, static void client_commit(Client *c); static void layer_commit(LayerSurface *l); -static void apply_border(Client *c); +static void client_draw_border(Client *c); static void client_set_opacity(Client *c, double opacity); static void init_baked_points(void); static void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, From a55c10f0056c7a1e820ab874e6963dd7036b3caf Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 1 Jul 2026 12:35:31 +0800 Subject: [PATCH 58/70] feat: shield for layershell --- src/animation/layer.h | 36 +++++++++++++++++++++++++++++++---- src/config/parse_config.h | 4 ++++ src/fetch/common.h | 6 +++++- src/mango.c | 40 +++++++++++++++++++++++++++++---------- 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/animation/layer.h b/src/animation/layer.h index 80d7e6ca..18046f68 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -1,13 +1,11 @@ void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *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; + *width = l->geom.width; + *height = l->geom.height; } } @@ -151,6 +149,34 @@ void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) { } } +void layer_draw_shield(LayerSurface *l) { + int32_t width, height; + + if (!l->mapped) + return; + + if (active_capture_count > 0 && l->shield_when_capture) { + + layer_actual_size(l, &width, &height); + + if (width <= 0 || height <= 0) { + wlr_scene_node_set_enabled(&l->shield->node, false); + return; + } + + wlr_scene_node_raise_to_top(&l->shield->node); + wlr_scene_node_set_position(&l->shield->node, 0, 0); + wlr_scene_rect_set_size(l->shield, width, height); + wlr_scene_node_set_enabled(&l->shield->node, true); + } else { + if (l->shield->node.enabled) { + wlr_scene_node_lower_to_bottom(&l->shield->node); + wlr_scene_node_set_position(&l->shield->node, 0, 0); + wlr_scene_node_set_enabled(&l->shield->node, false); + } + } +} + void layer_draw_shadow(LayerSurface *l) { if (!l->mapped || !l->shadow) @@ -575,8 +601,10 @@ bool layer_draw_frame(LayerSurface *l) { if (config.animations && config.layer_animations && l->animation.running && !l->noanim) { layer_animation_next_tick(l); + layer_draw_shield(l); layer_draw_shadow(l); } else { + layer_draw_shield(l); layer_draw_shadow(l); l->need_output_flush = false; } diff --git a/src/config/parse_config.h b/src/config/parse_config.h index fe8b7a0e..1cdc01cf 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -188,6 +188,7 @@ typedef struct { char *layer_name; // 布局名称 char *animation_type_open; char *animation_type_close; + int32_t shield_when_capture; int32_t noblur; int32_t noanim; int32_t noshadow; @@ -2310,6 +2311,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->layer_name = NULL; rule->animation_type_open = NULL; rule->animation_type_close = NULL; + rule->shield_when_capture = 0; rule->noblur = 0; rule->noanim = 0; rule->noshadow = 0; @@ -2332,6 +2334,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->animation_type_open = strdup(val); } else if (strcmp(key, "animation_type_close") == 0) { rule->animation_type_close = strdup(val); + } else if (strcmp(key, "shield_when_capture") == 0) { + rule->shield_when_capture = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "noblur") == 0) { rule->noblur = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "noanim") == 0) { diff --git a/src/fetch/common.h b/src/fetch/common.h index d017dff3..285120d5 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -160,9 +160,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, } if (node->type == WLR_SCENE_NODE_RECT) { - if (c) { + if (c && (c->type == XDGShell || c->type == X11)) { surface = client_surface(c); } + + if (l && l->type == LayerShell) { + surface = l->layer_surface->surface; + } } break; diff --git a/src/mango.c b/src/mango.c index 056d7b6d..2719c91b 100644 --- a/src/mango.c +++ b/src/mango.c @@ -522,6 +522,7 @@ typedef struct { Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_tree *popups; + struct wlr_scene_rect *shield; struct wlr_scene_shadow *shadow; struct wlr_scene_blur *blur; struct wlr_scene_layer_surface_v1 *scene_layer; @@ -542,6 +543,7 @@ typedef struct { int32_t noshadow; char *animation_type_open; char *animation_type_close; + bool shield_when_capture; bool need_output_flush; bool being_unmapped; } LayerSurface; @@ -2808,6 +2810,7 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { l->noanim = 0; l->dirty = false; + l->shield_when_capture = false; l->noblur = 0; l->shadow = NULL; l->need_output_flush = true; @@ -2820,6 +2823,7 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { l->layer_surface->namespace)) { r = &config.layer_rules[ji]; + APPLY_INT_PROP(l, r, shield_when_capture); APPLY_INT_PROP(l, r, noblur); APPLY_INT_PROP(l, r, noanim); APPLY_INT_PROP(l, r, noshadow); @@ -2828,6 +2832,13 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { } } + // 初始化屏蔽 + l->shield = + wlr_scene_rect_create(l->scene, 0, 0, (float[4]){0, 0, 0, 0xff}); + l->shield->node.data = l; + wlr_scene_node_lower_to_bottom(&l->shield->node); + wlr_scene_node_set_enabled(&l->shield->node, false); + // 初始化阴影 if (layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { @@ -2848,6 +2859,9 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { l->animation.duration = config.animation_duration_open; l->animation.action = OPEN; layer_set_pending_state(l); + } else { + l->animainit_geom = l->animation.current = l->current = l->pending = + l->geom; } // 刷新布局,让窗口能感应到exclude_zone变化以及设置独占表面 arrangelayers(l->mon); @@ -2891,20 +2905,26 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) { get_layer_target_geometry(l, &box); - if (config.animations && config.layer_animations && !l->noanim && - l->mapped && - layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && - layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND && - !wlr_box_equal(&box, &l->geom)) { - + if (!wlr_box_equal(&box, &l->geom)) { l->geom.x = box.x; l->geom.y = box.y; l->geom.width = box.width; l->geom.height = box.height; - l->animation.action = MOVE; - l->animation.duration = config.animation_duration_move; - l->need_output_flush = true; - layer_set_pending_state(l); + + if (config.animations && config.layer_animations && !l->noanim && + l->mapped && + layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM && + layer_surface->current.layer != + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { + l->animation.action = MOVE; + l->animation.duration = config.animation_duration_move; + l->need_output_flush = true; + layer_set_pending_state(l); + } else { + l->animainit_geom = l->animation.current = l->current = l->pending = + l->geom; + l->need_output_flush = true; + } } if (config.blur && config.blur_layer) { From ed31b411c5d40379183ae8e26687b60d898c1243 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 1 Jul 2026 15:03:07 +0800 Subject: [PATCH 59/70] update docs --- docs/window-management/rules.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md index 27216942..12f6b3f4 100644 --- a/docs/window-management/rules.md +++ b/docs/window-management/rules.md @@ -240,6 +240,7 @@ layerrule=layer_name:Values,Parameter:Values,Parameter:Values | `noblur` | integer | `0` / `1` | Disable blur | | `noanim` | integer | `0` / `1` | Disable layer animation | | `noshadow` | integer | `0` / `1` | Disable layer shadow | +| `shield_when_capture`| integer | `0` / `1` | Shield layer when captured | > **Tip:** For animation types, see [Animations](/docs/visuals/animations#animation-types). For visual effects, see [Window Effects](/docs/visuals/effects). From c644c43d28d7e1734a4159b8094a5159eb14b6c5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 2 Jul 2026 09:34:19 +0800 Subject: [PATCH 60/70] opt: optimize shield_when_capture of layer --- docs/window-management/rules.md | 2 +- src/animation/layer.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md index 12f6b3f4..3ef98d35 100644 --- a/docs/window-management/rules.md +++ b/docs/window-management/rules.md @@ -240,7 +240,7 @@ layerrule=layer_name:Values,Parameter:Values,Parameter:Values | `noblur` | integer | `0` / `1` | Disable blur | | `noanim` | integer | `0` / `1` | Disable layer animation | | `noshadow` | integer | `0` / `1` | Disable layer shadow | -| `shield_when_capture`| integer | `0` / `1` | Shield layer when captured | +| `shield_when_capture`| integer | `0` / `1` | Shield layer when captured.(it is better to combination with `noanim:1`) | > **Tip:** For animation types, see [Animations](/docs/visuals/animations#animation-types). For visual effects, see [Window Effects](/docs/visuals/effects). diff --git a/src/animation/layer.h b/src/animation/layer.h index 18046f68..e0c99793 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -408,6 +408,10 @@ void init_fadeout_layers(LayerSurface *l) { if (!l->mon || !l->scene) return; + if (l->shield_when_capture) { + return; + } + if ((l->animation_type_close && strcmp(l->animation_type_close, "none") == 0) || (!l->animation_type_close && From 65637b77a5935b49479b107579006f3dbcc4d85b Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 2 Jul 2026 16:00:42 +0800 Subject: [PATCH 61/70] fix: fix blur and shield node offset error for firfox --- src/animation/client.h | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 1e4dfe3d..555922c4 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -507,17 +507,20 @@ void client_draw_shield(Client *c, struct wlr_box clip_box) { return; } + struct wlr_box surface_relative_geom; + client_get_clip(c, &surface_relative_geom); + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { - clip_box.x = 0; - clip_box.y = 0; + clip_box.x = surface_relative_geom.x; + clip_box.y = surface_relative_geom.y; clip_box.width = c->animation.current.width - 2 * (int32_t)c->bw; clip_box.height = c->animation.current.height - 2 * (int32_t)c->bw; } if (active_capture_count > 0 && c->shield_when_capture) { - int32_t shield_x = clip_box.x + (int32_t)c->bw; - int32_t shield_y = clip_box.y + (int32_t)c->bw; + int32_t shield_x = clip_box.x - surface_relative_geom.x + (int32_t)c->bw; + int32_t shield_y = clip_box.y - surface_relative_geom.y + (int32_t)c->bw; wlr_scene_node_raise_to_top(&c->shield->node); wlr_scene_node_set_position(&c->shield->node, shield_x, shield_y); wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.height); @@ -541,18 +544,22 @@ void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { return; } else { if (config.blur && !c->noblur) { + + struct wlr_box surface_relative_geom; + client_get_clip(c, &surface_relative_geom); + if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouting)) { - clip_box.x = 0; - clip_box.y = 0; + clip_box.x = surface_relative_geom.x; + clip_box.y = surface_relative_geom.y; clip_box.width = c->animation.current.width - 2 * (int32_t)c->bw; clip_box.height = c->animation.current.height - 2 * (int32_t)c->bw; } - int32_t blur_x = clip_box.x + (int32_t)c->bw; - int32_t blur_y = clip_box.y + (int32_t)c->bw; + int32_t blur_x = clip_box.x - surface_relative_geom.x + (int32_t)c->bw; + int32_t blur_y = clip_box.y - surface_relative_geom.y + (int32_t)c->bw; wlr_scene_node_set_enabled(&c->blur->node, true); wlr_scene_node_set_position(&c->blur->node, blur_x, blur_y); wlr_scene_blur_set_size(c->blur, clip_box.width, clip_box.height); From 1725a3e2bb0be4576fd032d4f321a886f6d01f82 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Fri, 3 Jul 2026 20:32:34 +0800 Subject: [PATCH 62/70] feat: add foreign toplevel id to ipc --- src/ipc/ipc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h index ee93ed23..c3bfae63 100644 --- a/src/ipc/ipc.h +++ b/src/ipc/ipc.h @@ -143,6 +143,8 @@ static cJSON *build_client_json(Client *c) { cJSON_AddNumberToObject(obj, "id", c->id); cJSON_AddNumberToObject(obj, "pid", c->pid); + cJSON_AddStringToObject(obj, "foreign_toplevel_id", + c->ext_foreign_toplevel->identifier); cJSON_AddStringToObject(obj, "title", client_get_title(c)); cJSON_AddStringToObject(obj, "appid", client_get_appid(c)); cJSON_AddStringToObject(obj, "monitor", From 0182e24bc537e357ee7c1da9cc5fc5c487df7d8c Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 4 Jul 2026 09:54:16 +0800 Subject: [PATCH 63/70] deprecate: not need dwl ipc anymore --- docs/visuals/status-bar.md | 119 ++++++------ meson.build | 3 - protocols/dwl-ipc-unstable-v2.xml | 253 ------------------------ protocols/meson.build | 1 - src/animation/client.h | 12 +- src/ext-protocol/all.h | 1 - src/ext-protocol/dwl-ipc.h | 309 ------------------------------ src/mango.c | 7 - 8 files changed, 62 insertions(+), 643 deletions(-) delete mode 100644 protocols/dwl-ipc-unstable-v2.xml delete mode 100644 src/ext-protocol/dwl-ipc.h diff --git a/docs/visuals/status-bar.md b/docs/visuals/status-bar.md index f2924e83..2be1cbae 100644 --- a/docs/visuals/status-bar.md +++ b/docs/visuals/status-bar.md @@ -17,7 +17,12 @@ Add the following to your Waybar configuration: { "modules-left": [ "ext/workspaces", - "dwl/window" + "mango/layout", + "mango/window" + ], + "modules-right": [ + "mango/language", + "mango/keymode", ], "ext/workspaces": { "format": "{icon}", @@ -26,13 +31,33 @@ Add the following to your Waybar configuration: "on-click-right": "deactivate", "sort-by-id": true }, - "dwl/window": { - "format": "[{layout}] {title}" - } + "mango/keymode": { + "format": "[{default}]", + "format-default": " Default", + "format-test": " Test", + }, + "mango/window": { + "format": "{}", + "icon": true, + "on-click":"bash ~/.config/mango/scripts/screenshot.sh", + "on-click-right":"bash ~/tool/shotTranslate.sh shot", + "icon": false, + "icon-size": 20 + }, + "mango/layout": { + "format": "{}", + // "format-S": "Scroller", + // "format-T": "Tile", + }, + "mango/language": { + "format": "{short}", + "format-us": "󰌌 US", + "format-ru": "󰌌 RU" + }, } ``` -## Styling +## Styling Example You can style the tags using standard CSS in `style.css`. @@ -40,27 +65,13 @@ You can style the tags using standard CSS in `style.css`. ```css #workspaces { - border-radius: 4px; - border-width: 2px; - border-style: solid; border-color: #c9b890; - margin-left: 4px; - padding-left: 10px; - padding-right: 6px; background: rgba(40, 40, 40, 0.76); } #workspaces button { - border: none; background: none; - box-shadow: inherit; - text-shadow: inherit; color: #ddca9e; - padding: 1px; - padding-left: 1px; - padding-right: 1px; - margin-right: 2px; - margin-left: 2px; } #workspaces button.hidden { @@ -79,61 +90,39 @@ You can style the tags using standard CSS in `style.css`. #workspaces button.active { background-color: #ddca9e; color: #282828; - margin-top: 5px; - margin-bottom: 5px; - padding-top: 1px; - padding-bottom: 0px; - border-radius: 3px; } #workspaces button.urgent { background-color: #ef5e5e; color: #282828; - margin-top: 5px; - margin-bottom: 5px; - padding-top: 1px; - padding-bottom: 0px; - border-radius: 3px; -} - -#tags { - background-color: transparent; -} - -#tags button { - background-color: #fff; - color: #a585cd; -} - -#tags button:not(.occupied):not(.focused) { - font-size: 0; - min-width: 0; - min-height: 0; - margin: -17px; - padding: 0; - color: transparent; - background-color: transparent; -} - -#tags button.occupied { - background-color: #fff; - color: #cdc885; -} - -#tags button.focused { - background-color: rgb(186, 142, 213); - color: #fff; -} - -#tags button.urgent { - background: rgb(171, 101, 101); - color: #fff; } #window { - background-color: rgb(237, 196, 147); - color: rgb(63, 37, 5); + background-color: #CA9297; + color: #282828; } + +window#waybar.empty #window { + background: none; + margin: 0px; + padding: 0px; +} + +#layout { + background-color: #CA9297; + color: #282828; +} + +#language { + background-color: #CA9297; + color: #282828; +} + +#keymode { + background-color: #CA9297; + color: #282828; +} + ``` ## Complete Configuration Example diff --git a/meson.build b/meson.build index 938371e5..5f0870f4 100644 --- a/meson.build +++ b/meson.build @@ -121,7 +121,6 @@ executable('mango', ) # build mmsg -dwl_ipc_protocol = 'protocols/dwl-ipc-unstable-v2.xml' wayland_scanner_client_header = generator( wayland_scanner, @@ -138,8 +137,6 @@ wayland_scanner_private_code = generator( # use generator in mmsg target executable('mmsg', 'mmsg/mmsg.c', - wayland_scanner_private_code.process(dwl_ipc_protocol), - wayland_scanner_client_header.process(dwl_ipc_protocol), dependencies: [ libwayland_client_dep ], diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml deleted file mode 100644 index 5852107e..00000000 --- a/protocols/dwl-ipc-unstable-v2.xml +++ /dev/null @@ -1,253 +0,0 @@ - - - - - This protocol allows clients to update and get updates from dwl. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible - changes may be added together with the corresponding interface - version bump. - Backward incompatible changes are done by bumping the version - number in the protocol and interface names and resetting the - interface version. Once the protocol is to be declared stable, - the 'z' prefix and the version number in the protocol and - interface names are removed and the interface version number is - reset. - - - - - This interface is exposed as a global in wl_registry. - - Clients can use this interface to get a dwl_ipc_output. - After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. - The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. - - - - - Indicates that the client will not the dwl_ipc_manager object anymore. - Objects created through this instance are not affected. - - - - - - Get a dwl_ipc_outout for the specified wl_output. - - - - - - - - This event is sent after binding. - A roundtrip after binding guarantees the client recieved all tags. - - - - - - - This event is sent after binding. - A roundtrip after binding guarantees the client recieved all layouts. - - - - - - - - Observe and control a dwl output. - - Events are double-buffered: - Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. - - Request are not double-buffered: - The compositor will update immediately upon request. - - - - - - - - - - - Indicates to that the client no longer needs this dwl_ipc_output. - - - - - - Indicates the client should hide or show themselves. - If the client is visible then hide, if hidden then show. - - - - - - Indicates if the output is active. Zero is invalid, nonzero is valid. - - - - - - - Indicates that a tag has been updated. - - - - - - - - - - Indicates a new layout is selected. - - - - - - - Indicates the title has changed. - - - - - - - Indicates the appid has changed. - - - - - - - Indicates the layout has changed. Since layout symbols are dynamic. - As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. - You can ignore the zdwl_ipc_output.layout event. - - - - - - - Indicates that a sequence of status updates have finished and the client should redraw. - - - - - - - - - - - - The tags are updated as follows: - new_tags = (current_tags AND and_tags) XOR xor_tags - - - - - - - - - - - - This request allows clients to instruct the compositor to quit mango. - - - - - - - - - - - - - - - - Indicates if the selected client on this output is fullscreen. - - - - - - - Indicates if the selected client on this output is floating. - - - - - - - Indicates if x coordinates of the selected client. - - - - - - - Indicates if y coordinates of the selected client. - - - - - - - Indicates if width of the selected client. - - - - - - - Indicates if height of the selected client. - - - - - - - last map layer. - - - - - - - current keyboard layout. - - - - - - - current keybind mode. - - - - - - - scale factor of monitor. - - - - - - - diff --git a/protocols/meson.build b/protocols/meson.build index 922a76ed..925bf42a 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -21,7 +21,6 @@ wayland_xmls = [ wl_protocol_dir + '/staging/ext-workspace/ext-workspace-v1.xml', wl_protocol_dir + '/staging/tearing-control/tearing-control-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1.xml', - 'dwl-ipc-unstable-v2.xml', 'wlr-layer-shell-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml', ] diff --git a/src/animation/client.h b/src/animation/client.h index 555922c4..2c39e511 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -519,8 +519,10 @@ void client_draw_shield(Client *c, struct wlr_box clip_box) { } if (active_capture_count > 0 && c->shield_when_capture) { - int32_t shield_x = clip_box.x - surface_relative_geom.x + (int32_t)c->bw; - int32_t shield_y = clip_box.y - surface_relative_geom.y + (int32_t)c->bw; + int32_t shield_x = + clip_box.x - surface_relative_geom.x + (int32_t)c->bw; + int32_t shield_y = + clip_box.y - surface_relative_geom.y + (int32_t)c->bw; wlr_scene_node_raise_to_top(&c->shield->node); wlr_scene_node_set_position(&c->shield->node, shield_x, shield_y); wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.height); @@ -558,8 +560,10 @@ void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { c->animation.current.height - 2 * (int32_t)c->bw; } - int32_t blur_x = clip_box.x - surface_relative_geom.x + (int32_t)c->bw; - int32_t blur_y = clip_box.y - surface_relative_geom.y + (int32_t)c->bw; + int32_t blur_x = + clip_box.x - surface_relative_geom.x + (int32_t)c->bw; + int32_t blur_y = + clip_box.y - surface_relative_geom.y + (int32_t)c->bw; wlr_scene_node_set_enabled(&c->blur->node, true); wlr_scene_node_set_position(&c->blur->node, blur_x, blur_y); wlr_scene_blur_set_size(c->blur, clip_box.width, clip_box.height); diff --git a/src/ext-protocol/all.h b/src/ext-protocol/all.h index e9923c35..f0282626 100644 --- a/src/ext-protocol/all.h +++ b/src/ext-protocol/all.h @@ -1,4 +1,3 @@ -#include "dwl-ipc.h" #include "ext-workspace.h" #include "foreign-toplevel.h" #include "hdr.h" diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h deleted file mode 100644 index 53d96309..00000000 --- a/src/ext-protocol/dwl-ipc.h +++ /dev/null @@ -1,309 +0,0 @@ -#include "dwl-ipc-unstable-v2-protocol.h" - -static void dwl_ipc_manager_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id); -static void dwl_ipc_manager_destroy(struct wl_resource *resource); -static void dwl_ipc_manager_get_output(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, struct wl_resource *output); -static void dwl_ipc_manager_release(struct wl_client *client, - struct wl_resource *resource); -static void dwl_ipc_output_destroy(struct wl_resource *resource); -static void dwl_ipc_output_printstatus(Monitor *monitor); -static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); -static void dwl_ipc_output_set_client_tags(struct wl_client *client, - struct wl_resource *resource, - uint32_t and_tags, - uint32_t xor_tags); -static void dwl_ipc_output_set_layout(struct wl_client *client, - struct wl_resource *resource, - uint32_t index); -static void dwl_ipc_output_set_tags(struct wl_client *client, - struct wl_resource *resource, - uint32_t tagmask, uint32_t toggle_tagset); -static void dwl_ipc_output_quit(struct wl_client *client, - struct wl_resource *resource); -static void dwl_ipc_output_dispatch(struct wl_client *client, - struct wl_resource *resource, - const char *dispatch, const char *arg1, - const char *arg2, const char *arg3, - const char *arg4, const char *arg5); -static void dwl_ipc_output_release(struct wl_client *client, - struct wl_resource *resource); - -/* global event handlers */ -static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = { - .release = dwl_ipc_manager_release, - .get_output = dwl_ipc_manager_get_output}; -static struct zdwl_ipc_output_v2_interface dwl_output_implementation = { - .release = dwl_ipc_output_release, - .set_tags = dwl_ipc_output_set_tags, - .quit = dwl_ipc_output_quit, - .dispatch = dwl_ipc_output_dispatch, - .set_layout = dwl_ipc_output_set_layout, - .set_client_tags = dwl_ipc_output_set_client_tags}; - -void dwl_ipc_manager_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) { - struct wl_resource *manager_resource = - wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); - if (!manager_resource) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(manager_resource, - &dwl_manager_implementation, NULL, - dwl_ipc_manager_destroy); - - zdwl_ipc_manager_v2_send_tags(manager_resource, LENGTH(tags)); - - for (uint32_t i = 0; i < LENGTH(layouts); i++) - zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); -} - -void dwl_ipc_manager_destroy(struct wl_resource *resource) { - /* No state to destroy */ -} - -void dwl_ipc_manager_get_output(struct wl_client *client, - struct wl_resource *resource, uint32_t id, - struct wl_resource *output) { - DwlIpcOutput *ipc_output; - struct wlr_output *op = wlr_output_from_resource(output); - if (!op) - return; - Monitor *monitor = op->data; - struct wl_resource *output_resource = - wl_resource_create(client, &zdwl_ipc_output_v2_interface, - wl_resource_get_version(resource), id); - if (!output_resource) - return; - - ipc_output = ecalloc(1, sizeof(*ipc_output)); - ipc_output->resource = output_resource; - ipc_output->mon = monitor; - wl_resource_set_implementation(output_resource, &dwl_output_implementation, - ipc_output, dwl_ipc_output_destroy); - wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); - dwl_ipc_output_printstatus_to(ipc_output); -} - -void dwl_ipc_manager_release(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void dwl_ipc_output_destroy(struct wl_resource *resource) { - DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); - wl_list_remove(&ipc_output->link); - free(ipc_output); -} - -// 修改IPC输出函数,接受掩码参数 -void dwl_ipc_output_printstatus(Monitor *monitor) { - DwlIpcOutput *ipc_output; - wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) - dwl_ipc_output_printstatus_to(ipc_output); -} - -// 修改主IPC输出函数,根据掩码发送相应事件 -void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { - Monitor *monitor = ipc_output->mon; - Client *c = NULL, *focused = NULL; - struct wlr_keyboard *keyboard; - xkb_layout_index_t current; - int32_t tagmask, state, numclients, focused_client, tag; - const char *title, *appid, *symbol; - char kb_layout[32]; - focused = focustop(monitor); - zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); - - for (tag = 0; tag < LENGTH(tags); tag++) { - numclients = state = focused_client = 0; - tagmask = 1 << tag; - if ((tagmask & monitor->tagset[monitor->seltags]) != 0) - state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; - wl_list_for_each(c, &clients, link) { - if (c->mon != monitor) - continue; - if (!(c->tags & tagmask)) - continue; - if (c == focused) - focused_client = 1; - if (c->isurgent) - state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; - numclients++; - } - zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, - numclients, focused_client); - } - - title = focused ? client_get_title(focused) : ""; - appid = focused ? client_get_appid(focused) : ""; - - if (monitor->isoverview) { - symbol = overviewlayout.symbol; - } else { - symbol = monitor->pertag->ltidxs[monitor->pertag->curtag]->symbol; - } - - keyboard = &kb_group->wlr_group->keyboard; - current = xkb_state_serialize_layout(keyboard->xkb_state, - XKB_STATE_LAYOUT_EFFECTIVE); - get_layout_abbr(kb_layout, - xkb_keymap_layout_get_name(keyboard->keymap, current)); - - zdwl_ipc_output_v2_send_layout( - ipc_output->resource, - monitor->pertag->ltidxs[monitor->pertag->curtag] - layouts); - zdwl_ipc_output_v2_send_title(ipc_output->resource, title ? title : broken); - zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid ? appid : broken); - zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, symbol); - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { - zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, - focused ? focused->isfullscreen : 0); - } - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { - zdwl_ipc_output_v2_send_floating(ipc_output->resource, - focused ? focused->isfloating : 0); - } - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_X_SINCE_VERSION) { - zdwl_ipc_output_v2_send_x(ipc_output->resource, - focused ? focused->geom.x : 0); - } - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_Y_SINCE_VERSION) { - zdwl_ipc_output_v2_send_y(ipc_output->resource, - focused ? focused->geom.y : 0); - } - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_WIDTH_SINCE_VERSION) { - zdwl_ipc_output_v2_send_width(ipc_output->resource, - focused ? focused->geom.width : 0); - } - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_HEIGHT_SINCE_VERSION) { - zdwl_ipc_output_v2_send_height(ipc_output->resource, - focused ? focused->geom.height : 0); - } - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_LAST_LAYER_SINCE_VERSION) { - zdwl_ipc_output_v2_send_last_layer(ipc_output->resource, - monitor->last_open_surface); - } - - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_KB_LAYOUT_SINCE_VERSION) { - zdwl_ipc_output_v2_send_kb_layout(ipc_output->resource, kb_layout); - } - - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_KEYMODE_SINCE_VERSION) { - zdwl_ipc_output_v2_send_keymode(ipc_output->resource, keymode.mode); - } - - if (wl_resource_get_version(ipc_output->resource) >= - ZDWL_IPC_OUTPUT_V2_SCALEFACTOR_SINCE_VERSION) { - zdwl_ipc_output_v2_send_scalefactor(ipc_output->resource, - monitor->wlr_output->scale * 100); - } - - zdwl_ipc_output_v2_send_frame(ipc_output->resource); -} - -void dwl_ipc_output_set_client_tags(struct wl_client *client, - struct wl_resource *resource, - uint32_t and_tags, uint32_t xor_tags) { - DwlIpcOutput *ipc_output; - Monitor *monitor = NULL; - Client *selected_client = NULL; - uint32_t newtags = 0; - - ipc_output = wl_resource_get_user_data(resource); - if (!ipc_output) - return; - - monitor = ipc_output->mon; - selected_client = focustop(monitor); - if (!selected_client) - return; - - newtags = (selected_client->tags & and_tags) ^ xor_tags; - if (!newtags) - return; - - selected_client->tags = newtags; - if (selmon == monitor) - focusclient(focustop(monitor), 1); - arrange(selmon, false, false); - printstatus(IPC_WATCH_ARRANGGE); -} - -void dwl_ipc_output_set_layout(struct wl_client *client, - struct wl_resource *resource, uint32_t index) { - DwlIpcOutput *ipc_output; - Monitor *monitor = NULL; - - ipc_output = wl_resource_get_user_data(resource); - if (!ipc_output) - return; - - monitor = ipc_output->mon; - if (index >= LENGTH(layouts)) - index = 0; - - monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index]; - clear_fullscreen_and_maximized_state(monitor); - arrange(monitor, false, false); - printstatus(IPC_WATCH_ARRANGGE); -} - -void dwl_ipc_output_set_tags(struct wl_client *client, - struct wl_resource *resource, uint32_t tagmask, - uint32_t toggle_tagset) { - DwlIpcOutput *ipc_output; - Monitor *monitor = NULL; - uint32_t newtags = tagmask & TAGMASK; - - ipc_output = wl_resource_get_user_data(resource); - if (!ipc_output) - return; - monitor = ipc_output->mon; - - view_in_mon(&(Arg){.ui = newtags}, true, monitor, true); -} - -void dwl_ipc_output_quit(struct wl_client *client, - struct wl_resource *resource) { - quit(&(Arg){0}); -} - -void dwl_ipc_output_dispatch(struct wl_client *client, - struct wl_resource *resource, const char *dispatch, - const char *arg1, const char *arg2, - const char *arg3, const char *arg4, - const char *arg5) { - - int32_t (*func)(const Arg *); - Arg arg; - func = parse_func_name((char *)dispatch, &arg, (char *)arg1, (char *)arg2, - (char *)arg3, (char *)arg4, (char *)arg5); - if (func) { - func(&arg); - } - - if (arg.v) - free(arg.v); - if (arg.v2) - free(arg.v2); - if (arg.v3) - free(arg.v3); -} - -void dwl_ipc_output_release(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} diff --git a/src/mango.c b/src/mango.c index 2719c91b..a6d30c25 100644 --- a/src/mango.c +++ b/src/mango.c @@ -583,7 +583,6 @@ struct Monitor { uint32_t resizing_count_pending; uint32_t resizing_count_current; - struct wl_list dwl_ipc_outputs; int32_t gappih; /* horizontal gap between windows */ int32_t gappiv; /* vertical gap between windows */ int32_t gappoh; /* horizontal outer gaps */ @@ -3438,8 +3437,6 @@ void createmon(struct wl_listener *listener, void *data) { m->wlr_output = wlr_output; m->wlr_output->data = m; - wl_list_init(&m->dwl_ipc_outputs); - for (i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); @@ -6105,7 +6102,6 @@ void handle_print_status(struct wl_listener *listener, void *data) { } dwl_ext_workspace_printstatus(m); - dwl_ipc_output_printstatus(m); } } @@ -6470,9 +6466,6 @@ void setup(void) { wlr_log(WLR_INFO, "VR will not be available."); } - wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, - dwl_ipc_manager_bind); - // 创建顶层管理句柄 foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(dpy); struct wlr_xdg_foreign_registry *foreign_registry = From aabaa61906fcfd84a2179066990f7302bea10c27 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 4 Jul 2026 09:59:14 +0800 Subject: [PATCH 64/70] opt: rename some func name --- src/ext-protocol/ext-workspace.h | 4 +- src/ext-protocol/text-input.h | 90 ++++++++++++++++---------------- src/mango.c | 33 +++++------- 3 files changed, 61 insertions(+), 66 deletions(-) diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index 82b7ef1f..70bc2f6c 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -148,7 +148,7 @@ static void add_workspace_by_tag(int32_t tag, Monitor *m) { wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name); } -void dwl_ext_workspace_printstatus(Monitor *m) { +void mango_ext_workspace_printstatus(Monitor *m) { struct workspace *w; uint32_t tag_status = 0; @@ -199,7 +199,7 @@ void refresh_monitors_workspaces_status(Monitor *m) { } } - dwl_ext_workspace_printstatus(m); + mango_ext_workspace_printstatus(m); } void workspaces_init() { diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index 0eb89881..32a69bee 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -2,7 +2,7 @@ #include #include -struct dwl_input_method_relay { +struct mango_input_method_relay { struct wl_list text_inputs; struct wlr_input_method_v2 *input_method; struct wlr_surface *focused_surface; @@ -26,12 +26,12 @@ struct dwl_input_method_relay { struct wl_listener focused_surface_destroy; }; -struct dwl_input_method_popup { +struct mango_input_method_popup { uint32_t type; // must at first in struct struct wlr_input_popup_surface_v2 *popup_surface; struct wlr_scene_tree *tree; struct wlr_scene_tree *scene_surface; - struct dwl_input_method_relay *relay; + struct mango_input_method_relay *relay; struct wl_list link; struct wl_listener destroy; @@ -39,7 +39,7 @@ struct dwl_input_method_popup { }; struct text_input { - struct dwl_input_method_relay *relay; + struct mango_input_method_relay *relay; struct wlr_text_input_v3 *input; struct wl_list link; @@ -51,20 +51,20 @@ struct text_input { struct wlr_input_method_manager_v2 *input_method_manager; struct wlr_text_input_manager_v3 *text_input_manager; -struct dwl_input_method_relay *dwl_input_method_relay; +struct mango_input_method_relay *mango_input_method_relay; /*-------------------封装给外部调用-------------------------------*/ -bool dwl_im_keyboard_grab_forward_key(KeyboardGroup *keyboard, - struct wlr_keyboard_key_event *event); +bool mango_im_keyboard_grab_forward_key(KeyboardGroup *keyboard, + struct wlr_keyboard_key_event *event); -bool dwl_im_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard); +bool mango_im_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard); -struct dwl_input_method_relay *dwl_im_relay_create(); +struct mango_input_method_relay *mango_im_relay_create(); -void dwl_im_relay_finish(struct dwl_input_method_relay *relay); +void mango_im_relay_finish(struct mango_input_method_relay *relay); -void dwl_im_relay_set_focus(struct dwl_input_method_relay *relay, - struct wlr_surface *surface); +void mango_im_relay_set_focus(struct mango_input_method_relay *relay, + struct wlr_surface *surface); /*----------------------------------------------------------*/ /*------------------协议内部代码------------------------------*/ @@ -98,7 +98,7 @@ is_keyboard_emulated_by_input_method(struct wlr_keyboard *keyboard, static struct wlr_input_method_keyboard_grab_v2 * get_keyboard_grab(KeyboardGroup *keyboard) { struct wlr_input_method_v2 *input_method = - dwl_input_method_relay->input_method; + mango_input_method_relay->input_method; if (!input_method || !input_method->keyboard_grab) { return NULL; } @@ -115,7 +115,7 @@ get_keyboard_grab(KeyboardGroup *keyboard) { return input_method->keyboard_grab; } -bool dwl_im_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard) { +bool mango_im_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = get_keyboard_grab(keyboard); @@ -133,8 +133,8 @@ bool dwl_im_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard) { } } -bool dwl_im_keyboard_grab_forward_key(KeyboardGroup *keyboard, - struct wlr_keyboard_key_event *event) { +bool mango_im_keyboard_grab_forward_key(KeyboardGroup *keyboard, + struct wlr_keyboard_key_event *event) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = get_keyboard_grab(keyboard); @@ -150,7 +150,7 @@ bool dwl_im_keyboard_grab_forward_key(KeyboardGroup *keyboard, } static struct text_input * -get_active_text_input(struct dwl_input_method_relay *relay) { +get_active_text_input(struct mango_input_method_relay *relay) { struct text_input *text_input; if (!relay->input_method) { @@ -165,7 +165,7 @@ get_active_text_input(struct dwl_input_method_relay *relay) { return NULL; } -static void update_active_text_input(struct dwl_input_method_relay *relay) { +static void update_active_text_input(struct mango_input_method_relay *relay) { struct text_input *active_text_input = get_active_text_input(relay); if (relay->input_method && relay->active_text_input != active_text_input) { @@ -181,7 +181,7 @@ static void update_active_text_input(struct dwl_input_method_relay *relay) { } static void -update_text_inputs_focused_surface(struct dwl_input_method_relay *relay) { +update_text_inputs_focused_surface(struct mango_input_method_relay *relay) { struct text_input *text_input; wl_list_for_each(text_input, &relay->text_inputs, link) { struct wlr_text_input_v3 *input = text_input->input; @@ -207,8 +207,8 @@ update_text_inputs_focused_surface(struct dwl_input_method_relay *relay) { } } -static void update_popup_position(struct dwl_input_method_popup *popup) { - struct dwl_input_method_relay *relay = popup->relay; +static void update_popup_position(struct mango_input_method_popup *popup) { + struct mango_input_method_relay *relay = popup->relay; struct text_input *text_input = relay->active_text_input; struct wlr_box cursor_rect; struct wlr_xdg_surface *xdg_surface; @@ -282,8 +282,8 @@ static void update_popup_position(struct dwl_input_method_popup *popup) { }); } -static void update_popups_position(struct dwl_input_method_relay *relay) { - struct dwl_input_method_popup *popup; +static void update_popups_position(struct mango_input_method_relay *relay) { + struct mango_input_method_popup *popup; wl_list_for_each(popup, &relay->popups, link) { update_popup_position(popup); } @@ -291,7 +291,7 @@ static void update_popups_position(struct dwl_input_method_relay *relay) { static void handle_input_method_commit(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); struct text_input *text_input; struct wlr_input_method_v2 *input_method = relay->input_method; @@ -322,7 +322,7 @@ static void handle_input_method_commit(struct wl_listener *listener, static void handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, keyboard_grab_destroy); struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = relay->input_method->keyboard_grab; @@ -336,7 +336,7 @@ static void handle_keyboard_grab_destroy(struct wl_listener *listener, static void handle_input_method_grab_keyboard(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, input_method_grab_keyboard); struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; @@ -355,7 +355,7 @@ static void handle_input_method_grab_keyboard(struct wl_listener *listener, static void handle_input_method_destroy(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy); wl_list_remove(&relay->input_method_commit.link); wl_list_remove(&relay->input_method_grab_keyboard.link); @@ -369,7 +369,7 @@ static void handle_input_method_destroy(struct wl_listener *listener, static void handle_popup_surface_destroy(struct wl_listener *listener, void *data) { - struct dwl_input_method_popup *popup = + struct mango_input_method_popup *popup = wl_container_of(listener, popup, destroy); wlr_scene_node_destroy(&popup->tree->node); wl_list_remove(&popup->destroy.link); @@ -380,18 +380,18 @@ static void handle_popup_surface_destroy(struct wl_listener *listener, static void handle_popup_surface_commit(struct wl_listener *listener, void *data) { - struct dwl_input_method_popup *popup = + struct mango_input_method_popup *popup = wl_container_of(listener, popup, commit); update_popup_position(popup); } static void handle_input_method_new_popup_surface(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, input_method_new_popup_surface); - struct dwl_input_method_popup *popup = - ecalloc(1, sizeof(struct dwl_input_method_popup)); + struct mango_input_method_popup *popup = + ecalloc(1, sizeof(struct mango_input_method_popup)); popup->popup_surface = data; popup->relay = relay; @@ -415,7 +415,7 @@ static void handle_input_method_new_popup_surface(struct wl_listener *listener, } static void handle_new_input_method(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, new_input_method); struct wlr_input_method_v2 *input_method = data; if (seat != input_method->seat) { @@ -451,7 +451,7 @@ static void handle_new_input_method(struct wl_listener *listener, void *data) { update_active_text_input(relay); } -static void send_state_to_input_method(struct dwl_input_method_relay *relay) { +static void send_state_to_input_method(struct mango_input_method_relay *relay) { struct wlr_input_method_v2 *input_method = relay->input_method; struct wlr_text_input_v3 *input = relay->active_text_input->input; @@ -476,7 +476,7 @@ static void send_state_to_input_method(struct dwl_input_method_relay *relay) { static void handle_text_input_enable(struct wl_listener *listener, void *data) { struct text_input *text_input = wl_container_of(listener, text_input, enable); - struct dwl_input_method_relay *relay = text_input->relay; + struct mango_input_method_relay *relay = text_input->relay; update_active_text_input(relay); if (relay->active_text_input == text_input) { @@ -497,7 +497,7 @@ static void handle_text_input_disable(struct wl_listener *listener, static void handle_text_input_commit(struct wl_listener *listener, void *data) { struct text_input *text_input = wl_container_of(listener, text_input, commit); - struct dwl_input_method_relay *relay = text_input->relay; + struct mango_input_method_relay *relay = text_input->relay; if (relay->active_text_input == text_input) { update_popups_position(relay); @@ -519,7 +519,7 @@ static void handle_text_input_destroy(struct wl_listener *listener, } static void handle_new_text_input(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, new_text_input); struct wlr_text_input_v3 *wlr_text_input = data; struct text_input *text_input = ecalloc(1, sizeof(struct text_input)); @@ -549,16 +549,16 @@ static void handle_new_text_input(struct wl_listener *listener, void *data) { static void handle_focused_surface_destroy(struct wl_listener *listener, void *data) { - struct dwl_input_method_relay *relay = + struct mango_input_method_relay *relay = wl_container_of(listener, relay, focused_surface_destroy); assert(relay->focused_surface == data); - dwl_im_relay_set_focus(relay, NULL); + mango_im_relay_set_focus(relay, NULL); } -struct dwl_input_method_relay *dwl_im_relay_create() { - struct dwl_input_method_relay *relay = - ecalloc(1, sizeof(struct dwl_input_method_relay)); +struct mango_input_method_relay *mango_im_relay_create() { + struct mango_input_method_relay *relay = + ecalloc(1, sizeof(struct mango_input_method_relay)); wl_list_init(&relay->text_inputs); wl_list_init(&relay->popups); relay->popup_tree = wlr_scene_tree_create(&scene->tree); @@ -576,14 +576,14 @@ struct dwl_input_method_relay *dwl_im_relay_create() { return relay; } -void dwl_im_relay_finish(struct dwl_input_method_relay *relay) { +void mango_im_relay_finish(struct mango_input_method_relay *relay) { wl_list_remove(&relay->new_text_input.link); wl_list_remove(&relay->new_input_method.link); free(relay); } -void dwl_im_relay_set_focus(struct dwl_input_method_relay *relay, - struct wlr_surface *surface) { +void mango_im_relay_set_focus(struct mango_input_method_relay *relay, + struct wlr_surface *surface) { if (relay->focused_surface == surface) { return; } diff --git a/src/mango.c b/src/mango.c index a6d30c25..1609a97e 100644 --- a/src/mango.c +++ b/src/mango.c @@ -295,7 +295,7 @@ typedef struct { InputDevice *input_dev; } Switch; -struct dwl_animation { +struct mango_animation { bool should_animate; bool running; bool tagining; @@ -311,7 +311,7 @@ struct dwl_animation { int32_t action; }; -struct dwl_opacity_animation { +struct mango_opacity_animation { bool running; float current_opacity; float target_opacity; @@ -429,8 +429,8 @@ struct Client { float stack_proportion; float old_stack_proportion; bool need_output_flush; - struct dwl_animation animation; - struct dwl_opacity_animation opacity_animation; + struct mango_animation animation; + struct mango_opacity_animation opacity_animation; int32_t isterm, noswallow; int32_t allow_csd; int32_t force_fakemaximize; @@ -480,12 +480,6 @@ struct Client { bool is_logic_hide; }; -typedef struct { - struct wl_list link; - struct wl_resource *resource; - Monitor *mon; -} DwlIpcOutput; - typedef struct { uint32_t mod; xkb_keysym_t keysym; @@ -536,7 +530,7 @@ typedef struct { struct wl_listener unmap; struct wl_listener surface_commit; - struct dwl_animation animation; + struct mango_animation animation; bool dirty; int32_t noblur; int32_t noanim; @@ -2020,7 +2014,8 @@ void apply_window_snap(Client *c) { void focuslayer(LayerSurface *l) { focusclient(NULL, 0); - dwl_im_relay_set_focus(dwl_input_method_relay, l->layer_surface->surface); + mango_im_relay_set_focus(mango_input_method_relay, + l->layer_surface->surface); client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); } @@ -2651,7 +2646,7 @@ void cleanup(void) { destroykeyboardgroup(&kb_group->destroy, NULL); - dwl_im_relay_finish(dwl_input_method_relay); + mango_im_relay_finish(mango_input_method_relay); /* If it's not destroyed manually it will cause a use-after-free of * wlr_seat. Destroy it until it's fixed in the wlroots side */ @@ -4067,7 +4062,7 @@ void focusclient(Client *c, int32_t lift) { selmon->sel = NULL; // clear text input focus state - dwl_im_relay_set_focus(dwl_input_method_relay, NULL); + mango_im_relay_set_focus(mango_input_method_relay, NULL); wlr_seat_keyboard_notify_clear_focus(seat); check_vrr_enable(c); if (active_constraint) { @@ -4082,7 +4077,7 @@ void focusclient(Client *c, int32_t lift) { // set text input focus // must before client_notify_enter, // otherwise the position of text_input will be wrong. - dwl_im_relay_set_focus(dwl_input_method_relay, client_surface(c)); + mango_im_relay_set_focus(mango_input_method_relay, client_surface(c)); /* Have a client, so focus its top-level wlr_surface */ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); @@ -4446,7 +4441,7 @@ void keypress(struct wl_listener *listener, void *data) { if (hit_global) { return; } - if (!dwl_im_keyboard_grab_forward_key(group, event)) { + if (!mango_im_keyboard_grab_forward_key(group, event)) { wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); /* Pass unhandled keycodes along to the client. */ wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, @@ -4459,7 +4454,7 @@ void keypressmod(struct wl_listener *listener, void *data) { * pressed. We simply communicate this to the client. */ KeyboardGroup *group = wl_container_of(listener, group, modifiers); - if (!dwl_im_keyboard_grab_forward_modifiers(group)) { + if (!mango_im_keyboard_grab_forward_modifiers(group)) { wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); /* Send modifiers to the client. */ @@ -6101,7 +6096,7 @@ void handle_print_status(struct wl_listener *listener, void *data) { ipc_notify_last_surface_ws_name(m); } - dwl_ext_workspace_printstatus(m); + mango_ext_workspace_printstatus(m); } } @@ -6456,7 +6451,7 @@ void setup(void) { input_method_manager = wlr_input_method_manager_v2_create(dpy); text_input_manager = wlr_text_input_manager_v3_create(dpy); - dwl_input_method_relay = dwl_im_relay_create(); + mango_input_method_relay = mango_im_relay_create(); drm_lease_manager = wlr_drm_lease_v1_manager_create(dpy, backend); if (drm_lease_manager) { From d8b1cbd7b7e7d1ae62f14b5b6ae8aa0d458a69d3 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 4 Jul 2026 10:02:13 +0800 Subject: [PATCH 65/70] opt: Remove the useless certificates --- LICENSE.dwl | 692 ------------------------------------------------ LICENSE.dwm | 39 --- LICENSE.sway | 19 -- LICENSE.tinywl | 127 --------- LICENSE.wlroots | 21 -- 5 files changed, 898 deletions(-) delete mode 100644 LICENSE.dwl delete mode 100644 LICENSE.dwm delete mode 100644 LICENSE.sway delete mode 100644 LICENSE.tinywl delete mode 100644 LICENSE.wlroots diff --git a/LICENSE.dwl b/LICENSE.dwl deleted file mode 100644 index 658085a4..00000000 --- a/LICENSE.dwl +++ /dev/null @@ -1,692 +0,0 @@ -dwl - dwm for Wayland - -Copyright © 2020 dwl team - -See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - ----- - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICENSE.dwm b/LICENSE.dwm deleted file mode 100644 index 507e4dce..00000000 --- a/LICENSE.dwm +++ /dev/null @@ -1,39 +0,0 @@ -Portions of dwl based on dwm code are used under the following license: - -MIT/X Consortium License - -© 2006-2019 Anselm R Garbe -© 2006-2009 Jukka Salmi -© 2006-2007 Sander van Dijk -© 2007-2011 Peter Hartlich -© 2007-2009 Szabolcs Nagy -© 2007-2009 Christof Musik -© 2007-2009 Premysl Hruby -© 2007-2008 Enno Gottox Boland -© 2008 Martin Hurton -© 2008 Neale Pickett -© 2009 Mate Nagy -© 2010-2016 Hiltjo Posthuma -© 2010-2012 Connor Lane Smith -© 2011 Christoph Lohmann <20h@r-36.net> -© 2015-2016 Quentin Rameau -© 2015-2016 Eric Pruitt -© 2016-2017 Markus Teich - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.sway b/LICENSE.sway deleted file mode 100644 index 3e0cacc2..00000000 --- a/LICENSE.sway +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2016-2017 Drew DeVault - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.tinywl b/LICENSE.tinywl deleted file mode 100644 index 70236904..00000000 --- a/LICENSE.tinywl +++ /dev/null @@ -1,127 +0,0 @@ -dwl is originally based on TinyWL, which is used under the following license: - -This work is licensed under CC0, which effectively puts it in the public domain. - ---- - -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/LICENSE.wlroots b/LICENSE.wlroots deleted file mode 100644 index fe85581c..00000000 --- a/LICENSE.wlroots +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2017, 2018 Drew DeVault -Copyright (c) 2014 Jari Vetoniemi -Copyright (c) 2023 The wlroots contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From 78135af7539ca32e37111b1163d44265b139198e Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 4 Jul 2026 10:14:35 +0800 Subject: [PATCH 66/70] update docs --- docs/visuals/status-bar.md | 59 ++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/docs/visuals/status-bar.md b/docs/visuals/status-bar.md index 2be1cbae..99014b62 100644 --- a/docs/visuals/status-bar.md +++ b/docs/visuals/status-bar.md @@ -7,8 +7,6 @@ description: Configure Waybar for mangowm. mangowm is compatible with Waybar's `ext/workspaces` module (Wayland standard) or the `dwl/tags` module. We recommend `ext/workspaces` for the best experience. -> **Tip:** You can also use the `dwl/tags` module, but `ext/workspaces` provides better integration with mangowm's features. The `ext/workspaces` module requires **Waybar > 0.14.0**. - ### `config.jsonc` Add the following to your Waybar configuration: @@ -16,7 +14,7 @@ Add the following to your Waybar configuration: ```jsonc { "modules-left": [ - "ext/workspaces", + "mango/workspaces", "mango/layout", "mango/window" ], @@ -24,36 +22,30 @@ Add the following to your Waybar configuration: "mango/language", "mango/keymode", ], - "ext/workspaces": { - "format": "{icon}", - "ignore-hidden": true, - "on-click": "activate", - "on-click-right": "deactivate", - "sort-by-id": true + "mango/workspaces": { + "format": "{icon}", + "hide-empty": true, + "on-click": "activate", + "on-click-right": "toggle", + "overview-label": "OVERVIEW", }, - "mango/keymode": { - "format": "[{default}]", - "format-default": " Default", - "format-test": " Test", - }, - "mango/window": { + "mango/keymode": { + "format": "{}", + // "format-default": " Default", + // "format-test": " Test", + }, + "mango/window": { + "format": "{}", + "icon-size": 20 + }, + "mango/layout": { "format": "{}", - "icon": true, - "on-click":"bash ~/.config/mango/scripts/screenshot.sh", - "on-click-right":"bash ~/tool/shotTranslate.sh shot", - "icon": false, - "icon-size": 20 - }, - "mango/layout": { - "format": "{}", - // "format-S": "Scroller", - // "format-T": "Tile", - }, - "mango/language": { - "format": "{short}", - "format-us": "󰌌 US", - "format-ru": "󰌌 RU" - }, + // "format-S": "Scroller", + // "format-T": "Tile", + }, + "mango/language": { + "format": "{short}", + }, } ``` @@ -97,6 +89,11 @@ You can style the tags using standard CSS in `style.css`. color: #282828; } +#workspaces button.overview { + background-color: #ef5e5e; + color: #282828; +} + #window { background-color: #CA9297; color: #282828; From 96d1d3805a45a89048466fb98bd0b1f02689024a Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 4 Jul 2026 18:01:55 +0800 Subject: [PATCH 67/70] opt: disable gesture when disable_trackpad --- src/mango.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/mango.c b/src/mango.c index 1609a97e..a52294e0 100644 --- a/src/mango.c +++ b/src/mango.c @@ -2234,6 +2234,10 @@ int32_t ongesture(struct wlr_pointer_swipe_end_event *event) { void swipe_begin(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_begin_event *event = data; + if (config.disable_trackpad) { + return; + } + // Forward swipe begin event to client wlr_pointer_gestures_v1_send_swipe_begin(pointer_gestures, seat, event->time_msec, event->fingers); @@ -2242,6 +2246,10 @@ void swipe_begin(struct wl_listener *listener, void *data) { void swipe_update(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_update_event *event = data; + if (config.disable_trackpad) { + return; + } + swipe_fingers = event->fingers; // Accumulate swipe distance swipe_dx += event->dx; @@ -2254,6 +2262,11 @@ void swipe_update(struct wl_listener *listener, void *data) { void swipe_end(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_end_event *event = data; + + if (config.disable_trackpad) { + return; + } + ongesture(event); swipe_dx = 0; swipe_dy = 0; @@ -2265,6 +2278,10 @@ void swipe_end(struct wl_listener *listener, void *data) { void pinch_begin(struct wl_listener *listener, void *data) { struct wlr_pointer_pinch_begin_event *event = data; + if (config.disable_trackpad) { + return; + } + // Forward pinch begin event to client wlr_pointer_gestures_v1_send_pinch_begin(pointer_gestures, seat, event->time_msec, event->fingers); @@ -2273,6 +2290,10 @@ void pinch_begin(struct wl_listener *listener, void *data) { void pinch_update(struct wl_listener *listener, void *data) { struct wlr_pointer_pinch_update_event *event = data; + if (config.disable_trackpad) { + return; + } + // Forward pinch update event to client wlr_pointer_gestures_v1_send_pinch_update( pointer_gestures, seat, event->time_msec, event->dx, event->dy, @@ -2282,6 +2303,10 @@ void pinch_update(struct wl_listener *listener, void *data) { void pinch_end(struct wl_listener *listener, void *data) { struct wlr_pointer_pinch_end_event *event = data; + if (config.disable_trackpad) { + return; + } + // Forward pinch end event to client wlr_pointer_gestures_v1_send_pinch_end(pointer_gestures, seat, event->time_msec, event->cancelled); @@ -2290,6 +2315,10 @@ void pinch_end(struct wl_listener *listener, void *data) { void hold_begin(struct wl_listener *listener, void *data) { struct wlr_pointer_hold_begin_event *event = data; + if (config.disable_trackpad) { + return; + } + // Forward hold begin event to client wlr_pointer_gestures_v1_send_hold_begin(pointer_gestures, seat, event->time_msec, event->fingers); @@ -2298,6 +2327,10 @@ void hold_begin(struct wl_listener *listener, void *data) { void hold_end(struct wl_listener *listener, void *data) { struct wlr_pointer_hold_end_event *event = data; + if (config.disable_trackpad) { + return; + } + // Forward hold end event to client wlr_pointer_gestures_v1_send_hold_end(pointer_gestures, seat, event->time_msec, event->cancelled); From 516052ff14321215f8ab4a6edb5e63711dcc9f7a Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Mon, 6 Jul 2026 13:54:28 +0800 Subject: [PATCH 68/70] opt: rename some var --- docs/bindings/keys.md | 6 +++--- src/config/parse_config.h | 12 ++++++------ src/dispatch/bind_declare.h | 6 +++--- src/dispatch/bind_define.h | 18 +++++++++--------- src/mango.c | 8 ++++---- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index f9dbc4d5..d0c13a0a 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -177,9 +177,9 @@ bindr=Super,Super_L,spawn,rofi -show run | `setkeymode` | `mode` | Set keymode. | | `switch_keyboard_layout` | `[index]` | Switch keyboard layout. Optional index (0, 1, 2...) to switch to specific layout. | | `setoption` | `key,value` | Set config option temporarily. | -| `dpms_off_monitor` | `monitor_spec` | Shutdown monitor power but not remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | -| `dpms_on_monitor` | `monitor_spec` | Turn on monitor power. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | -| `dpms_toggle_monitor` | `monitor_spec` | Toggle monitor power but not remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). +| `sleep_monitor` | `monitor_spec` | Shutdown monitor power but not remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | +| `wakeup_monitor` | `monitor_spec` | Turn on monitor power. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | +| `sleep_toggle_monitor` | `monitor_spec` | Toggle monitor power but not remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | `disable_monitor` | `monitor_spec` | remove monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | | `enable_monitor` | `monitor_spec` | add monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | | `toggle_monitor` | `monitor_spec` | Toggle monitor add/remove. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). | diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 1cdc01cf..e4b6d7a8 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1297,14 +1297,14 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "toggle_monitor") == 0) { func = toggle_monitor; (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "dpms_off_monitor") == 0) { - func = dpms_off_monitor; + } else if (strcmp(func_name, "sleep_monitor") == 0) { + func = sleep_monitor; (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "dpms_on_monitor") == 0) { - func = dpms_on_monitor; + } else if (strcmp(func_name, "wakeup_monitor") == 0) { + func = wakeup_monitor; (*arg).v = strdup(arg_value); - } else if (strcmp(func_name, "dpms_toggle_monitor") == 0) { - func = dpms_toggle_monitor; + } else if (strcmp(func_name, "sleep_toggle_monitor") == 0) { + func = sleep_toggle_monitor; (*arg).v = strdup(arg_value); } else if (strcmp(func_name, "scroller_stack") == 0) { func = scroller_stack; diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 4cebd213..5f6e0f85 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -73,9 +73,9 @@ int32_t setoption(const Arg *arg); int32_t disable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg); int32_t toggle_monitor(const Arg *arg); -int32_t dpms_off_monitor(const Arg *arg); -int32_t dpms_on_monitor(const Arg *arg); -int32_t dpms_toggle_monitor(const Arg *arg); +int32_t sleep_monitor(const Arg *arg); +int32_t wakeup_monitor(const Arg *arg); +int32_t sleep_toggle_monitor(const Arg *arg); int32_t scroller_stack(const Arg *arg); int32_t toggle_all_floating(const Arg *arg); int32_t dwindle_toggle_split_direction(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index c449f53d..3258b20d 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1938,7 +1938,7 @@ int32_t disable_monitor(const Arg *arg) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, false); mango_output_commit(m); - m->only_dpms_off = 0; + m->only_sleep = 0; updatemons(NULL, NULL); break; } @@ -1952,7 +1952,7 @@ int32_t enable_monitor(const Arg *arg) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, true); mango_output_commit(m); - m->only_dpms_off = 0; + m->only_sleep = 0; updatemons(NULL, NULL); break; } @@ -1966,7 +1966,7 @@ int32_t toggle_monitor(const Arg *arg) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, !m->wlr_output->enabled); mango_output_commit(m); - m->only_dpms_off = 0; + m->only_sleep = 0; updatemons(NULL, NULL); break; } @@ -1974,14 +1974,14 @@ int32_t toggle_monitor(const Arg *arg) { return 0; } -int32_t dpms_off_monitor(const Arg *arg) { +int32_t sleep_monitor(const Arg *arg) { Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, false); mango_output_commit(m); - m->only_dpms_off = 1; + m->only_sleep = 1; updatemons(NULL, NULL); break; } @@ -1989,13 +1989,13 @@ int32_t dpms_off_monitor(const Arg *arg) { return 0; } -int32_t dpms_on_monitor(const Arg *arg) { +int32_t wakeup_monitor(const Arg *arg) { Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, true); mango_output_commit(m); - m->only_dpms_off = 0; + m->only_sleep = 0; updatemons(NULL, NULL); break; } @@ -2003,13 +2003,13 @@ int32_t dpms_on_monitor(const Arg *arg) { return 0; } -int32_t dpms_toggle_monitor(const Arg *arg) { +int32_t sleep_toggle_monitor(const Arg *arg) { Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (match_monitor_spec(arg->v, m)) { wlr_output_state_set_enabled(&m->pending, !m->wlr_output->enabled); mango_output_commit(m); - m->only_dpms_off = !m->wlr_output->enabled; + m->only_sleep = !m->wlr_output->enabled; updatemons(NULL, NULL); break; } diff --git a/src/mango.c b/src/mango.c index a52294e0..6c318ceb 100644 --- a/src/mango.c +++ b/src/mango.c @@ -588,7 +588,7 @@ struct Monitor { int32_t isoverview; int32_t is_jump_mode; int32_t is_in_hotarea; - int32_t only_dpms_off; + int32_t only_sleep; uint32_t visible_clients; uint32_t visible_tiling_clients; uint32_t visible_scroll_tiling_clients; @@ -5164,7 +5164,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test) { /* Ensure displays previously disabled by * wlr-output-power-management-v1 are properly handled*/ - m->only_dpms_off = 0; + m->only_sleep = 0; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, config_head->state.enabled); @@ -5314,7 +5314,7 @@ void powermgrsetmode(struct wl_listener *listener, void *data) { wlr_output_state_set_enabled(&m->pending, event->mode); mango_output_commit(m); - m->only_dpms_off = !event->mode; + m->only_sleep = !event->mode; updatemons(NULL, NULL); } @@ -6967,7 +6967,7 @@ void updatemons(struct wl_listener *listener, void *data) { m->wlr_output); config_head->state.enabled = 0; - if (m->only_dpms_off) { + if (m->only_sleep) { continue; } /* Remove this output from the layout to avoid cursor enter inside From 1da0cfba47b1bf62425a296345fce203f27828c9 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 7 Jul 2026 13:31:53 +0800 Subject: [PATCH 69/70] opt: Avoid unnecessary animation calculations --- src/animation/client.h | 809 ++++++++++++++----------------------- src/dispatch/bind_define.h | 3 +- src/mango.c | 2 +- 3 files changed, 313 insertions(+), 501 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 2c39e511..03466f37 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1,7 +1,27 @@ #include "wlr/util/log.h" + +static inline bool client_is_ignore_output_clip(Client *c) { + return c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && + !c->animation.tagouting); +} + +static inline struct ivec2 compute_edge_offsets(Client *c) { + struct ivec2 offsets = {0}; + if (client_is_ignore_output_clip(c)) + return offsets; + + struct wlr_box cur = c->animation.current; + offsets.width = + GEZERO(cur.x + cur.width - c->mon->m.x - c->mon->m.width); // right + offsets.height = + GEZERO(cur.y + cur.height - c->mon->m.y - c->mon->m.height); // bottom + offsets.x = GEZERO(c->mon->m.x - cur.x); // left + offsets.y = GEZERO(c->mon->m.y - cur.y); // top + return offsets; +} + void client_actual_size(Client *c, int32_t *width, int32_t *height) { *width = c->animation.current.width - 2 * (int32_t)c->bw; - *height = c->animation.current.height - 2 * (int32_t)c->bw; } @@ -13,55 +33,48 @@ struct fx_corner_radii set_client_corner_location(Client *c) { struct fx_corner_radii current_corner_location = corner_radii_all(config.border_radius); - if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && - !c->animation.tagouting)) { + if (client_is_ignore_output_clip(c)) return current_corner_location; - } struct wlr_box target_geom = config.animations ? c->animation.current : c->geom; if (target_geom.x + config.border_radius <= c->mon->m.x) { - current_corner_location.top_left = 0; // 清除左标志位 - current_corner_location.bottom_left = 0; // 清除左标志位 + current_corner_location.top_left = 0; + current_corner_location.bottom_left = 0; } if (target_geom.x + target_geom.width - config.border_radius >= c->mon->m.x + c->mon->m.width) { - current_corner_location.top_right = 0; // 清除右标志位 - current_corner_location.bottom_right = 0; // 清除右标志位 + current_corner_location.top_right = 0; + current_corner_location.bottom_right = 0; } if (target_geom.y + config.border_radius <= c->mon->m.y) { - current_corner_location.top_left = 0; // 清除上标志位 - current_corner_location.top_right = 0; // 清除上标志位 + current_corner_location.top_left = 0; + current_corner_location.top_right = 0; } if (target_geom.y + target_geom.height - config.border_radius >= c->mon->m.y + c->mon->m.height) { - current_corner_location.bottom_left = 0; // 清除下标志位 - current_corner_location.bottom_right = 0; // 清除下标志位 + current_corner_location.bottom_left = 0; + current_corner_location.bottom_right = 0; } return current_corner_location; } bool is_horizontal_stack_layout(Monitor *m) { - if (m->pertag->curtag && (m->pertag->ltidxs[m->pertag->curtag]->id == TILE || m->pertag->ltidxs[m->pertag->curtag]->id == DECK)) return true; - return false; } bool is_horizontal_right_stack_layout(Monitor *m) { - if (m->pertag->curtag && - (m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE)) + m->pertag->ltidxs[m->pertag->curtag]->id == RIGHT_TILE) return true; - return false; } int32_t is_special_animation_rule(Client *c) { - if (is_scroller_layout(c->mon) && !c->isfloating) { return DOWN; } else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) { @@ -182,30 +195,25 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, BufferData *buffer_data = (BufferData *)data; if (buffer_data->should_scale && buffer_data->height_scale < 1 && - buffer_data->width_scale < 1) { + buffer_data->width_scale < 1) buffer_data->should_scale = false; - } if (buffer_data->should_scale && buffer_data->height_scale == 1 && - buffer_data->width_scale < 1) { + buffer_data->width_scale < 1) buffer_data->should_scale = false; - } if (buffer_data->should_scale && buffer_data->height_scale < 1 && - buffer_data->width_scale == 1) { + buffer_data->width_scale == 1) buffer_data->should_scale = false; - } 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) { - int32_t surface_width = surface->current.width; int32_t surface_height = surface->current.height; @@ -217,31 +225,25 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx, : buffer_data->height_scale * surface_height; if (surface_width > buffer_data->width && - wlr_subsurface_try_from_wlr_surface(surface) == NULL) { + wlr_subsurface_try_from_wlr_surface(surface) == NULL) surface_width = buffer_data->width; - } if (surface_height > buffer_data->height && - wlr_subsurface_try_from_wlr_surface(surface) == NULL) { + wlr_subsurface_try_from_wlr_surface(surface) == NULL) surface_height = buffer_data->height; - } if (surface_width > buffer_data->width && - wlr_subsurface_try_from_wlr_surface(surface) != NULL) { + wlr_subsurface_try_from_wlr_surface(surface) != NULL) return; - } if (surface_height > buffer_data->height && - wlr_subsurface_try_from_wlr_surface(surface) != NULL) { + wlr_subsurface_try_from_wlr_surface(surface) != NULL) return; - } - if (surface_height > 0 && surface_width > 0) { + 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; @@ -253,8 +255,7 @@ void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer, int32_t sx, int32_t sy, void *data) { BufferData *buffer_data = (BufferData *)data; - int32_t surface_width = 0; - int32_t surface_height = 0; + int32_t surface_width = 0, surface_height = 0; bool is_subsurface = false; struct wlr_scene_tree *parent_tree = buffer->node.parent; @@ -284,38 +285,32 @@ void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer, } void buffer_set_effect(Client *c, BufferData data) { - if (!c || c->iskilling) return; if (c->animation.tagouting || c->animation.tagouted || - c->animation.tagining) { + c->animation.tagining) data.should_scale = false; - } if (c == grabc) data.should_scale = false; if (c->isfullscreen || (config.no_radius_when_single && c->mon && - c->mon->visible_tiling_clients == 1)) { + c->mon->visible_tiling_clients == 1)) data.corner_location = corner_radii_none(); - } - if (config.blur && !c->noblur) { + if (config.blur && !c->noblur) wlr_scene_blur_set_corner_radii(c->blur, data.corner_location); - } - if (c->overview_scene_surface) { + if (c->overview_scene_surface) wlr_scene_node_for_each_buffer( &c->scene_surface->node, scene_buffer_apply_overview_effect, &data); - } else { + else wlr_scene_node_for_each_buffer(&c->scene_surface->node, scene_buffer_apply_effect, &data); - } } -void client_draw_shadow(Client *c) { - +void client_draw_shadow(Client *c, struct ivec2 offsets) { if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow) return; @@ -336,18 +331,19 @@ void client_draw_shadow(Client *c) { ? corner_radii_none() : set_client_corner_location(c); - int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0; + int32_t bw = (int32_t)c->bw; + int32_t bwoffset = bw != 0 && hit_no_border ? bw : 0; int32_t width, height; client_actual_size(c, &width, &height); - int32_t delta = config.shadows_size + (int32_t)c->bw - bwoffset; + int32_t delta = config.shadows_size + bw - bwoffset; struct wlr_box client_box = { .x = bwoffset, .y = bwoffset, - .width = width + 2 * (int32_t)c->bw - 2 * bwoffset, - .height = height + 2 * (int32_t)c->bw - 2 * bwoffset, + .width = width + 2 * bw - 2 * bwoffset, + .height = height + 2 * bw - 2 * bwoffset, }; struct wlr_box shadow_box = { @@ -367,53 +363,45 @@ void client_draw_shadow(Client *c) { .corners = current_corner_location, }; + struct wlr_box cur = c->animation.current; struct wlr_box absolute_shadow_box = { - .x = shadow_box.x + c->animation.current.x, - .y = shadow_box.y + c->animation.current.y, + .x = shadow_box.x + cur.x, + .y = shadow_box.y + cur.y, .width = shadow_box.width, .height = shadow_box.height, }; - int32_t right_offset, bottom_offset, left_offset, top_offset; - - if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && - !c->animation.tagouting)) { - right_offset = 0; - bottom_offset = 0; - left_offset = 0; - top_offset = 0; + int32_t left, right, top, bottom; + if (client_is_ignore_output_clip(c)) { + left = right = top = bottom = 0; } else { - right_offset = - GEZERO(absolute_shadow_box.x + absolute_shadow_box.width - - c->mon->m.x - c->mon->m.width); - bottom_offset = - GEZERO(absolute_shadow_box.y + absolute_shadow_box.height - - c->mon->m.y - c->mon->m.height); - - left_offset = GEZERO(c->mon->m.x - absolute_shadow_box.x); - top_offset = GEZERO(c->mon->m.y - absolute_shadow_box.y); + right = GEZERO(absolute_shadow_box.x + absolute_shadow_box.width - + c->mon->m.x - c->mon->m.width); + bottom = GEZERO(absolute_shadow_box.y + absolute_shadow_box.height - + c->mon->m.y - c->mon->m.height); + left = GEZERO(c->mon->m.x - absolute_shadow_box.x); + top = GEZERO(c->mon->m.y - absolute_shadow_box.y); } - left_offset = MANGO_MIN(left_offset, shadow_box.width); - right_offset = MANGO_MIN(right_offset, shadow_box.width); - top_offset = MANGO_MIN(top_offset, shadow_box.height); - bottom_offset = MANGO_MIN(bottom_offset, shadow_box.height); + left = MANGO_MIN(left, shadow_box.width); + right = MANGO_MIN(right, shadow_box.width); + top = MANGO_MIN(top, shadow_box.height); + bottom = MANGO_MIN(bottom, shadow_box.height); - wlr_scene_node_set_position(&c->shadow->node, shadow_box.x + left_offset, - shadow_box.y + top_offset); + wlr_scene_node_set_position(&c->shadow->node, shadow_box.x + left, + shadow_box.y + top); - wlr_scene_shadow_set_size( - c->shadow, GEZERO(shadow_box.width - left_offset - right_offset), - GEZERO(shadow_box.height - top_offset - bottom_offset)); + wlr_scene_shadow_set_size(c->shadow, + GEZERO(shadow_box.width - left - right), + GEZERO(shadow_box.height - top - bottom)); - clipped_region.area.x = clipped_region.area.x - left_offset; - clipped_region.area.y = clipped_region.area.y - top_offset; + clipped_region.area.x = clipped_region.area.x - left; + clipped_region.area.y = clipped_region.area.y - top; wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } -void client_draw_groupbar(Client *c) { - +void client_draw_groupbar(Client *c, struct ivec2 offsets) { if (!c || !c->group_bar) return; @@ -445,30 +433,19 @@ void client_draw_groupbar(Client *c) { int32_t tw = c->animation.current.width; int32_t th = config.group_bar_height; - int32_t left_over = c->mon->m.x - tab_x; - int32_t right_over = tab_x + tw - c->mon->m.x - c->mon->m.width; - int32_t top_over = c->mon->m.y - tab_y; - int32_t bottom_over = - tab_y + config.group_bar_height - c->mon->m.y - c->mon->m.height; - - if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && - !c->animation.tagouting)) { - top_over = 0; - bottom_over = 0; - left_over = 0; - right_over = 0; - } + int32_t top_over = offsets.y; // top + int32_t bottom_over = offsets.height; // bottom + int32_t left_over = offsets.x; // left + int32_t right_over = offsets.width; // right if (top_over > 0) { tab_y = c->mon->m.y; th = config.group_bar_height - top_over; } - if (bottom_over > 0) { + if (bottom_over > 0) th = th - bottom_over; - } - if (right_over > 0) { + if (right_over > 0) tw = tw - right_over; - } if (left_over > 0) { tab_x = c->mon->m.x; tw = tw - left_over; @@ -500,34 +477,14 @@ void client_draw_groupbar(Client *c) { } } -void client_draw_shield(Client *c, struct wlr_box clip_box) { +void client_draw_shield(Client *c, struct ivec2 clip_box) { - if (clip_box.width <= 0 || clip_box.height <= 0) { - wlr_scene_node_set_enabled(&c->shield->node, false); - return; - } + int32_t shield_x = 0; + int32_t shield_y = 0; + int32_t shield_width = 0; + int32_t shield_height = 0; - struct wlr_box surface_relative_geom; - client_get_clip(c, &surface_relative_geom); - - if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && - !c->animation.tagouting)) { - clip_box.x = surface_relative_geom.x; - clip_box.y = surface_relative_geom.y; - clip_box.width = c->animation.current.width - 2 * (int32_t)c->bw; - clip_box.height = c->animation.current.height - 2 * (int32_t)c->bw; - } - - if (active_capture_count > 0 && c->shield_when_capture) { - int32_t shield_x = - clip_box.x - surface_relative_geom.x + (int32_t)c->bw; - int32_t shield_y = - clip_box.y - surface_relative_geom.y + (int32_t)c->bw; - wlr_scene_node_raise_to_top(&c->shield->node); - wlr_scene_node_set_position(&c->shield->node, shield_x, shield_y); - wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.height); - wlr_scene_node_set_enabled(&c->shield->node, true); - } else { + if (active_capture_count <= 0 || !c->shield_when_capture) { if (c->shield->node.enabled) { wlr_scene_node_lower_to_bottom(&c->shield->node); wlr_scene_node_set_position(&c->shield->node, 0, 0); @@ -535,41 +492,66 @@ void client_draw_shield(Client *c, struct wlr_box clip_box) { c->animation.current.height); wlr_scene_node_set_enabled(&c->shield->node, false); } + return; } + + if (client_is_ignore_output_clip(c)) { + shield_x = c->bw; + shield_y = c->bw; + shield_width = c->animation.current.width - 2 * (int32_t)c->bw; + shield_height = c->animation.current.height - 2 * (int32_t)c->bw; + } else { + shield_x = clip_box.x + (int32_t)c->bw; + shield_y = clip_box.y + (int32_t)c->bw; + shield_width = c->animation.current.width - 2 * (int32_t)c->bw - + clip_box.width - clip_box.x; + shield_height = c->animation.current.height - 2 * (int32_t)c->bw - + clip_box.height - clip_box.y; + } + + if (shield_width <= 0 || shield_height <= 0) { + wlr_scene_node_set_enabled(&c->shield->node, false); + return; + } + + wlr_scene_node_raise_to_top(&c->shield->node); + wlr_scene_node_set_position(&c->shield->node, shield_x, shield_y); + wlr_scene_rect_set_size(c->shield, shield_width, shield_height); + wlr_scene_node_set_enabled(&c->shield->node, true); } -void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) { +void client_draw_blur(Client *c, struct ivec2 clip_box) { if (c->isfullscreen) { - if (c->blur->node.enabled) { + if (c->blur->node.enabled) wlr_scene_node_set_enabled(&c->blur->node, false); - } return; - } else { - if (config.blur && !c->noblur) { + } - struct wlr_box surface_relative_geom; - client_get_clip(c, &surface_relative_geom); + int32_t blur_x = 0; + int32_t blur_y = 0; + int32_t blur_width = 0; + int32_t blur_height = 0; - if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && - !c->animation.tagouting)) { - clip_box.x = surface_relative_geom.x; - clip_box.y = surface_relative_geom.y; - clip_box.width = - c->animation.current.width - 2 * (int32_t)c->bw; - clip_box.height = - c->animation.current.height - 2 * (int32_t)c->bw; - } + if (config.blur && !c->noblur) { - int32_t blur_x = - clip_box.x - surface_relative_geom.x + (int32_t)c->bw; - int32_t blur_y = - clip_box.y - surface_relative_geom.y + (int32_t)c->bw; - wlr_scene_node_set_enabled(&c->blur->node, true); - wlr_scene_node_set_position(&c->blur->node, blur_x, blur_y); - wlr_scene_blur_set_size(c->blur, clip_box.width, clip_box.height); - } else { - wlr_scene_node_set_enabled(&c->blur->node, false); + if (client_is_ignore_output_clip(c)) { + blur_x = c->bw; + blur_y = c->bw; + blur_width = c->animation.current.width - 2 * (int32_t)c->bw; + blur_height = c->animation.current.height - 2 * (int32_t)c->bw; } + + blur_x = clip_box.x + (int32_t)c->bw; + blur_y = clip_box.y + (int32_t)c->bw; + blur_width = c->animation.current.width - 2 * (int32_t)c->bw - + clip_box.width - clip_box.x; + blur_height = c->animation.current.height - 2 * (int32_t)c->bw - + clip_box.height - clip_box.y; + wlr_scene_node_set_enabled(&c->blur->node, true); + wlr_scene_node_set_position(&c->blur->node, blur_x, blur_y); + wlr_scene_blur_set_size(c->blur, blur_width, blur_height); + } else { + wlr_scene_node_set_enabled(&c->blur->node, false); } } @@ -582,8 +564,8 @@ void global_draw_group_bar(Client *c, int32_t x, int32_t y, int32_t width, mango_group_bar_set_size(c->group_bar, width, height); } -void client_draw_split_border(Client *c, bool hit_no_border) { - +void client_draw_split_border(Client *c, bool hit_no_border, + struct ivec2 offsets) { if (c->iskilling || !c->mon || !client_surface(c)->mapped) return; @@ -591,78 +573,55 @@ void client_draw_split_border(Client *c, bool hit_no_border) { if (hit_no_border || !ISTILED(c) || layout->id != DWINDLE || !config.dwindle_manual_split || c->isfullscreen) { - if (c->splitindicator[0]->node.enabled) { + if (c->splitindicator[0]->node.enabled) wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); - } - if (c->splitindicator[1]->node.enabled) { + if (c->splitindicator[1]->node.enabled) wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); - } return; + } + + DwindleNode **root = &c->mon->pertag->dwindle_root[c->mon->pertag->curtag]; + DwindleNode *dnode = dwindle_find_leaf(*root, c); + if (!dnode) { + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); + return; + } + + if (dnode->custom_leaf_split_h) { + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, true); } else { - - DwindleNode **root = - &c->mon->pertag->dwindle_root[c->mon->pertag->curtag]; - DwindleNode *dnode = dwindle_find_leaf(*root, c); - - if (!dnode) { - wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); - wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); - return; - } else { - if (dnode->custom_leaf_split_h) { - wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); - wlr_scene_node_set_enabled(&c->splitindicator[1]->node, true); - } else { - wlr_scene_node_set_enabled(&c->splitindicator[0]->node, true); - wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); - } - } + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, true); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); } struct wlr_box fullgeom = c->animation.current; - // 一但在GEZERO如果使用无符号,那么其他数据也会转换为无符号导致没有负数出错 int32_t bw = (int32_t)c->bw; - - int32_t right_offset, bottom_offset, left_offset, top_offset; - - if (c == grabc) { - right_offset = 0; - bottom_offset = 0; - left_offset = 0; - top_offset = 0; - } else { - right_offset = - GEZERO(c->animation.current.x + c->animation.current.width - - c->mon->m.x - c->mon->m.width); - bottom_offset = - GEZERO(c->animation.current.y + c->animation.current.height - - c->mon->m.y - c->mon->m.height); - - left_offset = GEZERO(c->mon->m.x - c->animation.current.x); - top_offset = GEZERO(c->mon->m.y - c->animation.current.y); - } + int32_t left = offsets.x, right = offsets.width, top = offsets.y, + bottom = offsets.height; int32_t border_down_width = GEZERO(fullgeom.width - 2 * config.border_radius - - GEZERO((left_offset + right_offset) - config.border_radius)); + GEZERO((left + right) - config.border_radius)); int32_t border_down_height = - GEZERO(bw - bottom_offset - GEZERO(top_offset + bw - fullgeom.height)); + GEZERO(bw - bottom - GEZERO(top + bw - fullgeom.height)); int32_t border_right_width = - GEZERO(bw - right_offset - GEZERO(left_offset + bw - fullgeom.width)); + GEZERO(bw - right - GEZERO(left + bw - fullgeom.width)); int32_t border_right_height = GEZERO(fullgeom.height - 2 * config.border_radius - - GEZERO((top_offset + bottom_offset) - config.border_radius)); + GEZERO((top + bottom) - config.border_radius)); - int32_t border_down_x = GEZERO(config.border_radius + - GEZERO(left_offset - config.border_radius)); - int32_t border_down_y = GEZERO(fullgeom.height - bw) + - GEZERO(top_offset + bw - fullgeom.height); + int32_t border_down_x = + GEZERO(config.border_radius + GEZERO(left - config.border_radius)); + int32_t border_down_y = + GEZERO(fullgeom.height - bw) + GEZERO(top + bw - fullgeom.height); int32_t border_right_x = - GEZERO(fullgeom.width - bw) + GEZERO(left_offset + bw - fullgeom.width); - int32_t border_right_y = GEZERO(config.border_radius + - GEZERO(top_offset - config.border_radius)); + GEZERO(fullgeom.width - bw) + GEZERO(left + bw - fullgeom.width); + int32_t border_right_y = + GEZERO(config.border_radius + GEZERO(top - config.border_radius)); set_rect_size(c->splitindicator[0], border_down_width, border_down_height); set_rect_size(c->splitindicator[1], border_right_width, @@ -673,7 +632,7 @@ void client_draw_split_border(Client *c, bool hit_no_border) { border_right_y); } -void client_draw_border(Client *c) { +void client_draw_border(Client *c, struct ivec2 offsets) { if (!c || c->iskilling || !client_surface(c)->mapped) return; @@ -686,13 +645,12 @@ void client_draw_border(Client *c) { } return; } else { - if (!c->border->node.enabled) { + if (!c->border->node.enabled) wlr_scene_node_set_enabled(&c->border->node, true); - } } bool hit_no_border = check_hit_no_border(c); - client_draw_split_border(c, hit_no_border); + client_draw_split_border(c, hit_no_border, offsets); struct fx_corner_radii current_corner_location = c->isfullscreen || (config.no_radius_when_single && c->mon && @@ -713,61 +671,30 @@ void client_draw_border(Client *c) { c->fake_no_border = false; } - struct wlr_box clip_box = c->animation.current; - // 一但在GEZERO如果使用无符号,那么其他数据也会转换为无符号导致没有负数出错 + struct wlr_box cur = c->animation.current; int32_t bw = (int32_t)c->bw; + int32_t left = offsets.x, right = offsets.width, top = offsets.y, + bottom = offsets.height; - int32_t right_offset, bottom_offset, left_offset, top_offset; + int32_t inner_surface_width = GEZERO(cur.width - 2 * bw); + int32_t inner_surface_height = GEZERO(cur.height - 2 * bw); + int32_t inner_surface_x = GEZERO(bw - left); + int32_t inner_surface_y = GEZERO(bw - top); - if (c == grabc || (!ISSCROLLTILED(c) && !c->animation.tagining && - !c->animation.tagouting)) { - right_offset = 0; - bottom_offset = 0; - left_offset = 0; - top_offset = 0; - } else { - right_offset = - GEZERO(c->animation.current.x + c->animation.current.width - - c->mon->m.x - c->mon->m.width); - bottom_offset = - GEZERO(c->animation.current.y + c->animation.current.height - - c->mon->m.y - c->mon->m.height); + int32_t rect_x = left; + int32_t rect_y = top; + int32_t rect_width = GEZERO(cur.width - left - right); + int32_t rect_height = GEZERO(cur.height - top - bottom); - left_offset = GEZERO(c->mon->m.x - c->animation.current.x); - top_offset = GEZERO(c->mon->m.y - c->animation.current.y); - } - - int32_t inner_surface_width = GEZERO(clip_box.width - 2 * bw); - int32_t inner_surface_height = GEZERO(clip_box.height - 2 * bw); - - int32_t inner_surface_x = GEZERO(bw - left_offset); - int32_t inner_surface_y = GEZERO(bw - top_offset); - - int32_t rect_x = left_offset; - int32_t rect_y = top_offset; - - int32_t rect_width = - GEZERO(c->animation.current.width - left_offset - right_offset); - int32_t rect_height = - GEZERO(c->animation.current.height - top_offset - bottom_offset); - - if (left_offset > c->bw) - inner_surface_width = - inner_surface_width - left_offset + (int32_t)c->bw; - - if (top_offset > c->bw) + if (left > c->bw) + inner_surface_width = inner_surface_width - left + bw; + if (top > c->bw) + inner_surface_height = inner_surface_height - top + bw; + if (right > 0) + inner_surface_width = MANGO_MIN(cur.width, inner_surface_width + right); + if (bottom > 0) inner_surface_height = - inner_surface_height - top_offset + (int32_t)c->bw; - - if (right_offset > 0) { - inner_surface_width = - MANGO_MIN(clip_box.width, inner_surface_width + right_offset); - } - - if (bottom_offset > 0) { - inner_surface_height = - MANGO_MIN(clip_box.height, inner_surface_height + bottom_offset); - } + MANGO_MIN(cur.height, inner_surface_height + bottom); struct clipped_region clipped_region = { .area = {inner_surface_x, inner_surface_y, inner_surface_width, @@ -782,57 +709,35 @@ void client_draw_border(Client *c) { wlr_scene_rect_set_clipped_region(c->border, clipped_region); } -struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { - int32_t offsetx = 0, offsety = 0, offsetw = 0, offseth = 0; - struct ivec2 offset = {0, 0, 0, 0}; +struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box, + struct ivec2 offsets) { + struct ivec2 offset = {0}; if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted && !c->animation.tagouting) return offset; - int32_t bottom_out_offset = - GEZERO(c->animation.current.y + c->animation.current.height - - c->mon->m.y - c->mon->m.height); - int32_t right_out_offset = - GEZERO(c->animation.current.x + c->animation.current.width - - c->mon->m.x - c->mon->m.width); - int32_t left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x); - int32_t top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y); - - // 必须转换为int,否计算会没有负数导致判断错误 int32_t bw = (int32_t)c->bw; + int32_t left = offsets.x, right = offsets.width, top = offsets.y, + bottom = offsets.height; - /* - 计算窗口表面超出屏幕四个方向的偏差,避免窗口超出屏幕 - 需要主要border超出屏幕的时候不计算如偏差之内而是 - 要等窗口表面超出才开始计算偏差 - */ - if (ISSCROLLTILED(c) || c->animation.tagining || c->animation.tagouted || - c->animation.tagouting) { - if (left_out_offset > 0) { - offsetx = GEZERO(left_out_offset - bw); - clip_box->x = clip_box->x + offsetx; - clip_box->width = clip_box->width - offsetx; - } else if (right_out_offset > 0) { - offsetw = GEZERO(right_out_offset - bw); - clip_box->width = clip_box->width - offsetw; - } - - if (top_out_offset > 0) { - offsety = GEZERO(top_out_offset - bw); - clip_box->y = clip_box->y + offsety; - clip_box->height = clip_box->height - offsety; - } else if (bottom_out_offset > 0) { - offseth = GEZERO(bottom_out_offset - bw); - clip_box->height = clip_box->height - offseth; - } + if (left > 0) { + offset.x = GEZERO(left - bw); + clip_box->x += offset.x; + clip_box->width -= offset.x; + } else if (right > 0) { + offset.width = GEZERO(right - bw); + clip_box->width -= offset.width; } - // 窗口表面超出屏幕四个方向的偏差 - offset.x = offsetx; - offset.y = offsety; - offset.width = offsetw; - offset.height = offseth; + if (top > 0) { + offset.y = GEZERO(top - bw); + clip_box->y += offset.y; + clip_box->height -= offset.y; + } else if (bottom > 0) { + offset.height = GEZERO(bottom - bw); + clip_box->height -= offset.height; + } if ((clip_box->width + bw <= 0 || clip_box->height + bw <= 0) && (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { @@ -854,9 +759,8 @@ void client_set_drop_area(Client *c) { if (!c || !c->mon) return; - if (!c->enable_drop_area_draw && !c->droparea->node.enabled) { + if (!c->enable_drop_area_draw && !c->droparea->node.enabled) return; - } if (!c->enable_drop_area_draw && c->droparea->node.enabled) { wlr_scene_node_lower_to_bottom(&c->droparea->node); @@ -872,12 +776,10 @@ void client_set_drop_area(Client *c) { int32_t client_width = c->geom.width - 2 * bw; int32_t client_height = c->geom.height - 2 * bw; - // 光标在窗口客户区内的相对坐标 double rel_x = cursor->x - c->geom.x - bw; double rel_y = cursor->y - c->geom.y - bw; struct wlr_box drop_box; - const Layout *cur_layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag]; bool dwindle_familiar = cur_layout->id == DWINDLE && config.dwindle_drop_simple_split; @@ -917,7 +819,6 @@ void client_set_drop_area(Client *c) { } } else if (cur_layout->id == TILE || cur_layout->id == DECK || cur_layout->id == CENTER_TILE || cur_layout->id == RIGHT_TILE) { - if (c->ismaster) { if (c->mon->visible_tiling_clients == 1) { if (rel_x < client_width * 0.5) { @@ -979,7 +880,6 @@ void client_set_drop_area(Client *c) { drop_box.height = client_height; drop_direction = UNDIR; } - } else { if (rel_x < client_width * 0.5) { drop_direction = LEFT; @@ -995,7 +895,6 @@ void client_set_drop_area(Client *c) { drop_box.height = client_height; } } - } else { double dist_left = rel_x; double dist_right = client_width - rel_x; @@ -1030,23 +929,25 @@ void client_set_drop_area(Client *c) { } } - if (!first_draw && c->drop_direction == drop_direction) { + if (!first_draw && c->drop_direction == drop_direction) return; - } c->drop_direction = drop_direction; wlr_scene_node_set_position(&c->droparea->node, drop_box.x, drop_box.y); wlr_scene_rect_set_size(c->droparea, drop_box.width, drop_box.height); } -void client_apply_clip(Client *c, float factor) { +/* ---------- central rendering entry point ---------- */ +void client_apply_clip(Client *c, float factor) { if (c->iskilling || !client_surface(c)->mapped) return; + struct ivec2 offsets = compute_edge_offsets(c); + struct wlr_box clip_box; bool should_render_client_surface = false; - struct ivec2 offset; + struct ivec2 surface_clip_offset; BufferData buffer_data; struct fx_corner_radii current_corner_location = @@ -1059,14 +960,13 @@ void client_apply_clip(Client *c, float factor) { c->geom; client_get_clip(c, &clip_box); + surface_clip_offset = clip_to_hide(c, &clip_box, offsets); - offset = clip_to_hide(c, &clip_box); - - client_draw_border(c); - client_draw_shadow(c); - client_draw_groupbar(c); - client_draw_blur(c, clip_box, offset); - client_draw_shield(c, clip_box); + client_draw_border(c, offsets); + client_draw_shadow(c, offsets); + client_draw_groupbar(c, offsets); + client_draw_blur(c, surface_clip_offset); + client_draw_shield(c, surface_clip_offset); if (clip_box.width <= 0 || clip_box.height <= 0) { should_render_client_surface = false; @@ -1076,14 +976,12 @@ void client_apply_clip(Client *c, float factor) { wlr_scene_node_set_enabled(&c->scene_surface->node, true); } - if (!should_render_client_surface) { + if (!should_render_client_surface) return; - } - if (!c->overview_scene_surface) { + if (!c->overview_scene_surface) 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, @@ -1091,11 +989,9 @@ void client_apply_clip(Client *c, float factor) { return; } - // 获取窗口动画实时位置矩形 int32_t width, height; client_actual_size(c, &width, &height); - // 计算出除了边框的窗口实际剪切大小 struct wlr_box geometry; client_get_geometry(c, &geometry); clip_box = (struct wlr_box){ @@ -1110,17 +1006,14 @@ void client_apply_clip(Client *c, float factor) { clip_box.y = 0; } - // 检测窗口是否需要剪切超出屏幕部分,如果需要就调整实际要剪切的矩形 - offset = clip_to_hide(c, &clip_box); + surface_clip_offset = clip_to_hide(c, &clip_box, offsets); - // 应用窗口装饰 - client_draw_border(c); - client_draw_shadow(c); - client_draw_groupbar(c); - client_draw_shield(c, clip_box); - client_draw_blur(c, clip_box, offset); + client_draw_border(c, offsets); + client_draw_shadow(c, offsets); + client_draw_groupbar(c, offsets); + client_draw_shield(c, surface_clip_offset); + client_draw_blur(c, surface_clip_offset); - // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 if (clip_box.width <= 0 || clip_box.height <= 0) { should_render_client_surface = false; wlr_scene_node_set_enabled(&c->scene_surface->node, false); @@ -1129,21 +1022,18 @@ void client_apply_clip(Client *c, float factor) { wlr_scene_node_set_enabled(&c->scene_surface->node, true); } - // 不用在执行下面的窗口表面剪切和缩放等效果操作 - if (!should_render_client_surface) { + if (!should_render_client_surface) return; - } - // 应用窗口表面剪切 - if (!c->overview_scene_surface) { + if (!c->overview_scene_surface) wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); - } - // 获取剪切后的表面的实际大小用于计算缩放 - int32_t acutal_surface_width = geometry.width - offset.x - offset.width; - int32_t acutal_surface_height = geometry.height - offset.y - offset.height; + int32_t actual_surface_width = + geometry.width - surface_clip_offset.x - surface_clip_offset.width; + int32_t actual_surface_height = + geometry.height - surface_clip_offset.y - surface_clip_offset.height; - if (acutal_surface_width <= 0 || acutal_surface_height <= 0) + if (actual_surface_width <= 0 || actual_surface_height <= 0) return; buffer_data.should_scale = true; @@ -1156,9 +1046,9 @@ void client_apply_clip(Client *c, float factor) { buffer_data.height_scale = 1.0; } else { buffer_data.width_scale = - (float)buffer_data.width / acutal_surface_width; + (float)buffer_data.width / actual_surface_width; buffer_data.height_scale = - (float)buffer_data.height / acutal_surface_height; + (float)buffer_data.height / actual_surface_height; } buffer_set_effect(c, buffer_data); @@ -1168,8 +1058,6 @@ void fadeout_client_animation_next_tick(Client *c) { if (!c) return; - BufferData buffer_data; - struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -1179,14 +1067,13 @@ void fadeout_client_animation_next_tick(Client *c) { ? (double)passed_time / (double)c->animation.duration : 1.0; - int32_t type = c->animation.action = c->animation.action; + int32_t type = c->animation.action; double factor = find_animation_curve_at(animation_passed, type); 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; - int32_t x = c->animation.initial.x + (c->current.x - c->animation.initial.x) * factor; int32_t y = c->animation.initial.y + @@ -1203,10 +1090,8 @@ void fadeout_client_animation_next_tick(Client *c) { double opacity_eased_progress = find_animation_curve_at(animation_passed, OPAFADEOUT); - double percent = config.fadeout_begin_opacity - (opacity_eased_progress * config.fadeout_begin_opacity); - double opacity = MANGO_MAX(percent, 0); if (config.animation_fade_out && !c->nofadeout) @@ -1217,12 +1102,11 @@ void fadeout_client_animation_next_tick(Client *c) { strcmp(c->animation_type_close, "zoom") == 0) || (!c->animation_type_close && strcmp(config.animation_type_close, "zoom") == 0)) { - + BufferData buffer_data; buffer_data.width = width; buffer_data.height = height; buffer_data.width_scale = animation_passed; buffer_data.height_scale = animation_passed; - wlr_scene_node_for_each_buffer( &c->scene->node, snap_scene_buffer_apply_effect, &buffer_data); } @@ -1231,7 +1115,6 @@ void fadeout_client_animation_next_tick(Client *c) { wl_list_remove(&c->fadeout_link); wlr_scene_node_destroy(&c->scene->node); free(c); - c = NULL; } } @@ -1248,15 +1131,10 @@ void client_animation_next_tick(Client *c) { int32_t type = c->animation.action == NONE ? MOVE : c->animation.action; 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; - int32_t x = c->animation.initial.x + (c->current.x - c->animation.initial.x) * factor; int32_t y = c->animation.initial.y + @@ -1271,16 +1149,10 @@ void client_animation_next_tick(Client *c) { }; c->is_pending_open_animation = false; - client_apply_clip(c, factor); if (animation_passed >= 1.0) { - - // 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; c->animation.overining = false; @@ -1292,40 +1164,34 @@ void client_animation_next_tick(Client *c) { c->animation.current = c->geom; } + Client *pointer_c = NULL; + double sx, sy; xytonode(cursor->x, cursor->y, NULL, &pointer_c, NULL, NULL, &sx, &sy); - - surface = + struct wlr_surface *surface = pointer_c && pointer_c == c ? client_surface(pointer_c) : NULL; - // avoid game window force grab pointer in overview mode - if (surface && pointer_c == selmon->sel && !selmon->isoverview) { + if (surface && pointer_c == selmon->sel && !selmon->isoverview) wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - } - // end flush in next frame, not the current frame c->need_output_flush = false; } } void init_fadeout_client(Client *c) { - if (!c->mon || client_is_unmanaged(c)) return; - if (!c->scene) { + if (!c->scene) return; - } - if (c->shield_when_capture && active_capture_count > 0) { + if (c->shield_when_capture && active_capture_count > 0) return; - } if ((c->animation_type_close && strcmp(c->animation_type_close, "none") == 0) || (!c->animation_type_close && - strcmp(config.animation_type_close, "none") == 0)) { + strcmp(config.animation_type_close, "none") == 0)) return; - } Client *fadeout_client = ecalloc(1, sizeof(*fadeout_client)); @@ -1354,9 +1220,6 @@ void init_fadeout_client(Client *c) { fadeout_client->bw = c->bw; fadeout_client->nofadeout = c->nofadeout; - // 这里snap节点的坐标设置是使用的相对坐标,所以不能加上原来坐标 - // 这跟普通node有区别 - fadeout_client->animation.initial.x = 0; fadeout_client->animation.initial.y = 0; @@ -1374,10 +1237,9 @@ void init_fadeout_client(Client *c) { strcmp(config.animation_type_close, "slide") == 0)) { fadeout_client->current.y = 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 - fadeout_client->current.x = 0; // x无偏差,垂直划出 + ? c->mon->m.height - (c->animation.current.y - c->mon->m.y) + : c->mon->m.y - c->geom.height; + fadeout_client->current.x = 0; } else { fadeout_client->current.y = (fadeout_client->geom.height - @@ -1397,52 +1259,44 @@ void init_fadeout_client(Client *c) { wlr_scene_node_set_enabled(&fadeout_client->scene->node, true); wl_list_insert(&fadeout_clients, &fadeout_client->fadeout_link); - // 请求刷新屏幕 request_fresh_all_monitors(); } void client_commit(Client *c) { - c->current = c->pending; // 设置动画的结束位置 + c->current = c->pending; if (c->animation.should_animate) { - if (!c->animation.running) { + if (!c->animation.running) c->animation.current = c->animainit_geom; - } c->animation.initial = c->animainit_geom; c->animation.time_started = get_now_in_ms(); - - // 标记动画开始 c->animation.running = true; c->animation.should_animate = false; } - // 请求刷新屏幕 request_fresh_all_monitors(); } void client_set_pending_state(Client *c) { - if (!c || c->iskilling) return; - if (!config.animations) { + if (!config.animations) c->animation.should_animate = false; - } else if (config.animations && c->animation.tagining) { + else if (config.animations && c->animation.tagining) c->animation.should_animate = true; - } else if (c == grabc || (!c->is_pending_open_animation && - wlr_box_equal(&c->current, &c->pending))) { + else if (c == grabc || (!c->is_pending_open_animation && + wlr_box_equal(&c->current, &c->pending))) c->animation.should_animate = false; - } else { + else c->animation.should_animate = true; - } if (((c->animation_type_open && strcmp(c->animation_type_open, "none") == 0) || (!c->animation_type_open && strcmp(config.animation_type_open, "none") == 0)) && - c->animation.action == OPEN) { + c->animation.action == OPEN) c->animation.duration = 0; - } if (c->istagswitching) { c->animation.duration = 0; @@ -1459,58 +1313,46 @@ void client_set_pending_state(Client *c) { c->animation.duration = 0; } - // 开始动画 client_commit(c); c->dirty = true; } void resize(Client *c, struct wlr_box geo, int32_t interact) { - - // 动画设置的起始函数,这里用来计算一些动画的起始值 - // 动画起始位置大小是由于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; c->dirty = true; - // float_geom = c->geom; - bbox = (interact || c->isfloating || c->isfullscreen) ? &sgeom : &c->mon->w; + struct wlr_box *bbox = + (interact || c->isfloating || c->isfullscreen) ? &sgeom : &c->mon->w; + struct wlr_box clip; if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) { c->geom = geo; c->geom.width = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.width); c->geom.height = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.height); - } else { // 这里会限制不允许窗口划出屏幕 + } else { c->geom = geo; - applybounds( - c, - bbox); // 去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常 + applybounds(c, bbox); } if (!c->isnosizehint && !c->ismaximizescreen && !c->isfullscreen && - c->isfloating) { + c->isfloating) client_set_size_bound(c); - } - if (!c->is_pending_open_animation) { + if (!c->is_pending_open_animation) c->animation.begin_fade_in = false; - } - if (c->animation.overining) { + if (c->animation.overining) c->animation.action = OVERVIEW; - } else 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) { + else if (c->animation.action == OPEN && !c->animation.tagining && + !c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) + ; /* keep current action */ + else if (c->animation.tagouting) { c->animation.duration = config.animation_duration_tag; c->animation.action = TAG; } else if (c->animation.tagining) { @@ -1524,21 +1366,18 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->animation.action = MOVE; } - // 动画起始位置大小设置 - if (c->animation.tagouting) { + if (c->animation.tagouting) c->animainit_geom = c->animation.current; - } else if (c->animation.tagining) { + else if (c->animation.tagining) { c->animainit_geom.height = c->animation.current.height; c->animainit_geom.width = c->animation.current.width; - } else if (c->is_pending_open_animation) { + } else if (c->is_pending_open_animation) set_client_open_animation(c, c->geom); - } else { + else c->animainit_geom = c->animation.current; - } - if (c->isnoborder || c->iskilling) { + if (c->isnoborder || c->iskilling) c->bw = 0; - } bool hit_no_border = check_hit_no_border(c); if (hit_no_border && config.smartgaps) { @@ -1546,71 +1385,59 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->fake_no_border = true; } - // c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局 - if (!c->mon->isoverview || !config.ov_no_resize) { + if (!c->mon->isoverview || !config.ov_no_resize) c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); - } - if (c->configure_serial != 0) { + if (c->configure_serial != 0) c->mon->resizing_count_pending++; - } if (c == grabc) { + struct ivec2 offsets = compute_edge_offsets(c); + c->animation.running = false; c->need_output_flush = false; - 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); - client_draw_border(c); + client_draw_border(c, offsets); client_get_clip(c, &clip); - client_draw_shield(c, clip); - client_draw_shadow(c); - client_draw_groupbar(c); + client_draw_groupbar(c, offsets); + client_draw_shadow(c, offsets); + + struct ivec2 surface_clip_offset = clip_to_hide(c, &clip, offsets); + client_draw_shield(c, surface_clip_offset); + client_draw_blur(c, surface_clip_offset); + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); - if (config.blur && !c->noblur) - wlr_scene_blur_set_size(c->blur, - c->animation.current.width - 2 * c->bw, - c->animation.current.height - 2 * c->bw); return; } - // 如果不是工作区切换时划出去的窗口,就让动画的结束位置,就是上面的真实位置和大小 - // c->pending 决定动画的终点,一般在其他调用resize的函数的附近设置了 - if (!c->animation.tagouting && !c->iskilling) { + + if (!c->animation.tagouting && !c->iskilling) c->pending = c->geom; - } - if (c->swallowing && c->animation.action == OPEN) { + if (c->swallowing && c->animation.action == OPEN) c->animainit_geom = c->swallowing->animation.current; - } - if (c->swallowdby) { + if (c->swallowdby) c->animainit_geom = c->geom; - } if ((c->isglobal || c->isunglobal) && c->isfloating && - c->animation.action == TAG) { + c->animation.action == TAG) c->animainit_geom = c->geom; - } - if (c->scratchpad_switching_mon && c->isfloating) { + if (c->scratchpad_switching_mon && c->isfloating) c->animainit_geom = c->geom; - } if (config.animations && config.ov_no_resize && c->mon->isoverview && - c != c->mon->sel && c->animation.action == OVERVIEW) { + c != c->mon->sel && c->animation.action == OVERVIEW) set_overview_enter_animation(c); - } - if (!config.animations && config.ov_no_resize && c->mon->isoverview) { + if (!config.animations && config.ov_no_resize && c->mon->isoverview) c->animainit_geom = c->geom; - } - // 开始应用动画设置 client_set_pending_state(c); - setborder_color(c); } @@ -1640,7 +1467,6 @@ void client_set_focused_opacity_animation(Client *c) { c->opacity_animation.current_border_color, sizeof(c->opacity_animation.initial_border_color)); c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity; - c->opacity_animation.running = true; } @@ -1655,20 +1481,16 @@ void client_set_unfocused_opacity_animation(Client *c) { c->opacity_animation.duration = config.animation_duration_focus; 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(); - 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; - 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; @@ -1686,36 +1508,30 @@ bool client_apply_focus_opacity(Client *c) { double opacity_eased_progress = find_animation_curve_at(linear_progress, OPAFADEIN); - float percent = config.animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0; float opacity = c == selmon->sel ? c->focused_opacity : c->unfocused_opacity; - float target_opacity = percent * (1.0 - config.fadein_begin_opacity) + config.fadein_begin_opacity; - if (target_opacity > opacity) { + 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; client_set_opacity(c, target_opacity); if (config.blur && !c->noblur && !config.blur_optimized) { - wlr_scene_blur_set_strength( - c->blur, MIN(percent * (1.0 - config.fadein_begin_opacity) + - config.fadein_begin_opacity, - 1.0)); - wlr_scene_blur_set_alpha( - c->blur, MIN(percent * (1.0 - config.fadein_begin_opacity) + - config.fadein_begin_opacity, - 1.0)); + float blur_val = MIN(percent * (1.0 - config.fadein_begin_opacity) + + config.fadein_begin_opacity, + 1.0); + wlr_scene_blur_set_strength(c->blur, blur_val); + wlr_scene_blur_set_alpha(c->blur, blur_val); } client_set_border_color(c, c->opacity_animation.target_border_color); } else if (config.animations && c->opacity_animation.running) { - struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -1735,7 +1551,6 @@ bool client_apply_focus_opacity(Client *c) { eased_progress; client_set_opacity(c, c->opacity_animation.current_opacity); - // Animate border color for (int32_t i = 0; i < 4; i++) { c->opacity_animation.current_border_color[i] = c->opacity_animation.initial_border_color[i] + @@ -1744,11 +1559,10 @@ bool client_apply_focus_opacity(Client *c) { eased_progress; } client_set_border_color(c, c->opacity_animation.current_border_color); - if (linear_progress >= 1.0f) { + if (linear_progress >= 1.0f) c->opacity_animation.running = false; - } else { + else return true; - } } else if (c == selmon->sel) { c->opacity_animation.running = false; c->opacity_animation.current_opacity = c->focused_opacity; @@ -1767,13 +1581,11 @@ bool client_apply_focus_opacity(Client *c) { } bool client_draw_frame(Client *c) { - if (!c || !client_surface(c)->mapped) return false; - if (!c->need_output_flush) { + if (!c->need_output_flush) return client_apply_focus_opacity(c); - } if (config.animations && c->animation.running) { client_animation_next_tick(c); @@ -1785,6 +1597,5 @@ bool client_draw_frame(Client *c) { client_apply_clip(c, 1.0); c->need_output_flush = false; } - client_apply_focus_opacity(c); - return true; -} + return client_apply_focus_opacity(c); +} \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 3258b20d..8df865c9 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -2148,7 +2148,8 @@ int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) { leaf->custom_leaf_split_h = false; } bool hit_no_border = check_hit_no_border(c); - client_draw_split_border(c, hit_no_border); + struct ivec2 offsets = compute_edge_offsets(c); + client_draw_split_border(c, hit_no_border, offsets); return 0; } diff --git a/src/mango.c b/src/mango.c index 6c318ceb..0f4aeecd 100644 --- a/src/mango.c +++ b/src/mango.c @@ -843,7 +843,7 @@ get_nearest_output_mode(struct wlr_output *output, int32_t width, static void client_commit(Client *c); static void layer_commit(LayerSurface *l); -static void client_draw_border(Client *c); +static void client_draw_border(Client *c, struct ivec2 offsets); static void client_set_opacity(Client *c, double opacity); static void init_baked_points(void); static void scene_buffer_apply_opacity(struct wlr_scene_buffer *buffer, From a5390aa76dcb2a279d7211e1180f240b6715dc62 Mon Sep 17 00:00:00 2001 From: Vital Wang Date: Sat, 4 Jul 2026 12:12:25 +0200 Subject: [PATCH 70/70] Enable animation for comboview --- src/dispatch/bind_define.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 8df865c9..8559a7f5 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1765,7 +1765,7 @@ int32_t comboview(const Arg *arg) { arrange(selmon, false, false); } else { tag_combo = true; - view(&(Arg){.ui = newtags}, false); + view(&(Arg){.ui = newtags}, true); } printstatus(IPC_WATCH_ARRANGGE);