diff --git a/docs/installation.md b/docs/installation.md index d3b9afaa..6f3927a0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -185,7 +185,7 @@ mangowm is available in the **PikaOS package repository**. You can install it using the `pikman` package manager: ```bash -pikman install mangowc +pikman install mangowm ``` --- diff --git a/mangowm.scm b/mangowm.scm index 7d94166d..44031ed0 100644 --- a/mangowm.scm +++ b/mangowm.scm @@ -15,7 +15,7 @@ #:use-module (gnu packages ninja) #:use-module (gnu packages pkg-config) #:use-module (guix build-system meson) - #:use-module (guix licenses)) + #:use-module ((guix licenses) #:prefix license:)) (define-public mangowm-git @@ -36,10 +36,13 @@ (add-before 'configure 'patch-meson (lambda _ (substitute* "meson.build" + ;; MangoWM ignores sysconfdir handling for NixOS. + ;; We also need to skip that sysconfdir edits. + (("is_nixos = false") + "is_nixos = true") + ;; Unhardcode path. Fixes loading default config. (("'-DSYSCONFDIR=\\\"@0@\\\"'.format\\('/etc'\\)") - "'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)") - (("sysconfdir = sysconfdir.substring\\(prefix.length\\(\\)\\)") - ""))))))) + "'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)"))))))) (inputs (list wayland libinput libdrm @@ -52,14 +55,17 @@ pcre2 libxcb xcb-util-wm - wlroots + wlroots-0.19 scenefx)) (native-inputs (list pkg-config wayland-protocols)) - (home-page "https://github.com/DreamMaoMao/mangowm") + (home-page "https://github.com/mangowm/mango") (synopsis "Wayland compositor based on wlroots and scenefx") - (description "A Wayland compositor based on wlroots and scenefx, -inspired by dwl but aiming to be more feature-rich.") - (license gpl3))) + (description + "MangoWM is a modern, lightweight, high-performance Wayland compositor +built on dwl — crafted for speed, flexibility, and a customizable desktop experience.") + (license (list license:gpl3 ;mangowm itself, dwl + license:expat ;dwm, sway, wlroots + license:cc0)))) ;tinywl (define-deprecated-package mangowc mangowm-git) diff --git a/nix/hm-modules.nix b/nix/hm-modules.nix index 85d57908..f00d9c68 100644 --- a/nix/hm-modules.nix +++ b/nix/hm-modules.nix @@ -1,18 +1,22 @@ -self: { +self: +{ lib, config, pkgs, ... -}: let +}: +let cfg = config.wayland.windowManager.mango; + selflib = import ./lib.nix lib; variables = lib.concatStringsSep " " cfg.systemd.variables; extraCommands = lib.concatStringsSep " && " cfg.systemd.extraCommands; - systemdActivation = ''${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}; ${extraCommands}''; + systemdActivation = "${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}; ${extraCommands}"; autostart_sh = pkgs.writeShellScript "autostart.sh" '' ${lib.optionalString cfg.systemd.enable systemdActivation} ${cfg.autostart_sh} ''; -in { +in +{ options = { wayland.windowManager.mango = with lib; { enable = mkOption { @@ -54,7 +58,7 @@ in { "XCURSOR_THEME" "XCURSOR_SIZE" ]; - example = ["--all"]; + example = [ "--all" ]; description = '' Environment variables imported into the systemd and D-Bus user environment. ''; @@ -75,50 +79,195 @@ in { ''; }; settings = mkOption { - description = "mango config content"; - type = types.lines; - default = ""; - example = '' - # menu and terminal - bind=Alt,space,spawn,rofi -show drun - bind=Alt,Return,spawn,foot + type = + with lib.types; + let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Mango configuration value"; + }; + in + valueType; + default = { }; + description = '' + Mango configuration written in Nix. Entries with the same key + should be written as lists. Variables and colors names should be + quoted. See for more examples. + + ::: {.note} + This option uses a structured format that is converted to Mango's + configuration syntax. Nested attributes are flattened with underscore separators. + For example: `animation.duration_open = 400` becomes `animation_duration_open = 400` + + Keymodes (submaps) are supported via the special `keymode` attribute. Each keymode + is a nested attribute set under `keymode` that contains its own bindings. + ::: + ''; + example = lib.literalExpression '' + { + # Window effects + blur = 1; + blur_optimized = 1; + blur_params = { + radius = 5; + num_passes = 2; + }; + border_radius = 6; + focused_opacity = 1.0; + + # Animations - use underscores for multi-part keys + animations = 1; + animation_type_open = "slide"; + animation_type_close = "slide"; + animation_duration_open = 400; + animation_duration_close = 800; + + # Or use nested attrs (will be flattened with underscores) + animation_curve = { + open = "0.46,1.0,0.29,1"; + close = "0.08,0.92,0,1"; + }; + + # Use lists for duplicate keys like bind and tagrule + bind = [ + "SUPER,r,reload_config" + "Alt,space,spawn,rofi -show drun" + "Alt,Return,spawn,foot" + "ALT,R,setkeymode,resize" # Enter resize mode + ]; + + tagrule = [ + "id:1,layout_name:tile" + "id:2,layout_name:scroller" + ]; + + # Keymodes (submaps) for modal keybindings + keymode = { + resize = { + bind = [ + "NONE,Left,resizewin,-10,0" + "NONE,Escape,setkeymode,default" + ]; + }; + }; + } ''; }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration lines to add to `~/.config/mango/config.conf`. + This is useful for advanced configurations that don't fit the structured + settings format, or for options that aren't yet supported by the module. + ''; + example = '' + # Advanced config that doesn't fit structured format + special_option = 1 + ''; + }; + topPrefixes = mkOption { + type = with lib.types; listOf str; + default = [ ]; + description = '' + List of prefixes for attributes that should appear at the top of the config file. + Attributes starting with these prefixes will be sorted to the beginning. + ''; + example = [ "source" ]; + }; + bottomPrefixes = mkOption { + type = with lib.types; listOf str; + default = [ ]; + description = '' + List of prefixes for attributes that should appear at the bottom of the config file. + Attributes starting with these prefixes will be sorted to the end. + ''; + example = [ "source" ]; + }; autostart_sh = mkOption { - description = "WARRNING: This is a shell script, but no need to add shebang"; + description = '' + Shell script to run on mango startup. No shebang needed. + + When this option is set, the script will be written to + `~/.config/mango/autostart.sh` and an `exec-once` line + will be automatically added to the config to execute it. + ''; type = types.lines; default = ""; example = '' waybar & + dunst & ''; }; }; }; - config = lib.mkIf cfg.enable { - home.packages = [cfg.package]; - xdg.configFile = { - "mango/config.conf" = lib.mkIf (cfg.settings != "") { - text = cfg.settings; + config = lib.mkIf cfg.enable ( + let + finalConfigText = + # Support old string-based config during transition period + ( + if builtins.isString cfg.settings then + cfg.settings + else + lib.optionalString (cfg.settings != { }) ( + selflib.toMango { + topCommandsPrefixes = cfg.topPrefixes; + bottomCommandsPrefixes = cfg.bottomPrefixes; + } cfg.settings + ) + ) + + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig + + lib.optionalString (cfg.autostart_sh != "") "\nexec-once=~/.config/mango/autostart.sh\n"; + + validatedConfig = pkgs.runCommand "mango-config.conf" { } '' + cp ${pkgs.writeText "mango-config.conf" finalConfigText} "$out" + ${cfg.package}/bin/mango -c "$out" -p || exit 1 + ''; + in + { + # Backwards compatibility warning for old string-based config + warnings = lib.optional (builtins.isString cfg.settings) '' + wayland.windowManager.mango.settings: Using a string for settings is deprecated. + Please migrate to the new structured attribute set format. + See the module documentation for examples, or use the 'extraConfig' option for raw config strings. + The old string format will be removed in a future release. + ''; + + home.packages = [ cfg.package ]; + xdg.configFile = { + "mango/config.conf" = + lib.mkIf (cfg.settings != { } || cfg.extraConfig != "" || cfg.autostart_sh != "") + { + source = validatedConfig; + }; + "mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") { + source = autostart_sh; + executable = true; + }; }; - "mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") { - source = autostart_sh; - executable = true; - }; - }; - systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable { - Unit = { - Description = "mango compositor session"; - Documentation = ["man:systemd.special(7)"]; - BindsTo = ["graphical-session.target"]; - Wants = - [ + systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable { + Unit = { + Description = "mango compositor session"; + Documentation = [ "man:systemd.special(7)" ]; + BindsTo = [ "graphical-session.target" ]; + Wants = [ "graphical-session-pre.target" ] ++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; - After = ["graphical-session-pre.target"]; - Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; + After = [ "graphical-session-pre.target" ]; + Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; + }; }; - }; - }; + } + ); } diff --git a/nix/lib.nix b/nix/lib.nix new file mode 100644 index 00000000..9dfd2ff6 --- /dev/null +++ b/nix/lib.nix @@ -0,0 +1,312 @@ +lib: +let + inherit (lib) + attrNames + filterAttrs + foldl + generators + partition + removeAttrs + ; + + inherit (lib.strings) + concatMapStrings + hasPrefix + ; + + /** + Convert a structured Nix attribute set into Mango's configuration format. + + This function takes a nested attribute set and converts it into Mango-compatible + configuration syntax, supporting top, bottom, and regular command sections. + + Commands are flattened using the `flattenAttrs` function, and attributes are formatted as + `key = value` pairs. Lists are expanded as duplicate keys to match Mango's expected format. + + Configuration: + + * `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`). + * `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`). + + Attention: + + - The function ensures top commands appear **first** and bottom commands **last**. + - The generated configuration is a **single string**, suitable for writing to a config file. + - Lists are converted into multiple entries, ensuring compatibility with Mango. + + # Inputs + + Structured function argument: + + : topCommandsPrefixes (optional, default: `[]`) + : A list of prefixes that define **top** commands. Any key starting with one of these + prefixes will be placed at the beginning of the configuration. + : bottomCommandsPrefixes (optional, default: `[]`) + : A list of prefixes that define **bottom** commands. Any key starting with one of these + prefixes will be placed at the end of the configuration. + + Value: + + : The attribute set to be converted to Hyprland configuration format. + + # Type + + ``` + toMango :: AttrSet -> AttrSet -> String + ``` + + # Examples + :::{.example} + + ## Basic mangowc configuration + + ```nix + let + config = { + blur = 1; + blur_params_radius = 5; + border_radius = 6; + animations = 1; + animation_duration_open = 400; + }; + in lib.toMango {} config + ``` + + **Output:** + ``` + animations = 1 + animation_duration_open = 400 + blur = 1 + blur_params_radius = 5 + border_radius = 6 + ``` + + ## Using nested attributes + + ```nix + let + config = { + blur = 1; + blur_params = { + radius = 5; + num_passes = 2; + noise = 0.02; + }; + animation_curve = { + open = "0.46,1.0,0.29,1"; + close = "0.08,0.92,0,1"; + }; + }; + in lib.toMango {} config + ``` + + **Output:** + ``` + animation_curve_close = 0.08,0.92,0,1 + animation_curve_open = 0.46,1.0,0.29,1 + blur = 1 + blur_params_noise = 0.02 + blur_params_num_passes = 2 + blur_params_radius = 5 + ``` + + ## Using lists for duplicate keys + + ```nix + let + config = { + bind = [ + "SUPER,r,reload_config" + "Alt,space,spawn,rofi -show drun" + "Alt,Return,spawn,foot" + ]; + tagrule = [ + "id:1,layout_name:tile" + "id:2,layout_name:scroller" + ]; + }; + in lib.toMango {} config + ``` + + **Output:** + ``` + bind = SUPER,r,reload_config + bind = Alt,space,spawn,rofi -show drun + bind = Alt,Return,spawn,foot + tagrule = id:1,layout_name:tile + tagrule = id:2,layout_name:scroller + ``` + + ## Using keymodes (submaps) + + ```nix + let + config = { + bind = [ + "SUPER,Q,killclient" + "ALT,R,setkeymode,resize" + ]; + keymode = { + resize = { + bind = [ + "NONE,Left,resizewin,-10,0" + "NONE,Right,resizewin,10,0" + "NONE,Escape,setkeymode,default" + ]; + }; + }; + }; + in lib.toMango {} config + ``` + + **Output:** + ``` + bind = SUPER,Q,killclient + bind = ALT,R,setkeymode,resize + + keymode = resize + bind = NONE,Left,resizewin,-10,0 + bind = NONE,Right,resizewin,10,0 + bind = NONE,Escape,setkeymode,default + ``` + + ::: + */ + toMango = + { + topCommandsPrefixes ? [ ], + bottomCommandsPrefixes ? [ ], + }: + attrs: + let + toMango' = + attrs: + let + # Specially configured `toKeyValue` generator with support for duplicate keys + # and a legible key-value separator. + mkCommands = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault { } " = "; + listsAsDuplicateKeys = true; + indent = ""; # No indent, since we don't have nesting + }; + + # Extract keymode definitions if they exist + keymodes = attrs.keymode or { }; + attrsWithoutKeymodes = removeAttrs attrs [ "keymode" ]; + + # Generate keymode blocks + # Format: keymode=name\nbind=...\nbind=...\n + mkKeymodeBlock = + name: modeAttrs: + let + modeCommands = flattenAttrs (p: k: "${p}_${k}") modeAttrs; + in + "keymode = ${name}\n${mkCommands modeCommands}"; + + keymodeBlocks = + if keymodes == { } then + "" + else + "\n" + concatMapStrings (name: mkKeymodeBlock name keymodes.${name} + "\n") (attrNames keymodes); + + # Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`. + # Uses `flattenAttrs` with an underscore separator. + commands = flattenAttrs (p: k: "${p}_${k}") attrsWithoutKeymodes; + + # General filtering function to check if a key starts with any prefix in a given list. + filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; + + # Partition keys into top commands and the rest + result = partition (filterCommands topCommandsPrefixes) (attrNames commands); + topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; + remainingCommands = removeAttrs commands result.right; + + # Partition remaining commands into bottom commands and regular commands + result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; + bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; + regularCommands = removeAttrs remainingCommands result2.right; + in + # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. + # Keymodes are appended at the end. + concatMapStrings mkCommands [ + topCommands + regularCommands + bottomCommands + ] + + keymodeBlocks; + in + toMango' attrs; + + /** + Flatten a nested attribute set into a flat attribute set, using a custom key separator function. + + This function recursively traverses a nested attribute set and produces a flat attribute set + where keys are joined using a user-defined function (`pred`). It allows transforming deeply + nested structures into a single-level attribute set while preserving key-value relationships. + + Configuration: + + * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. + + # Inputs + + Structured function argument: + + : pred (required) + : A function that determines how parent and child keys should be combined into a single key. + It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. + + Value: + + : The nested attribute set to be flattened. + + # Type + + ``` + flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + + ```nix + let + nested = { + a = "3"; + b = { c = "4"; d = "5"; }; + }; + + separator = (prefix: key: "${prefix}.${key}"); # Use dot notation + in lib.flattenAttrs separator nested + ``` + + **Output:** + ```nix + { + "a" = "3"; + "b.c" = "4"; + "b.d" = "5"; + } + ``` + + ::: + */ + flattenAttrs = + pred: attrs: + let + flattenAttrs' = + prefix: attrs: + builtins.foldl' ( + acc: key: + let + value = attrs.${key}; + newKey = if prefix == "" then key else pred prefix key; + in + acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; }) + ) { } (builtins.attrNames attrs); + in + flattenAttrs' "" attrs; +in +{ + inherit flattenAttrs toMango; +} diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 07eafde5..f5992e29 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -552,7 +552,7 @@ int32_t restore_minimized(const Arg *arg) { if (selmon && selmon->sel && selmon->sel->is_in_scratchpad && selmon->sel->is_scratchpad_show) { - selmon->sel->isminimized = 0; + client_pending_minimized_state(selmon->sel, 0); selmon->sel->is_scratchpad_show = 0; selmon->sel->is_in_scratchpad = 0; selmon->sel->isnamedscratchpad = 0; @@ -863,7 +863,6 @@ int32_t spawn_shell(const Arg *arg) { } int32_t spawn(const Arg *arg) { - if (!arg->v) return 0; @@ -876,28 +875,21 @@ int32_t spawn(const Arg *arg) { dup2(STDERR_FILENO, STDOUT_FILENO); setsid(); - // 2. 解析参数 - char *argv[64]; - int32_t argc = 0; - char *token = strtok((char *)arg->v, " "); - while (token != NULL && argc < 63) { - wordexp_t p; - if (wordexp(token, &p, 0) == 0) { - argv[argc++] = p.we_wordv[0]; - } else { - argv[argc++] = token; - } - token = strtok(NULL, " "); + // 2. 对整个参数字符串进行单词展开 + wordexp_t p; + if (wordexp(arg->v, &p, 0) != 0) { + wlr_log(WLR_DEBUG, "mango: wordexp failed for '%s'\n", arg->v); + _exit(EXIT_FAILURE); } - argv[argc] = NULL; - // 3. 执行命令 - execvp(argv[0], argv); + // 3. 执行命令(p.we_wordv 已经是 argv 数组) + execvp(p.we_wordv[0], p.we_wordv); - // 4. execvp 失败时:打印错误并直接退出(避免 coredump) - wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", argv[0], + // 4. execvp 失败时:打印错误,释放 wordexp 资源,然后退出 + wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", p.we_wordv[0], strerror(errno)); - _exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作 + wordfree(&p); // 释放 wordexp 分配的内存 + _exit(EXIT_FAILURE); } return 0; } diff --git a/src/mango.c b/src/mango.c index fad86b20..8fdff709 100644 --- a/src/mango.c +++ b/src/mango.c @@ -806,6 +806,10 @@ static int32_t keep_idle_inhibit(void *data); static void check_keep_idle_inhibit(Client *c); static void pre_caculate_before_arrange(Monitor *m, bool want_animation, bool from_view, bool only_caculate); +static void client_pending_fullscreen_state(Client *c, int32_t isfullscreen); +static void client_pending_maximized_state(Client *c, int32_t ismaximized); +static void client_pending_minimized_state(Client *c, int32_t isminimized); + #include "data/static_keymap.h" #include "dispatch/bind_declare.h" #include "layout/layout.h" @@ -1063,11 +1067,33 @@ void clear_fullscreen_flag(Client *c) { } } +void client_pending_fullscreen_state(Client *c, int32_t isfullscreen) { + c->isfullscreen = isfullscreen; + + if (c->foreign_toplevel && !c->iskilling) + wlr_foreign_toplevel_handle_v1_set_fullscreen(c->foreign_toplevel, + isfullscreen); +} + +void client_pending_maximized_state(Client *c, int32_t ismaximized) { + c->ismaximizescreen = ismaximized; + if (c->foreign_toplevel && !c->iskilling) + wlr_foreign_toplevel_handle_v1_set_maximized(c->foreign_toplevel, + ismaximized); +} + +void client_pending_minimized_state(Client *c, int32_t isminimized) { + c->isminimized = isminimized; + if (c->foreign_toplevel && !c->iskilling) + wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, + isminimized); +} + void show_scratchpad(Client *c) { c->is_scratchpad_show = 1; if (c->isfullscreen || c->ismaximizescreen) { - c->isfullscreen = 0; // 清除窗口全屏标志 - c->ismaximizescreen = 0; + client_pending_fullscreen_state(c, 0); + client_pending_maximized_state(c, 0); c->bw = c->isnoborder ? 0 : config.borderpx; } @@ -1106,9 +1132,6 @@ void swallow(Client *c, Client *w) { c->bw = w->bw; c->isfloating = w->isfloating; c->isurgent = w->isurgent; - c->isfullscreen = w->isfullscreen; - c->ismaximizescreen = w->ismaximizescreen; - c->isminimized = w->isminimized; c->is_in_scratchpad = w->is_in_scratchpad; c->is_scratchpad_show = w->is_scratchpad_show; c->tags = w->tags; @@ -1120,6 +1143,7 @@ void swallow(Client *c, Client *w) { c->scroller_proportion = w->scroller_proportion; c->next_in_stack = w->next_in_stack; c->prev_in_stack = w->prev_in_stack; + if (w->next_in_stack) w->next_in_stack->prev_in_stack = c; if (w->prev_in_stack) @@ -1138,11 +1162,9 @@ void swallow(Client *c, Client *w) { if (!c->foreign_toplevel && c->mon) add_foreign_toplevel(c); - if (c->isminimized && c->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, - false); - wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, true); - } + client_pending_fullscreen_state(c, w->isfullscreen); + client_pending_maximized_state(c, w->ismaximizescreen); + client_pending_minimized_state(c, w->isminimized); } bool switch_scratchpad_client_state(Client *c) { @@ -4231,7 +4253,7 @@ void maximizenotify(struct wl_listener *listener, void *data) { void unminimize(Client *c) { if (c && c->is_in_scratchpad && c->is_scratchpad_show) { - c->isminimized = 0; + client_pending_minimized_state(c, 0); c->is_scratchpad_show = 0; c->is_in_scratchpad = 0; c->isnamedscratchpad = 0; @@ -4259,13 +4281,12 @@ void set_minimized(Client *c) { c->oldtags = c->mon->tagset[c->mon->seltags]; c->mini_restore_tag = c->tags; c->tags = 0; - c->isminimized = 1; + client_pending_minimized_state(c, 1); c->is_in_scratchpad = 1; c->is_scratchpad_show = 0; focusclient(focustop(selmon), 1); arrange(c->mon, false, false); wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, false); - wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, true); wl_list_remove(&c->link); // 从原来位置移除 wl_list_insert(clients.prev, &c->link); // 插入尾部 } @@ -5042,11 +5063,11 @@ setfloating(Client *c, int32_t floating) { if (floating == 1 && c != grabc) { if (c->isfullscreen) { - c->isfullscreen = 0; + client_pending_fullscreen_state(c, 0); client_set_fullscreen(c, 0); } - c->ismaximizescreen = 0; + client_pending_maximized_state(c, 0); exit_scroller_stack(c); // 重新计算居中的坐标 @@ -5171,12 +5192,12 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) { return; int32_t old_maximizescreen_state = c->ismaximizescreen; - c->ismaximizescreen = maximizescreen; + client_pending_maximized_state(c, maximizescreen); if (maximizescreen) { if (c->isfullscreen) { - c->isfullscreen = 0; + client_pending_fullscreen_state(c, 0); client_set_fullscreen(c, 0); } @@ -5189,10 +5210,8 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) { wlr_scene_node_raise_to_top(&c->scene->node); if (!is_scroller_layout(c->mon) || c->isfloating) resize(c, maximizescreen_box, 0); - c->ismaximizescreen = 1; } else { c->bw = c->isnoborder ? 0 : config.borderpx; - c->ismaximizescreen = 0; if (c->isfloating) setfloating(c, 1); } @@ -5240,6 +5259,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自 c->isfullscreen = fullscreen; client_set_fullscreen(c, fullscreen); + client_pending_fullscreen_state(c, fullscreen); if (fullscreen) { @@ -5247,7 +5267,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自 client_set_maximized(c, false); } - c->ismaximizescreen = 0; + client_pending_maximized_state(c, 0); exit_scroller_stack(c); c->isfakefullscreen = 0; @@ -5256,10 +5276,8 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自 wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 if (!is_scroller_layout(c->mon) || c->isfloating) resize(c, c->mon->m, 1); - c->isfullscreen = 1; } else { c->bw = c->isnoborder ? 0 : config.borderpx; - c->isfullscreen = 0; if (c->isfloating) setfloating(c, 1); } @@ -5451,8 +5469,7 @@ void show_hide_client(Client *c) { c->tags = c->oldtags; arrange(c->mon, false, false); } - c->isminimized = 0; - wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, false); + client_pending_minimized_state(c, 0); focusclient(c, 1); wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true); } @@ -5876,8 +5893,8 @@ void overview_backup(Client *c) { c->isfloating = 0; } if (c->isfullscreen || c->ismaximizescreen) { - c->isfullscreen = 0; // 清除窗口全屏标志 - c->ismaximizescreen = 0; + client_pending_fullscreen_state(c, 0); // 清除窗口全屏标志 + client_pending_maximized_state(c, 0); } c->bw = c->isnoborder ? 0 : config.borderpx; @@ -5907,8 +5924,8 @@ void overview_restore(Client *c, const Arg *arg) { } else if (want_restore_fullscreen(c) && c->isfullscreen) { setfullscreen(c, 1); } else { - c->isfullscreen = 0; - c->ismaximizescreen = 0; + client_pending_fullscreen_state(c, 0); + client_pending_maximized_state(c, 0); setfullscreen(c, false); } } else { @@ -6481,13 +6498,11 @@ void activatex11(struct wl_listener *listener, void *data) { return; if (c->isminimized) { - c->isminimized = 0; + client_pending_minimized_state(c, 0); c->tags = c->mini_restore_tag; c->is_scratchpad_show = 0; c->is_in_scratchpad = 0; c->isnamedscratchpad = 0; - wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, - false); setborder_color(c); if (VISIBLEON(c, c->mon)) { need_arrange = true;