Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af8fb49dd0 |
@@ -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'
|
||||
@@ -2,7 +2,7 @@
|
||||
name: Bug report
|
||||
about: Report a bug or a crash
|
||||
title: ''
|
||||
type: Bug
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 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" />
|
||||

|
||||
|
||||
## 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/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -53,12 +53,12 @@ It shows up as "niri Dynamic Cast Target" in the screencast window dialog.
|
||||
|
||||

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

|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 131 B |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 352 KiB After Width: | Height: | Size: 131 B |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 131 B |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 130 B |
@@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,6 @@ impl Headless {
|
||||
vrr_supported: false,
|
||||
vrr_enabled: false,
|
||||
logical: Some(logical_output(&output)),
|
||||
max_bpc: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -95,7 +95,6 @@ impl Winit {
|
||||
vrr_supported: false,
|
||||
vrr_enabled: false,
|
||||
logical: Some(logical_output(&output)),
|
||||
max_bpc: None,
|
||||
},
|
||||
)])));
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
})?;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||