Compare commits

..

10 commits
0.12.8 ... main

Author SHA1 Message Date
DreamMaoMao
e83a9dff82 fix: fix potential memory leaks 2026-04-06 15:39:39 +08:00
DreamMaoMao
e3a0d54b9a Merge pull request #826 from ch4og/guix-fixes
Multiple Guix package fixes
2026-04-05 18:43:49 +08:00
Nikita Mitasov
c44d12314c fix(guix): pin wlroots version
meta(guix): update home-page

meta(guix): more appropriate licenses field

refactor(guix): use more easier meson patch phase

meta(guix): update description
2026-04-05 18:43:49 +08:00
Ananya Timalsina
bb0160d7cf feat(nix): add build-time configuration validation
Uses `pkgs.runCommand` in the home-manager module to parse and validate
the generated config file prior to deployment, preventing broken setups.
2026-04-03 18:14:50 +08:00
Ananya Timalsina
a8afc0dc4d feat(nix): support old config format with deprecation warning 2026-04-03 18:14:45 +08:00
Ananya Timalsina
b63e93fc09 feat(nix): add keymode support for modal keybindings 2026-04-03 18:14:40 +08:00
Ananya Timalsina
c7c41c3103 refactor(nix): add structured config support to home-manager module
Convert settings from raw text to structured Nix attrs, following
Hyprland's module pattern. Implementation based 1:1 on Hyprland's
design - all credit to the Hyprland project.

- Add nix/lib.nix with toMango conversion function
- Support nested attrs, lists for duplicate keys
- Add extraConfig, topPrefixes, bottomPrefixes options
- Auto-add exec-once for autostart.sh

Adapted for mangowc syntax (underscore separators vs colons).
2026-04-03 18:14:35 +08:00
DreamMaoMao
52676492fe opt: optimize foreign toplevel state sync 2026-03-30 18:35:37 +08:00
DreamMaoMao
254eb8a4d4
Merge pull request #814 from atheeq-rhxn/docs-update
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
docs: update pikaos installation
2026-03-29 10:24:43 +08:00
atheeq-rhxn
bccdb651bd docs: update pikaos installation 2026-03-28 20:50:26 +05:30
6 changed files with 569 additions and 95 deletions

View file

@ -185,7 +185,7 @@ mangowm is available in the **PikaOS package repository**.
You can install it using the `pikman` package manager: You can install it using the `pikman` package manager:
```bash ```bash
pikman install mangowc pikman install mangowm
``` ```
--- ---

View file

@ -15,7 +15,7 @@
#:use-module (gnu packages ninja) #:use-module (gnu packages ninja)
#:use-module (gnu packages pkg-config) #:use-module (gnu packages pkg-config)
#:use-module (guix build-system meson) #:use-module (guix build-system meson)
#:use-module (guix licenses)) #:use-module ((guix licenses) #:prefix license:))
(define-public mangowm-git (define-public mangowm-git
@ -36,10 +36,13 @@
(add-before 'configure 'patch-meson (add-before 'configure 'patch-meson
(lambda _ (lambda _
(substitute* "meson.build" (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\\('/etc'\\)")
"'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)") "'-DSYSCONFDIR=\"@0@\"'.format(sysconfdir)")))))))
(("sysconfdir = sysconfdir.substring\\(prefix.length\\(\\)\\)")
"")))))))
(inputs (list wayland (inputs (list wayland
libinput libinput
libdrm libdrm
@ -52,14 +55,17 @@
pcre2 pcre2
libxcb libxcb
xcb-util-wm xcb-util-wm
wlroots wlroots-0.19
scenefx)) scenefx))
(native-inputs (list pkg-config wayland-protocols)) (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") (synopsis "Wayland compositor based on wlroots and scenefx")
(description "A Wayland compositor based on wlroots and scenefx, (description
inspired by dwl but aiming to be more feature-rich.") "MangoWM is a modern, lightweight, high-performance Wayland compositor
(license gpl3))) 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 (define-deprecated-package mangowc
mangowm-git) mangowm-git)

View file

