Compare commits

..

230 commits
0.10.9 ... main

Author SHA1 Message Date
DreamMaoMao
c55e069364 docs: update wlroots build message
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-03-20 09:15:47 +08: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
DreamMaoMao
028cf6e49e bump version to 0.12.7
Some checks failed
Sync website / sync-website (push) Has been cancelled
Sync wiki / sync-wiki (push) Has been cancelled
2026-03-16 11:56:41 +08:00
DreamMaoMao
0d8aedf691 fix: shouldn't arrange the closing monitor 2026-03-15 23:26:16 +08:00
DreamMaoMao
5606124da9
Merge pull request #774 from atheeq-rhxn/sync-docs
fix: simplify sync commit message to use source URL only
2026-03-15 20:52:45 +08:00
DreamMaoMao
d50d70172c
Merge pull request #773 from atheeq-rhxn/docs-update
docs: add index files & separations for better navigation
2026-03-15 20:52:33 +08:00
atheeq-rhxn
40a518fa84 fix: simplify sync commit message to use source URL only 2026-03-15 18:20:50 +05:30
atheeq-rhxn
d4321cafac docs: add index files & separations for better navigation 2026-03-15 18:05:01 +05:30
DreamMaoMao
7a1a3f0ca4 opt: preset _JAVA_AWT_WM_NONREPARENTING to 1
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-03-15 14:25:57 +08:00
DreamMaoMao
14fc6ca99f docs: add some note message to monitor rule 2026-03-14 21:35:09 +08:00
DreamMaoMao
32c36ba485 opt: optimzie code struct
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
2026-03-14 13:09:41 +08:00
DreamMaoMao
23af3c5595 opt: auto sync XCURSOR_SIZE and XCURSOR_THEME env from config 2026-03-14 12:50:31 +08:00
DreamMaoMao
b0ec0b4275 docs: remove some old describe 2026-03-14 12:43:49 +08:00
DreamMaoMao
5c52d578a8 docs: fix some sample 2026-03-14 12:24:01 +08:00
DreamMaoMao
427cf6f89f docs: update fcitx env suggest 2026-03-14 08:05:42 +08:00
DreamMaoMao
d5663d2c66
Merge pull request #764 from atheeq-rhxn/docs-update
docs: add AerynOS installation
2026-03-14 08:03:27 +08:00
atheeq-rhxn
1b2aeeec7e docs: add AerynOS installation 2026-03-13 23:40:44 +05:30
DreamMaoMao
d2679e63ff
Merge pull request #760 from atheeq-rhxn/sync-docs
chore(sync-website): update commit message
2026-03-13 20:39:07 +08:00
atheeq-rhxn
d28c8e43a1 chore(sync-website): update commit message 2026-03-13 18:02:08 +05:30
DreamMaoMao
537e4aa079 docs: update some describe 2026-03-13 20:07:14 +08:00
DreamMaoMao
e4da095d41 docs: udpate build dependency 2026-03-13 19:53:12 +08:00
DreamMaoMao
057e53745b docs: update refer repo 2026-03-13 19:41:45 +08:00
DreamMaoMao
a988551551
Merge pull request #759 from atheeq-rhxn/sync-docs
feat: add docs and sync with wiki & website
2026-03-13 19:22:00 +08:00
DreamMaoMao
d38197d7c3
Merge pull request #758 from tonybanters/main
Migrated all global config variables to unified `config.xxx` pattern
2026-03-13 11:46:08 +08:00
tonybanters
a7461d9d5b opt: use config.xxx instead of global presets 2026-03-13 11:44:19 +08:00
atheeq-rhxn
5906d9621e feat: add docs and sync with wiki & website 2026-03-12 10:31:29 +05:30
DreamMaoMao
1fc89d01eb bump versito to 0.12.6 2026-03-10 11:18:43 +08:00
DreamMaoMao
db30977196 opt: optimize popup unconstrain 2026-03-09 13:28:01 +08:00
DreamMaoMao
d441ca22f4 opt: set scroller stack to same first tag 2026-03-08 20:20:13 +08:00
DreamMaoMao
a607d63ae7 opt: reset size per when toggleview 2026-03-08 20:00:44 +08:00
DreamMaoMao
36fb9c017d
Merge pull request #746 from ch4og/main
fix(guix): add deprecated package variable with old naming.
2026-03-08 12:58:55 +08:00
DreamMaoMao
a4ad8d0d19 fix: miss judge isdrag when resize stack in scroller 2026-03-08 08:45:56 +08:00
Nikita Mitasov
89a0f7e315
fix(guix): add deprecated package variable with old naming. 2026-03-07 22:18:46 +03:00
DreamMaoMao
cfe492fbc4 opt: fix a minor judgment error 2026-03-07 23:14:17 +08:00
DreamMaoMao
31284b4b5d opt: optimize center tile layout resizewin 2026-03-07 22:54:31 +08:00
DreamMaoMao
09c1707931 opt: optimize size per caculate when resizewin 2026-03-07 21:41:21 +08:00
DreamMaoMao
d0eb7d7114 opt: remove some config in default config file 2026-03-07 17:40:22 +08:00
DreamMaoMao
636060972d opt: change some default config 2026-03-07 17:29:37 +08:00
DreamMaoMao
b1d744ad1f feat: export drag interval to able configure 2026-03-07 17:22:06 +08:00
DreamMaoMao
75c888bbe4 opt: optimize resizewin setp with keyboard 2026-03-07 16:17:10 +08:00
DreamMaoMao
89a4ec83a0 fix: avoid mutual influence of monitor rules 2026-03-07 14:03:59 +08:00
DreamMaoMao
fd68f188c6 opt: add some comment 2026-03-07 14:03:59 +08:00
DreamMaoMao
63b9ffb1a4 opt: opt the old size per init 2026-03-07 12:54:37 +08:00
DreamMaoMao
95481623cb
Merge pull request #742 from kanvolu/main
Added cycling both ways for switch_proportion_preset
2026-03-07 12:44:51 +08:00
kanvolu
10d7e5c6e3 Added cycling both ways for switch_proportion_preset
Now switch_proportion_preset requires an argument "prev" or "next" to
determine cycle direction
2026-03-07 12:36:59 +08:00
DreamMaoMao
9a17a0279c feat: add custom option to monitorrule 2026-03-06 18:21:49 +08:00
DreamMaoMao
11b4bb03bf feat: support the repeated exchange of the same two clients 2026-03-06 14:27:24 +08:00
DreamMaoMao
6522e18d08 opt: fix potential issues caused by uninitialization 2026-03-06 13:25:24 +08:00
DreamMaoMao
9df273cdf9 opt: clear some comment 2026-03-05 23:03:01 +08:00
DreamMaoMao
0f68187cd0 opt: support restore size per when master change 2026-03-05 20:59:34 +08:00
DreamMaoMao
bf10fabfc2 fix: popup unconstrain cross monitor 2026-03-05 08:27:59 +08:00
DreamMaoMao
7f99b5ff48 feat: use monitor spec to match windowrule monitor field 2026-03-04 12:17:44 +08:00
DreamMaoMao
1e1d41e626 feat: add windowrule option indleinhibit_when_focus 2026-03-03 14:51:32 +08:00
DreamMaoMao
9aa2d3cd33 opt: rename hide_source var to hide_cursor_source 2026-03-03 13:03:35 +08:00
DreamMaoMao
46e867deb9 opt: always arrangelayers if layer commit 2026-03-02 20:53:52 +08:00
DreamMaoMao
ad754167b7 fix: last_cursor surface destroy detect error 2026-03-02 09:40:50 +08:00
DreamMaoMao
0673a9241d
Merge pull request #724 from hboetes/fix-last-cursor-use-after-free
Fix use-after-free crash in cursor surface handling
2026-03-02 09:04:43 +08:00
DreamMaoMao
20dbffdfaf opt: avoid unnecessary action when layer surface commit 2026-03-02 08:35:32 +08:00
DreamMaoMao
263b1845bb opt: optimize layer focus change logic 2026-03-02 08:15:52 +08:00
Han Boetes
c403a47894 Fix use-after-free crash in cursor surface handling
### Problem

`setcursor()` stores the client-provided `wlr_surface` pointer in
`last_cursor.surface`, but never registers a destroy listener on it.
When the client exits (e.g. closing a launcher like fuzzel), the surface
is destroyed, but `last_cursor.surface` still holds the stale pointer.

If the cursor hide timeout fires while the cursor surface is alive, and
the client then exits, the next mouse movement calls
`handlecursoractivity()`, which passes the dangling pointer to
`wlr_cursor_set_surface()`. This causes a SIGSEGV in `wl_list_insert()`
inside libwayland-server, as the `wl_list` embedded in the destroyed
surface struct has been freed.

A secondary issue exists in `setcursorshape()`: when a client switches
from a custom cursor surface to a shape cursor, `last_cursor.surface` is
set to NULL but the destroy listener (if registered) is not removed,
leaving a dangling listener on the destroyed surface.

The crash only manifests when `cursor_hidden` is true at the moment of
the mouse movement, which is why it is intermittent and difficult to
reproduce.

### Root cause

Confirmed via `coredumpctl debug` and `bt full`:

```
#0  wl_list_insert (libwayland-server.so)
#1  wlr_cursor_set_surface (libwlroots)
#2  handlecursoractivity (mango.c)
#3  motionnotify (mango.c)
#4  motionrelative (mango.c)
#5  wl_signal_emit_mutable
#6  handle_libinput_readable
```

### Fix

- Add a `wl_listener` (`last_cursor_surface_destroy_listener`) that
clears `last_cursor.surface` and removes itself when the surface is
destroyed.
- Initialize the listener's link in `setup()` so `wl_list_empty()`
checks are reliable from the start.
- In `setcursor()`, remove any existing listener before registering a
new one on the incoming surface.
- In `setcursorshape()`, remove the destroy listener when switching to a
shape cursor.
- Add a NULL guard in `handlecursoractivity()` as a safety net.
2026-03-01 21:58:03 +01:00
DreamMaoMao
6cc7d16281 opt: only set on_demand layer focus when it request in init_commit 2026-03-02 00:56:47 +08:00
DreamMaoMao
9922ed26c7 update readme 2026-03-01 18:13:40 +08:00
DreamMaoMao
8c8d8025c7
Merge pull request #720 from mangowm/revert-685-optional-session-manager
Revert "Add nix option to enable/disable adding to session manager"
2026-03-01 18:04:41 +08:00
DreamMaoMao
008cb9726e
Revert "Add nix option to enable/disable adding to session manager" 2026-03-01 18:04:21 +08:00
DreamMaoMao
243848f43e bump version to 0.12.5 2026-03-01 12:02:57 +08:00
DreamMaoMao
755ffe06af update website in readme 2026-03-01 11:42:44 +08:00
DreamMaoMao
811610e481 rename mangowc to mangowm 2026-03-01 11:38:17 +08:00
DreamMaoMao
e935af07c1 project: rename guix file 2026-03-01 11:35:51 +08:00
DreamMaoMao
43965a1155 update readme 2026-03-01 11:21:24 +08:00
DreamMaoMao
94380a3e07
Merge pull request #559 from WeraPea/pointer-events-fix
fix: pointer events being one event behind
2026-02-28 23:36:57 +08:00
DreamMaoMao
835269f86b opt: make spawn and spawn_shell log to debug log 2026-02-26 23:22:51 +08:00
DreamMaoMao
43114bbf8d
Merge pull request #713 from noor-latif/fix/nix-update-xcbutilwm-to-libxcb-wm
fix(nix): update deprecated xorg package names to top-level
2026-02-26 17:53:06 +08:00
Noor Latif
b338391984 fix(nix): update deprecated xorg package names to top-level
xcbutilwm → libxcb-wm

This eliminates the deprecation warnings:
- "The xorg package set has been deprecated, 'xorg.xcbutilwm' has been renamed to 'libxcb-wm'"

Changes:
- nix/default.nix:14: xcbutilwm, → libxcb-wm,
- nix/default.nix:60: xcbutilwm → libxcb-wm
2026-02-26 09:25:25 +00:00
DreamMaoMao
4787402b12 opt: optimize monitorrule check 2026-02-26 13:30:42 +08:00
DreamMaoMao
cbc344ab88 fix: avoid opacity exceeds the threshold due to overshot animation curve 2026-02-26 08:29:27 +08:00
DreamMaoMao
1dac96b426 bump version to 0.12.4 2026-02-25 21:53:35 +08:00
DreamMaoMao
6667708d9a feat: monitor arg support multi spec match in disptach 2026-02-25 19:16:06 +08:00
DreamMaoMao
43d0f0f54a opt: remove useless code 2026-02-25 17:35:19 +08:00
DreamMaoMao
bc5cf2c7d7 opt: remove useless code 2026-02-25 17:25:05 +08:00
DreamMaoMao
c344dfe7a2
Merge pull request #709 from qaqland/clean-rendermon
opt: remove unused variable in function rendermon
2026-02-25 17:23:31 +08:00
qaqland
e1c038ae08 opt: remove unused variable in function rendermon
Signed-off-by: qaqland <anguoli@uniontech.com>
2026-02-25 16:58:18 +08:00
DreamMaoMao
6894a36019 opt: remove some portal file comment 2026-02-25 15:24:11 +08:00
DreamMaoMao
5412bbe3b6 Merge pull request #685 from jtliang24/optional-session-manager
Add nix option to enable/disable adding to session manager
2026-02-25 15:23:54 +08:00
DreamMaoMao
564df864bf fix: popup position constrain not work for some app 2026-02-25 15:23:54 +08:00
DreamMaoMao
a28647585f feat: set dbus env auto 2026-02-24 21:40:26 +08:00
Jiatao Liang
2f630c950e optional-session-manager 2026-02-23 22:59:31 -05:00
DreamMaoMao
f8fa7a856c opt: optimize frame skip logic 2026-02-23 08:01:45 +08:00
DreamMaoMao
6b2d694b23 update readme 2026-02-22 11:51:28 +08:00
DreamMaoMao
cad564ba56
Merge pull request #684 from Baba-Is-Win/patch-1
include tgmix in README.md
2026-02-22 09:51:02 +08:00
Mujk
ee8a7b5961 docs: fix guix installation instructions
- Rename GuixSD to Guix System (the distro was renamed in 2019)
2026-02-22 09:49:13 +08:00
DreamMaoMao
d1fd128981 fix: auto set monitor coordinate when no match monitor rule 2026-02-21 18:55:33 +08:00
DreamMaoMao
3d680523d6 opt: if app open when no monitor, init tags and size in updatemons 2026-02-21 17:01:16 +08:00
DreamMaoMao
07aed60245 opt: improve some risk judgments 2026-02-21 16:37:37 +08:00
Baba
4620bc691f
Merge branch 'DreamMaoMao:main' into patch-1 2026-02-20 08:52:41 -06:00
DreamMaoMao
722f9f6876
Merge pull request #683 from realcharmer/packaging
Include packaging status in README
2026-02-20 09:09:16 +08:00
Baba
595f9e3432
include tgmix in README.md 2026-02-19 14:24:56 -06:00
Emil Miler
f0259c6285 Include packaging status in README 2026-02-19 15:02:03 +01:00
DreamMaoMao
2f12f46919 opt: use base surface of client when xytonode in rect node 2026-02-19 19:07:44 +08:00
DreamMaoMao
68075c0044 feat: support index arg in switch_keyboard_layout 2026-02-19 11:23:08 +08:00
DreamMaoMao
23d7b11e27 fix: only apply scroller overspread to head and tail client 2026-02-19 11:17:36 +08:00
DreamMaoMao
6924ca8512 feat: add global option prefer_scroller_overspread 2026-02-19 00:00:58 +08:00
DreamMaoMao
259fdb3a87 bump versiont to 0.12.3 2026-02-18 13:26:24 +08:00
DreamMaoMao
c3dcee2c8e opt: remove useless code 2026-02-17 08:33:45 +08:00
DreamMaoMao
15729db193
Merge pull request #668 from Yappaholic/main
docs: add guix installation instructions
2026-02-16 13:16:56 +08:00
DreamMaoMao
fa88ebace0 project: version not use latest tag 2026-02-16 11:56:51 +08:00
DreamMaoMao
112fb5c007 opt: optimize code struct 2026-02-16 10:06:16 +08:00
DreamMaoMao
1158fb2e3c opt: don't skip frame when grab client 2026-02-16 07:46:42 +08:00
Yappaholic
26a616e3d5 docs: add guix installation instructions 2026-02-15 16:50:07 +03:00
DreamMaoMao
933638d1b8
Merge pull request #666 from squassina/main
fix warning about meson
2026-02-15 19:14:27 +08:00
Ricardo Squassina Lee
f75efa1312
Fix wayland protocol directory variable retrieval 2026-02-15 07:08:35 -03:00
DreamMaoMao
62ab00a7a3 update readme 2026-02-15 16:43:02 +08:00
DreamMaoMao
7ccbeae8b8 fix: if the progress not the child of main, not assume it is stop 2026-02-15 14:08:53 +08:00
DreamMaoMao
49cb5a9d7e feat: support frame skip for x11 app resize 2026-02-15 12:50:42 +08:00
DreamMaoMao
0696fe964d opt: optimize frame skip logic 2026-02-15 10:31:23 +08:00
DreamMaoMao
842b45b584 feat: add skip timer to avoid rermanently block render 2026-02-15 09:02:36 +08:00
DreamMaoMao
02067e3b1e fix: some client property missing init 2026-02-15 08:07:00 +08:00
DreamMaoMao
fdd54afb7e fix: some app frame skip fail when disable animaitons 2026-02-15 08:00:44 +08:00
DreamMaoMao
91110efe0e
Merge pull request #645 from jampe/feature-config-isfakefullscreen
support isfakefullscreen as windowrule property
2026-02-14 08:46:23 +08:00
DreamMaoMao
89413aacf5 fix: fix center tile size per reset 2026-02-14 08:35:30 +08:00
DreamMaoMao
0fe87e6286 fix: fix multi master focus record error 2026-02-13 20:23:03 +08:00
DreamMaoMao
c05eec7f53 feat: support restore stack from non-tile state 2026-02-13 20:02:23 +08:00
DreamMaoMao
8a924494c6 opt: the tagset is current tagset when open window in ov mode 2026-02-13 18:16:06 +08:00
DreamMaoMao
711498490b opt: not back to ov tag when view prev tag 2026-02-13 18:11:18 +08:00
DreamMaoMao
f251615524 opt: flush the blur background cache when unmap a background layer 2026-02-13 11:07:11 +08:00
DreamMaoMao
5ae8975b11 bump version to 0.12.2 2026-02-13 10:44:17 +08:00
DreamMaoMao
313adefd10 opt: better x11 coordinate adjust 2026-02-12 18:55:03 +08:00
DreamMaoMao
17acdae69c opt: make x11 floating window coordinate auto ajust the monitor change 2026-02-12 18:12:01 +08:00
DreamMaoMao
bc52b95c1e opt: make x11 unmanaged window coordinate auto ajust the monitor change 2026-02-12 18:10:02 +08:00
DreamMaoMao
53ee82a726 feat: make force_tiled_state as a option 2026-02-12 11:19:39 +08:00
DreamMaoMao
8484093e32 fix: crash when pointerfocus to a null scene client 2026-02-11 20:44:44 +08:00
DreamMaoMao
b5a157038c opt: tell the synckeymap timer not need to call anymore 2026-02-11 08:31:22 +08:00
DreamMaoMao
783cb86c56 feat: support match monitor make model serial 2026-02-10 10:31:31 +08:00
DreamMaoMao
c7f90cbc69 bump version to 0.12.1 2026-02-09 18:08:28 +08:00
DreamMaoMao
b05bc1ce65 opt: add btn_left and btn_right bind check in config check 2026-02-09 11:50:54 +08:00
DreamMaoMao
6b79a432a4 opt: allow none mode in some mouse button 2026-02-09 11:18:14 +08:00
Daniel Jampen
454145f6e0
support isfakefullscreen as windowrule property 2026-02-08 17:56:26 +01:00
DreamMaoMao
6970315822
Update README.md 2026-02-08 18:17:55 +08:00
DreamMaoMao
e8bf6380fb opt: turn keymap sync into XWAYLAND macro 2026-02-08 12:44:56 +08:00
DreamMaoMao
4820b7a8ab feat: allow single mod keybind 2026-02-08 12:15:44 +08:00
DreamMaoMao
faf2e1e9da opt: sync keymap to xwayland after xwayland ready 2026-02-08 12:13:04 +08:00
DreamMaoMao
acf8363714
Merge pull request #642 from iynaix/source-optional
feat: add a source-optional keyword
2026-02-08 11:27:22 +08:00
DreamMaoMao
241afb4b97 fix: warpcursor not apply in some case 2026-02-08 11:18:38 +08:00
Lin Xianyi
5de87db8ca feat: add a source-optional keyword 2026-02-08 11:15:16 +08:00
DreamMaoMao
9b92f139c0 fix: correct dmabuf version 2026-02-07 21:46:18 +08:00
DreamMaoMao
e658f5c90d opt: fix format 2026-02-07 19:57:07 +08:00
Yujonpradhananga
3db2ac58b4 opt: fix spelling mistake 2026-02-07 17:45:11 +08:00
DreamMaoMao
f8dfeedff1 fix: headless backend cant use keybarod and pointer 2026-02-07 17:32:38 +08:00
DreamMaoMao
2ca2ab739d
Merge pull request #640 from iynaix/mmsg-help
fix: improve help message for mmsg
2026-02-07 17:30:20 +08:00
DreamMaoMao
9e5bccb457
Merge pull request #639 from iynaix/config-exit-code
fix: -p exits with failure if there are parse failures
2026-02-07 17:30:06 +08:00
Lin Xianyi
2734d91e6a fix: improve help message for mmsg 2026-02-07 13:31:30 +08:00
Lin Xianyi
818652d20f fix: -p exits with failure if there are parse failures 2026-02-07 12:12:13 +08:00
DreamMaoMao
4586319bef
Merge pull request #637 from talned/main
Add DesktopNames entry to mango.desktop
2026-02-06 16:14:17 +08:00
Talmed
080dfaa3ae
Add DesktopNames entry to mango.desktop 2026-02-06 19:04:16 +11:00
DreamMaoMao
00a7e579c9 fix: miss reset root color when reload config 2026-02-06 07:43:09 +08:00
DreamMaoMao
8e898417a7 opt: key name case insensitive in keybind 2026-02-05 11:06:52 +08:00
DreamMaoMao
65fcd58949 opt: optimize file detect message 2026-02-04 21:50:44 +08:00
DreamMaoMao
2bda8e3bf1 opt: avoid double reset foreign toplevel handle 2026-02-04 21:42:05 +08:00
DreamMaoMao
a4faf2c494 opt: avoid stack inner per change when switch tty 2026-02-04 21:29:37 +08:00
DreamMaoMao
b0f839468c opt: don't change stack inner per when swallow 2026-02-04 17:42:13 +08:00
DreamMaoMao
8ba259fbb7 opt: avoid opacity flickering in focus animation when open new window 2026-02-04 09:45:26 +08:00
DreamMaoMao
16e361e1ca
Merge pull request #630 from axQuadratic/tagmon_fix
Fix uninitialised argument fields when calling `mmsg -d`
2026-02-04 09:05:01 +08:00
quadratic
336f873d83 fix: uninitialised argument fields when calling mmsg -d 2026-02-03 21:11:41 +01:00
DreamMaoMao
50b67ac539 fix: miss init arg value 2026-02-03 10:15:42 +08:00
DreamMaoMao
0546a2d4c4 opt: allow use comma symbol in spawn shell value 2026-02-03 10:06:26 +08:00
DreamMaoMao
eb0607501d opt: change drag resize request limit to 120hz for floating window 2026-02-02 14:54:09 +08:00
DreamMaoMao
bcee63fa76 feat: add config check in mango cli 2026-02-02 14:54:09 +08:00
DreamMaoMao
5172444e08 bump version to 0.12.0 2026-02-01 15:33:05 +08:00
DreamMaoMao
ca09aa69db opt: not hide null buffer layer if is not mapped 2026-01-30 08:47:12 +08:00
DreamMaoMao
aa5241fb7b fix: allow layer surface attach null buffer 2026-01-29 22:31:41 +08:00
DreamMaoMao
672706c71f fix: not apply vrr disalbe when enable it at the beginning 2026-01-29 11:58:28 +08:00
DreamMaoMao
d9f679a8e3 fix: tagcrossmon not apply in current monitor 2026-01-29 09:49:25 +08:00
DreamMaoMao
4591b69e4d fix: accel_profile can't set to 0 2026-01-29 09:34:58 +08:00
DreamMaoMao
e535aea28b fix: avoid redundant frame requests 2026-01-28 14:50:27 +08:00
DreamMaoMao
30692f6da0 feat: add option hotarea_corner 2026-01-28 09:39:27 +08:00
DreamMaoMao
2e6e23633e break change: remove useless option adaptive_sync 2026-01-28 09:39:27 +08:00
DreamMaoMao
6624d80522 break change: new monitorrule format 2026-01-28 09:39:27 +08:00
DreamMaoMao
05010a1114 fix: crash when some qt app commit a null surface 2026-01-26 18:35:08 +08:00
DreamMaoMao
00de523039 fix: border apply error for smartgap 2026-01-24 23:05:13 +08:00
DreamMaoMao
f1cca251b8 fix: bordercorlor not update when it is in open animaiton 2026-01-24 22:58:47 +08:00
DreamMaoMao
0652f99e6e opt: change preset config prefer 2026-01-24 22:58:47 +08:00
DreamMaoMao
334bc076a0 opt: optimize smartgap 2026-01-24 22:58:47 +08:00
DreamMaoMao
49921eadfa feat: add drag_corner drag_warp_cursor 2026-01-21 13:54:55 +08:00
DreamMaoMao
d78526d1e9 bump version to 0.11.0 2026-01-20 20:33:32 +08:00
DreamMaoMao
1124d47786 fix: fix exchange client error when not in scroller layout 2026-01-20 14:25:07 +08:00
DreamMaoMao
5942c5d807 opt: only disable animation for resizewin tiling window 2026-01-20 13:50:43 +08:00
DreamMaoMao
04ac4bf99c opt: find same stack first in direction find 2026-01-20 11:28:33 +08:00
DreamMaoMao
fc13b0ff15 opt: optimize global client focus logic 2026-01-19 22:18:05 +08:00
DreamMaoMao
43257ad49c opt: support center scroller stack in centerwin dispatch 2026-01-19 14:21:28 +08:00
DreamMaoMao
eff11a5912 opt: focusdir miss remember focuslink when between two different stack client 2026-01-19 12:13:51 +08:00
DreamMaoMao
34aa2e019e opt: optimize drag resize for scoller 2026-01-19 11:56:00 +08:00
DreamMaoMao
48c466254a feat: optimize focusdir to respect focusstack 2026-01-19 10:38:09 +08:00
DreamMaoMao
57d7801df2 opt: exit stack head client maximize and fullscreen state when toggle scroller stack 2026-01-18 23:31:48 +08:00
DreamMaoMao
00f56fa3aa opt: ignore direction arg if the direction not match the scroller direction 2026-01-18 23:13:43 +08:00
DreamMaoMao
deaa26c779 opt: disable animaiton for resize and move window dispatch 2026-01-18 22:54:01 +08:00
nixpup
e0d69ece59 feat: add scroller stack support 2026-01-18 20:06:27 +08:00
DreamMaoMao
48737bb58c
Merge pull request #583 from 0xwal/fix/fakefullscreen
fix: fakefullscreen toggle from fullscreen state
2026-01-17 15:49:46 +08:00
0xWal
c0f38e8a36
fix: fakefullscreen toggle from fullscreen state 🩹 2026-01-17 10:03:22 +03:00
DreamMaoMao
bc1f310e1c opt: not apply sloppyfocus if the surface is current pointer-focus surface 2026-01-15 13:15:34 +08:00
DreamMaoMao
06a4acd992
Merge pull request #574 from skissue/fix-comment
fix: comment on tag_animation_direction in default config
2026-01-15 11:43:59 +08:00
Ad
373377eb17 fix: comment on tag_animation_direction in default config 2026-01-13 23:28:44 -05:00
DreamMaoMao
764eb44b9b
Update README.md 2026-01-13 20:25:19 +08:00
DreamMaoMao
8e8c513beb opt: sloppyfocus not apply to tagouting client 2026-01-13 13:14:59 +08:00
DreamMaoMao
4efb8c5e06 fix: isfloating rule not follow monitor rule 2026-01-08 10:13:21 +08:00
werapi
c2a7146168 fix: pointer events being one event behind 2026-01-07 16:25:55 +01:00
DreamMaoMao
5a714b7562 opt: optimize sloppyfocus 2026-01-05 22:12:44 +08:00
DreamMaoMao
775931a4e1 bump version to 0.10.10 2026-01-04 19:59:35 +08:00
DreamMaoMao
67b37559a8 opt: ensure auto schedule next frame when animaiton not end 2026-01-04 07:18:04 +08:00
DreamMaoMao
d2894f0bab opt: use int instaead of uint in layout code 2026-01-03 10:38:42 +08:00
DreamMaoMao
2771053ee6 opt: animations logic all use the int type
avoid the coordinates being forcibly limited to positive numbers
2026-01-03 09:37:34 +08:00
DreamMaoMao
0d13b1002e opt: correct the layer animation coordinate data type 2026-01-03 09:19:18 +08:00
DreamMaoMao
23d550e04d fix: avoid use null mon when session not active 2026-01-01 14:48:49 +08:00
DreamMaoMao
ed557fa5aa fix: miss set floating window not overlap to non-custom pos client 2026-01-01 12:37:00 +08:00
DreamMaoMao
89e0805d54 opt: optimize code struct 2026-01-01 12:26:19 +08:00
DreamMaoMao
ec6d54148d opt: change view_current_to_back default to 0 2026-01-01 09:49:33 +08:00
DreamMaoMao
df2040c1dc fix: isoverlay windowrule not set to correct client 2025-12-31 17:32:15 +08:00
DreamMaoMao
5ee43c302a opt: not focus isunglobal window when focusstack 2025-12-31 17:27:43 +08:00
DreamMaoMao
0edcd32271 fix: offsetx and offsety not apply 2025-12-31 16:28:55 +08:00
DreamMaoMao
35d123ea2f
Merge pull request #547 from MemoritiK/main
Windowrule offset not applying
2025-12-31 15:50:32 +08:00
Smriti Khanal
b35a7f03e9 Fixed offset not applying 2025-12-31 12:01:11 +05:45
67 changed files with 7620 additions and 2746 deletions

61
.github/scripts/sync-wiki.py vendored Normal file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env python3
import json
import re
from pathlib import Path
DOCS_DIR = Path("docs")
WIKI_DIR = Path("wiki-temp")
FRONTMATTER_RE = re.compile(r"\A---\s*\n.*?^---\s*\n", re.DOTALL | re.MULTILINE)
DOCS_LINK_RE = re.compile(r"\[([^\]]+)\]\(/docs/(?:[^/)]+/)*([^/)#]+)(#[^)]+)?\)")
def collect_all_files() -> list[tuple[Path, str]]:
files = []
def from_dir(directory: Path) -> list[Path]:
meta = directory / "meta.json"
if meta.exists():
data = json.loads(meta.read_text())
return [directory / f"{p}.md" for p in data.get("pages", []) if (directory / f"{p}.md").exists()]
return sorted(directory.glob("*.md"))
for src in from_dir(DOCS_DIR):
files.append((src, "Home" if src.stem == "index" else src.stem))
for subdir in sorted(DOCS_DIR.iterdir()):
if subdir.is_dir():
for src in from_dir(subdir):
files.append((src, src.stem))
return files
def main() -> None:
files = collect_all_files()
contents = {src: src.read_text() for src, _ in files}
for src, dest_name in files:
text = FRONTMATTER_RE.sub("", contents[src], count=1).lstrip("\n")
text = DOCS_LINK_RE.sub(lambda m: f"[{m.group(1)}]({m.group(2)}{m.group(3) or ''})", text)
(WIKI_DIR / f"{dest_name}.md").write_text(text)
lines: list[str] = []
current_section = None
for src, dest_name in files:
section = "General" if src.parent == DOCS_DIR else src.parent.name.replace("-", " ").title()
if section != current_section:
if current_section is not None:
lines.append("")
lines.append(f"## {section}\n")
current_section = section
if dest_name != "Home":
title = dest_name.replace("-", " ").replace("_", " ").title()
lines.append(f"- [[{dest_name}|{title}]]")
(WIKI_DIR / "_Sidebar.md").write_text("\n".join(lines))
if __name__ == "__main__":
main()

View file

@ -16,7 +16,7 @@ jobs:
days-before-issue-stale: -1 days-before-issue-stale: -1
# 手动标记后14 天后关闭 # 手动标记后14 天后关闭
days-before-issue-close: 7 days-before-issue-close: 7
# 使用的标签(必须和你手动添加的标签一致) # 使用的标签
stale-issue-label: "stale" stale-issue-label: "stale"
# 自动关闭时自动加上的标签 # 自动关闭时自动加上的标签
close-issue-label: "automatic-closing" close-issue-label: "automatic-closing"

