mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffe25f5cc4 | |||
| 43e2cf14d2 | |||
| 2c59131f7f | |||
| 64c41fa2c8 | |||
| 4e0aa39113 | |||
| dcb80efc88 | |||
| 3d5de921cd | |||
| 8703feedee | |||
| a27d22571d | |||
| d10af92aea | |||
| 0bc83eda71 | |||
| 6fce5662e7 | |||
| 1c7c5b3f28 | |||
| b9d7812f1f | |||
| 655b9808b9 | |||
| 5cd31e5730 | |||
| de3fc2def0 | |||
| fd1d4b07fd | |||
| 8b5acd5e6e | |||
| 31bb9096e2 | |||
| dae93ee159 | |||
| 57a7347620 | |||
| 628891db2c | |||
| be6e25f5fb | |||
| e005a795e7 | |||
| 655fe413fb | |||
| ac6ff7ff41 | |||
| 84befb4e91 | |||
| d39f7bebf3 | |||
| 0dd9a42087 | |||
| 658941f2c3 | |||
| 6ccc4147ae | |||
| 46d5f5ec4d | |||
| c64e96d0d8 | |||
| 44d3a5b9a2 | |||
| 5d95de97a0 | |||
| 56174b2c34 | |||
| 310aa2b464 | |||
| d6c553091f | |||
| 097c415036 | |||
| 2d16c04869 | |||
| 249f2b7a21 | |||
| f3e5e13c45 | |||
| b13892ca63 | |||
| 777ad4ee5c | |||
| c21805bf70 | |||
| bfc2418267 | |||
| 77b4715e0b | |||
| c048abc8b5 | |||
| 4dd7578fe7 | |||
| ea72e4dae8 | |||
| 0c671ee493 | |||
| 324c1efd04 | |||
| 99e75b95b7 | |||
| 489a225fae | |||
| 85cb4b42f6 | |||
| be2e551a89 | |||
| ed3080d908 | |||
| 461ce5f363 | |||
| 624c799ebf |
Generated
+164
-178
@@ -10,9 +10,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
||||
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
@@ -38,9 +38,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-activity"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d"
|
||||
checksum = "39b801912a977c3fd52d80511fe1c0c8480c6f957f21ae2ce1b92ffe970cf4b9"
|
||||
dependencies = [
|
||||
"android-properties",
|
||||
"bitflags 2.4.1",
|
||||
@@ -113,9 +113,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "appendlist"
|
||||
@@ -161,7 +161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 4.0.1",
|
||||
"event-listener 4.0.3",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -177,7 +177,7 @@ dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand 2.0.1",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"slab",
|
||||
]
|
||||
|
||||
@@ -223,7 +223,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-io",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"parking",
|
||||
"polling 3.3.1",
|
||||
"rustix 0.38.28",
|
||||
@@ -247,7 +247,7 @@ version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c"
|
||||
dependencies = [
|
||||
"event-listener 4.0.1",
|
||||
"event-listener 4.0.3",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
@@ -277,7 +277,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -300,19 +300,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.6.0"
|
||||
version = "4.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46"
|
||||
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.74"
|
||||
version = "0.1.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
|
||||
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -350,7 +350,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -397,9 +397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "block-sys"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92"
|
||||
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
]
|
||||
@@ -425,7 +425,7 @@ dependencies = [
|
||||
"async-task",
|
||||
"fastrand 2.0.1",
|
||||
"futures-io",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"piper",
|
||||
"tracing",
|
||||
]
|
||||
@@ -453,7 +453,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -521,9 +521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.15.5"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3"
|
||||
checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
@@ -562,20 +562,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
|
||||
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading 0.7.4",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.11"
|
||||
version = "4.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
|
||||
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -583,9 +583,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.11"
|
||||
version = "4.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
|
||||
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -602,7 +602,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -693,9 +693,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -711,9 +711,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.17"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
|
||||
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -736,9 +736,9 @@ checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.10"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
@@ -818,7 +818,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||
dependencies = [
|
||||
"libloading 0.8.1",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -829,9 +829,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "drm"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58eefd79f5173683872c0c82d0f05c2dc3c583d631259f60bb7a323756b7ff2"
|
||||
checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bytemuck",
|
||||
@@ -842,9 +842,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "drm-ffi"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "220dd8c12ebf2b0cbaffa19e00de02f5f090d363fb900f16ea012c077eea1174"
|
||||
checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6"
|
||||
dependencies = [
|
||||
"drm-sys",
|
||||
"rustix 0.38.28",
|
||||
@@ -858,9 +858,9 @@ checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
||||
|
||||
[[package]]
|
||||
name = "drm-sys"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5115283ec60c99da8a9e5dc3c55f27680211e974c948cb6f3b51f0373190503b"
|
||||
checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.6.3",
|
||||
@@ -890,7 +890,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -928,9 +928,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "4.0.1"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84f2cdcf274580f2d63697192d744727b3198894b1bf02923643bf59e2c26712"
|
||||
checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
@@ -943,7 +943,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
|
||||
dependencies = [
|
||||
"event-listener 4.0.1",
|
||||
"event-listener 4.0.3",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -964,9 +964,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868"
|
||||
checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
@@ -1005,7 +1005,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1025,15 +1025,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
@@ -1052,9 +1052,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143"
|
||||
checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba"
|
||||
dependencies = [
|
||||
"fastrand 2.0.1",
|
||||
"futures-core",
|
||||
@@ -1065,21 +1065,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
@@ -1140,12 +1140,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "0.3.0"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177"
|
||||
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1176,7 +1176,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1406,16 +1406,6 @@ version = "0.2.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.1"
|
||||
@@ -1548,9 +1538,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
version = "0.5.6"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
|
||||
checksum = "7e045d70ddfbc984eacfa964ded019534e8f6cbf36f6410aee0ed5cefa5a9175"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"generator",
|
||||
@@ -1592,9 +1582,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
@@ -1652,7 +1642,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1703,7 +1693,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri"
|
||||
version = "0.1.0-alpha.2"
|
||||
version = "0.1.0-alpha.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
@@ -1714,11 +1704,10 @@ dependencies = [
|
||||
"directories",
|
||||
"git-version",
|
||||
"keyframe",
|
||||
"knuffel",
|
||||
"libc",
|
||||
"log",
|
||||
"logind-zbus",
|
||||
"miette",
|
||||
"niri-config",
|
||||
"notify-rust",
|
||||
"pipewire",
|
||||
"png",
|
||||
@@ -1738,6 +1727,18 @@ dependencies = [
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "niri-config"
|
||||
version = "0.1.0-alpha.3"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"directories",
|
||||
"knuffel",
|
||||
"miette",
|
||||
"smithay",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.4"
|
||||
@@ -1796,23 +1797,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0"
|
||||
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
dependencies = [
|
||||
"proc-macro-crate 2.0.1",
|
||||
"proc-macro-crate 3.0.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1974,9 +1975,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
@@ -2051,12 +2052,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "2.0.1"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a"
|
||||
checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd"
|
||||
dependencies = [
|
||||
"toml_datetime",
|
||||
"toml_edit 0.20.2",
|
||||
"toml_edit 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2085,18 +2085,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1de09527cd2ea2c2d59fb6c2f8c1ab8c71709ed9d1b6d60b0e1c9fbb6fdcb33c"
|
||||
checksum = "d135ede8821cf6376eb7a64148901e1690b788c11ae94dc297ae917dbc91dc0e"
|
||||
dependencies = [
|
||||
"profiling-procmacros",
|
||||
"tracy-client",
|
||||
@@ -2104,12 +2104,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "profiling-procmacros"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d8f36e3c621a72254893ed5cc57d1a069162adb3f98bfef610788661db6ad8d"
|
||||
checksum = "4b322d7d65c1ab449be3c890fcbd0db6e1092d0dd05d79dba2dd28032cebeb05"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2160,9 +2160,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -2365,40 +2365,40 @@ checksum = "621e3680f3e07db4c9c2c3fb07c6223ab2fab2e54bd3c04c3ae037990f428c32"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145"
|
||||
checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -2462,7 +2462,7 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/Smithay/smithay.git#b15d29e596b629ec25474b67cc9ecb2f1729d6a2"
|
||||
source = "git+https://github.com/Smithay/smithay.git#77686d70991d6aa86de2ed787a335ef1643132d8"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags 2.4.1",
|
||||
@@ -2481,7 +2481,7 @@ dependencies = [
|
||||
"input",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libloading 0.8.1",
|
||||
"libloading",
|
||||
"libseat",
|
||||
"once_cell",
|
||||
"pkg-config",
|
||||
@@ -2533,7 +2533,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "smithay-drm-extras"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/Smithay/smithay.git#b15d29e596b629ec25474b67cc9ecb2f1729d6a2"
|
||||
source = "git+https://github.com/Smithay/smithay.git#77686d70991d6aa86de2ed787a335ef1643132d8"
|
||||
dependencies = [
|
||||
"drm",
|
||||
"edid-rs",
|
||||
@@ -2566,9 +2566,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
checksum = "ccbca6f34534eb78dbee83f6b2c9442fea7113f43d9e80ea320f0972ae5dc08d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
@@ -2583,9 +2583,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.41"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2607,9 +2607,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.12"
|
||||
version = "0.12.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
|
||||
checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae"
|
||||
|
||||
[[package]]
|
||||
name = "tauri-winrt-notification"
|
||||
@@ -2623,35 +2623,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.1"
|
||||
version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.0.1",
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix 0.38.28",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.51"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
|
||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.51"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2699,21 +2699,21 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.20.2",
|
||||
"toml_edit 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -2731,9 +2731,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.2"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -2761,7 +2761,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2805,9 +2805,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracy-client"
|
||||
version = "0.16.4"
|
||||
version = "0.16.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82da0d50d9df1106619b1e5b118f39de779f7d8b9c3504485b291cb16fabd20f"
|
||||
checksum = "307e6b7030112fe9640fdd87988a40795549ba75c355f59485d14e6b444d2987"
|
||||
dependencies = [
|
||||
"loom",
|
||||
"once_cell",
|
||||
@@ -2816,9 +2816,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracy-client-sys"
|
||||
version = "0.22.0"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3db0b1cc1bb12a70457300d9affc07acb587390d971a796dac2f4d9bca8df776"
|
||||
checksum = "078c7ed72141b0e4369671a7f7af0eecffe18d753bf0296adca9c7add7276c9d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -2989,7 +2989,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3023,7 +3023,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3196,9 +3196,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57099a701fb3a8043f993e8228dc24229c7b942e2b009a1b962e54489ba1d3bf"
|
||||
checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -3229,15 +3229,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-wsapoll"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -3472,9 +3463,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.29.4"
|
||||
version = "0.29.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7"
|
||||
checksum = "c2376dab13e09c01ad8b679f0dbc7038af4ec43d9a91344338e37bd686481550"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"android-activity",
|
||||
@@ -3519,9 +3510,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.30"
|
||||
version = "0.5.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5"
|
||||
checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -3539,29 +3530,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a"
|
||||
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"gethostname",
|
||||
"libc",
|
||||
"libloading 0.7.4",
|
||||
"nix",
|
||||
"libloading",
|
||||
"once_cell",
|
||||
"winapi",
|
||||
"winapi-wsapoll",
|
||||
"rustix 0.38.28",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc"
|
||||
dependencies = [
|
||||
"nix",
|
||||
]
|
||||
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
@@ -3683,22 +3669,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.31"
|
||||
version = "0.7.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
|
||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.31"
|
||||
version = "0.7.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
|
||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+40
-20
@@ -1,48 +1,68 @@
|
||||
[package]
|
||||
name = "niri"
|
||||
version = "0.1.0-alpha.2"
|
||||
[workspace.package]
|
||||
version = "0.1.0-alpha.3"
|
||||
description = "A scrollable-tiling Wayland compositor"
|
||||
authors = ["Ivan Molodetskikh <yalterz@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/YaLTeR/niri"
|
||||
|
||||
[workspace.dependencies]
|
||||
bitflags = "2.4.1"
|
||||
directories = "5.0.1"
|
||||
tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
|
||||
[workspace.dependencies.smithay]
|
||||
git = "https://github.com/Smithay/smithay.git"
|
||||
# path = "../smithay"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.smithay-drm-extras]
|
||||
git = "https://github.com/Smithay/smithay.git"
|
||||
# path = "../smithay/smithay-drm-extras"
|
||||
|
||||
[package]
|
||||
name = "niri"
|
||||
version.workspace = true
|
||||
description.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/YaLTeR/niri"
|
||||
keywords = ["wayland", "compositor", "tiling", "smithay", "wm"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.75" }
|
||||
anyhow = { version = "1.0.79" }
|
||||
arrayvec = "0.7.4"
|
||||
async-channel = { version = "2.1.1", optional = true }
|
||||
async-io = { version = "1.13.0", optional = true }
|
||||
bitflags = "2.4.1"
|
||||
clap = { version = "4.4.11", features = ["derive"] }
|
||||
clap = { version = "4.4.13", features = ["derive"] }
|
||||
directories = "5.0.1"
|
||||
git-version = "0.3.9"
|
||||
keyframe = { version = "1.1.1", default-features = false }
|
||||
knuffel = "3.2.0"
|
||||
libc = "0.2.151"
|
||||
logind-zbus = { version = "3.1.2", optional = true }
|
||||
log = { version = "0.4.20", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
miette = "5.10.0"
|
||||
niri-config = { version = "0.1.0-alpha.3", path = "niri-config" }
|
||||
notify-rust = { version = "4.10.0", optional = true }
|
||||
pipewire = { version = "0.7.2", optional = true }
|
||||
png = "0.17.10"
|
||||
portable-atomic = { version = "1.6.0", default-features = false, features = ["float"] }
|
||||
profiling = "1.0.12"
|
||||
profiling = "1.0.13"
|
||||
sd-notify = "0.4.1"
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
smithay-drm-extras.workspace = true
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
tracy-client = { version = "0.16.4", default-features = false }
|
||||
tracing.workspace = true
|
||||
tracy-client = { version = "0.16.5", default-features = false }
|
||||
url = { version = "2.5.0", optional = true }
|
||||
xcursor = "0.3.5"
|
||||
zbus = { version = "3.14.1", optional = true }
|
||||
|
||||
[dependencies.smithay]
|
||||
git = "https://github.com/Smithay/smithay.git"
|
||||
# path = "../smithay"
|
||||
default-features = false
|
||||
workspace = true
|
||||
features = [
|
||||
"backend_drm",
|
||||
"backend_egl",
|
||||
@@ -58,10 +78,6 @@ features = [
|
||||
"wayland_frontend",
|
||||
]
|
||||
|
||||
[dependencies.smithay-drm-extras]
|
||||
git = "https://github.com/Smithay/smithay.git"
|
||||
# path = "../smithay/smithay-drm-extras"
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.4.0"
|
||||
proptest-derive = "0.4.0"
|
||||
@@ -80,8 +96,12 @@ debug = "line-tables-only"
|
||||
overflow-checks = true
|
||||
lto = "thin"
|
||||
|
||||
[profile.release.package.niri-config]
|
||||
# knuffel with chomsky generates a metric ton of debuginfo.
|
||||
debug = false
|
||||
|
||||
[package.metadata.generate-rpm]
|
||||
version = "0.1.0~alpha.2"
|
||||
version = "0.1.0~alpha.3"
|
||||
assets = [
|
||||
{ source = "target/release/niri", dest = "/usr/bin/", mode = "755" },
|
||||
{ source = "resources/niri-session", dest = "/usr/bin/", mode = "755" },
|
||||
|
||||
@@ -33,7 +33,10 @@ When a monitor disconnects, its workspaces will move to another monitor, but upo
|
||||
|
||||
## Building
|
||||
|
||||
For Fedora users, there's a COPR with built and packaged niri: https://copr.fedorainfracloud.org/coprs/yalter/niri/
|
||||
> [!TIP]
|
||||
> For Fedora users, there's a COPR with built and packaged niri: https://copr.fedorainfracloud.org/coprs/yalter/niri/
|
||||
>
|
||||
> NixOS users, check out https://github.com/sodiboo/niri-flake
|
||||
|
||||
First, install the dependencies for your distribution.
|
||||
|
||||
@@ -91,6 +94,10 @@ A step-by-step process for this is explained [on the wiki](https://github.com/Ya
|
||||
Niri also works with some parts of xdg-desktop-portal-gnome.
|
||||
In particular, it supports file choosers and monitor screencasting (e.g. to [OBS]).
|
||||
|
||||
### Xwayland
|
||||
|
||||
See [the wiki page](https://github.com/YaLTeR/niri/wiki/Xwayland) to learn how to use Xwayland with niri.
|
||||
|
||||
## Default Hotkeys
|
||||
|
||||
When running on a TTY, the Mod key is <kbd>Super</kbd>.
|
||||
@@ -112,6 +119,8 @@ The general system is: if a hotkey switches somewhere, then adding <kbd>Ctrl</kb
|
||||
| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>→</kbd> | Move the focused column to the right |
|
||||
| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>J</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>↓</kbd> | Move the focused window below in a column |
|
||||
| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>K</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>↑</kbd> | Move the focused window above in a column |
|
||||
| <kbd>Mod</kbd><kbd>Home</kbd> and <kbd>Mod</kbd><kbd>End</kbd> | Focus the first or the last column |
|
||||
| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>Home</kbd> and <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>End</kbd> | Move the focused column to the very start or to the very end |
|
||||
| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>H</kbd><kbd>J</kbd><kbd>K</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>Shift</kbd><kbd>←</kbd><kbd>↓</kbd><kbd>↑</kbd><kbd>→</kbd> | Focus the monitor to the side |
|
||||
| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>H</kbd><kbd>J</kbd><kbd>K</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>←</kbd><kbd>↓</kbd><kbd>↑</kbd><kbd>→</kbd> | Move the focused window to the monitor to the side |
|
||||
| <kbd>Mod</kbd><kbd>U</kbd> or <kbd>Mod</kbd><kbd>PageDown</kbd> | Switch to the workspace below |
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "niri-config"
|
||||
version.workspace = true
|
||||
description.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
directories.workspace = true
|
||||
knuffel = "3.2.0"
|
||||
miette = "5.10.0"
|
||||
smithay.workspace = true
|
||||
tracing.workspace = true
|
||||
@@ -1,9 +1,12 @@
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use directories::ProjectDirs;
|
||||
use miette::{miette, Context, IntoDiagnostic};
|
||||
use miette::{miette, Context, IntoDiagnostic, NarratableReportHandler};
|
||||
use smithay::input::keyboard::keysyms::KEY_NoSymbol;
|
||||
use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE};
|
||||
use smithay::input::keyboard::{Keysym, XkbConfig};
|
||||
@@ -17,19 +20,11 @@ pub struct Config {
|
||||
#[knuffel(children(name = "spawn-at-startup"))]
|
||||
pub spawn_at_startup: Vec<SpawnAtStartup>,
|
||||
#[knuffel(child, default)]
|
||||
pub focus_ring: FocusRing,
|
||||
pub layout: Layout,
|
||||
#[knuffel(child, default)]
|
||||
pub prefer_no_csd: bool,
|
||||
#[knuffel(child, default)]
|
||||
pub cursor: Cursor,
|
||||
#[knuffel(child, unwrap(children), default)]
|
||||
pub preset_column_widths: Vec<PresetWidth>,
|
||||
#[knuffel(child)]
|
||||
pub default_column_width: Option<DefaultColumnWidth>,
|
||||
#[knuffel(child, unwrap(argument), default = 16)]
|
||||
pub gaps: u16,
|
||||
#[knuffel(child, default)]
|
||||
pub struts: Struts,
|
||||
#[knuffel(
|
||||
child,
|
||||
unwrap(argument),
|
||||
@@ -53,6 +48,8 @@ pub struct Input {
|
||||
pub touchpad: Touchpad,
|
||||
#[knuffel(child, default)]
|
||||
pub tablet: Tablet,
|
||||
#[knuffel(child)]
|
||||
pub disable_power_key_handling: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq)]
|
||||
@@ -161,6 +158,22 @@ pub struct Mode {
|
||||
pub refresh: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
||||
pub struct Layout {
|
||||
#[knuffel(child, default)]
|
||||
pub focus_ring: FocusRing,
|
||||
#[knuffel(child, default = default_border())]
|
||||
pub border: FocusRing,
|
||||
#[knuffel(child, unwrap(children), default)]
|
||||
pub preset_column_widths: Vec<PresetWidth>,
|
||||
#[knuffel(child)]
|
||||
pub default_column_width: Option<DefaultColumnWidth>,
|
||||
#[knuffel(child, unwrap(argument), default = 16)]
|
||||
pub gaps: u16,
|
||||
#[knuffel(child, default)]
|
||||
pub struts: Struts,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SpawnAtStartup {
|
||||
#[knuffel(arguments)]
|
||||
@@ -190,6 +203,15 @@ impl Default for FocusRing {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn default_border() -> FocusRing {
|
||||
FocusRing {
|
||||
off: true,
|
||||
width: 4,
|
||||
active_color: Color::new(255, 200, 127, 255),
|
||||
inactive_color: Color::new(80, 80, 80, 255),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Color {
|
||||
#[knuffel(argument)]
|
||||
@@ -203,7 +225,7 @@ pub struct Color {
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
}
|
||||
@@ -300,12 +322,16 @@ pub enum Action {
|
||||
FullscreenWindow,
|
||||
FocusColumnLeft,
|
||||
FocusColumnRight,
|
||||
FocusColumnFirst,
|
||||
FocusColumnLast,
|
||||
FocusWindowDown,
|
||||
FocusWindowUp,
|
||||
FocusWindowOrWorkspaceDown,
|
||||
FocusWindowOrWorkspaceUp,
|
||||
MoveColumnLeft,
|
||||
MoveColumnRight,
|
||||
MoveColumnToFirst,
|
||||
MoveColumnToLast,
|
||||
MoveWindowDown,
|
||||
MoveWindowUp,
|
||||
MoveWindowDownOrToWorkspaceDown,
|
||||
@@ -362,6 +388,10 @@ pub struct DebugConfig {
|
||||
pub enable_color_transformations_capability: bool,
|
||||
#[knuffel(child)]
|
||||
pub enable_overlay_planes: bool,
|
||||
#[knuffel(child)]
|
||||
pub disable_cursor_plane: bool,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub render_drm_device: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for DebugConfig {
|
||||
@@ -372,12 +402,18 @@ impl Default for DebugConfig {
|
||||
wait_for_frame_completion_before_queueing: false,
|
||||
enable_color_transformations_capability: false,
|
||||
enable_overlay_planes: false,
|
||||
disable_cursor_plane: false,
|
||||
render_drm_device: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load(path: Option<PathBuf>) -> miette::Result<(Self, PathBuf)> {
|
||||
Self::load_internal(path).context("error loading config")
|
||||
}
|
||||
|
||||
fn load_internal(path: Option<PathBuf>) -> miette::Result<(Self, PathBuf)> {
|
||||
let path = if let Some(path) = path {
|
||||
path
|
||||
} else {
|
||||
@@ -407,7 +443,7 @@ impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config::parse(
|
||||
"default-config.kdl",
|
||||
include_str!("../resources/default-config.kdl"),
|
||||
include_str!("../../resources/default-config.kdl"),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
@@ -535,6 +571,10 @@ impl FromStr for SizeChange {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_miette_hook() -> Result<(), miette::InstallError> {
|
||||
miette::set_hook(Box::new(|_| Box::new(NarratableReportHandler::new())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use miette::NarratableReportHandler;
|
||||
@@ -574,6 +614,8 @@ mod tests {
|
||||
tablet {
|
||||
map-to-output "eDP-1"
|
||||
}
|
||||
|
||||
disable-power-key-handling
|
||||
}
|
||||
|
||||
output "eDP-1" {
|
||||
@@ -582,14 +624,39 @@ mod tests {
|
||||
mode "1920x1080@144"
|
||||
}
|
||||
|
||||
spawn-at-startup "alacritty" "-e" "fish"
|
||||
layout {
|
||||
focus-ring {
|
||||
width 5
|
||||
active-color 0 100 200 255
|
||||
inactive-color 255 200 100 0
|
||||
}
|
||||
|
||||
focus-ring {
|
||||
width 5
|
||||
active-color 0 100 200 255
|
||||
inactive-color 255 200 100 0
|
||||
border {
|
||||
width 3
|
||||
active-color 0 100 200 255
|
||||
inactive-color 255 200 100 0
|
||||
}
|
||||
|
||||
preset-column-widths {
|
||||
proportion 0.25
|
||||
proportion 0.5
|
||||
fixed 960
|
||||
fixed 1280
|
||||
}
|
||||
|
||||
default-column-width { proportion 0.25; }
|
||||
|
||||
gaps 8
|
||||
|
||||
struts {
|
||||
left 1
|
||||
right 2
|
||||
top 3
|
||||
}
|
||||
}
|
||||
|
||||
spawn-at-startup "alacritty" "-e" "fish"
|
||||
|
||||
prefer-no-csd
|
||||
|
||||
cursor {
|
||||
@@ -597,23 +664,6 @@ mod tests {
|
||||
xcursor-size 16
|
||||
}
|
||||
|
||||
preset-column-widths {
|
||||
proportion 0.25
|
||||
proportion 0.5
|
||||
fixed 960
|
||||
fixed 1280
|
||||
}
|
||||
|
||||
default-column-width { proportion 0.25; }
|
||||
|
||||
gaps 8
|
||||
|
||||
struts {
|
||||
left 1
|
||||
right 2
|
||||
top 3
|
||||
}
|
||||
|
||||
screenshot-path "~/Screenshots/screenshot.png"
|
||||
|
||||
binds {
|
||||
@@ -627,6 +677,7 @@ mod tests {
|
||||
|
||||
debug {
|
||||
animation-slowdown 2.0
|
||||
render-drm-device "/dev/dri/renderD129"
|
||||
}
|
||||
"#,
|
||||
Config {
|
||||
@@ -649,6 +700,7 @@ mod tests {
|
||||
tablet: Tablet {
|
||||
map_to_output: Some("eDP-1".to_owned()),
|
||||
},
|
||||
disable_power_key_handling: true,
|
||||
},
|
||||
outputs: vec![Output {
|
||||
off: false,
|
||||
@@ -661,44 +713,64 @@ mod tests {
|
||||
refresh: Some(144.),
|
||||
}),
|
||||
}],
|
||||
layout: Layout {
|
||||
focus_ring: FocusRing {
|
||||
off: false,
|
||||
width: 5,
|
||||
active_color: Color {
|
||||
r: 0,
|
||||
g: 100,
|
||||
b: 200,
|
||||
a: 255,
|
||||
},
|
||||
inactive_color: Color {
|
||||
r: 255,
|
||||
g: 200,
|
||||
b: 100,
|
||||
a: 0,
|
||||
},
|
||||
},
|
||||
border: FocusRing {
|
||||
off: false,
|
||||
width: 3,
|
||||
active_color: Color {
|
||||
r: 0,
|
||||
g: 100,
|
||||
b: 200,
|
||||
a: 255,
|
||||
},
|
||||
inactive_color: Color {
|
||||
r: 255,
|
||||
g: 200,
|
||||
b: 100,
|
||||
a: 0,
|
||||
},
|
||||
},
|
||||
preset_column_widths: vec![
|
||||
PresetWidth::Proportion(0.25),
|
||||
PresetWidth::Proportion(0.5),
|
||||
PresetWidth::Fixed(960),
|
||||
PresetWidth::Fixed(1280),
|
||||
],
|
||||
default_column_width: Some(DefaultColumnWidth(vec![PresetWidth::Proportion(
|
||||
0.25,
|
||||
)])),
|
||||
gaps: 8,
|
||||
struts: Struts {
|
||||
left: 1,
|
||||
right: 2,
|
||||
top: 3,
|
||||
bottom: 0,
|
||||
},
|
||||
},
|
||||
spawn_at_startup: vec![SpawnAtStartup {
|
||||
command: vec!["alacritty".to_owned(), "-e".to_owned(), "fish".to_owned()],
|
||||
}],
|
||||
focus_ring: FocusRing {
|
||||
off: false,
|
||||
width: 5,
|
||||
active_color: Color {
|
||||
r: 0,
|
||||
g: 100,
|
||||
b: 200,
|
||||
a: 255,
|
||||
},
|
||||
inactive_color: Color {
|
||||
r: 255,
|
||||
g: 200,
|
||||
b: 100,
|
||||
a: 0,
|
||||
},
|
||||
},
|
||||
prefer_no_csd: true,
|
||||
cursor: Cursor {
|
||||
xcursor_theme: String::from("breeze_cursors"),
|
||||
xcursor_size: 16,
|
||||
},
|
||||
preset_column_widths: vec![
|
||||
PresetWidth::Proportion(0.25),
|
||||
PresetWidth::Proportion(0.5),
|
||||
PresetWidth::Fixed(960),
|
||||
PresetWidth::Fixed(1280),
|
||||
],
|
||||
default_column_width: Some(DefaultColumnWidth(vec![PresetWidth::Proportion(0.25)])),
|
||||
gaps: 8,
|
||||
struts: Struts {
|
||||
left: 1,
|
||||
right: 2,
|
||||
top: 3,
|
||||
bottom: 0,
|
||||
},
|
||||
screenshot_path: Some(String::from("~/Screenshots/screenshot.png")),
|
||||
binds: Binds(vec![
|
||||
Bind {
|
||||
@@ -746,6 +818,7 @@ mod tests {
|
||||
]),
|
||||
debug: DebugConfig {
|
||||
animation_slowdown: 2.,
|
||||
render_drm_device: Some(PathBuf::from("/dev/dri/renderD129")),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
@@ -37,6 +37,12 @@ input {
|
||||
// existing outputs.
|
||||
map-to-output "eDP-1"
|
||||
}
|
||||
|
||||
// By default, niri will take over the power button to make it sleep
|
||||
// instead of power off.
|
||||
// Uncomment this if you would like to configure the power button elsewhere
|
||||
// (i.e. logind.conf).
|
||||
// disable-power-key-handling
|
||||
}
|
||||
|
||||
// You can configure outputs by their name, which you can find with wayland-info(1).
|
||||
@@ -69,26 +75,72 @@ input {
|
||||
position x=1280 y=0
|
||||
}
|
||||
|
||||
layout {
|
||||
// You can change how the focus ring looks.
|
||||
focus-ring {
|
||||
// Uncomment this line to disable the focus ring.
|
||||
// off
|
||||
|
||||
// How many logical pixels the ring extends out from the windows.
|
||||
width 4
|
||||
|
||||
// Color of the ring on the active monitor: red, green, blue, alpha.
|
||||
active-color 127 200 255 255
|
||||
|
||||
// Color of the ring on inactive monitors: red, green, blue, alpha.
|
||||
inactive-color 80 80 80 255
|
||||
}
|
||||
|
||||
// You can also add a border. It's similar to the focus ring, but always visible.
|
||||
border {
|
||||
// The settings are the same as for the focus ring.
|
||||
// If you enable the border, you probably want to disable the focus ring.
|
||||
off
|
||||
|
||||
width 4
|
||||
active-color 255 200 127 255
|
||||
inactive-color 80 80 80 255
|
||||
}
|
||||
|
||||
// You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between.
|
||||
preset-column-widths {
|
||||
// Proportion sets the width as a fraction of the output width, taking gaps into account.
|
||||
// For example, you can perfectly fit four windows sized "proportion 0.25" on an output.
|
||||
// The default preset widths are 1/3, 1/2 and 2/3 of the output.
|
||||
proportion 0.333
|
||||
proportion 0.5
|
||||
proportion 0.667
|
||||
|
||||
// Fixed sets the width in logical pixels exactly.
|
||||
// fixed 1920
|
||||
}
|
||||
|
||||
// You can change the default width of the new windows.
|
||||
default-column-width { proportion 0.5; }
|
||||
// If you leave the brackets empty, the windows themselves will decide their initial width.
|
||||
// default-column-width {}
|
||||
|
||||
// Set gaps around windows in logical pixels.
|
||||
gaps 16
|
||||
|
||||
// Struts shrink the area occupied by windows, similarly to layer-shell panels.
|
||||
// You can think of them as a kind of outer gaps. They are set in logical pixels.
|
||||
// Left and right struts will cause the next window to the side to always be visible.
|
||||
// Top and bottom struts will simply add outer gaps in addition to the area occupied by
|
||||
// layer-shell panels and regular gaps.
|
||||
struts {
|
||||
// left 64
|
||||
// right 64
|
||||
// top 64
|
||||
// bottom 64
|
||||
}
|
||||
}
|
||||
|
||||
// Add lines like this to spawn processes at startup.
|
||||
// Note that running niri as a session supports xdg-desktop-autostart,
|
||||
// which may be more convenient to use.
|
||||
// spawn-at-startup "alacritty" "-e" "fish"
|
||||
|
||||
// You can change how the focus ring looks.
|
||||
focus-ring {
|
||||
// Uncomment this line to disable the focus ring.
|
||||
// off
|
||||
|
||||
// How many logical pixels the ring extends out from the windows.
|
||||
width 4
|
||||
|
||||
// Color of the ring on the active monitor: red, green, blue, alpha.
|
||||
active-color 127 200 255 255
|
||||
|
||||
// Color of the ring on inactive monitors: red, green, blue, alpha.
|
||||
inactive-color 80 80 80 255
|
||||
}
|
||||
|
||||
cursor {
|
||||
// Change the theme and size of the cursor as well as set the
|
||||
// `XCURSOR_THEME` and `XCURSOR_SIZE` env variables.
|
||||
@@ -101,39 +153,6 @@ cursor {
|
||||
// Additionally, clients will be informed that they are tiled, removing some rounded corners.
|
||||
// prefer-no-csd
|
||||
|
||||
// You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between.
|
||||
preset-column-widths {
|
||||
// Proportion sets the width as a fraction of the output width, taking gaps into account.
|
||||
// For example, you can perfectly fit four windows sized "proportion 0.25" on an output.
|
||||
// The default preset widths are 1/3, 1/2 and 2/3 of the output.
|
||||
proportion 0.333
|
||||
proportion 0.5
|
||||
proportion 0.667
|
||||
|
||||
// Fixed sets the width in logical pixels exactly.
|
||||
// fixed 1920
|
||||
}
|
||||
|
||||
// You can change the default width of the new windows.
|
||||
default-column-width { proportion 0.5; }
|
||||
// If you leave the brackets empty, the windows themselves will decide their initial width.
|
||||
// default-column-width {}
|
||||
|
||||
// Set gaps around windows in logical pixels.
|
||||
gaps 16
|
||||
|
||||
// Struts shrink the area occupied by windows, similarly to layer-shell panels.
|
||||
// You can think of them as a kind of outer gaps. They are set in logical pixels.
|
||||
// Left and right struts will cause the next window to the side to always be visible.
|
||||
// Top and bottom struts will simply add outer gaps in addition to the area occupied by
|
||||
// layer-shell panels and regular gaps.
|
||||
struts {
|
||||
// left 64
|
||||
// right 64
|
||||
// top 64
|
||||
// bottom 64
|
||||
}
|
||||
|
||||
// You can change the path where screenshots are saved.
|
||||
// A ~ at the front will be expanded to the home directory.
|
||||
// The path is formatted with strftime(3) to give you the screenshot date and time.
|
||||
@@ -186,6 +205,11 @@ binds {
|
||||
// Mod+Ctrl+J { move-window-down-or-to-workspace-down; }
|
||||
// Mod+Ctrl+K { move-window-up-or-to-workspace-up; }
|
||||
|
||||
Mod+Home { focus-column-first; }
|
||||
Mod+End { focus-column-last; }
|
||||
Mod+Ctrl+Home { move-column-to-first; }
|
||||
Mod+Ctrl+End { move-column-to-last; }
|
||||
|
||||
Mod+Shift+H { focus-monitor-left; }
|
||||
Mod+Shift+J { focus-monitor-down; }
|
||||
Mod+Shift+K { focus-monitor-up; }
|
||||
|
||||
+27
-17
@@ -2,11 +2,12 @@ use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
|
||||
use crate::input::CompositorMod;
|
||||
use crate::niri::OutputRenderElements;
|
||||
use crate::Niri;
|
||||
|
||||
pub mod tty;
|
||||
@@ -26,8 +27,8 @@ pub enum RenderResult {
|
||||
Submitted,
|
||||
/// Rendering succeeded, but there was no damage.
|
||||
NoDamage,
|
||||
/// An error has occurred, the frame was not submitted.
|
||||
Error,
|
||||
/// The frame was not rendered and submitted, due to an error or otherwise.
|
||||
Skipped,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
@@ -45,10 +46,13 @@ impl Backend {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn renderer(&mut self) -> Option<&mut GlesRenderer> {
|
||||
pub fn with_primary_renderer<T>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut GlesRenderer) -> T,
|
||||
) -> Option<T> {
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.renderer(),
|
||||
Backend::Winit(winit) => Some(winit.renderer()),
|
||||
Backend::Tty(tty) => tty.with_primary_renderer(f),
|
||||
Backend::Winit(winit) => winit.with_primary_renderer(f),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +60,11 @@ impl Backend {
|
||||
&mut self,
|
||||
niri: &mut Niri,
|
||||
output: &Output,
|
||||
elements: &[OutputRenderElements<GlesRenderer>],
|
||||
target_presentation_time: Duration,
|
||||
) -> RenderResult {
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.render(niri, output, elements, target_presentation_time),
|
||||
Backend::Winit(winit) => winit.render(niri, output, elements),
|
||||
Backend::Tty(tty) => tty.render(niri, output, target_presentation_time),
|
||||
Backend::Winit(winit) => winit.render(niri, output),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +96,20 @@ impl Backend {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_dmabuf(&mut self, dmabuf: &Dmabuf) -> Result<(), ()> {
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.import_dmabuf(dmabuf),
|
||||
Backend::Winit(winit) => winit.import_dmabuf(dmabuf),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn early_import(&mut self, surface: &WlSurface) {
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.early_import(surface),
|
||||
Backend::Winit(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "dbus"), allow(unused))]
|
||||
pub fn connectors(&self) -> Arc<Mutex<HashMap<String, Output>>> {
|
||||
match self {
|
||||
@@ -107,18 +124,11 @@ impl Backend {
|
||||
) -> Option<smithay::backend::allocator::gbm::GbmDevice<smithay::backend::drm::DrmDeviceFd>>
|
||||
{
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.gbm_device(),
|
||||
Backend::Tty(tty) => tty.primary_gbm_device(),
|
||||
Backend::Winit(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.is_active(),
|
||||
Backend::Winit(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_monitors_active(&self, active: bool) {
|
||||
match self {
|
||||
Backend::Tty(tty) => tty.set_monitors_active(active),
|
||||
|
||||
+526
-204
File diff suppressed because it is too large
Load Diff
+33
-23
@@ -5,9 +5,11 @@ use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use niri_config::Config;
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::renderer::damage::OutputDamageTracker;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::{DebugFlags, Renderer};
|
||||
use smithay::backend::renderer::{DebugFlags, ImportDma, ImportEgl, Renderer};
|
||||
use smithay::backend::winit::{self, WinitEvent, WinitGraphicsBackend};
|
||||
use smithay::output::{Mode, Output, PhysicalProperties, Scale, Subpixel};
|
||||
use smithay::reexports::calloop::LoopHandle;
|
||||
@@ -17,8 +19,7 @@ use smithay::reexports::winit::window::WindowBuilder;
|
||||
use smithay::utils::Transform;
|
||||
|
||||
use super::RenderResult;
|
||||
use crate::config::Config;
|
||||
use crate::niri::{OutputRenderElements, RedrawState, State};
|
||||
use crate::niri::{RedrawState, State};
|
||||
use crate::utils::get_monotonic_time;
|
||||
use crate::Niri;
|
||||
|
||||
@@ -113,17 +114,14 @@ impl Winit {
|
||||
}
|
||||
|
||||
pub fn init(&mut self, niri: &mut Niri) {
|
||||
// For some reason, binding the display here causes damage tracker artifacts.
|
||||
//
|
||||
// use smithay::backend::renderer::ImportEgl;
|
||||
//
|
||||
// if let Err(err) = self
|
||||
// .backend
|
||||
// .renderer()
|
||||
// .bind_wl_display(&niri.display_handle)
|
||||
// {
|
||||
// warn!("error binding renderer wl_display: {err}");
|
||||
// }
|
||||
if let Err(err) = self
|
||||
.backend
|
||||
.renderer()
|
||||
.bind_wl_display(&niri.display_handle)
|
||||
{
|
||||
warn!("error binding renderer wl_display: {err}");
|
||||
}
|
||||
|
||||
niri.add_output(self.output.clone(), None);
|
||||
}
|
||||
|
||||
@@ -131,23 +129,25 @@ impl Winit {
|
||||
"winit".to_owned()
|
||||
}
|
||||
|
||||
pub fn renderer(&mut self) -> &mut GlesRenderer {
|
||||
self.backend.renderer()
|
||||
pub fn with_primary_renderer<T>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut GlesRenderer) -> T,
|
||||
) -> Option<T> {
|
||||
Some(f(self.backend.renderer()))
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&mut self,
|
||||
niri: &mut Niri,
|
||||
output: &Output,
|
||||
elements: &[OutputRenderElements<GlesRenderer>],
|
||||
) -> RenderResult {
|
||||
pub fn render(&mut self, niri: &mut Niri, output: &Output) -> RenderResult {
|
||||
let _span = tracy_client::span!("Winit::render");
|
||||
|
||||
// Render the elements.
|
||||
let elements = niri.render::<GlesRenderer>(self.backend.renderer(), output, true);
|
||||
|
||||
// Hand them over to winit.
|
||||
self.backend.bind().unwrap();
|
||||
let age = self.backend.buffer_age().unwrap();
|
||||
let res = self
|
||||
.damage_tracker
|
||||
.render_output(self.backend.renderer(), age, elements, [0.; 4])
|
||||
.render_output(self.backend.renderer(), age, &elements, [0.; 4])
|
||||
.unwrap();
|
||||
|
||||
niri.update_primary_scanout_output(output, &res.states);
|
||||
@@ -202,6 +202,16 @@ impl Winit {
|
||||
renderer.set_debug_flags(renderer.debug_flags() ^ DebugFlags::TINT);
|
||||
}
|
||||
|
||||
pub fn import_dmabuf(&mut self, dmabuf: &Dmabuf) -> Result<(), ()> {
|
||||
match self.backend.renderer().import_dmabuf(dmabuf, None) {
|
||||
Ok(_texture) => Ok(()),
|
||||
Err(err) => {
|
||||
debug!("error importing dmabuf: {err:?}");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connectors(&self) -> Arc<Mutex<HashMap<String, Output>>> {
|
||||
self.connectors.clone()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use smithay::{delegate_compositor, delegate_shm};
|
||||
|
||||
use super::xdg_shell;
|
||||
use crate::niri::{ClientState, State};
|
||||
use crate::utils::clone2;
|
||||
|
||||
impl CompositorHandler for State {
|
||||
fn compositor_state(&mut self) -> &mut CompositorState {
|
||||
@@ -81,6 +82,7 @@ impl CompositorHandler for State {
|
||||
let _span = tracy_client::span!("CompositorHandler::commit");
|
||||
|
||||
on_commit_buffer_handler::<Self>(surface);
|
||||
self.backend.early_import(surface);
|
||||
|
||||
if is_sync_subsurface(surface) {
|
||||
return;
|
||||
@@ -116,8 +118,9 @@ impl CompositorHandler for State {
|
||||
}
|
||||
|
||||
// This is a commit of a previously-mapped root or a non-toplevel root.
|
||||
if let Some((window, output)) = self.niri.layout.find_window_and_output(surface) {
|
||||
// This is a commit of a previously-mapped toplevel.
|
||||
if let Some(win_out) = self.niri.layout.find_window_and_output(surface) {
|
||||
let (window, output) = clone2(win_out);
|
||||
|
||||
window.on_commit();
|
||||
|
||||
// This is a commit of a previously-mapped toplevel.
|
||||
@@ -147,7 +150,7 @@ impl CompositorHandler for State {
|
||||
|
||||
// This is a commit of a non-root or a non-toplevel root.
|
||||
let root_window_output = self.niri.layout.find_window_and_output(&root_surface);
|
||||
if let Some((window, output)) = root_window_output {
|
||||
if let Some((window, output)) = root_window_output.map(clone2) {
|
||||
window.on_commit();
|
||||
self.niri.layout.update_window(&window);
|
||||
self.niri.queue_redraw(output);
|
||||
@@ -158,7 +161,7 @@ impl CompositorHandler for State {
|
||||
self.popups_handle_commit(surface);
|
||||
if let Some(popup) = self.niri.popups.find_popup(surface) {
|
||||
if let Some(output) = self.output_for_popup(&popup) {
|
||||
self.niri.queue_redraw(output);
|
||||
self.niri.queue_redraw(output.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-10
@@ -9,7 +9,6 @@ use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::renderer::ImportDma;
|
||||
use smithay::desktop::{PopupKind, PopupManager};
|
||||
use smithay::input::pointer::{CursorIcon, CursorImageStatus, PointerHandle};
|
||||
use smithay::input::{Seat, SeatHandler, SeatState};
|
||||
@@ -43,8 +42,8 @@ use smithay::{
|
||||
delegate_text_input_manager, delegate_virtual_keyboard_manager,
|
||||
};
|
||||
|
||||
use crate::layout::output_size;
|
||||
use crate::niri::State;
|
||||
use crate::utils::output_size;
|
||||
|
||||
impl SeatHandler for State {
|
||||
type KeyboardFocus = WlSurface;
|
||||
@@ -193,7 +192,7 @@ delegate_presentation!(State);
|
||||
|
||||
impl DmabufHandler for State {
|
||||
fn dmabuf_state(&mut self) -> &mut DmabufState {
|
||||
self.backend.tty().dmabuf_state()
|
||||
&mut self.niri.dmabuf_state
|
||||
}
|
||||
|
||||
fn dmabuf_imported(
|
||||
@@ -202,15 +201,11 @@ impl DmabufHandler for State {
|
||||
dmabuf: Dmabuf,
|
||||
notifier: ImportNotifier,
|
||||
) {
|
||||
let renderer = self.backend.renderer().expect(
|
||||
"the dmabuf global must be created and destroyed together with the output device",
|
||||
);
|
||||
match renderer.import_dmabuf(&dmabuf, None) {
|
||||
Ok(_texture) => {
|
||||
match self.backend.import_dmabuf(&dmabuf) {
|
||||
Ok(_) => {
|
||||
let _ = notifier.successful::<State>();
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("error importing dmabuf: {err:?}");
|
||||
Err(_) => {
|
||||
notifier.failed();
|
||||
}
|
||||
}
|
||||
|
||||
+25
-12
@@ -20,6 +20,7 @@ use smithay::wayland::shell::xdg::{
|
||||
use smithay::{delegate_kde_decoration, delegate_xdg_decoration, delegate_xdg_shell};
|
||||
|
||||
use crate::niri::State;
|
||||
use crate::utils::clone2;
|
||||
|
||||
impl XdgShellHandler for State {
|
||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||
@@ -123,8 +124,10 @@ impl XdgShellHandler for State {
|
||||
.layout
|
||||
.find_window_and_output(surface.wl_surface())
|
||||
{
|
||||
let window = window.clone();
|
||||
|
||||
if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) {
|
||||
if requested_output != current_output {
|
||||
if &requested_output != current_output {
|
||||
self.niri
|
||||
.layout
|
||||
.move_window_to_output(window.clone(), &requested_output);
|
||||
@@ -146,6 +149,7 @@ impl XdgShellHandler for State {
|
||||
.layout
|
||||
.find_window_and_output(surface.wl_surface())
|
||||
{
|
||||
let window = window.clone();
|
||||
self.niri.layout.set_fullscreen(&window, false);
|
||||
}
|
||||
}
|
||||
@@ -166,7 +170,7 @@ impl XdgShellHandler for State {
|
||||
.layout
|
||||
.find_window_and_output(surface.wl_surface());
|
||||
|
||||
let Some((window, output)) = win_out else {
|
||||
let Some((window, output)) = win_out.map(clone2) else {
|
||||
// I have no idea how this can happen, but I saw it happen once, in a weird interaction
|
||||
// involving laptop going to sleep and resuming.
|
||||
error!("toplevel missing from both unmapped_windows and layout");
|
||||
@@ -179,7 +183,7 @@ impl XdgShellHandler for State {
|
||||
|
||||
fn popup_destroyed(&mut self, surface: PopupSurface) {
|
||||
if let Some(output) = self.output_for_popup(&PopupKind::Xdg(surface)) {
|
||||
self.niri.queue_redraw(output);
|
||||
self.niri.queue_redraw(output.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,16 +193,25 @@ delegate_xdg_shell!(State);
|
||||
impl XdgDecorationHandler for State {
|
||||
fn new_decoration(&mut self, toplevel: ToplevelSurface) {
|
||||
let mode = if self.niri.config.borrow().prefer_no_csd {
|
||||
Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||
zxdg_toplevel_decoration_v1::Mode::ServerSide
|
||||
} else {
|
||||
None
|
||||
zxdg_toplevel_decoration_v1::Mode::ClientSide
|
||||
};
|
||||
toplevel.with_pending_state(|state| {
|
||||
state.decoration_mode = mode;
|
||||
state.decoration_mode = Some(mode);
|
||||
});
|
||||
}
|
||||
|
||||
fn request_mode(&mut self, toplevel: ToplevelSurface, mode: zxdg_toplevel_decoration_v1::Mode) {
|
||||
fn request_mode(
|
||||
&mut self,
|
||||
toplevel: ToplevelSurface,
|
||||
mut mode: zxdg_toplevel_decoration_v1::Mode,
|
||||
) {
|
||||
// If prefer-no-csd is unset, then insist on CSD.
|
||||
if !self.niri.config.borrow().prefer_no_csd {
|
||||
mode = zxdg_toplevel_decoration_v1::Mode::ClientSide;
|
||||
}
|
||||
|
||||
toplevel.with_pending_state(|state| {
|
||||
state.decoration_mode = Some(mode);
|
||||
});
|
||||
@@ -211,12 +224,12 @@ impl XdgDecorationHandler for State {
|
||||
|
||||
fn unset_mode(&mut self, toplevel: ToplevelSurface) {
|
||||
let mode = if self.niri.config.borrow().prefer_no_csd {
|
||||
Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||
zxdg_toplevel_decoration_v1::Mode::ServerSide
|
||||
} else {
|
||||
None
|
||||
zxdg_toplevel_decoration_v1::Mode::ClientSide
|
||||
};
|
||||
toplevel.with_pending_state(|state| {
|
||||
state.decoration_mode = mode;
|
||||
state.decoration_mode = Some(mode);
|
||||
});
|
||||
|
||||
// Only send configure if it's non-initial.
|
||||
@@ -288,7 +301,7 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_for_popup(&self, popup: &PopupKind) -> Option<Output> {
|
||||
pub fn output_for_popup(&self, popup: &PopupKind) -> Option<&Output> {
|
||||
let root = find_popup_root_surface(popup).ok()?;
|
||||
self.niri.output_for_root(&root)
|
||||
}
|
||||
@@ -304,7 +317,7 @@ impl State {
|
||||
|
||||
// Figure out if the root is a window or a layer surface.
|
||||
if let Some((window, output)) = self.niri.layout.find_window_and_output(&root) {
|
||||
self.unconstrain_window_popup(popup, &window, &output);
|
||||
self.unconstrain_window_popup(popup, window, output);
|
||||
} else if let Some((layer_surface, output)) = self.niri.layout.outputs().find_map(|o| {
|
||||
let map = layer_map_for_output(o);
|
||||
let layer_surface = map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)?;
|
||||
|
||||
+48
-13
@@ -1,6 +1,7 @@
|
||||
use std::any::Any;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use niri_config::{Action, Binds, LayoutAction, Modifiers};
|
||||
use smithay::backend::input::{
|
||||
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event,
|
||||
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _,
|
||||
@@ -20,7 +21,6 @@ use smithay::utils::{Logical, Point, SERIAL_COUNTER};
|
||||
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
|
||||
use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait};
|
||||
|
||||
use crate::config::{Action, Binds, LayoutAction, Modifiers};
|
||||
use crate::niri::State;
|
||||
use crate::screenshot_ui::ScreenshotUi;
|
||||
use crate::utils::{center, get_monotonic_time, spawn};
|
||||
@@ -209,6 +209,7 @@ impl State {
|
||||
pressed,
|
||||
*mods,
|
||||
&this.niri.screenshot_ui,
|
||||
this.niri.config.borrow().input.disable_power_key_handling,
|
||||
)
|
||||
},
|
||||
) else {
|
||||
@@ -244,6 +245,7 @@ impl State {
|
||||
}
|
||||
Action::ToggleDebugTint => {
|
||||
self.backend.toggle_debug_tint();
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::Spawn(command) => {
|
||||
spawn(command);
|
||||
@@ -251,15 +253,15 @@ impl State {
|
||||
Action::ScreenshotScreen => {
|
||||
let active = self.niri.layout.active_output().cloned();
|
||||
if let Some(active) = active {
|
||||
if let Some(renderer) = self.backend.renderer() {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
if let Err(err) = self.niri.screenshot(renderer, &active) {
|
||||
warn!("error taking screenshot: {err:?}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Action::ConfirmScreenshot => {
|
||||
if let Some(renderer) = self.backend.renderer() {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
match self.niri.screenshot_ui.capture(renderer) {
|
||||
Ok((size, pixels)) => {
|
||||
if let Err(err) = self.niri.save_screenshot(size, pixels) {
|
||||
@@ -270,7 +272,7 @@ impl State {
|
||||
warn!("error capturing screenshot: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.niri.screenshot_ui.close();
|
||||
self.niri
|
||||
@@ -286,18 +288,18 @@ impl State {
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::Screenshot => {
|
||||
if let Some(renderer) = self.backend.renderer() {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
self.niri.open_screenshot_ui(renderer);
|
||||
}
|
||||
});
|
||||
}
|
||||
Action::ScreenshotWindow => {
|
||||
let active = self.niri.layout.active_window();
|
||||
if let Some((window, output)) = active {
|
||||
if let Some(renderer) = self.backend.renderer() {
|
||||
if let Err(err) = self.niri.screenshot_window(renderer, &output, &window) {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
if let Err(err) = self.niri.screenshot_window(renderer, output, window) {
|
||||
warn!("error taking screenshot: {err:?}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Action::CloseWindow => {
|
||||
@@ -330,6 +332,16 @@ impl State {
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToFirst => {
|
||||
self.niri.layout.move_column_to_first();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToLast => {
|
||||
self.niri.layout.move_column_to_last();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowDown => {
|
||||
self.niri.layout.move_down();
|
||||
// FIXME: granular
|
||||
@@ -356,6 +368,12 @@ impl State {
|
||||
Action::FocusColumnRight => {
|
||||
self.niri.layout.focus_right();
|
||||
}
|
||||
Action::FocusColumnFirst => {
|
||||
self.niri.layout.focus_column_first();
|
||||
}
|
||||
Action::FocusColumnLast => {
|
||||
self.niri.layout.focus_column_last();
|
||||
}
|
||||
Action::FocusWindowDown => {
|
||||
self.niri.layout.focus_down();
|
||||
}
|
||||
@@ -800,12 +818,16 @@ impl State {
|
||||
|
||||
let mut frame = AxisFrame::new(event.time_msec()).source(source);
|
||||
if horizontal_amount != 0.0 {
|
||||
frame = frame
|
||||
.relative_direction(Axis::Horizontal, event.relative_direction(Axis::Horizontal));
|
||||
frame = frame.value(Axis::Horizontal, horizontal_amount);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
frame = frame.v120(Axis::Horizontal, discrete as i32);
|
||||
}
|
||||
}
|
||||
if vertical_amount != 0.0 {
|
||||
frame =
|
||||
frame.relative_direction(Axis::Vertical, event.relative_direction(Axis::Vertical));
|
||||
frame = frame.value(Axis::Vertical, vertical_amount);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
frame = frame.v120(Axis::Vertical, discrete as i32);
|
||||
@@ -1165,6 +1187,7 @@ fn should_intercept_key(
|
||||
pressed: bool,
|
||||
mods: ModifiersState,
|
||||
screenshot_ui: &ScreenshotUi,
|
||||
disable_power_key_handling: bool,
|
||||
) -> FilterResult<Option<Action>> {
|
||||
// Actions are only triggered on presses, release of the key
|
||||
// shouldn't try to intercept anything unless we have marked
|
||||
@@ -1173,7 +1196,14 @@ fn should_intercept_key(
|
||||
return FilterResult::Forward;
|
||||
}
|
||||
|
||||
let mut final_action = action(bindings, comp_mod, modified, raw, mods);
|
||||
let mut final_action = action(
|
||||
bindings,
|
||||
comp_mod,
|
||||
modified,
|
||||
raw,
|
||||
mods,
|
||||
disable_power_key_handling,
|
||||
);
|
||||
|
||||
// Allow only a subset of compositor actions while the screenshot UI is open, since the user
|
||||
// cannot see the screen.
|
||||
@@ -1210,6 +1240,7 @@ fn action(
|
||||
modified: Keysym,
|
||||
raw: Option<Keysym>,
|
||||
mods: ModifiersState,
|
||||
disable_power_key_handling: bool,
|
||||
) -> Option<Action> {
|
||||
use keysyms::*;
|
||||
|
||||
@@ -1220,7 +1251,7 @@ fn action(
|
||||
let vt = (modified - KEY_XF86Switch_VT_1 + 1) as i32;
|
||||
return Some(Action::ChangeVt(vt));
|
||||
}
|
||||
KEY_XF86PowerOff => return Some(Action::Suspend),
|
||||
KEY_XF86PowerOff if !disable_power_key_handling => return Some(Action::Suspend),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1307,8 +1338,9 @@ fn allowed_during_screenshot(action: &Action) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use niri_config::{Action, Bind, Binds, Key, Modifiers};
|
||||
|
||||
use super::*;
|
||||
use crate::config::{Action, Bind, Binds, Key, Modifiers};
|
||||
|
||||
#[test]
|
||||
fn bindings_suppress_keys() {
|
||||
@@ -1325,6 +1357,7 @@ mod tests {
|
||||
let mut suppressed_keys = HashSet::new();
|
||||
|
||||
let screenshot_ui = ScreenshotUi::new();
|
||||
let disable_power_key_handling = false;
|
||||
|
||||
// The key_code we pick is arbitrary, the only thing
|
||||
// that matters is that they are different between cases.
|
||||
@@ -1341,6 +1374,7 @@ mod tests {
|
||||
pressed,
|
||||
mods,
|
||||
&screenshot_ui,
|
||||
disable_power_key_handling,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -1356,6 +1390,7 @@ mod tests {
|
||||
pressed,
|
||||
mods,
|
||||
&screenshot_ui,
|
||||
disable_power_key_handling,
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
-4025
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,115 @@
|
||||
use std::iter::zip;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use niri_config::{self, Color};
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::Kind;
|
||||
use smithay::utils::{Logical, Point, Scale, Size};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FocusRing {
|
||||
buffers: [SolidColorBuffer; 4],
|
||||
locations: [Point<i32, Logical>; 4],
|
||||
is_off: bool,
|
||||
is_border: bool,
|
||||
width: i32,
|
||||
active_color: Color,
|
||||
inactive_color: Color,
|
||||
}
|
||||
|
||||
pub type FocusRingRenderElement = SolidColorRenderElement;
|
||||
|
||||
impl FocusRing {
|
||||
pub fn new(config: niri_config::FocusRing) -> Self {
|
||||
Self {
|
||||
buffers: Default::default(),
|
||||
locations: Default::default(),
|
||||
is_off: config.off,
|
||||
is_border: false,
|
||||
width: config.width.into(),
|
||||
active_color: config.active_color,
|
||||
inactive_color: config.inactive_color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_config(&mut self, config: niri_config::FocusRing) {
|
||||
self.is_off = config.off;
|
||||
self.width = config.width.into();
|
||||
self.active_color = config.active_color;
|
||||
self.inactive_color = config.inactive_color;
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
win_pos: Point<i32, Logical>,
|
||||
win_size: Size<i32, Logical>,
|
||||
is_border: bool,
|
||||
) {
|
||||
if is_border {
|
||||
self.buffers[0].resize((win_size.w + self.width * 2, self.width));
|
||||
self.buffers[1].resize((win_size.w + self.width * 2, self.width));
|
||||
self.buffers[2].resize((self.width, win_size.h));
|
||||
self.buffers[3].resize((self.width, win_size.h));
|
||||
|
||||
self.locations[0] = win_pos + Point::from((-self.width, -self.width));
|
||||
self.locations[1] = win_pos + Point::from((-self.width, win_size.h));
|
||||
self.locations[2] = win_pos + Point::from((-self.width, 0));
|
||||
self.locations[3] = win_pos + Point::from((win_size.w, 0));
|
||||
} else {
|
||||
let size = win_size + Size::from((self.width * 2, self.width * 2));
|
||||
self.buffers[0].resize(size);
|
||||
self.locations[0] = win_pos - Point::from((self.width, self.width));
|
||||
}
|
||||
|
||||
self.is_border = is_border;
|
||||
}
|
||||
|
||||
pub fn set_active(&mut self, is_active: bool) {
|
||||
let color = if is_active {
|
||||
self.active_color.into()
|
||||
} else {
|
||||
self.inactive_color.into()
|
||||
};
|
||||
|
||||
for buf in &mut self.buffers {
|
||||
buf.set_color(color);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, scale: Scale<f64>) -> impl Iterator<Item = FocusRingRenderElement> {
|
||||
let mut rv = ArrayVec::<_, 4>::new();
|
||||
|
||||
if self.is_off {
|
||||
return rv.into_iter();
|
||||
}
|
||||
|
||||
let mut push = |buffer, location: Point<i32, Logical>| {
|
||||
let elem = SolidColorRenderElement::from_buffer(
|
||||
buffer,
|
||||
location.to_physical_precise_round(scale),
|
||||
scale,
|
||||
1.,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
rv.push(elem);
|
||||
};
|
||||
|
||||
if self.is_border {
|
||||
for (buf, loc) in zip(&self.buffers, self.locations) {
|
||||
push(buf, loc);
|
||||
}
|
||||
} else {
|
||||
push(&self.buffers[0], self.locations[0]);
|
||||
}
|
||||
|
||||
rv.into_iter()
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub fn is_off(&self) -> bool {
|
||||
self.is_off
|
||||
}
|
||||
}
|
||||
+2217
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,584 @@
|
||||
use std::cmp::min;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use niri_config::SizeChange;
|
||||
use smithay::backend::renderer::element::utils::{
|
||||
CropRenderElement, Relocate, RelocateRenderElement,
|
||||
};
|
||||
use smithay::backend::renderer::{ImportAll, Renderer};
|
||||
use smithay::desktop::Window;
|
||||
use smithay::output::Output;
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale};
|
||||
|
||||
use super::workspace::{
|
||||
compute_working_area, ColumnWidth, OutputId, Workspace, WorkspaceRenderElement,
|
||||
};
|
||||
use super::{LayoutElement, Options};
|
||||
use crate::animation::Animation;
|
||||
use crate::utils::output_size;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Monitor<W: LayoutElement> {
|
||||
/// Output for this monitor.
|
||||
pub output: Output,
|
||||
// Must always contain at least one.
|
||||
pub workspaces: Vec<Workspace<W>>,
|
||||
/// Index of the currently active workspace.
|
||||
pub active_workspace_idx: usize,
|
||||
/// In-progress switch between workspaces.
|
||||
pub workspace_switch: Option<WorkspaceSwitch>,
|
||||
/// Configurable properties of the layout.
|
||||
pub options: Rc<Options>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WorkspaceSwitch {
|
||||
Animation(Animation),
|
||||
Gesture(WorkspaceSwitchGesture),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WorkspaceSwitchGesture {
|
||||
/// Index of the workspace where the gesture was started.
|
||||
pub center_idx: usize,
|
||||
/// Current, fractional workspace index.
|
||||
pub current_idx: f64,
|
||||
}
|
||||
|
||||
pub type MonitorRenderElement<R> =
|
||||
RelocateRenderElement<CropRenderElement<WorkspaceRenderElement<R>>>;
|
||||
|
||||
impl WorkspaceSwitch {
|
||||
pub fn current_idx(&self) -> f64 {
|
||||
match self {
|
||||
WorkspaceSwitch::Animation(anim) => anim.value(),
|
||||
WorkspaceSwitch::Gesture(gesture) => gesture.current_idx,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the workspace switch is [`Animation`].
|
||||
///
|
||||
/// [`Animation`]: WorkspaceSwitch::Animation
|
||||
#[must_use]
|
||||
fn is_animation(&self) -> bool {
|
||||
matches!(self, Self::Animation(..))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Monitor<W> {
|
||||
pub fn new(output: Output, workspaces: Vec<Workspace<W>>, options: Rc<Options>) -> Self {
|
||||
Self {
|
||||
output,
|
||||
workspaces,
|
||||
active_workspace_idx: 0,
|
||||
workspace_switch: None,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_workspace(&mut self) -> &mut Workspace<W> {
|
||||
&mut self.workspaces[self.active_workspace_idx]
|
||||
}
|
||||
|
||||
fn activate_workspace(&mut self, idx: usize) {
|
||||
if self.active_workspace_idx == idx {
|
||||
return;
|
||||
}
|
||||
|
||||
let current_idx = self
|
||||
.workspace_switch
|
||||
.as_ref()
|
||||
.map(|s| s.current_idx())
|
||||
.unwrap_or(self.active_workspace_idx as f64);
|
||||
|
||||
self.active_workspace_idx = idx;
|
||||
|
||||
self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(
|
||||
current_idx,
|
||||
idx as f64,
|
||||
Duration::from_millis(250),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn add_window(
|
||||
&mut self,
|
||||
workspace_idx: usize,
|
||||
window: W,
|
||||
activate: bool,
|
||||
width: ColumnWidth,
|
||||
is_full_width: bool,
|
||||
) {
|
||||
let workspace = &mut self.workspaces[workspace_idx];
|
||||
|
||||
workspace.add_window(window, activate, width, is_full_width);
|
||||
|
||||
// After adding a new window, workspace becomes this output's own.
|
||||
workspace.original_output = OutputId::new(&self.output);
|
||||
|
||||
if workspace_idx == self.workspaces.len() - 1 {
|
||||
// Insert a new empty workspace.
|
||||
let ws = Workspace::new(self.output.clone(), self.options.clone());
|
||||
self.workspaces.push(ws);
|
||||
}
|
||||
|
||||
if activate {
|
||||
self.activate_workspace(workspace_idx);
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_up_workspaces(&mut self) {
|
||||
assert!(self.workspace_switch.is_none());
|
||||
|
||||
for idx in (0..self.workspaces.len() - 1).rev() {
|
||||
if self.active_workspace_idx == idx {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !self.workspaces[idx].has_windows() {
|
||||
self.workspaces.remove(idx);
|
||||
if self.active_workspace_idx > idx {
|
||||
self.active_workspace_idx -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_left(&mut self) {
|
||||
self.active_workspace().move_left();
|
||||
}
|
||||
|
||||
pub fn move_right(&mut self) {
|
||||
self.active_workspace().move_right();
|
||||
}
|
||||
|
||||
pub fn move_column_to_first(&mut self) {
|
||||
self.active_workspace().move_column_to_first();
|
||||
}
|
||||
|
||||
pub fn move_column_to_last(&mut self) {
|
||||
self.active_workspace().move_column_to_last();
|
||||
}
|
||||
|
||||
pub fn move_down(&mut self) {
|
||||
self.active_workspace().move_down();
|
||||
}
|
||||
|
||||
pub fn move_up(&mut self) {
|
||||
self.active_workspace().move_up();
|
||||
}
|
||||
|
||||
pub fn move_down_or_to_workspace_down(&mut self) {
|
||||
let workspace = self.active_workspace();
|
||||
if workspace.columns.is_empty() {
|
||||
return;
|
||||
}
|
||||
let column = &mut workspace.columns[workspace.active_column_idx];
|
||||
let curr_idx = column.active_tile_idx;
|
||||
let new_idx = min(column.active_tile_idx + 1, column.tiles.len() - 1);
|
||||
if curr_idx == new_idx {
|
||||
self.move_to_workspace_down();
|
||||
} else {
|
||||
workspace.move_down();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_up_or_to_workspace_up(&mut self) {
|
||||
let workspace = self.active_workspace();
|
||||
if workspace.columns.is_empty() {
|
||||
return;
|
||||
}
|
||||
let curr_idx = workspace.columns[workspace.active_column_idx].active_tile_idx;
|
||||
let new_idx = curr_idx.saturating_sub(1);
|
||||
if curr_idx == new_idx {
|
||||
self.move_to_workspace_up();
|
||||
} else {
|
||||
workspace.move_up();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus_left(&mut self) {
|
||||
self.active_workspace().focus_left();
|
||||
}
|
||||
|
||||
pub fn focus_right(&mut self) {
|
||||
self.active_workspace().focus_right();
|
||||
}
|
||||
|
||||
pub fn focus_column_first(&mut self) {
|
||||
self.active_workspace().focus_column_first();
|
||||
}
|
||||
|
||||
pub fn focus_column_last(&mut self) {
|
||||
self.active_workspace().focus_column_last();
|
||||
}
|
||||
|
||||
pub fn focus_down(&mut self) {
|
||||
self.active_workspace().focus_down();
|
||||
}
|
||||
|
||||
pub fn focus_up(&mut self) {
|
||||
self.active_workspace().focus_up();
|
||||
}
|
||||
|
||||
pub fn focus_window_or_workspace_down(&mut self) {
|
||||
let workspace = self.active_workspace();
|
||||
if workspace.columns.is_empty() {
|
||||
self.switch_workspace_down();
|
||||
} else {
|
||||
let column = &workspace.columns[workspace.active_column_idx];
|
||||
let curr_idx = column.active_tile_idx;
|
||||
let new_idx = min(column.active_tile_idx + 1, column.tiles.len() - 1);
|
||||
if curr_idx == new_idx {
|
||||
self.switch_workspace_down();
|
||||
} else {
|
||||
workspace.focus_down();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus_window_or_workspace_up(&mut self) {
|
||||
let workspace = self.active_workspace();
|
||||
if workspace.columns.is_empty() {
|
||||
self.switch_workspace_up();
|
||||
} else {
|
||||
let curr_idx = workspace.columns[workspace.active_column_idx].active_tile_idx;
|
||||
let new_idx = curr_idx.saturating_sub(1);
|
||||
if curr_idx == new_idx {
|
||||
self.switch_workspace_up();
|
||||
} else {
|
||||
workspace.focus_up();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_to_workspace_up(&mut self) {
|
||||
let source_workspace_idx = self.active_workspace_idx;
|
||||
|
||||
let new_idx = source_workspace_idx.saturating_sub(1);
|
||||
if new_idx == source_workspace_idx {
|
||||
return;
|
||||
}
|
||||
|
||||
let workspace = &mut self.workspaces[source_workspace_idx];
|
||||
if workspace.columns.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let column = &workspace.columns[workspace.active_column_idx];
|
||||
let width = column.width;
|
||||
let is_full_width = column.is_full_width;
|
||||
let window =
|
||||
workspace.remove_window_by_idx(workspace.active_column_idx, column.active_tile_idx);
|
||||
|
||||
self.add_window(new_idx, window, true, width, is_full_width);
|
||||
}
|
||||
|
||||
pub fn move_to_workspace_down(&mut self) {
|
||||
let source_workspace_idx = self.active_workspace_idx;
|
||||
|
||||
let new_idx = min(source_workspace_idx + 1, self.workspaces.len() - 1);
|
||||
if new_idx == source_workspace_idx {
|
||||
return;
|
||||
}
|
||||
|
||||
let workspace = &mut self.workspaces[source_workspace_idx];
|
||||
if workspace.columns.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let column = &workspace.columns[workspace.active_column_idx];
|
||||
let width = column.width;
|
||||
let is_full_width = column.is_full_width;
|
||||
let window =
|
||||
workspace.remove_window_by_idx(workspace.active_column_idx, column.active_tile_idx);
|
||||
|
||||
self.add_window(new_idx, window, true, width, is_full_width);
|
||||
}
|
||||
|
||||
pub fn move_to_workspace(&mut self, idx: usize) {
|
||||
let source_workspace_idx = self.active_workspace_idx;
|
||||
|
||||
let new_idx = min(idx, self.workspaces.len() - 1);
|
||||
if new_idx == source_workspace_idx {
|
||||
return;
|
||||
}
|
||||
|
||||
let workspace = &mut self.workspaces[source_workspace_idx];
|
||||
if workspace.columns.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let column = &workspace.columns[workspace.active_column_idx];
|
||||
let width = column.width;
|
||||
let is_full_width = column.is_full_width;
|
||||
let window =
|
||||
workspace.remove_window_by_idx(workspace.active_column_idx, column.active_tile_idx);
|
||||
|
||||
self.add_window(new_idx, window, true, width, is_full_width);
|
||||
|
||||
// Don't animate this action.
|
||||
self.workspace_switch = None;
|
||||
|
||||
self.clean_up_workspaces();
|
||||
}
|
||||
|
||||
pub fn switch_workspace_up(&mut self) {
|
||||
self.activate_workspace(self.active_workspace_idx.saturating_sub(1));
|
||||
}
|
||||
|
||||
pub fn switch_workspace_down(&mut self) {
|
||||
self.activate_workspace(min(
|
||||
self.active_workspace_idx + 1,
|
||||
self.workspaces.len() - 1,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn switch_workspace(&mut self, idx: usize) {
|
||||
self.activate_workspace(min(idx, self.workspaces.len() - 1));
|
||||
// Don't animate this action.
|
||||
self.workspace_switch = None;
|
||||
|
||||
self.clean_up_workspaces();
|
||||
}
|
||||
|
||||
pub fn consume_into_column(&mut self) {
|
||||
self.active_workspace().consume_into_column();
|
||||
}
|
||||
|
||||
pub fn expel_from_column(&mut self) {
|
||||
self.active_workspace().expel_from_column();
|
||||
}
|
||||
|
||||
pub fn center_column(&mut self) {
|
||||
self.active_workspace().center_column();
|
||||
}
|
||||
|
||||
pub fn focus(&self) -> Option<&W> {
|
||||
let workspace = &self.workspaces[self.active_workspace_idx];
|
||||
if !workspace.has_windows() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let column = &workspace.columns[workspace.active_column_idx];
|
||||
Some(column.tiles[column.active_tile_idx].window())
|
||||
}
|
||||
|
||||
pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) {
|
||||
if let Some(WorkspaceSwitch::Animation(anim)) = &mut self.workspace_switch {
|
||||
anim.set_current_time(current_time);
|
||||
if anim.is_done() {
|
||||
self.workspace_switch = None;
|
||||
self.clean_up_workspaces();
|
||||
}
|
||||
}
|
||||
|
||||
for ws in &mut self.workspaces {
|
||||
ws.advance_animations(current_time, is_active);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn are_animations_ongoing(&self) -> bool {
|
||||
self.workspace_switch
|
||||
.as_ref()
|
||||
.is_some_and(|s| s.is_animation())
|
||||
|| self.workspaces.iter().any(|ws| ws.are_animations_ongoing())
|
||||
}
|
||||
|
||||
pub fn are_transitions_ongoing(&self) -> bool {
|
||||
self.workspace_switch.is_some()
|
||||
|| self.workspaces.iter().any(|ws| ws.are_animations_ongoing())
|
||||
}
|
||||
|
||||
pub fn update_config(&mut self, options: Rc<Options>) {
|
||||
for ws in &mut self.workspaces {
|
||||
ws.update_config(options.clone());
|
||||
}
|
||||
|
||||
if self.options.struts != options.struts {
|
||||
let view_size = output_size(&self.output);
|
||||
let working_area = compute_working_area(&self.output, options.struts);
|
||||
|
||||
for ws in &mut self.workspaces {
|
||||
ws.set_view_size(view_size, working_area);
|
||||
}
|
||||
}
|
||||
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
pub fn toggle_width(&mut self) {
|
||||
self.active_workspace().toggle_width();
|
||||
}
|
||||
|
||||
pub fn toggle_full_width(&mut self) {
|
||||
self.active_workspace().toggle_full_width();
|
||||
}
|
||||
|
||||
pub fn set_column_width(&mut self, change: SizeChange) {
|
||||
self.active_workspace().set_column_width(change);
|
||||
}
|
||||
|
||||
pub fn set_window_height(&mut self, change: SizeChange) {
|
||||
self.active_workspace().set_window_height(change);
|
||||
}
|
||||
|
||||
pub fn move_workspace_down(&mut self) {
|
||||
let new_idx = min(self.active_workspace_idx + 1, self.workspaces.len() - 1);
|
||||
if new_idx == self.active_workspace_idx {
|
||||
return;
|
||||
}
|
||||
|
||||
self.workspaces.swap(self.active_workspace_idx, new_idx);
|
||||
|
||||
if new_idx == self.workspaces.len() - 1 {
|
||||
// Insert a new empty workspace.
|
||||
let ws = Workspace::new(self.output.clone(), self.options.clone());
|
||||
self.workspaces.push(ws);
|
||||
}
|
||||
|
||||
self.activate_workspace(new_idx);
|
||||
self.workspace_switch = None;
|
||||
|
||||
self.clean_up_workspaces();
|
||||
}
|
||||
|
||||
pub fn move_workspace_up(&mut self) {
|
||||
let new_idx = self.active_workspace_idx.saturating_sub(1);
|
||||
if new_idx == self.active_workspace_idx {
|
||||
return;
|
||||
}
|
||||
|
||||
self.workspaces.swap(self.active_workspace_idx, new_idx);
|
||||
|
||||
if self.active_workspace_idx == self.workspaces.len() - 1 {
|
||||
// Insert a new empty workspace.
|
||||
let ws = Workspace::new(self.output.clone(), self.options.clone());
|
||||
self.workspaces.push(ws);
|
||||
}
|
||||
|
||||
self.activate_workspace(new_idx);
|
||||
self.workspace_switch = None;
|
||||
|
||||
self.clean_up_workspaces();
|
||||
}
|
||||
|
||||
pub fn window_under(
|
||||
&self,
|
||||
pos_within_output: Point<f64, Logical>,
|
||||
) -> Option<(&W, Option<Point<i32, Logical>>)> {
|
||||
match &self.workspace_switch {
|
||||
Some(switch) => {
|
||||
let size = output_size(&self.output);
|
||||
|
||||
let render_idx = switch.current_idx();
|
||||
let before_idx = render_idx.floor() as usize;
|
||||
let after_idx = render_idx.ceil() as usize;
|
||||
|
||||
let offset = ((render_idx - before_idx as f64) * size.h as f64).round() as i32;
|
||||
|
||||
let (idx, ws_offset) = if pos_within_output.y < (size.h - offset) as f64 {
|
||||
(before_idx, Point::from((0, offset)))
|
||||
} else {
|
||||
(after_idx, Point::from((0, -size.h + offset)))
|
||||
};
|
||||
|
||||
let ws = &self.workspaces[idx];
|
||||
let (win, win_pos) = ws.window_under(pos_within_output + ws_offset.to_f64())?;
|
||||
Some((win, win_pos.map(|p| p - ws_offset)))
|
||||
}
|
||||
None => {
|
||||
let ws = &self.workspaces[self.active_workspace_idx];
|
||||
ws.window_under(pos_within_output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_above_top_layer(&self) -> bool {
|
||||
// Render above the top layer only if the view is stationary.
|
||||
if self.workspace_switch.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ws = &self.workspaces[self.active_workspace_idx];
|
||||
ws.render_above_top_layer()
|
||||
}
|
||||
}
|
||||
|
||||
impl Monitor<Window> {
|
||||
pub fn render_elements<R: Renderer + ImportAll>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
) -> Vec<MonitorRenderElement<R>>
|
||||
where
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
let _span = tracy_client::span!("Monitor::render_elements");
|
||||
|
||||
let output_scale = Scale::from(self.output.current_scale().fractional_scale());
|
||||
let output_transform = self.output.current_transform();
|
||||
let output_mode = self.output.current_mode().unwrap();
|
||||
let size = output_transform.transform_size(output_mode.size);
|
||||
|
||||
match &self.workspace_switch {
|
||||
Some(switch) => {
|
||||
let render_idx = switch.current_idx();
|
||||
let before_idx = render_idx.floor() as usize;
|
||||
let after_idx = render_idx.ceil() as usize;
|
||||
|
||||
let offset = ((render_idx - before_idx as f64) * size.h as f64).round() as i32;
|
||||
|
||||
let before = self.workspaces[before_idx].render_elements(renderer);
|
||||
let after = self.workspaces[after_idx].render_elements(renderer);
|
||||
|
||||
let before = before.into_iter().filter_map(|elem| {
|
||||
Some(RelocateRenderElement::from_element(
|
||||
CropRenderElement::from_element(
|
||||
elem,
|
||||
output_scale,
|
||||
Rectangle::from_extemities((0, offset), (size.w, size.h)),
|
||||
)?,
|
||||
(0, -offset),
|
||||
Relocate::Relative,
|
||||
))
|
||||
});
|
||||
let after = after.into_iter().filter_map(|elem| {
|
||||
Some(RelocateRenderElement::from_element(
|
||||
CropRenderElement::from_element(
|
||||
elem,
|
||||
output_scale,
|
||||
Rectangle::from_extemities((0, 0), (size.w, offset)),
|
||||
)?,
|
||||
(0, -offset + size.h),
|
||||
Relocate::Relative,
|
||||
))
|
||||
});
|
||||
before.chain(after).collect()
|
||||
}
|
||||
None => {
|
||||
let elements = self.workspaces[self.active_workspace_idx].render_elements(renderer);
|
||||
elements
|
||||
.into_iter()
|
||||
.filter_map(|elem| {
|
||||
Some(RelocateRenderElement::from_element(
|
||||
CropRenderElement::from_element(
|
||||
elem,
|
||||
output_scale,
|
||||
// HACK: set infinite crop bounds due to a damage tracking bug
|
||||
// which causes glitched rendering for maximized GTK windows.
|
||||
// FIXME: use proper bounds after fixing the Crop element.
|
||||
Rectangle::from_loc_and_size(
|
||||
(-i32::MAX / 2, -i32::MAX / 2),
|
||||
(i32::MAX, i32::MAX),
|
||||
),
|
||||
// Rectangle::from_loc_and_size((0, 0), size),
|
||||
)?,
|
||||
(0, 0),
|
||||
Relocate::Relative,
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
use std::cmp::max;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
|
||||
use smithay::backend::renderer::element::Kind;
|
||||
use smithay::backend::renderer::{ImportAll, Renderer};
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
|
||||
|
||||
use super::focus_ring::FocusRing;
|
||||
use super::workspace::WorkspaceRenderElement;
|
||||
use super::{LayoutElement, Options};
|
||||
|
||||
/// Toplevel window with decorations.
|
||||
#[derive(Debug)]
|
||||
pub struct Tile<W: LayoutElement> {
|
||||
/// The toplevel window itself.
|
||||
window: W,
|
||||
|
||||
/// The border around the window.
|
||||
border: FocusRing,
|
||||
|
||||
/// Whether this tile is fullscreen.
|
||||
///
|
||||
/// This will update only when the `window` actually goes fullscreen, rather than right away,
|
||||
/// to avoid black backdrop flicker before the window has had a chance to resize.
|
||||
is_fullscreen: bool,
|
||||
|
||||
/// The black backdrop for fullscreen windows.
|
||||
fullscreen_backdrop: SolidColorBuffer,
|
||||
|
||||
/// The size we were requested to fullscreen into.
|
||||
fullscreen_size: Size<i32, Logical>,
|
||||
|
||||
/// Configurable properties of the layout.
|
||||
options: Rc<Options>,
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Tile<W> {
|
||||
pub fn new(window: W, options: Rc<Options>) -> Self {
|
||||
Self {
|
||||
window,
|
||||
border: FocusRing::new(options.border),
|
||||
is_fullscreen: false, // FIXME: up-to-date fullscreen right away, but we need size.
|
||||
fullscreen_backdrop: SolidColorBuffer::new((0, 0), [0., 0., 0., 1.]),
|
||||
fullscreen_size: Default::default(),
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_config(&mut self, options: Rc<Options>) {
|
||||
self.border.update_config(options.border);
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
pub fn update_window(&mut self) {
|
||||
// FIXME: remove when we can get a fullscreen size right away.
|
||||
if self.fullscreen_size != Size::from((0, 0)) {
|
||||
self.is_fullscreen = self.window.is_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn advance_animations(&mut self, _current_time: Duration, is_active: bool) {
|
||||
let width = self.border.width();
|
||||
self.border.update(
|
||||
(width, width).into(),
|
||||
self.window.size(),
|
||||
self.window.has_ssd(),
|
||||
);
|
||||
self.border.set_active(is_active);
|
||||
}
|
||||
|
||||
pub fn window(&self) -> &W {
|
||||
&self.window
|
||||
}
|
||||
|
||||
pub fn into_window(self) -> W {
|
||||
self.window
|
||||
}
|
||||
|
||||
/// Returns `None` if the border is hidden and `Some(width)` if it should be shown.
|
||||
fn effective_border_width(&self) -> Option<i32> {
|
||||
if self.is_fullscreen {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.border.is_off() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.border.width())
|
||||
}
|
||||
|
||||
/// Returns the location of the window's visual geometry within this Tile.
|
||||
pub fn window_loc(&self) -> Point<i32, Logical> {
|
||||
let mut loc = Point::from((0, 0));
|
||||
|
||||
// In fullscreen, center the window in the given size.
|
||||
if self.is_fullscreen {
|
||||
let window_size = self.window.size();
|
||||
let target_size = self.fullscreen_size;
|
||||
|
||||
// Windows aren't supposed to be larger than the fullscreen size, but in case we get
|
||||
// one, leave it at the top-left as usual.
|
||||
if window_size.w < target_size.w {
|
||||
loc.x += (target_size.w - window_size.w) / 2;
|
||||
}
|
||||
if window_size.h < target_size.h {
|
||||
loc.y += (target_size.h - window_size.h) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(width) = self.effective_border_width() {
|
||||
loc += (width, width).into();
|
||||
}
|
||||
|
||||
loc
|
||||
}
|
||||
|
||||
pub fn tile_size(&self) -> Size<i32, Logical> {
|
||||
let mut size = self.window.size();
|
||||
|
||||
if self.is_fullscreen {
|
||||
// Normally we'd just return the fullscreen size here, but this makes things a bit
|
||||
// nicer if a fullscreen window is bigger than the fullscreen size for some reason.
|
||||
size.w = max(size.w, self.fullscreen_size.w);
|
||||
size.h = max(size.h, self.fullscreen_size.h);
|
||||
return size;
|
||||
}
|
||||
|
||||
if let Some(width) = self.effective_border_width() {
|
||||
size.w = size.w.saturating_add(width * 2);
|
||||
size.h = size.h.saturating_add(width * 2);
|
||||
}
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
pub fn window_size(&self) -> Size<i32, Logical> {
|
||||
self.window.size()
|
||||
}
|
||||
|
||||
pub fn buf_loc(&self) -> Point<i32, Logical> {
|
||||
let mut loc = Point::from((0, 0));
|
||||
loc += self.window_loc();
|
||||
loc += self.window.buf_loc();
|
||||
loc
|
||||
}
|
||||
|
||||
pub fn is_in_input_region(&self, mut point: Point<f64, Logical>) -> bool {
|
||||
point -= self.window_loc().to_f64();
|
||||
self.window.is_in_input_region(point)
|
||||
}
|
||||
|
||||
pub fn is_in_activation_region(&self, point: Point<f64, Logical>) -> bool {
|
||||
let activation_region = Rectangle::from_loc_and_size((0, 0), self.tile_size());
|
||||
activation_region.to_f64().contains(point)
|
||||
}
|
||||
|
||||
pub fn request_tile_size(&mut self, mut size: Size<i32, Logical>) {
|
||||
// Can't go through effective_border_width() because we might be fullscreen.
|
||||
if !self.border.is_off() {
|
||||
let width = self.border.width();
|
||||
size.w = max(1, size.w - width * 2);
|
||||
size.h = max(1, size.h - width * 2);
|
||||
}
|
||||
|
||||
self.window.request_size(size);
|
||||
}
|
||||
|
||||
pub fn tile_width_for_window_width(&self, size: i32) -> i32 {
|
||||
if self.border.is_off() {
|
||||
size
|
||||
} else {
|
||||
size.saturating_add(self.border.width() * 2)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tile_height_for_window_height(&self, size: i32) -> i32 {
|
||||
if self.border.is_off() {
|
||||
size
|
||||
} else {
|
||||
size.saturating_add(self.border.width() * 2)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_height_for_tile_height(&self, size: i32) -> i32 {
|
||||
if self.border.is_off() {
|
||||
size
|
||||
} else {
|
||||
size.saturating_sub(self.border.width() * 2)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_fullscreen(&mut self, size: Size<i32, Logical>) {
|
||||
self.fullscreen_backdrop.resize(size);
|
||||
self.fullscreen_size = size;
|
||||
self.window.request_fullscreen(size);
|
||||
}
|
||||
|
||||
pub fn min_size(&self) -> Size<i32, Logical> {
|
||||
let mut size = self.window.min_size();
|
||||
|
||||
if let Some(width) = self.effective_border_width() {
|
||||
size.w = max(1, size.w);
|
||||
size.h = max(1, size.h);
|
||||
|
||||
size.w = size.w.saturating_add(width * 2);
|
||||
size.h = size.h.saturating_add(width * 2);
|
||||
}
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
pub fn max_size(&self) -> Size<i32, Logical> {
|
||||
let mut size = self.window.max_size();
|
||||
|
||||
if let Some(width) = self.effective_border_width() {
|
||||
if size.w > 0 {
|
||||
size.w = size.w.saturating_add(width * 2);
|
||||
}
|
||||
if size.h > 0 {
|
||||
size.h = size.h.saturating_add(width * 2);
|
||||
}
|
||||
}
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
pub fn has_ssd(&self) -> bool {
|
||||
self.effective_border_width().is_some() || self.window.has_ssd()
|
||||
}
|
||||
|
||||
pub fn render<R: Renderer + ImportAll>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
location: Point<i32, Logical>,
|
||||
scale: Scale<f64>,
|
||||
) -> Vec<WorkspaceRenderElement<R>>
|
||||
where
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
let mut rv = Vec::new();
|
||||
|
||||
let window_pos = location + self.window_loc();
|
||||
rv.extend(self.window.render(renderer, window_pos, scale));
|
||||
|
||||
if self.effective_border_width().is_some() {
|
||||
rv.extend(
|
||||
self.border
|
||||
.render(scale)
|
||||
.map(|elem| {
|
||||
RelocateRenderElement::from_element(
|
||||
elem,
|
||||
location.to_physical_precise_round(scale),
|
||||
Relocate::Relative,
|
||||
)
|
||||
})
|
||||
.map(Into::into),
|
||||
);
|
||||
}
|
||||
|
||||
if self.is_fullscreen {
|
||||
let elem = SolidColorRenderElement::from_buffer(
|
||||
&self.fullscreen_backdrop,
|
||||
location.to_physical_precise_round(scale),
|
||||
scale,
|
||||
1.,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
rv.push(elem.into());
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+9
-8
@@ -3,7 +3,6 @@ extern crate tracing;
|
||||
|
||||
mod animation;
|
||||
mod backend;
|
||||
mod config;
|
||||
mod cursor;
|
||||
#[cfg(feature = "dbus")]
|
||||
mod dbus;
|
||||
@@ -12,6 +11,7 @@ mod handlers;
|
||||
mod input;
|
||||
mod layout;
|
||||
mod niri;
|
||||
mod render_helpers;
|
||||
mod screenshot_ui;
|
||||
mod utils;
|
||||
mod watcher;
|
||||
@@ -27,12 +27,11 @@ use std::process::Command;
|
||||
use std::{env, mem};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use config::Config;
|
||||
#[cfg(not(feature = "xdp-gnome-screencast"))]
|
||||
use dummy_pw_utils as pw_utils;
|
||||
use git_version::git_version;
|
||||
use miette::{Context, NarratableReportHandler};
|
||||
use niri::{Niri, State};
|
||||
use niri_config::Config;
|
||||
use portable_atomic::Ordering;
|
||||
use sd_notify::NotifyState;
|
||||
use smithay::reexports::calloop::{self, EventLoop};
|
||||
@@ -109,13 +108,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let _client = tracy_client::Client::start();
|
||||
|
||||
// Set a better error printer for config loading.
|
||||
miette::set_hook(Box::new(|_| Box::new(NarratableReportHandler::new()))).unwrap();
|
||||
niri_config::set_miette_hook().unwrap();
|
||||
|
||||
// Handle subcommands.
|
||||
if let Some(subcommand) = cli.subcommand {
|
||||
match subcommand {
|
||||
Sub::Validate { config } => {
|
||||
Config::load(config).context("error loading config")?;
|
||||
Config::load(config)?;
|
||||
info!("config is valid");
|
||||
return Ok(());
|
||||
}
|
||||
@@ -129,7 +128,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
);
|
||||
|
||||
// Load the config.
|
||||
let (mut config, path) = match Config::load(cli.config).context("error loading config") {
|
||||
let (mut config, path) = match Config::load(cli.config) {
|
||||
Ok((config, path)) => (config, Some(path)),
|
||||
Err(err) => {
|
||||
warn!("{err:?}");
|
||||
@@ -163,8 +162,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Inhibit power key handling so we can suspend on it.
|
||||
#[cfg(feature = "dbus")]
|
||||
if let Err(err) = state.niri.inhibit_power_key() {
|
||||
warn!("error inhibiting power key: {err:?}");
|
||||
if !state.niri.config.borrow().input.disable_power_key_handling {
|
||||
if let Err(err) = state.niri.inhibit_power_key() {
|
||||
warn!("error inhibiting power key: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+379
-130
@@ -10,19 +10,24 @@ use std::{env, mem, thread};
|
||||
|
||||
use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as KdeDecorationsMode;
|
||||
use anyhow::Context;
|
||||
use niri_config::{Config, TrackLayout};
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::surface::{
|
||||
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
|
||||
};
|
||||
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
||||
use smithay::backend::renderer::element::utils::select_dmabuf_feedback;
|
||||
use smithay::backend::renderer::element::{
|
||||
default_primary_scanout_output_compare, render_elements, AsRenderElements, Kind, RenderElement,
|
||||
RenderElementStates,
|
||||
default_primary_scanout_output_compare, AsRenderElements, Element, Id, Kind, RenderElement,
|
||||
RenderElementStates, UnderlyingStorage,
|
||||
};
|
||||
use smithay::backend::renderer::gles::{
|
||||
GlesError, GlesFrame, GlesMapping, GlesRenderer, GlesTexture,
|
||||
};
|
||||
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::sync::SyncPoint;
|
||||
use smithay::backend::renderer::{Bind, ExportMem, Frame, ImportAll, Offscreen, Renderer};
|
||||
use smithay::backend::renderer::utils::CommitCounter;
|
||||
use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer};
|
||||
use smithay::desktop::utils::{
|
||||
bbox_from_surface_tree, output_update, send_dmabuf_feedback_surface_tree,
|
||||
send_frames_surface_tree, surface_presentation_feedback_flags_from_states,
|
||||
@@ -50,7 +55,7 @@ use smithay::reexports::wayland_server::backend::{
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::reexports::wayland_server::{Display, DisplayHandle};
|
||||
use smithay::utils::{
|
||||
ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform,
|
||||
Buffer, ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform,
|
||||
SERIAL_COUNTER,
|
||||
};
|
||||
use smithay::wayland::compositor::{
|
||||
@@ -58,7 +63,7 @@ use smithay::wayland::compositor::{
|
||||
CompositorState, SurfaceData, TraversalAction,
|
||||
};
|
||||
use smithay::wayland::cursor_shape::CursorShapeManagerState;
|
||||
use smithay::wayland::dmabuf::DmabufFeedback;
|
||||
use smithay::wayland::dmabuf::DmabufState;
|
||||
use smithay::wayland::input_method::InputMethodManagerState;
|
||||
use smithay::wayland::output::OutputManagerState;
|
||||
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsState};
|
||||
@@ -80,8 +85,8 @@ use smithay::wayland::text_input::TextInputManagerState;
|
||||
use smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState;
|
||||
|
||||
use crate::animation;
|
||||
use crate::backend::tty::{SurfaceDmabufFeedback, TtyFrame, TtyRenderer, TtyRendererError};
|
||||
use crate::backend::{Backend, RenderResult, Tty, Winit};
|
||||
use crate::config::{Config, TrackLayout};
|
||||
use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};
|
||||
#[cfg(feature = "dbus")]
|
||||
use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri};
|
||||
@@ -90,10 +95,13 @@ use crate::dbus::mutter_screen_cast::{self, ScreenCastToNiri};
|
||||
use crate::frame_clock::FrameClock;
|
||||
use crate::handlers::configure_lock_surface;
|
||||
use crate::input::TabletData;
|
||||
use crate::layout::{output_size, Layout, MonitorRenderElement};
|
||||
use crate::layout::{Layout, MonitorRenderElement};
|
||||
use crate::pw_utils::{Cast, PipeWire};
|
||||
use crate::render_helpers::{NiriRenderer, PrimaryGpuTextureRenderElement};
|
||||
use crate::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
||||
use crate::utils::{center, get_monotonic_time, make_screenshot_path, write_png_rgba8};
|
||||
use crate::utils::{
|
||||
center, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
|
||||
};
|
||||
|
||||
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.];
|
||||
const CLEAR_COLOR_LOCKED: [f32; 4] = [0.3, 0.1, 0.1, 1.];
|
||||
@@ -136,6 +144,7 @@ pub struct Niri {
|
||||
pub session_lock_state: SessionLockManagerState,
|
||||
pub shm_state: ShmState,
|
||||
pub output_manager_state: OutputManagerState,
|
||||
pub dmabuf_state: DmabufState,
|
||||
pub seat_state: SeatState<State>,
|
||||
pub tablet_state: TabletManagerState,
|
||||
pub text_input_state: TextInputManagerState,
|
||||
@@ -577,31 +586,32 @@ impl State {
|
||||
let ScreenshotToNiri::TakeScreenshot { include_cursor } = msg;
|
||||
let _span = tracy_client::span!("TakeScreenshot");
|
||||
|
||||
let Some(renderer) = self.backend.renderer() else {
|
||||
let msg = NiriToScreenshot::ScreenshotResult(None);
|
||||
if let Err(err) = to_screenshot.send_blocking(msg) {
|
||||
warn!("error sending None to screenshot: {err:?}");
|
||||
}
|
||||
return;
|
||||
};
|
||||
let rv = self.backend.with_primary_renderer(|renderer| {
|
||||
let on_done = {
|
||||
let to_screenshot = to_screenshot.clone();
|
||||
move |path| {
|
||||
let msg = NiriToScreenshot::ScreenshotResult(Some(path));
|
||||
if let Err(err) = to_screenshot.send_blocking(msg) {
|
||||
warn!("error sending path to screenshot: {err:?}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let on_done = {
|
||||
let to_screenshot = to_screenshot.clone();
|
||||
move |path| {
|
||||
let msg = NiriToScreenshot::ScreenshotResult(Some(path));
|
||||
let res = self
|
||||
.niri
|
||||
.screenshot_all_outputs(renderer, include_cursor, on_done);
|
||||
|
||||
if let Err(err) = res {
|
||||
warn!("error taking a screenshot: {err:?}");
|
||||
|
||||
let msg = NiriToScreenshot::ScreenshotResult(None);
|
||||
if let Err(err) = to_screenshot.send_blocking(msg) {
|
||||
warn!("error sending path to screenshot: {err:?}");
|
||||
warn!("error sending None to screenshot: {err:?}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let res = self
|
||||
.niri
|
||||
.screenshot_all_outputs(renderer, include_cursor, on_done);
|
||||
|
||||
if let Err(err) = res {
|
||||
warn!("error taking a screenshot: {err:?}");
|
||||
});
|
||||
|
||||
if rv.is_none() {
|
||||
let msg = NiriToScreenshot::ScreenshotResult(None);
|
||||
if let Err(err) = to_screenshot.send_blocking(msg) {
|
||||
warn!("error sending None to screenshot: {err:?}");
|
||||
@@ -645,6 +655,7 @@ impl Niri {
|
||||
let shm_state = ShmState::new::<State>(&display_handle, vec![]);
|
||||
let output_manager_state =
|
||||
OutputManagerState::new_with_xdg_output::<State>(&display_handle);
|
||||
let dmabuf_state = DmabufState::new();
|
||||
let mut seat_state = SeatState::new();
|
||||
let tablet_state = TabletManagerState::new::<State>(&display_handle);
|
||||
let pointer_gestures_state = PointerGesturesState::new::<State>(&display_handle);
|
||||
@@ -758,6 +769,7 @@ impl Niri {
|
||||
virtual_keyboard_state,
|
||||
shm_state,
|
||||
output_manager_state,
|
||||
dmabuf_state,
|
||||
seat_state,
|
||||
tablet_state,
|
||||
pointer_gestures_state,
|
||||
@@ -1033,6 +1045,10 @@ impl Niri {
|
||||
Some((output, pos_within_output))
|
||||
}
|
||||
|
||||
/// Returns the window under the position to be activated.
|
||||
///
|
||||
/// The cursor may be inside the window's activation region, but not within the window's input
|
||||
/// region.
|
||||
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Window> {
|
||||
if self.is_locked() || self.screenshot_ui.is_open() {
|
||||
return None;
|
||||
@@ -1043,6 +1059,10 @@ impl Niri {
|
||||
Some(window)
|
||||
}
|
||||
|
||||
/// Returns the window under the cursor to be activated.
|
||||
///
|
||||
/// The cursor may be inside the window's activation region, but not within the window's input
|
||||
/// region.
|
||||
pub fn window_under_cursor(&self) -> Option<&Window> {
|
||||
let pos = self.seat.get_pointer().unwrap().current_location();
|
||||
self.window_under(pos)
|
||||
@@ -1101,6 +1121,7 @@ impl Niri {
|
||||
self.layout
|
||||
.window_under(output, pos_within_output)
|
||||
.and_then(|(window, win_pos_within_output)| {
|
||||
let win_pos_within_output = win_pos_within_output?;
|
||||
window
|
||||
.surface_under(
|
||||
pos_within_output - win_pos_within_output.to_f64(),
|
||||
@@ -1220,7 +1241,7 @@ impl Niri {
|
||||
.or_else(|| self.global_space.outputs().next())
|
||||
}
|
||||
|
||||
pub fn output_for_root(&self, root: &WlSurface) -> Option<Output> {
|
||||
pub fn output_for_root(&self, root: &WlSurface) -> Option<&Output> {
|
||||
// Check the main layout.
|
||||
let win_out = self.layout.find_window_and_output(root);
|
||||
let layout_output = win_out.map(|(_, output)| output);
|
||||
@@ -1231,7 +1252,7 @@ impl Niri {
|
||||
.layer_for_surface(root, WindowSurfaceType::TOPLEVEL)
|
||||
.is_some()
|
||||
};
|
||||
let layer_shell_output = || self.layout.outputs().find(has_layer_surface).cloned();
|
||||
let layer_shell_output = || self.layout.outputs().find(has_layer_surface);
|
||||
|
||||
layout_output.or_else(layer_shell_output)
|
||||
}
|
||||
@@ -1288,11 +1309,11 @@ impl Niri {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn pointer_element(
|
||||
pub fn pointer_element<R: NiriRenderer>(
|
||||
&self,
|
||||
renderer: &mut GlesRenderer,
|
||||
renderer: &mut R,
|
||||
output: &Output,
|
||||
) -> Vec<OutputRenderElements<GlesRenderer>> {
|
||||
) -> Vec<OutputRenderElements<R>> {
|
||||
let _span = tracy_client::span!("Niri::pointer_element");
|
||||
let output_scale = output.current_scale();
|
||||
let output_pos = self.global_space.output_geometry(output).unwrap().loc;
|
||||
@@ -1336,19 +1357,23 @@ impl Niri {
|
||||
let pointer_pos =
|
||||
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
|
||||
|
||||
let texture = self
|
||||
.cursor_texture_cache
|
||||
.get(renderer, icon, scale, &cursor, idx);
|
||||
let texture = self.cursor_texture_cache.get(
|
||||
renderer.as_gles_renderer(),
|
||||
icon,
|
||||
scale,
|
||||
&cursor,
|
||||
idx,
|
||||
);
|
||||
|
||||
let pointer_elements = vec![OutputRenderElements::NamedPointer(
|
||||
TextureRenderElement::from_texture_buffer(
|
||||
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
|
||||
pointer_pos.to_f64(),
|
||||
&texture,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Kind::Cursor,
|
||||
),
|
||||
)),
|
||||
)];
|
||||
|
||||
(pointer_elements, pointer_pos)
|
||||
@@ -1481,12 +1506,12 @@ impl Niri {
|
||||
}
|
||||
}
|
||||
|
||||
fn render(
|
||||
pub fn render<R: NiriRenderer>(
|
||||
&self,
|
||||
renderer: &mut GlesRenderer,
|
||||
renderer: &mut R,
|
||||
output: &Output,
|
||||
include_pointer: bool,
|
||||
) -> Vec<OutputRenderElements<GlesRenderer>> {
|
||||
) -> Vec<OutputRenderElements<R>> {
|
||||
let _span = tracy_client::span!("Niri::render");
|
||||
|
||||
let output_scale = Scale::from(output.current_scale().fractional_scale());
|
||||
@@ -1558,8 +1583,7 @@ impl Niri {
|
||||
|
||||
// Get layer-shell elements.
|
||||
let layer_map = layer_map_for_output(output);
|
||||
let mut extend_from_layer = |elements: &mut Vec<OutputRenderElements<GlesRenderer>>,
|
||||
layer| {
|
||||
let mut extend_from_layer = |elements: &mut Vec<OutputRenderElements<R>>, layer| {
|
||||
let iter = layer_map
|
||||
.layers_on(layer)
|
||||
.filter_map(|surface| {
|
||||
@@ -1606,75 +1630,50 @@ impl Niri {
|
||||
fn redraw(&mut self, backend: &mut Backend, output: &Output) {
|
||||
let _span = tracy_client::span!("Niri::redraw");
|
||||
|
||||
let monitors_active = self.monitors_active;
|
||||
|
||||
// Verify our invariant.
|
||||
let state = self.output_state.get_mut(output).unwrap();
|
||||
assert!(matches!(
|
||||
state.redraw_state,
|
||||
RedrawState::Queued(_) | RedrawState::WaitingForEstimatedVBlankAndQueued(_)
|
||||
));
|
||||
|
||||
// FIXME: make this not cursed.
|
||||
let mut reset = || {
|
||||
let state = self.output_state.get_mut(output).unwrap();
|
||||
let target_presentation_time = state.frame_clock.next_presentation_time();
|
||||
|
||||
let mut res = RenderResult::Skipped;
|
||||
if self.monitors_active {
|
||||
// Update from the config and advance the animations.
|
||||
self.layout.advance_animations(target_presentation_time);
|
||||
state.unfinished_animations_remain = self
|
||||
.layout
|
||||
.monitor_for_output(output)
|
||||
.unwrap()
|
||||
.are_animations_ongoing();
|
||||
|
||||
// Also keep redrawing if the current cursor is animated.
|
||||
state.unfinished_animations_remain |= self
|
||||
.cursor_manager
|
||||
.is_current_cursor_animated(output.current_scale().integer_scale());
|
||||
|
||||
// Render.
|
||||
res = backend.render(self, output, target_presentation_time);
|
||||
}
|
||||
|
||||
let is_locked = self.is_locked();
|
||||
let state = self.output_state.get_mut(output).unwrap();
|
||||
|
||||
if res == RenderResult::Skipped {
|
||||
// Update the redraw state on failed render.
|
||||
state.redraw_state =
|
||||
if let RedrawState::WaitingForEstimatedVBlankAndQueued((token, _)) =
|
||||
if let RedrawState::WaitingForEstimatedVBlank(token)
|
||||
| RedrawState::WaitingForEstimatedVBlankAndQueued((token, _)) =
|
||||
state.redraw_state
|
||||
{
|
||||
RedrawState::WaitingForEstimatedVBlank(token)
|
||||
} else {
|
||||
RedrawState::Idle
|
||||
};
|
||||
|
||||
if matches!(self.lock_state, LockState::Locking { .. })
|
||||
&& state.lock_render_state == LockRenderState::Unlocked
|
||||
{
|
||||
// We needed to redraw this output for locking and failed.
|
||||
self.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
if !monitors_active {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if !backend.is_active() {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(renderer) = backend.renderer() else {
|
||||
reset();
|
||||
return;
|
||||
};
|
||||
|
||||
let state = self.output_state.get_mut(output).unwrap();
|
||||
let target_presentation_time = state.frame_clock.next_presentation_time();
|
||||
|
||||
// Update from the config and advance the animations.
|
||||
self.layout.advance_animations(target_presentation_time);
|
||||
state.unfinished_animations_remain = self
|
||||
.layout
|
||||
.monitor_for_output(output)
|
||||
.unwrap()
|
||||
.are_animations_ongoing();
|
||||
|
||||
// Also keep redrawing if the current cursor is animated.
|
||||
state.unfinished_animations_remain |= self
|
||||
.cursor_manager
|
||||
.is_current_cursor_animated(output.current_scale().integer_scale());
|
||||
|
||||
// Render the elements.
|
||||
let elements = self.render(renderer, output, true);
|
||||
|
||||
// Hand it over to the backend.
|
||||
let res = backend.render(self, output, &elements, target_presentation_time);
|
||||
|
||||
// Update the lock render state on successful render.
|
||||
let is_locked = self.is_locked();
|
||||
let state = self.output_state.get_mut(output).unwrap();
|
||||
if res != RenderResult::Error {
|
||||
} else {
|
||||
// Update the lock render state on successful render.
|
||||
state.lock_render_state = if is_locked {
|
||||
LockRenderState::Locked
|
||||
} else {
|
||||
@@ -1685,7 +1684,7 @@ impl Niri {
|
||||
// If we're in process of locking the session, check if the requirements were met.
|
||||
match mem::take(&mut self.lock_state) {
|
||||
LockState::Locking(confirmation) => {
|
||||
if res == RenderResult::Error {
|
||||
if res == RenderResult::Skipped {
|
||||
if state.lock_render_state == LockRenderState::Unlocked {
|
||||
// We needed to render a locked frame on this output but failed.
|
||||
self.unlock();
|
||||
@@ -1728,10 +1727,9 @@ impl Niri {
|
||||
// Render and send to PipeWire screencast streams.
|
||||
#[cfg(feature = "xdp-gnome-screencast")]
|
||||
{
|
||||
let renderer = backend
|
||||
.renderer()
|
||||
.expect("renderer must not have disappeared");
|
||||
self.send_for_screen_cast(renderer, output, &elements, target_presentation_time);
|
||||
backend.with_primary_renderer(|renderer| {
|
||||
self.render_for_screen_cast(renderer, output, target_presentation_time);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1833,18 +1831,45 @@ impl Niri {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_dmabuf_feedbacks(&self, output: &Output, feedback: &DmabufFeedback) {
|
||||
pub fn send_dmabuf_feedbacks(
|
||||
&self,
|
||||
output: &Output,
|
||||
feedback: &SurfaceDmabufFeedback,
|
||||
render_element_states: &RenderElementStates,
|
||||
) {
|
||||
let _span = tracy_client::span!("Niri::send_dmabuf_feedbacks");
|
||||
|
||||
// We can unconditionally send the current output's feedback to regular and layer-shell
|
||||
// surfaces, as they can only be displayed on a single output at a time. Even if a surface
|
||||
// is currently invisible, this is the DMABUF feedback that it should know about.
|
||||
for win in self.layout.windows_for_output(output) {
|
||||
win.send_dmabuf_feedback(output, |_, _| Some(output.clone()), |_, _| feedback);
|
||||
win.send_dmabuf_feedback(
|
||||
output,
|
||||
|_, _| Some(output.clone()),
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
&feedback.render,
|
||||
&feedback.scanout,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for surface in layer_map_for_output(output).layers() {
|
||||
surface.send_dmabuf_feedback(output, |_, _| Some(output.clone()), |_, _| feedback);
|
||||
surface.send_dmabuf_feedback(
|
||||
output,
|
||||
|_, _| Some(output.clone()),
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
&feedback.render,
|
||||
&feedback.scanout,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(surface) = &self.output_state[output].lock_surface {
|
||||
@@ -1852,7 +1877,14 @@ impl Niri {
|
||||
surface.wl_surface(),
|
||||
output,
|
||||
|_, _| Some(output.clone()),
|
||||
|_, _| feedback,
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
&feedback.render,
|
||||
&feedback.scanout,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1861,7 +1893,14 @@ impl Niri {
|
||||
surface,
|
||||
output,
|
||||
surface_primary_scanout_output,
|
||||
|_, _| feedback,
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
&feedback.render,
|
||||
&feedback.scanout,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1870,7 +1909,14 @@ impl Niri {
|
||||
surface,
|
||||
output,
|
||||
surface_primary_scanout_output,
|
||||
|_, _| feedback,
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
&feedback.render,
|
||||
&feedback.scanout,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2010,19 +2056,21 @@ impl Niri {
|
||||
}
|
||||
|
||||
#[cfg(feature = "xdp-gnome-screencast")]
|
||||
fn send_for_screen_cast(
|
||||
fn render_for_screen_cast(
|
||||
&mut self,
|
||||
renderer: &mut GlesRenderer,
|
||||
output: &Output,
|
||||
elements: &[OutputRenderElements<GlesRenderer>],
|
||||
target_presentation_time: Duration,
|
||||
) {
|
||||
let _span = tracy_client::span!("Niri::send_for_screen_cast");
|
||||
let _span = tracy_client::span!("Niri::render_for_screen_cast");
|
||||
|
||||
let size = output.current_mode().unwrap().size;
|
||||
let scale = Scale::from(output.current_scale().fractional_scale());
|
||||
|
||||
for cast in &mut self.casts {
|
||||
let mut elements = None;
|
||||
|
||||
let mut casts = mem::take(&mut self.casts);
|
||||
for cast in &mut casts {
|
||||
if !cast.is_active.get() {
|
||||
continue;
|
||||
}
|
||||
@@ -2068,6 +2116,9 @@ impl Niri {
|
||||
let dmabuf = cast.dmabufs.borrow()[&fd].clone();
|
||||
|
||||
// FIXME: Hidden / embedded / metadata cursor
|
||||
let elements = elements
|
||||
.get_or_insert_with(|| self.render::<GlesRenderer>(renderer, output, true));
|
||||
|
||||
if let Err(err) = render_to_dmabuf(renderer, dmabuf, size, scale, elements) {
|
||||
error!("error rendering to dmabuf: {err:?}");
|
||||
continue;
|
||||
@@ -2081,6 +2132,7 @@ impl Niri {
|
||||
|
||||
cast.last_frame_time = target_presentation_time;
|
||||
}
|
||||
self.casts = casts;
|
||||
}
|
||||
|
||||
#[cfg(feature = "xdp-gnome-screencast")]
|
||||
@@ -2132,7 +2184,7 @@ impl Niri {
|
||||
.filter_map(|output| {
|
||||
let size = output.current_mode().unwrap().size;
|
||||
let scale = Scale::from(output.current_scale().fractional_scale());
|
||||
let elements = self.render(renderer, &output, true);
|
||||
let elements = self.render::<GlesRenderer>(renderer, &output, true);
|
||||
|
||||
let res = render_to_texture(renderer, size, scale, Fourcc::Abgr8888, &elements);
|
||||
let screenshot = match res {
|
||||
@@ -2159,7 +2211,7 @@ impl Niri {
|
||||
|
||||
let size = output.current_mode().unwrap().size;
|
||||
let scale = Scale::from(output.current_scale().fractional_scale());
|
||||
let elements = self.render(renderer, output, true);
|
||||
let elements = self.render::<GlesRenderer>(renderer, output, true);
|
||||
let pixels = render_to_vec(renderer, size, scale, Fourcc::Abgr8888, &elements)?;
|
||||
|
||||
self.save_screenshot(size, pixels)
|
||||
@@ -2283,7 +2335,7 @@ impl Niri {
|
||||
size.w = max(size.w, geom.loc.x + geom.size.w);
|
||||
size.h = max(size.h, geom.loc.y + geom.size.h);
|
||||
|
||||
let output_elements = self.render(renderer, &output, include_pointer);
|
||||
let output_elements = self.render::<GlesRenderer>(renderer, &output, include_pointer);
|
||||
elements.extend(output_elements.into_iter().map(|elem| {
|
||||
RelocateRenderElement::from_element(elem, geom.loc, Relocate::Relative)
|
||||
}));
|
||||
@@ -2388,16 +2440,6 @@ impl Niri {
|
||||
}
|
||||
}
|
||||
|
||||
render_elements! {
|
||||
#[derive(Debug)]
|
||||
pub OutputRenderElements<R> where R: ImportAll;
|
||||
Monitor = MonitorRenderElement<R>,
|
||||
Wayland = WaylandSurfaceRenderElement<R>,
|
||||
NamedPointer = TextureRenderElement<<R as Renderer>::TextureId>,
|
||||
SolidColor = SolidColorRenderElement,
|
||||
ScreenshotUi = ScreenshotUiRenderElement<R>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClientState {
|
||||
pub compositor_state: CompositorClientState,
|
||||
@@ -2488,8 +2530,6 @@ fn render_to_dmabuf(
|
||||
scale: Scale<f64>,
|
||||
elements: &[OutputRenderElements<GlesRenderer>],
|
||||
) -> anyhow::Result<()> {
|
||||
use smithay::backend::renderer::element::Element;
|
||||
|
||||
let _span = tracy_client::span!("render_to_dmabuf");
|
||||
|
||||
let output_rect = Rectangle::from_loc_and_size((0, 0), size);
|
||||
@@ -2511,3 +2551,212 @@ fn render_to_dmabuf(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Manual RenderElement implementation due to AsGlesFrame requirement.
|
||||
#[derive(Debug)]
|
||||
pub enum OutputRenderElements<R: NiriRenderer> {
|
||||
Monitor(MonitorRenderElement<R>),
|
||||
Wayland(WaylandSurfaceRenderElement<R>),
|
||||
NamedPointer(PrimaryGpuTextureRenderElement),
|
||||
SolidColor(SolidColorRenderElement),
|
||||
ScreenshotUi(ScreenshotUiRenderElement),
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
||||
fn id(&self) -> &Id {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.id(),
|
||||
Self::Wayland(elem) => elem.id(),
|
||||
Self::NamedPointer(elem) => elem.id(),
|
||||
Self::SolidColor(elem) => elem.id(),
|
||||
Self::ScreenshotUi(elem) => elem.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.current_commit(),
|
||||
Self::Wayland(elem) => elem.current_commit(),
|
||||
Self::NamedPointer(elem) => elem.current_commit(),
|
||||
Self::SolidColor(elem) => elem.current_commit(),
|
||||
Self::ScreenshotUi(elem) => elem.current_commit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.geometry(scale),
|
||||
Self::Wayland(elem) => elem.geometry(scale),
|
||||
Self::NamedPointer(elem) => elem.geometry(scale),
|
||||
Self::SolidColor(elem) => elem.geometry(scale),
|
||||
Self::ScreenshotUi(elem) => elem.geometry(scale),
|
||||
}
|
||||
}
|
||||
|
||||
fn transform(&self) -> Transform {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.transform(),
|
||||
Self::Wayland(elem) => elem.transform(),
|
||||
Self::NamedPointer(elem) => elem.transform(),
|
||||
Self::SolidColor(elem) => elem.transform(),
|
||||
Self::ScreenshotUi(elem) => elem.transform(),
|
||||
}
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.src(),
|
||||
Self::Wayland(elem) => elem.src(),
|
||||
Self::NamedPointer(elem) => elem.src(),
|
||||
Self::SolidColor(elem) => elem.src(),
|
||||
Self::ScreenshotUi(elem) => elem.src(),
|
||||
}
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> Vec<Rectangle<i32, Physical>> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.damage_since(scale, commit),
|
||||
Self::Wayland(elem) => elem.damage_since(scale, commit),
|
||||
Self::NamedPointer(elem) => elem.damage_since(scale, commit),
|
||||
Self::SolidColor(elem) => elem.damage_since(scale, commit),
|
||||
Self::ScreenshotUi(elem) => elem.damage_since(scale, commit),
|
||||
}
|
||||
}
|
||||
|
||||
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.opaque_regions(scale),
|
||||
Self::Wayland(elem) => elem.opaque_regions(scale),
|
||||
Self::NamedPointer(elem) => elem.opaque_regions(scale),
|
||||
Self::SolidColor(elem) => elem.opaque_regions(scale),
|
||||
Self::ScreenshotUi(elem) => elem.opaque_regions(scale),
|
||||
}
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.alpha(),
|
||||
Self::Wayland(elem) => elem.alpha(),
|
||||
Self::NamedPointer(elem) => elem.alpha(),
|
||||
Self::SolidColor(elem) => elem.alpha(),
|
||||
Self::ScreenshotUi(elem) => elem.alpha(),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> Kind {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.kind(),
|
||||
Self::Wayland(elem) => elem.kind(),
|
||||
Self::NamedPointer(elem) => elem.kind(),
|
||||
Self::SolidColor(elem) => elem.kind(),
|
||||
Self::ScreenshotUi(elem) => elem.kind(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderElement<GlesRenderer> for OutputRenderElements<GlesRenderer> {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut GlesFrame<'_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), GlesError> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.draw(frame, src, dst, damage),
|
||||
Self::Wayland(elem) => elem.draw(frame, src, dst, damage),
|
||||
Self::NamedPointer(elem) => {
|
||||
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
Self::SolidColor(elem) => {
|
||||
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
Self::ScreenshotUi(elem) => {
|
||||
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.underlying_storage(renderer),
|
||||
Self::Wayland(elem) => elem.underlying_storage(renderer),
|
||||
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
|
||||
Self::SolidColor(elem) => elem.underlying_storage(renderer),
|
||||
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'render, 'alloc> RenderElement<TtyRenderer<'render, 'alloc>>
|
||||
for OutputRenderElements<TtyRenderer<'render, 'alloc>>
|
||||
{
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut TtyFrame<'render, 'alloc, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), TtyRendererError<'render, 'alloc>> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.draw(frame, src, dst, damage),
|
||||
Self::Wayland(elem) => elem.draw(frame, src, dst, damage),
|
||||
Self::NamedPointer(elem) => {
|
||||
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
Self::SolidColor(elem) => {
|
||||
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
Self::ScreenshotUi(elem) => {
|
||||
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn underlying_storage(
|
||||
&self,
|
||||
renderer: &mut TtyRenderer<'render, 'alloc>,
|
||||
) -> Option<UnderlyingStorage> {
|
||||
match self {
|
||||
Self::Monitor(elem) => elem.underlying_storage(renderer),
|
||||
Self::Wayland(elem) => elem.underlying_storage(renderer),
|
||||
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
|
||||
Self::SolidColor(elem) => elem.underlying_storage(renderer),
|
||||
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> From<MonitorRenderElement<R>> for OutputRenderElements<R> {
|
||||
fn from(x: MonitorRenderElement<R>) -> Self {
|
||||
Self::Monitor(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> From<WaylandSurfaceRenderElement<R>> for OutputRenderElements<R> {
|
||||
fn from(x: WaylandSurfaceRenderElement<R>) -> Self {
|
||||
Self::Wayland(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> From<PrimaryGpuTextureRenderElement> for OutputRenderElements<R> {
|
||||
fn from(x: PrimaryGpuTextureRenderElement) -> Self {
|
||||
Self::NamedPointer(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> From<SolidColorRenderElement> for OutputRenderElements<R> {
|
||||
fn from(x: SolidColorRenderElement) -> Self {
|
||||
Self::SolidColor(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> From<ScreenshotUiRenderElement> for OutputRenderElements<R> {
|
||||
fn from(x: ScreenshotUiRenderElement) -> Self {
|
||||
Self::ScreenshotUi(x)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
|
||||
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::utils::CommitCounter;
|
||||
use smithay::backend::renderer::{Bind, ExportMem, ImportAll, Offscreen, Renderer, Texture};
|
||||
use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};
|
||||
|
||||
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
|
||||
|
||||
/// Trait with our main renderer requirements to save on the typing.
|
||||
pub trait NiriRenderer:
|
||||
ImportAll
|
||||
+ ExportMem
|
||||
+ Bind<Dmabuf>
|
||||
+ Offscreen<GlesTexture>
|
||||
+ Renderer<TextureId = Self::NiriTextureId, Error = Self::NiriError>
|
||||
+ AsGlesRenderer
|
||||
{
|
||||
// Associated types to work around the instability of associated type bounds.
|
||||
type NiriTextureId: Texture + Clone + 'static;
|
||||
type NiriError: std::error::Error
|
||||
+ Send
|
||||
+ Sync
|
||||
+ From<<GlesRenderer as Renderer>::Error>
|
||||
+ 'static;
|
||||
}
|
||||
|
||||
impl<R> NiriRenderer for R
|
||||
where
|
||||
R: ImportAll + ExportMem + Bind<Dmabuf> + Offscreen<GlesTexture> + AsGlesRenderer,
|
||||
R::TextureId: Texture + Clone + 'static,
|
||||
R::Error: std::error::Error + Send + Sync + From<<GlesRenderer as Renderer>::Error> + 'static,
|
||||
{
|
||||
type NiriTextureId = R::TextureId;
|
||||
type NiriError = R::Error;
|
||||
}
|
||||
|
||||
/// Trait for getting the underlying `GlesRenderer`.
|
||||
pub trait AsGlesRenderer {
|
||||
fn as_gles_renderer(&mut self) -> &mut GlesRenderer;
|
||||
}
|
||||
|
||||
impl AsGlesRenderer for GlesRenderer {
|
||||
fn as_gles_renderer(&mut self) -> &mut GlesRenderer {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'render, 'alloc> AsGlesRenderer for TtyRenderer<'render, 'alloc> {
|
||||
fn as_gles_renderer(&mut self) -> &mut GlesRenderer {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for getting the underlying `GlesFrame`.
|
||||
pub trait AsGlesFrame<'frame>
|
||||
where
|
||||
Self: 'frame,
|
||||
{
|
||||
fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame>;
|
||||
}
|
||||
|
||||
impl<'frame> AsGlesFrame<'frame> for GlesFrame<'frame> {
|
||||
fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'render, 'alloc, 'frame> AsGlesFrame<'frame> for TtyFrame<'render, 'alloc, 'frame> {
|
||||
fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame> {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for a texture from the primary GPU for rendering with the primary GPU.
|
||||
#[derive(Debug)]
|
||||
pub struct PrimaryGpuTextureRenderElement(pub TextureRenderElement<GlesTexture>);
|
||||
|
||||
impl Element for PrimaryGpuTextureRenderElement {
|
||||
fn id(&self) -> &Id {
|
||||
self.0.id()
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
self.0.current_commit()
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
self.0.geometry(scale)
|
||||
}
|
||||
|
||||
fn transform(&self) -> Transform {
|
||||
self.0.transform()
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
self.0.src()
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> Vec<Rectangle<i32, Physical>> {
|
||||
self.0.damage_since(scale, commit)
|
||||
}
|
||||
|
||||
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
|
||||
self.0.opaque_regions(scale)
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
self.0.alpha()
|
||||
}
|
||||
|
||||
fn kind(&self) -> Kind {
|
||||
self.0.kind()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderElement<GlesRenderer> for PrimaryGpuTextureRenderElement {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut GlesFrame<'_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), GlesError> {
|
||||
let gles_frame = frame.as_gles_frame();
|
||||
RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
|
||||
// If scanout for things other than Wayland buffers is implemented, this will need to take
|
||||
// the target GPU into account.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'render, 'alloc> RenderElement<TtyRenderer<'render, 'alloc>>
|
||||
for PrimaryGpuTextureRenderElement
|
||||
{
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut TtyFrame<'_, '_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), TtyRendererError<'render, 'alloc>> {
|
||||
let gles_frame = frame.as_gles_frame();
|
||||
RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn underlying_storage(
|
||||
&self,
|
||||
_renderer: &mut TtyRenderer<'render, 'alloc>,
|
||||
) -> Option<UnderlyingStorage> {
|
||||
// If scanout for things other than Wayland buffers is implemented, this will need to take
|
||||
// the target GPU into account.
|
||||
None
|
||||
}
|
||||
}
|
||||
+149
-16
@@ -5,19 +5,21 @@ use std::mem;
|
||||
|
||||
use anyhow::Context;
|
||||
use arrayvec::ArrayVec;
|
||||
use niri_config::Action;
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::input::{ButtonState, MouseButton};
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
|
||||
use smithay::backend::renderer::element::Kind;
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
|
||||
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::utils::CommitCounter;
|
||||
use smithay::backend::renderer::ExportMem;
|
||||
use smithay::input::keyboard::{Keysym, ModifiersState};
|
||||
use smithay::output::{Output, WeakOutput};
|
||||
use smithay::render_elements;
|
||||
use smithay::utils::{Physical, Point, Rectangle, Size, Transform};
|
||||
use smithay::utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform};
|
||||
|
||||
use crate::config::Action;
|
||||
use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
|
||||
use crate::render_helpers::PrimaryGpuTextureRenderElement;
|
||||
|
||||
const BORDER: i32 = 2;
|
||||
|
||||
@@ -45,11 +47,10 @@ pub struct OutputData {
|
||||
locations: [Point<i32, Physical>; 8],
|
||||
}
|
||||
|
||||
render_elements! {
|
||||
#[derive(Debug)]
|
||||
pub ScreenshotUiRenderElement<R>;
|
||||
Screenshot = TextureRenderElement<R::TextureId>,
|
||||
SolidColor = SolidColorRenderElement,
|
||||
#[derive(Debug)]
|
||||
pub enum ScreenshotUiRenderElement {
|
||||
Screenshot(PrimaryGpuTextureRenderElement),
|
||||
SolidColor(SolidColorRenderElement),
|
||||
}
|
||||
|
||||
impl ScreenshotUi {
|
||||
@@ -236,10 +237,7 @@ impl ScreenshotUi {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_output(
|
||||
&self,
|
||||
output: &Output,
|
||||
) -> ArrayVec<ScreenshotUiRenderElement<GlesRenderer>, 9> {
|
||||
pub fn render_output(&self, output: &Output) -> ArrayVec<ScreenshotUiRenderElement, 9> {
|
||||
let _span = tracy_client::span!("ScreenshotUi::render_output");
|
||||
|
||||
let Self::Open { output_data, .. } = self else {
|
||||
@@ -266,14 +264,14 @@ impl ScreenshotUi {
|
||||
|
||||
// The screenshot itself goes last.
|
||||
elements.push(
|
||||
TextureRenderElement::from_texture_buffer(
|
||||
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
|
||||
(0., 0.),
|
||||
&output_data.texture_buffer,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Kind::Unspecified,
|
||||
)
|
||||
))
|
||||
.into(),
|
||||
);
|
||||
|
||||
@@ -446,3 +444,138 @@ pub fn rect_from_corner_points(
|
||||
let y2 = max(a.y, b.y);
|
||||
Rectangle::from_extemities((x1, y1), (x2 + scale, y2 + scale))
|
||||
}
|
||||
|
||||
// Manual RenderElement implementation due to AsGlesFrame requirement.
|
||||
impl Element for ScreenshotUiRenderElement {
|
||||
fn id(&self) -> &Id {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.id(),
|
||||
Self::SolidColor(elem) => elem.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.current_commit(),
|
||||
Self::SolidColor(elem) => elem.current_commit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.geometry(scale),
|
||||
Self::SolidColor(elem) => elem.geometry(scale),
|
||||
}
|
||||
}
|
||||
|
||||
fn transform(&self) -> Transform {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.transform(),
|
||||
Self::SolidColor(elem) => elem.transform(),
|
||||
}
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.src(),
|
||||
Self::SolidColor(elem) => elem.src(),
|
||||
}
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> Vec<Rectangle<i32, Physical>> {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.damage_since(scale, commit),
|
||||
Self::SolidColor(elem) => elem.damage_since(scale, commit),
|
||||
}
|
||||
}
|
||||
|
||||
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.opaque_regions(scale),
|
||||
Self::SolidColor(elem) => elem.opaque_regions(scale),
|
||||
}
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.alpha(),
|
||||
Self::SolidColor(elem) => elem.alpha(),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> Kind {
|
||||
match self {
|
||||
Self::Screenshot(elem) => elem.kind(),
|
||||
Self::SolidColor(elem) => elem.kind(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderElement<GlesRenderer> for ScreenshotUiRenderElement {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut GlesFrame<'_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), GlesError> {
|
||||
match self {
|
||||
Self::Screenshot(elem) => {
|
||||
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
Self::SolidColor(elem) => {
|
||||
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
|
||||
// If scanout for things other than Wayland buffers is implemented, this will need to take
|
||||
// the target GPU into account.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'render, 'alloc> RenderElement<TtyRenderer<'render, 'alloc>> for ScreenshotUiRenderElement {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut TtyFrame<'render, 'alloc, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), TtyRendererError<'render, 'alloc>> {
|
||||
match self {
|
||||
Self::Screenshot(elem) => {
|
||||
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
Self::SolidColor(elem) => {
|
||||
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn underlying_storage(
|
||||
&self,
|
||||
_renderer: &mut TtyRenderer<'render, 'alloc>,
|
||||
) -> Option<UnderlyingStorage> {
|
||||
// If scanout for things other than Wayland buffers is implemented, this will need to take
|
||||
// the target GPU into account.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SolidColorRenderElement> for ScreenshotUiRenderElement {
|
||||
fn from(x: SolidColorRenderElement) -> Self {
|
||||
Self::SolidColor(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrimaryGpuTextureRenderElement> for ScreenshotUiRenderElement {
|
||||
fn from(x: PrimaryGpuTextureRenderElement) -> Self {
|
||||
Self::Screenshot(x)
|
||||
}
|
||||
}
|
||||
|
||||
+16
-2
@@ -11,10 +11,14 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::{ensure, Context};
|
||||
use directories::UserDirs;
|
||||
use niri_config::Config;
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::rustix::time::{clock_gettime, ClockId};
|
||||
use smithay::utils::{Logical, Point, Rectangle};
|
||||
use smithay::utils::{Logical, Point, Rectangle, Size};
|
||||
|
||||
use crate::config::Config;
|
||||
pub fn clone2<T: Clone, U: Clone>(t: (&T, &U)) -> (T, U) {
|
||||
(t.0.clone(), t.1.clone())
|
||||
}
|
||||
|
||||
pub fn get_monotonic_time() -> Duration {
|
||||
let ts = clock_gettime(ClockId::Monotonic);
|
||||
@@ -25,6 +29,16 @@ pub fn center(rect: Rectangle<i32, Logical>) -> Point<i32, Logical> {
|
||||
rect.loc + rect.size.downscale(2).to_point()
|
||||
}
|
||||
|
||||
pub fn output_size(output: &Output) -> Size<i32, Logical> {
|
||||
let output_scale = output.current_scale().integer_scale();
|
||||
let output_transform = output.current_transform();
|
||||
let output_mode = output.current_mode().unwrap();
|
||||
|
||||
output_transform
|
||||
.transform_size(output_mode.size)
|
||||
.to_logical(output_scale)
|
||||
}
|
||||
|
||||
pub fn make_screenshot_path(config: &Config) -> anyhow::Result<Option<PathBuf>> {
|
||||
let Some(path) = &config.screenshot_path else {
|
||||
return Ok(None);
|
||||
|
||||
Reference in New Issue
Block a user