@ -1,18 +1,22 @@
self: { self:
{
lib, lib,
config, config,
pkgs, pkgs,
... ...
}: let }:
let
cfg = config.wayland.windowManager.mango; cfg = config.wayland.windowManager.mango;
selflib = import ./lib.nix lib;
variables = lib.concatStringsSep " " cfg.systemd.variables; variables = lib.concatStringsSep " " cfg.systemd.variables;
extraCommands = lib.concatStringsSep " && " cfg.systemd.extraCommands; 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" '' autostart_sh = pkgs.writeShellScript "autostart.sh" ''
${lib.optionalString cfg.systemd.enable systemdActivation} ${lib.optionalString cfg.systemd.enable systemdActivation}
${cfg.autostart_sh} ${cfg.autostart_sh}
''; '';
in { in
{
options = { options = {
wayland.windowManager.mango = with lib; { wayland.windowManager.mango = with lib; {
enable = mkOption { enable = mkOption {
@ -54,7 +58,7 @@ in {
"XCURSOR_THEME" "XCURSOR_THEME"
"XCURSOR_SIZE" "XCURSOR_SIZE"
]; ];
example = ["--all"]; example = [ "--all" ];
description = '' description = ''
Environment variables imported into the systemd and D-Bus user environment. Environment variables imported into the systemd and D-Bus user environment.
''; '';
@ -75,31 +79,176 @@ in {
''; '';
}; };
settings = mkOption { settings = mkOption {
description = "mango config content"; type =
type = types.lines; with lib.types;
default = ""; let
example = '' valueType =
# menu and terminal nullOr (oneOf [
bind=Alt,space,spawn,rofi -show drun bool
bind=Alt,Return,spawn,foot 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 <https://mangowc.vercel.app/docs> 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 { 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; type = types.lines;
default = ""; default = "";
example = '' example = ''
waybar & waybar &
dunst &
''; '';
}; };
}; };
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable (
home.packages = [cfg.package]; 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 = { xdg.configFile = {
"mango/config.conf" = lib.mkIf (cfg.settings != "") { "mango/config.conf" =
text = cfg.settings; lib.mkIf (cfg.settings != { } || cfg.extraConfig != "" || cfg.autostart_sh != "")
{
source = validatedConfig;
}; };
"mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") { "mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") {
source = autostart_sh; source = autostart_sh;
@ -109,16 +258,16 @@ in {
systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable { systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable {
Unit = { Unit = {
Description = "mango compositor session"; Description = "mango compositor session";
Documentation = ["man:systemd.special(7)"]; Documentation = [ "man:systemd.special(7)" ];
BindsTo = ["graphical-session.target"]; BindsTo = [ "graphical-session.target" ];
Wants = Wants = [
[
"graphical-session-pre.target" "graphical-session-pre.target"
] ]
++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; ++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
After = ["graphical-session-pre.target"]; After = [ "graphical-session-pre.target" ];
Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
}; };
}; };
}; }
);
} }

312
nix/lib.nix Normal file
View file

@ -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;
}

View file

@ -552,7 +552,7 @@ int32_t restore_minimized(const Arg *arg) {
if (selmon && selmon->sel && selmon->sel->is_in_scratchpad && if (selmon && selmon->sel && selmon->sel->is_in_scratchpad &&
selmon->sel->is_scratchpad_show) { 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_scratchpad_show = 0;
selmon->sel->is_in_scratchpad = 0; selmon->sel->is_in_scratchpad = 0;
selmon->sel->isnamedscratchpad = 0; selmon->sel->isnamedscratchpad = 0;
@ -863,7 +863,6 @@ int32_t spawn_shell(const Arg *arg) {
} }
int32_t spawn(const Arg *arg) { int32_t spawn(const Arg *arg) {
if (!arg->v) if (!arg->v)
return 0; return 0;
@ -876,28 +875,21 @@ int32_t spawn(const Arg *arg) {
dup2(STDERR_FILENO, STDOUT_FILENO); dup2(STDERR_FILENO, STDOUT_FILENO);
setsid(); setsid();
// 2. 解析参数 // 2. 对整个参数字符串进行单词展开
char *argv[64];
int32_t argc = 0;
char *token = strtok((char *)arg->v, " ");
while (token != NULL && argc < 63) {
wordexp_t p; wordexp_t p;
if (wordexp(token, &p, 0) == 0) { if (wordexp(arg->v, &p, 0) != 0) {
argv[argc++] = p.we_wordv[0]; wlr_log(WLR_DEBUG, "mango: wordexp failed for '%s'\n", arg->v);
} else { _exit(EXIT_FAILURE);
argv[argc++] = token;
} }
token = strtok(NULL, " ");
}
argv[argc] = NULL;
// 3. 执行命令 // 3. 执行命令p.we_wordv 已经是 argv 数组)
execvp(argv[0], argv); execvp(p.we_wordv[0], p.we_wordv);
// 4. execvp 失败时:打印错误并直接退出(避免 coredump // 4. execvp 失败时:打印错误,释放 wordexp 资源,然后退出
wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", argv[0], wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", p.we_wordv[0],
strerror(errno)); strerror(errno));
_exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作 wordfree(&p); // 释放 wordexp 分配的内存
_exit(EXIT_FAILURE);
} }
return 0; return 0;
} }

View file

@ -806,6 +806,10 @@ static int32_t keep_idle_inhibit(void *data);
static void check_keep_idle_inhibit(Client *c); static void check_keep_idle_inhibit(Client *c);
static void pre_caculate_before_arrange(Monitor *m, bool want_animation, static void pre_caculate_before_arrange(Monitor *m, bool want_animation,
bool from_view, bool only_caculate); 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 "data/static_keymap.h"
#include "dispatch/bind_declare.h" #include "dispatch/bind_declare.h"
#include "layout/layout.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) { void show_scratchpad(Client *c) {
c->is_scratchpad_show = 1; c->is_scratchpad_show = 1;
if (c->isfullscreen || c->ismaximizescreen) { if (c->isfullscreen || c->ismaximizescreen) {
c->isfullscreen = 0; // 清除窗口全屏标志 client_pending_fullscreen_state(c, 0);
c->ismaximizescreen = 0; client_pending_maximized_state(c, 0);
c->bw = c->isnoborder ? 0 : config.borderpx; c->bw = c->isnoborder ? 0 : config.borderpx;
} }
@ -1106,9 +1132,6 @@ void swallow(Client *c, Client *w) {
c->bw = w->bw; c->bw = w->bw;
c->isfloating = w->isfloating; c->isfloating = w->isfloating;
c->isurgent = w->isurgent; 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_in_scratchpad = w->is_in_scratchpad;
c->is_scratchpad_show = w->is_scratchpad_show; c->is_scratchpad_show = w->is_scratchpad_show;
c->tags = w->tags; c->tags = w->tags;
@ -1120,6 +1143,7 @@ void swallow(Client *c, Client *w) {
c->scroller_proportion = w->scroller_proportion; c->scroller_proportion = w->scroller_proportion;
c->next_in_stack = w->next_in_stack; c->next_in_stack = w->next_in_stack;
c->prev_in_stack = w->prev_in_stack; c->prev_in_stack = w->prev_in_stack;
if (w->next_in_stack) if (w->next_in_stack)
w->next_in_stack->prev_in_stack = c; w->next_in_stack->prev_in_stack = c;
if (w->prev_in_stack) if (w->prev_in_stack)
@ -1138,11 +1162,9 @@ void swallow(Client *c, Client *w) {
if (!c->foreign_toplevel && c->mon) if (!c->foreign_toplevel && c->mon)
add_foreign_toplevel(c); add_foreign_toplevel(c);
if (c->isminimized && c->foreign_toplevel) { client_pending_fullscreen_state(c, w->isfullscreen);
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, client_pending_maximized_state(c, w->ismaximizescreen);
false); client_pending_minimized_state(c, w->isminimized);
wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, true);
}
} }
bool switch_scratchpad_client_state(Client *c) { bool switch_scratchpad_client_state(Client *c) {
@ -4231,7 +4253,7 @@ void maximizenotify(struct wl_listener *listener, void *data) {
void unminimize(Client *c) { void unminimize(Client *c) {
if (c && c->is_in_scratchpad && c->is_scratchpad_show) { 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_scratchpad_show = 0;
c->is_in_scratchpad = 0; c->is_in_scratchpad = 0;
c->isnamedscratchpad = 0; c->isnamedscratchpad = 0;
@ -4259,13 +4281,12 @@ void set_minimized(Client *c) {
c->oldtags = c->mon->tagset[c->mon->seltags]; c->oldtags = c->mon->tagset[c->mon->seltags];
c->mini_restore_tag = c->tags; c->mini_restore_tag = c->tags;
c->tags = 0; c->tags = 0;
c->isminimized = 1; client_pending_minimized_state(c, 1);
c->is_in_scratchpad = 1; c->is_in_scratchpad = 1;
c->is_scratchpad_show = 0; c->is_scratchpad_show = 0;
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
arrange(c->mon, false, false); arrange(c->mon, false, false);
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, 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_remove(&c->link); // 从原来位置移除
wl_list_insert(clients.prev, &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 (floating == 1 && c != grabc) {
if (c->isfullscreen) { if (c->isfullscreen) {
c->isfullscreen = 0; client_pending_fullscreen_state(c, 0);
client_set_fullscreen(c, 0); client_set_fullscreen(c, 0);
} }
c->ismaximizescreen = 0; client_pending_maximized_state(c, 0);
exit_scroller_stack(c); exit_scroller_stack(c);
// 重新计算居中的坐标 // 重新计算居中的坐标
@ -5171,12 +5192,12 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) {
return; return;
int32_t old_maximizescreen_state = c->ismaximizescreen; int32_t old_maximizescreen_state = c->ismaximizescreen;
c->ismaximizescreen = maximizescreen; client_pending_maximized_state(c, maximizescreen);
if (maximizescreen) { if (maximizescreen) {
if (c->isfullscreen) { if (c->isfullscreen) {
c->isfullscreen = 0; client_pending_fullscreen_state(c, 0);
client_set_fullscreen(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); wlr_scene_node_raise_to_top(&c->scene->node);
if (!is_scroller_layout(c->mon) || c->isfloating) if (!is_scroller_layout(c->mon) || c->isfloating)
resize(c, maximizescreen_box, 0); resize(c, maximizescreen_box, 0);
c->ismaximizescreen = 1;
} else { } else {
c->bw = c->isnoborder ? 0 : config.borderpx; c->bw = c->isnoborder ? 0 : config.borderpx;
c->ismaximizescreen = 0;
if (c->isfloating) if (c->isfloating)
setfloating(c, 1); setfloating(c, 1);
} }
@ -5240,6 +5259,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
c->isfullscreen = fullscreen; c->isfullscreen = fullscreen;
client_set_fullscreen(c, fullscreen); client_set_fullscreen(c, fullscreen);
client_pending_fullscreen_state(c, fullscreen);
if (fullscreen) { if (fullscreen) {
@ -5247,7 +5267,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
client_set_maximized(c, false); client_set_maximized(c, false);
} }
c->ismaximizescreen = 0; client_pending_maximized_state(c, 0);
exit_scroller_stack(c); exit_scroller_stack(c);
c->isfakefullscreen = 0; c->isfakefullscreen = 0;
@ -5256,10 +5276,8 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层
if (!is_scroller_layout(c->mon) || c->isfloating) if (!is_scroller_layout(c->mon) || c->isfloating)
resize(c, c->mon->m, 1); resize(c, c->mon->m, 1);
c->isfullscreen = 1;
} else { } else {
c->bw = c->isnoborder ? 0 : config.borderpx; c->bw = c->isnoborder ? 0 : config.borderpx;
c->isfullscreen = 0;
if (c->isfloating) if (c->isfloating)
setfloating(c, 1); setfloating(c, 1);
} }
@ -5451,8 +5469,7 @@ void show_hide_client(Client *c) {
c->tags = c->oldtags; c->tags = c->oldtags;
arrange(c->mon, false, false); arrange(c->mon, false, false);
} }
c->isminimized = 0; client_pending_minimized_state(c, 0);
wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel, false);
focusclient(c, 1); focusclient(c, 1);
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true); wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true);
} }
@ -5876,8 +5893,8 @@ void overview_backup(Client *c) {
c->isfloating = 0; c->isfloating = 0;
} }
if (c->isfullscreen || c->ismaximizescreen) { if (c->isfullscreen || c->ismaximizescreen) {
c->isfullscreen = 0; // 清除窗口全屏标志 client_pending_fullscreen_state(c, 0); // 清除窗口全屏标志
c->ismaximizescreen = 0; client_pending_maximized_state(c, 0);
} }
c->bw = c->isnoborder ? 0 : config.borderpx; 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) { } else if (want_restore_fullscreen(c) && c->isfullscreen) {
setfullscreen(c, 1); setfullscreen(c, 1);
} else { } else {
c->isfullscreen = 0; client_pending_fullscreen_state(c, 0);
c->ismaximizescreen = 0; client_pending_maximized_state(c, 0);
setfullscreen(c, false); setfullscreen(c, false);
} }
} else { } else {
@ -6481,13 +6498,11 @@ void activatex11(struct wl_listener *listener, void *data) {
return; return;
if (c->isminimized) { if (c->isminimized) {
c->isminimized = 0; client_pending_minimized_state(c, 0);
c->tags = c->mini_restore_tag; c->tags = c->mini_restore_tag;
c->is_scratchpad_show = 0; c->is_scratchpad_show = 0;
c->is_in_scratchpad = 0; c->is_in_scratchpad = 0;
c->isnamedscratchpad = 0; c->isnamedscratchpad = 0;
wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel,
false);
setborder_color(c); setborder_color(c);
if (VISIBLEON(c, c->mon)) { if (VISIBLEON(c, c->mon)) {
need_arrange = true; need_arrange = true;