Compare commits

...

38 commits
0.12.7 ... main

Author SHA1 Message Date
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
DreamMaoMao
c89f8147f9 bump version to 0.12.8 2026-03-29 08:40:40 +08:00
atheeq-rhxn
bccdb651bd docs: update pikaos installation 2026-03-28 20:50:26 +05:30
DreamMaoMao
f94ddc671e opt: always notify the position change to x11 client 2026-03-27 07:31:18 +08:00
DreamMaoMao
b55de28749 opt: dont force request resize when the x11 app reject resize
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
2026-03-26 18:57:02 +08:00
DreamMaoMao
63256ea31b break: rename force_maximize to force_fakemaximize 2026-03-26 18:05:25 +08:00
DreamMaoMao
91e10b239f docs: add scroll_button desc
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-03-25 20:11:57 +08:00
DreamMaoMao
958a6d8768 opt: clear size per record when drag a window to floating 2026-03-25 19:50:42 +08:00
DreamMaoMao
4ccd56ad01 fix: miss make grabc exit scroll stack 2026-03-25 19:44:29 +08:00
DreamMaoMao
585e9ae4b6 opt: change zoom_initial_ratio default to 0.4 2026-03-25 16:20:07 +08:00
DreamMaoMao
d69682aef9 opt: limit button range to 272-279 2026-03-25 11:57:04 +08:00
DreamMaoMao
e6429f8733 opt: not unset maximize state if enable force_maximize 2026-03-24 17:23:42 +08:00
DreamMaoMao
d967153c73
Merge pull request #798 from axQuadratic/mmsg_oflag
mmsg: fix -o flag causing empty output when used with -g
2026-03-23 11:04:58 +08:00
DreamMaoMao
064bcad6f7 opt: optimize fullscreen state change 2026-03-23 10:56:35 +08:00
quadratic
0232dcda82 mmsg: fix -o flag causing subsequent flags to be ignored when used with -g 2026-03-22 20:13:48 +01:00
DreamMaoMao
225fbda574
Merge pull request #786 from kiikii-dev/main
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
Add playback section to keys.md
2026-03-22 11:37:22 +08:00
DreamMaoMao
ccefa572e1 fix: make sure run the last frame even if the animation time is unreasonable 2026-03-20 22:43:09 +08:00
DreamMaoMao
c55e069364 docs: update wlroots build message
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
2026-03-20 09:15:47 +08:00
Kiki
ab6e8a6545
Add playback section to keys.md
Under the "Media Controls" section on "keys.md", add playback keybindings through the Playerctl command-line utility.
2026-03-19 16:29:15 +01:00
DreamMaoMao
deb47e8ab9 opt: not need to reset float_geom in setfullscreen and setmaximizescreen
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-03-19 17:02:48 +08:00
DreamMaoMao
ca665cc6f8 opt: optimize state change between flating maximizescrenn and fullscreen 2026-03-19 15:27:43 +08:00
DreamMaoMao
c776356efe fix: size per not restroe when togglefloating 2026-03-19 10:24:43 +08:00
DreamMaoMao
6eb3378c0c opt: dont restore size per whe toggle_all_floating 2026-03-19 08:45:28 +08:00
DreamMaoMao
c2559f6c7c feat: add dispatch toggle_all_floating 2026-03-19 08:00:05 +08:00
DreamMaoMao
949063804a fix: open_as_floating not match tag correctly 2026-03-19 07:36:50 +08:00
DreamMaoMao
17c037171a docs: update docs 2026-03-18 23:51:05 +08:00
DreamMaoMao
6c81384c53 feat: add tag rule option open_as_floating 2026-03-18 23:43:01 +08:00
DreamMaoMao
833d8a2ef1
Merge pull request #777 from beeb5k/main
meta(nix): update homepage repository link and description
2026-03-18 14:10:07 +08:00
DreamMaoMao
17434d6262 opt: allowe space on both sides of the plus sign when parse mod key 2026-03-17 19:44:54 +08:00
beeb5k
6c88999ada
meta(nix): update description 2026-03-16 19:02:36 +05:30
beeb5k
47b5c9aa2e
meta: update homepage repository links 2026-03-16 16:46:01 +05:30
20 changed files with 729 additions and 143 deletions