44
.github/workflows/sync-website.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: Sync website
on:
push:
branches: [main]
paths:
- docs/**
concurrency:
group: sync-website
cancel-in-progress: true
jobs:
sync-website:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
token: ${{ github.token }}
- name: Checkout website
uses: actions/checkout@v4
with:
repository: mangowm/mangowm.github.io
path: website
token: ${{ secrets.WEBSITE_SYNC_TOKEN }}
fetch-depth: 1
- name: Sync docs
run: |
rm -rf website/apps/web/content/docs
cp -r docs website/apps/web/content/docs
- name: Commit and push
working-directory: website
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add apps/web/content/docs
git diff --staged --quiet || git commit \
-m "docs: content update from mangowm/mango" \
-m "${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}"
git push

40
.github/workflows/sync-wiki.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: Sync wiki
on:
push:
branches: [main]
paths:
- docs/**
concurrency:
group: sync-wiki
cancel-in-progress: true
permissions:
contents: write
jobs:
sync-wiki:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Clone wiki
run: |
git clone --depth 1 \
https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.wiki.git \
wiki-temp
- name: Sync docs to wiki
run: |
find wiki-temp -not -path 'wiki-temp/.git*' -type f -delete
python3 .github/scripts/sync-wiki.py
- name: Commit and push
working-directory: wiki-temp
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git diff --staged --quiet || git commit -m "sync from ${{ github.sha }}"
git push

View file

@ -1,6 +1,6 @@
# Mango Wayland Compositor # Mango Wayland Compositor
<div> <div>
<img src="https://github.com/DreamMaoMao/mangowc/blob/main/assets/mango-transparency-256.png" alt="MangoWC Logo" width="120"/> <img src="https://github.com/mangowm/mango/blob/main/assets/mango-transparency-256.png" alt="MangoWM Logo" width="120"/>
</div> </div>
This project's development is based on [dwl](https://codeberg.org/dwl/dwl/). This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
@ -23,25 +23,22 @@ This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
- Ipc support(get/send message from/to compositor by external program) - Ipc support(get/send message from/to compositor by external program)
- Hycov-like overview - Hycov-like overview
- Window effects from scenefx (blur, shadow, corner radius, opacity) - Window effects from scenefx (blur, shadow, corner radius, opacity)
- Zero flickering - every frame is perfect.
Master-Stack Layout https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
https://github.com/user-attachments/assets/a9d4776e-b50b-48fb-94ce-651d8a749b8a # Mango's Vision
Scroller Layout **Mango's primary goal is stability**: After months of testing and development—and aside from a few lingering GPU compatibility issues—it should now be stable enough. I don't plan on making many breaking changes.
https://github.com/user-attachments/assets/c9bf9415-fad1-4400-bcdc-3ad2d76de85a **Mango's preference is practicality**: I tend to add features that genuinely help with daily workflows—things that make our work more convenient.
Layer animation
https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
**Mango won't cater to every user preference**: For niche feature requests, I'll take a wait-and-see approach. I'll only consider adding them if they get a significant number of upvotes.
# Our discord # Our discord
[mangowc](https://discord.gg/CPjbDxesh5) [mangowm](https://discord.gg/CPjbDxesh5)
# Supported layouts # Supported layouts
- tile - tile
- scroller - scroller
- monocle - monocle
@ -51,21 +48,20 @@ https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
- vertical_tile - vertical_tile
- vertical_grid - vertical_grid
- vertical_scroller - vertical_scroller
- tgmix
# Installation # Installation
[![Packaging status](https://repology.org/badge/vertical-allrepos/mangowm.svg)](https://repology.org/project/mangowm/versions)
## Dependencies ## Dependencies
- glibc
- wayland - wayland
- wayland-protocols - wayland-protocols
- libinput - libinput
- libdrm - libdrm
- libxkbcommon - libxkbcommon
- pixman - pixman
- git
- meson
- ninja
- libdisplay-info - libdisplay-info
- libliftoff - libliftoff
- hwdata - hwdata
@ -75,9 +71,9 @@ https://github.com/user-attachments/assets/014c893f-115c-4ae9-8342-f9ae3e9a0df0
- libxcb - libxcb
## Arch Linux ## Arch Linux
The package is in the Arch User Repository and is availble for manual download [here](https://aur.archlinux.org/packages/mangowc-git) or through a AUR helper like yay: The package is in the Arch User Repository and is available for manual download [here](https://aur.archlinux.org/packages/mangowm-git) or through a AUR helper like yay:
```bash ```bash
yay -S mangowc-git yay -S mangowm-git
``` ```
@ -91,12 +87,12 @@ eselect repository enable guru
emerge --sync guru emerge --sync guru
``` ```
Then, add `gui-libs/scenefx` and `gui-wm/mangowc` to the `package.accept_keywords`. Then, add `gui-libs/scenefx` and `gui-wm/mangowm` to the `package.accept_keywords`.
Finally, install the package: Finally, install the package:
```bash ```bash
emerge --ask --verbose gui-wm/mangowc emerge --ask --verbose gui-wm/mangowm
``` ```
## Fedora Linux ## Fedora Linux
@ -106,13 +102,42 @@ First, add the [Terra Repository](https://terra.fyralabs.com/).
Then, install the package: Then, install the package:
```bash ```bash
dnf install mangowc dnf install mangowm
``` ```
## Guix System
The package definition is described in the source repository.
First, add `mangowm` channel to `channels.scm` file:
```scheme
;; In $HOME/.config/guix/channels.scm
(cons (channel
(name 'mangowm)
(url "https://github.com/mangowm/mango.git")
(branch "main"))
... ;; Your other channels
%default-channels)
```
Then, run `guix pull` and after update you can either run
`guix install mangowm` or add it to your configuration via:
```scheme
(use-modules (mangowm)) ;; Add mangowm module
;; Add mangowm to packages list
(packages (cons*
mangowm-git
... ;; Other packages you specified
%base-packages))
```
And then rebuild your system.
## Other ## Other
```bash ```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 cd wlroots
meson build -Dprefix=/usr meson build -Dprefix=/usr
sudo ninja -C build install sudo ninja -C build install
@ -122,8 +147,8 @@ cd scenefx
meson build -Dprefix=/usr meson build -Dprefix=/usr
sudo ninja -C build install sudo ninja -C build install
git clone https://github.com/DreamMaoMao/mangowc.git git clone https://github.com/mangowm/mango.git
cd mangowc cd mangowm
meson build -Dprefix=/usr meson build -Dprefix=/usr
sudo ninja -C build install sudo ninja -C build install
``` ```
@ -181,9 +206,9 @@ git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
## Config Documentation ## Config Documentation
Refer to the repo wiki [wiki](https://github.com/DreamMaoMao/mango/wiki/) Refer to the repo wiki [wiki](https://github.com/mangowm/mango/wiki/)
or the website docs [docs](https://mangowc.vercel.app/docs) or the website docs [docs](https://mangowm.github.io/)
# NixOS + Home-manager # NixOS + Home-manager
@ -203,7 +228,7 @@ Here's an example of using the modules in a flake:
}; };
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.url = "github:hercules-ci/flake-parts";
mango = { mango = {
url = "github:DreamMaoMao/mango"; url = "github:mangowm/mango";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
@ -265,9 +290,9 @@ Here's an example of using the modules in a flake:
To package mango for other distributions, you can check the reference setup for: To package mango for other distributions, you can check the reference setup for:
- [nix](https://github.com/DreamMaoMao/mangowc/blob/main/nix/default.nix) - [nix](https://github.com/mangowm/mango/blob/main/nix/default.nix)
- [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowc-git). - [arch](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mangowm-git).
- [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowc) - [gentoo](https://data.gpo.zugaina.org/guru/gui-wm/mangowm)
You might need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git). You might need to package `scenefx` for your distribution, check availability [here](https://github.com/wlrfx/scenefx.git).
@ -285,3 +310,15 @@ Read The Friendly Manual on packaging software in your distribution first.
- https://github.com/swaywm/sway - Sample of Wayland protocol - https://github.com/swaywm/sway - Sample of Wayland protocol
- https://github.com/wlrfx/scenefx - Make it simple to add window effect. - https://github.com/wlrfx/scenefx - Make it simple to add window effect.
# Sponsor
At present, I can only accept sponsorship through an encrypted connection.
If you find this project helpful to you, you can offer sponsorship in the following ways.
<img width="650" height="870" alt="image" src="https://github.com/user-attachments/assets/8c860317-90d2-4071-971d-f1a92b674469" />
Thanks to the following friends for their sponsorship of this project
[@tonybanters](https://github.com/tonybanters)

View file

@ -26,7 +26,7 @@ focused_opacity=1.0
unfocused_opacity=1.0 unfocused_opacity=1.0
# Animation Configuration(support type:zoom,slide) # Animation Configuration(support type:zoom,slide)
# tag_animation_direction: 0-horizontal,1-vertical # tag_animation_direction: 1-horizontal,0-vertical
animations=1 animations=1
layer_animations=1 layer_animations=1
animation_type_open=slide animation_type_open=slide
@ -240,12 +240,11 @@ bind=CTRL+ALT,Left,resizewin,-50,+0
bind=CTRL+ALT,Right,resizewin,+50,+0 bind=CTRL+ALT,Right,resizewin,+50,+0
# Mouse Button Bindings # Mouse Button Bindings
# NONE mode key only work in ov mode # btn_left and btn_right can't bind none mod key
mousebind=SUPER,btn_left,moveresize,curmove mousebind=SUPER,btn_left,moveresize,curmove
mousebind=NONE,btn_middle,togglemaximizescreen,0 mousebind=NONE,btn_middle,togglemaximizescreen,0
mousebind=SUPER,btn_right,moveresize,curresize mousebind=SUPER,btn_right,moveresize,curresize
mousebind=NONE,btn_left,toggleoverview,1
mousebind=NONE,btn_right,killclient,0
# Axis Bindings # Axis Bindings
axisbind=SUPER,UP,viewtoleft_have_client axisbind=SUPER,UP,viewtoleft_have_client

View file

@ -0,0 +1,5 @@
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=wlr
org.freedesktop.impl.portal.Screenshot=wlr
org.freedesktop.impl.portal.Inhibit=none

View file

@ -1,6 +1,7 @@
[Desktop Entry] [Desktop Entry]
Encoding=UTF-8 Encoding=UTF-8
Name=Mango Name=Mango
DesktopNames=mango;wlroots
Comment=mango WM Comment=mango WM
Exec=mango Exec=mango
Icon=mango Icon=mango

15
docs/bindings/index.mdx Normal file
View file

@ -0,0 +1,15 @@
---
title: Bindings & Input
description: Keybindings, mouse gestures, and input devices.
icon: Keyboard
---
Configure how you interact with mangowm using flexible keybindings and input options.
<Cards>
<Card href="/docs/bindings/keys" title="Key Bindings" description="Keyboard shortcuts and modes" />
<Card href="/docs/bindings/mouse-gestures" title="Mouse Gestures" description="Touchpad and mouse gestures" />
</Cards>

205
docs/bindings/keys.md Normal file
View file

@ -0,0 +1,205 @@
---
title: Key Bindings
description: Define keyboard shortcuts and modes.
---
## Syntax
Key bindings follow this format:
```ini
bind[flags]=MODIFIERS,KEY,COMMAND,PARAMETERS
```
- **Modifiers**: `SUPER`, `CTRL`, `ALT`, `SHIFT`, `NONE` (combine with `+`, e.g. `SUPER+CTRL+ALT`).
- **Key**: Key name (from `xev` or `wev`) or keycode (e.g., `code:24` for `q`).
> **Info:** `bind` automatically converts keysym to keycode for comparison. This makes it compatible with all keyboard layouts, but the matching may not always be precise. If a key combination doesn't work on your keyboard layout, use a keycode instead (e.g., `code:24` instead of `q`).
### Flags
- `l`: Works even when screen is locked.
- `s`: Uses keysym instead of keycode to bind.
- `r`: Triggers on key release instead of press.
- `p`: Pass key event to client.
**Examples:**
```ini
bind=SUPER,Q,killclient
bindl=SUPER,L,spawn,swaylock
# Using keycode instead of key name
bind=ALT,code:24,killclient
# Combining keycodes for modifiers and keys
bind=code:64,code:24,killclient
bind=code:64+code:133,code:24,killclient
# Bind with no modifier
bind=NONE,XF86MonBrightnessUp,spawn,brightnessctl set +5%
# Bind a modifier key itself as the trigger key
bind=alt,shift_l,switch_keyboard_layout
```
## Key Modes (Submaps)
You can divide key bindings into named modes. Rules:
1. Set `keymode=<name>` before a group of `bind` lines — those binds only apply in that mode.
2. If no `keymode` is set before a bind, it belongs to the `default` mode.
3. The special `common` keymode applies its binds **across all modes**.
Use `setkeymode` to switch modes, and `mmsg -b` to query the current mode.
```ini
# Binds in 'common' apply in every mode
keymode=common
bind=SUPER,r,reload_config
# Default mode bindings
keymode=default
bind=ALT,Return,spawn,foot
bind=SUPER,F,setkeymode,resize
# 'resize' mode bindings
keymode=resize
bind=NONE,Left,resizewin,-10,0
bind=NONE,Right,resizewin,+10,0
bind=NONE,Escape,setkeymode,default
```
### Single Modifier Key Binding
When binding a modifier key itself, use `NONE` for press and the modifier name for release:
```ini
# Trigger on press of Super key
bind=none,Super_L,spawn,rofi -show run
# Trigger on release of Super key
bindr=Super,Super_L,spawn,rofi -show run
```
## Dispatchers List
### Window Management
| Command | Param | Description |
| :--- | :--- | :--- |
| `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). |
| `toggleglobal` | - | Pin window to all tags. |
| `toggle_render_border` | - | Toggle border rendering. |
| `centerwin` | - | Center the floating window. |
| `minimized` | - | Minimize window to scratchpad. |
| `restore_minimized` | - | Restore window from scratchpad. |
| `toggle_scratchpad` | - | Toggle scratchpad. |
| `toggle_named_scratchpad` | `appid,title,cmd` | Toggle named scratchpad. Launches app if not running, otherwise shows/hides it. |
### Focus & Movement
| Command | Param | Description |
| :--- | :--- | :--- |
| `focusdir` | `left/right/up/down` | Focus window in direction. |
| `focusstack` | `next/prev` | Cycle focus within the stack. |
| `focuslast` | - | Focus the previously active window. |
| `exchange_client` | `left/right/up/down` | Swap window with neighbor in direction. |
| `exchange_stack_client` | `next/prev` | Exchange window position in stack. |
| `zoom` | - | Swap focused window with Master. |
### Tags & Monitors
| Command | Param | Description |
| :--- | :--- | :--- |
| `view` | `-1/0/1-9` or `mask [,synctag]` | View tag. `-1` = previous tagset, `0` = all tags, `1-9` = specific tag, mask e.g. `1\|3\|5`. Optional `synctag` (0/1) syncs the action to all monitors. |
| `viewtoleft` | `[synctag]` | View previous tag. Optional `synctag` (0/1) syncs to all monitors. |
| `viewtoright` | `[synctag]` | View next tag. Optional `synctag` (0/1) syncs to all monitors. |
| `viewtoleft_have_client` | `[synctag]` | View left tag and focus client if present. Optional `synctag` (0/1). |
| `viewtoright_have_client` | `[synctag]` | View right tag and focus client if present. Optional `synctag` (0/1). |
| `viewcrossmon` | `tag,monitor_spec` | View specified tag on specified monitor. |
| `tag` | `1-9 [,synctag]` | Move window to tag. Optional `synctag` (0/1) syncs to all monitors. |
| `tagsilent` | `1-9` | Move window to tag without focusing it. |
| `tagtoleft` | `[synctag]` | Move window to left tag. Optional `synctag` (0/1). |
| `tagtoright` | `[synctag]` | Move window to right tag. Optional `synctag` (0/1). |
| `tagcrossmon` | `tag,monitor_spec` | Move window to specified tag on specified monitor. |
| `toggletag` | `0-9` | Toggle tag on window (0 means all tags). |
| `toggleview` | `1-9` | Toggle tag view. |
| `comboview` | `1-9` | View multi tags pressed simultaneously. |
| `focusmon` | `left/right/up/down/monitor_spec` | Focus monitor by direction or [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
| `tagmon` | `left/right/up/down/monitor_spec,[keeptag]` | Move window to monitor by direction or [monitor spec](/docs/configuration/monitors#monitor-spec-format). `keeptag` is 0 or 1. |
### Layouts
| Command | Param | Description |
| :--- | :--- | :--- |
| `setlayout` | `name` | Switch to layout (e.g., `scroller`, `tile`). |
| `switch_layout` | - | Cycle through available layouts. |
| `incnmaster` | `+1/-1` | Increase/Decrease number of master windows. |
| `setmfact` | `+0.05` | Increase/Decrease master area size. |
| `set_proportion` | `float` | Set scroller window proportion (0.01.0). |
| `switch_proportion_preset` | - | Cycle proportion presets of scroller window. |
| `scroller_stack` | `left/right/up/down` | Move window inside/outside scroller stack by direction. |
| `incgaps` | `+/-value` | Adjust gap size. |
| `togglegaps` | - | Toggle gaps. |
### System
| Command | Param | Description |
| :--- | :--- | :--- |
| `spawn` | `cmd` | Execute a command. |
| `spawn_shell` | `cmd` | Execute shell command (supports pipes `\|`). |
| `spawn_on_empty` | `cmd,tagnumber` | Open command on empty tag. |
| `reload_config` | - | Hot-reload configuration. |
| `quit` | - | Exit mangowm. |
| `toggleoverview` | - | Toggle overview mode. |
| `create_virtual_output` | - | Create a headless monitor (for VNC/Sunshine). |
| `destroy_all_virtual_output` | - | Destroy all virtual monitors. |
| `toggleoverlay` | - | Toggle overlay state for the focused window. |
| `toggle_trackpad_enable` | - | Toggle trackpad enable. |
| `setkeymode` | `mode` | Set keymode. |
| `switch_keyboard_layout` | `[index]` | Switch keyboard layout. Optional index (0, 1, 2...) to switch to specific layout. |
| `setoption` | `key,value` | Set config option temporarily. |
| `disable_monitor` | `monitor_spec` | Shutdown monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
| `enable_monitor` | `monitor_spec` | Power on monitor. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
| `toggle_monitor` | `monitor_spec` | Toggle monitor power. Accepts a [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
### Media Controls
> **Warning:** Some keyboards don't send standard media keys. Run `wev` and press your key to check the exact key name.
#### Brightness
Requires: `brightnessctl`
```ini
bind=NONE,XF86MonBrightnessUp,spawn,brightnessctl s +2%
bind=SHIFT,XF86MonBrightnessUp,spawn,brightnessctl s 100%
bind=NONE,XF86MonBrightnessDown,spawn,brightnessctl s 2%-
bind=SHIFT,XF86MonBrightnessDown,spawn,brightnessctl s 1%
```
#### Volume
Requires: `wpctl` (WirePlumber)
```ini
bind=NONE,XF86AudioRaiseVolume,spawn,wpctl set-volume @DEFAULT_SINK@ 5%+
bind=NONE,XF86AudioLowerVolume,spawn,wpctl set-volume @DEFAULT_SINK@ 5%-
bind=NONE,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SINK@ toggle
bind=SHIFT,XF86AudioMute,spawn,wpctl set-mute @DEFAULT_SOURCE@ toggle
```
### Floating Window Movement
| Command | Param | Description |
| :--- | :--- | :--- |
| `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. |

4
docs/bindings/meta.json Normal file
View file

@ -0,0 +1,4 @@
{
"title": "Bindings & Input",
"pages": ["keys", "mouse-gestures"]
}

View file

@ -0,0 +1,116 @@
---
title: Mouse & Gestures
description: Configure mouse buttons, scrolling, gestures, and lid switches.
---
## Mouse Bindings
Assign actions to mouse button presses with optional modifier keys.
### Syntax
```ini
mousebind=MODIFIERS,BUTTON,COMMAND,PARAMETERS
```
- **Modifiers**: `SUPER`, `CTRL`, `ALT`, `SHIFT`, `NONE`. Combine with `+` (e.g., `SUPER+CTRL`)
- **Buttons**: `btn_left`, `btn_right`, `btn_middle`, `btn_side`, `btn_extra`, `btn_forward`, `btn_back`, `btn_task`
> **Warning:** When modifiers are set to `NONE`, only `btn_middle` works in normal mode. `btn_left` and `btn_right` only work in overview mode.
### Examples
```ini
# Window manipulation
mousebind=SUPER,btn_left,moveresize,curmove
mousebind=SUPER,btn_right,moveresize,curresize
mousebind=SUPER+CTRL,btn_right,killclient
# Overview mode (requires NONE modifier)
mousebind=NONE,btn_left,toggleoverview,-1
mousebind=NONE,btn_right,killclient,0
mousebind=NONE,btn_middle,togglemaximizescreen,0
```
---
## Axis Bindings
Map scroll wheel movements to actions for workspace and window navigation.
### Syntax
```ini
axisbind=MODIFIERS,DIRECTION,COMMAND,PARAMETERS
```
- **Direction**: `UP`, `DOWN`, `LEFT`, `RIGHT`
### Examples
```ini
axisbind=SUPER,UP,viewtoleft_have_client
axisbind=SUPER,DOWN,viewtoright_have_client
```
---
## Gesture Bindings
Enable touchpad swipe gestures for navigation and window management.
### Syntax
```ini
gesturebind=MODIFIERS,DIRECTION,FINGERS,COMMAND,PARAMETERS
```
- **Direction**: `up`, `down`, `left`, `right`
- **Fingers**: `3` or `4`
> **Info:** Gestures require proper touchpad configuration. See [Input Devices](/docs/configuration/input) for touchpad settings like `tap_to_click` and `disable_while_typing`.
### Examples
```ini
# 3-finger: Window focus
gesturebind=none,left,3,focusdir,left
gesturebind=none,right,3,focusdir,right
gesturebind=none,up,3,focusdir,up
gesturebind=none,down,3,focusdir,down
# 4-finger: Workspace navigation
gesturebind=none,left,4,viewtoleft_have_client
gesturebind=none,right,4,viewtoright_have_client
gesturebind=none,up,4,toggleoverview
gesturebind=none,down,4,toggleoverview
```
---
## Switch Bindings
Trigger actions on hardware events like laptop lid open/close.
### Syntax
```ini
switchbind=FOLD_STATE,COMMAND,PARAMETERS
```
- **Fold State**: `fold` (lid closed), `unfold` (lid opened)
> **Warning:** Disable system lid handling in `/etc/systemd/logind.conf`:
>
> ```ini
> HandleLidSwitch=ignore
> HandleLidSwitchExternalPower=ignore
> HandleLidSwitchDocked=ignore
> ```
### Examples
```ini
switchbind=fold,spawn,swaylock -f -c 000000
switchbind=unfold,spawn,wlr-dpms on
```

View file

@ -0,0 +1,87 @@
---
title: Basic Configuration
description: Learn how to configure mangowm files, environment variables, and autostart scripts.
---
## Configuration File
mangowm uses a simple configuration file format. By default, it looks for a configuration file in `~/.config/mango/`.
1. **Locate Default Config**
A fallback configuration is provided at `/etc/mango/config.conf`. You can use this as a reference.
2. **Create User Config**
Copy the default config to your local config directory to start customizing.
```bash
mkdir -p ~/.config/mango
cp /etc/mango/config.conf ~/.config/mango/config.conf
```
3. **Launch with Custom Config (Optional)**
If you prefer to keep your config elsewhere, you can launch mango with the `-c` flag.
```bash
mango -c /path/to/your_config.conf
```
### Sub-Configuration
To keep your configuration organized, you can split it into multiple files and include them using the `source` keyword.
```ini
# Import keybindings from a separate file
source=~/.config/mango/bind.conf
# Relative paths work too
source=./theme.conf
# Optional: ignore if file doesn't exist (useful for shared configs)
source-optional=~/.config/mango/optional.conf
```
### Validate Configuration
You can check your configuration for errors without starting mangowm:
```bash
mango -c /path/to/config.conf -p
```
Use with `source-optional` for shared configs across different setups.
## Environment Variables
You can define environment variables directly within your config file. These are set before the window manager fully initializes.
> **Warning:** Environment variables defined here will be **reset** every time you reload the configuration.
```ini
env=QT_IM_MODULES,wayland;fcitx
env=XMODIFIERS,@im=fcitx
```
## Autostart
mangowm can automatically run commands or scripts upon startup. There are two modes for execution:
| Command | Behavior | Usage Case |
| :--- | :--- | :--- |
| `exec-once` | Runs **only once** when mangowm starts. | Status bars, Wallpapers, Notification daemons |
| `exec` | Runs **every time** the config is reloaded. | Scripts that need to refresh settings |
### Example Setup
```ini
# Start the status bar once
exec-once=waybar
# Set wallpaper
exec-once=swaybg -i ~/.config/mango/wallpaper/room.png
# Reload a custom script on config change
exec=bash ~/.config/mango/reload-settings.sh
```

View file

@ -0,0 +1,21 @@
---
title: Configuration
description: Configure mangowm with config files, environment variables, and autostart.
icon: Settings
---
Configure mangowm through config files, environment variables, and autostart.
<Cards>
<Card href="/docs/configuration/basics" title="Basics" description="Config files, env vars, exec-once, exec" />
<Card href="/docs/configuration/monitors" title="Monitors" description="Monitor settings and resolution" />
<Card href="/docs/configuration/input" title="Input" description="Keyboard, mouse, and touchpad" />
<Card href="/docs/configuration/xdg-portals" title="XDG Portals" description="File pickers and notifications" />
<Card href="/docs/configuration/miscellaneous" title="Miscellaneous" description="Additional options" />
</Cards>

151
docs/configuration/input.md Normal file
View file

@ -0,0 +1,151 @@
---
title: Input Devices
description: Configure keyboard layouts, mouse sensitivity, and touchpad gestures.
---
## Device Configuration
mangowm provides granular control over different input devices.
### Keyboard Settings
Control key repeat rates and layout rules.
| Setting | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `repeat_rate` | `int` | `25` | How many times a key repeats per second. |
| `repeat_delay` | `int` | `600` | Delay (ms) before a held key starts repeating. |
| `numlockon` | `0` or `1` | `0` | Enable NumLock on startup. |
| `xkb_rules_rules` | `string` | - | XKB rules file (e.g., `evdev`, `base`). Usually auto-detected. |
| `xkb_rules_model` | `string` | - | Keyboard model (e.g., `pc104`, `macbook`). |
| `xkb_rules_layout` | `string` | - | Keyboard layout code (e.g., `us`, `de`, `us,de`). |
| `xkb_rules_variant` | `string` | - | Layout variant (e.g., `dvorak`, `colemak`, `intl`). |
| `xkb_rules_options` | `string` | - | XKB options (e.g., `caps:escape`, `ctrl:nocaps`). |
**Example:**
```ini
repeat_rate=40
repeat_delay=300
numlockon=1
xkb_rules_layout=us,de
xkb_rules_variant=dvorak
xkb_rules_options=caps:escape,ctrl:nocaps
```
---
### Trackpad Settings
Specific settings for laptop touchpads. Some settings may require a relogin to take effect.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `disable_trackpad` | `0` | Set to `1` to disable the trackpad entirely. |
| `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_method` | `1` | `1` (Two-finger), `2` (Edge), `4` (Button). |
| `click_method` | `1` | `1` (Button areas), `2` (Clickfinger). |
| `drag_lock` | `1` | Lock dragging after tapping. |
| `disable_while_typing` | `1` | Disable trackpad while typing. |
| `left_handed` | `0` | Swap left/right buttons. |
| `middle_button_emulation` | `0` | Emulate middle button. |
| `swipe_min_threshold` | `1` | Minimum swipe threshold. |
---
**Detailed descriptions:**
- `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.
- `2` — Edge scrolling: send scroll events when a finger moves along the bottom or right edge.
- `4` — Button scrolling: send scroll events when a button is held and the device moves along a scroll axis.
- `click_method` values:
- `0` — No software click emulation.
- `1` — Button areas: use software-defined areas on the touchpad to generate button events.
- `2` — Clickfinger: the number of fingers determines which button is pressed.
- `accel_profile` values:
- `0` — No acceleration.
- `1` — Flat: no dynamic acceleration. Pointer speed = original input speed × (1 + `accel_speed`).
- `2` — Adaptive: slow movement results in less acceleration, fast movement results in more.
- `button_map` values:
- `0` — 1/2/3 finger tap maps to left / right / middle.
- `1` — 1/2/3 finger tap maps to left / middle / right.
- `send_events_mode` values:
- `0` — Send events from this device normally.
- `1` — Do not send events from this device.
- `2` — Disable this device when an external pointer device is plugged in.
---
### Mouse Settings
Configuration for external mice.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `mouse_natural_scrolling` | `0` | Invert scrolling direction. |
| `accel_profile` | `2` | `0` (None), `1` (Flat), `2` (Adaptive). |
| `accel_speed` | `0.0` | Speed adjustment (-1.0 to 1.0). |
| `left_handed` | `0` | Swap left and right buttons. |
| `middle_button_emulation` | `0` | Emulate middle button. |
| `swipe_min_threshold` | `1` | Minimum swipe threshold. |
| `send_events_mode` | `0` | `0` (Enabled), `1` (Disabled), `2` (Disabled on external mouse). |
| `button_map` | `0` | `0` (Left/right/middle), `1` (Left/middle/right). |
---
---
## Keyboard Layout Switching
To bind multiple layouts and toggle between them, define the layouts in `xkb_rules_layout` and use `xkb_rules_options` to set a toggle key combination. Then bind `switch_keyboard_layout` to trigger a switch.
```ini
# Define two layouts: US QWERTY and US Dvorak
xkb_rules_layout=us,us
xkb_rules_variant=,dvorak
xkb_rules_options=grp:lalt_lshift_toggle
```
Or bind it manually to a key:
```ini
# Bind Alt+Shift_L to cycle keyboard layout
bind=alt,shift_l,switch_keyboard_layout
```
Use `mmsg -g -k` to query the current keyboard layout at any time.
---
## Input Method Editor (IME)
To use Fcitx5 or IBus, set these environment variables in your config file.
> **Info:** These settings require a restart of the window manager to take effect.
**For Fcitx5:**
```ini
env=GTK_IM_MODULE,fcitx
env=QT_IM_MODULE,fcitx
env=QT_IM_MODULES,wayland;fcitx
env=SDL_IM_MODULE,fcitx
env=XMODIFIERS,@im=fcitx
env=GLFW_IM_MODULE,ibus
```
**For IBus:**
```ini
env=GTK_IM_MODULE,ibus
env=QT_IM_MODULE,ibus
env=XMODIFIERS,@im=ibus
```

View file

@ -0,0 +1,4 @@
{
"title": "Configuration",
"pages": ["basics", "monitors", "input", "xdg-portals", "miscellaneous"]
}

View file

@ -0,0 +1,51 @@
---
title: Miscellaneous
description: Advanced settings for XWayland, focus behavior, and system integration.
---
## System & Hardware
| Setting | Default | Description |
| :--- | :--- | :--- |
| `xwayland_persistence` | `1` | Keep XWayland running even when no X11 apps are open (reduces startup lag). |
| `syncobj_enable` | `0` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** |
| `allow_lock_transparent` | `0` | Allow the lock screen to be transparent. |
| `allow_shortcuts_inhibit` | `1` | Allow shortcuts to be inhibited by clients. |
| `vrr` | - | Set via [monitor rule](/docs/configuration/monitors#monitor-rules). |
## Focus & Input
| Setting | Default | Description |
| :--- | :--- | :--- |
| `focus_on_activate` | `1` | Automatically focus windows when they request activation. |
| `sloppyfocus` | `1` | Focus follows the mouse cursor. |
| `warpcursor` | `1` | Warp the cursor to the center of the window when focus changes via keyboard. |
| `cursor_hide_timeout` | `0` | Hide the cursor after `N` seconds of inactivity (`0` to disable). |
| `drag_tile_to_tile` | `0` | Allow dragging a tiled window onto another to swap their positions. |
| `drag_corner` | `3` | Corner for drag-to-tile detection (0: none, 13: corners, 4: auto-detect). |
| `drag_warp_cursor` | `1` | Warp cursor when dragging windows to tile. |
| `axis_bind_apply_timeout` | `100` | Timeout (ms) for detecting consecutive scroll events for axis bindings. |
| `axis_scroll_factor` | `1.0` | Scroll factor for axis scroll speed (0.110.0). |
## Multi-Monitor & Tags
| Setting | Default | Description |
| :--- | :--- | :--- |
| `focus_cross_monitor` | `0` | Allow directional focus to cross monitor boundaries. |
| `exchange_cross_monitor` | `0` | Allow exchanging clients across monitor boundaries. |
| `focus_cross_tag` | `0` | Allow directional focus to cross into other tags. |
| `view_current_to_back` | `0` | Toggling the current tag switches back to the previously viewed tag. |
| `scratchpad_cross_monitor` | `0` | Share the scratchpad pool across all monitors. |
| `single_scratchpad` | `1` | Only allow one scratchpad (named or standard) to be visible at a time. |
| `circle_layout` | - | A comma-separated list of layouts `switch_layout` cycles through,the value sample:`tile,scroller`. |
## Window Behavior
| Setting | Default | Description |
| :--- | :--- | :--- |
| `enable_floating_snap` | `0` | Snap floating windows to edges or other windows. |
| `snap_distance` | `30` | Max distance (pixels) to trigger floating snap. |
| `no_border_when_single` | `0` | Remove window borders when only one window is visible on the tag. |
| `idleinhibit_ignore_visible` | `0` | Allow invisible clients (e.g., background audio players) to inhibit idle. |
| `drag_tile_refresh_interval` | `8.0` | Interval (1.016.0) to refresh tiled window resize during drag. Too small may cause application lag. |
| `drag_floating_refresh_interval` | `8.0` | Interval (1.016.0) to refresh floating window resize during drag. Too small may cause application lag. |

View file

@ -0,0 +1,276 @@
---
title: Monitors
description: Manage display outputs, resolution, scaling, and tearing.
---
## Monitor Rules
You can configure each display output individually using the `monitorrule` keyword.
**Syntax:**
```ini
monitorrule=name:Values,Parameter:Values,Parameter:Values
```
> **Info:** If any of the matching fields (`name`, `make`, `model`, `serial`) are set, **all** of the set ones must match to be considered a match. Use `wlr-randr` to get your monitor's name, make, model, and serial.
### Parameters
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `name` | string | Any | Match by monitor name (supports regex) |
| `make` | string | Any | Match by monitor manufacturer |
| `model` | string | Any | Match by monitor model |
| `serial` | string | Any | Match by monitor serial number |
| `width` | integer | 0-9999 | Monitor width |
| `height` | integer | 0-9999 | Monitor height |
| `refresh` | float | 0.001-9999.0 | Monitor refresh rate |
| `x` | integer | 0-99999 | X position |
| `y` | integer | 0-99999 | Y position |
| `scale` | float | 0.01-100.0 | Monitor scale |
| `vrr` | integer | 0, 1 | Enable variable refresh rate |
| `rr` | integer | 0-7 | Monitor transform |
| `custom` | integer | 0, 1 | Enable custom mode (not supported on all displays — may cause black screen) |
### Transform Values
| Value | Rotation |
| :--- | :--- |
| `0` | No transform |
| `1` | 90° counter-clockwise |
| `2` | 180° counter-clockwise |
| `3` | 270° counter-clockwise |
| `4` | 180° vertical flip |
| `5` | Flip + 90° counter-clockwise |
| `6` | Flip + 180° counter-clockwise |
| `7` | Flip + 270° counter-clockwise |
> **Critical:** If you use XWayland applications, **never use negative coordinates** for your monitor positions. This is a known XWayland bug that causes click events to malfunction. Always arrange your monitors starting from `0,0` and extend into positive coordinates.
> **Note:** that "name" is a regular expression. If you want an exact match, you need to add `^` and `$` to the beginning and end of the expression, for example, `^eDP-1$` matches exactly the string `eDP-1`.
### Examples
```ini
# Laptop display: 1080p, 60Hz, positioned at origin
monitorrule=name:^eDP-1$,width:1920,height:1080,refresh:60,x:0,y:10
# Match by make and model instead of name
monitorrule=make:Chimei Innolux Corporation,model:0x15F5,width:1920,height:1080,refresh:60,x:0,y:0
# Virtual monitor with pattern matching
monitorrule=name:HEADLESS-.*,width:1920,height:1080,refresh:60,x:1926,y:0,scale:1,rr:0,vrr:0
```
---
## Monitor Spec Format
Several commands (`focusmon`, `tagmon`, `disable_monitor`, `enable_monitor`, `toggle_monitor`, `viewcrossmon`, `tagcrossmon`) accept a **monitor_spec** string to identify a monitor.
**Format:**
```text
name:xxx&&make:xxx&&model:xxx&&serial:xxx
```
- Any field can be omitted and there is no order requirement.
- If all fields are omitted, the string is treated as the monitor name directly (e.g., `eDP-1`).
- Use `wlr-randr` to find your monitor's name, make, model, and serial.
**Examples:**
```bash
# By name (shorthand)
mmsg -d toggle_monitor,eDP-1
# By make and model
mmsg -d toggle_monitor,make:Chimei Innolux Corporation&&model:0x15F5
# By serial
mmsg -d toggle_monitor,serial:12345678
```
---
## Tearing (Game Mode)
Tearing allows games to bypass the compositor's VSync for lower latency.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `allow_tearing` | `0` | Global tearing control: `0` (Disable), `1` (Enable), `2` (Fullscreen only). |
### Configuration
**Enable Globally:**
```ini
allow_tearing=1
```
**Enable per Window:**
Use a window rule to force tearing for specific games.
```ini
windowrule=force_tearing:1,title:vkcube
```
### Tearing Behavior Matrix
| `force_tearing` \ `allow_tearing` | DISABLED (0) | ENABLED (1) | FULLSCREEN_ONLY (2) |
| :--- | :--- | :--- | :--- |
| **UNSPECIFIED** (0) | Not Allowed | Follows tearing_hint | Only fullscreen follows tearing_hint |
| **ENABLED** (1) | Not Allowed | Allowed | Only fullscreen allowed |
| **DISABLED** (2) | Not Allowed | Not Allowed | Not Allowed |
### Graphics Card Compatibility
> **Warning:** Some graphics cards require setting the `WLR_DRM_NO_ATOMIC` environment variable before mango starts to successfully enable tearing.
Add this to `/etc/environment` and reboot:
```bash
WLR_DRM_NO_ATOMIC=1
```
Or run mango with the environment variable:
```bash
WLR_DRM_NO_ATOMIC=1 mango
```
---
## GPU Compatibility
If mango cannot display correctly or shows a black screen, try selecting a specific GPU:
```bash
# Use a single GPU
WLR_DRM_DEVICES=/dev/dri/card1 mango
# Use multiple GPUs
WLR_DRM_DEVICES=/dev/dri/card0:/dev/dri/card1 mango
```
Some GPUs have compatibility issues with `syncobj_enable=1` — it may crash apps like `kitty` that use syncobj. Set `WLR_DRM_NO_ATOMIC=1` in `/etc/environment` and reboot to resolve this.
---
## Power Management
You can control monitor power using the `mmsg` IPC tool.
```bash
# Turn off
mmsg -d disable_monitor,eDP-1
# Turn on
mmsg -d enable_monitor,eDP-1
# Toggle
mmsg -d toggle_monitor,eDP-1
```
You can also use `wlr-randr` for monitor management:
```bash
# Turn off monitor
wlr-randr --output eDP-1 --off
# Turn on monitor
wlr-randr --output eDP-1 --on
# Show all monitors
wlr-randr
```
---
## Screen Scale
### Without Global Scale (Recommended)
- If you do not use XWayland apps, you can use monitor rules or `wlr-randr` to set a global monitor scale.
- If you are using XWayland apps, it is not recommended to set a global monitor scale.
You can set scale like this, for example with a 1.4 factor.
**Dependencies:**
```bash
yay -S xorg-xrdb
yay -S xwayland-satellite
```
**In config file:**
```ini
env=QT_AUTO_SCREEN_SCALE_FACTOR,1
env=QT_WAYLAND_FORCE_DPI,140
```
**In autostart:**
```bash
echo "Xft.dpi: 140" | xrdb -merge
gsettings set org.gnome.desktop.interface text-scaling-factor 1.4
```
**Edit autostart for XWayland:**
```bash
# Start xwayland
/usr/sbin/xwayland-satellite :11 &
# Apply scale 1.4 for xwayland
sleep 0.5s && echo "Xft.dpi: 140" | xrdb -merge
```
### Using xwayland-satellite to Prevent Blurry XWayland Apps
If you use fractional scaling, you can use `xwayland-satellite` to automatically scale XWayland apps to prevent blurriness, for example with a scale of 1.4.
**Dependencies:**
```bash
yay -S xwayland-satellite
```
**In config file:**
```ini
env=DISPLAY,:2
exec-once=xwayland-satellite :2
monitorrule=name:eDP-1,width:1920,height:1080,refresh:60,x:0,y:0,scale:1.4,vrr:0,rr:0
```
> **Warning:** Use a `DISPLAY` value other than `:1` to avoid conflicting with mangowm.
---
## Virtual Monitors
You can create and manage virtual displays through IPC commands:
```bash
# Create virtual output
mmsg -d create_virtual_output
# Destroy all virtual outputs
mmsg -d destroy_all_virtual_output
```
You can configure virtual monitors using `wlr-randr`:
```bash
# Show all monitors
wlr-randr
# Configure virtual monitor
wlr-randr --output HEADLESS-1 --pos 1921,0 --scale 1 --custom-mode 1920x1080@60Hz
```
Virtual monitors can be used for screen sharing with tools like [Sunshine](https://github.com/LizardByte/Sunshine) and [Moonlight](https://github.com/moonlight-stream/moonlight-android), allowing other devices to act as extended monitors.

View file

@ -0,0 +1,76 @@
---
title: XDG Portals
description: Set up screen sharing, clipboard, keyring, and file pickers using XDG portals.
---
## Portal Configuration
You can customize portal settings via the following paths:
- **User Configuration (Priority):** `~/.config/xdg-desktop-portal/mango-portals.conf`
- **System Fallback:** `/usr/share/xdg-desktop-portal/mango-portals.conf`
> **Warning:** If you previously added `dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=wlroots` to your config, remove it. Mango now handles this automatically.
## Screen Sharing
To enable screen sharing (OBS, Discord, WebRTC), you need `xdg-desktop-portal-wlr`.
1. **Install Dependencies**
`pipewire`, `pipewire-pulse`, `xdg-desktop-portal-wlr`
2. **Optional: Add to autostart**
In some situations the portal may not start automatically. You can add this to your autostart script to ensure it launches:
```bash
/usr/lib/xdg-desktop-portal-wlr &
```
3. **Restart your computer** to apply changes.
### Known Issues
- **Window screen sharing:** Some applications may have issues sharing individual windows. See [#184](https://github.com/mangowm/mango/pull/184) for workarounds.
- **Screen recording lag:** If you experience stuttering during screen recording, see [xdg-desktop-portal-wlr#351](https://github.com/emersion/xdg-desktop-portal-wlr/issues/351).
## Clipboard Manager
Use `cliphist` to manage clipboard history.
**Dependencies:** `wl-clipboard`, `cliphist`, `wl-clip-persist`
**Autostart Config:**
```bash
# Keep clipboard content after app closes
wl-clip-persist --clipboard regular --reconnect-tries 0 &
# Watch clipboard and store history
wl-paste --type text --watch cliphist store &
```
## GNOME Keyring
If you need to store passwords or secrets (e.g., for VS Code or Minecraft launchers), install `gnome-keyring`.
**Configuration:**
Add the following to `~/.config/xdg-desktop-portal/mango-portals.conf`:
```ini
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=wlr
org.freedesktop.impl.portal.Screenshot=wlr
org.freedesktop.impl.portal.Secret=gnome-keyring
org.freedesktop.impl.portal.Inhibit=none
```
## File Picker (File Selector)
**Dependencies:** `xdg-desktop-portal`, `xdg-desktop-portal-gtk`
Reboot your computer once to apply.

101
docs/faq.md Normal file
View file

@ -0,0 +1,101 @@
---
title: FAQ
description: Frequently asked questions and troubleshooting.
---
### How do I arrange tiled windows with my mouse?
You can enable the `drag_tile_to_tile` option in your config. This allows you to drag a tiled window onto another to swap them.
```ini
drag_tile_to_tile=1
```
---
### Why is my background blurry or why does blur look wrong?
Blur applies to the transparent areas of windows. To disable it entirely, set `blur=0`.
If you are experiencing **performance issues with blur**, make sure `blur_optimized=1` (the default). This caches the wallpaper as the blur background, which is much cheaper on the GPU:
```ini
blur_optimized=1
```
---
### Blur shows my wallpaper instead of the real background content
This is expected behavior when `blur_optimized=1` (the default). The optimizer caches the wallpaper to reduce GPU load — windows will blur against the wallpaper rather than the actual content stacked beneath them.
If you want blur to composite against the true background (i.e., show whatever is actually behind the window), set:
```ini
blur_optimized=0
```
> **Warning:** Disabling `blur_optimized` significantly increases GPU consumption and may cause rendering lag, especially on lower-end hardware.
---
### My games are lagging or stuttering
Try enabling **SyncObj** timeline support and **Adaptive Sync** (VRR) if your monitor supports it.
```ini
syncobj_enable=1
adaptive_sync=1
```
---
### My games have high input latency
You can enable **Tearing** (similar to VSync off).
First, enable it globally:
```ini
allow_tearing=1
```
Then force it for your specific game:
```ini
windowrule=force_tearing:1,title:Counter-Strike 2
```
> **Warning:** Some graphics cards require setting `WLR_DRM_NO_ATOMIC=1` before mango starts for tearing to work. Add it to `/etc/environment` and reboot, or launch mango with `WLR_DRM_NO_ATOMIC=1 mango`. See [Monitors — Tearing](/docs/configuration/monitors#tearing-game-mode) for details.
---
### How do I use pipes `|` in spawn commands?
The standard `spawn` command does not support shell pipes directly. You must use `spawn_shell` instead.
```ini
bind=SUPER,P,spawn_shell,echo "hello" | rofi -dmenu
```
---
### Certain key combinations do not work on my keyboard layout.
`bind` automatically converts keysym to keycode, which is compatible with most layouts but can sometimes be imprecise. If a key combination is not triggering, use the **keycode** directly instead of the key name.
Run `wev` and press the key to find its keycode, then use it in your bind:
```ini
# Instead of:
bind=ALT,q,killclient
# Use the keycode (e.g., code:24 = q on most layouts):
bind=ALT,code:24,killclient
```
You can also use `binds` (the `s` flag) to match by keysym instead of keycode:
```ini
binds=ALT,q,killclient
```

42
docs/index.md Normal file
View file

@ -0,0 +1,42 @@
---
title: Introduction
description: A lightweight and feature-rich Wayland compositor based on dwl.
---
**mango** is a Wayland compositor based on [dwl](https://codeberg.org/dwl/dwl/). It aims to be as lightweight as `dwl` and can be built completely within a few seconds, without compromising on functionality.
> **Philosophy:** **Lightweight & Fast**: mango is designed to be minimal yet functional. It compiles in seconds and offers a robust set of features out of the box.
## Feature Highlights
Beyond basic window management, mangowm provides a rich set of features designed for a modern Wayland experience.
- **[Animations](/docs/visuals/animations)** — Smooth, customizable animations for opening, moving, closing windows and tag switching.
- **[Layouts](/docs/window-management/layouts)** — Supports Scroller, Master-Stack, Monocle, Grid, Deck, and more, with per-tag layouts.
- **[Visual Effects](/docs/visuals/effects)** — Built-in blur, shadows, corner radius, and opacity effects powered by scenefx.
- **[IPC & Scripting](/docs/ipc)** — Control the compositor externally with robust IPC support for custom scripts and widgets.
## Additional Features
- **XWayland Support** — Excellent compatibility for legacy X11 applications.
- **Tag System** — Uses tags instead of workspaces, allowing separate window layouts for each tag.
- **Input Methods** — Great support for text input v2/v3 (Fcitx5, IBus).
- **Window States** — Rich states including swallow, minimize, maximize, fullscreen, and overlay.
- **Hot-Reload Config** — Simple external configuration that supports hot-reloading without restarting.
- **Scratchpads** — Support for both Sway-like and named scratchpads.
## Community
- **[Join the mangowm Discord](https://discord.gg/CPjbDxesh5)** — Chat with the community, get support, share your setup, and stay updated with the latest mangowm news.
- **[Join the GitHub Discussions](https://github.com/mangowm/mango/discussions)** — Ask questions, request features, report issues, or share ideas directly with contributors and other users.
## Acknowledgements
This project is built upon the hard work of several open-source projects:
- **[wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)** — Implementation of the Wayland protocol.
- **[mwc](https://github.com/nikoloc/mwc)** — Basal window animation reference.
- **[dwl](https://codeberg.org/dwl/dwl)** — Basal dwl features.
- **[sway](https://github.com/swaywm/sway)** — Sample implementation of the Wayland protocol.
- **[scenefx](https://github.com/wlrfx/scenefx)** — Library to simplify adding window effects.

239
docs/installation.md Normal file
View file

@ -0,0 +1,239 @@
---
title: Installation
description: Install mangowm on AerynOS, Arch, Fedora, Gentoo, Guix System, NixOS, PikaOS, or build from source.
---
## Package Installation
mangowm is available as a pre-built package on several distributions. Choose your distribution below.
---
### AerynOS
mangowm is available in the **AerynOS package repository**.
You can install it using the `moss` package manager:
```bash
sudo moss install mangowm
```
---
### Arch Linux
mangowm is available in the **Arch User Repository (AUR)**.
You can install it using an AUR helper like `yay` or `paru`:
```bash
yay -S mangowm-git
```
> **Tip:** This package pulls the latest git version, ensuring you have the newest features and fixes.
---
### Fedora
The package is in the third-party **Terra repository**. First, add the Terra Repository.
> **Warning:** Both commands require root privileges. Use `sudo` if needed.
```bash
dnf install --nogpgcheck --repofrompath 'terra,https://repos.fyralabs.com/terra$releasever' terra-release
```
Then, install the package:
```bash
dnf install mangowm
```
---
### Gentoo
The package is hosted in the community-maintained **GURU** repository.
1. **Add the GURU repository**
```bash
emerge --ask --verbose eselect-repository
eselect repository enable guru
emerge --sync guru
```
2. **Unmask packages**
Add the required packages to your `package.accept_keywords` file:
- `gui-libs/scenefx`
- `gui-wm/mangowm`
3. **Install mango**
```bash
emerge --ask --verbose gui-wm/mangowm
```
---
### Guix System
The package definition is described in the source repository.
1. **Add mango channel**
Add to `$HOME/.config/guix/channels.scm`:
```scheme
(cons (channel
(name 'mangowm)
(url "https://github.com/mangowm/mango.git")
(branch "main"))
%default-channels)
```
2. **Install**
After running `guix pull`, you can install mangowm:
```bash
guix install mangowm
```
Or add it to your system configuration using the mangowm module:
```scheme
(use-modules (mangowm))
(packages (cons*
mangowm-git
... ;; Other packages
%base-packages))
```
> **Tip:** For more information, see the [Guix System documentation](https://guix.gnu.org/manual/devel/en/html_node/Channels.html).
---
### NixOS
The repository provides a Flake with a NixOS module.
1. **Add flake input**
```nix
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
mangowm = {
url = "github:mangowm/mango";
inputs.nixpkgs.follows = "nixpkgs";
};
# other inputs ...
};
}
```
2. **Import the NixOS module**
**Option A — Import in `configuration.nix`:**
```nix
# configuration.nix (or any other file that you import)
{inputs, ...}: {
imports = [
inputs.mangowm.nixosModules.mango
# .. other imports ...
];
# ...
}
```
**Option B — Import directly in flake:**
```nix
# flake.nix
{
# ...
outputs = { self, nixpkgs, mangowm, ...}@inputs: let
inherit (nixpkgs) lib;
# ...
in {
nixosConfigurations.YourHostName = lib.nixosSystem {
modules = [
mangowm.nixosModules.mango # or inputs.mangowm.nixosModules.mango
# other imports ...
];
};
}
}
```
3. **Enable the module**
```nix
# configuration.nix (or any other file that you import)
{
programs.mango.enable = true;
}
```
4. **Extra options**
- `programs.mango.package` — the mango package to use, allows usage of custom mango drvs
- `programs.mango.addLoginEntry` (default: `true`) — adds login entry to the display manager
---
### PikaOS
mangowm is available in the **PikaOS package repository**.
You can install it using the `pikman` package manager:
```bash
pikman install mangowc
```
---
## Building from Source
If your distribution isn't listed above, or you want the latest unreleased changes, you can build mangowm from source.
> **Info:** Ensure the following dependencies are installed before proceeding:
> - `wayland`
> - `wayland-protocols`
> - `libinput`
> - `libdrm`
> - `libxkbcommon`
> - `pixman`
> - `libdisplay-info`
> - `libliftoff`
> - `hwdata`
> - `seatd`
> - `pcre2`
> - `xorg-xwayland`
> - `libxcb`
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.3 https://gitlab.freedesktop.org/wlroots/wlroots.git
cd wlroots
meson build -Dprefix=/usr
sudo ninja -C build install
```
2. **Build scenefx**
This library handles the visual effects.
```bash
git clone -b 0.4.1 https://github.com/wlrfx/scenefx.git
cd scenefx
meson build -Dprefix=/usr
sudo ninja -C build install
```
3. **Build mangowm**
Finally, compile the compositor itself.
```bash
git clone https://github.com/mangowm/mango.git
cd mango
meson build -Dprefix=/usr
sudo ninja -C build install
```

154
docs/ipc.md Normal file
View file

@ -0,0 +1,154 @@
---
title: IPC
description: Control mangowm programmatically using mmsg.
---
## Introduction
mangowm includes a powerful IPC (Inter-Process Communication) tool called `mmsg`. This allows you to query the window manager's state, watch for events, and execute commands from external scripts.
## Basic Usage
The general syntax for `mmsg` is:
```bash
mmsg [-OTLq]
mmsg [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d <cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]
mmsg [-o <output>] (-g | -w) [-OotlcvmfxekbA]
```
### Options
| Flag | Description |
| :--- | :--- |
| `-q` | Quit mangowm. |
| `-g` | **Get** values (tags, layout, focused client). |
| `-s` | **Set** values (switch tags, layouts). |
| `-w` | **Watch** mode (streams events). |
| `-O` | Get all output (monitor) information. |
| `-T` | Get number of tags. |
| `-L` | Get all available layouts. |
| `-o` | Select output (monitor). |
| `-t` | Get/set selected tags (set with `[+-^.]`). |
| `-l` | Get/set current layout. |
| `-c` | Get title and appid of focused client. |
| `-v` | Get visibility of statusbar. |
| `-m` | Get fullscreen status. |
| `-f` | Get floating status. |
| `-d` | **Dispatch** an internal command. |
| `-x` | Get focused client geometry. |
| `-e` | Get the name of the last focused layer. |
| `-k` | Get current keyboard layout. |
| `-b` | Get current keybind mode. |
| `-A` | Get scale factor of monitor. |
## Examples
### Tag Management
You can perform arithmetic on tags using the `-t` flag with `-s` (set).
```bash
# Switch to Tag 1
mmsg -t 1
# Add Tag 2 to current view (Multiview)
mmsg -s -t 2+
# Remove Tag 2 from current view
mmsg -s -t 2-
# Toggle Tag 2
mmsg -s -t 2^
```
### Layouts
Switch layouts programmatically. Layout codes: `S` (Scroller), `T` (Tile), `G` (Grid), `M` (Monocle), `K` (Deck), `CT` (Center Tile), `RT` (Right Tile), `VS` (Vertical Scroller), `VT` (Vertical Tile), `VG` (Vertical Grid), `VK` (Vertical Deck), `TG` (TGMix).
```bash
# Switch to Scroller
mmsg -l "S"
# Switch to Tile
mmsg -l "T"
```
### Dispatching Commands
Any command available in `config.conf` keybindings can be run via IPC.
```bash
# Close the focused window
mmsg -d killclient
# Resize window by +10 width
mmsg -d resizewin,+10,0
# Toggle fullscreen
mmsg -d togglefullscreen
# Disable a monitor power
mmsg -d disable_monitor,eDP-1
```
### Monitoring & Status
Use `-g` or `-w` to build custom status bars or automation scripts.
```bash
# Watch for all message changes
mmsg -w
# Get all messages without watch
mmsg -g
# Watch focused client appid and title
mmsg -w -c
# Get all available outputs
mmsg -O
# Get all tags message
mmsg -g -t
# Get current focused client message
mmsg -g -c
# Get current keyboard layout
mmsg -g -k
# Get current keybind mode
mmsg -g -b
# Get scale factor of current monitor
mmsg -g -A
```
#### Tag Message Format
- State: 0 → none, 1 → active, 2 → urgent
Example output:
| Monitor | Tag Number | Tag State | Clients in Tag | Focused Client |
|---------|------------|-----------|----------------|----------------|
| eDP-1 | tag 2 | 0 | 1 | 0 |
| Monitor | occupied tags mask | active tags mask | urgent tags mask |
|---------|--------------------|------------------|------------------|
| eDP-1 | 14 | 6 | 0 |
## Virtual Monitors
You can create headless outputs for screen mirroring or remote desktop access (e.g., Sunshine/Moonlight).
```bash
# Create a virtual output
mmsg -d create_virtual_output
# Configure it (set resolution)
wlr-randr --output HEADLESS-1 --pos 1920,0 --mode 1920x1080@60Hz
# Destroy all virtual outputs
mmsg -d destroy_all_virtual_output

17
docs/meta.json Normal file
View file

@ -0,0 +1,17 @@
{
"title": "mangowm",
"pages": [
"---Getting Started---",
"index",
"installation",
"quick-start",
"---Configuration---",
"configuration",
"visuals",
"window-management",
"bindings",
"---Reference---",
"ipc",
"faq"
]
}

88
docs/quick-start.md Normal file
View file

@ -0,0 +1,88 @@
---
title: Quick Start
description: Basic configuration and first steps with mangowm.
---
Now that you have mangowm installed, let's get your environment set up.
## Initial Setup
1. **Create Configuration Directory**
mangowm looks for configuration files in `~/.config/mango/`.
```bash
mkdir -p ~/.config/mango
```
2. **Copy Default Config**
A default configuration file is provided at `/etc/mango/config.conf`. Copy it to your local directory to start customizing.
```bash
cp /etc/mango/config.conf ~/.config/mango/config.conf
```
3. **Launch mangowm**
You can now start the compositor from your TTY.
```bash
mango
```
Optional: To specify a custom config file path:
```bash
mango -c /path/to/your/config.conf
```
## Essential Keybindings
mangowm uses the following keybinds by default:
| Key Combination | Action |
| :--- | :--- |
| `Alt` + `Return` | Open Terminal (defaults to `foot`) |
| `Alt` + `Space` | Open Launcher (defaults to `rofi`) |
| `Alt` + `Q` | Close (Kill) the active window |
| `Super` + `M` | Quit mangowm |
| `Super` + `F` | Toggle Fullscreen |
| `Alt` + `Arrow Keys` | Move focus (Left, Right, Up, Down) |
| `Ctrl` + `1-9` | Switch to Tag 1-9 |
| `Alt` + `1-9` | Move window to Tag 1-9 |
> **Warning:** Some default bindings rely on specific tools like `foot` (terminal) and `rofi` (launcher). Ensure you have them installed or update your `config.conf` to use your preferred alternatives.
## Recommended Tools
To get a fully functional desktop experience, we recommend installing the following components:
| Category | Recommended Tools |
| :--- | :--- |
| Application Launcher | rofi, bemenu, wmenu, fuzzel |
| Terminal Emulator | foot, wezterm, alacritty, kitty, ghostty |
| Status Bar | waybar, eww, quickshell, ags |
| Desktop Shell | Noctalia, DankMaterialShell |
| Wallpaper Setup | swww, swaybg |
| Notification Daemon | swaync, dunst, mako |
| Desktop Portal | xdg-desktop-portal, xdg-desktop-portal-wlr, xdg-desktop-portal-gtk |
| Clipboard | wl-clipboard, wl-clip-persist, cliphist |
| Gamma Control / Night Light | wlsunset, gammastep |
| Miscellaneous | xfce-polkit, wlogout |
## Example Configuration
Check out the [example configuration](https://github.com/DreamMaoMao/mango-config) by the creator of mangowm, including complete setups for mangowm, Waybar, Rofi, and more.
```bash
git clone https://github.com/DreamMaoMao/mango-config.git ~/.config/mango
```
## Next Steps
Now that you are up and running, dive deeper into customizing mangowm:
- [Configure Monitors](/docs/configuration/monitors) — Set up resolution, scaling, and multi-monitor layouts.
- [Window Rules](/docs/window-management/rules#window-rules) — Define how specific applications should behave.
- [Appearance](/docs/visuals/theming) — Customize colors, borders, gaps, and effects.

108
docs/visuals/animations.md Normal file
View file

@ -0,0 +1,108 @@
---
title: Animations
description: Configure smooth transitions for windows and layers.
---
## Enabling Animations
mangowm supports animations for both standard windows and layer shell surfaces (like bars and notifications).
```ini
animations=1
layer_animations=1
```
## Animation Types
You can define different animation styles for opening and closing windows and layer surfaces.
Available types: `slide`, `zoom`, `fade`, `none`.
```ini
animation_type_open=zoom
animation_type_close=slide
layer_animation_type_open=slide
layer_animation_type_close=slide
```
## Fade Settings
Control the fade-in and fade-out effects for animations.
```ini
animation_fade_in=1
animation_fade_out=1
fadein_begin_opacity=0.5
fadeout_begin_opacity=0.5
```
- `animation_fade_in` — Enable fade-in effect (0: disable, 1: enable)
- `animation_fade_out` — Enable fade-out effect (0: disable, 1: enable)
- `fadein_begin_opacity` — Starting opacity for fade-in animations (0.01.0)
- `fadeout_begin_opacity` — Starting opacity for fade-out animations (0.01.0)
## Zoom Settings
Adjust the zoom ratios for zoom animations.
```ini
zoom_initial_ratio=0.3
zoom_end_ratio=0.8
```
- `zoom_initial_ratio` — Initial zoom ratio
- `zoom_end_ratio` — End zoom ratio
## Durations
Control the speed of animations (in milliseconds).
| Setting | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `animation_duration_move` | integer | `500` | Move animation duration (ms) |
| `animation_duration_open` | integer | `400` | Open animation duration (ms) |
| `animation_duration_tag` | integer | `300` | Tag animation duration (ms) |
| `animation_duration_close` | integer | `300` | Close animation duration (ms) |
| `animation_duration_focus` | integer | `0` | Focus change (opacity transition) animation duration (ms) |
```ini
animation_duration_move=500
animation_duration_open=400
animation_duration_tag=300
animation_duration_close=300
animation_duration_focus=0
```
## Custom Bezier Curves
Bezier curves determine the "feel" of an animation (e.g., linear vs. bouncy). The format is `x1,y1,x2,y2`.
You can visualize and generate curve values using online tools like [cssportal.com](https://www.cssportal.com/css-cubic-bezier-generator/) or [easings.net](https://easings.net).
| Setting | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `animation_curve_open` | string | `0.46,1.0,0.29,0.99` | Open animation bezier curve |
| `animation_curve_move` | string | `0.46,1.0,0.29,0.99` | Move animation bezier curve |
| `animation_curve_tag` | string | `0.46,1.0,0.29,0.99` | Tag animation bezier curve |
| `animation_curve_close` | string | `0.46,1.0,0.29,0.99` | Close animation bezier curve |
| `animation_curve_focus` | string | `0.46,1.0,0.29,0.99` | Focus change (opacity transition) animation bezier curve |
| `animation_curve_opafadein` | string | `0.46,1.0,0.29,0.99` | Open opacity animation bezier curve |
| `animation_curve_opafadeout` | string | `0.5,0.5,0.5,0.5` | Close opacity animation bezier curve |
```ini
animation_curve_open=0.46,1.0,0.29,0.99
animation_curve_move=0.46,1.0,0.29,0.99
animation_curve_tag=0.46,1.0,0.29,0.99
animation_curve_close=0.46,1.0,0.29,0.99
animation_curve_focus=0.46,1.0,0.29,0.99
animation_curve_opafadein=0.46,1.0,0.29,0.99
animation_curve_opafadeout=0.5,0.5,0.5,0.5
```
## Tag Animation Direction
Control the direction of tag switch animations.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `tag_animation_direction` | `1` | Tag animation direction (1: horizontal, 0: vertical) |

82
docs/visuals/effects.md Normal file
View file

@ -0,0 +1,82 @@
---
title: Window Effects
description: Add visual polish with blur, shadows, and opacity.
---
## Blur
Blur creates a frosted glass effect for transparent windows.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `blur` | `0` | Enable blur for windows. |
| `blur_layer` | `0` | Enable blur for layer surfaces (like bars/docks). |
| `blur_optimized` | `1` | Caches the wallpaper and blur background, significantly reducing GPU usage. Disabling it will significantly increase GPU consumption and may cause rendering lag. **Highly recommended.** |
| `blur_params_radius` | `5` | The strength (radius) of the blur. |
| `blur_params_num_passes` | `1` | Number of passes. Higher = smoother but more expensive. |
| `blur_params_noise` | `0.02` | Blur noise level. |
| `blur_params_brightness` | `0.9` | Blur brightness adjustment. |
| `blur_params_contrast` | `0.9` | Blur contrast adjustment. |
| `blur_params_saturation` | `1.2` | Blur saturation adjustment. |
> **Warning:** Blur has a relatively high impact on performance. If your hardware is limited, it is not recommended to enable it. If you experience lag with blur on, ensure `blur_optimized=1` — disabling it will significantly increase GPU consumption and may cause rendering lag. To disable blur entirely, set `blur=0`.
---
## Shadows
Drop shadows help distinguish floating windows from the background.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `shadows` | `0` | Enable shadows. |
| `layer_shadows` | `0` | Enable shadows for layer surfaces. |
| `shadow_only_floating` | `1` | Only draw shadows for floating windows (saves performance). |
| `shadows_size` | `10` | Size of the shadow. |
| `shadows_blur` | `15` | Shadow blur amount. |
| `shadows_position_x` | `0` | Shadow X offset. |
| `shadows_position_y` | `0` | Shadow Y offset. |
| `shadowscolor` | `0x000000ff` | Color of the shadow. |
```ini
# Example shadows configuration
shadows=1
layer_shadows=1
shadow_only_floating=1
shadows_size=12
shadows_blur=15
shadows_position_x=0
shadows_position_y=0
shadowscolor=0x000000ff
```
---
## Opacity & Corner Radius
Control the transparency and roundness of your windows.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `border_radius` | `0` | Window corner radius in pixels. |
| `border_radius_location_default` | `0` | Corner radius location: `0` (all), `1` (top-left), `2` (top-right), `3` (bottom-left), `4` (bottom-right), `5` (closest corner). |
| `no_radius_when_single` | `0` | Disable radius if only one window is visible. |
| `focused_opacity` | `1.0` | Opacity for the active window (0.0 - 1.0). |
| `unfocused_opacity` | `1.0` | Opacity for inactive windows (0.0 - 1.0). |
```ini
# Window corner radius in pixels
border_radius=0
# Corner radius location (0=all, 1=top-left, 2=top-right, 3=bottom-left, 4=bottom-right)
border_radius_location_default=0
# Disable radius if only one window is visible
no_radius_when_single=0
# Opacity for the active window (0.0 - 1.0)
focused_opacity=1.0
# Opacity for inactive windows
unfocused_opacity=1.0
```

19
docs/visuals/index.mdx Normal file
View file

@ -0,0 +1,19 @@
---
title: Visuals
description: Customize borders, colors, effects, and animations.
icon: Palette
---
Customize the look of your desktop.
<Cards>
<Card href="/docs/visuals/theming" title="Theming" description="Borders, colors, and cursor" />
<Card href="/docs/visuals/status-bar" title="Status Bar" description="Built-in status bar" />
<Card href="/docs/visuals/effects" title="Effects" description="Blur, shadows, rounded corners" />
<Card href="/docs/visuals/animations" title="Animations" description="Window and tag animations" />
</Cards>

4
docs/visuals/meta.json Normal file
View file

@ -0,0 +1,4 @@
{
"title": "Visuals",
"pages": ["theming", "status-bar", "effects", "animations"]
}

141
docs/visuals/status-bar.md Normal file
View file

@ -0,0 +1,141 @@
---
title: Status Bar
description: Configure Waybar for mangowm.
---
## Module Configuration
mangowm is compatible with Waybar's `ext/workspaces` module (Wayland standard) or the `dwl/tags` module. We recommend `ext/workspaces` for the best experience.
> **Tip:** You can also use the `dwl/tags` module, but `ext/workspaces` provides better integration with mangowm's features. The `ext/workspaces` module requires **Waybar > 0.14.0**.
### `config.jsonc`
Add the following to your Waybar configuration:
```jsonc
{
"modules-left": [
"ext/workspaces",
"dwl/window"
],
"ext/workspaces": {
"format": "{icon}",
"ignore-hidden": true,
"on-click": "activate",
"on-click-right": "deactivate",
"sort-by-id": true
},
"dwl/window": {
"format": "[{layout}] {title}"
}
}
```
## Styling
You can style the tags using standard CSS in `style.css`.
### `style.css`
```css
#workspaces {
border-radius: 4px;
border-width: 2px;
border-style: solid;
border-color: #c9b890;
margin-left: 4px;
padding-left: 10px;
padding-right: 6px;
background: rgba(40, 40, 40, 0.76);
}
#workspaces button {
border: none;
background: none;
box-shadow: inherit;
text-shadow: inherit;
color: #ddca9e;
padding: 1px;
padding-left: 1px;
padding-right: 1px;
margin-right: 2px;
margin-left: 2px;
}
#workspaces button.hidden {
color: #9e906f;
background-color: transparent;
}
#workspaces button.visible {
color: #ddca9e;
}
#workspaces button:hover {
color: #d79921;
}
#workspaces button.active {
background-color: #ddca9e;
color: #282828;
margin-top: 5px;
margin-bottom: 5px;
padding-top: 1px;
padding-bottom: 0px;
border-radius: 3px;
}
#workspaces button.urgent {
background-color: #ef5e5e;
color: #282828;
margin-top: 5px;
margin-bottom: 5px;
padding-top: 1px;
padding-bottom: 0px;
border-radius: 3px;
}
#tags {
background-color: transparent;
}
#tags button {
background-color: #fff;
color: #a585cd;
}
#tags button:not(.occupied):not(.focused) {
font-size: 0;
min-width: 0;
min-height: 0;
margin: -17px;
padding: 0;
color: transparent;
background-color: transparent;
}
#tags button.occupied {
background-color: #fff;
color: #cdc885;
}
#tags button.focused {
background-color: rgb(186, 142, 213);
color: #fff;
}
#tags button.urgent {
background: rgb(171, 101, 101);
color: #fff;
}
#window {
background-color: rgb(237, 196, 147);
color: rgb(63, 37, 5);
}
```
## Complete Configuration Example
> **Tip:** You can find a complete Waybar configuration for mangowm at [waybar-config](https://github.com/DreamMaoMao/waybar-config).

56
docs/visuals/theming.md Normal file
View file

@ -0,0 +1,56 @@
---
title: Theming
description: Customize the visual appearance of borders, colors, and the cursor.
---
## Dimensions
Control the sizing of window borders and gaps.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `borderpx` | `4` | Border width in pixels. |
| `gappih` | `5` | Horizontal inner gap (between windows). |
| `gappiv` | `5` | Vertical inner gap. |
| `gappoh` | `10` | Horizontal outer gap (between windows and screen edges). |
| `gappov` | `10` | Vertical outer gap. |
## Colors
Colors are defined in `0xRRGGBBAA` hex format.
```ini
# Background color of the root window
rootcolor=0x323232ff
# Inactive window border
bordercolor=0x444444ff
# Active window border
focuscolor=0xc66b25ff
# Urgent window border (alerts)
urgentcolor=0xad401fff
```
### State-Specific Colors
You can also color-code windows based on their state:
| State | Config Key | Default Color |
| :--- | :--- | :--- |
| Maximized | `maximizescreencolor` | `0x89aa61ff` |
| Scratchpad | `scratchpadcolor` | `0x516c93ff` |
| Global | `globalcolor` | `0xb153a7ff` |
| Overlay | `overlaycolor` | `0x14a57cff` |
> **Tip:** For scratchpad window sizing, see [Scratchpad](/docs/window-management/scratchpad) configuration.
## Cursor Theme
Set the size and theme of your mouse cursor.
```ini
cursor_size=24
cursor_theme=Adwaita
```

View file

@ -0,0 +1,19 @@
---
title: Window Management
description: Layouts, rules, and window behavior.
icon: LayoutGrid
---
Window management with layouts, rules, and scratchpad support.
<Cards>
<Card href="/docs/window-management/layouts" title="Layouts" description="Tile, scroller, monocle, grid, deck" />
<Card href="/docs/window-management/rules" title="Rules" description="Window rules and conditions" />
<Card href="/docs/window-management/overview" title="Overview" description="Window states and properties" />
<Card href="/docs/window-management/scratchpad" title="Scratchpad" description="Quick access to applications" />
</Cards>

View file

@ -0,0 +1,99 @@
---
title: Layouts
description: Configure and switch between different window layouts.
---
## Supported Layouts
mangowm supports a variety of layouts that can be assigned per tag.
- `tile`
- `scroller`
- `monocle`
- `grid`
- `deck`
- `center_tile`
- `vertical_tile`
- `right_tile`
- `vertical_scroller`
- `vertical_grid`
- `vertical_deck`
- `tgmix`
---
## Scroller Layout
The Scroller layout positions windows in a scrollable strip, similar to PaperWM.
### Configuration
| Setting | Default | Description |
| :--- | :--- | :--- |
| `scroller_structs` | `20` | Width reserved on sides when window ratio is 1. |
| `scroller_default_proportion` | `0.9` | Default width proportion for new windows. |
| `scroller_focus_center` | `0` | Always center the focused window (1 = enable). |
| `scroller_prefer_center` | `0` | Center focused window only if it was outside the view. |
| `scroller_prefer_overspread` | `1` | Allow windows to overspread when there's extra space. |
| `edge_scroller_pointer_focus` | `1` | Focus windows even if partially off-screen. |
| `scroller_proportion_preset` | `0.5,0.8,1.0` | Presets for cycling window widths. |
| `scroller_ignore_proportion_single` | `1` | Ignore proportion adjustments for single windows. |
| `scroller_default_proportion_single` | `1.0` | Default proportion for single windows in scroller. **Requires `scroller_ignore_proportion_single=0` to take effect.** |
> **Warning:** `scroller_prefer_overspread`, `scroller_focus_center`, and `scroller_prefer_center` interact with each other. Their priority order is:
>
> **scroller_prefer_overspread > scroller_focus_center > scroller_prefer_center**
>
> To ensure a lower-priority setting takes effect, you must set all higher-priority options to `0`.
```ini
# Example scroller configuration
scroller_structs=20
scroller_default_proportion=0.9
scroller_focus_center=0
scroller_prefer_center=0
scroller_prefer_overspread=1
edge_scroller_pointer_focus=1
scroller_default_proportion_single=1.0
scroller_proportion_preset=0.5,0.8,1.0
```
---
## Master-Stack Layouts
These settings apply to layouts like `tile` and `center_tile`.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `new_is_master` | `1` | New windows become the master window. |
| `default_mfact` | `0.55` | The split ratio between master and stack areas. |
| `default_nmaster` | `1` | Number of allowed master windows. |
| `smartgaps` | `0` | Disable gaps when only one window is present. |
| `center_master_overspread` | `0` | (Center Tile) Master spreads across screen if no stack exists. |
| `center_when_single_stack` | `1` | (Center Tile) Center master when only one stack window exists. |
```ini
# Example master-stack configuration
new_is_master=1
smartgaps=0
default_mfact=0.55
default_nmaster=1
```
---
## Switching Layouts
You can switch layouts dynamically or set a default for specific tags using [Tag Rules](/docs/window-management/rules#tag-rules).
**Keybinding Examples:**
```ini
# Cycle through layouts
bind=SUPER,n,switch_layout
# Set specific layout
bind=SUPER,t,setlayout,tile
bind=SUPER,s,setlayout,scroller
```

View file

@ -0,0 +1,4 @@
{
"title": "Window Management",
"pages": ["layouts", "rules", "overview", "scratchpad"]
}

View file

@ -0,0 +1,29 @@
---
title: Overview
description: Configure the overview mode for window navigation.
---
## Overview Settings
| Setting | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `hotarea_size` | integer | `10` | Hot area size in pixels. |
| `enable_hotarea` | integer | `1` | Enable hot areas (0: disable, 1: enable). |
| `hotarea_corner` | integer | `2` | Hot area corner (0: top-left, 1: top-right, 2: bottom-left, 3: bottom-right). |
| `ov_tab_mode` | integer | `0` | Overview tab mode (0: disable, 1: enable). |
| `overviewgappi` | integer | `5` | Inner gap in overview mode. |
| `overviewgappo` | integer | `30` | Outer gap in overview mode. |
### Setting Descriptions
- `enable_hotarea` — Toggles overview when the cursor enters the configured corner.
- `hotarea_size` — Size of the hot area trigger zone in pixels.
- `hotarea_corner` — Corner that triggers the hot area (0: top-left, 1: top-right, 2: bottom-left, 3: bottom-right).
- `ov_tab_mode` — Circles focus through windows in overview; releasing the mod key exits overview.
### Mouse Interaction in Overview
When in overview mode:
- **Left mouse button** — Jump to (focus) a window.
- **Right mouse button** — Close a window.

View file

@ -0,0 +1,250 @@
---
title: Rules
description: Define behavior for specific windows, tags, and layers.
---
## Window Rules
Window rules allow you to set specific properties (floating, opacity, size, animations, etc.) for applications based on their `appid` or `title`. You can set all parameters in one line, and if you both set appid and title, the window will only follow the rules when appid and title both match.
**Format:**
```ini
windowrule=Parameter:Values,title:Values
windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values
```
### State & Behavior Parameters
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `appid` | string | Any | Match by application ID, supports regex |
| `title` | string | Any | Match by window title, supports regex |
| `isfloating` | integer | `0` / `1` | Force floating state |
| `isfullscreen` | integer | `0` / `1` | Force fullscreen state |
| `isfakefullscreen` | integer | `0` / `1` | Force fake-fullscreen state (window stays constrained) |
| `isglobal` | integer | `0` / `1` | Open as global window (sticky across tags) |
| `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 |
| `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 |
| `noopenmaximized` | integer | `0` / `1` | Window does not open as maximized mode |
| `single_scratchpad` | integer | `0` / `1` (default 1) | Only show one out of named scratchpads or the normal scratchpad |
| `allow_shortcuts_inhibit` | integer | `0` / `1` (default 1) | Allow shortcuts to be inhibited by clients |
| `indleinhibit_when_focus` | integer | `0` / `1` (default 0) | Automatically keep idle inhibit active when this window is focused |
### Geometry & Position
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `width` | integer | 0-9999 | Window width when it becomes a floating window |
| `height` | integer | 0-9999 | Window height when it becomes a floating window |
| `offsetx` | integer | -999-999 | X offset from center (%), 100 is the edge of screen with outer gap |
| `offsety` | integer | -999-999 | Y offset from center (%), 100 is the edge of screen with outer gap |
| `monitor` | string | Any | Assign to monitor by [monitor spec](/docs/configuration/monitors#monitor-spec-format) (name, make, model, or serial) |
| `tags` | integer | 1-9 | Assign to specific tag |
| `no_force_center` | integer | `0` / `1` | Window does not force center |
| `isnosizehint` | integer | `0` / `1` | Don't use min size and max size for size hints |
### Visuals & Decoration
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `noblur` | integer | `0` / `1` | Window does not have blur effect |
| `isnoborder` | integer | `0` / `1` | Remove window border |
| `isnoshadow` | integer | `0` / `1` | Not apply shadow |
| `isnoradius` | integer | `0` / `1` | Not apply corner radius |
| `isnoanimation` | integer | `0` / `1` | Not apply animation |
| `focused_opacity` | integer | `0` / `1` | Window focused opacity |
| `unfocused_opacity` | integer | `0` / `1` | Window unfocused opacity |
| `allow_csd` | integer | `0` / `1` | Allow client side decoration |
> **Tip:** For detailed visual effects configuration, see the [Window Effects](/docs/visuals/effects) page for blur, shadows, and opacity settings.
### Layout & Scroller
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `scroller_proportion` | float | 0.1-1.0 | Set scroller proportion |
| `scroller_proportion_single` | float | 0.1-1.0 | Set scroller auto adjust proportion when it is single window |
> **Tip:** For comprehensive layout configuration, see the [Layouts](/docs/window-management/layouts) page for all layout options and detailed settings.
### Animation
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `animation_type_open` | string | zoom, slide, fade, none | Set open animation |
| `animation_type_close` | string | zoom, slide, fade, none | Set close animation |
| `nofadein` | integer | `0` / `1` | Window ignores fade-in animation |
| `nofadeout` | integer | `0` / `1` | Window ignores fade-out animation |
> **Tip:** For detailed animation configuration, see the [Animations](/docs/visuals/animations) page for available types and settings.
### Terminal & Swallowing
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `isterm` | integer | `0` / `1` | A new GUI window will replace the isterm window when it is opened |
| `noswallow` | integer | `0` / `1` | The window will not replace the isterm window |
### Global & Special Windows
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `globalkeybinding` | string | `[mod combination][-][key]` | Global keybinding (only works for Wayland apps) |
| `isunglobal` | integer | `0` / `1` | Open as unmanaged global window (for desktop pets or camera windows) |
| `isnamedscratchpad` | integer | `0` / `1` | 0: disable, 1: named scratchpad |
> **Tip:** For scratchpad usage, see the [Scratchpad](/docs/window-management/scratchpad) page for detailed configuration examples.
### Performance & Tearing
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `force_tearing` | integer | `0` / `1` | Set window to tearing state, refer to [Tearing](/docs/configuration/monitors#tearing-game-mode) |
### Examples
```ini
# Set specific window size and position
windowrule=width:1000,height:900,appid:yesplaymusic,title:Demons
# Global keybindings for OBS Studio
windowrule=globalkeybinding:ctrl+alt-o,appid:com.obsproject.Studio
windowrule=globalkeybinding:ctrl+alt+n,appid:com.obsproject.Studio
windowrule=isopensilent:1,appid:com.obsproject.Studio
# Force tearing for games
windowrule=force_tearing:1,title:vkcube
windowrule=force_tearing:1,title:Counter-Strike 2
# Named scratchpad for file manager
windowrule=isnamedscratchpad:1,width:1280,height:800,appid:st-yazi
# Custom opacity for specific apps
windowrule=focused_opacity:0.8,appid:firefox
windowrule=unfocused_opacity:0.6,appid:foot
# Disable blur for selection tools
windowrule=noblur:1,appid:slurp
# Position windows relative to screen center
windowrule=offsetx:20,offsety:-30,width:800,height:600,appid:alacritty
# Send to specific tag and monitor
windowrule=tags:9,monitor:HDMI-A-1,appid:discord
# Terminal swallowing setup
windowrule=isterm:1,appid:st
windowrule=noswallow:1,appid:foot
# Disable client-side decorations
windowrule=allow_csd:1,appid:firefox
# Unmanaged global window (desktop pets, camera)
windowrule=isunglobal:1,appid:cheese
# Named scratchpad toggle
bind=alt,h,toggle_named_scratchpad,st-yazi,none,st -c st-yazi -e yazi
```
---
## Tag Rules
You can set all parameters in one line. If only `id` is set, the rule is followed when the id matches. If any of `monitor_name`, `monitor_make`, `monitor_model`, or `monitor_serial` are set, the rule is followed only if **all** of the set monitor fields match.
> **Warning:** Layouts set in tag rules have a higher priority than monitor rule layouts.
**Format:**
```ini
tagrule=id:Values,Parameter:Values,Parameter:Values
tagrule=id:Values,monitor_name:eDP-1,Parameter:Values,Parameter:Values
tagrule=id:Values,monitor_make:xxx,monitor_model:xxx,Parameter:Values
```
> **Tip:** See [Layouts](/docs/window-management/layouts#supported-layouts) for detailed descriptions of each layout type.
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `id` | integer | 0-9 | Match by tag id, 0 means the ~0 tag |
| `monitor_name` | string | monitor name | Match by monitor name |
| `monitor_make` | string | monitor make | Match by monitor manufacturer |
| `monitor_model` | string | monitor model | Match by monitor model |
| `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 |
### Examples
```ini
# Set layout for specific tags
tagrule=id:1,layout_name:scroller
tagrule=id:2,layout_name:scroller
# Limit to specific monitor
tagrule=id:1,monitor_name:eDP-1,layout_name:scroller
tagrule=id:2,monitor_name:eDP-1,layout_name:scroller
# Persistent tags (1-4) with layout assignment
tagrule=id:1,no_hide:1,layout_name:scroller
tagrule=id:2,no_hide:1,layout_name:scroller
tagrule=id:3,monitor_name:eDP-1,no_hide:1,layout_name:scroller
tagrule=id:4,monitor_name:eDP-1,no_hide:1,layout_name:scroller
# Advanced tag configuration with master layout settings
tagrule=id:5,layout_name:tile,nmaster:2,mfact:0.6
tagrule=id:6,monitor_name:HDMI-A-1,layout_name:monocle,no_render_border:1
```
> **Tip:** For Waybar configuration with persistent tags, see [Status Bar](/docs/visuals/status-bar) documentation.
---
## Layer Rules
You can set all parameters in one line. Target "layer shell" surfaces like status bars (`waybar`), launchers (`rofi`), or notification daemons.
**Format:**
```ini
layerrule=layer_name:Values,Parameter:Values,Parameter:Values
```
> **Tip:** You can use `mmsg -e` to get the last open layer name for debugging.
| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `layer_name` | string | layer name | Match name of layer, supports regex |
| `animation_type_open` | string | slide, zoom, fade, none | Set open animation |
| `animation_type_close` | string | slide, zoom, fade, none | Set close animation |
| `noblur` | integer | `0` / `1` | Disable blur |
| `noanim` | integer | `0` / `1` | Disable layer animation |
| `noshadow` | integer | `0` / `1` | Disable layer shadow |
> **Tip:** For animation types, see [Animations](/docs/visuals/animations#animation-types). For visual effects, see [Window Effects](/docs/visuals/effects).
### Examples
```ini
# No blur or animation for slurp selection layer (avoids occlusion and ghosting in screenshots)
layerrule=noanim:1,noblur:1,layer_name:selection
# Zoom animation for Rofi with multiple parameters
layerrule=animation_type_open:zoom,noanim:0,layer_name:rofi
# Disable animations and shadows for notification daemon
layerrule=noanim:1,noshadow:1,layer_name:swaync
# Multiple effects for launcher
layerrule=animation_type_open:slide,animation_type_close:fade,noblur:1,layer_name:wofi
```

View file

@ -0,0 +1,73 @@
---
title: Scratchpad
description: Manage hidden "scratchpad" windows for quick access.
---
mangowm supports two types of scratchpads: the standard pool (Sway-like) and named scratchpads.
## Standard Scratchpad
Any window can be sent to the "scratchpad" pile, which hides it. You can then cycle through them.
**Keybindings:**
```ini
# Send current window to scratchpad
bind=SUPER,i,minimized
# Toggle (show/hide) the scratchpad
bind=ALT,z,toggle_scratchpad
# Retrieve window from scratchpad (restore)
bind=SUPER+SHIFT,i,restore_minimized
```
---
## Named Scratchpad
Named scratchpads are bound to specific keys and applications. When triggered, mangowm will either launch the app (if not running) or toggle its visibility.
**1. Define the Window Rule**
You must identify the app using a unique `appid` or `title` and mark it as a named scratchpad. The application must support setting a custom appid or title at launch. Common examples:
- `st -c my-appid` — sets the appid
- `kitty -T my-title` — sets the window title
- `foot --app-id my-appid` — sets the appid
Use `none` as a placeholder when you only want to match by one field.
```ini
# Match by appid
windowrule=isnamedscratchpad:1,width:1280,height:800,appid:st-yazi
# Match by title
windowrule=isnamedscratchpad:1,width:1000,height:700,title:kitty-scratch
```
**2. Bind the Toggle Key**
Format: `bind=MOD,KEY,toggle_named_scratchpad,appid,title,command`
Use `none` for whichever field you are not matching on.
```ini
# Match by appid: launch 'st' with class 'st-yazi' running 'yazi'
bind=alt,h,toggle_named_scratchpad,st-yazi,none,st -c st-yazi -e yazi
# Match by title: launch 'kitty' with window title 'kitty-scratch'
bind=alt,k,toggle_named_scratchpad,none,kitty-scratch,kitty -T kitty-scratch
```
---
## Appearance
You can customize the size of scratchpad windows relative to the screen.
```ini
scratchpad_width_ratio=0.8
scratchpad_height_ratio=0.9
scratchpadcolor=0x516c93ff
```

View file

@ -1,4 +1,4 @@
(define-module (mangowc) (define-module (mangowm)
#:use-module (guix download) #:use-module (guix download)
#:use-module (guix git-download) #:use-module (guix git-download)
#:use-module (guix gexp) #:use-module (guix gexp)
@ -18,11 +18,11 @@
#:use-module (guix licenses)) #:use-module (guix licenses))
(define-public mangowc-git (define-public mangowm-git
(package (package
(name "mangowc") (name "mangowm")
(version "git") (version "git")
(source (local-file "." "mangowc-checkout" (source (local-file "." "mangowm-checkout"
#:recursive? #t #:recursive? #t
#:select? (or (git-predicate (current-source-directory)) #:select? (or (git-predicate (current-source-directory))
(const #t)))) (const #t))))
@ -55,10 +55,13 @@
wlroots wlroots
scenefx)) scenefx))
(native-inputs (list pkg-config wayland-protocols)) (native-inputs (list pkg-config wayland-protocols))
(home-page "https://github.com/DreamMaoMao/mangowc") (home-page "https://github.com/DreamMaoMao/mangowm")
(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 "A Wayland compositor based on wlroots and scenefx,
inspired by dwl but aiming to be more feature-rich.") inspired by dwl but aiming to be more feature-rich.")
(license gpl3))) (license gpl3)))
mangowc-git (define-deprecated-package mangowc
mangowm-git)
mangowm-git

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'], project('mango', ['c', 'cpp'],
version : '0.10.9', version : '0.12.7',
) )
subdir('protocols') subdir('protocols')
@ -56,7 +56,7 @@ endif
if is_git_repo if is_git_repo
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag # 如果是 Git 目录,获取 Commit Hash 和最新的 tag
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip() commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
latest_tag = run_command(git, 'describe', '--tags', '--abbrev=0', check : false).stdout().strip() latest_tag = meson.project_version()
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash) version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
else else
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串 # 如果不是 Git 目录,使用项目版本号和 "release" 字符串
@ -147,5 +147,7 @@ executable('mmsg',
) )
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions') desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
install_data('mango.desktop', install_dir : desktop_install_dir) portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal')
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango')) install_data('assets/mango.desktop', install_dir : desktop_install_dir)
install_data('assets/mango-portals.conf', install_dir : portal_install_dir)
install_data('assets/config.conf', install_dir : join_paths(sysconfdir, 'mango'))

View file

@ -8,13 +8,13 @@
extern char *argv0; extern char *argv0;
/* use main(int argc, char *argv[]) */ /* use main(int32_t argc, char *argv[]) */
#define ARGBEGIN \ #define ARGBEGIN \
for (argv0 = *argv, argv++, argc--; \ for (argv0 = *argv, argv++, argc--; \
argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \ argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
char argc_; \ char argc_; \
char **argv_; \ char **argv_; \
int brk_; \ int32_t brk_; \
if (argv[0][1] == '-' && argv[0][2] == '\0') { \ if (argv[0][1] == '-' && argv[0][2] == '\0') { \
argv++; \ argv++; \
argc--; \ argc--; \

View file

@ -25,31 +25,31 @@ static enum {
WATCH = 1 << 2 | GET, WATCH = 1 << 2 | GET,
} mode = NONE; } mode = NONE;
static int Oflag; static int32_t Oflag;
static int Tflag; static int32_t Tflag;
static int Lflag; static int32_t Lflag;
static int oflag; static int32_t oflag;
static int tflag; static int32_t tflag;
static int lflag; static int32_t lflag;
static int cflag; static int32_t cflag;
static int vflag; static int32_t vflag;
static int mflag; static int32_t mflag;
static int fflag; static int32_t fflag;
static int qflag; static int32_t qflag;
static int dflag; static int32_t dflag;
static int xflag; static int32_t xflag;
static int eflag; static int32_t eflag;
static int kflag; static int32_t kflag;
static int bflag; static int32_t bflag;
static int Aflag; static int32_t Aflag;
static uint32_t occ, seltags, total_clients, urg; static uint32_t occ, seltags, total_clients, urg;
static char *output_name; static char *output_name;
static int tagcount; static int32_t tagcount;
static char *tagset; static char *tagset;
static char *layout_name; static char *layout_name;
static int layoutcount, layout_idx; static int32_t layoutcount, layout_idx;
static char *client_tags; static char *client_tags;
static char *dispatch_cmd; static char *dispatch_cmd;
static char *dispatch_arg1; static char *dispatch_arg1;
@ -87,7 +87,7 @@ static void noop_description(void *data, struct wl_output *wl_output,
// 将 n 转换为 9 位二进制字符串,结果存入 buf至少长度 10 // 将 n 转换为 9 位二进制字符串,结果存入 buf至少长度 10
void bin_str_9bits(char *buf, uint32_t n) { void bin_str_9bits(char *buf, uint32_t n) {
for (int i = 8; i >= 0; i--) { for (int32_t i = 8; i >= 0; i--) {
*buf++ = ((n >> i) & 1) ? '1' : '0'; *buf++ = ((n >> i) & 1) ? '1' : '0';
} }
*buf = '\0'; // 字符串结尾 *buf = '\0'; // 字符串结尾
@ -324,7 +324,7 @@ static void dwl_ipc_output_frame(void *data,
if (tflag) { if (tflag) {
uint32_t mask = seltags; uint32_t mask = seltags;
char *t = tagset; char *t = tagset;
int i = 0; int32_t i = 0;
for (; *t && *t >= '0' && *t <= '9'; t++) for (; *t && *t >= '0' && *t <= '9'; t++)
i = *t - '0' + i * 10; i = *t - '0' + i * 10;
@ -354,7 +354,7 @@ static void dwl_ipc_output_frame(void *data,
if (cflag) { if (cflag) {
uint32_t and = ~0, xor = 0; uint32_t and = ~0, xor = 0;
char *t = client_tags; char *t = client_tags;
int i = 0; int32_t i = 0;
for (; *t && *t >= '0' && *t <= '9'; t++) for (; *t && *t >= '0' && *t <= '9'; t++)
i = *t - '0' + i * 10; i = *t - '0' + i * 10;
@ -500,16 +500,52 @@ static const struct wl_registry_listener registry_listener = {
static void usage(void) { static void usage(void) {
fprintf(stderr, fprintf(stderr,
"usage:" "mmsg - MangoWM IPC\n"
"\t%s [-OTLq]\n" "\n"
"\t%s [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d " "SYNOPSIS:\n"
"\tmmsg [-OTLq]\n"
"\tmmsg [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d "
"<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n" "<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n"
"\t%s [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n", "\tmmsg [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n"
argv0, argv0, argv0); "\n"
"OPERATION MODES:\n"
"\t-g Get values (tags, layout, focused client)\n"
"\t-s Set values (switch tags, layouts)\n"
"\t-w Watch mode (stream events)\n"
"\n"
"GENERAL OPTIONS:\n"
"\t-O Get all output (monitor) information\n"
"\t-T Get number of tags\n"
"\t-L Get all available layouts\n"
"\t-q Quit mango\n"
"\t-o <output> Select output (monitor)\n"
"\n"
"GET OPTIONS (used with -g or -w):\n"
"\t-O Get output name\n"
"\t-o Get output (monitor) focus information\n"
"\t-t Get selected tags\n"
"\t-l Get current layout\n"
"\t-c Get title and appid of focused clients\n"
"\t-v Get visibility of statusbar\n"
"\t-m Get fullscreen status\n"
"\t-f Get floating status\n"
"\t-x Get focused client geometry\n"
"\t-e Get name of last focused layer\n"
"\t-k Get current keyboard layout\n"
"\t-b Get current keybind mode\n"
"\t-A Get scale factor of monitor\n"
"\n"
"SET OPTIONS (used with -s):\n"
"\t-o <output> Select output (monitor)\n"
"\t-t <tags> Set selected tags (can be used with [+-^.] "
"modifiers)\n"
"\t-l <layout> Set current layout\n"
"\t-c <tags> Get title and appid of focused client\n"
"\t-d <cmd>,<args...> Dispatch internal command (max 5 args)\n");
exit(2); exit(2);
} }
int main(int argc, char *argv[]) { int32_t main(int32_t argc, char *argv[]) {
ARGBEGIN { ARGBEGIN {
case 'q': case 'q':
qflag = 1; qflag = 1;

View file

@ -11,7 +11,7 @@
wayland, wayland,
wayland-protocols, wayland-protocols,
wayland-scanner, wayland-scanner,
xcbutilwm, libxcb-wm,
xwayland, xwayland,
meson, meson,
ninja, ninja,
@ -57,7 +57,7 @@ stdenv.mkDerivation {
] ]
++ lib.optionals enableXWayland [ ++ lib.optionals enableXWayland [
libX11 libX11
xcbutilwm libxcb-wm
xwayland xwayland
]; ];
@ -67,8 +67,8 @@ stdenv.mkDerivation {
meta = { meta = {
mainProgram = "mango"; mainProgram = "mango";
description = "A streamlined but feature-rich Wayland compositor"; description = "Practical and Powerful wayland compositor (dwm but wayland)";
homepage = "https://github.com/DreamMaoMao/mango"; homepage = "https://github.com/mangowm/mango";
license = lib.licenses.gpl3Plus; license = lib.licenses.gpl3Plus;
maintainers = []; maintainers = [];
platforms = lib.platforms.unix; platforms = lib.platforms.unix;

View file

@ -1,6 +1,6 @@
wayland_scanner = find_program('wayland-scanner') wayland_scanner = find_program('wayland-scanner')
wayland_protos_dep = dependency('wayland-protocols') wayland_protos_dep = dependency('wayland-protocols')
wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir') wl_protocol_dir = wayland_protos_dep.get_variable(pkgconfig:'pkgdatadir')
wayland_scanner_code = generator( wayland_scanner_code = generator(
wayland_scanner, wayland_scanner,
output: '@BASENAME@-protocol.c', output: '@BASENAME@-protocol.c',

View file

@ -1,29 +1,30 @@
void client_actual_size(Client *c, uint32_t *width, uint32_t *height) { void client_actual_size(Client *c, int32_t *width, int32_t *height) {
*width = c->animation.current.width - 2 * c->bw; *width = c->animation.current.width - 2 * (int32_t)c->bw;
*height = c->animation.current.height - 2 * c->bw; *height = c->animation.current.height - 2 * (int32_t)c->bw;
} }
void set_rect_size(struct wlr_scene_rect *rect, int width, int height) { void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) {
wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height)); wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height));
} }
enum corner_location set_client_corner_location(Client *c) { enum corner_location set_client_corner_location(Client *c) {
enum corner_location current_corner_location = CORNER_LOCATION_ALL; enum corner_location current_corner_location = CORNER_LOCATION_ALL;
struct wlr_box target_geom = animations ? c->animation.current : c->geom; struct wlr_box target_geom =
if (target_geom.x + border_radius <= c->mon->m.x) { config.animations ? c->animation.current : c->geom;
current_corner_location &= ~CORNER_LOCATION_LEFT; // 清除左标志位 if (target_geom.x + config.border_radius <= c->mon->m.x) {
current_corner_location &= ~CORNER_LOCATION_LEFT;
} }
if (target_geom.x + target_geom.width - border_radius >= if (target_geom.x + target_geom.width - config.border_radius >=
c->mon->m.x + c->mon->m.width) { c->mon->m.x + c->mon->m.width) {
current_corner_location &= ~CORNER_LOCATION_RIGHT; // 清除右标志位 current_corner_location &= ~CORNER_LOCATION_RIGHT;
} }
if (target_geom.y + border_radius <= c->mon->m.y) { if (target_geom.y + config.border_radius <= c->mon->m.y) {
current_corner_location &= ~CORNER_LOCATION_TOP; // 清除上标志位 current_corner_location &= ~CORNER_LOCATION_TOP;
} }
if (target_geom.y + target_geom.height - border_radius >= if (target_geom.y + target_geom.height - config.border_radius >=
c->mon->m.y + c->mon->m.height) { c->mon->m.y + c->mon->m.height) {
current_corner_location &= ~CORNER_LOCATION_BOTTOM; // 清除下标志位 current_corner_location &= ~CORNER_LOCATION_BOTTOM;
} }
return current_corner_location; return current_corner_location;
} }
@ -47,22 +48,23 @@ bool is_horizontal_right_stack_layout(Monitor *m) {
return false; return false;
} }
int is_special_animaiton_rule(Client *c) { int32_t is_special_animation_rule(Client *c) {
if (is_scroller_layout(c->mon) && !c->isfloating) { if (is_scroller_layout(c->mon) && !c->isfloating) {
return DOWN; return DOWN;
} else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) { } else if (c->mon->visible_tiling_clients == 1 && !c->isfloating) {
return DOWN; return DOWN;
} else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating &&
!new_is_master && is_horizontal_stack_layout(c->mon)) { !config.new_is_master && is_horizontal_stack_layout(c->mon)) {
return RIGHT; return RIGHT;
} else if (!c->isfloating && new_is_master && } else if (!c->isfloating && config.new_is_master &&
is_horizontal_stack_layout(c->mon)) { is_horizontal_stack_layout(c->mon)) {
return LEFT; return LEFT;
} else if (c->mon->visible_tiling_clients == 2 && !c->isfloating && } else if (c->mon->visible_tiling_clients == 2 && !c->isfloating &&
!new_is_master && is_horizontal_right_stack_layout(c->mon)) { !config.new_is_master &&
is_horizontal_right_stack_layout(c->mon)) {
return LEFT; return LEFT;
} else if (!c->isfloating && new_is_master && } else if (!c->isfloating && config.new_is_master &&
is_horizontal_right_stack_layout(c->mon)) { is_horizontal_right_stack_layout(c->mon)) {
return RIGHT; return RIGHT;
} else { } else {
@ -70,14 +72,15 @@ int is_special_animaiton_rule(Client *c) {
} }
} }
void set_client_open_animaiton(Client *c, struct wlr_box geo) { void set_client_open_animation(Client *c, struct wlr_box geo) {
int slide_direction; int32_t slide_direction;
int horizontal, horizontal_value; int32_t horizontal, horizontal_value;
int vertical, vertical_value; int32_t vertical, vertical_value;
int special_direction; int32_t special_direction;
int center_x, center_y; int32_t center_x, center_y;
if ((!c->animation_type_open && strcmp(animation_type_open, "fade") == 0) || if ((!c->animation_type_open &&
strcmp(config.animation_type_open, "fade") == 0) ||
(c->animation_type_open && (c->animation_type_open &&
strcmp(c->animation_type_open, "fade") == 0)) { strcmp(c->animation_type_open, "fade") == 0)) {
c->animainit_geom.width = geo.width; c->animainit_geom.width = geo.width;
@ -86,17 +89,17 @@ void set_client_open_animaiton(Client *c, struct wlr_box geo) {
c->animainit_geom.y = geo.y; c->animainit_geom.y = geo.y;
return; return;
} else if ((!c->animation_type_open && } else if ((!c->animation_type_open &&
strcmp(animation_type_open, "zoom") == 0) || strcmp(config.animation_type_open, "zoom") == 0) ||
(c->animation_type_open && (c->animation_type_open &&
strcmp(c->animation_type_open, "zoom") == 0)) { strcmp(c->animation_type_open, "zoom") == 0)) {
c->animainit_geom.width = geo.width * zoom_initial_ratio; c->animainit_geom.width = geo.width * config.zoom_initial_ratio;
c->animainit_geom.height = geo.height * zoom_initial_ratio; c->animainit_geom.height = geo.height * config.zoom_initial_ratio;
c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width) / 2; c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width) / 2;
c->animainit_geom.y = c->animainit_geom.y =
geo.y + (geo.height - c->animainit_geom.height) / 2; geo.y + (geo.height - c->animainit_geom.height) / 2;
return; return;
} else { } else {
special_direction = is_special_animaiton_rule(c); special_direction = is_special_animation_rule(c);
center_x = c->geom.x + c->geom.width / 2; center_x = c->geom.x + c->geom.width / 2;
center_y = c->geom.y + c->geom.height / 2; center_y = c->geom.y + c->geom.height / 2;
if (special_direction == UNDIR) { if (special_direction == UNDIR) {
@ -147,15 +150,15 @@ void set_client_open_animaiton(Client *c, struct wlr_box geo) {
} }
} }
void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, void snap_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
int sy, void *data) { int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data; BufferData *buffer_data = (BufferData *)data;
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
buffer_data->height); buffer_data->height);
} }
void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy, void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
void *data) { int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data; BufferData *buffer_data = (BufferData *)data;
if (buffer_data->should_scale && buffer_data->height_scale < 1 && if (buffer_data->should_scale && buffer_data->height_scale < 1 &&
@ -183,8 +186,8 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy,
if (buffer_data->should_scale) { if (buffer_data->should_scale) {
uint32_t surface_width = surface->current.width; int32_t surface_width = surface->current.width;
uint32_t surface_height = surface->current.height; int32_t surface_height = surface->current.height;
surface_width = buffer_data->width_scale < 1 surface_width = buffer_data->width_scale < 1
? surface_width ? surface_width
@ -223,7 +226,7 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy,
if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL) if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL)
return; return;
wlr_scene_buffer_set_corner_radius(buffer, border_radius, wlr_scene_buffer_set_corner_radius(buffer, config.border_radius,
buffer_data->corner_location); buffer_data->corner_location);
} }
@ -241,7 +244,7 @@ void buffer_set_effect(Client *c, BufferData data) {
data.should_scale = false; data.should_scale = false;
if (c->isnoradius || c->isfullscreen || if (c->isnoradius || c->isfullscreen ||
(no_radius_when_single && c->mon && (config.no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1)) { c->mon->visible_tiling_clients == 1)) {
data.corner_location = CORNER_LOCATION_NONE; data.corner_location = CORNER_LOCATION_NONE;
} }
@ -255,7 +258,7 @@ void client_draw_shadow(Client *c) {
if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow) if (c->iskilling || !client_surface(c)->mapped || c->isnoshadow)
return; return;
if (!shadows || (!c->isfloating && shadow_only_floating)) { if (!config.shadows || (!c->isfloating && config.shadow_only_floating)) {
if (c->shadow->node.enabled) if (c->shadow->node.enabled)
wlr_scene_node_set_enabled(&c->shadow->node, false); wlr_scene_node_set_enabled(&c->shadow->node, false);
return; return;
@ -266,43 +269,40 @@ void client_draw_shadow(Client *c) {
bool hit_no_border = check_hit_no_border(c); bool hit_no_border = check_hit_no_border(c);
enum corner_location current_corner_location = enum corner_location current_corner_location =
c->isfullscreen || (no_radius_when_single && c->mon && c->isfullscreen || (config.no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1) c->mon->visible_tiling_clients == 1)
? CORNER_LOCATION_NONE ? CORNER_LOCATION_NONE
: CORNER_LOCATION_ALL; : CORNER_LOCATION_ALL;
int bwoffset = c->bw != 0 && hit_no_border ? (int)c->bw : 0; int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0;
uint32_t width, height; int32_t width, height;
client_actual_size(c, &width, &height); client_actual_size(c, &width, &height);
uint32_t delta = shadows_size + c->bw - bwoffset; int32_t delta = config.shadows_size + (int32_t)c->bw - bwoffset;
/* we calculate where to clip the shadow */
struct wlr_box client_box = { struct wlr_box client_box = {
.x = bwoffset, .x = bwoffset,
.y = bwoffset, .y = bwoffset,
.width = width + (int)c->bw - bwoffset, .width = width + (int32_t)c->bw - bwoffset,
.height = height + (int)c->bw - bwoffset, .height = height + (int32_t)c->bw - bwoffset,
}; };
struct wlr_box shadow_box = { struct wlr_box shadow_box = {
.x = shadows_position_x + bwoffset, .x = config.shadows_position_x + bwoffset,
.y = shadows_position_y + bwoffset, .y = config.shadows_position_y + bwoffset,
.width = width + 2 * delta, .width = width + 2 * delta,
.height = height + 2 * delta, .height = height + 2 * delta,
}; };
struct wlr_box intersection_box; struct wlr_box intersection_box;
wlr_box_intersection(&intersection_box, &client_box, &shadow_box); wlr_box_intersection(&intersection_box, &client_box, &shadow_box);
/* clipped region takes shadow relative coords, so we translate everything intersection_box.x -= config.shadows_position_x + bwoffset;
* by its position */ intersection_box.y -= config.shadows_position_y + bwoffset;
intersection_box.x -= shadows_position_x + bwoffset;
intersection_box.y -= shadows_position_y + bwoffset;
struct clipped_region clipped_region = { struct clipped_region clipped_region = {
.area = intersection_box, .area = intersection_box,
.corner_radius = border_radius, .corner_radius = config.border_radius,
.corners = current_corner_location, .corners = current_corner_location,
}; };
@ -313,7 +313,7 @@ void client_draw_shadow(Client *c) {
.height = shadow_box.height, .height = shadow_box.height,
}; };
int right_offset, bottom_offset, left_offset, top_offset; int32_t right_offset, bottom_offset, left_offset, top_offset;
if (c == grabc) { if (c == grabc) {
right_offset = 0; right_offset = 0;
@ -356,32 +356,31 @@ void apply_border(Client *c) {
bool hit_no_border = check_hit_no_border(c); bool hit_no_border = check_hit_no_border(c);
enum corner_location current_corner_location; enum corner_location current_corner_location;
if (c->isfullscreen || (no_radius_when_single && c->mon && if (c->isfullscreen || (config.no_radius_when_single && c->mon &&
c->mon->visible_tiling_clients == 1)) { c->mon->visible_tiling_clients == 1)) {
current_corner_location = CORNER_LOCATION_NONE; current_corner_location = CORNER_LOCATION_NONE;
} else { } else {
current_corner_location = set_client_corner_location(c); current_corner_location = set_client_corner_location(c);
} }
// Handle no-border cases if (hit_no_border && config.smartgaps) {
if (hit_no_border && smartgaps) {
c->bw = 0; c->bw = 0;
c->fake_no_border = true; c->fake_no_border = true;
} else if (hit_no_border && !smartgaps) { } else if (hit_no_border && !config.smartgaps) {
wlr_scene_rect_set_size(c->border, 0, 0); wlr_scene_rect_set_size(c->border, 0, 0);
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
c->fake_no_border = true; c->fake_no_border = true;
return; return;
} else if (!c->isfullscreen && VISIBLEON(c, c->mon)) { } else if (!c->isfullscreen && VISIBLEON(c, c->mon)) {
c->bw = c->isnoborder ? 0 : borderpx; c->bw = c->isnoborder ? 0 : config.borderpx;
c->fake_no_border = false; c->fake_no_border = false;
} }
struct wlr_box clip_box = c->animation.current; struct wlr_box clip_box = c->animation.current;
// 一但在GEZERO如果使用无符号那么其他数据也会转换为无符号导致没有负数出错 // 一但在GEZERO如果使用无符号那么其他数据也会转换为无符号导致没有负数出错
int bw = (int)c->bw; int32_t bw = (int32_t)c->bw;
int right_offset, bottom_offset, left_offset, top_offset; int32_t right_offset, bottom_offset, left_offset, top_offset;
if (c == grabc) { if (c == grabc) {
right_offset = 0; right_offset = 0;
@ -400,25 +399,27 @@ void apply_border(Client *c) {
top_offset = GEZERO(c->mon->m.y - c->animation.current.y); top_offset = GEZERO(c->mon->m.y - c->animation.current.y);
} }
int inner_surface_width = GEZERO(clip_box.width - 2 * bw); int32_t inner_surface_width = GEZERO(clip_box.width - 2 * bw);
int inner_surface_height = GEZERO(clip_box.height - 2 * bw); int32_t inner_surface_height = GEZERO(clip_box.height - 2 * bw);
int inner_surface_x = GEZERO(bw - left_offset); int32_t inner_surface_x = GEZERO(bw - left_offset);
int inner_surface_y = GEZERO(bw - top_offset); int32_t inner_surface_y = GEZERO(bw - top_offset);
int rect_x = left_offset; int32_t rect_x = left_offset;
int rect_y = top_offset; int32_t rect_y = top_offset;
int rect_width = int32_t rect_width =
GEZERO(c->animation.current.width - left_offset - right_offset); GEZERO(c->animation.current.width - left_offset - right_offset);
int rect_height = int32_t rect_height =
GEZERO(c->animation.current.height - top_offset - bottom_offset); GEZERO(c->animation.current.height - top_offset - bottom_offset);
if (left_offset > c->bw) if (left_offset > c->bw)
inner_surface_width = inner_surface_width - left_offset + c->bw; inner_surface_width =
inner_surface_width - left_offset + (int32_t)c->bw;
if (top_offset > c->bw) if (top_offset > c->bw)
inner_surface_height = inner_surface_height - top_offset + c->bw; inner_surface_height =
inner_surface_height - top_offset + (int32_t)c->bw;
if (right_offset > 0) { if (right_offset > 0) {
inner_surface_width = inner_surface_width =
@ -433,37 +434,37 @@ void apply_border(Client *c) {
struct clipped_region clipped_region = { struct clipped_region clipped_region = {
.area = {inner_surface_x, inner_surface_y, inner_surface_width, .area = {inner_surface_x, inner_surface_y, inner_surface_width,
inner_surface_height}, inner_surface_height},
.corner_radius = border_radius, .corner_radius = config.border_radius,
.corners = current_corner_location, .corners = current_corner_location,
}; };
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
wlr_scene_rect_set_size(c->border, rect_width, rect_height); wlr_scene_rect_set_size(c->border, rect_width, rect_height);
wlr_scene_node_set_position(&c->border->node, rect_x, rect_y); wlr_scene_node_set_position(&c->border->node, rect_x, rect_y);
wlr_scene_rect_set_corner_radius(c->border, border_radius, wlr_scene_rect_set_corner_radius(c->border, config.border_radius,
current_corner_location); current_corner_location);
wlr_scene_rect_set_clipped_region(c->border, clipped_region); wlr_scene_rect_set_clipped_region(c->border, clipped_region);
} }
struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) {
int offsetx = 0, offsety = 0, offsetw = 0, offseth = 0; int32_t offsetx = 0, offsety = 0, offsetw = 0, offseth = 0;
struct ivec2 offset = {0, 0, 0, 0}; struct ivec2 offset = {0, 0, 0, 0};
if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted && if (!ISSCROLLTILED(c) && !c->animation.tagining && !c->animation.tagouted &&
!c->animation.tagouting) !c->animation.tagouting)
return offset; return offset;
int bottom_out_offset = int32_t bottom_out_offset =
GEZERO(c->animation.current.y + c->animation.current.height - GEZERO(c->animation.current.y + c->animation.current.height -
c->mon->m.y - c->mon->m.height); c->mon->m.y - c->mon->m.height);
int right_out_offset = int32_t right_out_offset =
GEZERO(c->animation.current.x + c->animation.current.width - GEZERO(c->animation.current.x + c->animation.current.width -
c->mon->m.x - c->mon->m.width); c->mon->m.x - c->mon->m.width);
int left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x); int32_t left_out_offset = GEZERO(c->mon->m.x - c->animation.current.x);
int top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y); int32_t top_out_offset = GEZERO(c->mon->m.y - c->animation.current.y);
// 必须转换为int否计算会没有负数导致判断错误 // 必须转换为int否计算会没有负数导致判断错误
int bw = (int)c->bw; int32_t bw = (int32_t)c->bw;
/* /*
@ -522,7 +523,7 @@ void client_apply_clip(Client *c, float factor) {
enum corner_location current_corner_location = enum corner_location current_corner_location =
set_client_corner_location(c); set_client_corner_location(c);
if (!animations) { if (!config.animations) {
c->animation.running = false; c->animation.running = false;
c->need_output_flush = false; c->need_output_flush = false;
c->animainit_geom = c->current = c->pending = c->animation.current = c->animainit_geom = c->current = c->pending = c->animation.current =
@ -547,7 +548,7 @@ void client_apply_clip(Client *c, float factor) {
} }
// 获取窗口动画实时位置矩形 // 获取窗口动画实时位置矩形
uint32_t width, height; int32_t width, height;
client_actual_size(c, &width, &height); client_actual_size(c, &width, &height);
// 计算出除了边框的窗口实际剪切大小 // 计算出除了边框的窗口实际剪切大小
@ -590,8 +591,8 @@ void client_apply_clip(Client *c, float factor) {
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
// 获取剪切后的表面的实际大小用于计算缩放 // 获取剪切后的表面的实际大小用于计算缩放
int acutal_surface_width = geometry.width - offset.x - offset.width; int32_t acutal_surface_width = geometry.width - offset.x - offset.width;
int acutal_surface_height = geometry.height - offset.y - offset.height; int32_t acutal_surface_height = geometry.height - offset.y - offset.height;
if (acutal_surface_width <= 0 || acutal_surface_height <= 0) if (acutal_surface_width <= 0 || acutal_surface_height <= 0)
return; return;
@ -623,24 +624,23 @@ void fadeout_client_animation_next_tick(Client *c) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double animation_passed = double animation_passed =
c->animation.duration c->animation.duration
? (double)passed_time / (double)c->animation.duration ? (double)passed_time / (double)c->animation.duration
: 1.0; : 1.0;
int type = c->animation.action = c->animation.action; int32_t type = c->animation.action = c->animation.action;
double factor = find_animation_curve_at(animation_passed, type); double factor = find_animation_curve_at(animation_passed, type);
uint32_t width = c->animation.initial.width + int32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor; (c->current.width - c->animation.initial.width) * factor;
uint32_t height = int32_t height = c->animation.initial.height +
c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor; (c->current.height - c->animation.initial.height) * factor;
uint32_t x = c->animation.initial.x + int32_t x = c->animation.initial.x +
(c->current.x - c->animation.initial.x) * factor; (c->current.x - c->animation.initial.x) * factor;
uint32_t y = c->animation.initial.y + int32_t y = c->animation.initial.y +
(c->current.y - c->animation.initial.y) * factor; (c->current.y - c->animation.initial.y) * factor;
wlr_scene_node_set_position(&c->scene->node, x, y); wlr_scene_node_set_position(&c->scene->node, x, y);
@ -655,19 +655,19 @@ void fadeout_client_animation_next_tick(Client *c) {
double opacity_eased_progress = double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEOUT); find_animation_curve_at(animation_passed, OPAFADEOUT);
double percent = fadeout_begin_opacity - double percent = config.fadeout_begin_opacity -
(opacity_eased_progress * fadeout_begin_opacity); (opacity_eased_progress * config.fadeout_begin_opacity);
double opacity = MAX(percent, 0); double opacity = MAX(percent, 0);
if (animation_fade_out && !c->nofadeout) if (config.animation_fade_out && !c->nofadeout)
wlr_scene_node_for_each_buffer(&c->scene->node, wlr_scene_node_for_each_buffer(&c->scene->node,
scene_buffer_apply_opacity, &opacity); scene_buffer_apply_opacity, &opacity);
if ((c->animation_type_close && if ((c->animation_type_close &&
strcmp(c->animation_type_close, "zoom") == 0) || strcmp(c->animation_type_close, "zoom") == 0) ||
(!c->animation_type_close && (!c->animation_type_close &&
strcmp(animation_type_close, "zoom") == 0)) { strcmp(config.animation_type_close, "zoom") == 0)) {
buffer_data.width = width; buffer_data.width = width;
buffer_data.height = height; buffer_data.height = height;
@ -690,23 +690,22 @@ void client_animation_next_tick(Client *c) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double animation_passed = double animation_passed =
c->animation.duration c->animation.duration
? (double)passed_time / (double)c->animation.duration ? (double)passed_time / (double)c->animation.duration
: 1.0; : 1.0;
int type = c->animation.action == NONE ? MOVE : c->animation.action; int32_t type = c->animation.action == NONE ? MOVE : c->animation.action;
double factor = find_animation_curve_at(animation_passed, type); double factor = find_animation_curve_at(animation_passed, type);
Client *pointer_c = NULL; Client *pointer_c = NULL;
double sx = 0, sy = 0; double sx = 0, sy = 0;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
uint32_t width = c->animation.initial.width + int32_t width = c->animation.initial.width +
(c->current.width - c->animation.initial.width) * factor; (c->current.width - c->animation.initial.width) * factor;
uint32_t height = int32_t height = c->animation.initial.height +
c->animation.initial.height +
(c->current.height - c->animation.initial.height) * factor; (c->current.height - c->animation.initial.height) * factor;
int32_t x = c->animation.initial.x + int32_t x = c->animation.initial.x +
@ -769,79 +768,78 @@ void init_fadeout_client(Client *c) {
if ((c->animation_type_close && if ((c->animation_type_close &&
strcmp(c->animation_type_close, "none") == 0) || strcmp(c->animation_type_close, "none") == 0) ||
(!c->animation_type_close && (!c->animation_type_close &&
strcmp(animation_type_close, "none") == 0)) { strcmp(config.animation_type_close, "none") == 0)) {
return; return;
} }
Client *fadeout_cient = ecalloc(1, sizeof(*fadeout_cient)); Client *fadeout_client = ecalloc(1, sizeof(*fadeout_client));
wlr_scene_node_set_enabled(&c->scene->node, true); wlr_scene_node_set_enabled(&c->scene->node, true);
client_set_border_color(c, bordercolor); client_set_border_color(c, config.bordercolor);
fadeout_cient->scene = fadeout_client->scene =
wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]); wlr_scene_tree_snapshot(&c->scene->node, layers[LyrFadeOut]);
wlr_scene_node_set_enabled(&c->scene->node, false); wlr_scene_node_set_enabled(&c->scene->node, false);
if (!fadeout_cient->scene) { if (!fadeout_client->scene) {
free(fadeout_cient); free(fadeout_client);
return; return;
} }
fadeout_cient->animation.duration = animation_duration_close; fadeout_client->animation.duration = config.animation_duration_close;
fadeout_cient->geom = fadeout_cient->current = fadeout_client->geom = fadeout_client->current =
fadeout_cient->animainit_geom = fadeout_cient->animation.initial = fadeout_client->animainit_geom = fadeout_client->animation.initial =
c->animation.current; c->animation.current;
fadeout_cient->mon = c->mon; fadeout_client->mon = c->mon;
fadeout_cient->animation_type_close = c->animation_type_close; fadeout_client->animation_type_close = c->animation_type_close;
fadeout_cient->animation.action = CLOSE; fadeout_client->animation.action = CLOSE;
fadeout_cient->bw = c->bw; fadeout_client->bw = c->bw;
fadeout_cient->nofadeout = c->nofadeout; fadeout_client->nofadeout = c->nofadeout;
// 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标 // 这里snap节点的坐标设置是使用的相对坐标所以不能加上原来坐标
// 这跟普通node有区别 // 这跟普通node有区别
fadeout_cient->animation.initial.x = 0; fadeout_client->animation.initial.x = 0;
fadeout_cient->animation.initial.y = 0; fadeout_client->animation.initial.y = 0;
if ((!c->animation_type_close && if ((!c->animation_type_close &&
strcmp(animation_type_close, "fade") == 0) || strcmp(config.animation_type_close, "fade") == 0) ||
(c->animation_type_close && (c->animation_type_close &&
strcmp(c->animation_type_close, "fade") == 0)) { strcmp(c->animation_type_close, "fade") == 0)) {
fadeout_cient->current.x = 0; fadeout_client->current.x = 0;
fadeout_cient->current.y = 0; fadeout_client->current.y = 0;
fadeout_cient->current.width = 0; fadeout_client->current.width = 0;
fadeout_cient->current.height = 0; fadeout_client->current.height = 0;
} else if ((c->animation_type_close && } else if ((c->animation_type_close &&
strcmp(c->animation_type_close, "slide") == 0) || strcmp(c->animation_type_close, "slide") == 0) ||
(!c->animation_type_close && (!c->animation_type_close &&
strcmp(animation_type_close, "slide") == 0)) { strcmp(config.animation_type_close, "slide") == 0)) {
fadeout_cient->current.y = fadeout_client->current.y =
c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2 c->geom.y + c->geom.height / 2 > c->mon->m.y + c->mon->m.height / 2
? c->mon->m.height - ? c->mon->m.height -
(c->animation.current.y - c->mon->m.y) // down out (c->animation.current.y - c->mon->m.y) // down out
: c->mon->m.y - c->geom.height; // up out : c->mon->m.y - c->geom.height; // up out
fadeout_cient->current.x = 0; // x无偏差垂直划出 fadeout_client->current.x = 0; // x无偏差垂直划出
} else { } else {
fadeout_cient->current.y = fadeout_client->current.y =
(fadeout_cient->geom.height - (fadeout_client->geom.height -
fadeout_cient->geom.height * zoom_end_ratio) / fadeout_client->geom.height * config.zoom_end_ratio) /
2; 2;
fadeout_cient->current.x = fadeout_client->current.x =
(fadeout_cient->geom.width - (fadeout_client->geom.width -
fadeout_cient->geom.width * zoom_end_ratio) / fadeout_client->geom.width * config.zoom_end_ratio) /
2; 2;
fadeout_cient->current.width = fadeout_client->current.width =
fadeout_cient->geom.width * zoom_end_ratio; fadeout_client->geom.width * config.zoom_end_ratio;
fadeout_cient->current.height = fadeout_client->current.height =
fadeout_cient->geom.height * zoom_end_ratio; fadeout_client->geom.height * config.zoom_end_ratio;
} }
fadeout_cient->animation.time_started = get_now_in_ms(); fadeout_client->animation.time_started = get_now_in_ms();
wlr_scene_node_set_enabled(&fadeout_cient->scene->node, true); wlr_scene_node_set_enabled(&fadeout_client->scene->node, true);
wl_list_insert(&fadeout_clients, &fadeout_cient->fadeout_link); wl_list_insert(&fadeout_clients, &fadeout_client->fadeout_link);
// 请求刷新屏幕 // 请求刷新屏幕
if (c->mon) request_fresh_all_monitors();
wlr_output_schedule_frame(c->mon->wlr_output);
} }
void client_commit(Client *c) { void client_commit(Client *c) {
@ -860,8 +858,7 @@ void client_commit(Client *c) {
c->animation.should_animate = false; c->animation.should_animate = false;
} }
// 请求刷新屏幕 // 请求刷新屏幕
if (c->mon) request_fresh_all_monitors();
wlr_output_schedule_frame(c->mon->wlr_output);
} }
void client_set_pending_state(Client *c) { void client_set_pending_state(Client *c) {
@ -869,12 +866,11 @@ void client_set_pending_state(Client *c) {
if (!c || c->iskilling) if (!c || c->iskilling)
return; return;
// 判断是否需要动画 if (!config.animations) {
if (!animations) {
c->animation.should_animate = false; c->animation.should_animate = false;
} else if (animations && c->animation.tagining) { } else if (config.animations && c->animation.tagining) {
c->animation.should_animate = true; c->animation.should_animate = true;
} else if (!animations || c == grabc || } else if (!config.animations || c == grabc ||
(!c->is_pending_open_animation && (!c->is_pending_open_animation &&
wlr_box_equal(&c->current, &c->pending))) { wlr_box_equal(&c->current, &c->pending))) {
c->animation.should_animate = false; c->animation.should_animate = false;
@ -885,7 +881,7 @@ void client_set_pending_state(Client *c) {
if (((c->animation_type_open && if (((c->animation_type_open &&
strcmp(c->animation_type_open, "none") == 0) || strcmp(c->animation_type_open, "none") == 0) ||
(!c->animation_type_open && (!c->animation_type_open &&
strcmp(animation_type_open, "none") == 0)) && strcmp(config.animation_type_open, "none") == 0)) &&
c->animation.action == OPEN) { c->animation.action == OPEN) {
c->animation.duration = 0; c->animation.duration = 0;
} }
@ -910,7 +906,7 @@ void client_set_pending_state(Client *c) {
c->dirty = true; c->dirty = true;
} }
void resize(Client *c, struct wlr_box geo, int interact) { void resize(Client *c, struct wlr_box geo, int32_t interact) {
// 动画设置的起始函数,这里用来计算一些动画的起始值 // 动画设置的起始函数,这里用来计算一些动画的起始值
// 动画起始位置大小是由于c->animainit_geom确定的 // 动画起始位置大小是由于c->animainit_geom确定的
@ -932,8 +928,8 @@ void resize(Client *c, struct wlr_box geo, int interact) {
if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) { if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) {
c->geom = geo; c->geom = geo;
c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width);
c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height);
} else { // 这里会限制不允许窗口划出屏幕 } else { // 这里会限制不允许窗口划出屏幕
c->geom = geo; c->geom = geo;
applybounds( applybounds(
@ -954,16 +950,16 @@ void resize(Client *c, struct wlr_box geo, int interact) {
!c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) { !c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) {
c->animation.action = c->animation.action; c->animation.action = c->animation.action;
} else if (c->animation.tagouting) { } else if (c->animation.tagouting) {
c->animation.duration = animation_duration_tag; c->animation.duration = config.animation_duration_tag;
c->animation.action = TAG; c->animation.action = TAG;
} else if (c->animation.tagining) { } else if (c->animation.tagining) {
c->animation.duration = animation_duration_tag; c->animation.duration = config.animation_duration_tag;
c->animation.action = TAG; c->animation.action = TAG;
} else if (c->is_pending_open_animation) { } else if (c->is_pending_open_animation) {
c->animation.duration = animation_duration_open; c->animation.duration = config.animation_duration_open;
c->animation.action = OPEN; c->animation.action = OPEN;
} else { } else {
c->animation.duration = animation_duration_move; c->animation.duration = config.animation_duration_move;
c->animation.action = MOVE; c->animation.action = MOVE;
} }
@ -974,7 +970,7 @@ void resize(Client *c, struct wlr_box geo, int interact) {
c->animainit_geom.height = c->animation.current.height; c->animainit_geom.height = c->animation.current.height;
c->animainit_geom.width = c->animation.current.width; c->animainit_geom.width = c->animation.current.width;
} else if (c->is_pending_open_animation) { } else if (c->is_pending_open_animation) {
set_client_open_animaiton(c, c->geom); set_client_open_animation(c, c->geom);
} else { } else {
c->animainit_geom = c->animation.current; c->animainit_geom = c->animation.current;
} }
@ -983,10 +979,20 @@ void resize(Client *c, struct wlr_box geo, int interact) {
c->bw = 0; c->bw = 0;
} }
bool hit_no_border = check_hit_no_border(c);
if (hit_no_border && config.smartgaps) {
c->bw = 0;
c->fake_no_border = true;
}
// c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局 // c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局
c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw, c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw); c->geom.height - 2 * c->bw);
if (c->configure_serial != 0) {
c->mon->resizing_count_pending++;
}
if (c == grabc) { if (c == grabc) {
c->animation.running = false; c->animation.running = false;
c->need_output_flush = false; c->need_output_flush = false;
@ -1041,63 +1047,43 @@ bool client_draw_fadeout_frame(Client *c) {
void client_set_focused_opacity_animation(Client *c) { void client_set_focused_opacity_animation(Client *c) {
float *border_color = get_border_color(c); float *border_color = get_border_color(c);
if (!animations) { if (!config.animations) {
setborder_color(c); setborder_color(c);
return; return;
} }
c->opacity_animation.duration = animation_duration_focus; c->opacity_animation.duration = config.animation_duration_focus;
memcpy(c->opacity_animation.target_border_color, border_color, memcpy(c->opacity_animation.target_border_color, border_color,
sizeof(c->opacity_animation.target_border_color)); sizeof(c->opacity_animation.target_border_color));
c->opacity_animation.target_opacity = c->focused_opacity; c->opacity_animation.target_opacity = c->focused_opacity;
c->opacity_animation.time_started = get_now_in_ms(); c->opacity_animation.time_started = get_now_in_ms();
if (c->opacity_animation.running) {
memcpy(c->opacity_animation.initial_border_color, memcpy(c->opacity_animation.initial_border_color,
c->opacity_animation.current_border_color, c->opacity_animation.current_border_color,
sizeof(c->opacity_animation.initial_border_color)); sizeof(c->opacity_animation.initial_border_color));
c->opacity_animation.initial_opacity = c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
c->opacity_animation.current_opacity;
} else {
memcpy(c->opacity_animation.initial_border_color, border_color,
sizeof(c->opacity_animation.initial_border_color));
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.initial_opacity = c->unfocused_opacity;
c->opacity_animation.current_opacity = c->unfocused_opacity;
}
c->opacity_animation.running = true; c->opacity_animation.running = true;
} }
void client_set_unfocused_opacity_animation(Client *c) { void client_set_unfocused_opacity_animation(Client *c) {
// Start border color animation to unfocused
float *border_color = get_border_color(c); float *border_color = get_border_color(c);
if (!animations) { if (!config.animations) {
setborder_color(c); setborder_color(c);
return; return;
} }
c->opacity_animation.duration = animation_duration_focus; c->opacity_animation.duration = config.animation_duration_focus;
memcpy(c->opacity_animation.target_border_color, border_color, memcpy(c->opacity_animation.target_border_color, border_color,
sizeof(c->opacity_animation.target_border_color)); sizeof(c->opacity_animation.target_border_color));
// Start opacity animation to unfocused // Start opacity animation to unfocused
c->opacity_animation.target_opacity = c->unfocused_opacity; c->opacity_animation.target_opacity = c->unfocused_opacity;
c->opacity_animation.time_started = get_now_in_ms(); c->opacity_animation.time_started = get_now_in_ms();
if (c->opacity_animation.running) {
memcpy(c->opacity_animation.initial_border_color, memcpy(c->opacity_animation.initial_border_color,
c->opacity_animation.current_border_color, c->opacity_animation.current_border_color,
sizeof(c->opacity_animation.initial_border_color)); sizeof(c->opacity_animation.initial_border_color));
c->opacity_animation.initial_opacity = c->opacity_animation.initial_opacity = c->opacity_animation.current_opacity;
c->opacity_animation.current_opacity;
} else {
memcpy(c->opacity_animation.initial_border_color, border_color,
sizeof(c->opacity_animation.initial_border_color));
memcpy(c->opacity_animation.current_border_color, border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.initial_opacity = c->focused_opacity;
c->opacity_animation.current_opacity = c->focused_opacity;
}
c->opacity_animation.running = true; c->opacity_animation.running = true;
} }
@ -1113,7 +1099,7 @@ bool client_apply_focus_opacity(Client *c) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t passed_time = timespec_to_ms(&now) - c->animation.time_started; int32_t passed_time = timespec_to_ms(&now) - c->animation.time_started;
double linear_progress = double linear_progress =
c->animation.duration c->animation.duration
? (double)passed_time / (double)c->animation.duration ? (double)passed_time / (double)c->animation.duration
@ -1122,23 +1108,29 @@ bool client_apply_focus_opacity(Client *c) {
double opacity_eased_progress = double opacity_eased_progress =
find_animation_curve_at(linear_progress, OPAFADEIN); find_animation_curve_at(linear_progress, OPAFADEIN);
float percent = float percent = config.animation_fade_in && !c->nofadein
animation_fade_in && !c->nofadein ? opacity_eased_progress : 1.0; ? opacity_eased_progress
: 1.0;
float opacity = float opacity =
c == selmon->sel ? c->focused_opacity : c->unfocused_opacity; c == selmon->sel ? c->focused_opacity : c->unfocused_opacity;
float target_opacity = float target_opacity = percent * (1.0 - config.fadein_begin_opacity) +
percent * (1.0 - fadein_begin_opacity) + fadein_begin_opacity; config.fadein_begin_opacity;
if (target_opacity > opacity) { if (target_opacity > opacity) {
target_opacity = opacity; target_opacity = opacity;
} }
memcpy(c->opacity_animation.current_border_color,
c->opacity_animation.target_border_color,
sizeof(c->opacity_animation.current_border_color));
c->opacity_animation.current_opacity = target_opacity;
client_set_opacity(c, target_opacity); client_set_opacity(c, target_opacity);
} else if (animations && c->opacity_animation.running) { client_set_border_color(c, c->opacity_animation.target_border_color);
} else if (config.animations && c->opacity_animation.running) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t passed_time = int32_t passed_time =
timespec_to_ms(&now) - c->opacity_animation.time_started; timespec_to_ms(&now) - c->opacity_animation.time_started;
double linear_progress = double linear_progress =
c->opacity_animation.duration c->opacity_animation.duration
@ -1155,7 +1147,7 @@ bool client_apply_focus_opacity(Client *c) {
client_set_opacity(c, c->opacity_animation.current_opacity); client_set_opacity(c, c->opacity_animation.current_opacity);
// Animate border color // Animate border color
for (int i = 0; i < 4; i++) { for (int32_t i = 0; i < 4; i++) {
c->opacity_animation.current_border_color[i] = c->opacity_animation.current_border_color[i] =
c->opacity_animation.initial_border_color[i] + c->opacity_animation.initial_border_color[i] +
(c->opacity_animation.target_border_color[i] - (c->opacity_animation.target_border_color[i] -
@ -1163,7 +1155,7 @@ bool client_apply_focus_opacity(Client *c) {
eased_progress; eased_progress;
} }
client_set_border_color(c, c->opacity_animation.current_border_color); client_set_border_color(c, c->opacity_animation.current_border_color);
if (linear_progress == 1.0f) { if (linear_progress >= 1.0f) {
c->opacity_animation.running = false; c->opacity_animation.running = false;
} else { } else {
return true; return true;
@ -1194,7 +1186,7 @@ bool client_draw_frame(Client *c) {
return client_apply_focus_opacity(c); return client_apply_focus_opacity(c);
} }
if (animations && c->animation.running) { if (config.animations && c->animation.running) {
client_animation_next_tick(c); client_animation_next_tick(c);
} else { } else {
wlr_scene_node_set_position(&c->scene->node, c->pending.x, wlr_scene_node_set_position(&c->scene->node, c->pending.x,

View file

@ -1,22 +1,22 @@
struct dvec2 calculate_animation_curve_at(double t, int type) { struct dvec2 calculate_animation_curve_at(double t, int32_t type) {
struct dvec2 point; struct dvec2 point;
double *animation_curve; double *animation_curve;
if (type == MOVE) { if (type == MOVE) {
animation_curve = animation_curve_move; animation_curve = config.animation_curve_move;
} else if (type == OPEN) { } else if (type == OPEN) {
animation_curve = animation_curve_open; animation_curve = config.animation_curve_open;
} else if (type == TAG) { } else if (type == TAG) {
animation_curve = animation_curve_tag; animation_curve = config.animation_curve_tag;
} else if (type == CLOSE) { } else if (type == CLOSE) {
animation_curve = animation_curve_close; animation_curve = config.animation_curve_close;
} else if (type == FOCUS) { } else if (type == FOCUS) {
animation_curve = animation_curve_focus; animation_curve = config.animation_curve_focus;
} else if (type == OPAFADEIN) { } else if (type == OPAFADEIN) {
animation_curve = animation_curve_opafadein; animation_curve = config.animation_curve_opafadein;
} else if (type == OPAFADEOUT) { } else if (type == OPAFADEOUT) {
animation_curve = animation_curve_opafadeout; animation_curve = config.animation_curve_opafadeout;
} else { } else {
animation_curve = animation_curve_move; animation_curve = config.animation_curve_move;
} }
point.x = 3 * t * (1 - t) * (1 - t) * animation_curve[0] + point.x = 3 * t * (1 - t) * (1 - t) * animation_curve[0] +
@ -41,41 +41,41 @@ void init_baked_points(void) {
baked_points_opafadeout = baked_points_opafadeout =
calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadeout)); calloc(BAKED_POINTS_COUNT, sizeof(*baked_points_opafadeout));
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_move[i] = calculate_animation_curve_at( baked_points_move[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), MOVE); (double)i / (BAKED_POINTS_COUNT - 1), MOVE);
} }
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_open[i] = calculate_animation_curve_at( baked_points_open[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), OPEN); (double)i / (BAKED_POINTS_COUNT - 1), OPEN);
} }
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_tag[i] = calculate_animation_curve_at( baked_points_tag[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), TAG); (double)i / (BAKED_POINTS_COUNT - 1), TAG);
} }
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_close[i] = calculate_animation_curve_at( baked_points_close[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), CLOSE); (double)i / (BAKED_POINTS_COUNT - 1), CLOSE);
} }
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_focus[i] = calculate_animation_curve_at( baked_points_focus[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), FOCUS); (double)i / (BAKED_POINTS_COUNT - 1), FOCUS);
} }
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_opafadein[i] = calculate_animation_curve_at( baked_points_opafadein[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), OPAFADEIN); (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEIN);
} }
for (uint32_t i = 0; i < BAKED_POINTS_COUNT; i++) { for (int32_t i = 0; i < BAKED_POINTS_COUNT; i++) {
baked_points_opafadeout[i] = calculate_animation_curve_at( baked_points_opafadeout[i] = calculate_animation_curve_at(
(double)i / (BAKED_POINTS_COUNT - 1), OPAFADEOUT); (double)i / (BAKED_POINTS_COUNT - 1), OPAFADEOUT);
} }
} }
double find_animation_curve_at(double t, int type) { double find_animation_curve_at(double t, int32_t type) {
uint32_t down = 0; int32_t down = 0;
uint32_t up = BAKED_POINTS_COUNT - 1; int32_t up = BAKED_POINTS_COUNT - 1;
uint32_t middle = (up + down) / 2; int32_t middle = (up + down) / 2;
struct dvec2 *baked_points; struct dvec2 *baked_points;
if (type == MOVE) { if (type == MOVE) {
baked_points = baked_points_move; baked_points = baked_points_move;
@ -106,7 +106,8 @@ double find_animation_curve_at(double t, int type) {
return baked_points[up].y; return baked_points[up].y;
} }
static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly, static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
int32_t ly,
struct wlr_scene_tree *snapshot_tree) { struct wlr_scene_tree *snapshot_tree) {
if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) { if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) {
return true; return true;
@ -249,3 +250,13 @@ struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node,
return snapshot; return snapshot;
} }
void request_fresh_all_monitors(void) {
Monitor *m = NULL;
wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) {
continue;
}
wlr_output_schedule_frame(m->wlr_output);
}
}

View file

@ -1,4 +1,4 @@
void layer_actual_size(LayerSurface *l, uint32_t *width, uint32_t *height) { void layer_actual_size(LayerSurface *l, int32_t *width, int32_t *height) {
struct wlr_box box; struct wlr_box box;
if (l->animation.running) { if (l->animation.running) {
@ -42,7 +42,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
.height = state->desired_height}; .height = state->desired_height};
// 水平方向定位 // 水平方向定位
const uint32_t both_horiz = const int32_t both_horiz =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
if (box.width == 0) { if (box.width == 0) {
box.x = bounds.x; box.x = bounds.x;
@ -57,7 +57,7 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
} }
// 垂直方向定位 // 垂直方向定位
const uint32_t both_vert = const int32_t both_vert =
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if (box.height == 0) { if (box.height == 0) {
box.y = bounds.y; box.y = bounds.y;
@ -101,10 +101,10 @@ void get_layer_target_geometry(LayerSurface *l, struct wlr_box *target_box) {
} }
void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) { void set_layer_dir_animaiton(LayerSurface *l, struct wlr_box *geo) {
int slide_direction; int32_t slide_direction;
int horizontal, horizontal_value; int32_t horizontal, horizontal_value;
int vertical, vertical_value; int32_t vertical, vertical_value;
int center_x, center_y; int32_t center_x, center_y;
if (!l) if (!l)
return; return;
@ -156,17 +156,16 @@ void layer_draw_shadow(LayerSurface *l) {
if (!l->mapped || !l->shadow) if (!l->mapped || !l->shadow)
return; return;
if (!shadows || !layer_shadows || l->noshadow) { if (!config.shadows || !config.layer_shadows || l->noshadow) {
wlr_scene_shadow_set_size(l->shadow, 0, 0); wlr_scene_shadow_set_size(l->shadow, 0, 0);
return; return;
} }
uint32_t width, height; int32_t width, height;
layer_actual_size(l, &width, &height); layer_actual_size(l, &width, &height);
uint32_t delta = shadows_size; int32_t delta = config.shadows_size;
/* we calculate where to clip the shadow */
struct wlr_box layer_box = { struct wlr_box layer_box = {
.x = 0, .x = 0,
.y = 0, .y = 0,
@ -175,23 +174,21 @@ void layer_draw_shadow(LayerSurface *l) {
}; };
struct wlr_box shadow_box = { struct wlr_box shadow_box = {
.x = shadows_position_x, .x = config.shadows_position_x,
.y = shadows_position_y, .y = config.shadows_position_y,
.width = width + 2 * delta, .width = width + 2 * delta,
.height = height + 2 * delta, .height = height + 2 * delta,
}; };
struct wlr_box intersection_box; struct wlr_box intersection_box;
wlr_box_intersection(&intersection_box, &layer_box, &shadow_box); wlr_box_intersection(&intersection_box, &layer_box, &shadow_box);
/* clipped region takes shadow relative coords, so we translate everything intersection_box.x -= config.shadows_position_x;
* by its position */ intersection_box.y -= config.shadows_position_y;
intersection_box.x -= shadows_position_x;
intersection_box.y -= shadows_position_y;
struct clipped_region clipped_region = { struct clipped_region clipped_region = {
.area = intersection_box, .area = intersection_box,
.corner_radius = border_radius, .corner_radius = config.border_radius,
.corners = border_radius_location_default, .corners = config.border_radius_location_default,
}; };
wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y); wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y);
@ -200,8 +197,8 @@ void layer_draw_shadow(LayerSurface *l) {
wlr_scene_shadow_set_clipped_region(l->shadow, clipped_region); wlr_scene_shadow_set_clipped_region(l->shadow, clipped_region);
} }
void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
int sy, void *data) { int32_t sx, int32_t sy, void *data) {
BufferData *buffer_data = (BufferData *)data; BufferData *buffer_data = (BufferData *)data;
struct wlr_scene_surface *scene_surface = struct wlr_scene_surface *scene_surface =
@ -212,8 +209,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
struct wlr_surface *surface = scene_surface->surface; struct wlr_surface *surface = scene_surface->surface;
uint32_t surface_width = surface->current.width * buffer_data->width_scale; int32_t surface_width = surface->current.width * buffer_data->width_scale;
uint32_t surface_height = int32_t surface_height =
surface->current.height * buffer_data->height_scale; surface->current.height * buffer_data->height_scale;
if (surface_height > 0 && surface_width > 0) { if (surface_height > 0 && surface_width > 0) {
@ -222,7 +219,8 @@ void layer_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx,
} }
void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, void layer_fadeout_scene_buffer_apply_effect(struct wlr_scene_buffer *buffer,
int sx, int sy, void *data) { int32_t sx, int32_t sy,
void *data) {
BufferData *buffer_data = (BufferData *)data; BufferData *buffer_data = (BufferData *)data;
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width, wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
buffer_data->height); buffer_data->height);
@ -235,23 +233,22 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started;
double animation_passed = double animation_passed =
l->animation.duration l->animation.duration
? (double)passed_time / (double)l->animation.duration ? (double)passed_time / (double)l->animation.duration
: 1.0; : 1.0;
int type = l->animation.action = l->animation.action; int32_t type = l->animation.action = l->animation.action;
double factor = find_animation_curve_at(animation_passed, type); double factor = find_animation_curve_at(animation_passed, type);
uint32_t width = l->animation.initial.width + int32_t width = l->animation.initial.width +
(l->current.width - l->animation.initial.width) * factor; (l->current.width - l->animation.initial.width) * factor;
uint32_t height = int32_t height = l->animation.initial.height +
l->animation.initial.height +
(l->current.height - l->animation.initial.height) * factor; (l->current.height - l->animation.initial.height) * factor;
uint32_t x = l->animation.initial.x + int32_t x = l->animation.initial.x +
(l->current.x - l->animation.initial.x) * factor; (l->current.x - l->animation.initial.x) * factor;
uint32_t y = l->animation.initial.y + int32_t y = l->animation.initial.y +
(l->current.y - l->animation.initial.y) * factor; (l->current.y - l->animation.initial.y) * factor;
wlr_scene_node_set_position(&l->scene->node, x, y); wlr_scene_node_set_position(&l->scene->node, x, y);
@ -261,7 +258,7 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
buffer_data.height = height; buffer_data.height = height;
if ((!l->animation_type_close && if ((!l->animation_type_close &&
strcmp(layer_animation_type_close, "zoom") == 0) || strcmp(config.layer_animation_type_close, "zoom") == 0) ||
(l->animation_type_close && (l->animation_type_close &&
strcmp(l->animation_type_close, "zoom") == 0)) { strcmp(l->animation_type_close, "zoom") == 0)) {
wlr_scene_node_for_each_buffer(&l->scene->node, wlr_scene_node_for_each_buffer(&l->scene->node,
@ -279,12 +276,12 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) {
double opacity_eased_progress = double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEOUT); find_animation_curve_at(animation_passed, OPAFADEOUT);
double percent = fadeout_begin_opacity - double percent = config.fadeout_begin_opacity -
(opacity_eased_progress * fadeout_begin_opacity); (opacity_eased_progress * config.fadeout_begin_opacity);
double opacity = MAX(percent, 0.0f); double opacity = MAX(percent, 0.0f);
if (animation_fade_out) if (config.animation_fade_out)
wlr_scene_node_for_each_buffer(&l->scene->node, wlr_scene_node_for_each_buffer(&l->scene->node,
scene_buffer_apply_opacity, &opacity); scene_buffer_apply_opacity, &opacity);
@ -304,35 +301,34 @@ void layer_animation_next_tick(LayerSurface *l) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t passed_time = timespec_to_ms(&now) - l->animation.time_started; int32_t passed_time = timespec_to_ms(&now) - l->animation.time_started;
double animation_passed = double animation_passed =
l->animation.duration l->animation.duration
? (double)passed_time / (double)l->animation.duration ? (double)passed_time / (double)l->animation.duration
: 1.0; : 1.0;
int type = l->animation.action == NONE ? MOVE : l->animation.action; int32_t type = l->animation.action == NONE ? MOVE : l->animation.action;
double factor = find_animation_curve_at(animation_passed, type); double factor = find_animation_curve_at(animation_passed, type);
uint32_t width = l->animation.initial.width + int32_t width = l->animation.initial.width +
(l->current.width - l->animation.initial.width) * factor; (l->current.width - l->animation.initial.width) * factor;
uint32_t height = int32_t height = l->animation.initial.height +
l->animation.initial.height +
(l->current.height - l->animation.initial.height) * factor; (l->current.height - l->animation.initial.height) * factor;
uint32_t x = l->animation.initial.x + int32_t x = l->animation.initial.x +
(l->current.x - l->animation.initial.x) * factor; (l->current.x - l->animation.initial.x) * factor;
uint32_t y = l->animation.initial.y + int32_t y = l->animation.initial.y +
(l->current.y - l->animation.initial.y) * factor; (l->current.y - l->animation.initial.y) * factor;
double opacity_eased_progress = double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEIN); find_animation_curve_at(animation_passed, OPAFADEIN);
double opacity = double opacity =
MIN(fadein_begin_opacity + MIN(config.fadein_begin_opacity +
opacity_eased_progress * (1.0 - fadein_begin_opacity), opacity_eased_progress * (1.0 - config.fadein_begin_opacity),
1.0f); 1.0f);
if (animation_fade_in) if (config.animation_fade_in)
wlr_scene_node_for_each_buffer(&l->scene->node, wlr_scene_node_for_each_buffer(&l->scene->node,
scene_buffer_apply_opacity, &opacity); scene_buffer_apply_opacity, &opacity);
@ -348,7 +344,7 @@ void layer_animation_next_tick(LayerSurface *l) {
} }
if ((!l->animation_type_open && if ((!l->animation_type_open &&
strcmp(layer_animation_type_open, "zoom") == 0) || strcmp(config.layer_animation_type_open, "zoom") == 0) ||
(l->animation_type_open && (l->animation_type_open &&
strcmp(l->animation_type_open, "zoom") == 0)) { strcmp(l->animation_type_open, "zoom") == 0)) {
wlr_scene_node_for_each_buffer( wlr_scene_node_for_each_buffer(
@ -371,7 +367,7 @@ void layer_animation_next_tick(LayerSurface *l) {
void init_fadeout_layers(LayerSurface *l) { void init_fadeout_layers(LayerSurface *l) {
if (!animations || !layer_animations || l->noanim) { if (!config.animations || !config.layer_animations || l->noanim) {
return; return;
} }
@ -381,7 +377,7 @@ void init_fadeout_layers(LayerSurface *l) {
if ((l->animation_type_close && if ((l->animation_type_close &&
strcmp(l->animation_type_close, "none") == 0) || strcmp(l->animation_type_close, "none") == 0) ||
(!l->animation_type_close && (!l->animation_type_close &&
strcmp(layer_animation_type_close, "none") == 0)) { strcmp(config.layer_animation_type_close, "none") == 0)) {
return; return;
} }
@ -404,7 +400,7 @@ void init_fadeout_layers(LayerSurface *l) {
return; return;
} }
fadeout_layer->animation.duration = animation_duration_close; fadeout_layer->animation.duration = config.animation_duration_close;
fadeout_layer->geom = fadeout_layer->current = fadeout_layer->geom = fadeout_layer->current =
fadeout_layer->animainit_geom = fadeout_layer->animation.initial = fadeout_layer->animainit_geom = fadeout_layer->animation.initial =
l->animation.current; l->animation.current;
@ -420,14 +416,14 @@ void init_fadeout_layers(LayerSurface *l) {
fadeout_layer->animation.initial.y = 0; fadeout_layer->animation.initial.y = 0;
if ((!l->animation_type_close && if ((!l->animation_type_close &&
strcmp(layer_animation_type_close, "zoom") == 0) || strcmp(config.layer_animation_type_close, "zoom") == 0) ||
(l->animation_type_close && (l->animation_type_close &&
strcmp(l->animation_type_close, "zoom") == 0)) { strcmp(l->animation_type_close, "zoom") == 0)) {
// 算出要设置的绝对坐标和大小 // 算出要设置的绝对坐标和大小
fadeout_layer->current.width = fadeout_layer->current.width =
(float)l->animation.current.width * zoom_end_ratio; (float)l->animation.current.width * config.zoom_end_ratio;
fadeout_layer->current.height = fadeout_layer->current.height =
(float)l->animation.current.height * zoom_end_ratio; (float)l->animation.current.height * config.zoom_end_ratio;
fadeout_layer->current.x = usable_area.x + usable_area.width / 2 - fadeout_layer->current.x = usable_area.x + usable_area.width / 2 -
fadeout_layer->current.width / 2; fadeout_layer->current.width / 2;
fadeout_layer->current.y = usable_area.y + usable_area.height / 2 - fadeout_layer->current.y = usable_area.y + usable_area.height / 2 -
@ -439,7 +435,7 @@ void init_fadeout_layers(LayerSurface *l) {
fadeout_layer->current.y - l->animation.current.y; fadeout_layer->current.y - l->animation.current.y;
} else if ((!l->animation_type_close && } else if ((!l->animation_type_close &&
strcmp(layer_animation_type_close, "slide") == 0) || strcmp(config.layer_animation_type_close, "slide") == 0) ||
(l->animation_type_close && (l->animation_type_close &&
strcmp(l->animation_type_close, "slide") == 0)) { strcmp(l->animation_type_close, "slide") == 0)) {
// 获取slide动画的结束绝对坐标和大小 // 获取slide动画的结束绝对坐标和大小
@ -484,17 +480,18 @@ void layer_set_pending_state(LayerSurface *l) {
if (l->animation.action == OPEN && !l->animation.running) { if (l->animation.action == OPEN && !l->animation.running) {
if ((!l->animation_type_open && if ((!l->animation_type_open &&
strcmp(layer_animation_type_open, "zoom") == 0) || strcmp(config.layer_animation_type_open, "zoom") == 0) ||
(l->animation_type_open && (l->animation_type_open &&
strcmp(l->animation_type_open, "zoom") == 0)) { strcmp(l->animation_type_open, "zoom") == 0)) {
l->animainit_geom.width = l->geom.width * zoom_initial_ratio; l->animainit_geom.width = l->geom.width * config.zoom_initial_ratio;
l->animainit_geom.height = l->geom.height * zoom_initial_ratio; l->animainit_geom.height =
l->geom.height * config.zoom_initial_ratio;
l->animainit_geom.x = usable_area.x + usable_area.width / 2 - l->animainit_geom.x = usable_area.x + usable_area.width / 2 -
l->animainit_geom.width / 2; l->animainit_geom.width / 2;
l->animainit_geom.y = usable_area.y + usable_area.height / 2 - l->animainit_geom.y = usable_area.y + usable_area.height / 2 -
l->animainit_geom.height / 2; l->animainit_geom.height / 2;
} else if ((!l->animation_type_open && } else if ((!l->animation_type_open &&
strcmp(layer_animation_type_open, "slide") == 0) || strcmp(config.layer_animation_type_open, "slide") == 0) ||
(l->animation_type_open && (l->animation_type_open &&
strcmp(l->animation_type_open, "slide") == 0)) { strcmp(l->animation_type_open, "slide") == 0)) {
@ -508,8 +505,7 @@ void layer_set_pending_state(LayerSurface *l) {
} else { } else {
l->animainit_geom = l->animation.current; l->animainit_geom = l->animation.current;
} }
// 判断是否需要动画 if (!config.animations || !config.layer_animations || l->noanim ||
if (!animations || !layer_animations || l->noanim ||
l->layer_surface->current.layer == l->layer_surface->current.layer ==
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ||
l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { l->layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) {
@ -521,7 +517,7 @@ void layer_set_pending_state(LayerSurface *l) {
if (((l->animation_type_open && if (((l->animation_type_open &&
strcmp(l->animation_type_open, "none") == 0) || strcmp(l->animation_type_open, "none") == 0) ||
(!l->animation_type_open && (!l->animation_type_open &&
strcmp(layer_animation_type_open, "none") == 0)) && strcmp(config.layer_animation_type_open, "none") == 0)) &&
l->animation.action == OPEN) { l->animation.action == OPEN) {
l->animation.should_animate = false; l->animation.should_animate = false;
} }
@ -568,7 +564,8 @@ bool layer_draw_frame(LayerSurface *l) {
return false; return false;
} }
if (animations && layer_animations && l->animation.running && !l->noanim) { if (config.animations && config.layer_animations && l->animation.running &&
!l->noanim) {
layer_animation_next_tick(l); layer_animation_next_tick(l);
layer_draw_shadow(l); layer_draw_shadow(l);
} else { } else {

View file

@ -7,11 +7,11 @@ void set_tagin_animation(Monitor *m, Client *c) {
if (m->pertag->curtag > m->pertag->prevtag) { if (m->pertag->curtag > m->pertag->prevtag) {
c->animainit_geom.x = tag_animation_direction == VERTICAL c->animainit_geom.x = config.tag_animation_direction == VERTICAL
? c->animation.current.x ? c->animation.current.x
: MAX(c->mon->m.x + c->mon->m.width, : MAX(c->mon->m.x + c->mon->m.width,
c->geom.x + c->mon->m.width); c->geom.x + c->mon->m.width);
c->animainit_geom.y = tag_animation_direction == VERTICAL c->animainit_geom.y = config.tag_animation_direction == VERTICAL
? MAX(c->mon->m.y + c->mon->m.height, ? MAX(c->mon->m.y + c->mon->m.height,
c->geom.y + c->mon->m.height) c->geom.y + c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
@ -19,11 +19,11 @@ void set_tagin_animation(Monitor *m, Client *c) {
} else { } else {
c->animainit_geom.x = c->animainit_geom.x =
tag_animation_direction == VERTICAL config.tag_animation_direction == VERTICAL
? c->animation.current.x ? c->animation.current.x
: MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); : MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width);
c->animainit_geom.y = c->animainit_geom.y =
tag_animation_direction == VERTICAL config.tag_animation_direction == VERTICAL
? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) ? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
} }
@ -39,7 +39,8 @@ void set_arrange_visible(Monitor *m, Client *c, bool want_animation) {
client_set_suspended(c, false); client_set_suspended(c, false);
if (!c->animation.tag_from_rule && want_animation && if (!c->animation.tag_from_rule && want_animation &&
m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { m->pertag->prevtag != 0 && m->pertag->curtag != 0 &&
config.animations) {
c->animation.tagining = true; c->animation.tagining = true;
set_tagin_animation(m, c); set_tagin_animation(m, c);
} else { } else {
@ -57,10 +58,10 @@ void set_tagout_animation(Monitor *m, Client *c) {
if (m->pertag->curtag > m->pertag->prevtag) { if (m->pertag->curtag > m->pertag->prevtag) {
c->pending = c->geom; c->pending = c->geom;
c->pending.x = c->pending.x =
tag_animation_direction == VERTICAL config.tag_animation_direction == VERTICAL
? c->animation.current.x ? c->animation.current.x
: MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); : MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width);
c->pending.y = tag_animation_direction == VERTICAL c->pending.y = config.tag_animation_direction == VERTICAL
? MIN(c->mon->m.y - c->geom.height, ? MIN(c->mon->m.y - c->geom.height,
c->geom.y - c->mon->m.height) c->geom.y - c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
@ -68,11 +69,11 @@ void set_tagout_animation(Monitor *m, Client *c) {
resize(c, c->geom, 0); resize(c, c->geom, 0);
} else { } else {
c->pending = c->geom; c->pending = c->geom;
c->pending.x = tag_animation_direction == VERTICAL c->pending.x = config.tag_animation_direction == VERTICAL
? c->animation.current.x ? c->animation.current.x
: MAX(c->mon->m.x + c->mon->m.width, : MAX(c->mon->m.x + c->mon->m.width,
c->geom.x + c->mon->m.width); c->geom.x + c->mon->m.width);
c->pending.y = tag_animation_direction == VERTICAL c->pending.y = config.tag_animation_direction == VERTICAL
? MAX(c->mon->m.y + c->mon->m.height, ? MAX(c->mon->m.y + c->mon->m.height,
c->geom.y + c->mon->m.height) c->geom.y + c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
@ -82,7 +83,8 @@ void set_tagout_animation(Monitor *m, Client *c) {
void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) { void set_arrange_hidden(Monitor *m, Client *c, bool want_animation) {
if ((c->tags & (1 << (m->pertag->prevtag - 1))) && if ((c->tags & (1 << (m->pertag->prevtag - 1))) &&
m->pertag->prevtag != 0 && m->pertag->curtag != 0 && animations) { m->pertag->prevtag != 0 && m->pertag->curtag != 0 &&
config.animations) {
c->animation.tagouting = true; c->animation.tagouting = true;
c->animation.tagining = false; c->animation.tagining = false;
set_tagout_animation(m, c); set_tagout_animation(m, c);

View file

@ -6,7 +6,7 @@
*/ */
/* Leave these functions first; they're used in the others */ /* Leave these functions first; they're used in the others */
static inline int client_is_x11(Client *c) { static inline int32_t client_is_x11(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
return c->type == X11; return c->type == X11;
#endif #endif
@ -21,14 +21,15 @@ static inline struct wlr_surface *client_surface(Client *c) {
return c->surface.xdg->surface; return c->surface.xdg->surface;
} }
static inline int toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, static inline int32_t toplevel_from_wlr_surface(struct wlr_surface *s,
Client **pc,
LayerSurface **pl) { LayerSurface **pl) {
struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface;
struct wlr_surface *root_surface; struct wlr_surface *root_surface;
struct wlr_layer_surface_v1 *layer_surface; struct wlr_layer_surface_v1 *layer_surface;
Client *c = NULL; Client *c = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
int type = -1; int32_t type = -1;
#ifdef XWAYLAND #ifdef XWAYLAND
struct wlr_xwayland_surface *xsurface; struct wlr_xwayland_surface *xsurface;
#endif #endif
@ -88,7 +89,7 @@ end:
/* The others */ /* The others */
static inline void client_activate_surface(struct wlr_surface *s, static inline void client_activate_surface(struct wlr_surface *s,
int activated) { int32_t activated) {
struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_toplevel *toplevel;
#ifdef XWAYLAND #ifdef XWAYLAND
struct wlr_xwayland_surface *xsurface; struct wlr_xwayland_surface *xsurface;
@ -113,7 +114,7 @@ static inline const char *client_get_appid(Client *c) {
: "broken"; : "broken";
} }
static inline int client_get_pid(Client *c) { static inline int32_t client_get_pid(Client *c) {
pid_t pid; pid_t pid;
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
@ -169,7 +170,7 @@ static inline Client *client_get_parent(Client *c) {
return p; return p;
} }
static inline int client_has_children(Client *c) { static inline int32_t client_has_children(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
return !wl_list_empty(&c->surface.xwayland->children); return !wl_list_empty(&c->surface.xwayland->children);
@ -189,7 +190,7 @@ static inline const char *client_get_title(Client *c) {
: "broken"; : "broken";
} }
static inline int client_is_float_type(Client *c) { static inline int32_t client_is_float_type(Client *c) {
struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_toplevel_state state; struct wlr_xdg_toplevel_state state;
@ -229,12 +230,12 @@ static inline int client_is_float_type(Client *c) {
state.min_height == state.max_height)); state.min_height == state.max_height));
} }
static inline int client_is_rendered_on_mon(Client *c, Monitor *m) { static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) {
/* This is needed for when you don't want to check formal assignment, /* This is needed for when you don't want to check formal assignment,
* but rather actual displaying of the pixels. * but rather actual displaying of the pixels.
* Usually VISIBLEON suffices and is also faster. */ * Usually VISIBLEON suffices and is also faster. */
struct wlr_surface_output *s; struct wlr_surface_output *s;
int unused_lx, unused_ly; int32_t unused_lx, unused_ly;
if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
return 0; return 0;
wl_list_for_each(s, &client_surface(c)->current_outputs, wl_list_for_each(s, &client_surface(c)->current_outputs,
@ -242,32 +243,7 @@ static inline int client_is_rendered_on_mon(Client *c, Monitor *m) {
return 0; return 0;
} }
static inline int client_is_stopped(Client *c) { static inline int32_t client_is_unmanaged(Client *c) {
int pid;
siginfo_t in = {0};
#ifdef XWAYLAND
if (client_is_x11(c))
return 0;
#endif
wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
if (waitid(P_PID, pid, &in, WNOHANG | WCONTINUED | WSTOPPED | WNOWAIT) <
0) {
/* This process is not our child process, while is very unluckely that
* it is stopped, in order to do not skip frames assume that it is. */
if (errno == ECHILD)
return 1;
} else if (in.si_pid) {
if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
return 1;
if (in.si_code == CLD_CONTINUED)
return 0;
}
return 0;
}
static inline int client_is_unmanaged(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
return c->surface.xwayland->override_redirect; return c->surface.xwayland->override_redirect;
@ -299,7 +275,7 @@ static inline void client_set_border_color(Client *c,
wlr_scene_rect_set_color(c->border, color); wlr_scene_rect_set_color(c->border, color);
} }
static inline void client_set_fullscreen(Client *c, int fullscreen) { static inline void client_set_fullscreen(Client *c, int32_t fullscreen) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
@ -318,9 +294,24 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
uint32_t height) { uint32_t height) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
struct wlr_surface_state *state =
&c->surface.xwayland->surface->current;
if ((int32_t)c->geom.width - 2 * (int32_t)c->bw ==
(int32_t)state->width &&
(int32_t)c->geom.height - 2 * (int32_t)c->bw ==
(int32_t)state->height &&
(int32_t)c->surface.xwayland->x ==
(int32_t)c->geom.x + (int32_t)c->bw &&
(int32_t)c->surface.xwayland->y ==
(int32_t)c->geom.y + (int32_t)c->bw) {
return 0;
}
wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw, wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
c->geom.y + c->bw, width, height); c->geom.y + c->bw, width, height);
return 0; return 1;
} }
#endif #endif
if ((int32_t)width == c->surface.xdg->toplevel->current.width && if ((int32_t)width == c->surface.xdg->toplevel->current.width &&
@ -379,7 +370,7 @@ static inline void client_set_tiled(Client *c, uint32_t edges) {
} }
} }
static inline void client_set_suspended(Client *c, int suspended) { static inline void client_set_suspended(Client *c, int32_t suspended) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
return; return;
@ -388,7 +379,7 @@ static inline void client_set_suspended(Client *c, int suspended) {
wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
} }
static inline int client_should_ignore_focus(Client *c) { static inline int32_t client_should_ignore_focus(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
@ -403,7 +394,7 @@ static inline int client_should_ignore_focus(Client *c) {
return 0; return 0;
} }
static inline int client_is_x11_popup(Client *c) { static inline int32_t client_is_x11_popup(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
@ -432,7 +423,7 @@ static inline int client_is_x11_popup(Client *c) {
return 0; return 0;
} }
static inline int client_should_global(Client *c) { static inline int32_t client_should_global(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
@ -445,7 +436,7 @@ static inline int client_should_global(Client *c) {
return 0; return 0;
} }
static inline int client_should_overtop(Client *c) { static inline int32_t client_should_overtop(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
@ -457,7 +448,7 @@ static inline int client_should_overtop(Client *c) {
return 0; return 0;
} }
static inline int client_wants_focus(Client *c) { static inline int32_t client_wants_focus(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
return client_is_unmanaged(c) && return client_is_unmanaged(c) &&
wlr_xwayland_surface_override_redirect_wants_focus( wlr_xwayland_surface_override_redirect_wants_focus(
@ -468,7 +459,7 @@ static inline int client_wants_focus(Client *c) {
return 0; return 0;
} }
static inline int client_wants_fullscreen(Client *c) { static inline int32_t client_wants_fullscreen(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
return c->surface.xwayland->fullscreen; return c->surface.xwayland->fullscreen;

View file

@ -36,8 +36,8 @@ void *ecalloc(size_t nmemb, size_t size) {
return p; return p;
} }
int fd_set_nonblock(int fd) { int32_t fd_set_nonblock(int32_t fd) {
int flags = fcntl(fd, F_GETFL); int32_t flags = fcntl(fd, F_GETFL);
if (flags < 0) { if (flags < 0) {
perror("fcntl(F_GETFL):"); perror("fcntl(F_GETFL):");
return -1; return -1;
@ -50,8 +50,8 @@ int fd_set_nonblock(int fd) {
return 0; return 0;
} }
int regex_match(const char *pattern, const char *str) { int32_t regex_match(const char *pattern, const char *str) {
int errnum; int32_t errnum;
PCRE2_SIZE erroffset; PCRE2_SIZE erroffset;
if (!pattern || !str) { if (!pattern || !str) {
@ -70,7 +70,7 @@ int regex_match(const char *pattern, const char *str) {
pcre2_match_data *match_data = pcre2_match_data *match_data =
pcre2_match_data_create_from_pattern(re, NULL); pcre2_match_data_create_from_pattern(re, NULL);
int ret = int32_t ret =
pcre2_match(re, (PCRE2_SPTR)str, strlen(str), 0, 0, match_data, NULL); pcre2_match(re, (PCRE2_SPTR)str, strlen(str), 0, 0, match_data, NULL);
pcre2_match_data_free(match_data); pcre2_match_data_free(match_data);
@ -92,3 +92,85 @@ uint32_t get_now_in_ms(void) {
uint32_t timespec_to_ms(struct timespec *ts) { uint32_t timespec_to_ms(struct timespec *ts) {
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000; return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000;
} }
char *join_strings(char *arr[], const char *sep) {
if (!arr || !arr[0]) {
char *empty = malloc(1);
if (empty)
empty[0] = '\0';
return empty;
}
size_t total_len = 0;
int count = 0;
for (int i = 0; arr[i] != NULL; i++) {
total_len += strlen(arr[i]);
count++;
}
if (count > 0) {
total_len += strlen(sep) * (count - 1);
}
char *result = malloc(total_len + 1);
if (!result)
return NULL;
result[0] = '\0';
for (int i = 0; arr[i] != NULL; i++) {
if (i > 0)
strcat(result, sep);
strcat(result, arr[i]);
}
return result;
}
char *join_strings_with_suffix(char *arr[], const char *suffix,
const char *sep) {
if (!arr || !arr[0]) {
char *empty = malloc(1);
if (empty)
empty[0] = '\0';
return empty;
}
size_t total_len = 0;
int count = 0;
for (int i = 0; arr[i] != NULL; i++) {
total_len += strlen(arr[i]) + strlen(suffix);
count++;
}
if (count > 0) {
total_len += strlen(sep) * (count - 1);
}
char *result = malloc(total_len + 1);
if (!result)
return NULL;
result[0] = '\0';
for (int i = 0; arr[i] != NULL; i++) {
if (i > 0)
strcat(result, sep);
strcat(result, arr[i]);
strcat(result, suffix);
}
return result;
}
char *string_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (len < 0)
return NULL;
char *str = malloc(len + 1);
if (!str)
return NULL;
va_start(args, fmt);
vsnprintf(str, len + 1, fmt, args);
va_end(args);
return str;
}

View file

@ -3,8 +3,12 @@
void die(const char *fmt, ...); void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size); void *ecalloc(size_t nmemb, size_t size);
int fd_set_nonblock(int fd); int32_t fd_set_nonblock(int32_t fd);
int regex_match(const char *pattern_mb, const char *str_mb); int32_t regex_match(const char *pattern_mb, const char *str_mb);
void wl_list_append(struct wl_list *list, struct wl_list *object); void wl_list_append(struct wl_list *list, struct wl_list *object);
uint32_t get_now_in_ms(void); uint32_t get_now_in_ms(void);
uint32_t timespec_to_ms(struct timespec *ts); uint32_t timespec_to_ms(struct timespec *ts);
char *join_strings(char *arr[], const char *sep);
char *join_strings_with_suffix(char *arr[], const char *suffix,
const char *sep);
char *string_printf(const char *fmt, ...);

File diff suppressed because it is too large Load diff

View file

@ -1,128 +1,9 @@
// TODO: remove this file in the future, replace all global variables with #define MODKEY WLR_MODIFIER_ALT
// config.xxx
/* speedie's mango config */ static const char *tags[] = {
"1", "2", "3", "4", "5", "6", "7", "8", "9",
};
#define COLOR(hex) \
{((hex >> 24) & 0xFF) / 255.0f, ((hex >> 16) & 0xFF) / 255.0f, \
((hex >> 8) & 0xFF) / 255.0f, (hex & 0xFF) / 255.0f}
/* animaion */
char *animation_type_open = "slide"; // 是否启用动画 //slide,zoom
char *animation_type_close = "slide"; // 是否启用动画 //slide,zoom
char *layer_animation_type_open = "slide"; // 是否启用layer动画 //slide,zoom
char *layer_animation_type_close = "slide"; // 是否启用layer动画 //slide,zoom
int animations = 1; // 是否启用动画
int layer_animations = 0; // 是否启用layer动画
int tag_animation_direction = HORIZONTAL; // 标签动画方向
int animation_fade_in = 1; // Enable animation fade in
int animation_fade_out = 1; // Enable animation fade out
float zoom_initial_ratio = 0.3; // 动画起始窗口比例
float zoom_end_ratio = 0.8; // 动画结束窗口比例
float fadein_begin_opacity = 0.5; // Begin opac window ratio for animations
float fadeout_begin_opacity = 0.5; // Begin opac window ratio for animations
uint32_t animation_duration_move = 500; // Animation move speed
uint32_t animation_duration_open = 400; // Animation open speed
uint32_t animation_duration_tag = 300; // Animation tag speed
uint32_t animation_duration_close = 300; // Animation close speed
uint32_t animation_duration_focus = 0; // Animation focus opacity speed
double animation_curve_move[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_open[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_tag[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_close[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_focus[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_opafadein[4] = {0.46, 1.0, 0.29, 0.99}; // 动画曲线
double animation_curve_opafadeout[4] = {0.5, 0.5, 0.5, 0.5}; // 动画曲线
/* appearance */
uint32_t axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔
uint32_t focus_on_activate = 1; // 收到窗口激活请求是否自动跳转聚焦
uint32_t new_is_master = 1; // 新窗口是否插在头部
double default_mfact = 0.55f; // master 窗口比例
uint32_t default_nmaster = 1; // 默认master数量
int center_master_overspread = 0; // 中心master时是否铺满
int center_when_single_stack = 1; // 单个stack时是否居中
/* logging */
int log_level = WLR_ERROR;
uint32_t numlockon = 0; // 是否打开右边小键盘
uint32_t capslock = 0; // 是否启用快捷键
uint32_t ov_tab_mode = 0; // alt tab切换模式
uint32_t hotarea_size = 10; // 热区大小,10x10
uint32_t enable_hotarea = 1; // 是否启用鼠标热区
int smartgaps = 0; /* 1 means no outer gap when there is only one window */
int sloppyfocus = 1; /* focus follows mouse */
uint32_t gappih = 5; /* horiz inner gap between windows */
uint32_t gappiv = 5; /* vert inner gap between windows */
uint32_t gappoh = 10; /* horiz outer gap between windows and screen edge */
uint32_t gappov = 10; /* vert outer gap between windows and screen edge */
float scratchpad_width_ratio = 0.8;
float scratchpad_height_ratio = 0.9;
int scroller_structs = 20;
float scroller_default_proportion = 0.9;
float scroller_default_proportion_single = 1.0;
int scroller_ignore_proportion_single = 0;
int scroller_focus_center = 0;
int scroller_prefer_center = 0;
int focus_cross_monitor = 0;
int focus_cross_tag = 0;
int exchange_cross_monitor = 0;
int scratchpad_cross_monitor = 0;
int view_current_to_back = 1;
int no_border_when_single = 0;
int no_radius_when_single = 0;
int snap_distance = 30;
int enable_floating_snap = 0;
int drag_tile_to_tile = 0;
uint32_t cursor_size = 24;
uint32_t cursor_hide_timeout = 0;
uint32_t swipe_min_threshold = 1;
int idleinhibit_ignore_visible =
0; /* 1 means idle inhibitors will disable idle tracking even if it's
surface isn't visible */
uint32_t borderpx = 4; /* border pixel of windows */
float rootcolor[] = COLOR(0x323232ff);
float bordercolor[] = COLOR(0x444444ff);
float focuscolor[] = COLOR(0xc66b25ff);
float maximizescreencolor[] = COLOR(0x89aa61ff);
float urgentcolor[] = COLOR(0xad401fff);
float scratchpadcolor[] = COLOR(0x516c93ff);
float globalcolor[] = COLOR(0xb153a7ff);
float overlaycolor[] = COLOR(0x14a57cff);
// char *cursor_theme = "Bibata-Modern-Ice";
int overviewgappi = 5; /* overview时 窗口与边缘 缝隙大小 */
int overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */
/* To conform the xdg-protocol, set the alpha to zero to restore the old
* behavior */
float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0};
int warpcursor = 1; /* Warp cursor to focused client */
int xwayland_persistence = 1; /* xwayland persistence */
int syncobj_enable = 0;
int adaptive_sync = 0;
int allow_lock_transparent = 0;
double drag_refresh_interval = 16.0;
int allow_tearing = TEARING_DISABLED;
int allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
/* keyboard */
/*
only layout can modify after fisrt init
other fields change will be ignored.
*/
char xkb_rules_rules[256];
char xkb_rules_model[256];
char xkb_rules_layout[256];
char xkb_rules_variant[256];
char xkb_rules_options[256];
/* keyboard */
static const struct xkb_rule_names xkb_fallback_rules = { static const struct xkb_rule_names xkb_fallback_rules = {
.layout = "us", .layout = "us",
.variant = NULL, .variant = NULL,
@ -130,107 +11,3 @@ static const struct xkb_rule_names xkb_fallback_rules = {
.rules = NULL, .rules = NULL,
.options = NULL, .options = NULL,
}; };
static const struct xkb_rule_names xkb_default_rules = {
.options = NULL,
};
struct xkb_rule_names xkb_rules = {
/* can specify fields: rules, model, layout, variant, options */
/* example:
.options = "ctrl:nocaps",
*/
.rules = xkb_rules_rules, .model = xkb_rules_model,
.layout = xkb_rules_layout, .variant = xkb_rules_variant,
.options = xkb_rules_options,
};
int repeat_rate = 25;
int repeat_delay = 600;
/* Trackpad */
int disable_trackpad = 0;
int tap_to_click = 1;
int tap_and_drag = 1;
int drag_lock = 1;
int mouse_natural_scrolling = 0;
int trackpad_natural_scrolling = 0;
int disable_while_typing = 1;
int left_handed = 0;
int middle_button_emulation = 0;
int single_scratchpad = 1;
int edge_scroller_pointer_focus = 1;
/* You can choose between:
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
LIBINPUT_CONFIG_SCROLL_2FG
LIBINPUT_CONFIG_SCROLL_EDGE
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
*/
enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
uint32_t scroll_button = 274;
/* You can choose between:
LIBINPUT_CONFIG_CLICK_METHOD_NONE
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
*/
enum libinput_config_click_method click_method =
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
double axis_scroll_factor = 1.0;
/* You can choose between:
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
*/
uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
/* You can choose between:
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
*/
enum libinput_config_accel_profile accel_profile =
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
double accel_speed = 0.0;
/* You can choose between:
LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
*/
enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT
static const char *tags[] = {
"1", "2", "3", "4", "5", "6", "7", "8", "9",
};
float focused_opacity = 1.0;
float unfocused_opacity = 1.0;
int border_radius = 0;
int border_radius_location_default = CORNER_LOCATION_ALL;
int blur = 0;
int blur_layer = 0;
int blur_optimized = 1;
struct blur_data blur_params;
int blur_params_num_passes = 1;
int blur_params_radius = 5;
float blur_params_noise = 0.02;
float blur_params_brightness = 0.9;
float blur_params_contrast = 0.9;
float blur_params_saturation = 1.2;
int shadows = 0;
int shadow_only_floating = 1;
int layer_shadows = 0;
uint32_t shadows_size = 10;
double shadows_blur = 15;
int shadows_position_x = 0;
int shadows_position_y = 0;
float shadowscolor[] = COLOR(0x000000ff);
;

View file

@ -1,71 +1,73 @@
int minimized(const Arg *arg); int32_t minimized(const Arg *arg);
int restore_minimized(const Arg *arg); int32_t restore_minimized(const Arg *arg);
int toggle_scratchpad(const Arg *arg); int32_t toggle_scratchpad(const Arg *arg);
int focusdir(const Arg *arg); int32_t focusdir(const Arg *arg);
int toggleoverview(const Arg *arg); int32_t toggleoverview(const Arg *arg);
int set_proportion(const Arg *arg); int32_t set_proportion(const Arg *arg);
int switch_proportion_preset(const Arg *arg); int32_t switch_proportion_preset(const Arg *arg);
int zoom(const Arg *arg); int32_t zoom(const Arg *arg);
int tagsilent(const Arg *arg); int32_t tagsilent(const Arg *arg);
int tagtoleft(const Arg *arg); int32_t tagtoleft(const Arg *arg);
int tagtoright(const Arg *arg); int32_t tagtoright(const Arg *arg);
int tagcrossmon(const Arg *arg); int32_t tagcrossmon(const Arg *arg);
int viewtoleft(const Arg *arg); int32_t viewtoleft(const Arg *arg);
int viewtoright(const Arg *arg); int32_t viewtoright(const Arg *arg);
int viewtoleft_have_client(const Arg *arg); int32_t viewtoleft_have_client(const Arg *arg);
int viewtoright_have_client(const Arg *arg); int32_t viewtoright_have_client(const Arg *arg);
int viewcrossmon(const Arg *arg); int32_t viewcrossmon(const Arg *arg);
int togglefloating(const Arg *arg); int32_t togglefloating(const Arg *arg);
int togglefullscreen(const Arg *arg); int32_t togglefullscreen(const Arg *arg);
int togglemaximizescreen(const Arg *arg); int32_t togglemaximizescreen(const Arg *arg);
int togglegaps(const Arg *arg); int32_t togglegaps(const Arg *arg);
int tagmon(const Arg *arg); int32_t tagmon(const Arg *arg);
int spawn(const Arg *arg); int32_t spawn(const Arg *arg);
int spawn_shell(const Arg *arg); int32_t spawn_shell(const Arg *arg);
int spawn_on_empty(const Arg *arg); int32_t spawn_on_empty(const Arg *arg);
int setkeymode(const Arg *arg); int32_t setkeymode(const Arg *arg);
int switch_keyboard_layout(const Arg *arg); int32_t switch_keyboard_layout(const Arg *arg);
int setlayout(const Arg *arg); int32_t setlayout(const Arg *arg);
int switch_layout(const Arg *arg); int32_t switch_layout(const Arg *arg);
int setmfact(const Arg *arg); int32_t setmfact(const Arg *arg);
int quit(const Arg *arg); int32_t quit(const Arg *arg);
int moveresize(const Arg *arg); int32_t moveresize(const Arg *arg);
int exchange_client(const Arg *arg); int32_t exchange_client(const Arg *arg);
int exchange_stack_client(const Arg *arg); int32_t exchange_stack_client(const Arg *arg);
int killclient(const Arg *arg); int32_t killclient(const Arg *arg);
int toggleglobal(const Arg *arg); int32_t toggleglobal(const Arg *arg);
int incnmaster(const Arg *arg); int32_t incnmaster(const Arg *arg);
int focusmon(const Arg *arg); int32_t focusmon(const Arg *arg);
int focusstack(const Arg *arg); int32_t focusstack(const Arg *arg);
int chvt(const Arg *arg); int32_t chvt(const Arg *arg);
int reload_config(const Arg *arg); int32_t reload_config(const Arg *arg);
int smartmovewin(const Arg *arg); int32_t smartmovewin(const Arg *arg);
int smartresizewin(const Arg *arg); int32_t smartresizewin(const Arg *arg);
int centerwin(const Arg *arg); int32_t centerwin(const Arg *arg);
int bind_to_view(const Arg *arg); int32_t bind_to_view(const Arg *arg);
int toggletag(const Arg *arg); int32_t toggletag(const Arg *arg);
int toggleview(const Arg *arg); int32_t toggleview(const Arg *arg);
int tag(const Arg *arg); int32_t tag(const Arg *arg);
int comboview(const Arg *arg); int32_t comboview(const Arg *arg);
int incgaps(const Arg *arg); int32_t incgaps(const Arg *arg);
int incigaps(const Arg *arg); int32_t incigaps(const Arg *arg);
int incihgaps(const Arg *arg); int32_t incihgaps(const Arg *arg);
int incivgaps(const Arg *arg); int32_t incivgaps(const Arg *arg);
int incogaps(const Arg *arg); int32_t incogaps(const Arg *arg);
int incohgaps(const Arg *arg); int32_t incohgaps(const Arg *arg);
int incovgaps(const Arg *arg); int32_t incovgaps(const Arg *arg);
int defaultgaps(const Arg *arg); int32_t defaultgaps(const Arg *arg);
int togglefakefullscreen(const Arg *arg); int32_t togglefakefullscreen(const Arg *arg);
int toggleoverlay(const Arg *arg); int32_t toggleoverlay(const Arg *arg);
int movewin(const Arg *arg); int32_t movewin(const Arg *arg);
int resizewin(const Arg *arg); int32_t resizewin(const Arg *arg);
int toggle_named_scratchpad(const Arg *arg); int32_t toggle_named_scratchpad(const Arg *arg);
int toggle_render_border(const Arg *arg); int32_t toggle_render_border(const Arg *arg);
int create_virtual_output(const Arg *arg); int32_t create_virtual_output(const Arg *arg);
int destroy_all_virtual_output(const Arg *arg); int32_t destroy_all_virtual_output(const Arg *arg);
int focuslast(const Arg *arg); int32_t focuslast(const Arg *arg);
int toggle_trackpad_enable(const Arg *arg); int32_t toggle_trackpad_enable(const Arg *arg);
int setoption(const Arg *arg); int32_t setoption(const Arg *arg);
int disable_monitor(const Arg *arg); int32_t disable_monitor(const Arg *arg);
int enable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg);
int toggle_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);

File diff suppressed because it is too large Load diff

View file

@ -112,7 +112,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
Client *c = NULL, *focused = NULL; Client *c = NULL, *focused = NULL;
struct wlr_keyboard *keyboard; struct wlr_keyboard *keyboard;
xkb_layout_index_t current; xkb_layout_index_t current;
int tagmask, state, numclients, focused_client, tag; int32_t tagmask, state, numclients, focused_client, tag;
const char *title, *appid, *symbol; const char *title, *appid, *symbol;
char kb_layout[32]; char kb_layout[32];
focused = focustop(monitor); focused = focustop(monitor);
@ -238,7 +238,7 @@ void dwl_ipc_output_set_client_tags(struct wl_client *client,
selected_client->tags = newtags; selected_client->tags = newtags;
if (selmon == monitor) if (selmon == monitor)
focusclient(focustop(monitor), 1); focusclient(focustop(monitor), 1);
arrange(selmon, false); arrange(selmon, false, false);
printstatus(); printstatus();
} }
@ -257,7 +257,7 @@ void dwl_ipc_output_set_layout(struct wl_client *client,
monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index]; monitor->pertag->ltidxs[monitor->pertag->curtag] = &layouts[index];
clear_fullscreen_and_maximized_state(monitor); clear_fullscreen_and_maximized_state(monitor);
arrange(monitor, false); arrange(monitor, false, false);
printstatus(); printstatus();
} }
@ -287,7 +287,7 @@ void dwl_ipc_output_dispatch(struct wl_client *client,
const char *arg3, const char *arg4, const char *arg3, const char *arg4,
const char *arg5) { const char *arg5) {
int (*func)(const Arg *); int32_t (*func)(const Arg *);
Arg arg; Arg arg;
func = parse_func_name((char *)dispatch, &arg, (char *)arg1, (char *)arg2, func = parse_func_name((char *)dispatch, &arg, (char *)arg1, (char *)arg2,
(char *)arg3, (char *)arg4, (char *)arg5); (char *)arg3, (char *)arg4, (char *)arg5);

View file

@ -102,7 +102,7 @@ static void remove_workspace_by_tag(uint32_t tag, Monitor *m) {
} }
} }
static void add_workspace_by_tag(int tag, Monitor *m) { static void add_workspace_by_tag(int32_t tag, Monitor *m) {
const char *name = get_name_from_tag(tag); const char *name = get_name_from_tag(tag);
struct workspace *workspace = ecalloc(1, sizeof(*workspace)); struct workspace *workspace = ecalloc(1, sizeof(*workspace));
@ -162,7 +162,7 @@ void dwl_ext_workspace_printstatus(Monitor *m) {
} }
void refresh_monitors_workspaces_status(Monitor *m) { void refresh_monitors_workspaces_status(Monitor *m) {
int i; int32_t i;
if (m->isoverview) { if (m->isoverview) {
for (i = 1; i <= LENGTH(tags); i++) { for (i = 1; i <= LENGTH(tags); i++) {

View file

@ -15,7 +15,7 @@ void handle_foreign_activate_request(struct wl_listener *listener, void *data) {
c->is_scratchpad_show = 0; c->is_scratchpad_show = 0;
setborder_color(c); setborder_color(c);
show_hide_client(c); show_hide_client(c);
arrange(c->mon, true); arrange(c->mon, true, false);
return; return;
} }
@ -60,7 +60,7 @@ void handle_foreign_minimize_request(struct wl_listener *listener, void *data) {
c->is_scratchpad_show = 0; c->is_scratchpad_show = 0;
setborder_color(c); setborder_color(c);
show_hide_client(c); show_hide_client(c);
arrange(c->mon, true); arrange(c->mon, true, false);
return; return;
} }
} }

View file

@ -60,7 +60,7 @@ void handle_tearing_new_object(struct wl_listener *listener, void *data) {
bool check_tearing_frame_allow(Monitor *m) { bool check_tearing_frame_allow(Monitor *m) {
/* never allow tearing when disabled */ /* never allow tearing when disabled */
if (!allow_tearing) { if (!config.allow_tearing) {
return false; return false;
} }
@ -72,7 +72,7 @@ bool check_tearing_frame_allow(Monitor *m) {
} }
/* allow tearing for any window when requested or forced */ /* allow tearing for any window when requested or forced */
if (allow_tearing == TEARING_ENABLED) { if (config.allow_tearing == TEARING_ENABLED) {
if (c->force_tearing == STATE_UNSPECIFIED) { if (c->force_tearing == STATE_UNSPECIFIED) {
return c->tearing_hint; return c->tearing_hint;
} else { } else {
@ -87,7 +87,8 @@ bool check_tearing_frame_allow(Monitor *m) {
if (c->force_tearing == STATE_UNSPECIFIED) { if (c->force_tearing == STATE_UNSPECIFIED) {
/* honor the tearing hint or the fullscreen-force preference */ /* honor the tearing hint or the fullscreen-force preference */
return c->tearing_hint || allow_tearing == TEARING_FULLSCREEN_ONLY; return c->tearing_hint ||
config.allow_tearing == TEARING_FULLSCREEN_ONLY;
} }
/* honor tearing as requested by action */ /* honor tearing as requested by action */

View file

@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) {
return NULL; return NULL;
} }
Monitor *output_nearest_to(int lx, int ly) {
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; } bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; }
static bool static bool
@ -225,7 +216,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
Monitor *output = NULL; Monitor *output = NULL;
struct wlr_xdg_positioner_rules pointer_rules; struct wlr_xdg_positioner_rules pointer_rules;
struct wlr_box output_box; struct wlr_box output_box;
int lx, ly; int32_t lx, ly;
struct wlr_box popup_box; struct wlr_box popup_box;
if (!text_input || !relay->focused_surface || if (!text_input || !relay->focused_surface ||
@ -255,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
cursor_rect = (struct wlr_box){0}; cursor_rect = (struct wlr_box){0};
} }
output = output_nearest_to(cursor_rect.x, cursor_rect.y); output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y);
if (!output_is_usable(output)) { if (!output_is_usable(output)) {
return; return;
} }

View file

@ -1,22 +1,22 @@
bool check_hit_no_border(Client *c) { bool check_hit_no_border(Client *c) {
int i;
bool hit_no_border = false; bool hit_no_border = false;
if (!render_border) { if (!render_border) {
hit_no_border = true; hit_no_border = true;
} }
for (i = 0; i < config.tag_rules_count; i++) { if (c->mon && !c->mon->isoverview &&
if (c->tags & (1 << (config.tag_rules[i].id - 1)) && c->mon->pertag->no_render_border[get_tags_first_tag_num(c->tags)]) {
config.tag_rules[i].no_render_border) {
hit_no_border = true; hit_no_border = true;
} }
}
if (no_border_when_single && c && c->mon && c->mon->visible_clients == 1) { if (config.no_border_when_single && c && c->mon &&
((ISSCROLLTILED(c) && c->mon->visible_scroll_tiling_clients == 1) ||
c->mon->visible_clients == 1)) {
hit_no_border = true; hit_no_border = true;
} }
return hit_no_border; return hit_no_border;
} }
Client *termforwin(Client *w) { Client *termforwin(Client *w) {
Client *c = NULL; Client *c = NULL;
@ -37,7 +37,7 @@ Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title) {
const char *appid, *title; const char *appid, *title;
Client *c = NULL; Client *c = NULL;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!scratchpad_cross_monitor && c->mon != selmon) { if (!config.scratchpad_cross_monitor && c->mon != selmon) {
continue; continue;
} }
@ -74,16 +74,19 @@ Client *get_client_by_id_or_title(const char *arg_id, const char *arg_title) {
return target_client; return target_client;
} }
struct wlr_box // 计算客户端居中坐标 struct wlr_box // 计算客户端居中坐标
setclient_coordinate_center(Client *c, struct wlr_box geom, int offsetx, setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom,
int offsety) { int32_t offsetx, int32_t offsety) {
struct wlr_box tempbox; struct wlr_box tempbox;
int offset = 0; int32_t offset = 0;
int len = 0; int32_t len = 0;
Monitor *m = c->mon ? c->mon : selmon; Monitor *m = tm ? tm : selmon;
if (!m)
return geom;
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0; uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
if (!c->no_force_center) { if (!c->no_force_center && m) {
tempbox.x = m->w.x + (m->w.width - geom.width) / 2; tempbox.x = m->w.x + (m->w.width - geom.width) / 2;
tempbox.y = m->w.y + (m->w.height - geom.height) / 2; tempbox.y = m->w.y + (m->w.height - geom.height) / 2;
} else { } else {
@ -135,9 +138,9 @@ static bool is_window_rule_matches(const ConfigWinRule *r, const char *appid,
Client *center_tiled_select(Monitor *m) { Client *center_tiled_select(Monitor *m) {
Client *c = NULL; Client *c = NULL;
Client *target_c = NULL; Client *target_c = NULL;
long int mini_distance = -1; int64_t mini_distance = -1;
int dirx, diry; int32_t dirx, diry;
long int distance; int64_t distance;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (c && VISIBLEON(c, m) && ISSCROLLTILED(c) && if (c && VISIBLEON(c, m) && ISSCROLLTILED(c) &&
client_surface(c)->mapped && !c->isfloating && client_surface(c)->mapped && !c->isfloating &&
@ -157,12 +160,12 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
bool ignore_align) { bool ignore_align) {
Client *c = NULL; Client *c = NULL;
Client **tempClients = NULL; // 初始化为 NULL Client **tempClients = NULL; // 初始化为 NULL
int last = -1; int32_t last = -1;
// 第一次遍历,计算客户端数量 // 第一次遍历,计算客户端数量
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (c && (findfloating || !c->isfloating) && !c->isunglobal && if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
(focus_cross_monitor || c->mon == tc->mon) && (config.focus_cross_monitor || c->mon == tc->mon) &&
(c->tags & c->mon->tagset[c->mon->seltags])) { (c->tags & c->mon->tagset[c->mon->seltags])) {
last++; last++;
} }
@ -183,30 +186,30 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
last = -1; last = -1;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (c && (findfloating || !c->isfloating) && !c->isunglobal && if (c && (findfloating || !c->isfloating) && !c->isunglobal &&
(focus_cross_monitor || c->mon == tc->mon) && (config.focus_cross_monitor || c->mon == tc->mon) &&
(c->tags & c->mon->tagset[c->mon->seltags])) { (c->tags & c->mon->tagset[c->mon->seltags])) {
last++; last++;
tempClients[last] = c; tempClients[last] = c;
} }
} }
int sel_x = tc->geom.x; int32_t sel_x = tc->geom.x;
int sel_y = tc->geom.y; int32_t sel_y = tc->geom.y;
long long int distance = LLONG_MAX; int64_t distance = LLONG_MAX;
long long int same_monitor_distance = LLONG_MAX; int64_t same_monitor_distance = LLONG_MAX;
Client *tempFocusClients = NULL; Client *tempFocusClients = NULL;
Client *tempSameMonitorFocusClients = NULL; Client *tempSameMonitorFocusClients = NULL;
switch (arg->i) { switch (arg->i) {
case UP: case UP:
if (!ignore_align) { if (!ignore_align) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y && if (tempClients[_i]->geom.y < sel_y &&
tempClients[_i]->geom.x == sel_x && tempClients[_i]->geom.x == sel_x &&
tempClients[_i]->mon == tc->mon) { tempClients[_i]->mon == tc->mon) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -216,11 +219,32 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
if (!tempFocusClients) { if (!tempFocusClients) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y < sel_y) { if (tempClients[_i]->geom.y < sel_y) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -237,13 +261,13 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
break; break;
case DOWN: case DOWN:
if (!ignore_align) { if (!ignore_align) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y && if (tempClients[_i]->geom.y > sel_y &&
tempClients[_i]->geom.x == sel_x && tempClients[_i]->geom.x == sel_x &&
tempClients[_i]->mon == tc->mon) { tempClients[_i]->mon == tc->mon) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -253,11 +277,32 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
if (!tempFocusClients) { if (!tempFocusClients) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.y > sel_y) { if (tempClients[_i]->geom.y > sel_y) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -274,13 +319,13 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
break; break;
case LEFT: case LEFT:
if (!ignore_align) { if (!ignore_align) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x && if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->geom.y == sel_y && tempClients[_i]->geom.y == sel_y &&
tempClients[_i]->mon == tc->mon) { tempClients[_i]->mon == tc->mon) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -290,11 +335,32 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
if (!tempFocusClients) { if (!tempFocusClients) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x < sel_x) { if (tempClients[_i]->geom.x < sel_x) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -311,13 +377,13 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
break; break;
case RIGHT: case RIGHT:
if (!ignore_align) { if (!ignore_align) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x && if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->geom.y == sel_y && tempClients[_i]->geom.y == sel_y &&
tempClients[_i]->mon == tc->mon) { tempClients[_i]->mon == tc->mon) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -327,11 +393,32 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
} }
} }
if (!tempFocusClients) { if (!tempFocusClients) {
for (int _i = 0; _i <= last; _i++) { for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x &&
tempClients[_i]->mon == tc->mon &&
client_is_in_same_stack(tc, tempClients[_i], NULL)) {
int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int32_t dis_y = tempClients[_i]->geom.y - sel_y;
int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) {
distance = tmp_distance;
tempFocusClients = tempClients[_i];
}
if (tempClients[_i]->mon == tc->mon &&
tmp_distance < same_monitor_distance) {
same_monitor_distance = tmp_distance;
tempSameMonitorFocusClients = tempClients[_i];
}
}
}
}
if (!tempFocusClients) {
for (int32_t _i = 0; _i <= last; _i++) {
if (tempClients[_i]->geom.x > sel_x) { if (tempClients[_i]->geom.x > sel_x) {
int dis_x = tempClients[_i]->geom.x - sel_x; int32_t dis_x = tempClients[_i]->geom.x - sel_x;
int dis_y = tempClients[_i]->geom.y - sel_y; int32_t dis_y = tempClients[_i]->geom.y - sel_y;
long long int tmp_distance = int64_t tmp_distance =
dis_x * dis_x + dis_y * dis_y; // 计算距离 dis_x * dis_x + dis_y * dis_y; // 计算距离
if (tmp_distance < distance) { if (tmp_distance < distance) {
distance = tmp_distance; distance = tmp_distance;
@ -369,7 +456,9 @@ Client *direction_select(const Arg *arg) {
} }
return find_client_by_direction( return find_client_by_direction(
tc, arg, true, is_scroller_layout(selmon) && !selmon->isoverview); tc, arg, true,
(is_scroller_layout(selmon) || is_centertile_layout(selmon)) &&
!selmon->isoverview);
} }
/* We probably should change the name of this, it sounds like /* We probably should change the name of this, it sounds like
@ -396,6 +485,9 @@ Client *get_next_stack_client(Client *c, bool reverse) {
if (&next->link == &clients) if (&next->link == &clients)
continue; /* wrap past the sentinel node */ continue; /* wrap past the sentinel node */
if (next->isunglobal)
continue;
if (next != c && next->mon && VISIBLEON(next, c->mon)) if (next != c && next->mon && VISIBLEON(next, c->mon))
return next; return next;
} }
@ -404,6 +496,9 @@ Client *get_next_stack_client(Client *c, bool reverse) {
if (&next->link == &clients) if (&next->link == &clients)
continue; /* wrap past the sentinel node */ continue; /* wrap past the sentinel node */
if (next->isunglobal)
continue;
if (next != c && next->mon && VISIBLEON(next, c->mon)) if (next != c && next->mon && VISIBLEON(next, c->mon))
return next; return next;
} }
@ -414,25 +509,25 @@ Client *get_next_stack_client(Client *c, bool reverse) {
float *get_border_color(Client *c) { float *get_border_color(Client *c) {
if (c->mon != selmon) { if (c->mon != selmon) {
return bordercolor; return config.bordercolor;
} else if (c->isurgent) { } else if (c->isurgent) {
return urgentcolor; return config.urgentcolor;
} else if (c->is_in_scratchpad && selmon && c == selmon->sel) { } else if (c->is_in_scratchpad && selmon && c == selmon->sel) {
return scratchpadcolor; return config.scratchpadcolor;
} else if (c->isglobal && selmon && c == selmon->sel) { } else if (c->isglobal && selmon && c == selmon->sel) {
return globalcolor; return config.globalcolor;
} else if (c->isoverlay && selmon && c == selmon->sel) { } else if (c->isoverlay && selmon && c == selmon->sel) {
return overlaycolor; return config.overlaycolor;
} else if (c->ismaximizescreen && selmon && c == selmon->sel) { } else if (c->ismaximizescreen && selmon && c == selmon->sel) {
return maximizescreencolor; return config.maximizescreencolor;
} else if (selmon && c == selmon->sel) { } else if (selmon && c == selmon->sel) {
return focuscolor; return config.focuscolor;
} else { } else {
return bordercolor; return config.bordercolor;
} }
} }
int is_single_bit_set(uint32_t x) { return x && !(x & (x - 1)); } int32_t is_single_bit_set(uint32_t x) { return x && !(x & (x - 1)); }
bool client_only_in_one_tag(Client *c) { bool client_only_in_one_tag(Client *c) {
uint32_t masked = c->tags & TAGMASK; uint32_t masked = c->tags & TAGMASK;
@ -442,3 +537,96 @@ bool client_only_in_one_tag(Client *c) {
return false; return false;
} }
} }
Client *get_scroll_stack_head(Client *c) {
Client *scroller_stack_head = c;
if (!scroller_stack_head)
return NULL;
while (scroller_stack_head->prev_in_stack) {
scroller_stack_head = scroller_stack_head->prev_in_stack;
}
return scroller_stack_head;
}
bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
if (!sc || !tc)
return false;
uint32_t id = sc->mon->pertag->ltidxs[sc->mon->pertag->curtag]->id;
if (id != SCROLLER && id != VERTICAL_SCROLLER && id != TILE &&
id != VERTICAL_TILE && id != DECK && id != VERTICAL_DECK &&
id != CENTER_TILE && id != RIGHT_TILE && id != TGMIX)
return false;
if (id == SCROLLER || id == VERTICAL_SCROLLER) {
Client *source_stack_head = get_scroll_stack_head(sc);
Client *target_stack_head = get_scroll_stack_head(tc);
Client *fc_head = fc ? get_scroll_stack_head(fc) : NULL;
if (fc && fc->prev_in_stack && fc_head == source_stack_head)
return false;
if (source_stack_head == target_stack_head)
return true;
else
return false;
}
if (id == TILE || id == VERTICAL_TILE || id == DECK ||
id == VERTICAL_DECK || id == RIGHT_TILE) {
if (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
else
return true;
}
if (id == TGMIX) {
if (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
if (!sc->ismaster && sc->mon->visible_tiling_clients <= 3)
return true;
}
if (id == CENTER_TILE) {
if (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
if (sc->geom.x == tc->geom.x)
return true;
else
return false;
}
return false;
}
Client *get_focused_stack_client(Client *sc) {
if (!sc || sc->isfloating)
return sc;
Client *tc = NULL;
Client *fc = focustop(sc->mon);
if (fc->isfloating || sc->isfloating)
return sc;
wl_list_for_each(tc, &fstack, flink) {
if (tc->iskilling || tc->isunglobal)
continue;
if (!VISIBLEON(tc, sc->mon))
continue;
if (tc == fc)
continue;
if (client_is_in_same_stack(sc, tc, fc)) {
return tc;
}
}
return sc;
}

View file

@ -19,11 +19,11 @@ pid_t getparentprocess(pid_t p) {
return (pid_t)v; return (pid_t)v;
} }
int isdescprocess(pid_t p, pid_t c) { int32_t isdescprocess(pid_t p, pid_t c) {
while (p != c && c != 0) while (p != c && c != 0)
c = getparentprocess(c); c = getparentprocess(c);
return (int)c; return (int32_t)c;
} }
void get_layout_abbr(char *abbr, const char *full_name) { void get_layout_abbr(char *abbr, const char *full_name) {
@ -31,7 +31,7 @@ void get_layout_abbr(char *abbr, const char *full_name) {
abbr[0] = '\0'; abbr[0] = '\0';
// 1. 尝试在映射表中查找 // 1. 尝试在映射表中查找
for (int i = 0; layout_mappings[i].full_name != NULL; i++) { for (int32_t i = 0; layout_mappings[i].full_name != NULL; i++) {
if (strcmp(full_name, layout_mappings[i].full_name) == 0) { if (strcmp(full_name, layout_mappings[i].full_name) == 0) {
strcpy(abbr, layout_mappings[i].abbr); strcpy(abbr, layout_mappings[i].abbr);
return; return;
@ -83,7 +83,7 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
Client *c = NULL; Client *c = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
int layer; int32_t layer;
for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
@ -100,10 +100,6 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
surface = wlr_scene_surface_try_from_buffer( surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node)) wlr_scene_buffer_from_node(node))
->surface; ->surface;
else if (node->type == WLR_SCENE_NODE_RECT) {
surface = NULL;
break;
}
/* start from the topmost layer, /* start from the topmost layer,
find a sureface that can be focused by pointer, find a sureface that can be focused by pointer,
@ -119,6 +115,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
l = pnode->data; l = pnode->data;
} }
} }
if (node->type == WLR_SCENE_NODE_RECT) {
if (c) {
surface = client_surface(c);
}
break;
}
} }
if (psurface) if (psurface)

View file

@ -26,6 +26,14 @@ bool is_scroller_layout(Monitor *m) {
return false; return false;
} }
bool is_centertile_layout(Monitor *m) {
if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE)
return true;
return false;
}
uint32_t get_tag_status(uint32_t tag, Monitor *m) { uint32_t get_tag_status(uint32_t tag, Monitor *m) {
Client *c = NULL; Client *c = NULL;
uint32_t status = 0; uint32_t status = 0;
@ -88,3 +96,81 @@ Monitor *xytomon(double x, double y) {
struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
return o ? o->data : NULL; return o ? o->data : NULL;
} }
Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) {
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
bool match_monitor_spec(char *spec, Monitor *m) {
if (!spec || !m)
return false;
// if the spec does not contain a colon, treat it as a match on the monitor
// name
if (strchr(spec, ':') == NULL) {
return regex_match(spec, m->wlr_output->name);
}
char *spec_copy = strdup(spec);
if (!spec_copy)
return false;
char *name_rule = NULL;
char *make_rule = NULL;
char *model_rule = NULL;
char *serial_rule = NULL;
char *token = strtok(spec_copy, "&&");
while (token) {
char *colon = strchr(token, ':');
if (colon) {
*colon = '\0';
char *key = token;
char *value = colon + 1;
if (strcmp(key, "name") == 0)
name_rule = strdup(value);
else if (strcmp(key, "make") == 0)
make_rule = strdup(value);
else if (strcmp(key, "model") == 0)
model_rule = strdup(value);
else if (strcmp(key, "serial") == 0)
serial_rule = strdup(value);
}
token = strtok(NULL, "&&");
}
bool match = true;
if (name_rule) {
if (!regex_match(name_rule, m->wlr_output->name))
match = false;
}
if (make_rule) {
if (!m->wlr_output->make || strcmp(make_rule, m->wlr_output->make) != 0)
match = false;
}
if (model_rule) {
if (!m->wlr_output->model ||
strcmp(model_rule, m->wlr_output->model) != 0)
match = false;
}
if (serial_rule) {
if (!m->wlr_output->serial ||
strcmp(serial_rule, m->wlr_output->serial) != 0)
match = false;
}
free(spec_copy);
free(name_rule);
free(make_rule);
free(model_rule);
free(serial_rule);
return match;
}

View file

@ -1,8 +1,99 @@
void save_old_size_per(Monitor *m) {
Client *c = NULL;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
}
}
}
void restore_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
if (!m || !c)
return;
if (!m->wlr_output->enabled)
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc)) {
fc->old_ismaster = fc->ismaster;
}
}
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
pre_caculate_before_arrange(m, false, false, true);
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
if (current_layout->id == SCROLLER ||
current_layout->id == VERTICAL_SCROLLER || current_layout->id == GRID ||
current_layout->id == VERTICAL_GRID || current_layout->id == DECK ||
current_layout->id == VERTICAL_DECK || current_layout->id == MONOCLE) {
return;
}
if (current_layout->id == CENTER_TILE) {
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) {
set_size_per(m, fc);
}
}
return;
}
// it is possible that the current floating window is moved to another tag,
// but the tag has not executed save_old_size_per
// so it must be judged whether their old size values are initial values
if (!c->ismaster && c->old_stack_inner_per < 1.0 &&
c->old_stack_inner_per > 0.0f && c->stack_inner_per < 1.0 &&
c->stack_inner_per > 0.0f) {
c->stack_inner_per = (1.0 - c->stack_inner_per) *
c->old_stack_inner_per /
(1.0 - c->old_stack_inner_per);
}
if (c->ismaster && c->old_master_inner_per < 1.0 &&
c->old_master_inner_per > 0.0f && c->master_inner_per < 1.0 &&
c->master_inner_per > 0.0f) {
c->master_inner_per = (1.0 - c->master_inner_per) *
c->old_master_inner_per /
(1.0 - c->old_master_inner_per);
}
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c && !fc->ismaster &&
fc->old_ismaster && fc->old_stack_inner_per < 1.0 &&
fc->old_stack_inner_per > 0.0f && fc->stack_inner_per < 1.0 &&
fc->stack_inner_per > 0.0f) {
fc->stack_inner_per = (1.0 - fc->stack_inner_per) *
fc->old_stack_inner_per /
(1.0 - fc->old_stack_inner_per);
fc->old_ismaster = false;
}
}
}
void set_size_per(Monitor *m, Client *c) { void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL; Client *fc = NULL;
bool found = false; bool found = false;
if (!m || !c)
return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
wl_list_for_each(fc, &clients, link) { wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) {
if (current_layout->id == CENTER_TILE &&
(fc->isleftstack ^ c->isleftstack))
continue;
c->master_mfact_per = fc->master_mfact_per; c->master_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per; c->master_inner_per = fc->master_inner_per;
c->stack_inner_per = fc->stack_inner_per; c->stack_inner_per = fc->stack_inner_per;
@ -18,8 +109,9 @@ void set_size_per(Monitor *m, Client *c) {
} }
} }
void resize_tile_master_horizontal(Client *grabc, bool isdrag, int offsetx, void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
int offsety, uint32_t time, int type) { int32_t offsety, uint32_t time,
int32_t type) {
Client *tc = NULL; Client *tc = NULL;
float delta_x, delta_y; float delta_x, delta_y;
Client *next = NULL; Client *next = NULL;
@ -38,8 +130,7 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int offsetx,
break; break;
} }
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
next = tc; next = tc;
begin_find_nextnext = true; begin_find_nextnext = true;
continue; continue;
@ -55,8 +146,7 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int offsetx,
break; break;
} }
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
prev = tc; prev = tc;
begin_find_prevprev = true; begin_find_prevprev = true;
continue; continue;
@ -192,6 +282,23 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int offsetx,
// 应用到所有平铺窗口 // 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) { wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
if (!isdrag && tc != grabc) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f &&
(type != CENTER_TILE ||
!(grabc->isleftstack ^ tc->isleftstack)))
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
tc->master_mfact_per = new_master_mfact_per; tc->master_mfact_per = new_master_mfact_per;
} }
} }
@ -200,20 +307,20 @@ void resize_tile_master_horizontal(Client *grabc, bool isdrag, int offsetx,
grabc->stack_inner_per = new_stack_inner_per; grabc->stack_inner_per = new_stack_inner_per;
if (!isdrag) { if (!isdrag) {
arrange(grabc->mon, false); arrange(grabc->mon, false, false);
return; return;
} }
if (last_apply_drap_time == 0 || if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_refresh_interval) { time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(grabc->mon, false); arrange(grabc->mon, false, false);
last_apply_drap_time = time; last_apply_drap_time = time;
} }
} }
} }
void resize_tile_master_vertical(Client *grabc, bool isdrag, int offsetx, void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
int offsety, uint32_t time, int type) { int32_t offsety, uint32_t time, int32_t type) {
Client *tc = NULL; Client *tc = NULL;
float delta_x, delta_y; float delta_x, delta_y;
Client *next = NULL; Client *next = NULL;
@ -224,8 +331,7 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int offsetx,
for (node = grabc->link.next; node != &clients; node = node->next) { for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link); tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
next = tc; next = tc;
break; break;
} }
@ -235,8 +341,7 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int offsetx,
for (node = grabc->link.prev; node != &clients; node = node->prev) { for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link); tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
ISTILED(tc)) { // 根据你的实际字段名调整
prev = tc; prev = tc;
break; break;
} }
@ -349,6 +454,20 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int offsetx,
// 应用到所有平铺窗口 // 应用到所有平铺窗口
wl_list_for_each(tc, &clients, link) { wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) { if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
if (!isdrag && tc != grabc && type != CENTER_TILE) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f)
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
tc->master_mfact_per = new_master_mfact_per; tc->master_mfact_per = new_master_mfact_per;
} }
} }
@ -357,25 +476,28 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int offsetx,
grabc->stack_inner_per = new_stack_inner_per; grabc->stack_inner_per = new_stack_inner_per;
if (!isdrag) { if (!isdrag) {
arrange(grabc->mon, false); arrange(grabc->mon, false, false);
return; return;
} }
if (last_apply_drap_time == 0 || if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_refresh_interval) { time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(grabc->mon, false); arrange(grabc->mon, false, false);
last_apply_drap_time = time; last_apply_drap_time = time;
} }
} }
} }
void resize_tile_scroller(Client *grabc, bool isdrag, int offsetx, int offsety, void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
uint32_t time, bool isvertical) { int32_t offsety, uint32_t time, bool isvertical) {
Client *tc = NULL;
float delta_x, delta_y; float delta_x, delta_y;
float new_scroller_proportion; float new_scroller_proportion;
float new_stack_proportion;
Client *stack_head = get_scroll_stack_head(grabc);
if (grabc && grabc->mon->visible_tiling_clients == 1 && if (grabc && grabc->mon->visible_tiling_clients == 1 &&
!scroller_ignore_proportion_single) !config.scroller_ignore_proportion_single)
return; return;
if (!start_drag_window && isdrag) { if (!start_drag_window && isdrag) {
@ -384,7 +506,8 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int offsetx, int offsety,
start_drag_window = true; start_drag_window = true;
// 记录初始状态 // 记录初始状态
grabc->old_scroller_pproportion = grabc->scroller_proportion; stack_head->old_scroller_pproportion = stack_head->scroller_proportion;
grabc->old_stack_proportion = grabc->stack_proportion;
grabc->cursor_in_left_half = grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2; cursor->x < grabc->geom.x + grabc->geom.width / 2;
@ -404,15 +527,26 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int offsetx, int offsety,
grabc->old_master_inner_per = grabc->master_inner_per; grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per; grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom; grabc->drag_begin_geom = grabc->geom;
grabc->old_scroller_pproportion = grabc->scroller_proportion; stack_head->old_scroller_pproportion =
stack_head->scroller_proportion;
grabc->old_stack_proportion = grabc->stack_proportion;
grabc->cursor_in_upper_half = false; grabc->cursor_in_upper_half = false;
grabc->cursor_in_left_half = false; grabc->cursor_in_left_half = false;
} }
delta_x = (float)(offsetx) * (grabc->old_scroller_pproportion) / if (isvertical) {
grabc->drag_begin_geom.width; delta_y = (float)(offsety) *
delta_y = (float)(offsety) * (grabc->old_scroller_pproportion) / (stack_head->old_scroller_pproportion) /
grabc->drag_begin_geom.height; grabc->drag_begin_geom.height;
delta_x = (float)(offsetx) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.width;
} else {
delta_x = (float)(offsetx) *
(stack_head->old_scroller_pproportion) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.height;
}
bool moving_up; bool moving_up;
bool moving_down; bool moving_down;
@ -447,34 +581,107 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int offsetx, int offsety,
delta_x = -fabsf(delta_x); delta_x = -fabsf(delta_x);
} }
if (isvertical) {
if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) {
delta_x = delta_x * -1.0f;
}
if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) {
if (moving_right) {
delta_x = -fabsf(delta_x);
} else {
delta_x = fabsf(delta_x);
}
}
if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) {
if (moving_left) {
delta_x = -fabsf(delta_x);
} else {
delta_x = fabsf(delta_x);
}
}
if (isdrag) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
}
} else {
if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) {
delta_y = delta_y * -1.0f;
}
if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) {
if (moving_down) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
}
if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) {
if (moving_up) {
delta_y = -fabsf(delta_y);
} else {
delta_y = fabsf(delta_y);
}
}
if (isdrag) {
if (moving_left) {
delta_x = -fabsf(delta_x);
} else {
delta_x = fabsf(delta_x);
}
}
}
// 直接设置新的比例,基于初始值 + 变化量 // 直接设置新的比例,基于初始值 + 变化量
if (isvertical) { if (isvertical) {
new_scroller_proportion = grabc->old_scroller_pproportion + delta_y; new_scroller_proportion =
stack_head->old_scroller_pproportion + delta_y;
new_stack_proportion = grabc->old_stack_proportion + delta_x;
} else { } else {
new_scroller_proportion = grabc->old_scroller_pproportion + delta_x; new_scroller_proportion =
stack_head->old_scroller_pproportion + delta_x;
new_stack_proportion = grabc->old_stack_proportion + delta_y;
} }
// 应用限制,确保比例在合理范围内 // 应用限制,确保比例在合理范围内
new_scroller_proportion = new_scroller_proportion =
fmaxf(0.1f, fminf(1.0f, new_scroller_proportion)); fmaxf(0.1f, fminf(1.0f, new_scroller_proportion));
new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion));
grabc->scroller_proportion = new_scroller_proportion; grabc->stack_proportion = new_stack_proportion;
stack_head->scroller_proportion = new_scroller_proportion;
wl_list_for_each(tc, &clients, link) {
if (!isdrag && new_stack_proportion != 1.0f &&
grabc->old_stack_proportion != 1.0f && tc != grabc &&
ISTILED(tc) && get_scroll_stack_head(tc) == stack_head) {
tc->stack_proportion = (1.0f - new_stack_proportion) /
(1.0f - grabc->old_stack_proportion) *
tc->stack_proportion;
}
}
if (!isdrag) { if (!isdrag) {
arrange(grabc->mon, false); arrange(grabc->mon, false, false);
return; return;
} }
if (last_apply_drap_time == 0 || if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > drag_refresh_interval) { time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(grabc->mon, false); arrange(grabc->mon, false, false);
last_apply_drap_time = time; last_apply_drap_time = time;
} }
} }
} }
void resize_tile_client(Client *grabc, bool isdrag, int offsetx, int offsety, void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx,
uint32_t time) { int32_t offsety, uint32_t time) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen) if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return; return;
@ -502,14 +709,26 @@ void resize_tile_client(Client *grabc, bool isdrag, int offsetx, int offsety,
} }
} }
void reset_size_per_mon(Monitor *m, int tile_cilent_num, /* If there are no calculation omissions,
these two functions will never be triggered.
Just in case to facilitate the final investigation*/
void check_size_per_valid(Client *c) {
if (c->ismaster) {
assert(c->master_inner_per > 0.0f && c->master_inner_per <= 1.0f);
} else {
assert(c->stack_inner_per > 0.0f && c->stack_inner_per <= 1.0f);
}
}
void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
double total_left_stack_hight_percent, double total_left_stack_hight_percent,
double total_right_stack_hight_percent, double total_right_stack_hight_percent,
double total_stack_hight_percent, double total_stack_hight_percent,
double total_master_inner_percent, int master_num, double total_master_inner_percent, int32_t master_num,
int stack_num) { int32_t stack_num) {
Client *c = NULL; Client *c = NULL;
int i = 0; int32_t i = 0;
uint32_t stack_index = 0; uint32_t stack_index = 0;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag]; uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
@ -517,6 +736,7 @@ void reset_size_per_mon(Monitor *m, int tile_cilent_num,
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) { if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) { if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true; c->ismaster = true;
c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f; c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f;
@ -532,17 +752,20 @@ void reset_size_per_mon(Monitor *m, int tile_cilent_num,
: 1.0f; : 1.0f;
} }
i++; i++;
check_size_per_valid(c);
} }
} }
} else { } else {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) { if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) { if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true; c->ismaster = true;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) { if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
c->stack_inner_per = c->stack_inner_per =
stack_num > 1 ? 1.0f / ((stack_num - 1) / 2) : 1.0f; stack_num > 1 ? 1.0f / ((stack_num - 1) / 2.0f)
: 1.0f;
} else { } else {
c->stack_inner_per = c->stack_inner_per =
stack_num > 1 ? 2.0f / stack_num : 1.0f; stack_num > 1 ? 2.0f / stack_num : 1.0f;
@ -571,59 +794,53 @@ void reset_size_per_mon(Monitor *m, int tile_cilent_num,
} }
} }
i++; i++;
check_size_per_valid(c);
} }
} }
} }
} }
void reset_multi_tag_client_per(Monitor *m) { void pre_caculate_before_arrange(Monitor *m, bool want_animation,
Client *c = NULL; bool from_view, bool only_caculate) {
wl_list_for_each(c, &clients, link) {
if (c->isglobal || c->isunglobal) {
set_size_per(m, c);
}
if (!VISIBLEON(c, m))
continue;
if (!client_only_in_one_tag(c)) {
set_size_per(m, c);
}
}
}
void // 17
arrange(Monitor *m, bool want_animation) {
Client *c = NULL; Client *c = NULL;
double total_stack_inner_percent = 0; double total_stack_inner_percent = 0;
double total_master_inner_percent = 0; double total_master_inner_percent = 0;
double total_right_stack_hight_percent = 0; double total_right_stack_hight_percent = 0;
double total_left_stack_hight_percent = 0; double total_left_stack_hight_percent = 0;
int i = 0; int32_t i = 0;
int nmasters = 0; int32_t nmasters = 0;
int stack_index = 0; int32_t stack_index = 0;
int master_num = 0; int32_t master_num = 0;
int stack_num = 0; int32_t stack_num = 0;
if (!m)
return;
if (!m->wlr_output->enabled)
return;
m->visible_clients = 0; m->visible_clients = 0;
m->visible_tiling_clients = 0; m->visible_tiling_clients = 0;
m->visible_scroll_tiling_clients = 0; m->visible_scroll_tiling_clients = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) {
exit_scroller_stack(c);
}
if (from_view && (c->isglobal || c->isunglobal)) {
set_size_per(m, c);
}
if (c->mon == m && (c->isglobal || c->isunglobal)) { if (c->mon == m && (c->isglobal || c->isunglobal)) {
c->tags = m->tagset[m->seltags]; c->tags = m->tagset[m->seltags];
if (c->mon->sel == NULL) }
focusclient(c, 0);
if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) {
focusclient(c, 1);
} }
if (VISIBLEON(c, m)) { if (VISIBLEON(c, m)) {
if (from_view && !client_only_in_one_tag(c)) {
set_size_per(m, c);
}
if (!c->isunglobal) if (!c->isunglobal)
m->visible_clients++; m->visible_clients++;
@ -631,7 +848,7 @@ arrange(Monitor *m, bool want_animation) {
m->visible_tiling_clients++; m->visible_tiling_clients++;
} }
if (ISSCROLLTILED(c)) { if (ISSCROLLTILED(c) && !c->prev_in_stack) {
m->visible_scroll_tiling_clients++; m->visible_scroll_tiling_clients++;
} }
} }
@ -669,14 +886,17 @@ arrange(Monitor *m, bool want_animation) {
i++; i++;
} }
if (!only_caculate)
set_arrange_visible(m, c, want_animation); set_arrange_visible(m, c, want_animation);
} else { } else {
if (!only_caculate)
set_arrange_hidden(m, c, want_animation); set_arrange_hidden(m, c, want_animation);
} }
} }
if (c->mon == m && c->ismaximizescreen && !c->animation.tagouted && if (!only_caculate && c->mon == m && c->ismaximizescreen &&
!c->animation.tagouting && VISIBLEON(c, m)) { !c->animation.tagouted && !c->animation.tagouting &&
VISIBLEON(c, m)) {
reset_maximizescreen_size(c); reset_maximizescreen_size(c);
} }
} }
@ -685,6 +905,18 @@ arrange(Monitor *m, bool want_animation) {
m, m->visible_tiling_clients, total_left_stack_hight_percent, m, m->visible_tiling_clients, total_left_stack_hight_percent,
total_right_stack_hight_percent, total_stack_inner_percent, total_right_stack_hight_percent, total_stack_inner_percent,
total_master_inner_percent, master_num, stack_num); total_master_inner_percent, master_num, stack_num);
}
void // 17
arrange(Monitor *m, bool want_animation, bool from_view) {
if (!m)
return;
if (!m->wlr_output->enabled)
return;
pre_caculate_before_arrange(m, want_animation, from_view, false);
if (m->isoverview) { if (m->isoverview) {
overviewlayout.arrange(m); overviewlayout.arrange(m);

View file

@ -1,13 +1,15 @@
// 网格布局窗口大小和位置计算 // 网格布局窗口大小和位置计算
void grid(Monitor *m) { void grid(Monitor *m) {
uint32_t i, n; int32_t i, n;
uint32_t cx, cy, cw, ch; int32_t cx, cy, cw, ch;
uint32_t dx; int32_t dx;
uint32_t cols, rows, overcols; int32_t cols, rows, overcols;
Client *c = NULL; Client *c = NULL;
n = 0; n = 0;
int target_gappo = enablegaps ? m->isoverview ? overviewgappo : gappoh : 0; int32_t target_gappo =
int target_gappi = enablegaps ? m->isoverview ? overviewgappi : gappih : 0; enablegaps ? m->isoverview ? config.overviewgappo : config.gappoh : 0;
int32_t target_gappi =
enablegaps ? m->isoverview ? config.overviewgappi : config.gappih : 0;
float single_width_ratio = m->isoverview ? 0.7 : 0.9; float single_width_ratio = m->isoverview ? 0.7 : 0.9;
float single_height_ratio = m->isoverview ? 0.8 : 0.9; float single_height_ratio = m->isoverview ? 0.8 : 0.9;
@ -110,19 +112,23 @@ void grid(Monitor *m) {
} }
void deck(Monitor *m) { void deck(Monitor *m) {
uint32_t mw, my; int32_t mw, my;
int i, n = 0; int32_t i, n = 0;
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
float mfact; float mfact;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
uint32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; cur_gappih =
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
n = m->visible_tiling_clients; n = m->visible_tiling_clients;
@ -140,8 +146,8 @@ void deck(Monitor *m) {
: m->pertag->mfacts[m->pertag->curtag]; : m->pertag->mfacts[m->pertag->curtag];
// Calculate master width including outer gaps // Calculate master width including outer gaps
if (n > m->nmaster) if (n > nmasters)
mw = m->nmaster ? round((m->w.width - 2 * cur_gappoh) * mfact) : 0; mw = nmasters ? round((m->w.width - 2 * cur_gappoh) * mfact) : 0;
else else
mw = m->w.width - 2 * cur_gappoh; mw = m->w.width - 2 * cur_gappoh;
@ -149,7 +155,7 @@ void deck(Monitor *m) {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c))
continue; continue;
if (i < m->nmaster) { if (i < nmasters) {
c->master_mfact_per = mfact; c->master_mfact_per = mfact;
// Master area clients // Master area clients
resize( resize(
@ -158,7 +164,7 @@ void deck(Monitor *m) {
.y = m->w.y + cur_gappov + my, .y = m->w.y + cur_gappov + my,
.width = mw, .width = mw,
.height = (m->w.height - 2 * cur_gappov - my) / .height = (m->w.height - 2 * cur_gappov - my) /
(MIN(n, m->nmaster) - i)}, (MIN(n, nmasters) - i)},
0); 0);
my += c->geom.height; my += c->geom.height;
} else { } else {
@ -181,16 +187,19 @@ void deck(Monitor *m) {
void horizontal_scroll_adjust_fullandmax(Client *c, void horizontal_scroll_adjust_fullandmax(Client *c,
struct wlr_box *target_geom) { struct wlr_box *target_geom) {
Monitor *m = c->mon; Monitor *m = c->mon;
uint32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
cur_gappih = cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih; ? 0
cur_gappoh = : cur_gappih;
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
cur_gappov = ? 0
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; : cur_gappoh;
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
? 0
: cur_gappov;
if (c->isfullscreen) { if (c->isfullscreen) {
target_geom->height = m->m.height; target_geom->height = m->m.height;
@ -210,28 +219,94 @@ void horizontal_scroll_adjust_fullandmax(Client *c,
target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2; target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2;
} }
void arrange_stack(Client *scroller_stack_head, struct wlr_box geometry,
int32_t gappiv) {
int32_t stack_size = 0;
Client *iter = scroller_stack_head;
while (iter) {
stack_size++;
iter = iter->next_in_stack;
}
if (stack_size == 0)
return;
float total_proportion = 0.0f;
iter = scroller_stack_head;
while (iter) {
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) {
iter->stack_proportion =
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
}
total_proportion += iter->stack_proportion;
iter = iter->next_in_stack;
}
iter = scroller_stack_head;
while (iter) {
iter->stack_proportion = iter->stack_proportion / total_proportion;
iter = iter->next_in_stack;
}
int32_t client_height;
int32_t current_y = geometry.y;
int32_t remain_client_height = geometry.height - (stack_size - 1) * gappiv;
float remain_proportion = 1.0f;
iter = scroller_stack_head;
while (iter) {
client_height =
remain_client_height * (iter->stack_proportion / remain_proportion);
struct wlr_box client_geom = {.x = geometry.x,
.y = current_y,
.width = geometry.width,
.height = client_height};
resize(iter, client_geom, 0);
remain_proportion -= iter->stack_proportion;
remain_client_height -= client_height;
current_y += client_height + gappiv;
iter = iter->next_in_stack;
}
}
void horizontal_check_scroller_root_inside_mon(Client *c,
struct wlr_box *geometry) {
if (!GEOMINSIDEMON(geometry, c->mon)) {
geometry->x = c->mon->w.x + (c->mon->w.width - geometry->width) / 2;
}
}
// 滚动布局 // 滚动布局
void scroller(Monitor *m) { void scroller(Monitor *m) {
uint32_t i, n, j; int32_t i, n, j;
float single_proportion = 1.0; float single_proportion = 1.0;
Client *c = NULL, *root_client = NULL; Client *c = NULL, *root_client = NULL;
Client **tempClients = NULL; // 初始化为 NULL Client **tempClients = NULL; // 初始化为 NULL
struct wlr_box target_geom; struct wlr_box target_geom;
int focus_client_index = 0; int32_t focus_client_index = 0;
bool need_scroller = false; bool need_scroller = false;
uint32_t cur_gappih = enablegaps ? m->gappih : 0; bool over_overspread_to_left = false;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
cur_gappih = cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih; ? 0
cur_gappoh = : cur_gappih;
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
cur_gappov = ? 0
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; : cur_gappoh;
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
? 0
: cur_gappov;
uint32_t max_client_width = m->w.width - 2 * scroller_structs - cur_gappih; int32_t max_client_width =
m->w.width - 2 * config.scroller_structs - cur_gappih;
n = m->visible_scroll_tiling_clients; n = m->visible_scroll_tiling_clients;
@ -249,25 +324,26 @@ void scroller(Monitor *m) {
// 第二次遍历,填充 tempClients // 第二次遍历,填充 tempClients
j = 0; j = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
tempClients[j] = c; tempClients[j] = c;
j++; j++;
} }
} }
if (n == 1 && !scroller_ignore_proportion_single && if (n == 1 && !config.scroller_ignore_proportion_single &&
!tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) { !tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) {
c = tempClients[0]; c = tempClients[0];
single_proportion = c->scroller_proportion_single > 0.0f single_proportion = c->scroller_proportion_single > 0.0f
? c->scroller_proportion_single ? c->scroller_proportion_single
: scroller_default_proportion_single; : config.scroller_default_proportion_single;
target_geom.height = m->w.height - 2 * cur_gappov; target_geom.height = m->w.height - 2 * cur_gappov;
target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion; target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion;
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
resize(c, target_geom, 0); horizontal_check_scroller_root_inside_mon(c, &target_geom);
arrange_stack(c, target_geom, cur_gappiv);
free(tempClients); // 释放内存 free(tempClients); // 释放内存
return; return;
} }
@ -281,6 +357,11 @@ void scroller(Monitor *m) {
root_client = center_tiled_select(m); root_client = center_tiled_select(m);
} }
// root_client might be in a stack, find the stack head
if (root_client) {
root_client = get_scroll_stack_head(root_client);
}
if (!root_client) { if (!root_client) {
free(tempClients); // 释放内存 free(tempClients); // 释放内存
return; return;
@ -289,9 +370,9 @@ void scroller(Monitor *m) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
c = tempClients[i]; c = tempClients[i];
if (root_client == c) { if (root_client == c) {
if (c->geom.x >= m->w.x + scroller_structs && if (c->geom.x >= m->w.x + config.scroller_structs &&
c->geom.x + c->geom.width <= c->geom.x + c->geom.width <=
m->w.x + m->w.width - scroller_structs) { m->w.x + m->w.width - config.scroller_structs) {
need_scroller = false; need_scroller = false;
} else { } else {
need_scroller = true; need_scroller = true;
@ -301,7 +382,48 @@ void scroller(Monitor *m) {
} }
} }
if (n == 1 && scroller_ignore_proportion_single) { bool need_apply_overspread =
config.scroller_prefer_overspread &&
m->visible_scroll_tiling_clients > 1 &&
(focus_client_index == 0 || focus_client_index == n - 1) &&
tempClients[focus_client_index]->scroller_proportion < 1.0f;
if (need_apply_overspread) {
if (focus_client_index == 0) {
over_overspread_to_left = true;
} else {
over_overspread_to_left = false;
}
if (over_overspread_to_left &&
(!INSIDEMON(tempClients[1]) ||
(tempClients[1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else if (!over_overspread_to_left &&
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
(tempClients[focus_client_index - 1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else {
need_apply_overspread = false;
}
}
bool need_apply_center =
config.scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
(config.scroller_prefer_center && !need_apply_overspread &&
(!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_width) +
(tempClients[focus_client_index]->scroller_proportion *
max_client_width) >
m->w.width - 2 * config.scroller_structs - cur_gappih)));
if (n == 1 && config.scroller_ignore_proportion_single) {
need_scroller = true; need_scroller = true;
} }
@ -315,31 +437,47 @@ void scroller(Monitor *m) {
&target_geom); &target_geom);
if (tempClients[focus_client_index]->isfullscreen) { if (tempClients[focus_client_index]->isfullscreen) {
target_geom.x = m->m.x; target_geom.x = m->m.x;
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else if (tempClients[focus_client_index]->ismaximizescreen) { } else if (tempClients[focus_client_index]->ismaximizescreen) {
target_geom.x = m->w.x + cur_gappoh; target_geom.x = m->w.x + cur_gappoh;
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else if (need_scroller) { } else if (need_scroller) {
if (scroller_focus_center || if (need_apply_center) {
((!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_width) +
(root_client->scroller_proportion * max_client_width) >
m->w.width - 2 * scroller_structs - cur_gappih)) &&
scroller_prefer_center)) {
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
} else if (need_apply_overspread) {
if (over_overspread_to_left) {
target_geom.x = m->w.x + config.scroller_structs;
} else { } else {
target_geom.x = root_client->geom.x > m->w.x + (m->w.width) / 2 target_geom.x =
? m->w.x + (m->w.width - m->w.x +
root_client->scroller_proportion * (m->w.width -
tempClients[focus_client_index]->scroller_proportion *
max_client_width - max_client_width -
scroller_structs) config.scroller_structs);
: m->w.x + scroller_structs;
} }
resize(tempClients[focus_client_index], target_geom, 0);
} else {
target_geom.x = tempClients[focus_client_index]->geom.x >
m->w.x + (m->w.width) / 2
? m->w.x + (m->w.width -
tempClients[focus_client_index]
->scroller_proportion *
max_client_width -
config.scroller_structs)
: m->w.x + config.scroller_structs;
}
horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else { } else {
target_geom.x = c->geom.x; target_geom.x = c->geom.x;
resize(tempClients[focus_client_index], target_geom, 0); horizontal_check_scroller_root_inside_mon(
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} }
for (i = 1; i <= focus_client_index; i++) { for (i = 1; i <= focus_client_index; i++) {
@ -349,7 +487,7 @@ void scroller(Monitor *m) {
target_geom.x = tempClients[focus_client_index - i + 1]->geom.x - target_geom.x = tempClients[focus_client_index - i + 1]->geom.x -
cur_gappih - target_geom.width; cur_gappih - target_geom.width;
resize(c, target_geom, 0); arrange_stack(c, target_geom, cur_gappiv);
} }
for (i = 1; i < n - focus_client_index; i++) { for (i = 1; i < n - focus_client_index; i++) {
@ -359,19 +497,19 @@ void scroller(Monitor *m) {
target_geom.x = tempClients[focus_client_index + i - 1]->geom.x + target_geom.x = tempClients[focus_client_index + i - 1]->geom.x +
cur_gappih + cur_gappih +
tempClients[focus_client_index + i - 1]->geom.width; tempClients[focus_client_index + i - 1]->geom.width;
resize(c, target_geom, 0); arrange_stack(c, target_geom, cur_gappiv);
} }
free(tempClients); // 最后释放内存 free(tempClients); // 最后释放内存
} }
void center_tile(Monitor *m) { void center_tile(Monitor *m) {
uint32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw; int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw;
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
double mfact = 0; double mfact = 0;
int master_num = 0; int32_t master_num = 0;
int stack_num = 0; int32_t stack_num = 0;
n = m->visible_tiling_clients; n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag]; master_num = m->pertag->nmasters[m->pertag->curtag];
@ -389,18 +527,22 @@ void center_tile(Monitor *m) {
} }
// 间隙参数处理 // 间隙参数处理
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0; // 内部垂直间隙 int32_t cur_gappiv = enablegaps ? m->gappiv : 0; // 内部垂直间隙
uint32_t cur_gappih = enablegaps ? m->gappih : 0; // 内部水平间隙 int32_t cur_gappih = enablegaps ? m->gappih : 0; // 内部水平间隙
uint32_t cur_gappov = enablegaps ? m->gappov : 0; // 外部垂直间隙 int32_t cur_gappov = enablegaps ? m->gappov : 0; // 外部垂直间隙
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; // 外部水平间隙 int32_t cur_gappoh = enablegaps ? m->gappoh : 0; // 外部水平间隙
// 智能间隙处理 // 智能间隙处理
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; cur_gappiv =
cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; cur_gappih =
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag]; int32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag]; : m->pertag->mfacts[m->pertag->curtag];
@ -411,17 +553,18 @@ void center_tile(Monitor *m) {
tw = mw; tw = mw;
// 判断是否需要主区域铺满 // 判断是否需要主区域铺满
int should_overspread = center_master_overspread && (n <= nmasters); int32_t should_overspread =
config.center_master_overspread && (n <= nmasters);
uint32_t master_surplus_height = int32_t master_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1));
float master_surplus_ratio = 1.0; float master_surplus_ratio = 1.0;
uint32_t slave_left_surplus_height = int32_t slave_left_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num / 2 - 1)); (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num / 2 - 1));
float slave_left_surplus_ratio = 1.0; float slave_left_surplus_ratio = 1.0;
uint32_t slave_right_surplus_height = int32_t slave_right_surplus_height =
(m->w.height - 2 * cur_gappov - (m->w.height - 2 * cur_gappov -
cur_gappiv * ie * ((stack_num + 1) / 2 - 1)); cur_gappiv * ie * ((stack_num + 1) / 2 - 1));
float slave_right_surplus_ratio = 1.0; float slave_right_surplus_ratio = 1.0;
@ -437,7 +580,7 @@ void center_tile(Monitor *m) {
mx = cur_gappoh + tw + cur_gappih * ie; mx = cur_gappoh + tw + cur_gappih * ie;
} else if (n - nmasters == 1) { } else if (n - nmasters == 1) {
// 单个堆叠窗口的处理 // 单个堆叠窗口的处理
if (center_when_single_stack) { if (config.center_when_single_stack) {
// stack在右边master居中左边空着 // stack在右边master居中左边空着
tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie; tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie;
mx = cur_gappoh + tw + cur_gappih * ie; // master居中 mx = cur_gappoh + tw + cur_gappih * ie; // master居中
@ -494,7 +637,7 @@ void center_tile(Monitor *m) {
my += c->geom.height + cur_gappiv * ie; my += c->geom.height + cur_gappiv * ie;
} else { } else {
// 堆叠区域窗口 // 堆叠区域窗口
uint32_t stack_index = i - nmasters; int32_t stack_index = i - nmasters;
if (n - nmasters == 1) { if (n - nmasters == 1) {
// 单个堆叠窗口 // 单个堆叠窗口
@ -513,8 +656,8 @@ void center_tile(Monitor *m) {
c->master_mfact_per = mfact; c->master_mfact_per = mfact;
} }
int stack_x; int32_t stack_x;
if (center_when_single_stack) { if (config.center_when_single_stack) {
// 放在右侧master居中时stack在右边 // 放在右侧master居中时stack在右边
stack_x = m->w.x + mx + mw + cur_gappih * ie; stack_x = m->w.x + mx + mw + cur_gappih * ie;
} else { } else {
@ -553,7 +696,7 @@ void center_tile(Monitor *m) {
c->master_mfact_per = mfact; c->master_mfact_per = mfact;
} }
int stack_x = m->w.x + mx + mw + cur_gappih * ie; int32_t stack_x = m->w.x + mx + mw + cur_gappih * ie;
resize(c, resize(c,
(struct wlr_box){.x = stack_x, (struct wlr_box){.x = stack_x,
@ -582,7 +725,7 @@ void center_tile(Monitor *m) {
c->master_mfact_per = mfact; c->master_mfact_per = mfact;
} }
int stack_x = m->w.x + cur_gappoh; int32_t stack_x = m->w.x + cur_gappoh;
resize(c, resize(c,
(struct wlr_box){.x = stack_x, (struct wlr_box){.x = stack_x,
.y = m->w.y + oty, .y = m->w.y + oty,
@ -598,12 +741,12 @@ void center_tile(Monitor *m) {
} }
void tile(Monitor *m) { void tile(Monitor *m) {
uint32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty; int32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty;
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
double mfact = 0; double mfact = 0;
int master_num = 0; int32_t master_num = 0;
int stack_num = 0; int32_t stack_num = 0;
n = m->visible_tiling_clients; n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag]; master_num = m->pertag->nmasters[m->pertag->curtag];
@ -613,15 +756,19 @@ void tile(Monitor *m) {
if (n == 0) if (n == 0)
return; return;
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
uint32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; cur_gappiv =
cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; cur_gappih =
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
wl_list_for_each(fc, &clients, link) { wl_list_for_each(fc, &clients, link) {
@ -641,11 +788,11 @@ void tile(Monitor *m) {
i = 0; i = 0;
my = ty = cur_gappov; my = ty = cur_gappov;
uint32_t master_surplus_height = int32_t master_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1));
float master_surplus_ratio = 1.0; float master_surplus_ratio = 1.0;
uint32_t slave_surplus_height = int32_t slave_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1)); (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1));
float slave_surplus_ratio = 1.0; float slave_surplus_ratio = 1.0;
@ -708,12 +855,12 @@ void tile(Monitor *m) {
} }
void right_tile(Monitor *m) { void right_tile(Monitor *m) {
uint32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty; int32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty;
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
double mfact = 0; double mfact = 0;
int master_num = 0; int32_t master_num = 0;
int stack_num = 0; int32_t stack_num = 0;
n = m->visible_tiling_clients; n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag]; master_num = m->pertag->nmasters[m->pertag->curtag];
@ -723,15 +870,19 @@ void right_tile(Monitor *m) {
if (n == 0) if (n == 0)
return; return;
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
uint32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; cur_gappiv =
cur_gappih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; cur_gappih =
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
wl_list_for_each(fc, &clients, link) { wl_list_for_each(fc, &clients, link) {
@ -751,11 +902,11 @@ void right_tile(Monitor *m) {
i = 0; i = 0;
my = ty = cur_gappov; my = ty = cur_gappov;
uint32_t master_surplus_height = int32_t master_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1)); (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1));
float master_surplus_ratio = 1.0; float master_surplus_ratio = 1.0;
uint32_t slave_surplus_height = int32_t slave_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1)); (m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1));
float slave_surplus_ratio = 1.0; float slave_surplus_ratio = 1.0;
@ -823,11 +974,13 @@ monocle(Monitor *m) {
Client *c = NULL; Client *c = NULL;
struct wlr_box geom; struct wlr_box geom;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; cur_gappoh =
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c))
@ -843,7 +996,7 @@ monocle(Monitor *m) {
} }
void tgmix(Monitor *m) { void tgmix(Monitor *m) {
uint32_t n = m->visible_tiling_clients; int32_t n = m->visible_tiling_clients;
if (n <= 3) { if (n <= 3) {
tile(m); tile(m);
return; return;

View file

@ -17,8 +17,8 @@ static void tgmix(Monitor *m);
Layout overviewlayout = {"󰃇", overview, "overview"}; Layout overviewlayout = {"󰃇", overview, "overview"};
enum { enum {
SCROLLER,
TILE, TILE,
SCROLLER,
GRID, GRID,
MONOCLE, MONOCLE,
DECK, DECK,
@ -34,8 +34,8 @@ enum {
Layout layouts[] = { Layout layouts[] = {
// 最少两个,不能删除少于两个 // 最少两个,不能删除少于两个
/* symbol arrange function name */ /* symbol arrange function name */
{"S", scroller, "scroller", SCROLLER}, // 滚动布局
{"T", tile, "tile", TILE}, // 平铺布局 {"T", tile, "tile", TILE}, // 平铺布局
{"S", scroller, "scroller", SCROLLER}, // 滚动布局
{"G", grid, "grid", GRID}, // 格子布局 {"G", grid, "grid", GRID}, // 格子布局
{"M", monocle, "monocle", MONOCLE}, // 单屏布局 {"M", monocle, "monocle", MONOCLE}, // 单屏布局
{"K", deck, "deck", DECK}, // 卡片布局 {"K", deck, "deck", DECK}, // 卡片布局

View file

@ -1,10 +1,10 @@
void vertical_tile(Monitor *m) { void vertical_tile(Monitor *m) {
uint32_t i, n = 0, w, r, ie = enablegaps, mh, mx, tx; int32_t i, n = 0, w, r, ie = enablegaps, mh, mx, tx;
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
double mfact = 0; double mfact = 0;
int master_num = 0; int32_t master_num = 0;
int stack_num = 0; int32_t stack_num = 0;
n = m->visible_tiling_clients; n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag]; master_num = m->pertag->nmasters[m->pertag->curtag];
@ -14,15 +14,19 @@ void vertical_tile(Monitor *m) {
if (n == 0) if (n == 0)
return; return;
uint32_t cur_gapih = enablegaps ? m->gappih : 0; int32_t cur_gapih = enablegaps ? m->gappih : 0;
uint32_t cur_gapiv = enablegaps ? m->gappiv : 0; int32_t cur_gapiv = enablegaps ? m->gappiv : 0;
uint32_t cur_gapoh = enablegaps ? m->gappoh : 0; int32_t cur_gapoh = enablegaps ? m->gappoh : 0;
uint32_t cur_gapov = enablegaps ? m->gappov : 0; int32_t cur_gapov = enablegaps ? m->gappov : 0;
cur_gapih = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapih; cur_gapih =
cur_gapiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapiv; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapih;
cur_gapoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapoh; cur_gapiv =
cur_gapov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapov; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapiv;
cur_gapoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapoh;
cur_gapov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gapov;
wl_list_for_each(fc, &clients, link) { wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc)) if (VISIBLEON(fc, m) && ISTILED(fc))
@ -42,11 +46,11 @@ void vertical_tile(Monitor *m) {
i = 0; i = 0;
mx = tx = cur_gapoh; mx = tx = cur_gapoh;
uint32_t master_surplus_width = int32_t master_surplus_width =
(m->w.width - 2 * cur_gapoh - cur_gapih * ie * (master_num - 1)); (m->w.width - 2 * cur_gapoh - cur_gapih * ie * (master_num - 1));
float master_surplus_ratio = 1.0; float master_surplus_ratio = 1.0;
uint32_t slave_surplus_width = int32_t slave_surplus_width =
(m->w.width - 2 * cur_gapoh - cur_gapih * ie * (stack_num - 1)); (m->w.width - 2 * cur_gapoh - cur_gapih * ie * (stack_num - 1));
float slave_surplus_ratio = 1.0; float slave_surplus_ratio = 1.0;
@ -105,19 +109,23 @@ void vertical_tile(Monitor *m) {
} }
void vertical_deck(Monitor *m) { void vertical_deck(Monitor *m) {
uint32_t mh, mx; int32_t mh, mx;
int i, n = 0; int32_t i, n = 0;
Client *c = NULL; Client *c = NULL;
Client *fc = NULL; Client *fc = NULL;
float mfact; float mfact;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
cur_gappiv = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv; cur_gappiv =
cur_gappoh = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh; config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappov = smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov; cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
n = m->visible_tiling_clients; n = m->visible_tiling_clients;
@ -134,8 +142,8 @@ void vertical_deck(Monitor *m) {
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag]; : m->pertag->mfacts[m->pertag->curtag];
if (n > m->nmaster) if (n > nmasters)
mh = m->nmaster ? round((m->w.height - 2 * cur_gappov) * mfact) : 0; mh = nmasters ? round((m->w.height - 2 * cur_gappov) * mfact) : 0;
else else
mh = m->w.height - 2 * cur_gappov; mh = m->w.height - 2 * cur_gappov;
@ -143,13 +151,13 @@ void vertical_deck(Monitor *m) {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c))
continue; continue;
if (i < m->nmaster) { if (i < nmasters) {
resize( resize(
c, c,
(struct wlr_box){.x = m->w.x + cur_gappoh + mx, (struct wlr_box){.x = m->w.x + cur_gappoh + mx,
.y = m->w.y + cur_gappov, .y = m->w.y + cur_gappov,
.width = (m->w.width - 2 * cur_gappoh - mx) / .width = (m->w.width - 2 * cur_gappoh - mx) /
(MIN(n, m->nmaster) - i), (MIN(n, nmasters) - i),
.height = mh}, .height = mh},
0); 0);
mx += c->geom.width; mx += c->geom.width;
@ -170,16 +178,19 @@ void vertical_deck(Monitor *m) {
void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) { void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
Monitor *m = c->mon; Monitor *m = c->mon;
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv; ? 0
cur_gappov = : cur_gappiv;
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
cur_gappoh = ? 0
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; : cur_gappov;
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
? 0
: cur_gappoh;
if (c->isfullscreen) { if (c->isfullscreen) {
target_geom->width = m->m.width; target_geom->width = m->m.width;
@ -199,29 +210,94 @@ void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2; target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2;
} }
void arrange_stack_vertical(Client *scroller_stack_head,
struct wlr_box geometry, int32_t gappih) {
int32_t stack_size = 0;
Client *iter = scroller_stack_head;
while (iter) {
stack_size++;
iter = iter->next_in_stack;
}
if (stack_size == 0)
return;
float total_proportion = 0.0f;
iter = scroller_stack_head;
while (iter) {
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) {
iter->stack_proportion =
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
}
total_proportion += iter->stack_proportion;
iter = iter->next_in_stack;
}
iter = scroller_stack_head;
while (iter) {
iter->stack_proportion = iter->stack_proportion / total_proportion;
iter = iter->next_in_stack;
}
int32_t client_width;
int32_t current_x = geometry.x;
int32_t remain_client_width = geometry.width - (stack_size - 1) * gappih;
float remain_proportion = 1.0f;
iter = scroller_stack_head;
while (iter) {
client_width =
remain_client_width * (iter->stack_proportion / remain_proportion);
struct wlr_box client_geom = {.y = geometry.y,
.x = current_x,
.height = geometry.height,
.width = client_width};
resize(iter, client_geom, 0);
remain_proportion -= iter->stack_proportion;
remain_client_width -= client_width;
current_x += client_width + gappih;
iter = iter->next_in_stack;
}
}
void vertical_check_scroller_root_inside_mon(Client *c,
struct wlr_box *geometry) {
if (!GEOMINSIDEMON(geometry, c->mon)) {
geometry->y = c->mon->w.y + (c->mon->w.height - geometry->height) / 2;
}
}
// 竖屏滚动布局 // 竖屏滚动布局
void vertical_scroller(Monitor *m) { void vertical_scroller(Monitor *m) {
uint32_t i, n, j; int32_t i, n, j;
float single_proportion = 1.0; float single_proportion = 1.0;
Client *c = NULL, *root_client = NULL; Client *c = NULL, *root_client = NULL;
Client **tempClients = NULL; Client **tempClients = NULL;
struct wlr_box target_geom; struct wlr_box target_geom;
int focus_client_index = 0; int32_t focus_client_index = 0;
bool need_scroller = false; bool need_scroller = false;
uint32_t cur_gappiv = enablegaps ? m->gappiv : 0; bool over_overspread_to_up = false;
uint32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
uint32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
cur_gappiv = cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv; ? 0
cur_gappov = : cur_gappiv;
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappov; cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
cur_gappoh = ? 0
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappoh; : cur_gappov;
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
? 0
: cur_gappoh;
uint32_t max_client_height = int32_t max_client_height =
m->w.height - 2 * scroller_structs - cur_gappiv; m->w.height - 2 * config.scroller_structs - cur_gappiv;
n = m->visible_scroll_tiling_clients; n = m->visible_scroll_tiling_clients;
@ -236,25 +312,26 @@ void vertical_scroller(Monitor *m) {
j = 0; j = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
tempClients[j] = c; tempClients[j] = c;
j++; j++;
} }
} }
if (n == 1 && !scroller_ignore_proportion_single && if (n == 1 && !config.scroller_ignore_proportion_single &&
!tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) { !tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) {
c = tempClients[0]; c = tempClients[0];
single_proportion = c->scroller_proportion_single > 0.0f single_proportion = c->scroller_proportion_single > 0.0f
? c->scroller_proportion_single ? c->scroller_proportion_single
: scroller_default_proportion_single; : config.scroller_default_proportion_single;
target_geom.width = m->w.width - 2 * cur_gappoh; target_geom.width = m->w.width - 2 * cur_gappoh;
target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion; target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion;
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
resize(c, target_geom, 0); vertical_check_scroller_root_inside_mon(c, &target_geom);
arrange_stack_vertical(c, target_geom, cur_gappih);
free(tempClients); free(tempClients);
return; return;
} }
@ -268,6 +345,11 @@ void vertical_scroller(Monitor *m) {
root_client = center_tiled_select(m); root_client = center_tiled_select(m);
} }
// root_client might be in a stack, find the stack head
if (root_client) {
root_client = get_scroll_stack_head(root_client);
}
if (!root_client) { if (!root_client) {
free(tempClients); free(tempClients);
return; return;
@ -276,9 +358,9 @@ void vertical_scroller(Monitor *m) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
c = tempClients[i]; c = tempClients[i];
if (root_client == c) { if (root_client == c) {
if (c->geom.y >= m->w.y + scroller_structs && if (c->geom.y >= m->w.y + config.scroller_structs &&
c->geom.y + c->geom.height <= c->geom.y + c->geom.height <=
m->w.y + m->w.height - scroller_structs) { m->w.y + m->w.height - config.scroller_structs) {
need_scroller = false; need_scroller = false;
} else { } else {
need_scroller = true; need_scroller = true;
@ -288,7 +370,48 @@ void vertical_scroller(Monitor *m) {
} }
} }
if (n == 1 && scroller_ignore_proportion_single) { bool need_apply_overspread =
config.scroller_prefer_overspread &&
m->visible_scroll_tiling_clients > 1 &&
(focus_client_index == 0 || focus_client_index == n - 1) &&
tempClients[focus_client_index]->scroller_proportion < 1.0f;
if (need_apply_overspread) {
if (focus_client_index == 0) {
over_overspread_to_up = true;
} else {
over_overspread_to_up = false;
}
if (over_overspread_to_up &&
(!INSIDEMON(tempClients[1]) ||
(tempClients[1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else if (!over_overspread_to_up &&
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
(tempClients[focus_client_index - 1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else {
need_apply_overspread = false;
}
}
bool need_apply_center =
config.scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
(config.scroller_prefer_center && !need_apply_overspread &&
(!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_height) +
(tempClients[focus_client_index]->scroller_proportion *
max_client_height) >
m->w.height - 2 * config.scroller_structs - cur_gappiv)));
if (n == 1 && config.scroller_ignore_proportion_single) {
need_scroller = true; need_scroller = true;
} }
@ -303,31 +426,49 @@ void vertical_scroller(Monitor *m) {
if (tempClients[focus_client_index]->isfullscreen) { if (tempClients[focus_client_index]->isfullscreen) {
target_geom.y = m->m.y; target_geom.y = m->m.y;
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} else if (tempClients[focus_client_index]->ismaximizescreen) { } else if (tempClients[focus_client_index]->ismaximizescreen) {
target_geom.y = m->w.y + cur_gappov; target_geom.y = m->w.y + cur_gappov;
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} else if (need_scroller) { } else if (need_scroller) {
if (scroller_focus_center || if (need_apply_center) {
((!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_height) +
(root_client->scroller_proportion * max_client_height) >
m->w.height - 2 * scroller_structs - cur_gappiv)) &&
scroller_prefer_center)) {
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
} else if (need_apply_overspread) {
if (over_overspread_to_up) {
target_geom.y = m->w.y + config.scroller_structs;
} else {
target_geom.y =
m->w.y +
(m->w.height -
tempClients[focus_client_index]->scroller_proportion *
max_client_height -
config.scroller_structs);
}
} else { } else {
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2 target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
? m->w.y + (m->w.height - ? m->w.y + (m->w.height -
root_client->scroller_proportion * tempClients[focus_client_index]
->scroller_proportion *
max_client_height - max_client_height -
scroller_structs) config.scroller_structs)
: m->w.y + scroller_structs; : m->w.y + config.scroller_structs;
} }
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} else { } else {
target_geom.y = c->geom.y; target_geom.y = c->geom.y;
resize(tempClients[focus_client_index], target_geom, 0); vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
&target_geom);
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} }
for (i = 1; i <= focus_client_index; i++) { for (i = 1; i <= focus_client_index; i++) {
@ -337,7 +478,7 @@ void vertical_scroller(Monitor *m) {
target_geom.y = tempClients[focus_client_index - i + 1]->geom.y - target_geom.y = tempClients[focus_client_index - i + 1]->geom.y -
cur_gappiv - target_geom.height; cur_gappiv - target_geom.height;
resize(c, target_geom, 0); arrange_stack_vertical(c, target_geom, cur_gappih);
} }
for (i = 1; i < n - focus_client_index; i++) { for (i = 1; i < n - focus_client_index; i++) {
@ -347,20 +488,22 @@ void vertical_scroller(Monitor *m) {
target_geom.y = tempClients[focus_client_index + i - 1]->geom.y + target_geom.y = tempClients[focus_client_index + i - 1]->geom.y +
cur_gappiv + cur_gappiv +
tempClients[focus_client_index + i - 1]->geom.height; tempClients[focus_client_index + i - 1]->geom.height;
resize(c, target_geom, 0); arrange_stack_vertical(c, target_geom, cur_gappih);
} }
free(tempClients); free(tempClients);
} }
void vertical_grid(Monitor *m) { void vertical_grid(Monitor *m) {
uint32_t i, n; int32_t i, n;
uint32_t cx, cy, cw, ch; int32_t cx, cy, cw, ch;
uint32_t dy; int32_t dy;
uint32_t rows, cols, overrows; int32_t rows, cols, overrows;
Client *c = NULL; Client *c = NULL;
int target_gappo = enablegaps ? m->isoverview ? overviewgappo : gappov : 0; int32_t target_gappo =
int target_gappi = enablegaps ? m->isoverview ? overviewgappi : gappiv : 0; enablegaps ? m->isoverview ? config.overviewgappo : config.gappov : 0;
int32_t target_gappi =
enablegaps ? m->isoverview ? config.overviewgappi : config.gappiv : 0;
float single_width_ratio = m->isoverview ? 0.7 : 0.9; float single_width_ratio = m->isoverview ? 0.7 : 0.9;
float single_height_ratio = m->isoverview ? 0.8 : 0.9; float single_height_ratio = m->isoverview ? 0.8 : 0.9;

File diff suppressed because it is too large Load diff