Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] af8fb49dd0 build(deps): bump pangocairo from 0.21.5 to 0.22.0
Bumps [pangocairo](https://github.com/gtk-rs/gtk-rs-core) from 0.21.5 to 0.22.0.
- [Release notes](https://github.com/gtk-rs/gtk-rs-core/releases)
- [Changelog](https://github.com/gtk-rs/gtk-rs-core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gtk-rs/gtk-rs-core/compare/0.21.5...0.22.0)

---
updated-dependencies:
- dependency-name: pangocairo
  dependency-version: 0.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-25 06:28:57 +00:00
69 changed files with 643 additions and 1048 deletions
+12
View File
@@ -0,0 +1,12 @@
# LFS configuration for images from the wiki
*.png filter=lfs diff=lfs merge=lfs -text
# Exclude LFS-tracked files from the tarball
/docs/wiki/img/ export-ignore
# exclude .gitattributes itself from the tarball
.gitattributes export-ignore
# tip: can be tested using
# git archive --format=tar.gz --output=source.tar.gz HEAD && \
# tar tfvz source.tar.gz | grep -e '.png' -e '.gitattributes'
+1 -1
View File
@@ -2,7 +2,7 @@
name: Bug report
about: Report a bug or a crash
title: ''
type: Bug
labels: bug
assignees: ''
---
Generated
+228 -103
View File
@@ -4,42 +4,39 @@ version = 4
[[package]]
name = "accesskit"
version = "0.24.1"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320"
dependencies = [
"uuid",
]
checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99"
[[package]]
name = "accesskit_atspi_common"
version = "0.19.0"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e98018dbef3583d751dbb96e07b8728fb99581360e1c3df408af16f4a80b821"
checksum = "890d241cf51fc784f0ac5ac34dfc847421f8d39da6c7c91a0fcc987db62a8267"
dependencies = [
"accesskit",
"accesskit_consumer",
"atspi-common",
"phf",
"serde",
"thiserror 1.0.69",
"zvariant",
]
[[package]]
name = "accesskit_consumer"
version = "0.37.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950720ce064757a1b629caad3a408e8d2c63bb01f29b8a3ff8daa331053ffeb"
checksum = "db81010a6895d8707f9072e6ce98070579b43b717193d2614014abd5cb17dd43"
dependencies = [
"accesskit",
"hashbrown 0.16.1",
"hashbrown 0.15.5",
]
[[package]]
name = "accesskit_unix"
version = "0.22.0"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5376ba4cc23312587634abb5250b1ce8618f01a55915608209aafd01efb4bf8c"
checksum = "301e55b39cfc15d9c48943ce5f572204a551646700d0e8efa424585f94fec528"
dependencies = [
"accesskit",
"accesskit_atspi_common",
@@ -374,19 +371,20 @@ checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a"
[[package]]
name = "atspi"
version = "0.29.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d"
checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c"
dependencies = [
"atspi-common",
"atspi-connection",
"atspi-proxies",
]
[[package]]
name = "atspi-common"
version = "0.13.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d"
checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb"
dependencies = [
"enumflags2",
"serde",
@@ -399,10 +397,22 @@ dependencies = [
]
[[package]]
name = "atspi-proxies"
version = "0.13.0"
name = "atspi-connection"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc"
checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938"
dependencies = [
"atspi-common",
"atspi-proxies",
"futures-lite",
"zbus",
]
[[package]]
name = "atspi-proxies"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c"
dependencies = [
"atspi-common",
"serde",
@@ -537,8 +547,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4"
dependencies = [
"bitflags 2.11.1",
"cairo-sys-rs",
"glib",
"cairo-sys-rs 0.21.5",
"glib 0.21.5",
"libc",
]
[[package]]
name = "cairo-rs"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95"
dependencies = [
"bitflags 2.11.1",
"cairo-sys-rs 0.22.0",
"glib 0.22.5",
"libc",
]
@@ -548,7 +570,18 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c28280c6b12055b5e39e4554271ae4e6630b27c0da9148c4cf6485fc6d245c"
dependencies = [
"glib-sys",
"glib-sys 0.21.5",
"libc",
"system-deps",
]
[[package]]
name = "cairo-sys-rs"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54"
dependencies = [
"glib-sys 0.22.3",
"libc",
"system-deps",
]
@@ -1140,12 +1173,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "foreign-types"
version = "0.5.0"
@@ -1281,8 +1308,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "debb0d39e3cdd84626edfd54d6e4a6ba2da9a0ef2e796e691c4e9f8646fda00c"
dependencies = [
"gdk-pixbuf-sys",
"gio",
"glib",
"gio 0.21.5",
"glib 0.21.5",
"libc",
]
@@ -1292,9 +1319,9 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd95ad50b9a3d2551e25dd4f6892aff0b772fe5372d84514e9d0583af60a0ce7"
dependencies = [
"gio-sys",
"glib-sys",
"gobject-sys",
"gio-sys 0.21.5",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"libc",
"system-deps",
]
@@ -1305,13 +1332,13 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756564212bbe4a4ce05d88ffbd2582581ac6003832d0d32822d0825cca84bfbf"
dependencies = [
"cairo-rs",
"cairo-rs 0.21.5",
"gdk-pixbuf",
"gdk4-sys",
"gio",
"glib",
"gio 0.21.5",
"glib 0.21.5",
"libc",
"pango",
"pango 0.21.5",
]
[[package]]
@@ -1320,13 +1347,13 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6d4e5b3ccf591826a4adcc83f5f57b4e59d1925cb4bf620b0d645f79498b034"
dependencies = [
"cairo-sys-rs",
"cairo-sys-rs 0.21.5",
"gdk-pixbuf-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"gio-sys 0.21.5",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"libc",
"pango-sys",
"pango-sys 0.21.5",
"pkg-config",
"system-deps",
]
@@ -1412,8 +1439,25 @@ dependencies = [
"futures-core",
"futures-io",
"futures-util",
"gio-sys",
"glib",
"gio-sys 0.21.5",
"glib 0.21.5",
"libc",
"pin-project-lite",
"smallvec",
]
[[package]]
name = "gio"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401b600a9795c46ff45890146968b712c96ce4e9393798804133e137bd81d89c"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"gio-sys 0.22.0",
"glib 0.22.5",
"libc",
"pin-project-lite",
"smallvec",
@@ -1425,8 +1469,21 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22"
dependencies = [
"glib-sys",
"gobject-sys",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"libc",
"system-deps",
"windows-sys 0.61.2",
]
[[package]]
name = "gio-sys"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64729ba2772c080448f9f966dba8f4456beeb100d8c28a865ef8a0f2ef4987e1"
dependencies = [
"glib-sys 0.22.3",
"gobject-sys 0.22.0",
"libc",
"system-deps",
"windows-sys 0.61.2",
@@ -1481,10 +1538,30 @@ dependencies = [
"futures-executor",
"futures-task",
"futures-util",
"gio-sys",
"glib-macros",
"glib-sys",
"gobject-sys",
"gio-sys 0.21.5",
"glib-macros 0.21.5",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"libc",
"memchr",
"smallvec",
]
[[package]]
name = "glib"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1b7df55594e0e787d1560e23f7e12d7360d0b22e7b7c228ec2488b9e59b1b6b"
dependencies = [
"bitflags 2.11.1",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"glib-macros 0.22.2",
"glib-sys 0.22.3",
"gobject-sys 0.22.0",
"libc",
"memchr",
"smallvec",
@@ -1503,6 +1580,18 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "glib-macros"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda575994e3689b1bc12f89c3df621ead46ff292623b76b4710a3a5b79be54bb"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "glib-sys"
version = "0.21.5"
@@ -1513,6 +1602,16 @@ dependencies = [
"system-deps",
]
[[package]]
name = "glib-sys"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1eb23a616a3dbc7fc15bbd26f58756ff0b04c8a894df3f0680cd21011db6a642"
dependencies = [
"libc",
"system-deps",
]
[[package]]
name = "glob"
version = "0.3.3"
@@ -1525,7 +1624,18 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294"
dependencies = [
"glib-sys",
"glib-sys 0.21.5",
"libc",
"system-deps",
]
[[package]]
name = "gobject-sys"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18eda93f09d3778f38255b231b17ef67195013a592c91624a4daf8bead875565"
dependencies = [
"glib-sys 0.22.3",
"libc",
"system-deps",
]
@@ -1536,7 +1646,7 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2730030ac9db663fd8bfe1e7093742c1cafb92db9c315c9417c29032341fe2f9"
dependencies = [
"glib",
"glib 0.21.5",
"graphene-sys",
"libc",
]
@@ -1547,7 +1657,7 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915e32091ea9ad241e4b044af62b7351c2d68aeb24f489a0d7f37a0fc484fd93"
dependencies = [
"glib-sys",
"glib-sys 0.21.5",
"libc",
"pkg-config",
"system-deps",
@@ -1559,13 +1669,13 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e755de9d8c5896c5beaa028b89e1969d067f1b9bf1511384ede971f5983aa153"
dependencies = [
"cairo-rs",
"cairo-rs 0.21.5",
"gdk4",
"glib",
"glib 0.21.5",
"graphene-rs",
"gsk4-sys",
"libc",
"pango",
"pango 0.21.5",
]
[[package]]
@@ -1574,13 +1684,13 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ce91472391146f482065f1041876d8f869057b195b95399414caa163d72f4f7"
dependencies = [
"cairo-sys-rs",
"cairo-sys-rs 0.21.5",
"gdk4-sys",
"glib-sys",
"gobject-sys",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"graphene-sys",
"libc",
"pango-sys",
"pango-sys 0.21.5",
"system-deps",
]
@@ -1590,19 +1700,19 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acb21d53cfc6f7bfaf43549731c43b67ca47d87348d81c8cfc4dcdd44828e1a4"
dependencies = [
"cairo-rs",
"cairo-rs 0.21.5",
"field-offset",
"futures-channel",
"gdk-pixbuf",
"gdk4",
"gio",
"glib",
"gio 0.21.5",
"glib 0.21.5",
"graphene-rs",
"gsk4",
"gtk4-macros",
"gtk4-sys",
"libc",
"pango",
"pango 0.21.5",
]
[[package]]
@@ -1623,16 +1733,16 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "842577fe5a1ee15d166cd3afe804ce0cab6173bc789ca32e21308834f20088dd"
dependencies = [
"cairo-sys-rs",
"cairo-sys-rs 0.21.5",
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"gio-sys 0.21.5",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"graphene-sys",
"gsk4-sys",
"libc",
"pango-sys",
"pango-sys 0.21.5",
"system-deps",
]
@@ -1652,16 +1762,7 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
"foldhash",
]
[[package]]
@@ -1942,12 +2043,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb09e12bf8f73342b3315c839d0a7668cc0ccebd78490c49fec48bab15d5484b"
dependencies = [
"gdk4",
"gio",
"glib",
"gio 0.21.5",
"glib 0.21.5",
"gtk4",
"libadwaita-sys",
"libc",
"pango",
"pango 0.21.5",
]
[[package]]
@@ -1957,12 +2058,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7f94227ba87eb596fecada2491f04e357d507324142f77bf76d9e6be4a3e31"
dependencies = [
"gdk4-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"gio-sys 0.21.5",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"gtk4-sys",
"libc",
"pango-sys",
"pango-sys 0.21.5",
"system-deps",
]
@@ -2272,7 +2373,7 @@ dependencies = [
"niri-config",
"niri-ipc",
"ordered-float",
"pango",
"pango 0.21.5",
"pangocairo",
"pipewire",
"pkg-config",
@@ -2686,10 +2787,22 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d1d85e2078077a065bb7fc072783d5bcd4e51b379f22d67107d0a16937eb69"
dependencies = [
"gio",
"glib",
"gio 0.21.5",
"glib 0.21.5",
"libc",
"pango-sys",
"pango-sys 0.21.5",
]
[[package]]
name = "pango"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4804fb6018c6604eac198f0f897320d3696c9af7983cde056f07cef93cac9202"
dependencies = [
"gio 0.22.5",
"glib 0.22.5",
"libc",
"pango-sys 0.22.0",
]
[[package]]
@@ -2698,35 +2811,47 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4f06627d36ed5ff303d2df65211fc2e52ba5b17bf18dd80ff3d9628d6e06cfd"
dependencies = [
"glib-sys",
"gobject-sys",
"glib-sys 0.21.5",
"gobject-sys 0.21.5",
"libc",
"system-deps",
]
[[package]]
name = "pango-sys"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd111a20ca90fedf03e09c59783c679c00900f1d8491cca5399f5e33609d5d6"
dependencies = [
"glib-sys 0.22.3",
"gobject-sys 0.22.0",
"libc",
"system-deps",
]
[[package]]
name = "pangocairo"
version = "0.21.5"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b36c5c84304072939d860595d9bda2a797d3bd6f7215e20b8ccd0e72d84da8c8"
checksum = "d9f15369c787b1cc59a5b86eff6afffd5a9716c5beb4969d20b307cebfe7e407"
dependencies = [
"cairo-rs",
"glib",
"cairo-rs 0.22.0",
"glib 0.22.5",
"libc",
"pango",
"pango 0.22.4",
"pangocairo-sys",
]
[[package]]
name = "pangocairo-sys"
version = "0.21.5"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadbb01ad38be76e0d37e329d40ba0f3f9ef261d7b84b05201d7a0f14f819406"
checksum = "d95cb73468373b9e568abb1afbaf5b42fe6ab9128fc41b5f2adbf69451c3c77f"
dependencies = [
"cairo-sys-rs",
"glib-sys",
"cairo-sys-rs 0.22.0",
"glib-sys 0.22.3",
"libc",
"pango-sys",
"pango-sys 0.22.0",
"system-deps",
]
+5 -2
View File
@@ -51,8 +51,11 @@ readme = "README.md"
keywords = ["wayland", "compositor", "tiling", "smithay", "wm"]
[dependencies]
accesskit = { version = "0.24", optional = true }
accesskit_unix = { version = "0.22", optional = true }
# accesskit_unix 0.18 has a regression where it doesn't work in normal configurations.
# accesskit 0.21 is its correct dependent version.
# https://github.com/niri-wm/niri/issues/3594
accesskit = { version = "0.21", optional = true }
accesskit_unix = { version = "0.17", optional = true }
anyhow.workspace = true
arrayvec = "0.7.6"
async-channel = "2.5.0"
+7 -12
View File
@@ -10,7 +10,7 @@
<a href="https://niri-wm.github.io/niri/Getting-Started.html">Getting Started</a> | <a href="https://niri-wm.github.io/niri/Configuration%3A-Introduction.html">Configuration</a> | <a href="https://github.com/niri-wm/niri/discussions/325">Setup&nbsp;Showcase</a>
</p>
<img width="1280" height="720" alt="niri with a few windows open" src="https://github.com/user-attachments/assets/dea5909e-1859-4aaa-9d88-d37f9663e00b" />
![niri with a few windows open](https://github.com/user-attachments/assets/535e6530-2f44-4b84-a883-1240a3eee6e9)
## About
@@ -39,7 +39,6 @@ When a monitor disconnects, its workspaces will move to another monitor, but upo
- Group windows into [tabs](https://niri-wm.github.io/niri/Tabs.html)
- Configurable layout: gaps, borders, struts, window sizes
- [Gradient borders](https://niri-wm.github.io/niri/Configuration%3A-Layout.html#gradients) with Oklab and Oklch support
- [Background blur](https://niri-wm.github.io/niri/Window-Effects.html) for windows and layer-shell surfaces
- [Animations](https://github.com/niri-wm/niri/assets/1794388/ce178da2-af9e-4c51-876f-8709c241d95e) with support for [custom shaders](https://github.com/niri-wm/niri/assets/1794388/27a238d6-0a22-4692-b794-30dc7a626fad)
- Live-reloading config
- Works with [screen readers](https://niri-wm.github.io/niri/Accessibility.html)
@@ -48,10 +47,7 @@ When a monitor disconnects, its workspaces will move to another monitor, but upo
https://github.com/niri-wm/niri/assets/1794388/bce834b0-f205-434e-a027-b373495f9729
Also check out these videos that showcase a lot of the niri functionality:
- [Niri Is My New Favorite Wayland Compositor](https://www.youtube.com/watch?v=DeYx2exm04M) by Brodie Robertson
- [How Is niri This Good? Live Demo + Config](https://www.youtube.com/watch?v=7XmD5UyyhZQ) by Nick Janetakis
Also check out this video from Brodie Robertson that showcases a lot of the niri functionality: [Niri Is My New Favorite Wayland Compositor](https://youtu.be/DeYx2exm04M)
## Status
@@ -60,7 +56,7 @@ Many people are daily-driving niri, and are happy to help in our [Matrix channel
Give it a try!
Follow the instructions on the [Getting Started](https://niri-wm.github.io/niri/Getting-Started.html) page.
Grab a desktop shell like [DankMaterialShell] or [Noctalia] (or build a more traditional setup): niri by itself is not a complete desktop environment.
Have your [waybar]s and [fuzzel]s ready: niri is not a complete desktop environment.
Also check out [awesome-niri], a list of niri-related links and projects.
Here are some points you may have questions about:
@@ -113,8 +109,8 @@ Here are some other projects which implement a similar workflow:
- [PaperWM]: scrollable tiling on top of GNOME Shell.
- [karousel]: scrollable tiling on top of KDE.
- [scroll](https://github.com/dawsers/scroll) and [papersway]: scrollable tiling on top of sway/i3.
- Hyprland has a built-in [scrolling layout](https://wiki.hypr.land/Configuring/Layouts/Scrolling-Layout/).
- [Paneru] and [PaperWM.spoon]: scrollable tiling on top of macOS.
- [hyprscrolling] and [hyprslidr]: scrollable tiling on top of Hyprland.
- [PaperWM.spoon]: scrollable tiling on top of macOS.
## Contact
@@ -128,9 +124,8 @@ We also have a community Discord server: https://discord.gg/vT8Sfjy7sx
[awesome-niri]: https://github.com/niri-wm/awesome-niri
[karousel]: https://github.com/peterfajdiga/karousel
[papersway]: https://spwhitton.name/tech/code/papersway/
[Paneru]: https://github.com/karinushka/paneru
[hyprscrolling]: https://github.com/hyprwm/hyprland-plugins/tree/main/hyprscrolling
[hyprslidr]: https://gitlab.com/magus/hyprslidr
[PaperWM.spoon]: https://github.com/mogenson/PaperWM.spoon
[Matrix channel]: https://matrix.to/#/#niri:matrix.org
[OpenTabletDriver]: https://opentabletdriver.net/
[DankMaterialShell]: https://danklinux.com/
[Noctalia]: https://noctalia.dev/
-2
View File
@@ -88,7 +88,6 @@ nav:
- Window Effects: Window-Effects.md
- Packaging niri: Packaging-niri.md
- Integrating niri: Integrating-niri.md
- Security Model: Security-Model.md
- Accessibility: Accessibility.md
- Name and Logo: Name-and-Logo.md
- FAQ: FAQ.md
@@ -112,7 +111,6 @@ nav:
- Design Principles: Development:-Design-Principles.md
- Developing niri: Development:-Developing-niri.md
- Documenting niri: Development:-Documenting-niri.md
- Releasing niri: Development:-Releasing-niri.md
- Fractional Layout: Development:-Fractional-Layout.md
- Redraw Loop: Development:-Redraw-Loop.md
- Animation Timing: Development:-Animation-Timing.md
-2
View File
@@ -33,8 +33,6 @@ JetBrains IDEs can run directly on Wayland, but it's not the default.
For JetBrainsRuntime > 17, you can set the flag `-Dawt.toolkit.name=WLToolkit` inside of `help -> edit custom vm options -> add`.
If the settings window fails to load under Wayland, and the UI becomes unresponsive afterwards, also set the flag `-Dsun.awt.wl.WindowDecorationStyle=builtin` in the custom vm options. This gives the settings window a titlebar, but it at least makes the IDE functional.
### WezTerm
> [!NOTE]
+21
View File
@@ -324,6 +324,27 @@ debug {
}
```
### `keep-max-bpc-unchanged`
<sup>Since: 25.08</sup>
When connecting monitors, niri sets their max bpc to 8 in order to reduce display bandwidth and to potentially allow more monitors to be connected at once.
Restricting bpc to 8 is not a problem since we don't support HDR or color management yet and can't really make use of higher bpc.
Apparently, setting max bpc to 8 breaks some displays driven by AMDGPU.
If this happens to you, set this debug flag, which will prevent niri from changing max bpc.
AMDGPU bug report: https://gitlab.freedesktop.org/drm/amd/-/issues/4487.
<sup>Since: 25.11</sup>
This setting is deprecated and does nothing: niri no longer sets max bpc.
The old niri behavior with this setting enabled matches the new behavior.
```kdl
debug {
keep-max-bpc-unchanged
}
```
### Key Bindings
These are not debug options, but rather key bindings.
+1 -8
View File
@@ -90,7 +90,6 @@ input {
// off
map-to-output "eDP-1"
// map-to-focused-output
// map-to-focused-window
// left-handed
// calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0
}
@@ -283,16 +282,10 @@ Valid output names are the same as the ones used for output configuration.
<sup>Since: 0.1.7</sup> When a tablet is not mapped to any output, it will map to the union of all connected outputs, without aspect ratio correction.
Settings specific to `tablet`:
Setting specific to `tablet`:
- `map-to-focused-output`: <sup>Since: 26.04</sup> will map the tablet to the focused output, takes precedence over `map-to-output`.
- `map-to-focused-window`: <sup>Since: next release</sup> will map the tablet to the focused window's geometry, takes precedence over `map-to-focused-output` and `map-to-output`.
Falls back to those when no window is focused (for example, in the overview).
When the tablet is also mapped to a specific output via `map-to-output`, the `map-to-focused-window` flag will map the tablet to the active window on that output.
If the tablet isn't mapped to any specific output, it will map the tablet to the current focused window regardless of where it is.
### General Settings
These settings are not specific to a particular input device.
-22
View File
@@ -15,7 +15,6 @@ output "eDP-1" {
variable-refresh-rate // on-demand=true
focus-at-startup
backdrop-color "#001100"
// max-bpc 8
hot-corners {
// off
@@ -280,27 +279,6 @@ output "HDMI-A-1" {
}
```
### `max-bpc`
<sup>Since: next release</sup>
Set the maximum bits per channel (BPC) for this output.
You *do not* need to set this option normally.
It influences the encoding of the display signal on the wire and *is not* directly related to the color bitness or framebuffer format.
Setting `max-bpc` to a low value may help if you hit a bandwidth issue (can't set a monitor configuration that works on other compositor).
Otherwise, you're advised to leave it unset (keeping a default, usually high value) and let the GPU driver figure things out automatically.
Valid values are `6`, `8`, `10`, `12`, `14`, `16`.
```kdl
// Set 8 max-bpc on HDMI-A-1 to lower the bandwidth.
output "HDMI-A-1" {
max-bpc 8
}
```
### `hot-corners`
<sup>Since: 25.11</sup>
-146
View File
@@ -1,146 +0,0 @@
This is a checklist of things to release a new niri version.
We'll use `26.04` as the example new version.
When making a patch release, append the patch number like `26.04.1`.
## Prepare the release notes
Plan for a few days of work, this usually takes a while.
During this process, also check:
- that all additions are marked with "next release" on the wiki,
- if anything needs updating in `README.md`.
## Bump version
We use `year.month.patch` versioning.
If the month contains a leading zero, drop it from the crate version (Cargo requirement).
You can use the command from [cargo-edit](https://github.com/killercup/cargo-edit):
```
cargo set-version 26.4.0
```
Then, manually update version in:
- `[package.metadata.generate-rpm]` in Cargo.toml
- Dependency example in `niri-ipc/README.md`
- Dependency example in `niri-ipc/src/lib.rs`
Do a full text search for the old version to make sure there are no other places.
## Replace all "Since: next release" mentions
Do a full text search for `next release`, replace everything with the new version number.
## Build, test, push, and have the CI run
Run all tests:
```
RUN_SLOW_TESTS=1 cargo test --release --all
```
- Run `cargo package -p niri-ipc` and make sure it succeeds.
- Make sure the CI passes.
- Make sure the niri-git COPR build passes.
## Trigger the "Prepare release" workflow on GitHub Actions
Set the "Public version" input to a version like `26.04`.
This workflow will:
- do some pre-release checks like grepping the wiki for "next version",
- make a vendored dependency archive,
- build and test niri with that dependency archive,
- draft a new GitHub release with the archive attached.
It will NOT override an existing draft release with the same name so the release notes are safe.
Make sure it succeeds and grab the vendored dependency archive that it produces.
## Update the niri COPR spec, update licenses in .spec.rpkg
You can grab the previous spec from [the last build](https://copr.fedorainfracloud.org/coprs/yalter/niri/builds/) in the COPR.
- Update version global to `26.04`.
- Update commit global to the commit hash corresponding to the release commit.
You can use `git rev-parse HEAD`.
- Reset the `Release:` number to 1 if it was higher.
To run a test build, you can download the vendored dependency archive from the last step.
Comment/uncomment `Source:` and `%autosetup` lines accordingly.
Download the source files:
```
spectool -g niri.spec
```
Build RPMs:
```
fedpkg --release 44 mockbuild
```
During the build, it will print the list of licenses.
Update it in both the COPR spec and in `niri.spec.rpkg` accordingly.
If you had to update `niri.spec.rpkg` and therefore make another commit to the niri repo, make sure to update the commit hash in the COPR spec again.
Revert any temporary changes that you did to the COPR spec for local testing.
## Create and push the release git tag
The tag starts with a `v`:
```
git tag -am "v26.04 release" v26.04
git push origin v26.04
```
While you can let GitHub create the tag automatically upon creating the release, this is not recommended.
GitHub creates a *lightweight* tag, but we want an annotated tag that plays better with various tooling.
## Publish the release on GitHub
- Either upload the vendored dependencies file to your draft release with the release notes, or move the release notes to the GitHub-created release (the difference is that it's attributed to github-actions).
- Set the tag to `v26.04`.
- Set the release title to `v26.04`.
- Check "Create a discussion for this release".
## Publish the niri-ipc crate
```
cargo publish -p niri-ipc
```
## Kick off the COPR build
Upload on the web or:
```
copr-cli build niri niri.spec
```
## Announce the release
Chat rooms, social media, etc.
## Update wayland.app protocol data
- Install [wlprobe](https://github.com/PolyMeilex/wlprobe).
- Clone https://github.com/vially/wayland-explorer.
- Generate data:
```
wlprobe > ./src/data/compositors/niri.json
```
- Manually add `"version": "26.04"`, then clean up the diff from unrelated changes, for example:
- The number of `wl_output`s will change depending on how many monitors you have connected.
- The number of `wp_drm_lease_device_v1` will change depending on your number of GPUs.
- `org_kde_kwin_server_decoration_manager` and `zxdg_decoration_manager_v1` will only appear with `prefer-no-csd`.
- Create a pull request.
-4
View File
@@ -63,7 +63,3 @@ Alternatively, some desktop environments and shells work with niri, and can give
- Many [XFCE](https://www.xfce.org/) components work on Wayland, including niri. See [their wiki](https://wiki.xfce.org/releng/wayland_roadmap#component_specific_status) for details.
- There are complete desktop shells based on Quickshell that support niri, for example [DankMaterialShell](https://github.com/AvengeMedia/DankMaterialShell) and [Noctalia](https://github.com/noctalia-dev/noctalia-shell).
- You can run a [COSMIC](https://system76.com/cosmic/) session with niri using [cosmic-ext-extra-sessions](https://github.com/Drakulix/cosmic-ext-extra-sessions).
### Security model
See the [Security Model](./Security-Model.md) page for an overview of niri's security model.
+3 -3
View File
@@ -53,12 +53,12 @@ It shows up as "niri Dynamic Cast Target" in the screencast window dialog.
![Screencast dialog showing niri Dynamic Cast Target.](https://github.com/user-attachments/assets/e236ce74-98ec-4f3a-a99b-29ac1ff324dd)
Choose it, then use the following binds to change what it shows.
The stream won't start until you make your first target selection.
When you select it, it will start as an empty, transparent video stream.
Then, you can use the following binds to change what it shows:
- `set-dynamic-cast-window` to cast the focused window.
- `set-dynamic-cast-monitor` to cast the focused monitor.
- `clear-dynamic-cast-target` to reset to an empty video stream.
- `clear-dynamic-cast-target` to go back to an empty stream.
You can also use these actions from the command line, for example to interactively pick which window to cast:
-61
View File
@@ -1,61 +0,0 @@
Niri assumes that programs running unsandboxed on the host are **trusted**.
This is a reasonable assumption because programs running on the host have a wide variety of ways to get all access they need, even without niri.
For instance:
- They can set `$LD_PRELOAD` in `.bashrc` or similar files to load an arbitrary library into all processes.
- They can replace binaries in `$PATH` with malicious code.
- They can interpose any socket in `$XDG_RUNTIME_DIR`, like Wayland, and do keylogging or record window contents.
- They can scan the filesystem for secrets: SSH keys, password stores, etc.
- They can connect to an unlocked keyring and steal credentials.
- And so on and so forth.
## Unsandboxed clients
Anything with access to niri's Wayland socket can, among other things:
- Record the user's screen via [wlr-screencopy](https://wayland.app/protocols/wlr-screencopy-unstable-v1).
- Emulate input via [wlr-virtual-pointer](https://wayland.app/protocols/wlr-virtual-pointer-unstable-v1) and [virtual-keyboard](https://wayland.app/protocols/virtual-keyboard-unstable-v1).
- Get the user's clipboard contents via [wlr-data-control](https://wayland.app/protocols/ext-data-control-v1).
- Create arbitrary fullscreen surfaces through [wlr-layer-shell](https://wayland.app/protocols/wlr-layer-shell-unstable-v1) that can steal the user's input, pretend to be a password entry, or lock the user out of their session.
- Kill a running lockscreen, create a new lock surface, and tell niri to unlock a locked session.
Anything with access to niri's [IPC](./IPC.md) socket can, among other things:
- Spawn a Wayland client which can do everything in the list above.
Anything with access to niri's D-Bus interfaces can, among other things:
- Record the user's screen via the screencast interface.
- Fully listen to and emulate input from the user's keyboard via the accessibility interface.
Also, while niri doesn't directly integrate Xwayland, it's worth reminding that anything with access to the X11 `$DISPLAY` (which comes both as a socket file on disk **and** as an abstract socket in the network namespace) can intercept and emulate all input and record the contents of any X11 windows on the same `$DISPLAY` (but not Wayland windows).
## Running untrusted clients
Considering all of the above, for running untrusted clients, you need a proper sandbox that:
- Removes niri's IPC socket.
- Prevents D-Bus access to host services.
- Uses a filtered Wayland socket.
For creating a filtered Wayland socket, you can use the [security-context](https://wayland.app/protocols/security-context-v1) protocol which niri implements.
All unsafe protocols are made inaccessible through this filtered Wayland socket.
One sandbox that satisfies all of these criteria is the [Flatpak](https://flatpak.org/) sandbox.
Importantly, filtering just the Wayland socket (and leaving, for example, unrestricted D-Bus access) is **not enough** to prevent untrusted clients from doing bad things.
## Lock screen
When the session is locked via [ext-session-lock](https://wayland.app/protocols/ext-session-lock-v1), most actions (keybindings) are automatically disabled.
Only a very small set of safe actions is allowed.
In particular, spawning will not work, with the exception of binds explicitly configured with `allow-when-locked=true`.
Importantly, the **quit** action is allowed—you can always quit niri, even when on a lock screen.
Therefore, you must ensure that quitting niri does not drop you into an unprotected TTY commandline.
Usually, a display manager, like GDM, will do this for you: when niri exits (via the quit bind or if it crashes), it'll put you back into a safe password prompt.
Other than quitting, the only way to exit a lock screen is for the lock screen client to tell niri to unlock the session.
If the lock screen client crashes, the session remains locked with a solid red background.
In this case, another lock screen client can take over (so you can start a fresh lock screen if it crashes, and still unlock your session).
-2
View File
@@ -6,8 +6,6 @@ You can apply background effects to windows and layer-shell surfaces.
These include blur, xray, saturation, and noise.
They can be enabled in the `background-effect {}` section of [window](./Configuration:-Window-Rules.md#background-effect) or [layer](./Configuration:-Layer-Rules.md#background-effect) rules.
![Screenshot with blur](./img/blur.png)
The window needs to be semitransparent for you to see the background effect (otherwise it's fully covered by the opaque window).
Focus ring and border can also cover the background effect, see [this FAQ entry](./FAQ.md#why-are-transparent-windows-tinted-why-is-the-borderfocus-ring-showing-up-through-semitransparent-windows) for how to change this.
-2
View File
@@ -17,7 +17,6 @@
* [Window Effects](./Window-Effects.md)
* [Packaging niri](./Packaging-niri.md)
* [Integrating niri](./Integrating-niri.md)
* [Security Model](./Security-Model.md)
* [Accessibility](./Accessibility.md)
* [Name and Logo](./Name-and-Logo.md)
* [FAQ](./FAQ.md)
@@ -43,7 +42,6 @@
* [Design Principles](./Development:-Design-Principles.md)
* [Developing niri](./Development:-Developing-niri.md)
* [Documenting niri](./Development:-Documenting-niri.md)
* [Releasing niri](./Development:-Releasing-niri.md)
* [Fractional Layout](./Development:-Fractional-Layout.md)
* [Redraw Loop](./Development:-Redraw-Loop.md)
* [Animation Timing](./Development:-Animation-Timing.md)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 130 B

Generated
+25 -4
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1781607440,
"narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=",
"lastModified": 1757967192,
"narHash": "sha256-/aA9A/OBmnuOMgwfzdsXRusqzUpd8rQnQY8jtrHK+To=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3e41b24abd260e8f71dbe2f5737d24122f972158",
"rev": "0d7c15863b251a7a50265e57c1dca1a7add2e291",
"type": "github"
},
"original": {
@@ -18,7 +18,28 @@
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1757989933,
"narHash": "sha256-9cpKYWWPCFhgwQTww8S94rTXgg8Q8ydFv9fXM6I8xQM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "8249aa3442fb9b45e615a35f39eca2fe5510d7c3",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
}
},
+39 -16
View File
@@ -2,12 +2,22 @@
{
description = "Niri: A scrollable-tiling Wayland compositor.";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# NOTE: This is not necessary for end users
# You can omit it with `inputs.rust-overlay.follows = ""`
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
rust-overlay,
}:
let
revision = self.shortRev or self.dirtyShortRev or "unknown";
@@ -125,12 +135,12 @@
''
+ lib.optionalString withSystemd ''
install -Dm755 resources/niri-session $out/bin/niri-session
install -Dm644 resources/niri{.service,-shutdown.target} -t $out/lib/systemd/user
install -Dm644 resources/niri{.service,-shutdown.target} -t $out/share/systemd/user
'';
env = {
# Force linking with libEGL and libwayland-client so they end up in RPATH and
# can be discovered by `dlopen()`
# Force linking with libEGL and libwayland-client
# so they can be discovered by `dlopen()`
RUSTFLAGS = toString (
map (arg: "-C link-arg=" + arg) [
"-Wl,--push-state,--no-as-needed"
@@ -172,20 +182,33 @@
system:
let
pkgs = nixpkgsFor.${system};
rustfmt' = pkgs.rustfmt.override { asNightly = true; };
rust-bin = rust-overlay.lib.mkRustBin { } pkgs;
inherit (self.packages.${system}) niri;
in
{
default = pkgs.mkShell {
packages = builtins.attrValues {
inherit (pkgs)
rustc
cargo
clippy
cargo-insta
;
inherit rustfmt';
};
packages = [
# We don't use the toolchain from nixpkgs
# because we prefer a nightly toolchain
# and we *require* a nightly rustfmt
(rust-bin.selectLatestNightlyWith (
toolchain:
toolchain.default.override {
extensions = [
# includes already:
# rustc
# cargo
# rust-std
# rust-docs
# rustfmt-preview
# clippy-preview
"rust-analyzer"
"rust-src"
];
}
))
pkgs.cargo-insta
];
nativeBuildInputs = [
pkgs.rustPlatform.bindgenHook
@@ -202,8 +225,8 @@
# It is required for `dlopen()` to work on some libraries; see the comment
# in the package expression
#
# This should only be set with `RUSTFLAGS="$RUSTFLAGS -C your-flags"`
RUSTFLAGS = niri.RUSTFLAGS;
# This should only be set with `CARGO_BUILD_RUSTFLAGS="$CARGO_BUILD_RUSTFLAGS -C your-flags"`
CARGO_BUILD_RUSTFLAGS = niri.RUSTFLAGS;
};
};
}
-9
View File
@@ -52,9 +52,6 @@ pub enum Trigger {
TouchpadScrollUp,
TouchpadScrollLeft,
TouchpadScrollRight,
TabletStylusButton1,
TabletStylusButton2,
TabletStylusButton3,
}
bitflags! {
@@ -1003,12 +1000,6 @@ impl FromStr for Key {
Trigger::TouchpadScrollLeft
} else if key.eq_ignore_ascii_case("TouchpadScrollRight") {
Trigger::TouchpadScrollRight
} else if key.eq_ignore_ascii_case("TabletStylusButton1") {
Trigger::TabletStylusButton1
} else if key.eq_ignore_ascii_case("TabletStylusButton2") {
Trigger::TabletStylusButton2
} else if key.eq_ignore_ascii_case("TabletStylusButton3") {
Trigger::TabletStylusButton3
} else {
let mut keysym = keysym_from_name(key, KEYSYM_CASE_INSENSITIVE);
// The keyboard event handling code can receive either
+4
View File
@@ -10,6 +10,7 @@ pub struct Debug {
pub enable_overlay_planes: bool,
pub disable_cursor_plane: bool,
pub disable_direct_scanout: bool,
pub keep_max_bpc_unchanged: bool,
pub restrict_primary_scanout_to_matching_format: bool,
pub force_disable_connectors_on_resume: bool,
pub render_drm_device: Option<PathBuf>,
@@ -41,6 +42,8 @@ pub struct DebugPart {
#[knuffel(child)]
pub disable_direct_scanout: Option<Flag>,
#[knuffel(child)]
pub keep_max_bpc_unchanged: Option<Flag>,
#[knuffel(child)]
pub restrict_primary_scanout_to_matching_format: Option<Flag>,
#[knuffel(child)]
pub force_disable_connectors_on_resume: Option<Flag>,
@@ -79,6 +82,7 @@ impl MergeWith<DebugPart> for Debug {
enable_overlay_planes,
disable_cursor_plane,
disable_direct_scanout,
keep_max_bpc_unchanged,
restrict_primary_scanout_to_matching_format,
force_disable_connectors_on_resume,
force_pipewire_invalid_modifier,
-2
View File
@@ -366,8 +366,6 @@ pub struct Tablet {
#[knuffel(child)]
pub map_to_focused_output: bool,
#[knuffel(child)]
pub map_to_focused_window: bool,
#[knuffel(child)]
pub left_handed: bool,
}
+2 -11
View File
@@ -720,7 +720,6 @@ mod tests {
tablet {
map-to-output "eDP-1"
map-to-focused-output
map-to-focused-window
calibration-matrix 1.0 2.0 3.0 \
4.0 5.0 6.0
}
@@ -745,7 +744,6 @@ mod tests {
transform "flipped-90"
position x=10 y=20
mode "1920x1080@144"
max-bpc 10
variable-refresh-rate on-demand=true
background-color "rgba(25, 25, 102, 1.0)"
hot-corners {
@@ -858,7 +856,7 @@ mod tests {
window-open { off; }
window-close {
curve "cubic-bezier" 0.05 0.7 0.1 1
curve "cubic-bezier" 0.05 0.7 0.1 1
}
recent-windows-close {
@@ -1115,7 +1113,6 @@ mod tests {
"eDP-1",
),
map_to_focused_output: true,
map_to_focused_window: true,
left_handed: false,
},
touch: Touch {
@@ -1161,11 +1158,6 @@ mod tests {
y: 20,
},
),
max_bpc: Some(
MaxBpc(
_10,
),
),
mode: Some(
Mode {
custom: false,
@@ -1211,7 +1203,6 @@ mod tests {
scale: None,
transform: Normal,
position: None,
max_bpc: None,
mode: Some(
Mode {
custom: true,
@@ -1238,7 +1229,6 @@ mod tests {
scale: None,
transform: Normal,
position: None,
max_bpc: None,
mode: None,
modeline: Some(
Modeline {
@@ -2252,6 +2242,7 @@ mod tests {
enable_overlay_planes: false,
disable_cursor_plane: false,
disable_direct_scanout: false,
keep_max_bpc_unchanged: false,
restrict_primary_scanout_to_matching_format: false,
force_disable_connectors_on_resume: false,
render_drm_device: Some(
-42
View File
@@ -59,8 +59,6 @@ pub struct Output {
pub transform: Transform,
#[knuffel(child)]
pub position: Option<Position>,
#[knuffel(child, unwrap(argument))]
pub max_bpc: Option<MaxBpc>,
#[knuffel(child)]
pub mode: Option<Mode>,
#[knuffel(child)]
@@ -103,7 +101,6 @@ impl Default for Output {
scale: None,
transform: Transform::Normal,
position: None,
max_bpc: None,
mode: None,
modeline: None,
variable_refresh_rate: None,
@@ -131,9 +128,6 @@ pub struct Position {
pub y: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct MaxBpc(pub niri_ipc::MaxBpc);
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Default)]
pub struct Vrr {
#[knuffel(property, default = false)]
@@ -263,42 +257,6 @@ impl OutputName {
}
}
impl<S: ErrorSpan> knuffel::DecodeScalar<S> for MaxBpc {
fn type_check(
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
ctx: &mut Context<S>,
) {
if let Some(type_name) = &type_name {
ctx.emit_error(DecodeError::unexpected(
type_name,
"type name",
"no type name expected for this node",
));
}
}
fn raw_decode(
value: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
ctx: &mut Context<S>,
) -> Result<Self, DecodeError<S>> {
match &**value {
knuffel::ast::Literal::Int(ref val) => match u8::try_from(val) {
Ok(v) => niri_ipc::MaxBpc::try_from(v)
.map(MaxBpc)
.map_err(|e| DecodeError::conversion(value, e)),
Err(e) => {
ctx.emit_error(DecodeError::conversion(value, e));
Ok(Self::default())
}
},
_ => {
ctx.emit_error(DecodeError::scalar_kind(knuffel::decode::Kind::Int, value));
Ok(Self::default())
}
}
}
}
impl<S: ErrorSpan> knuffel::Decode<S> for Mode {
fn decode_node(node: &SpannedNode<S>, ctx: &mut Context<S>) -> Result<Self, DecodeError<S>> {
if let Some(type_name) = &node.type_name {
-58
View File
@@ -1097,12 +1097,6 @@ pub enum OutputAction {
#[cfg_attr(feature = "clap", command(flatten))]
vrr: VrrToSet,
},
/// Set the maximum bits per channel (bit depth).
MaxBpc {
/// Maximum bits per channel to set.
#[cfg_attr(feature = "clap", arg())]
max_bpc: MaxBpc,
},
}
/// Output mode to set.
@@ -1234,8 +1228,6 @@ pub struct Output {
///
/// `None` if the output is not mapped to any logical output (for example, if it is disabled).
pub logical: Option<LogicalOutput>,
/// Maximum bits per channel (bit depth), if known.
pub max_bpc: Option<u8>,
}
/// Output mode.
@@ -1299,32 +1291,6 @@ pub enum Transform {
Flipped270,
}
/// Output maximum bits per channel.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum MaxBpc {
/// 6-bit.
#[serde(rename = "6")]
_6 = 6,
/// 8-bit.
#[default]
#[serde(rename = "8")]
_8 = 8,
/// 10-bit.
#[serde(rename = "10")]
_10 = 10,
/// 12-bit.
#[serde(rename = "12")]
_12 = 12,
/// 14-bit.
#[serde(rename = "14")]
_14 = 14,
/// 16-bit.
#[serde(rename = "16")]
_16 = 16,
}
/// Toplevel window.
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
@@ -1902,30 +1868,6 @@ impl FromStr for Transform {
}
}
impl TryFrom<u8> for MaxBpc {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
6 => Ok(MaxBpc::_6),
8 => Ok(MaxBpc::_8),
10 => Ok(MaxBpc::_10),
12 => Ok(MaxBpc::_12),
14 => Ok(MaxBpc::_14),
16 => Ok(MaxBpc::_16),
_ => Err("invalid max-bpc, can be 6, 8, 10, 12, 14, 16"),
}
}
}
impl FromStr for MaxBpc {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s.parse::<u8>().unwrap_or_default())
}
}
impl FromStr for Layer {
type Err = &'static str;
+3 -1
View File
@@ -38,6 +38,7 @@ SourceLicense: GPL-3.0-or-later
# 0BSD OR MIT OR Apache-2.0
# Apache-2.0
# Apache-2.0 AND MIT
# Apache-2.0 OR BSL-1.0
# Apache-2.0 OR MIT
# Apache-2.0 OR MIT OR Unlicense
# Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
@@ -52,10 +53,11 @@ SourceLicense: GPL-3.0-or-later
# MIT OR Apache-2.0 OR Zlib
# MIT OR Zlib OR Apache-2.0
# MPL-2.0
# Unicode-3.0
# Unlicense OR MIT
# Zlib
# Zlib OR Apache-2.0 OR MIT
License: ((MIT OR Apache-2.0) AND BSD-3-Clause) AND ((MIT OR Apache-2.0) AND Unicode-3.0) AND (0BSD OR MIT OR Apache-2.0) AND Apache-2.0 AND (Apache-2.0 AND MIT) AND (Apache-2.0 OR MIT) AND (Apache-2.0 OR MIT OR Unlicense) AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND BSD-2-Clause AND (BSD-2-Clause OR Apache-2.0 OR MIT) AND (BSD-3-Clause OR MIT OR Apache-2.0) AND GPL-3.0-or-later AND ISC AND MIT AND (MIT OR Apache-2.0) AND (MIT OR Apache-2.0 OR LGPL-2.1-or-later) AND (MIT OR Apache-2.0 OR Zlib) AND (MIT OR Zlib OR Apache-2.0) AND MPL-2.0 AND (Unlicense OR MIT) AND Zlib AND (Zlib OR Apache-2.0 OR MIT)
License: ((MIT OR Apache-2.0) AND BSD-3-Clause) AND ((MIT OR Apache-2.0) AND Unicode-3.0) AND (0BSD OR MIT OR Apache-2.0) AND (Apache-2.0) AND (Apache-2.0 AND MIT) AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR MIT) AND (Apache-2.0 OR MIT OR Unlicense) AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (BSD-2-Clause) AND (BSD-2-Clause OR Apache-2.0 OR MIT) AND (BSD-3-Clause OR MIT OR Apache-2.0) AND (GPL-3.0-or-later) AND (ISC) AND (MIT) AND (MIT OR Apache-2.0) AND (MIT OR Apache-2.0 OR LGPL-2.1-or-later) AND (MIT OR Apache-2.0 OR Zlib) AND (MIT OR Zlib OR Apache-2.0) AND (MPL-2.0) AND (Unicode-3.0) AND (Unlicense OR MIT) AND (Zlib) AND (Zlib OR Apache-2.0 OR MIT)
# LICENSE.dependencies contains a full license breakdown
URL: https://github.com/niri-wm/niri
-1
View File
@@ -383,7 +383,6 @@ binds {
// Example media keys mapping using playerctl.
// This will work with any MPRIS-enabled media player.
XF86AudioPlay allow-when-locked=true { spawn-sh "playerctl play-pause"; }
XF86AudioPause allow-when-locked=true { spawn-sh "playerctl play-pause"; }
XF86AudioStop allow-when-locked=true { spawn-sh "playerctl stop"; }
XF86AudioPrev allow-when-locked=true { spawn-sh "playerctl previous"; }
XF86AudioNext allow-when-locked=true { spawn-sh "playerctl next"; }
+1 -1
View File
@@ -15,7 +15,7 @@ if [ -n "$SHELL" ] &&
! (echo "$SHELL" | grep -q "false") &&
! (echo "$SHELL" | grep -q "nologin"); then
if [ "$1" != '-l' ]; then
exec bash -c "exec -l '$SHELL' -c 'exec $0 -l $*'"
exec bash -c "exec -l '$SHELL' -c '$0 -l $*'"
else
shift
fi
+1 -4
View File
@@ -3,7 +3,7 @@ use std::thread;
use accesskit::{
ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Live, Node, NodeId, Role,
Tree, TreeId, TreeUpdate,
Tree, TreeUpdate,
};
use accesskit_unix::Adapter;
use calloop::LoopHandle;
@@ -220,7 +220,6 @@ impl Niri {
let update = TreeUpdate {
nodes,
tree: None,
tree_id: TreeId::ROOT,
focus,
};
@@ -247,7 +246,6 @@ impl Niri {
let update = TreeUpdate {
nodes: vec![(ID_ANNOUNCEMENT, node)],
tree: None,
tree_id: TreeId::ROOT,
focus: self.a11y.focus,
};
@@ -341,7 +339,6 @@ impl Niri {
(ID_MRU, mru),
],
tree: Some(tree),
tree_id: TreeId::ROOT,
focus,
}
}
-1
View File
@@ -109,7 +109,6 @@ impl Headless {
vrr_supported: false,
vrr_enabled: false,
logical: Some(logical_output(&output)),
max_bpc: None,
},
);
+56 -143
View File
@@ -14,7 +14,7 @@ use anyhow::{anyhow, bail, ensure, Context};
use bytemuck::cast_slice_mut;
use drm_ffi::drm_mode_modeinfo;
use libc::dev_t;
use niri_config::output::{MaxBpc, Modeline};
use niri_config::output::Modeline;
use niri_config::{Config, OutputName};
use niri_ipc::{HSyncPolarity, VSyncPolarity};
use smithay::backend::allocator::dmabuf::Dmabuf;
@@ -70,11 +70,7 @@ use crate::render_helpers::renderer::AsGlesRenderer;
use crate::render_helpers::{resources, shaders, RenderCtx, RenderTarget};
use crate::utils::{get_monotonic_time, is_laptop_panel, logical_output, PanelOrientation};
const SUPPORTED_COLOR_FORMATS: [Fourcc; 8] = [
Fourcc::Xrgb2101010,
Fourcc::Xbgr2101010,
Fourcc::Argb2101010,
Fourcc::Abgr2101010,
const SUPPORTED_COLOR_FORMATS: [Fourcc; 4] = [
Fourcc::Xrgb8888,
Fourcc::Xbgr8888,
Fourcc::Argb8888,
@@ -409,8 +405,6 @@ struct ConnectorProperties<'a> {
device: &'a DrmDevice,
connector: connector::Handle,
properties: Vec<(property::Info, property::RawValue)>,
has_change: bool,
requests: AtomicModeReq,
}
impl Tty {
@@ -682,19 +676,16 @@ impl Tty {
// Apply pending gamma changes and restore our existing gamma.
let device = self.devices.get_mut(&node).unwrap();
for (crtc, surface) in device.surfaces.iter_mut() {
if let Ok(mut props) =
if let Ok(props) =
ConnectorProperties::try_new(&device.drm, surface.connector)
{
let max_bpc = self
.config
.borrow()
.outputs
.find(&surface.name)
.and_then(|o| o.max_bpc);
set_connector_properties(&mut props, max_bpc, true);
match reset_hdr(&props) {
Ok(()) => (),
Err(err) => debug!("couldn't reset HDR properties: {err:?}"),
}
} else {
warn!("failed to get connector properties");
}
};
if let Some(ramp) = surface.pending_gamma_change.take() {
let ramp = ramp.as_deref();
@@ -1311,10 +1302,13 @@ impl Tty {
debug!("picking mode: {mode:?}");
let mut orientation = None;
if let Ok(mut props) = ConnectorProperties::try_new(&device.drm, connector.handle()) {
set_connector_properties(&mut props, config.max_bpc, true);
if let Ok(props) = ConnectorProperties::try_new(&device.drm, connector.handle()) {
match reset_hdr(&props) {
Ok(()) => (),
Err(err) => debug!("couldn't reset HDR properties: {err:?}"),
}
match props.get_panel_orientation() {
match get_panel_orientation(&props) {
Ok(x) => orientation = Some(x),
Err(err) => {
trace!("couldn't get panel orientation: {err:?}");
@@ -1322,7 +1316,7 @@ impl Tty {
}
} else {
warn!("failed to get connector properties");
}
};
let mut gamma_props = GammaProps::new(&device.drm, crtc)
.map_err(|err| debug!("couldn't get gamma properties: {err:?}"))
@@ -2200,15 +2194,6 @@ impl Tty {
OutputId::next()
});
let props = ConnectorProperties::try_new(&device.drm, connector.handle()).ok();
let max_bpc = props.as_ref().and_then(|p| p.find(c"max bpc").ok());
let max_bpc = max_bpc.and_then(|(info, value)| {
info.value_type()
.convert_value(*value)
.as_unsigned_range()
.map(|v| v as u8)
});
let ipc_output = niri_ipc::Output {
name: connector_name,
make: output_name.make.unwrap_or_else(|| "Unknown".into()),
@@ -2221,7 +2206,6 @@ impl Tty {
vrr_supported,
vrr_enabled,
logical,
max_bpc,
};
ipc_outputs.insert(id, ipc_output);
@@ -2438,13 +2422,6 @@ impl Tty {
},
};
if let Ok(mut props) = ConnectorProperties::try_new(&device.drm, surface.connector)
{
set_connector_properties(&mut props, config.max_bpc, false);
} else {
warn!("failed to get connector properties");
}
let change_mode = surface.compositor.pending_mode() != mode;
let vrr_enabled = surface.compositor.vrr_enabled();
@@ -3273,8 +3250,6 @@ impl<'a> ConnectorProperties<'a> {
device,
connector,
properties,
has_change: false,
requests: AtomicModeReq::new(),
})
}
@@ -3287,115 +3262,35 @@ impl<'a> ConnectorProperties<'a> {
Err(anyhow!("couldn't find property: {name:?}"))
}
fn get_panel_orientation(&self) -> anyhow::Result<Transform> {
let (info, value) = self.find(c"panel orientation")?;
match info.value_type().convert_value(*value) {
property::Value::Enum(Some(val)) => match val.value() {
// "Normal"
0 => Ok(Transform::Normal),
// "Upside Down"
1 => Ok(Transform::_180),
// "Left Side Up"
2 => Ok(Transform::_90),
// "Right Side Up"
3 => Ok(Transform::_270),
_ => bail!("panel orientation has invalid value: {:?}", val),
},
_ => bail!("panel orientation has wrong value type"),
}
}
fn reset_hdr(&mut self) -> anyhow::Result<()> {
const DRM_MODE_COLORIMETRY_DEFAULT: u64 = 0;
let (info, value) = self.find(c"HDR_OUTPUT_METADATA")?;
let property::ValueType::Blob = info.value_type() else {
bail!("wrong property type")
};
if *value != 0 {
self.requests
.add_raw_property(self.connector.into(), info.handle(), 0);
self.has_change = true;
}
let (info, value) = self.find(c"Colorspace")?;
let property::ValueType::Enum(_) = info.value_type() else {
bail!("wrong property type")
};
if *value != DRM_MODE_COLORIMETRY_DEFAULT {
self.requests.add_raw_property(
self.connector.into(),
info.handle(),
DRM_MODE_COLORIMETRY_DEFAULT,
);
self.has_change = true;
}
Ok(())
}
fn set_max_bpc(&mut self, max_bpc: MaxBpc) -> anyhow::Result<u64> {
let (info, value) = self.find(c"max bpc")?;
let property::ValueType::UnsignedRange(min, max) = info.value_type() else {
bail!("wrong property type")
};
let max_bpc = max_bpc.0 as u64;
if !(min..=max).contains(&max_bpc) {
bail!("max-bpc {max_bpc} outside valid range of [{min}, {max}]");
}
let property::Value::UnsignedRange(value) = info.value_type().convert_value(*value) else {
bail!("wrong property type")
};
if value != max_bpc {
self.requests.add_raw_property(
self.connector.into(),
info.handle(),
property::Value::UnsignedRange(max_bpc).into(),
);
self.has_change = true;
}
Ok(max_bpc)
}
fn commit(&mut self) -> anyhow::Result<()> {
if self.has_change {
self.device.atomic_commit(
AtomicCommitFlags::ALLOW_MODESET,
std::mem::take(&mut self.requests),
)?;
}
Ok(())
}
}
fn set_connector_properties(
props: &mut ConnectorProperties,
max_bpc: Option<MaxBpc>,
reset_hdr: bool,
) {
if let Some(max_bpc) = max_bpc {
if let Err(err) = props.set_max_bpc(max_bpc) {
debug!("failed to set `max bpc` property: {err}");
}
const DRM_MODE_COLORIMETRY_DEFAULT: u64 = 0;
fn reset_hdr(props: &ConnectorProperties) -> anyhow::Result<()> {
let (info, value) = props.find(c"HDR_OUTPUT_METADATA")?;
let property::ValueType::Blob = info.value_type() else {
bail!("wrong property type")
};
if *value != 0 {
props
.device
.set_property(props.connector, info.handle(), 0)
.context("error setting property")?;
}
if reset_hdr {
if let Err(err) = props.reset_hdr() {
debug!("failed to set HDR properties: {err}");
}
let (info, value) = props.find(c"Colorspace")?;
let property::ValueType::Enum(_) = info.value_type() else {
bail!("wrong property type")
};
if *value != DRM_MODE_COLORIMETRY_DEFAULT {
props
.device
.set_property(props.connector, info.handle(), DRM_MODE_COLORIMETRY_DEFAULT)
.context("error setting property")?;
}
if let Err(err) = props.commit() {
warn!("failed to atomically commit properties: {err}");
}
Ok(())
}
fn is_vrr_capable(device: &DrmDevice, connector: connector::Handle) -> Option<bool> {
@@ -3403,6 +3298,24 @@ fn is_vrr_capable(device: &DrmDevice, connector: connector::Handle) -> Option<bo
info.value_type().convert_value(value).as_boolean()
}
fn get_panel_orientation(props: &ConnectorProperties) -> anyhow::Result<Transform> {
let (info, value) = props.find(c"panel orientation")?;
match info.value_type().convert_value(*value) {
property::Value::Enum(Some(val)) => match val.value() {
// "Normal"
0 => Ok(Transform::Normal),
// "Upside Down"
1 => Ok(Transform::_180),
// "Left Side Up"
2 => Ok(Transform::_90),
// "Right Side Up"
3 => Ok(Transform::_270),
_ => bail!("panel orientation has invalid value: {:?}", val),
},
_ => bail!("panel orientation has wrong value type"),
}
}
pub fn set_gamma_for_crtc(
device: &DrmDevice,
crtc: crtc::Handle,
-1
View File
@@ -95,7 +95,6 @@ impl Winit {
vrr_supported: false,
vrr_enabled: false,
logical: Some(logical_output(&output)),
max_bpc: None,
},
)])));
+199 -302
View File
@@ -1,4 +1,5 @@
use std::any::Any;
use std::cmp::min;
use std::collections::hash_map::Entry;
use std::collections::HashSet;
use std::time::Duration;
@@ -296,68 +297,41 @@ impl State {
let device_output = event.device().output(self);
let device_output = device_output.filter(|output| self.niri.output_exists(output));
let device_output = device_output.as_ref();
let mapped_output = device_output.or_else(|| self.niri.output_for_tablet());
let (target_geo, keep_ratio, px, transform) =
if let Some(output) = device_output.or_else(|| self.niri.output_for_tablet()) {
(
self.niri.global_space.output_geometry(output).unwrap(),
true,
1. / output.current_scale().fractional_scale(),
output.current_transform(),
)
} else {
let geo = self.global_bounding_rectangle()?;
// If the tablet is configured to map to the focused window, use that window's geometry on
// the mapped output (or on the focused output if no specific output is mapped).
let map_to_focused_window = self.niri.config.borrow().input.tablet.map_to_focused_window;
// But only if the keyboard focus is on the layout, so that it doesn't trigger on the lock
// screen and such.
let window_target = if map_to_focused_window && self.niri.keyboard_focus.is_layout() {
let output = mapped_output.or_else(|| self.niri.layout.active_output());
output.and_then(|output| {
let monitor = self.niri.layout.monitor_for_output(output)?;
let mut rect = monitor.active_window_visual_rectangle()?;
let output_geo = self.niri.global_space.output_geometry(output)?;
rect.loc += output_geo.loc.to_f64();
Some((rect, output))
})
} else {
None
};
// FIXME: this 1 px size should ideally somehow be computed for the rightmost output
// corresponding to the position on the right when clamping.
let output = self.niri.global_space.outputs().next().unwrap();
let scale = output.current_scale().fractional_scale();
let (target_geo, keep_ratio, px, transform) = if let Some((rect, output)) = window_target {
(
rect,
true,
1. / output.current_scale().fractional_scale(),
output.current_transform(),
)
} else if let Some(output) = mapped_output {
let geo = self.niri.global_space.output_geometry(output).unwrap();
(
geo.to_f64(),
true,
1. / output.current_scale().fractional_scale(),
output.current_transform(),
)
} else {
let geo = self.global_bounding_rectangle()?.to_f64();
// FIXME: this 1 px size should ideally somehow be computed for the rightmost output
// corresponding to the position on the right when clamping.
let output = self.niri.global_space.outputs().next().unwrap();
let scale = output.current_scale().fractional_scale();
// Do not keep ratio for the unified mode as this is what OpenTabletDriver expects.
(geo, false, 1. / scale, Transform::Normal)
};
// Do not keep ratio for the unified mode as this is what OpenTabletDriver expects.
(geo, false, 1. / scale, Transform::Normal)
};
let mut pos = {
let size = transform.invert().transform_size(target_geo.size);
transform.transform_point_in(event.position_transformed(size.to_i32_round()), &size)
transform.transform_point_in(event.position_transformed(size), &size.to_f64())
};
if keep_ratio {
pos.x /= target_geo.size.w;
pos.y /= target_geo.size.h;
pos.x /= target_geo.size.w as f64;
pos.y /= target_geo.size.h as f64;
let device = event.device();
if let Some(device) = (&device as &dyn Any).downcast_ref::<input::Device>() {
if let Some(data) = self.niri.tablets.get(device) {
// This code does the same thing as mutter with "keep aspect ratio" enabled.
let size = transform.invert().transform_size(target_geo.size);
let output_aspect_ratio = size.w / size.h;
let output_aspect_ratio = size.w as f64 / size.h as f64;
let ratio = data.aspect_ratio / output_aspect_ratio;
if ratio > 1. {
@@ -368,13 +342,13 @@ impl State {
}
};
pos.x *= target_geo.size.w;
pos.y *= target_geo.size.h;
pos.x *= target_geo.size.w as f64;
pos.y *= target_geo.size.h as f64;
}
pos.x = pos.x.clamp(0.0, target_geo.size.w - px);
pos.y = pos.y.clamp(0.0, target_geo.size.h - px);
Some(pos + target_geo.loc)
pos.x = pos.x.clamp(0.0, target_geo.size.w as f64 - px);
pos.y = pos.y.clamp(0.0, target_geo.size.h as f64 - px);
Some(pos + target_geo.loc.to_f64())
}
fn is_inhibiting_shortcuts(&self) -> bool {
@@ -2546,10 +2520,16 @@ impl State {
if let Some(output) = self.niri.screenshot_ui.selection_output() {
let geom = self.niri.global_space.output_geometry(output).unwrap();
let point = (new_pos - geom.loc.to_f64())
let mut point = (new_pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round::<i32>();
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = point.x.clamp(0, size.w - 1);
point.y = point.y.clamp(0, size.h - 1);
self.niri.screenshot_ui.pointer_motion(point, None);
}
@@ -2677,10 +2657,16 @@ impl State {
if let Some(output) = self.niri.screenshot_ui.selection_output() {
let geom = self.niri.global_space.output_geometry(output).unwrap();
let point = (pos - geom.loc.to_f64())
let mut point = (pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round::<i32>();
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = point.x.clamp(0, size.w - 1);
point.y = point.y.clamp(0, size.h - 1);
self.niri.screenshot_ui.pointer_motion(point, None);
}
@@ -2765,11 +2751,10 @@ impl State {
return;
}
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let modifiers = modifiers_from_state(mods);
let mod_down = modifiers.contains(mod_key.to_modifiers());
if ButtonState::Pressed == button_state {
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let modifiers = modifiers_from_state(mods);
let mut is_mru_open = false;
if let Some(mru_output) = self.niri.window_mru_ui.output() {
is_mru_open = true;
@@ -2806,9 +2791,6 @@ impl State {
let bindings =
make_binds_iter(&config, &mut self.niri.window_mru_ui, modifiers);
find_configured_bind(bindings, mod_key, trigger, mods)
})
.filter(|bind| {
!self.niri.screenshot_ui.is_open() || allowed_during_screenshot(&bind.action)
}) {
self.niri.suppressed_buttons.insert(button_code);
self.handle_bind(bind.clone());
@@ -2850,41 +2832,44 @@ impl State {
}
}
if button == Some(MouseButton::Middle) && !pointer.is_grabbed() && mod_down {
let output_ws = if is_overview_open {
self.niri.workspace_under_cursor(true)
} else {
// We don't want to accidentally "catch" the wrong workspace during
// animations.
self.niri.output_under_cursor().and_then(|output| {
let mon = self.niri.layout.monitor_for_output(&output)?;
Some((output, mon.active_workspace_ref()))
})
};
if let Some((output, ws)) = output_ws {
let ws_id = ws.id();
self.niri.layout.focus_output(&output);
let location = pointer.current_location();
let start_data = PointerGrabStartData {
focus: None,
button: button_code,
location,
if button == Some(MouseButton::Middle) && !pointer.is_grabbed() {
let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());
if mod_down {
let output_ws = if is_overview_open {
self.niri.workspace_under_cursor(true)
} else {
// We don't want to accidentally "catch" the wrong workspace during
// animations.
self.niri.output_under_cursor().and_then(|output| {
let mon = self.niri.layout.monitor_for_output(&output)?;
Some((output, mon.active_workspace_ref()))
})
};
let grab = SpatialMovementGrab::new(start_data, output, ws_id, false);
pointer.set_grab(self, grab, serial, Focus::Clear);
self.niri
.cursor_manager
.set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));
// FIXME: granular.
self.niri.queue_redraw_all();
if let Some((output, ws)) = output_ws {
let ws_id = ws.id();
// Don't activate the window under the cursor to avoid unnecessary
// scrolling when e.g. Mod+MMB clicking on a partially off-screen window.
return;
self.niri.layout.focus_output(&output);
let location = pointer.current_location();
let start_data = PointerGrabStartData {
focus: None,
button: button_code,
location,
};
let grab = SpatialMovementGrab::new(start_data, output, ws_id, false);
pointer.set_grab(self, grab, serial, Focus::Clear);
self.niri
.cursor_manager
.set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));
// FIXME: granular.
self.niri.queue_redraw_all();
// Don't activate the window under the cursor to avoid unnecessary
// scrolling when e.g. Mod+MMB clicking on a partially off-screen window.
return;
}
}
}
@@ -2893,6 +2878,7 @@ impl State {
// Check if we need to start an interactive move.
if button == Some(MouseButton::Left) && !pointer.is_grabbed() {
let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());
if is_overview_open || mod_down {
let location = pointer.current_location();
@@ -2926,69 +2912,72 @@ impl State {
}
}
// Check if we need to start an interactive resize.
else if button == Some(MouseButton::Right) && !pointer.is_grabbed() && mod_down {
let location = pointer.current_location();
let (output, pos_within_output) = self.niri.output_under(location).unwrap();
let edges = self
.niri
.layout
.resize_edges_under(output, pos_within_output)
.unwrap_or(ResizeEdge::empty());
if !edges.is_empty() {
// See if we got a double resize-click gesture.
// FIXME: deduplicate with resize_request in xdg-shell somehow.
let time = get_monotonic_time();
let last_cell = mapped.last_interactive_resize_start();
let mut last = last_cell.get();
last_cell.set(Some((time, edges)));
// Floating windows don't have either of the double-resize-click
// gestures, so just allow it to resize.
if mapped.is_floating() {
last = None;
last_cell.set(None);
}
if let Some((last_time, last_edges)) = last {
if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME {
// Allow quick resize after a triple click.
last_cell.set(None);
let intersection = edges.intersection(last_edges);
if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
// FIXME: don't activate once we can pass specific windows
// to actions.
self.niri.layout.activate_window(&window);
self.niri.layout.toggle_full_width();
}
if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
self.niri.layout.activate_window(&window);
self.niri.layout.reset_window_height(Some(&window));
}
// FIXME: granular.
self.niri.queue_redraw_all();
return;
}
}
self.niri.layout.activate_window(&window);
if self
else if button == Some(MouseButton::Right) && !pointer.is_grabbed() {
let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());
if mod_down {
let location = pointer.current_location();
let (output, pos_within_output) = self.niri.output_under(location).unwrap();
let edges = self
.niri
.layout
.interactive_resize_begin(window.clone(), edges)
{
let start_data = PointerGrabStartData {
focus: None,
button: button_code,
location,
};
let grab = ResizeGrab::new(start_data, window.clone());
pointer.set_grab(self, grab, serial, Focus::Clear);
self.niri
.cursor_manager
.set_cursor_image(CursorImageStatus::Named(edges.cursor_icon()));
.resize_edges_under(output, pos_within_output)
.unwrap_or(ResizeEdge::empty());
if !edges.is_empty() {
// See if we got a double resize-click gesture.
// FIXME: deduplicate with resize_request in xdg-shell somehow.
let time = get_monotonic_time();
let last_cell = mapped.last_interactive_resize_start();
let mut last = last_cell.get();
last_cell.set(Some((time, edges)));
// Floating windows don't have either of the double-resize-click
// gestures, so just allow it to resize.
if mapped.is_floating() {
last = None;
last_cell.set(None);
}
if let Some((last_time, last_edges)) = last {
if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME {
// Allow quick resize after a triple click.
last_cell.set(None);
let intersection = edges.intersection(last_edges);
if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
// FIXME: don't activate once we can pass specific windows
// to actions.
self.niri.layout.activate_window(&window);
self.niri.layout.toggle_full_width();
}
if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
self.niri.layout.activate_window(&window);
self.niri.layout.reset_window_height(Some(&window));
}
// FIXME: granular.
self.niri.queue_redraw_all();
return;
}
}
self.niri.layout.activate_window(&window);
if self
.niri
.layout
.interactive_resize_begin(window.clone(), edges)
{
let start_data = PointerGrabStartData {
focus: None,
button: button_code,
location,
};
let grab = ResizeGrab::new(start_data, window.clone());
pointer.set_grab(self, grab, serial, Focus::Clear);
self.niri.cursor_manager.set_cursor_image(
CursorImageStatus::Named(edges.cursor_icon()),
);
}
}
}
}
@@ -3028,25 +3017,20 @@ impl State {
if button == Some(MouseButton::Left) && self.niri.screenshot_ui.is_open() {
if button_state == ButtonState::Pressed {
let pos = pointer.current_location();
// If we'll be moving the existing selection, use the selection output.
let output = if mod_down {
self.niri.screenshot_ui.selection_output()
} else {
self.niri.output_under(pos).map(|(out, _)| out)
};
if let Some(output) = output.cloned() {
if let Some((output, _)) = self.niri.output_under(pos) {
let output = output.clone();
let geom = self.niri.global_space.output_geometry(&output).unwrap();
let point = (pos - geom.loc.to_f64())
let mut point = (pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round();
if self
.niri
.screenshot_ui
.pointer_down(output, point, None, mod_down)
{
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = min(size.w - 1, point.x);
point.y = min(size.h - 1, point.y);
if self.niri.screenshot_ui.pointer_down(output, point, None) {
self.niri.queue_redraw_all();
}
}
@@ -3162,21 +3146,13 @@ impl State {
mod_key,
Trigger::WheelScrollLeft,
mods,
)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
);
let bind_right = find_configured_bind(
bindings,
mod_key,
Trigger::WheelScrollRight,
mods,
)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
);
(bind_left, bind_right)
};
@@ -3257,17 +3233,9 @@ impl State {
mod_key,
Trigger::WheelScrollUp,
mods,
)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
);
let bind_down =
find_configured_bind(bindings, mod_key, Trigger::WheelScrollDown, mods)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
find_configured_bind(bindings, mod_key, Trigger::WheelScrollDown, mods);
(bind_up, bind_down)
};
@@ -3410,17 +3378,9 @@ impl State {
mod_key,
Trigger::TouchpadScrollLeft,
mods,
)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
);
let bind_right =
find_configured_bind(bindings, mod_key, Trigger::TouchpadScrollRight, mods)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
find_configured_bind(bindings, mod_key, Trigger::TouchpadScrollRight, mods);
drop(config);
if let Some(right) = bind_right {
@@ -3448,17 +3408,9 @@ impl State {
mod_key,
Trigger::TouchpadScrollUp,
mods,
)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
);
let bind_down =
find_configured_bind(bindings, mod_key, Trigger::TouchpadScrollDown, mods)
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
find_configured_bind(bindings, mod_key, Trigger::TouchpadScrollDown, mods);
drop(config);
if let Some(down) = bind_down {
@@ -3562,10 +3514,16 @@ impl State {
if let Some(output) = self.niri.screenshot_ui.selection_output() {
let geom = self.niri.global_space.output_geometry(output).unwrap();
let point = (pos - geom.loc.to_f64())
let mut point = (pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round::<i32>();
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = point.x.clamp(0, size.w - 1);
point.y = point.y.clamp(0, size.h - 1);
self.niri.screenshot_ui.pointer_motion(point, None);
}
@@ -3638,29 +3596,19 @@ impl State {
let under = self.niri.contents_under(pos);
if self.niri.screenshot_ui.is_open() {
let mod_key = self.backend.mod_key(&self.niri.config.borrow());
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let modifiers = modifiers_from_state(mods);
let mod_down = modifiers.contains(mod_key.to_modifiers());
// If we'll be moving the existing selection, use the selection output.
let output = if mod_down {
self.niri.screenshot_ui.selection_output()
} else {
under.output.as_ref()
};
if let Some(output) = output.cloned() {
if let Some(output) = under.output.clone() {
let geom = self.niri.global_space.output_geometry(&output).unwrap();
let point = (pos - geom.loc.to_f64())
let mut point = (pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round();
if self
.niri
.screenshot_ui
.pointer_down(output, point, None, mod_down)
{
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = min(size.w - 1, point.x);
point.y = min(size.h - 1, point.y);
if self.niri.screenshot_ui.pointer_down(output, point, None) {
self.niri.queue_redraw_all();
}
}
@@ -3778,53 +3726,11 @@ impl State {
}
fn on_tablet_tool_button<I: InputBackend>(&mut self, event: I::TabletToolButtonEvent) {
const BTN_STYLUS3: u32 = 0x149;
const BTN_STYLUS: u32 = 0x14b;
const BTN_STYLUS2: u32 = 0x14c;
let tool = self.niri.seat.tablet_seat().get_tool(&event.tool());
if let Some(tool) = tool {
let button = event.button();
if self.niri.suppressed_buttons.remove(&button) {
return;
}
let trigger = match button {
BTN_STYLUS => Some(Trigger::TabletStylusButton1),
BTN_STYLUS2 => Some(Trigger::TabletStylusButton2),
BTN_STYLUS3 => Some(Trigger::TabletStylusButton3),
_ => None,
};
if let Some(trigger) = trigger {
if event.button_state() == ButtonState::Pressed {
let mod_key = self.backend.mod_key(&self.niri.config.borrow());
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let modifiers = modifiers_from_state(mods);
if self.niri.mods_with_tablet_stylus_binds.contains(&modifiers) {
let bind = {
let config = self.niri.config.borrow();
let bindings = config.binds.0.iter();
find_configured_bind(bindings, mod_key, trigger, mods)
}
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
if let Some(bind) = bind {
self.niri.suppressed_buttons.insert(button);
self.handle_bind(bind.clone());
return;
}
}
}
}
tool.button(
button,
event.button(),
event.button_state(),
SERIAL_COUNTER.next_serial(),
event.time_msec(),
@@ -4164,28 +4070,24 @@ impl State {
let under = self.niri.contents_under(pos);
let mod_key = self.backend.mod_key(&self.niri.config.borrow());
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let mods = modifiers_from_state(mods);
let mod_down = mods.contains(mod_key.to_modifiers());
if self.niri.screenshot_ui.is_open() {
// If we'll be moving the existing selection, use the selection output.
let output = if mod_down {
self.niri.screenshot_ui.selection_output()
} else {
under.output.as_ref()
};
if let Some(output) = output.cloned() {
if let Some(output) = under.output.clone() {
let geom = self.niri.global_space.output_geometry(&output).unwrap();
let point = (pos - geom.loc.to_f64())
let mut point = (pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round();
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = min(size.w - 1, point.x);
point.y = min(size.h - 1, point.y);
if self
.niri
.screenshot_ui
.pointer_down(output, point, Some(slot), mod_down)
.pointer_down(output, point, Some(slot))
{
self.niri.queue_redraw_all();
}
@@ -4204,6 +4106,10 @@ impl State {
}
}
} else if !handle.is_grabbed() {
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let mods = modifiers_from_state(mods);
let mod_down = mods.contains(mod_key.to_modifiers());
if self.niri.layout.is_overview_open()
&& !mod_down
&& under.layer.is_none()
@@ -4316,10 +4222,16 @@ impl State {
if let Some(output) = self.niri.screenshot_ui.selection_output().cloned() {
let geom = self.niri.global_space.output_geometry(&output).unwrap();
let point = (pos - geom.loc.to_f64())
let mut point = (pos - geom.loc.to_f64())
.to_physical(output.current_scale().fractional_scale())
.to_i32_round::<i32>();
let size = output.current_mode().unwrap().size;
let transform = output.current_transform();
let size = transform.transform_size(size);
point.x = point.x.clamp(0, size.w - 1);
point.y = point.y.clamp(0, size.h - 1);
self.niri.screenshot_ui.pointer_motion(point, Some(slot));
self.niri.queue_redraw(&output);
}
@@ -4695,9 +4607,6 @@ fn allowed_during_screenshot(action: &Action) -> bool {
| Action::Suspend
| Action::PowerOffMonitors
| Action::PowerOnMonitors
// Intended for binds such as volume up/down, lock the screen, etc.
| Action::Spawn(_)
| Action::SpawnSh(_)
// The screenshot UI can handle these.
| Action::MoveColumnLeft
| Action::MoveColumnLeftOrToMonitorLeft
@@ -5111,18 +5020,6 @@ pub fn mods_with_finger_scroll_binds(mod_key: ModKey, binds: &Binds) -> HashSet<
)
}
pub fn mods_with_tablet_stylus_binds(mod_key: ModKey, binds: &Binds) -> HashSet<Modifiers> {
mods_with_binds(
mod_key,
binds,
&[
Trigger::TabletStylusButton1,
Trigger::TabletStylusButton2,
Trigger::TabletStylusButton3,
],
)
}
fn grab_allows_hot_corner(grab: &(dyn PointerGrab<State> + 'static)) -> bool {
let grab = grab.as_any();
+1 -6
View File
@@ -220,7 +220,7 @@ pub fn handle_msg(mut msg: Msg, json: bool) -> anyhow::Result<()> {
let print = |surface: &niri_ipc::LayerSurface| {
println!(" Surface:");
println!(" Namespace: \"{}\"", surface.namespace);
println!(" Namespace: \"{}\"", &surface.namespace);
let interactivity = match surface.keyboard_interactivity {
niri_ipc::LayerSurfaceKeyboardInteractivity::None => "none",
@@ -568,7 +568,6 @@ fn print_output(output: Output) -> anyhow::Result<()> {
vrr_supported,
vrr_enabled,
logical,
max_bpc,
} = output;
let serial = serial.as_deref().unwrap_or("Unknown");
@@ -652,10 +651,6 @@ fn print_output(output: Output) -> anyhow::Result<()> {
println!(" Transform: {transform}");
}
if let Some(max_bpc) = max_bpc {
println!(" Max bits per channel: {max_bpc}");
}
println!(" Available modes:");
for (idx, mode) in modes.into_iter().enumerate() {
let Mode {
+5 -6
View File
@@ -345,17 +345,16 @@ impl<W: LayoutElement> FloatingSpace<W> {
compute_toplevel_bounds(border_config, self.working_area.size)
}
/// Returns the geometry of the active window relative to and clamped to the working area.
/// Returns the geometry of the active tile relative to and clamped to the working area.
///
/// During animations, assumes the final tile position.
pub fn active_window_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
let (tile, offset) = self.tiles_with_offsets().next()?;
let window_pos = offset + tile.window_loc();
let window_size = tile.window_size();
let window_rect = Rectangle::new(window_pos, window_size);
let tile_size = tile.tile_size();
let tile_rect = Rectangle::new(offset, tile_size);
self.working_area.intersection(window_rect)
self.working_area.intersection(tile_rect)
}
pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
+3 -3
View File
@@ -1342,15 +1342,15 @@ impl<W: LayoutElement> Monitor<W> {
self.clean_up_workspaces();
}
/// Returns the geometry of the active window relative to and clamped to the output.
/// Returns the geometry of the active tile relative to and clamped to the output.
///
/// During animations, assumes the final view position.
pub fn active_window_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
if self.overview_open {
return None;
}
self.active_workspace_ref().active_window_visual_rectangle()
self.active_workspace_ref().active_tile_visual_rectangle()
}
fn workspace_size(&self, zoom: f64) -> Size<f64, Logical> {
+6 -6
View File
@@ -2540,10 +2540,10 @@ impl<W: LayoutElement> ScrollingSpace<W> {
Some(hint_area)
}
/// Returns the geometry of the active window relative to and clamped to the view.
/// Returns the geometry of the active tile relative to and clamped to the view.
///
/// During animations, assumes the final view position.
pub fn active_window_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
let col = self.columns.get(self.active_column_idx)?;
let final_view_offset = self.view_offset.target();
@@ -2551,12 +2551,12 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let (tile, tile_off) = col.tiles().nth(col.active_tile_idx).unwrap();
let window_pos = view_off + tile_off + tile.window_loc();
let window_size = tile.window_size();
let window_rect = Rectangle::new(window_pos, window_size);
let tile_pos = view_off + tile_off;
let tile_size = tile.tile_size();
let tile_rect = Rectangle::new(tile_pos, tile_size);
let view = Rectangle::from_size(self.view_size);
view.intersection(window_rect)
view.intersection(tile_rect)
}
pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
+3 -3
View File
@@ -1609,11 +1609,11 @@ impl<W: LayoutElement> Workspace<W> {
floating.chain(scrolling)
}
pub fn active_window_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
if self.floating_is_active.get() {
self.floating.active_window_visual_rectangle()
self.floating.active_tile_visual_rectangle()
} else {
self.scrolling.active_window_visual_rectangle()
self.scrolling.active_tile_visual_rectangle()
}
}
+2 -15
View File
@@ -14,7 +14,6 @@ use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as
use anyhow::{bail, ensure, Context};
use calloop::futures::Scheduler;
use niri_config::debug::PreviewRender;
use niri_config::output::MaxBpc;
use niri_config::{
Config, FloatOrInt, Key, Modifiers, OutputName, TrackLayout, WarpMouseToFocusMode,
WorkspaceReference, Xkb,
@@ -134,7 +133,7 @@ use crate::input::scroll_swipe_gesture::ScrollSwipeGesture;
use crate::input::scroll_tracker::ScrollTracker;
use crate::input::{
apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_mouse_binds,
mods_with_tablet_stylus_binds, mods_with_wheel_binds, TabletData,
mods_with_wheel_binds, TabletData,
};
use crate::ipc::server::IpcServer;
use crate::layer::mapped::LayerSurfaceRenderElement;
@@ -376,7 +375,6 @@ pub struct Niri {
pub horizontal_wheel_tracker: ScrollTracker,
pub mods_with_mouse_binds: HashSet<Modifiers>,
pub mods_with_wheel_binds: HashSet<Modifiers>,
pub mods_with_tablet_stylus_binds: HashSet<Modifiers>,
pub vertical_finger_scroll_tracker: ScrollTracker,
pub horizontal_finger_scroll_tracker: ScrollTracker,
pub mods_with_finger_scroll_binds: HashSet<Modifiers>,
@@ -936,7 +934,7 @@ impl State {
let monitor = self.niri.layout.monitor_for_output(output).unwrap();
let mut rv = false;
let rect = monitor.active_window_visual_rectangle();
let rect = monitor.active_tile_visual_rectangle();
if let Some(rect) = rect {
let output_geo = self.niri.global_space.output_geometry(output).unwrap();
@@ -993,12 +991,6 @@ impl State {
pub fn confirm_mru(&mut self) {
if let Some(window) = self.niri.close_mru(MruCloseRequest::Confirm) {
// focus_window() will warp the cursor to the window only when the keyboard focus is on
// the layout. However, right now the keyboard focus is still on the MRU (that we had
// just closed) since it's only updated at the end of the event loop cycle. Force-update
// the keyboard focus here to make cursor warping work.
self.update_keyboard_focus();
self.focus_window(&window);
}
}
@@ -1541,8 +1533,6 @@ impl State {
.on_hotkey_config_updated(new_mod_key);
self.niri.mods_with_mouse_binds = mods_with_mouse_binds(new_mod_key, &config.binds);
self.niri.mods_with_wheel_binds = mods_with_wheel_binds(new_mod_key, &config.binds);
self.niri.mods_with_tablet_stylus_binds =
mods_with_tablet_stylus_binds(new_mod_key, &config.binds);
self.niri.mods_with_finger_scroll_binds =
mods_with_finger_scroll_binds(new_mod_key, &config.binds);
}
@@ -1935,7 +1925,6 @@ impl State {
None
}
}
niri_ipc::OutputAction::MaxBpc { max_bpc } => config.max_bpc = Some(MaxBpc(max_bpc)),
});
self.reload_output_config();
@@ -2416,7 +2405,6 @@ impl Niri {
let mods_with_mouse_binds = mods_with_mouse_binds(mod_key, &config_.binds);
let mods_with_wheel_binds = mods_with_wheel_binds(mod_key, &config_.binds);
let mods_with_finger_scroll_binds = mods_with_finger_scroll_binds(mod_key, &config_.binds);
let mods_with_tablet_stylus_binds = mods_with_tablet_stylus_binds(mod_key, &config_.binds);
let screenshot_ui = ScreenshotUi::new(animation_clock.clone(), config.clone());
let window_mru_ui = WindowMruUi::new(config.clone());
@@ -2598,7 +2586,6 @@ impl Niri {
horizontal_wheel_tracker: ScrollTracker::new(120),
mods_with_mouse_binds,
mods_with_wheel_binds,
mods_with_tablet_stylus_binds,
// 10 is copied from Clutter: DISCRETE_SCROLL_STEP.
vertical_finger_scroll_tracker: ScrollTracker::new(10),
+1 -1
View File
@@ -200,7 +200,7 @@ fn refresh_workspace_group(protocol_state: &mut ExtWorkspaceManagerState, output
// Send workspace_enter for all existing workspaces on this output.
for group in &data.instances {
let manager: &ExtWorkspaceManagerV1 = group.data().unwrap();
for ws in protocol_state.workspaces.values() {
for (_, ws) in protocol_state.workspaces.iter() {
if ws.output.as_ref() != Some(output) {
continue;
}
+1 -1
View File
@@ -250,7 +250,7 @@ impl OutputManagementManagerState {
notify_new_head(self, output, conf);
}
}
for old in self.current_state.keys() {
for (old, _) in self.current_state.iter() {
if !new_state.contains_key(old) {
changed = true;
notify_removed_head(&mut self.clients, old);
+4 -4
View File
@@ -209,7 +209,7 @@ impl Blur {
let mut fbos = [0; 2];
gl.GenFramebuffers(fbos.len() as _, fbos.as_mut_ptr());
gl.BindFramebuffer(ffi::FRAMEBUFFER, fbos[0]);
gl.BindFramebuffer(ffi::DRAW_FRAMEBUFFER, fbos[0]);
let program = &self.program.0.down;
gl.UseProgram(program.program);
@@ -244,7 +244,7 @@ impl Blur {
trace!("drawing down {src} to {dst}");
gl.FramebufferTexture2D(
ffi::FRAMEBUFFER,
ffi::DRAW_FRAMEBUFFER,
ffi::COLOR_ATTACHMENT0,
ffi::TEXTURE_2D,
dst,
@@ -307,7 +307,7 @@ impl Blur {
trace!("drawing up {src} to {dst}");
gl.FramebufferTexture2D(
ffi::FRAMEBUFFER,
ffi::DRAW_FRAMEBUFFER,
ffi::COLOR_ATTACHMENT0,
ffi::TEXTURE_2D,
dst,
@@ -333,7 +333,7 @@ impl Blur {
gl.DisableVertexAttribArray(program.attrib_vert as u32);
gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
gl.BindFramebuffer(ffi::DRAW_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(fbos.len() as _, fbos.as_ptr());
})?;
-3
View File
@@ -560,9 +560,6 @@ fn key_name(screen_reader: bool, mod_key: ModKey, key: &Key) -> String {
Trigger::TouchpadScrollUp => String::from("Touchpad Scroll Up"),
Trigger::TouchpadScrollLeft => String::from("Touchpad Scroll Left"),
Trigger::TouchpadScrollRight => String::from("Touchpad Scroll Right"),
Trigger::TabletStylusButton1 => String::from("Tablet Stylus Button 1"),
Trigger::TabletStylusButton2 => String::from("Tablet Stylus Button 2"),
Trigger::TabletStylusButton3 => String::from("Tablet Stylus Button 3"),
};
name.push_str(&pretty);
+9 -34
View File
@@ -799,8 +799,6 @@ impl ScreenshotUi {
}
/// The pointer has moved to `point` relative to the current selection output.
///
/// The point may be outside output bounds.
pub fn pointer_motion(&mut self, point: Point<i32, Physical>, slot: Option<TouchSlot>) {
let Self::Open {
selection,
@@ -840,8 +838,7 @@ impl ScreenshotUi {
selection.1 += delta;
selection.2 += delta;
} else {
let size = output_data[&selection.0].size;
selection.2 = Point::new(point.x.clamp(0, size.w - 1), point.y.clamp(0, size.h - 1));
selection.2 = point;
}
self.update_buffers();
@@ -852,7 +849,6 @@ impl ScreenshotUi {
output: Output,
point: Point<i32, Physical>,
slot: Option<TouchSlot>,
move_existing: bool,
) -> bool {
let Self::Open {
selection,
@@ -887,23 +883,6 @@ impl ScreenshotUi {
return false;
}
if move_existing {
if output != selection.0 {
return false;
}
*button = Button::Down {
touch_slot: slot,
on_capture_button: false,
last_pos: (output, point),
move_state: Some(MoveState {
pointer_offset: point - selection.1,
touch_slot: slot,
}),
};
return true;
}
let Some(output_data) = output_data.get(&output) else {
return false;
};
@@ -930,11 +909,6 @@ impl ScreenshotUi {
last_pos: (output.clone(), point),
move_state: None,
};
let point = Point::new(
point.x.clamp(0, output_data.size.w - 1),
point.y.clamp(0, output_data.size.h - 1),
);
*selection = (output, point, point);
self.update_buffers();
@@ -965,14 +939,15 @@ impl ScreenshotUi {
return None;
};
if touch_slot != slot {
// This is not our main touch, but it might be the move touch. If so, stop the move.
if let Some(state) = move_state {
if state.touch_slot.is_some_and(|m_slot| Some(m_slot) == slot) {
*move_state = None;
}
};
// Check if this is a move touch and if so, stop the move.
if let Some(state) = move_state {
if state.touch_slot.is_some_and(|m_slot| Some(m_slot) == slot) {
*move_state = None;
return None;
}
};
if touch_slot != slot {
return None;
}