View file

@ -137,7 +137,7 @@ And then rebuild your system.
## Other
```bash
git clone -b 0.19.2 https://gitlab.freedesktop.org/wlroots/wlroots.git
git clone -b 0.19.3 https://gitlab.freedesktop.org/wlroots/wlroots.git
cd wlroots
meson build -Dprefix=/usr
sudo ninja -C build install

View file

@ -34,7 +34,7 @@ animation_type_close=slide
animation_fade_in=1
animation_fade_out=1
tag_animation_direction=1
zoom_initial_ratio=0.3
zoom_initial_ratio=0.4
zoom_end_ratio=0.8
fadein_begin_opacity=0.5
fadeout_begin_opacity=0.8

View file

@ -90,6 +90,7 @@ bindr=Super,Super_L,spawn,rofi -show run
| :--- | :--- | :--- |
| `killclient` | - | Close the focused window. |
| `togglefloating` | - | Toggle floating state. |
| `toggle_all_floating` | - | Toggle all visible clients floating state. |
| `togglefullscreen` | - | Toggle fullscreen. |
| `togglefakefullscreen` | - | Toggle "fake" fullscreen (remains constrained). |
| `togglemaximizescreen` | - | Maximize window (keep decoration/bar). |
@ -194,6 +195,16 @@ bind=NONE,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SINK@ toggle
bind=SHIFT,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SOURCE@ toggle
```
#### Playback
Requires: `playerctl`
```ini
bind=NONE,XF86AudioNext,spawn,playerctl next
bind=NONE,XF86AudioPrev,spawn,playerctl previous
bind=NONE,XF86AudioPlay,spawn,playerctl play-pause
```
### Floating Window Movement
| Command | Param | Description |
@ -201,4 +212,4 @@ bind=SHIFT,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SOURCE@ toggle
| `smartmovewin` | `left/right/up/down` | Move floating window by snap distance. |
| `smartresizewin` | `left/right/up/down` | Resize floating window by snap distance. |
| `movewin` | `(x,y)` | Move floating window. |
| `resizewin` | `(width,height)` | Resize window. |
| `resizewin` | `(width,height)` | Resize window. |

View file

@ -45,6 +45,7 @@ Specific settings for laptop touchpads. Some settings may require a relogin to t
| `tap_to_click` | `1` | Tap to trigger a left click. |
| `tap_and_drag` | `1` | Tap and hold to drag items. |
| `trackpad_natural_scrolling` | `0` | Invert scrolling direction (natural scrolling). |
| `scroll_button` | `274` | The mouse button that use for scrolling(272 to 279).
| `scroll_method` | `1` | `1` (Two-finger), `2` (Edge), `4` (Button). |
| `click_method` | `1` | `1` (Button areas), `2` (Clickfinger). |
| `drag_lock` | `1` | Lock dragging after tapping. |
@ -57,6 +58,16 @@ Specific settings for laptop touchpads. Some settings may require a relogin to t
**Detailed descriptions:**
- `scroll_button` values:
- `272` — Left button.
- `273` — Right button.
- `274` — Middle button.
- `275` — Side button.
- `276` — Extra button.
- `277` — Forward button.
- `278` — Back button.
- `279` — Task button.
- `scroll_method` values:
- `0` — Never send scroll events (no scrolling).
- `1` — Two-finger scrolling: send scroll events when two fingers are logically down on the device.

View file

@ -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
```
---
@ -214,7 +214,7 @@ You will need to build `wlroots` and `scenefx` manually as well.
1. **Build wlroots**
Clone and install the specific version required (check README for latest version).
```bash
git clone -b 0.19.2 https://gitlab.freedesktop.org/wlroots/wlroots.git
git clone -b 0.19.3 https://gitlab.freedesktop.org/wlroots/wlroots.git
cd wlroots
meson build -Dprefix=/usr
sudo ninja -C build install

View file

@ -46,7 +46,7 @@ fadeout_begin_opacity=0.5
Adjust the zoom ratios for zoom animations.
```ini
zoom_initial_ratio=0.3
zoom_initial_ratio=0.4
zoom_end_ratio=0.8
```

View file

@ -27,7 +27,7 @@ windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values
| `isoverlay` | integer | `0` / `1` | Make it always in top layer |
| `isopensilent` | integer | `0` / `1` | Open without focus |
| `istagsilent` | integer | `0` / `1` | Don't focus if client is not in current view tag |
| `force_maximize` | integer | `0` / `1` (default 1) | The state of client default to maximized |
| `force_fakemaximize` | integer | `0` / `1` (default 1) | The state of client set to fake maximized |
| `ignore_maximize` | integer | `0` / `1` (default 1) | Don't handle maximize request from client |
| `ignore_minimize` | integer | `0` / `1` (default 1) | Don't handle minimize request from client |
| `force_tiled_state` | integer | `0` / `1` | Deceive the window into thinking it is tiling, so it better adheres to assigned dimensions |
@ -179,6 +179,7 @@ tagrule=id:Values,monitor_make:xxx,monitor_model:xxx,Parameter:Values
| `monitor_serial` | string | monitor serial | Match by monitor serial number |
| `layout_name` | string | layout name | Layout name to set |
| `no_render_border` | integer | `0` / `1` | Disable render border |
| `open_as_floating` | integer | `0` / `1` | New open window will be floating|
| `no_hide` | integer | `0` / `1` | Not hide even if the tag is empty |
| `nmaster` | integer | 0, 99 | Number of master windows |
| `mfact` | float | 0.10.9 | Master area factor |

View file

@ -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)

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
version : '0.12.7',
version : '0.12.8',
)
subdir('protocols')

View file

@ -569,12 +569,12 @@ int32_t main(int32_t argc, char *argv[]) {
mode = WATCH;
break;
case 'o':
if (mode == SET)
if (mode == GET || mode == WATCH)
oflag = 1;
else if (mode == SET)
output_name = EARGF(usage());
else
output_name = ARGF();
if (!output_name)
oflag = 1;
break;
case 't':
tflag = 1;

View file

@ -67,8 +67,8 @@ stdenv.mkDerivation {
meta = {
mainProgram = "mango";
description = "A streamlined but feature-rich Wayland compositor";
homepage = "https://github.com/DreamMaoMao/mango";
description = "Practical and Powerful wayland compositor (dwm but wayland)";
homepage = "https://github.com/mangowm/mango";
license = lib.licenses.gpl3Plus;
maintainers = [];
platforms = lib.platforms.unix;

View file

@ -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 <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 {
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";
};
};
};
};
}
);
}

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

@ -723,6 +723,8 @@ 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
@ -752,8 +754,6 @@ void client_animation_next_tick(Client *c) {
// end flush in next frame, not the current frame
c->need_output_flush = false;
}
client_apply_clip(c, factor);
}
void init_fadeout_client(Client *c) {

View file

@ -294,9 +294,8 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
uint32_t height) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_surface_state *state =
&c->surface.xwayland->surface->current;
struct wlr_xwayland_surface *surface = c->surface.xwayland;
struct wlr_surface_state *state = &surface->surface->current;
if ((int32_t)c->geom.width - 2 * (int32_t)c->bw ==
(int32_t)state->width &&
@ -309,6 +308,22 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
return 0;
}
xcb_size_hints_t *size_hints = surface->size_hints;
int32_t width = c->geom.width - 2 * c->bw;
int32_t height = c->geom.height - 2 * c->bw;
if (c->mon && c->mon->isoverview && size_hints &&
c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width &&
c->geom.height - 2 * (int32_t)c->bw < size_hints->min_height)
return 0;
if (size_hints &&
c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width)
width = size_hints->min_width;
if (size_hints &&
c->geom.height - 2 * (int32_t)c->bw < size_hints->min_height)
height = size_hints->min_height;
wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
c->geom.y + c->bw, width, height);
return 1;
@ -350,7 +365,7 @@ static inline void client_set_maximized(Client *c, bool maximized) {
static inline void client_set_tiled(Client *c, uint32_t edges) {
struct wlr_xdg_toplevel *toplevel;
#ifdef XWAYLAND
if (client_is_x11(c) && c->force_maximize) {
if (client_is_x11(c) && c->force_fakemaximize) {
wlr_xwayland_surface_set_maximized(c->surface.xwayland,
edges != WLR_EDGE_NONE,
edges != WLR_EDGE_NONE);
@ -365,7 +380,7 @@ static inline void client_set_tiled(Client *c, uint32_t edges) {
wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
}
if (c->force_maximize) {
if (c->force_fakemaximize) {
wlr_xdg_toplevel_set_maximized(toplevel, edges != WLR_EDGE_NONE);
}
}

View file

@ -90,7 +90,7 @@ typedef struct {
int32_t no_force_center;
int32_t isterm;
int32_t allow_csd;
int32_t force_maximize;
int32_t force_fakemaximize;
int32_t force_tiled_state;
int32_t force_tearing;
int32_t noswallow;
@ -168,6 +168,7 @@ typedef struct {
float mfact;
int32_t nmaster;
int32_t no_render_border;
int32_t open_as_floating;
int32_t no_hide;
} ConfigTagRule;
@ -634,9 +635,14 @@ uint32_t parse_mod(const char *mod_str) {
// 分割处理每个部分
token = strtok_r(input_copy, "+", &saveptr);
while (token != NULL) {
// 去除空白
while (*token == ' ' || *token == '\t')
token++;
// 去除前后空白
trim_whitespace(token);
// 如果 token 变成空字符串则跳过
if (*token == '\0') {
token = strtok_r(NULL, "+", &saveptr);
continue;
}
if (strncmp(token, "code:", 5) == 0) {
// 处理 code: 形式
@ -1196,6 +1202,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
} else if (strcmp(func_name, "scroller_stack") == 0) {
func = scroller_stack;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "toggle_all_floating") == 0) {
func = toggle_all_floating;
} else {
return NULL;
}
@ -1899,6 +1907,7 @@ bool parse_option(Config *config, char *key, char *value) {
rule->nmaster = 0;
rule->mfact = 0.0f;
rule->no_render_border = 0;
rule->open_as_floating = 0;
rule->no_hide = 0;
bool parse_error = false;
@ -1927,6 +1936,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->monitor_serial = strdup(val);
} else if (strcmp(key, "no_render_border") == 0) {
rule->no_render_border = CLAMP_INT(atoi(val), 0, 1);
} else if (strcmp(key, "open_as_floating") == 0) {
rule->open_as_floating = CLAMP_INT(atoi(val), 0, 1);
} else if (strcmp(key, "no_hide") == 0) {
rule->no_hide = CLAMP_INT(atoi(val), 0, 1);
} else if (strcmp(key, "nmaster") == 0) {
@ -2047,7 +2058,7 @@ bool parse_option(Config *config, char *key, char *value) {
rule->indleinhibit_when_focus = -1;
rule->isterm = -1;
rule->allow_csd = -1;
rule->force_maximize = -1;
rule->force_fakemaximize = -1;
rule->force_tiled_state = -1;
rule->force_tearing = -1;
rule->noswallow = -1;
@ -2161,8 +2172,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->isterm = atoi(val);
} else if (strcmp(key, "allow_csd") == 0) {
rule->allow_csd = atoi(val);
} else if (strcmp(key, "force_maximize") == 0) {
rule->force_maximize = atoi(val);
} else if (strcmp(key, "force_fakemaximize") == 0) {
rule->force_fakemaximize = atoi(val);
} else if (strcmp(key, "force_tiled_state") == 0) {
rule->force_tiled_state = atoi(val);
} else if (strcmp(key, "force_tearing") == 0) {
@ -3202,7 +3213,7 @@ void override_config(void) {
config.accel_profile = CLAMP_INT(config.accel_profile, 0, 2);
config.accel_speed = CLAMP_FLOAT(config.accel_speed, -1.0f, 1.0f);
config.scroll_method = CLAMP_INT(config.scroll_method, 0, 4);
config.scroll_button = CLAMP_INT(config.scroll_button, 272, 276);
config.scroll_button = CLAMP_INT(config.scroll_button, 272, 279);
config.click_method = CLAMP_INT(config.click_method, 0, 2);
config.send_events_mode = CLAMP_INT(config.send_events_mode, 0, 2);
config.button_map = CLAMP_INT(config.button_map, 0, 1);
@ -3252,7 +3263,7 @@ void set_value_default() {
config.animation_fade_in = 1;
config.animation_fade_out = 1;
config.tag_animation_direction = HORIZONTAL;
config.zoom_initial_ratio = 0.3f;
config.zoom_initial_ratio = 0.4f;
config.zoom_end_ratio = 0.8f;
config.fadein_begin_opacity = 0.5f;
config.fadeout_begin_opacity = 0.5f;
@ -3780,6 +3791,8 @@ void parse_tagrule(Monitor *m) {
m->pertag->mfacts[tr.id] = tr.mfact;
if (tr.no_render_border >= 0)
m->pertag->no_render_border[tr.id] = tr.no_render_border;
if (tr.open_as_floating >= 0)
m->pertag->open_as_floating[tr.id] = tr.open_as_floating;
}
}

View file

@ -70,3 +70,4 @@ int32_t disable_monitor(const Arg *arg);
int32_t enable_monitor(const Arg *arg);
int32_t toggle_monitor(const Arg *arg);
int32_t scroller_stack(const Arg *arg);
int32_t toggle_all_floating(const Arg *arg);

View file

@ -376,7 +376,11 @@ int32_t moveresize(const Arg *arg) {
/* Float the window and tell motionnotify to grab it */
if (grabc->isfloating == 0 && arg->ui == CurMove) {
grabc->drag_to_tile = true;
exit_scroller_stack(grabc);
setfloating(grabc, 1);
grabc->old_stack_inner_per = 0.0f;
grabc->old_master_inner_per = 0.0f;
set_size_per(grabc->mon, grabc);
}
switch (cursor_mode = arg->ui) {
@ -548,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;
@ -1866,3 +1870,27 @@ int32_t scroller_stack(const Arg *arg) {
arrange(selmon, false, false);
return 0;
}
int32_t toggle_all_floating(const Arg *arg) {
if (!selmon || !selmon->sel)
return 0;
Client *c = NULL;
bool should_floating = !selmon->sel->isfloating;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, selmon)) {
if (c->isfloating && !should_floating) {
c->old_master_inner_per = 0.0f;
c->old_stack_inner_per = 0.0f;
set_size_per(selmon, c);
}
if (c->isfloating != should_floating) {
setfloating(c, should_floating);
}
}
}
return 0;
}

View file

@ -1,15 +1,12 @@
bool check_hit_no_border(Client *c) {
int32_t i;
bool hit_no_border = false;
if (!render_border) {
hit_no_border = true;
}
for (i = 0; i < config.tag_rules_count; i++) {
if (c->tags & (1 << (config.tag_rules[i].id - 1)) &&
config.tag_rules[i].no_render_border) {
hit_no_border = true;
}
if (c->mon && !c->mon->isoverview &&
c->mon->pertag->no_render_border[get_tags_first_tag_num(c->tags)]) {
hit_no_border = true;
}
if (config.no_border_when_single && c && c->mon &&
@ -19,6 +16,7 @@ bool check_hit_no_border(Client *c) {
}
return hit_no_border;
}
Client *termforwin(Client *w) {
Client *c = NULL;

View file

@ -391,7 +391,7 @@ struct Client {
struct dwl_opacity_animation opacity_animation;
int32_t isterm, noswallow;
int32_t allow_csd;
int32_t force_maximize;
int32_t force_fakemaximize;
int32_t force_tiled_state;
pid_t pid;
Client *swallowing, *swallowedby;
@ -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"
@ -933,8 +937,9 @@ struct Pertag {
uint32_t curtag, prevtag; /* current and previous tag */
int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
bool no_hide[LENGTH(tags) + 1]; /* no_hide per tag */
bool no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */
int32_t no_hide[LENGTH(tags) + 1]; /* no_hide per tag */
int32_t no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */
int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */
const Layout
*ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */
};
@ -1062,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;
}
@ -1105,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;
@ -1119,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)
@ -1137,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) {
@ -1324,7 +1347,7 @@ void toggle_hotarea(int32_t x_root, int32_t y_root) {
static void apply_rule_properties(Client *c, const ConfigWinRule *r) {
APPLY_INT_PROP(c, r, isterm);
APPLY_INT_PROP(c, r, allow_csd);
APPLY_INT_PROP(c, r, force_maximize);
APPLY_INT_PROP(c, r, force_fakemaximize);
APPLY_INT_PROP(c, r, force_tiled_state);
APPLY_INT_PROP(c, r, force_tearing);
APPLY_INT_PROP(c, r, noswallow);
@ -1402,6 +1425,25 @@ void set_float_malposition(Client *tc) {
tc->float_geom.y = tc->geom.y = y;
}
void client_reset_mon_tags(Client *c, Monitor *mon, uint32_t newtags) {
if (!newtags && mon && !mon->isoverview) {
c->tags = mon->tagset[mon->seltags];
} else if (!newtags && mon && mon->isoverview) {
c->tags = mon->ovbk_current_tagset;
} else if (newtags) {
c->tags = newtags;
} else {
c->tags = mon->tagset[mon->seltags];
}
}
void check_match_tag_floating_rule(Client *c, Monitor *mon) {
if (c->tags && !c->isfloating && mon && !c->swallowedby &&
mon->pertag->open_as_floating[get_tags_first_tag_num(c->tags)]) {
c->isfloating = 1;
}
}
void applyrules(Client *c) {
/* rule matching */
const char *appid, *title;
@ -1526,6 +1568,7 @@ void applyrules(Client *c) {
int32_t fullscreen_state_backup =
c->isfullscreen || client_wants_fullscreen(c);
setmon(c, mon, newtags,
!c->isopensilent &&
!(client_is_x11_popup(c) && client_should_ignore_focus(c)) &&
@ -1533,6 +1576,11 @@ void applyrules(Client *c) {
(!c->istagsilent || !newtags ||
newtags & mon->tagset[mon->seltags]));
if (!c->isfloating) {
c->old_stack_inner_per = c->stack_inner_per;
c->old_master_inner_per = c->master_inner_per;
}
if (c->mon &&
!(c->mon == selmon && c->tags & c->mon->tagset[c->mon->seltags]) &&
!c->isopensilent && !c->istagsilent) {
@ -4042,12 +4090,12 @@ void init_client_properties(Client *c) {
c->master_mfact_per = 0.0f;
c->master_inner_per = 0.0f;
c->stack_inner_per = 0.0f;
c->old_stack_inner_per = 1.0f;
c->old_master_inner_per = 1.0f;
c->old_master_mfact_per = 1.0f;
c->old_stack_inner_per = 0.0f;
c->old_master_inner_per = 0.0f;
c->old_master_mfact_per = 0.0f;
c->isterm = 0;
c->allow_csd = 0;
c->force_maximize = 0;
c->force_fakemaximize = 0;
c->force_tiled_state = 1;
c->force_tearing = 0;
c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
@ -4205,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;
@ -4233,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); // 插入尾部
}
@ -5015,12 +5062,12 @@ setfloating(Client *c, int32_t floating) {
if (floating == 1 && c != grabc) {
if (c->isfullscreen || c->ismaximizescreen) {
c->isfullscreen = 0; // 清除窗口全屏标志
c->ismaximizescreen = 0;
c->bw = c->isnoborder ? 0 : config.borderpx;
if (c->isfullscreen) {
client_pending_fullscreen_state(c, 0);
client_set_fullscreen(c, 0);
}
client_pending_maximized_state(c, 0);
exit_scroller_stack(c);
// 重新计算居中的坐标
@ -5062,7 +5109,8 @@ setfloating(Client *c, int32_t floating) {
// 让当前tag中的全屏窗口退出全屏参与平铺
wl_list_for_each(fc, &clients,
link) if (fc && fc != c && VISIBLEON(fc, c->mon) &&
c->tags & fc->tags && ISFULLSCREEN(fc)) {
c->tags & fc->tags && ISFULLSCREEN(fc) &&
old_floating_state) {
clear_fullscreen_flag(fc);
}
}
@ -5076,7 +5124,8 @@ setfloating(Client *c, int32_t floating) {
layers[c->isfloating ? LyrTop : LyrTile]);
}
if (!c->isfloating && old_floating_state) {
if (!c->isfloating && old_floating_state &&
(c->old_stack_inner_per > 0.0f || c->old_master_inner_per > 0.0f)) {
restore_size_per(c->mon, c);
}
@ -5084,7 +5133,7 @@ setfloating(Client *c, int32_t floating) {
save_old_size_per(c->mon);
}
if (!c->force_maximize)
if (!c->force_fakemaximize)
client_set_maximized(c, false);
if (!c->isfloating || c->force_tiled_state) {
@ -5095,6 +5144,12 @@ setfloating(Client *c, int32_t floating) {
}
arrange(c->mon, false, false);
if (!c->isfloating) {
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
}
setborder_color(c);
printstatus();
}
@ -5137,18 +5192,17 @@ 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)
setfullscreen(c, 0);
if (c->isfullscreen) {
client_pending_fullscreen_state(c, 0);
client_set_fullscreen(c, 0);
}
exit_scroller_stack(c);
if (c->isfloating)
c->float_geom = c->geom;
maximizescreen_box.x = c->mon->w.x + config.gappoh;
maximizescreen_box.y = c->mon->w.y + config.gappov;
maximizescreen_box.width = c->mon->w.width - 2 * config.gappoh;
@ -5156,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);
}
@ -5174,9 +5226,9 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) {
save_old_size_per(c->mon);
}
if (!c->force_maximize && !c->ismaximizescreen) {
if (!c->force_fakemaximize && !c->ismaximizescreen) {
client_set_maximized(c, false);
} else if (!c->force_maximize && c->ismaximizescreen) {
} else if (!c->force_fakemaximize && c->ismaximizescreen) {
client_set_maximized(c, true);
}
@ -5207,26 +5259,25 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
c->isfullscreen = fullscreen;
client_set_fullscreen(c, fullscreen);
client_pending_fullscreen_state(c, fullscreen);
if (fullscreen) {
if (c->ismaximizescreen)
setmaximizescreen(c, 0);
if (c->ismaximizescreen && !c->force_fakemaximize) {
client_set_maximized(c, false);
}
client_pending_maximized_state(c, 0);
exit_scroller_stack(c);
if (c->isfloating)
c->float_geom = c->geom;
c->isfakefullscreen = 0;
c->bw = 0;
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);
}
@ -5375,15 +5426,8 @@ void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) {
/* Make sure window actually overlaps with the monitor */
reset_foreign_tolevel(c);
resize(c, c->geom, 0);
if (!newtags && !m->isoverview) {
c->tags = m->tagset[m->seltags];
} else if (!newtags && m->isoverview) {
c->tags = m->ovbk_current_tagset;
} else if (newtags) {
c->tags = newtags;
} else {
c->tags = m->tagset[m->seltags];
}
client_reset_mon_tags(c, m, newtags);
check_match_tag_floating_rule(c, m);
setfloating(c, c->isfloating);
setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
}
@ -5425,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);
}
@ -5850,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;
@ -5881,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 {
@ -6455,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;