mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d8f640503 | |||
| b18cfbae23 | |||
| f64e7e14c3 | |||
| e8c9bfc06a | |||
| 07452f50a8 | |||
| 642c5acebb | |||
| 0886dedff1 | |||
| cc88a7d42e | |||
| c0829087da | |||
| b6f6d6a7c2 | |||
| 5ff8b89aaf | |||
| 927abad4b4 | |||
| 3d31f9860a | |||
| 8867a4f84c | |||
| 88f4c1d610 | |||
| ddcb5c5e10 | |||
| cd90dfc7be | |||
| a778ab3897 | |||
| 4c2f49d566 | |||
| 49d7052bb3 | |||
| 07be7e7eae | |||
| 97c8717d1e | |||
| 3ac0a751fe | |||
| 8b39f986d9 | |||
| 354c365a03 | |||
| e0ebf1bdff | |||
| 11633aef98 | |||
| 9193245871 | |||
| 7baf10b751 | |||
| f5d91c5ecc | |||
| 69e3edb5a3 | |||
| d58bb4eaa3 | |||
| c5fe25f422 | |||
| 600cffb009 | |||
| b9d14a9eda | |||
| 0e7e398df3 | |||
| 86bdc6898b | |||
| e5ca335115 | |||
| fce5d66878 | |||
| 05d218113c | |||
| ef6af6adc1 | |||
| 6632699e00 | |||
| d3e72245b0 | |||
| 13fe9c8ac3 |
Generated
+325
-91
@@ -133,9 +133,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.91"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
|
||||
[[package]]
|
||||
name = "appendlist"
|
||||
@@ -203,7 +203,7 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand 2.2.0",
|
||||
"futures-lite 2.3.0",
|
||||
"slab",
|
||||
]
|
||||
@@ -304,7 +304,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -339,7 +339,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -411,7 +411,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -501,7 +501,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -699,7 +699,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -934,6 +934,17 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
@@ -1025,7 +1036,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1093,9 +1104,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
@@ -1150,7 +1161,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1237,7 +1248,7 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
|
||||
dependencies = [
|
||||
"fastrand 2.1.1",
|
||||
"fastrand 2.2.0",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"parking",
|
||||
@@ -1252,7 +1263,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1463,7 +1474,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1479,9 +1490,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.0"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
|
||||
[[package]]
|
||||
name = "glib"
|
||||
@@ -1514,7 +1525,7 @@ dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1600,9 +1611,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gtk4"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e2d105ce672f5cdcb5af2602e91c2901e91c72da15ab76f613ad57ecf04c6d"
|
||||
checksum = "d34465497f5a4c182c9c94a582a187db7d6af0863f28e87ccf4379f21f0e2a22"
|
||||
dependencies = [
|
||||
"cairo-rs",
|
||||
"field-offset",
|
||||
@@ -1628,7 +1639,7 @@ dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1700,13 +1711,142 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1855,7 +1995,7 @@ dependencies = [
|
||||
"libc",
|
||||
"proc-macro2",
|
||||
"regex",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"terminal_size 0.2.6",
|
||||
]
|
||||
|
||||
@@ -1915,9 +2055,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libadwaita"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878"
|
||||
checksum = "8611ee9fb85e7606c362b513afcaf5b59853f79e4d98caaaf581d99465014247"
|
||||
dependencies = [
|
||||
"gdk4",
|
||||
"gio",
|
||||
@@ -1946,9 +2086,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
version = "0.2.162"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
|
||||
[[package]]
|
||||
name = "libdisplay-info"
|
||||
@@ -1971,7 +2111,7 @@ checksum = "ea1cd31036b732a546d845f9485c56b1b606b5e476b0821c680dd66c8cd6fcee"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1987,7 +2127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2081,6 +2221,12 @@ version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
@@ -2193,7 +2339,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2244,7 +2390,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@@ -2258,7 +2404,7 @@ dependencies = [
|
||||
"clap",
|
||||
"directories",
|
||||
"drm-ffi",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand 2.2.0",
|
||||
"futures-util",
|
||||
"git-version",
|
||||
"glam",
|
||||
@@ -2298,7 +2444,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri-config"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"csscolorparser",
|
||||
@@ -2315,7 +2461,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri-ipc"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"schemars",
|
||||
@@ -2325,7 +2471,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri-visual-tests"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gtk4",
|
||||
@@ -2424,10 +2570,10 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2694,9 +2840,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "4.4.0"
|
||||
version = "4.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97"
|
||||
checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -2821,7 +2967,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2850,7 +2996,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2872,7 +3018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand 2.2.0",
|
||||
"futures-io",
|
||||
]
|
||||
|
||||
@@ -3070,7 +3216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3101,7 +3247,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3357,7 +3503,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3395,7 +3541,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3406,7 +3552,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3429,7 +3575,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3512,7 +3658,7 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/Smithay/smithay.git#521344d21e6c9140ad7884ebf4d7e6b98d979854"
|
||||
source = "git+https://github.com/Smithay/smithay.git#5e137dcebc9f2de4d026180dfc4ce81282f7f14f"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags 2.6.0",
|
||||
@@ -3584,7 +3730,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "smithay-drm-extras"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/Smithay/smithay.git#521344d21e6c9140ad7884ebf4d7e6b98d979854"
|
||||
source = "git+https://github.com/Smithay/smithay.git#5e137dcebc9f2de4d026180dfc4ce81282f7f14f"
|
||||
dependencies = [
|
||||
"drm",
|
||||
"libdisplay-info",
|
||||
@@ -3609,6 +3755,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -3662,15 +3814,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.85"
|
||||
version = "2.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
@@ -3720,7 +3883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.1.1",
|
||||
"fastrand 2.2.0",
|
||||
"once_cell",
|
||||
"rustix 0.38.38",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -3774,7 +3937,7 @@ checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3807,20 +3970,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
@@ -3886,7 +4044,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3983,12 +4141,6 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
@@ -4001,15 +4153,6 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
@@ -4024,15 +4167,27 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
@@ -4110,7 +4265,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4144,7 +4299,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4406,7 +4561,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4417,7 +4572,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4722,6 +4877,18 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
@@ -4836,6 +5003,30 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "3.15.2"
|
||||
@@ -4920,7 +5111,50 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+13
-11
@@ -2,7 +2,7 @@
|
||||
members = ["niri-visual-tests"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
description = "A scrollable-tiling Wayland compositor"
|
||||
authors = ["Ivan Molodetskikh <yalterz@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -11,11 +11,11 @@ repository = "https://github.com/YaLTeR/niri"
|
||||
rust-version = "1.77"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.90"
|
||||
anyhow = "1.0.93"
|
||||
bitflags = "2.6.0"
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
k9 = "0.12.0"
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
@@ -55,19 +55,19 @@ calloop = { version = "0.14.1", features = ["executor", "futures-io"] }
|
||||
clap = { workspace = true, features = ["string"] }
|
||||
directories = "5.0.1"
|
||||
drm-ffi = "0.9.0"
|
||||
fastrand = "2.1.1"
|
||||
fastrand = "2.2.0"
|
||||
futures-util = { version = "0.3.31", default-features = false, features = ["std", "io"] }
|
||||
git-version = "0.3.9"
|
||||
glam = "0.29.0"
|
||||
glam = "0.29.2"
|
||||
input = { version = "0.9.1", features = ["libinput_1_21"] }
|
||||
keyframe = { version = "1.1.1", default-features = false }
|
||||
libc = "0.2.161"
|
||||
libc = "0.2.162"
|
||||
libdisplay-info = "0.1.0"
|
||||
log = { version = "0.4.22", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
niri-config = { version = "0.1.9", path = "niri-config" }
|
||||
niri-ipc = { version = "0.1.9", path = "niri-ipc", features = ["clap"] }
|
||||
niri-config = { version = "0.1.10", path = "niri-config" }
|
||||
niri-ipc = { version = "0.1.10", path = "niri-ipc", features = ["clap"] }
|
||||
notify-rust = { version = "~4.10.0", optional = true }
|
||||
ordered-float = "4.4.0"
|
||||
ordered-float = "4.5.0"
|
||||
pango = { version = "0.20.4", features = ["v1_44"] }
|
||||
pangocairo = "0.20.4"
|
||||
pipewire = { git = "https://gitlab.freedesktop.org/pipewire/pipewire-rs.git", optional = true, features = ["v0_3_33"] }
|
||||
@@ -81,7 +81,7 @@ smithay-drm-extras.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
tracy-client.workspace = true
|
||||
url = { version = "2.5.2", optional = true }
|
||||
url = { version = "2.5.3", optional = true }
|
||||
wayland-backend = "0.3.7"
|
||||
wayland-scanner = "0.31.5"
|
||||
xcursor = "0.3.8"
|
||||
@@ -124,6 +124,8 @@ xdp-gnome-screencast = ["dbus", "pipewire"]
|
||||
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client/default"]
|
||||
# Enables the on-demand Tracy profiler instrumentation.
|
||||
profile-with-tracy-ondemand = ["profile-with-tracy", "tracy-client/ondemand", "tracy-client/manual-lifetime"]
|
||||
# Enables Tracy allocation profiling.
|
||||
profile-with-tracy-allocations = ["profile-with-tracy"]
|
||||
# Enables dinit integration (global environment).
|
||||
dinit = []
|
||||
|
||||
@@ -137,7 +139,7 @@ lto = "thin"
|
||||
debug = false
|
||||
|
||||
[package.metadata.generate-rpm]
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
assets = [
|
||||
{ source = "target/release/niri", dest = "/usr/bin/", mode = "755" },
|
||||
{ source = "resources/niri-session", dest = "/usr/bin/", mode = "755" },
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
libclang,
|
||||
libdisplay-info,
|
||||
libinput,
|
||||
libseat,
|
||||
seatd,
|
||||
libxkbcommon,
|
||||
mesa,
|
||||
pango,
|
||||
@@ -90,7 +90,7 @@
|
||||
libGL
|
||||
libdisplay-info
|
||||
libinput
|
||||
libseat
|
||||
seatd
|
||||
libxkbcommon
|
||||
mesa # libgbm
|
||||
pango
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
# Force linking with libEGL and libwayland-client
|
||||
# so they can be discovered by `dlopen()`
|
||||
CARGO_BUILD_RUSTFLAGS = toString (
|
||||
RUSTFLAGS = toString (
|
||||
map (arg: "-C link-arg=" + arg) [
|
||||
"-Wl,--push-state,--no-as-needed"
|
||||
"-lEGL"
|
||||
@@ -208,7 +208,7 @@
|
||||
# in the package expression
|
||||
#
|
||||
# This should only be set with `CARGO_BUILD_RUSTFLAGS="$CARGO_BUILD_RUSTFLAGS -C your-flags"`
|
||||
inherit (niri) CARGO_BUILD_RUSTFLAGS;
|
||||
CARGO_BUILD_RUSTFLAGS = niri.RUSTFLAGS;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ bitflags.workspace = true
|
||||
csscolorparser = "0.7.0"
|
||||
knuffel = "3.2.0"
|
||||
miette = "5.10.0"
|
||||
niri-ipc = { version = "0.1.9", path = "../niri-ipc" }
|
||||
regex = "1.11.0"
|
||||
niri-ipc = { version = "0.1.10", path = "../niri-ipc" }
|
||||
regex = "1.11.1"
|
||||
smithay = { workspace = true, features = ["backend_libinput"] }
|
||||
tracing.workspace = true
|
||||
tracy-client.workspace = true
|
||||
|
||||
+38
-8
@@ -188,6 +188,8 @@ pub struct Touchpad {
|
||||
pub disabled_on_external_mouse: bool,
|
||||
#[knuffel(child)]
|
||||
pub middle_emulation: bool,
|
||||
#[knuffel(child, unwrap(argument), default = FloatOrInt(1.0))]
|
||||
pub scroll_factor: FloatOrInt<0, 100>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
@@ -208,6 +210,8 @@ pub struct Mouse {
|
||||
pub left_handed: bool,
|
||||
#[knuffel(child)]
|
||||
pub middle_emulation: bool,
|
||||
#[knuffel(child, unwrap(argument), default = FloatOrInt(1.0))]
|
||||
pub scroll_factor: FloatOrInt<0, 100>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
@@ -597,6 +601,8 @@ pub struct InsertHint {
|
||||
pub off: bool,
|
||||
#[knuffel(child, default = Self::default().color)]
|
||||
pub color: Color,
|
||||
#[knuffel(child)]
|
||||
pub gradient: Option<Gradient>,
|
||||
}
|
||||
|
||||
impl Default for InsertHint {
|
||||
@@ -604,6 +610,7 @@ impl Default for InsertHint {
|
||||
Self {
|
||||
off: false,
|
||||
color: Color::from_rgba8_unpremul(127, 200, 255, 128),
|
||||
gradient: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -666,7 +673,7 @@ pub struct Cursor {
|
||||
#[knuffel(child, unwrap(argument), default = 24)]
|
||||
pub xcursor_size: u8,
|
||||
#[knuffel(child)]
|
||||
pub hide_on_key_press: bool,
|
||||
pub hide_when_typing: bool,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub hide_after_inactive_ms: Option<u32>,
|
||||
}
|
||||
@@ -676,7 +683,7 @@ impl Default for Cursor {
|
||||
Self {
|
||||
xcursor_theme: String::from("default"),
|
||||
xcursor_size: 24,
|
||||
hide_on_key_press: false,
|
||||
hide_when_typing: false,
|
||||
hide_after_inactive_ms: None,
|
||||
}
|
||||
}
|
||||
@@ -1526,6 +1533,10 @@ pub struct DebugConfig {
|
||||
pub disable_resize_throttling: bool,
|
||||
#[knuffel(child)]
|
||||
pub disable_transactions: bool,
|
||||
#[knuffel(child)]
|
||||
pub keep_laptop_panel_on_when_lid_is_closed: bool,
|
||||
#[knuffel(child)]
|
||||
pub disable_monitor_names: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -1907,13 +1918,17 @@ impl OutputName {
|
||||
if self.make.is_none() && self.model.is_none() && self.serial.is_none() {
|
||||
self.connector.to_string()
|
||||
} else {
|
||||
let make = self.make.as_deref().unwrap_or("Unknown");
|
||||
let model = self.model.as_deref().unwrap_or("Unknown");
|
||||
let serial = self.serial.as_deref().unwrap_or("Unknown");
|
||||
format!("{make} {model} {serial}")
|
||||
self.format_make_model_serial()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_make_model_serial(&self) -> String {
|
||||
let make = self.make.as_deref().unwrap_or("Unknown");
|
||||
let model = self.model.as_deref().unwrap_or("Unknown");
|
||||
let serial = self.serial.as_deref().unwrap_or("Unknown");
|
||||
format!("{make} {model} {serial}")
|
||||
}
|
||||
|
||||
pub fn matches(&self, target: &str) -> bool {
|
||||
// Match by connector.
|
||||
if target.eq_ignore_ascii_case(&self.connector) {
|
||||
@@ -2955,6 +2970,7 @@ mod tests {
|
||||
scroll-button 272
|
||||
tap-button-map "left-middle-right"
|
||||
disabled-on-external-mouse
|
||||
scroll-factor 0.9
|
||||
}
|
||||
|
||||
mouse {
|
||||
@@ -2964,6 +2980,7 @@ mod tests {
|
||||
scroll-method "no-scroll"
|
||||
scroll-button 273
|
||||
middle-emulation
|
||||
scroll-factor 0.2
|
||||
}
|
||||
|
||||
trackpoint {
|
||||
@@ -3051,6 +3068,7 @@ mod tests {
|
||||
|
||||
insert-hint {
|
||||
color "rgb(255, 200, 127)"
|
||||
gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3061,7 +3079,7 @@ mod tests {
|
||||
cursor {
|
||||
xcursor-theme "breeze_cursors"
|
||||
xcursor-size 16
|
||||
hide-on-key-press
|
||||
hide-when-typing
|
||||
hide-after-inactive-ms 3000
|
||||
}
|
||||
|
||||
@@ -3165,6 +3183,7 @@ mod tests {
|
||||
left_handed: false,
|
||||
disabled_on_external_mouse: true,
|
||||
middle_emulation: false,
|
||||
scroll_factor: FloatOrInt(0.9),
|
||||
},
|
||||
mouse: Mouse {
|
||||
off: false,
|
||||
@@ -3175,6 +3194,7 @@ mod tests {
|
||||
scroll_button: Some(273),
|
||||
left_handed: false,
|
||||
middle_emulation: true,
|
||||
scroll_factor: FloatOrInt(0.2),
|
||||
},
|
||||
trackpoint: Trackpoint {
|
||||
off: true,
|
||||
@@ -3253,6 +3273,16 @@ mod tests {
|
||||
insert_hint: InsertHint {
|
||||
off: false,
|
||||
color: Color::from_rgba8_unpremul(255, 200, 127, 255),
|
||||
gradient: Some(Gradient {
|
||||
from: Color::from_rgba8_unpremul(10, 20, 30, 255),
|
||||
to: Color::from_rgba8_unpremul(0, 128, 255, 255),
|
||||
angle: 180,
|
||||
relative_to: GradientRelativeTo::WorkspaceView,
|
||||
in_: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Srgb,
|
||||
hue_interpolation: HueInterpolation::Shorter,
|
||||
},
|
||||
}),
|
||||
},
|
||||
preset_column_widths: vec![
|
||||
PresetSize::Proportion(0.25),
|
||||
@@ -3286,7 +3316,7 @@ mod tests {
|
||||
cursor: Cursor {
|
||||
xcursor_theme: String::from("breeze_cursors"),
|
||||
xcursor_size: 16,
|
||||
hide_on_key_press: true,
|
||||
hide_when_typing: true,
|
||||
hide_after_inactive_ms: Some(3000),
|
||||
},
|
||||
screenshot_path: Some(String::from("~/Screenshots/screenshot.png")),
|
||||
|
||||
+5
-1
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "niri-ipc"
|
||||
version.workspace = true
|
||||
description.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
description = "Types and helpers for interfacing with the niri Wayland compositor."
|
||||
keywords = ["wayland"]
|
||||
categories = ["api-bindings", "os"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, optional = true }
|
||||
schemars = { version = "0.8.21", optional = true }
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# niri-ipc
|
||||
|
||||
Types and helpers for interfacing with the [niri](https://github.com/YaLTeR/niri) Wayland compositor.
|
||||
|
||||
## Backwards compatibility
|
||||
|
||||
This crate follows the niri version.
|
||||
It is **not** API-stable in terms of the Rust semver.
|
||||
In particular, expect new struct fields and enum variants to be added in patch version bumps.
|
||||
|
||||
Use an exact version requirement to avoid breaking changes:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
niri-ipc = "=0.1.10"
|
||||
```
|
||||
@@ -19,6 +19,20 @@
|
||||
//!
|
||||
//! This crate follows the niri version. It is **not** API-stable in terms of the Rust semver. In
|
||||
//! particular, expect new struct fields and enum variants to be added in patch version bumps.
|
||||
//!
|
||||
//! Use an exact version requirement to avoid breaking changes:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! niri-ipc = "=0.1.10"
|
||||
//! ```
|
||||
//!
|
||||
//! ## Features
|
||||
//!
|
||||
//! This crate defines the following features:
|
||||
//! - `json-schema`: derives the [schemars](https://lib.rs/crates/schemars) `JsonSchema` trait for
|
||||
//! the types.
|
||||
//! - `clap`: derives the clap CLI parsing traits for some types. Used internally by niri itself.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -8,11 +8,11 @@ edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_4"] }
|
||||
adw = { version = "0.7.1", package = "libadwaita", features = ["v1_4"] }
|
||||
anyhow.workspace = true
|
||||
gtk = { version = "0.9.2", package = "gtk4", features = ["v4_12"] }
|
||||
niri = { version = "0.1.9", path = ".." }
|
||||
niri-config = { version = "0.1.9", path = "../niri-config" }
|
||||
gtk = { version = "0.9.3", package = "gtk4", features = ["v4_12"] }
|
||||
niri = { version = "0.1.10", path = ".." }
|
||||
niri-config = { version = "0.1.10", path = "../niri-config" }
|
||||
smithay.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
||||
@@ -486,6 +486,7 @@ binds {
|
||||
|
||||
// The quit action will show a confirmation dialog to avoid accidental exits.
|
||||
Mod+Shift+E { quit; }
|
||||
Ctrl+Alt+Delete { quit; }
|
||||
|
||||
// Powers off the monitors. To turn them back on, do any input like
|
||||
// moving the mouse or pressing any other key.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[preferred]
|
||||
default=gnome;gtk;
|
||||
org.freedesktop.impl.portal.Access=gtk;
|
||||
org.freedesktop.impl.portal.Secret=gnome-keyring;
|
||||
|
||||
+92
-28
@@ -61,7 +61,7 @@ use crate::niri::{Niri, RedrawState, State};
|
||||
use crate::render_helpers::debug::draw_damage;
|
||||
use crate::render_helpers::renderer::AsGlesRenderer;
|
||||
use crate::render_helpers::{resources, shaders, RenderTarget};
|
||||
use crate::utils::{get_monotonic_time, logical_output};
|
||||
use crate::utils::{get_monotonic_time, is_laptop_panel, logical_output};
|
||||
|
||||
const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888];
|
||||
|
||||
@@ -636,22 +636,31 @@ impl Tty {
|
||||
connector,
|
||||
crtc: Some(crtc),
|
||||
} => {
|
||||
if let Err(err) = self.connector_connected(niri, node, connector, crtc) {
|
||||
warn!("error connecting connector: {err:?}");
|
||||
}
|
||||
let connector_name = format_connector_name(&connector);
|
||||
let output_name =
|
||||
make_output_name(&device.drm, connector.handle(), connector_name, false);
|
||||
debug!(
|
||||
"new connector: {} \"{}\"",
|
||||
&output_name.connector,
|
||||
output_name.format_make_model_serial(),
|
||||
);
|
||||
|
||||
// Assign an id to this crtc.
|
||||
device.output_ids.insert(crtc, OutputId::next());
|
||||
}
|
||||
DrmScanEvent::Disconnected {
|
||||
crtc: Some(crtc), ..
|
||||
} => {
|
||||
self.connector_disconnected(niri, node, crtc);
|
||||
removed.push(crtc);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this is better done in connector_disconnected(), but currently we call that to
|
||||
// turn off outputs temporarily, too. So we can't do this there.
|
||||
for crtc in &removed {
|
||||
self.connector_disconnected(niri, node, *crtc);
|
||||
}
|
||||
|
||||
let Some(device) = self.devices.get_mut(&node) else {
|
||||
error!("device disappeared");
|
||||
return;
|
||||
@@ -663,7 +672,12 @@ impl Tty {
|
||||
}
|
||||
}
|
||||
|
||||
self.refresh_ipc_outputs(niri);
|
||||
// This will connect any new connectors if needed, and apply other changes, such as
|
||||
// connecting back the internal laptop monitor once it becomes the only monitor left.
|
||||
//
|
||||
// It will also call refresh_ipc_outputs(), which will catch the disconnected connectors
|
||||
// above.
|
||||
self.on_output_config_changed(niri);
|
||||
}
|
||||
|
||||
fn device_removed(&mut self, device_id: dev_t, niri: &mut Niri) {
|
||||
@@ -749,7 +763,12 @@ impl Tty {
|
||||
|
||||
let device = self.devices.get_mut(&node).context("missing device")?;
|
||||
|
||||
let output_name = make_output_name(&device.drm, connector.handle(), connector_name.clone());
|
||||
let output_name = make_output_name(
|
||||
&device.drm,
|
||||
connector.handle(),
|
||||
connector_name.clone(),
|
||||
self.config.borrow().debug.disable_monitor_names,
|
||||
);
|
||||
|
||||
let non_desktop = find_drm_property(&device.drm, connector.handle(), "non-desktop")
|
||||
.and_then(|(_, info, value)| info.value_type().convert_value(value).as_boolean())
|
||||
@@ -767,10 +786,6 @@ impl Tty {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// This should be unique per CRTC connection, however currently we can call
|
||||
// connector_connected() multiple times for turning the output off and on.
|
||||
device.output_ids.entry(crtc).or_insert_with(OutputId::next);
|
||||
|
||||
let config = self
|
||||
.config
|
||||
.borrow()
|
||||
@@ -779,11 +794,6 @@ impl Tty {
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
if config.off {
|
||||
debug!("output is disabled in the config");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for m in connector.modes() {
|
||||
trace!("{m:?}");
|
||||
}
|
||||
@@ -1564,8 +1574,12 @@ impl Tty {
|
||||
for (connector, crtc) in device.drm_scanner.crtcs() {
|
||||
let connector_name = format_connector_name(connector);
|
||||
let physical_size = connector.size();
|
||||
let output_name =
|
||||
make_output_name(&device.drm, connector.handle(), connector_name.clone());
|
||||
let output_name = make_output_name(
|
||||
&device.drm,
|
||||
connector.handle(),
|
||||
connector_name.clone(),
|
||||
self.config.borrow().debug.disable_monitor_names,
|
||||
);
|
||||
|
||||
let surface = device.surfaces.get(&crtc);
|
||||
let current_crtc_mode = surface.map(|surface| surface.compositor.pending_mode());
|
||||
@@ -1711,6 +1725,24 @@ impl Tty {
|
||||
}
|
||||
self.update_output_config_on_resume = false;
|
||||
|
||||
// Figure out if we should disable laptop panels.
|
||||
let mut disable_laptop_panels = false;
|
||||
if niri.is_lid_closed {
|
||||
let config = self.config.borrow();
|
||||
if !config.debug.keep_laptop_panel_on_when_lid_is_closed {
|
||||
// Check if any external monitor is connected.
|
||||
'outer: for device in self.devices.values() {
|
||||
for (connector, _crtc) in device.drm_scanner.crtcs() {
|
||||
if !is_laptop_panel(&format_connector_name(connector)) {
|
||||
disable_laptop_panels = true;
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let should_disable = |connector: &str| disable_laptop_panels && is_laptop_panel(connector);
|
||||
|
||||
let mut to_disconnect = vec![];
|
||||
let mut to_connect = vec![];
|
||||
|
||||
@@ -1723,7 +1755,7 @@ impl Tty {
|
||||
.find(&surface.name)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
if config.off {
|
||||
if config.off || should_disable(&surface.name.connector) {
|
||||
to_disconnect.push((node, crtc));
|
||||
continue;
|
||||
}
|
||||
@@ -1819,12 +1851,21 @@ impl Tty {
|
||||
}
|
||||
|
||||
// Check if already enabled.
|
||||
if device.surfaces.contains_key(&crtc) {
|
||||
if device.surfaces.contains_key(&crtc)
|
||||
|| device
|
||||
.non_desktop_connectors
|
||||
.contains(&(connector.handle(), crtc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let connector_name = format_connector_name(connector);
|
||||
let output_name = make_output_name(&device.drm, connector.handle(), connector_name);
|
||||
let output_name = make_output_name(
|
||||
&device.drm,
|
||||
connector.handle(),
|
||||
connector_name,
|
||||
self.config.borrow().debug.disable_monitor_names,
|
||||
);
|
||||
let config = self
|
||||
.config
|
||||
.borrow()
|
||||
@@ -1833,8 +1874,8 @@ impl Tty {
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
if !config.off {
|
||||
to_connect.push((node, connector.clone(), crtc));
|
||||
if !(config.off || should_disable(&output_name.connector)) {
|
||||
to_connect.push((node, connector.clone(), crtc, output_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1843,7 +1884,11 @@ impl Tty {
|
||||
self.connector_disconnected(niri, node, crtc);
|
||||
}
|
||||
|
||||
for (node, connector, crtc) in to_connect {
|
||||
// Sort by output name to get more predictable first focused output at initial compositor
|
||||
// startup, when multiple connectors appear at once.
|
||||
to_connect.sort_unstable_by(|a, b| a.3.compare(&b.3));
|
||||
|
||||
for (node, connector, crtc, _name) in to_connect {
|
||||
if let Err(err) = self.connector_connected(niri, node, connector, crtc) {
|
||||
warn!("error connecting connector: {err:?}");
|
||||
}
|
||||
@@ -1878,12 +1923,21 @@ impl Tty {
|
||||
}
|
||||
|
||||
// Check if already enabled.
|
||||
if device.surfaces.contains_key(&crtc) {
|
||||
if device.surfaces.contains_key(&crtc)
|
||||
|| device
|
||||
.non_desktop_connectors
|
||||
.contains(&(connector.handle(), crtc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let connector_name = format_connector_name(connector);
|
||||
let output_name = make_output_name(&device.drm, connector.handle(), connector_name);
|
||||
let output_name = make_output_name(
|
||||
&device.drm,
|
||||
connector.handle(),
|
||||
connector_name,
|
||||
self.config.borrow().debug.disable_monitor_names,
|
||||
);
|
||||
if output_name.matches(target) {
|
||||
return Some(output_name);
|
||||
}
|
||||
@@ -2481,7 +2535,17 @@ fn make_output_name(
|
||||
device: &DrmDevice,
|
||||
connector: connector::Handle,
|
||||
connector_name: String,
|
||||
disable_monitor_names: bool,
|
||||
) -> OutputName {
|
||||
if disable_monitor_names {
|
||||
return OutputName {
|
||||
connector: connector_name,
|
||||
make: None,
|
||||
model: None,
|
||||
serial: None,
|
||||
};
|
||||
}
|
||||
|
||||
let info = get_edid_info(device, connector)
|
||||
.map_err(|err| warn!("error getting EDID info for {connector_name}: {err:?}"))
|
||||
.ok();
|
||||
|
||||
@@ -8,6 +8,7 @@ use zbus::{dbus_interface, fdo, SignalContext};
|
||||
|
||||
use super::Start;
|
||||
use crate::backend::IpcOutputMap;
|
||||
use crate::utils::is_laptop_panel;
|
||||
|
||||
pub struct DisplayConfig {
|
||||
ipc_outputs: Arc<Mutex<IpcOutputMap>>,
|
||||
@@ -63,7 +64,7 @@ impl DisplayConfig {
|
||||
.map(|output| {
|
||||
// Loosely matches the check in Mutter.
|
||||
let c = &output.name;
|
||||
let is_laptop_panel = matches!(c.get(..4), Some("eDP-" | "LVDS" | "DSI-"));
|
||||
let is_laptop_panel = is_laptop_panel(c);
|
||||
let display_name = make_display_name(output, is_laptop_panel);
|
||||
|
||||
let mut properties = HashMap::new();
|
||||
|
||||
+30
-23
@@ -7,6 +7,7 @@ use std::io::Write;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::drm::DrmNode;
|
||||
@@ -74,12 +75,14 @@ use crate::protocols::gamma_control::{GammaControlHandler, GammaControlManagerSt
|
||||
use crate::protocols::mutter_x11_interop::MutterX11InteropHandler;
|
||||
use crate::protocols::output_management::{OutputManagementHandler, OutputManagementManagerState};
|
||||
use crate::protocols::screencopy::{Screencopy, ScreencopyHandler, ScreencopyManagerState};
|
||||
use crate::utils::{output_size, send_scale_transform};
|
||||
use crate::utils::{output_size, send_scale_transform, with_toplevel_role};
|
||||
use crate::{
|
||||
delegate_foreign_toplevel, delegate_gamma_control, delegate_mutter_x11_interop,
|
||||
delegate_output_management, delegate_screencopy,
|
||||
};
|
||||
|
||||
pub const XDG_ACTIVATION_TOKEN_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
impl SeatHandler for State {
|
||||
type KeyboardFocus = WlSurface;
|
||||
type PointerFocus = WlSurface;
|
||||
@@ -137,11 +140,12 @@ impl TabletSeatHandler for State {
|
||||
delegate_tablet_manager!(State);
|
||||
|
||||
impl PointerConstraintsHandler for State {
|
||||
fn new_constraint(&mut self, _surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
||||
self.niri.maybe_activate_pointer_constraint(
|
||||
pointer.current_location(),
|
||||
&self.niri.pointer_focus,
|
||||
);
|
||||
fn new_constraint(&mut self, _surface: &WlSurface, _pointer: &PointerHandle<Self>) {
|
||||
// Pointer constraints track pointer focus internally, so make sure it's up to date before
|
||||
// activating a new one.
|
||||
self.refresh_pointer_contents();
|
||||
|
||||
self.niri.maybe_activate_pointer_constraint();
|
||||
}
|
||||
|
||||
fn cursor_position_hint(
|
||||
@@ -158,19 +162,20 @@ impl PointerConstraintsHandler for State {
|
||||
return;
|
||||
}
|
||||
|
||||
// Logically the following two checks should always succeed (so, they should print
|
||||
// error!()s if they fail). However, currently both can fail because niri's pointer focus
|
||||
// doesn't take pointer grabs into account. So if you start, say, a middle-drag in Blender,
|
||||
// then touchpad-swipe the window away, the niri pointer focus will change, even though the
|
||||
// real pointer focus remains on the Blender surface due to the click grab.
|
||||
// Note: this is surface under pointer, not pointer focus. So if you start, say, a
|
||||
// middle-drag in Blender, then touchpad-swipe the window away, the surface under pointer
|
||||
// will change, even though the real pointer focus remains on the Blender surface due to
|
||||
// the click grab.
|
||||
//
|
||||
// FIXME: add error!()s when niri pointer focus takes grabs into account. Alternatively,
|
||||
// recompute the surface origin here (but that is a bit clunky).
|
||||
let Some((ref focused_surface, origin)) = self.niri.pointer_focus.surface else {
|
||||
// Ideally we would just use the constraint surface, but we need its origin. So this is
|
||||
// more of a hack because pointer contents has the surface origin available.
|
||||
//
|
||||
// FIXME: use the constraint surface somehow, don't use pointer contents.
|
||||
let Some((ref surface_under_pointer, origin)) = self.niri.pointer_contents.surface else {
|
||||
return;
|
||||
};
|
||||
|
||||
if focused_surface != surface {
|
||||
if surface_under_pointer != surface {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -459,12 +464,12 @@ impl ForeignToplevelHandler for State {
|
||||
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>) {
|
||||
if let Some((mapped, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)
|
||||
{
|
||||
if !mapped
|
||||
.toplevel()
|
||||
.current_state()
|
||||
.capabilities
|
||||
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
|
||||
{
|
||||
let has_fullscreen_cap = with_toplevel_role(mapped.toplevel(), |role| {
|
||||
role.current
|
||||
.capabilities
|
||||
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
|
||||
});
|
||||
if !has_fullscreen_cap {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -622,16 +627,18 @@ impl XdgActivationHandler for State {
|
||||
|
||||
fn request_activation(
|
||||
&mut self,
|
||||
_token: XdgActivationToken,
|
||||
token: XdgActivationToken,
|
||||
token_data: XdgActivationTokenData,
|
||||
surface: WlSurface,
|
||||
) {
|
||||
if token_data.timestamp.elapsed().as_secs() < 10 {
|
||||
if token_data.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT {
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&surface) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
self.niri.layer_shell_on_demand_focus = None;
|
||||
self.niri.queue_redraw_all();
|
||||
|
||||
self.niri.activation_state.remove_token(&token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ impl XdgShellHandler for State {
|
||||
if focus.id().same_client_as(&wl_surface.id()) {
|
||||
// Deny move requests from DnD grabs to work around
|
||||
// https://gitlab.gnome.org/GNOME/gtk/-/issues/7113
|
||||
let is_dnd_grab = grab.as_any().downcast_ref::<DnDGrab<Self>>().is_some();
|
||||
let is_dnd_grab = grab.as_any().is::<DnDGrab<Self>>();
|
||||
|
||||
if !is_dnd_grab {
|
||||
grab_start_data =
|
||||
@@ -103,8 +103,7 @@ impl XdgShellHandler for State {
|
||||
if focus.id().same_client_as(&wl_surface.id()) {
|
||||
// Deny move requests from DnD grabs to work around
|
||||
// https://gitlab.gnome.org/GNOME/gtk/-/issues/7113
|
||||
let is_dnd_grab =
|
||||
grab.as_any().downcast_ref::<DnDGrab<Self>>().is_some();
|
||||
let is_dnd_grab = grab.as_any().is::<DnDGrab<Self>>();
|
||||
|
||||
if !is_dnd_grab {
|
||||
grab_start_data =
|
||||
@@ -149,7 +148,6 @@ impl XdgShellHandler for State {
|
||||
PointerOrTouchStartData::Pointer(start_data) => {
|
||||
let grab = MoveGrab::new(start_data, window);
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
self.niri.pointer_grab_ongoing = true;
|
||||
}
|
||||
PointerOrTouchStartData::Touch(start_data) => {
|
||||
let touch = self.niri.seat.get_touch().unwrap();
|
||||
@@ -247,7 +245,6 @@ impl XdgShellHandler for State {
|
||||
PointerOrTouchStartData::Pointer(start_data) => {
|
||||
let grab = ResizeGrab::new(start_data, window);
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
self.niri.pointer_grab_ongoing = true;
|
||||
}
|
||||
PointerOrTouchStartData::Touch(start_data) => {
|
||||
let touch = self.niri.seat.get_touch().unwrap();
|
||||
|
||||
+56
-36
@@ -423,7 +423,7 @@ impl State {
|
||||
}
|
||||
|
||||
fn hide_cursor_if_needed(&mut self) {
|
||||
if !self.niri.config.borrow().cursor.hide_on_key_press {
|
||||
if !self.niri.config.borrow().cursor.hide_when_typing {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1320,12 +1320,17 @@ impl State {
|
||||
self.niri.tablet_cursor_location = None;
|
||||
|
||||
// Check if we have an active pointer constraint.
|
||||
//
|
||||
// FIXME: ideally this should use the pointer focus with up-to-date global location.
|
||||
let mut pointer_confined = None;
|
||||
if let Some(focus) = &self.niri.pointer_focus.surface {
|
||||
let pos_within_surface = pos - focus.1;
|
||||
if let Some(under) = &self.niri.pointer_contents.surface {
|
||||
// No need to check if the pointer focus surface matches, because here we're checking
|
||||
// for an already-active constraint, and the constraint is deactivated when the focused
|
||||
// surface changes.
|
||||
let pos_within_surface = pos - under.1;
|
||||
|
||||
let mut pointer_locked = false;
|
||||
with_pointer_constraint(&focus.0, &pointer, |constraint| {
|
||||
with_pointer_constraint(&under.0, &pointer, |constraint| {
|
||||
let Some(constraint) = constraint else { return };
|
||||
if !constraint.is_active() {
|
||||
return;
|
||||
@@ -1343,7 +1348,7 @@ impl State {
|
||||
pointer_locked = true;
|
||||
}
|
||||
PointerConstraint::Confined(confine) => {
|
||||
pointer_confined = Some((focus.clone(), confine.region().cloned()));
|
||||
pointer_confined = Some((under.clone(), confine.region().cloned()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1352,7 +1357,7 @@ impl State {
|
||||
if pointer_locked {
|
||||
pointer.relative_motion(
|
||||
self,
|
||||
Some(focus.clone()),
|
||||
Some(under.clone()),
|
||||
&RelativeMotionEvent {
|
||||
delta: event.delta(),
|
||||
delta_unaccel: event.delta_unaccel(),
|
||||
@@ -1410,7 +1415,7 @@ impl State {
|
||||
self.niri.screenshot_ui.pointer_motion(point);
|
||||
}
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(new_pos);
|
||||
let under = self.niri.contents_under(new_pos);
|
||||
|
||||
// Handle confined pointer.
|
||||
if let Some((focus_surface, region)) = pointer_confined {
|
||||
@@ -1448,10 +1453,7 @@ impl State {
|
||||
|
||||
self.niri.handle_focus_follows_mouse(&under);
|
||||
|
||||
// Activate a new confinement if necessary.
|
||||
self.niri.maybe_activate_pointer_constraint(new_pos, &under);
|
||||
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
self.niri.pointer_contents.clone_from(&under);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
@@ -1475,6 +1477,9 @@ impl State {
|
||||
|
||||
pointer.frame(self);
|
||||
|
||||
// Activate a new confinement if necessary.
|
||||
self.niri.maybe_activate_pointer_constraint();
|
||||
|
||||
// Redraw to update the cursor position.
|
||||
// FIXME: redraw only outputs overlapping the cursor.
|
||||
self.niri.queue_redraw_all();
|
||||
@@ -1509,12 +1514,11 @@ impl State {
|
||||
self.niri.screenshot_ui.pointer_motion(point);
|
||||
}
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
let under = self.niri.contents_under(pos);
|
||||
|
||||
self.niri.handle_focus_follows_mouse(&under);
|
||||
|
||||
self.niri.maybe_activate_pointer_constraint(pos, &under);
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
self.niri.pointer_contents.clone_from(&under);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
@@ -1528,6 +1532,8 @@ impl State {
|
||||
|
||||
pointer.frame(self);
|
||||
|
||||
self.niri.maybe_activate_pointer_constraint();
|
||||
|
||||
// We moved the pointer, show it.
|
||||
self.niri.pointer_hidden = false;
|
||||
|
||||
@@ -1582,7 +1588,6 @@ impl State {
|
||||
};
|
||||
let grab = MoveGrab::new(start_data, window.clone());
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
self.niri.pointer_grab_ongoing = true;
|
||||
self.niri
|
||||
.cursor_manager
|
||||
.set_cursor_image(CursorImageStatus::Named(CursorIcon::Move));
|
||||
@@ -1648,7 +1653,6 @@ impl State {
|
||||
};
|
||||
let grab = ResizeGrab::new(start_data, window.clone());
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
self.niri.pointer_grab_ongoing = true;
|
||||
self.niri.cursor_manager.set_cursor_image(
|
||||
CursorImageStatus::Named(edges.cursor_icon()),
|
||||
);
|
||||
@@ -1684,7 +1688,6 @@ impl State {
|
||||
};
|
||||
let grab = SpatialMovementGrab::new(start_data, output);
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
self.niri.pointer_grab_ongoing = true;
|
||||
self.niri
|
||||
.cursor_manager
|
||||
.set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));
|
||||
@@ -1693,11 +1696,11 @@ impl State {
|
||||
}
|
||||
};
|
||||
|
||||
self.update_pointer_focus();
|
||||
self.update_pointer_contents();
|
||||
|
||||
if ButtonState::Pressed == button_state {
|
||||
let layer_focus = self.niri.pointer_focus.layer.clone();
|
||||
self.niri.focus_layer_surface_if_on_demand(layer_focus);
|
||||
let layer_under = self.niri.pointer_contents.layer.clone();
|
||||
self.niri.focus_layer_surface_if_on_demand(layer_under);
|
||||
}
|
||||
|
||||
if let Some(button) = event.button() {
|
||||
@@ -1878,14 +1881,23 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
let scroll_factor = match source {
|
||||
AxisSource::Wheel => self.niri.config.borrow().input.mouse.scroll_factor.0,
|
||||
AxisSource::Finger => self.niri.config.borrow().input.touchpad.scroll_factor.0,
|
||||
_ => 1.0,
|
||||
};
|
||||
|
||||
let horizontal_amount = horizontal_amount.unwrap_or_else(|| {
|
||||
// Winit backend, discrete scrolling.
|
||||
horizontal_amount_v120.unwrap_or(0.0) / 120. * 15.
|
||||
});
|
||||
}) * scroll_factor;
|
||||
let vertical_amount = vertical_amount.unwrap_or_else(|| {
|
||||
// Winit backend, discrete scrolling.
|
||||
vertical_amount_v120.unwrap_or(0.0) / 120. * 15.
|
||||
});
|
||||
}) * scroll_factor;
|
||||
|
||||
let horizontal_amount_v120 = horizontal_amount_v120.map(|x| x * scroll_factor);
|
||||
let vertical_amount_v120 = vertical_amount_v120.map(|x| x * scroll_factor);
|
||||
|
||||
let mut frame = AxisFrame::new(event.time_msec()).source(source);
|
||||
if horizontal_amount != 0.0 {
|
||||
@@ -1914,7 +1926,7 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
self.update_pointer_focus();
|
||||
self.update_pointer_contents();
|
||||
|
||||
let pointer = &self.niri.seat.get_pointer().unwrap();
|
||||
pointer.axis(self, frame);
|
||||
@@ -1929,7 +1941,7 @@ impl State {
|
||||
return;
|
||||
};
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
let under = self.niri.contents_under(pos);
|
||||
|
||||
let tablet_seat = self.niri.seat.tablet_seat();
|
||||
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));
|
||||
@@ -1981,7 +1993,7 @@ impl State {
|
||||
tool.tip_down(serial, event.time_msec());
|
||||
|
||||
if let Some(pos) = self.niri.tablet_cursor_location {
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
let under = self.niri.contents_under(pos);
|
||||
if let Some(window) = under.window {
|
||||
self.niri.layout.activate_window(&window);
|
||||
|
||||
@@ -2011,7 +2023,7 @@ impl State {
|
||||
return;
|
||||
};
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
let under = self.niri.contents_under(pos);
|
||||
|
||||
let tablet_seat = self.niri.seat.tablet_seat();
|
||||
let display_handle = self.niri.display_handle.clone();
|
||||
@@ -2043,6 +2055,7 @@ impl State {
|
||||
self.move_cursor(pos);
|
||||
}
|
||||
|
||||
self.niri.pointer_hidden = false;
|
||||
self.niri.tablet_cursor_location = None;
|
||||
}
|
||||
}
|
||||
@@ -2076,7 +2089,7 @@ impl State {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2167,7 +2180,7 @@ impl State {
|
||||
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2210,7 +2223,7 @@ impl State {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2228,7 +2241,7 @@ impl State {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2245,7 +2258,7 @@ impl State {
|
||||
fn on_gesture_pinch_update<I: InputBackend>(&mut self, event: I::GesturePinchUpdateEvent) {
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2264,7 +2277,7 @@ impl State {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2282,7 +2295,7 @@ impl State {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2300,7 +2313,7 @@ impl State {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.niri.seat.get_pointer().unwrap();
|
||||
|
||||
if self.update_pointer_focus() {
|
||||
if self.update_pointer_contents() {
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
@@ -2339,7 +2352,7 @@ impl State {
|
||||
return;
|
||||
};
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||
let under = self.niri.contents_under(touch_location);
|
||||
|
||||
if !handle.is_grabbed() {
|
||||
if let Some(window) = under.window {
|
||||
@@ -2392,7 +2405,7 @@ impl State {
|
||||
let Some(touch_location) = self.compute_touch_location(&evt) else {
|
||||
return;
|
||||
};
|
||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||
let under = self.niri.contents_under(touch_location);
|
||||
handle.motion(
|
||||
self,
|
||||
under.surface,
|
||||
@@ -2421,6 +2434,13 @@ impl State {
|
||||
return;
|
||||
};
|
||||
|
||||
if switch == Switch::Lid {
|
||||
let is_closed = evt.state() == SwitchState::On;
|
||||
debug!("lid switch {}", if is_closed { "closed" } else { "opened" });
|
||||
self.niri.is_lid_closed = is_closed;
|
||||
self.backend.on_output_config_changed(&mut self.niri);
|
||||
}
|
||||
|
||||
let action = {
|
||||
let bindings = &self.niri.config.borrow().switch_events;
|
||||
find_configured_switch_action(bindings, switch, evt.state())
|
||||
|
||||
@@ -34,7 +34,6 @@ impl MoveGrab {
|
||||
state.niri.layout.interactive_move_end(&self.window);
|
||||
// FIXME: only redraw the window output.
|
||||
state.niri.queue_redraw_all();
|
||||
state.niri.pointer_grab_ongoing = false;
|
||||
state
|
||||
.niri
|
||||
.cursor_manager
|
||||
@@ -113,7 +112,7 @@ impl PointerGrab<State> for MoveGrab {
|
||||
.output_under(handle.current_location())
|
||||
.map(|(output, _)| output)
|
||||
.cloned();
|
||||
// TODO: workspace switch gesture.
|
||||
// FIXME: workspace switch gesture.
|
||||
if let Some(output) = output {
|
||||
self.is_moving = true;
|
||||
data.niri.layout.view_offset_gesture_begin(&output, false);
|
||||
|
||||
@@ -22,7 +22,6 @@ impl ResizeGrab {
|
||||
|
||||
fn on_ungrab(&mut self, state: &mut State) {
|
||||
state.niri.layout.interactive_resize_end(&self.window);
|
||||
state.niri.pointer_grab_ongoing = false;
|
||||
state
|
||||
.niri
|
||||
.cursor_manager
|
||||
|
||||
@@ -50,7 +50,6 @@ impl SpatialMovementGrab {
|
||||
state.niri.queue_redraw(&output);
|
||||
}
|
||||
|
||||
state.niri.pointer_grab_ongoing = false;
|
||||
state
|
||||
.niri
|
||||
.cursor_manager
|
||||
|
||||
+8
-28
@@ -19,13 +19,11 @@ use niri_ipc::{Event, KeyboardLayouts, OutputConfigChanged, Reply, Request, Resp
|
||||
use smithay::reexports::calloop::generic::Generic;
|
||||
use smithay::reexports::calloop::{Interest, LoopHandle, Mode, PostAction};
|
||||
use smithay::reexports::rustix::fs::unlink;
|
||||
use smithay::wayland::compositor::with_states;
|
||||
use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;
|
||||
|
||||
use crate::backend::IpcOutputMap;
|
||||
use crate::layout::workspace::WorkspaceId;
|
||||
use crate::niri::State;
|
||||
use crate::utils::version;
|
||||
use crate::utils::{version, with_toplevel_role};
|
||||
use crate::window::Mapped;
|
||||
|
||||
// If an event stream client fails to read events fast enough that we accumulate more than this
|
||||
@@ -361,22 +359,12 @@ async fn handle_event_stream_client(client: EventStreamClient) -> anyhow::Result
|
||||
}
|
||||
|
||||
fn make_ipc_window(mapped: &Mapped, workspace_id: Option<WorkspaceId>) -> niri_ipc::Window {
|
||||
let wl_surface = mapped.toplevel().wl_surface();
|
||||
with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
niri_ipc::Window {
|
||||
id: mapped.id().get(),
|
||||
title: role.title.clone(),
|
||||
app_id: role.app_id.clone(),
|
||||
workspace_id: workspace_id.map(|id| id.get()),
|
||||
is_focused: mapped.is_focused(),
|
||||
}
|
||||
with_toplevel_role(mapped.toplevel(), |role| niri_ipc::Window {
|
||||
id: mapped.id().get(),
|
||||
title: role.title.clone(),
|
||||
app_id: role.app_id.clone(),
|
||||
workspace_id: workspace_id.map(|id| id.get()),
|
||||
is_focused: mapped.is_focused(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -559,15 +547,7 @@ impl State {
|
||||
let workspace_id = ws_id.map(|id| id.get());
|
||||
let mut changed = ipc_win.workspace_id != workspace_id;
|
||||
|
||||
let wl_surface = mapped.toplevel().wl_surface();
|
||||
changed |= with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
changed |= with_toplevel_role(mapped.toplevel(), |role| {
|
||||
ipc_win.title != role.title || ipc_win.app_id != role.app_id
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use niri_config::{CornerRadius, FloatOrInt};
|
||||
use smithay::utils::{Logical, Point, Rectangle, Size};
|
||||
|
||||
use super::focus_ring::{FocusRing, FocusRingRenderElement};
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InsertHintElement {
|
||||
inner: FocusRing,
|
||||
}
|
||||
|
||||
pub type InsertHintRenderElement = FocusRingRenderElement;
|
||||
|
||||
impl InsertHintElement {
|
||||
pub fn new(config: niri_config::InsertHint) -> Self {
|
||||
Self {
|
||||
inner: FocusRing::new(niri_config::FocusRing {
|
||||
off: config.off,
|
||||
width: FloatOrInt(0.),
|
||||
active_color: config.color,
|
||||
inactive_color: config.color,
|
||||
active_gradient: config.gradient,
|
||||
inactive_gradient: config.gradient,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_config(&mut self, config: niri_config::InsertHint) {
|
||||
self.inner.update_config(niri_config::FocusRing {
|
||||
off: config.off,
|
||||
width: FloatOrInt(0.),
|
||||
active_color: config.color,
|
||||
inactive_color: config.color,
|
||||
active_gradient: config.gradient,
|
||||
inactive_gradient: config.gradient,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_shaders(&mut self) {
|
||||
self.inner.update_shaders();
|
||||
}
|
||||
|
||||
pub fn update_render_elements(
|
||||
&mut self,
|
||||
size: Size<f64, Logical>,
|
||||
view_rect: Rectangle<f64, Logical>,
|
||||
radius: CornerRadius,
|
||||
scale: f64,
|
||||
) {
|
||||
self.inner
|
||||
.update_render_elements(size, true, false, view_rect, radius, scale);
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
renderer: &mut impl NiriRenderer,
|
||||
location: Point<f64, Logical>,
|
||||
) -> impl Iterator<Item = FocusRingRenderElement> {
|
||||
self.inner.render(renderer, location)
|
||||
}
|
||||
}
|
||||
+171
-59
@@ -30,12 +30,14 @@
|
||||
//! making the primary output their original output.
|
||||
|
||||
use std::cmp::min;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use niri_config::{
|
||||
CenterFocusedColumn, Config, FloatOrInt, PresetSize, Struts, Workspace as WorkspaceConfig,
|
||||
CenterFocusedColumn, Config, CornerRadius, FloatOrInt, PresetSize, Struts,
|
||||
Workspace as WorkspaceConfig,
|
||||
};
|
||||
use niri_ipc::SizeChange;
|
||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||
@@ -64,6 +66,7 @@ use crate::window::ResolvedWindowRules;
|
||||
|
||||
pub mod closing_window;
|
||||
pub mod focus_ring;
|
||||
pub mod insert_hint_element;
|
||||
pub mod monitor;
|
||||
pub mod opening_window;
|
||||
pub mod tile;
|
||||
@@ -85,59 +88,6 @@ niri_render_elements! {
|
||||
pub type LayoutElementRenderSnapshot =
|
||||
RenderSnapshot<BakedBuffer<TextureBuffer<GlesTexture>>, BakedBuffer<SolidColorBuffer>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InteractiveMoveState<W: LayoutElement> {
|
||||
/// Initial rubberbanding; the window remains in the layout.
|
||||
Starting {
|
||||
/// The window we're moving.
|
||||
window_id: W::Id,
|
||||
/// Current pointer delta from the starting location.
|
||||
pointer_delta: Point<f64, Logical>,
|
||||
/// Pointer location within the visual window geometry as ratio from geometry size.
|
||||
///
|
||||
/// This helps the pointer remain inside the window as it resizes.
|
||||
pointer_ratio_within_window: (f64, f64),
|
||||
},
|
||||
/// Moving; the window is no longer in the layout.
|
||||
Moving(InteractiveMoveData<W>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InteractiveMoveData<W: LayoutElement> {
|
||||
/// The window being moved.
|
||||
pub(self) tile: Tile<W>,
|
||||
/// Output where the window is currently located/rendered.
|
||||
pub(self) output: Output,
|
||||
/// Current pointer position within output.
|
||||
pub(self) pointer_pos_within_output: Point<f64, Logical>,
|
||||
/// Window column width.
|
||||
pub(self) width: ColumnWidth,
|
||||
/// Whether the window column was full-width.
|
||||
pub(self) is_full_width: bool,
|
||||
/// Pointer location within the visual window geometry as ratio from geometry size.
|
||||
///
|
||||
/// This helps the pointer remain inside the window as it resizes.
|
||||
pub(self) pointer_ratio_within_window: (f64, f64),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InteractiveResizeData {
|
||||
pub(self) edges: ResizeEdge,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ConfigureIntent {
|
||||
/// A configure is not needed (no changes to server pending state).
|
||||
NotNeeded,
|
||||
/// A configure is throttled (due to resizing too fast for example).
|
||||
Throttled,
|
||||
/// Can send the configure if it isn't throttled externally (only size changed).
|
||||
CanSend,
|
||||
/// Should send the configure regardless of external throttling (something other than size
|
||||
/// changed).
|
||||
ShouldSend,
|
||||
}
|
||||
|
||||
pub trait LayoutElement {
|
||||
/// Type that can be used as a unique ID of this element.
|
||||
type Id: PartialEq + std::fmt::Debug + Clone;
|
||||
@@ -256,6 +206,14 @@ pub struct Layout<W: LayoutElement> {
|
||||
/// This normally indicates that the layout has keyboard focus, but not always. E.g. when the
|
||||
/// screenshot UI is open, it keeps the layout drawing as active.
|
||||
is_active: bool,
|
||||
/// Map from monitor name to id of its last active workspace.
|
||||
///
|
||||
/// This data is stored upon monitor removal and is used to restore the active workspace when
|
||||
/// the monitor is reconnected.
|
||||
///
|
||||
/// The workspace id does not necessarily point to a valid workspace. If it doesn't, then it is
|
||||
/// simply ignored.
|
||||
last_active_workspace_id: HashMap<String, WorkspaceId>,
|
||||
/// Ongoing interactive move.
|
||||
interactive_move: Option<InteractiveMoveState<W>>,
|
||||
/// Configurable properties of the layout.
|
||||
@@ -331,6 +289,59 @@ impl Default for Options {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InteractiveMoveState<W: LayoutElement> {
|
||||
/// Initial rubberbanding; the window remains in the layout.
|
||||
Starting {
|
||||
/// The window we're moving.
|
||||
window_id: W::Id,
|
||||
/// Current pointer delta from the starting location.
|
||||
pointer_delta: Point<f64, Logical>,
|
||||
/// Pointer location within the visual window geometry as ratio from geometry size.
|
||||
///
|
||||
/// This helps the pointer remain inside the window as it resizes.
|
||||
pointer_ratio_within_window: (f64, f64),
|
||||
},
|
||||
/// Moving; the window is no longer in the layout.
|
||||
Moving(InteractiveMoveData<W>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InteractiveMoveData<W: LayoutElement> {
|
||||
/// The window being moved.
|
||||
pub(self) tile: Tile<W>,
|
||||
/// Output where the window is currently located/rendered.
|
||||
pub(self) output: Output,
|
||||
/// Current pointer position within output.
|
||||
pub(self) pointer_pos_within_output: Point<f64, Logical>,
|
||||
/// Window column width.
|
||||
pub(self) width: ColumnWidth,
|
||||
/// Whether the window column was full-width.
|
||||
pub(self) is_full_width: bool,
|
||||
/// Pointer location within the visual window geometry as ratio from geometry size.
|
||||
///
|
||||
/// This helps the pointer remain inside the window as it resizes.
|
||||
pub(self) pointer_ratio_within_window: (f64, f64),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InteractiveResizeData {
|
||||
pub(self) edges: ResizeEdge,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ConfigureIntent {
|
||||
/// A configure is not needed (no changes to server pending state).
|
||||
NotNeeded,
|
||||
/// A configure is throttled (due to resizing too fast for example).
|
||||
Throttled,
|
||||
/// Can send the configure if it isn't throttled externally (only size changed).
|
||||
CanSend,
|
||||
/// Should send the configure regardless of external throttling (something other than size
|
||||
/// changed).
|
||||
ShouldSend,
|
||||
}
|
||||
|
||||
/// Tile that was just removed from the layout.
|
||||
pub struct RemovedTile<W: LayoutElement> {
|
||||
tile: Tile<W>,
|
||||
@@ -430,6 +441,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
Self {
|
||||
monitor_set: MonitorSet::NoOutputs { workspaces: vec![] },
|
||||
is_active: true,
|
||||
last_active_workspace_id: HashMap::new(),
|
||||
interactive_move: None,
|
||||
options: Rc::new(options),
|
||||
}
|
||||
@@ -447,6 +459,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
Self {
|
||||
monitor_set: MonitorSet::NoOutputs { workspaces },
|
||||
is_active: true,
|
||||
last_active_workspace_id: HashMap::new(),
|
||||
interactive_move: None,
|
||||
options: opts,
|
||||
}
|
||||
@@ -461,6 +474,9 @@ impl<W: LayoutElement> Layout<W> {
|
||||
} => {
|
||||
let primary = &mut monitors[primary_idx];
|
||||
|
||||
let ws_id_to_activate = self.last_active_workspace_id.remove(&output.name());
|
||||
let mut active_workspace_idx = None;
|
||||
|
||||
let mut stopped_primary_ws_switch = false;
|
||||
|
||||
let mut workspaces = vec![];
|
||||
@@ -480,6 +496,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
// another monitor. However, we will add an empty workspace in the end
|
||||
// instead.
|
||||
if ws.has_windows() || ws.name.is_some() {
|
||||
if Some(ws.id()) == ws_id_to_activate {
|
||||
active_workspace_idx = Some(workspaces.len());
|
||||
}
|
||||
|
||||
workspaces.push(ws);
|
||||
}
|
||||
|
||||
@@ -497,6 +517,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
|
||||
workspaces.reverse();
|
||||
|
||||
if let Some(idx) = &mut active_workspace_idx {
|
||||
*idx = workspaces.len() - *idx - 1;
|
||||
}
|
||||
|
||||
// Make sure there's always an empty workspace.
|
||||
workspaces.push(Workspace::new(output.clone(), self.options.clone()));
|
||||
|
||||
@@ -504,7 +528,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
ws.set_output(Some(output.clone()));
|
||||
}
|
||||
|
||||
monitors.push(Monitor::new(output, workspaces, self.options.clone()));
|
||||
let mut monitor = Monitor::new(output, workspaces, self.options.clone());
|
||||
monitor.active_workspace_idx = active_workspace_idx.unwrap_or(0);
|
||||
monitors.push(monitor);
|
||||
|
||||
MonitorSet::Normal {
|
||||
monitors,
|
||||
primary_idx,
|
||||
@@ -515,11 +542,19 @@ impl<W: LayoutElement> Layout<W> {
|
||||
// We know there are no empty workspaces there, so add one.
|
||||
workspaces.push(Workspace::new(output.clone(), self.options.clone()));
|
||||
|
||||
for workspace in &mut workspaces {
|
||||
let ws_id_to_activate = self.last_active_workspace_id.remove(&output.name());
|
||||
let mut active_workspace_idx = 0;
|
||||
|
||||
for (i, workspace) in workspaces.iter_mut().enumerate() {
|
||||
workspace.set_output(Some(output.clone()));
|
||||
|
||||
if Some(workspace.id()) == ws_id_to_activate {
|
||||
active_workspace_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
let monitor = Monitor::new(output, workspaces, self.options.clone());
|
||||
let mut monitor = Monitor::new(output, workspaces, self.options.clone());
|
||||
monitor.active_workspace_idx = active_workspace_idx;
|
||||
|
||||
MonitorSet::Normal {
|
||||
monitors: vec![monitor],
|
||||
@@ -542,6 +577,12 @@ impl<W: LayoutElement> Layout<W> {
|
||||
.position(|mon| &mon.output == output)
|
||||
.expect("trying to remove non-existing output");
|
||||
let monitor = monitors.remove(idx);
|
||||
|
||||
self.last_active_workspace_id.insert(
|
||||
monitor.output_name().clone(),
|
||||
monitor.workspaces[monitor.active_workspace_idx].id(),
|
||||
);
|
||||
|
||||
let mut workspaces = monitor.workspaces;
|
||||
|
||||
for ws in &mut workspaces {
|
||||
@@ -2258,13 +2299,21 @@ impl<W: LayoutElement> Layout<W> {
|
||||
.find(|ws| ws.id() == ws_id)
|
||||
.unwrap();
|
||||
|
||||
// TODO: empty workspaces need to reset their view position right away for this to
|
||||
// look right.
|
||||
let position = ws.get_insert_position(move_.pointer_pos_within_output - offset);
|
||||
|
||||
let rules = move_.tile.window().rules();
|
||||
let border_width = move_.tile.effective_border_width().unwrap_or(0.);
|
||||
let corner_radius = rules
|
||||
.geometry_corner_radius
|
||||
.map_or(CornerRadius::default(), |radius| {
|
||||
radius.expanded_by(border_width as f32)
|
||||
});
|
||||
|
||||
ws.set_insert_hint(InsertHint {
|
||||
position,
|
||||
width: move_.width,
|
||||
is_full_width: move_.is_full_width,
|
||||
corner_radius,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5674,6 +5723,69 @@ mod tests {
|
||||
check_ops(&ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_active_workspace_is_preserved() {
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
Op::AddWindow {
|
||||
id: 1,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::FocusWorkspaceDown,
|
||||
Op::AddWindow {
|
||||
id: 2,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::RemoveOutput(1),
|
||||
Op::AddOutput(1),
|
||||
];
|
||||
|
||||
let mut layout = Layout::default();
|
||||
for op in ops {
|
||||
op.apply(&mut layout);
|
||||
}
|
||||
|
||||
let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
assert_eq!(monitors[0].active_workspace_idx, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_active_workspace_is_preserved_with_other_outputs() {
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
Op::AddOutput(2),
|
||||
Op::AddWindow {
|
||||
id: 1,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::FocusWorkspaceDown,
|
||||
Op::AddWindow {
|
||||
id: 2,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::RemoveOutput(1),
|
||||
Op::AddOutput(1),
|
||||
];
|
||||
|
||||
let mut layout = Layout::default();
|
||||
for op in ops {
|
||||
op.apply(&mut layout);
|
||||
}
|
||||
|
||||
let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
assert_eq!(monitors[1].active_workspace_idx, 1);
|
||||
}
|
||||
|
||||
fn arbitrary_spacing() -> impl Strategy<Value = f64> {
|
||||
// Give equal weight to:
|
||||
// - 0: the element is disabled
|
||||
|
||||
+1
-1
@@ -392,7 +392,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
}
|
||||
|
||||
/// Returns `None` if the border is hidden and `Some(width)` if it should be shown.
|
||||
fn effective_border_width(&self) -> Option<f64> {
|
||||
pub fn effective_border_width(&self) -> Option<f64> {
|
||||
if self.is_fullscreen {
|
||||
return None;
|
||||
}
|
||||
|
||||
+23
-17
@@ -4,11 +4,10 @@ use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use niri_config::{
|
||||
CenterFocusedColumn, OutputName, PresetSize, Struts, Workspace as WorkspaceConfig,
|
||||
CenterFocusedColumn, CornerRadius, OutputName, PresetSize, Struts, Workspace as WorkspaceConfig,
|
||||
};
|
||||
use niri_ipc::SizeChange;
|
||||
use ordered_float::NotNan;
|
||||
use smithay::backend::renderer::element::Kind;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::desktop::{layer_map_for_output, Window};
|
||||
use smithay::output::Output;
|
||||
@@ -17,13 +16,13 @@ use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};
|
||||
|
||||
use super::closing_window::{ClosingWindow, ClosingWindowRenderElement};
|
||||
use super::insert_hint_element::{InsertHintElement, InsertHintRenderElement};
|
||||
use super::tile::{Tile, TileRenderElement, TileRenderSnapshot};
|
||||
use super::{ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile};
|
||||
use crate::animation::Animation;
|
||||
use crate::input::swipe_tracker::SwipeTracker;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use crate::render_helpers::RenderTarget;
|
||||
use crate::utils::id::IdCounter;
|
||||
use crate::utils::transaction::{Transaction, TransactionBlocker};
|
||||
@@ -111,8 +110,8 @@ pub struct Workspace<W: LayoutElement> {
|
||||
/// Indication where an interactively-moved window is about to be placed.
|
||||
insert_hint: Option<InsertHint>,
|
||||
|
||||
/// Buffer for the insert hint.
|
||||
insert_hint_buffer: SolidColorBuffer,
|
||||
/// Insert hint element for rendering.
|
||||
insert_hint_element: InsertHintElement,
|
||||
|
||||
/// Configurable properties of the layout as received from the parent monitor.
|
||||
pub(super) base_options: Rc<Options>,
|
||||
@@ -138,6 +137,7 @@ pub struct InsertHint {
|
||||
pub position: InsertPosition,
|
||||
pub width: ColumnWidth,
|
||||
pub is_full_width: bool,
|
||||
pub corner_radius: CornerRadius,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -173,6 +173,7 @@ niri_render_elements! {
|
||||
WorkspaceRenderElement<R> => {
|
||||
Tile = TileRenderElement<R>,
|
||||
ClosingWindow = ClosingWindowRenderElement,
|
||||
InsertHint = InsertHintRenderElement,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +441,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
view_offset_before_fullscreen: None,
|
||||
closing_windows: vec![],
|
||||
insert_hint: None,
|
||||
insert_hint_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
|
||||
insert_hint_element: InsertHintElement::new(options.insert_hint),
|
||||
base_options,
|
||||
options,
|
||||
name: config.map(|c| c.name.0),
|
||||
@@ -480,7 +481,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
view_offset_before_fullscreen: None,
|
||||
closing_windows: vec![],
|
||||
insert_hint: None,
|
||||
insert_hint_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
|
||||
insert_hint_element: InsertHintElement::new(options.insert_hint),
|
||||
base_options,
|
||||
options,
|
||||
name: config.map(|c| c.name.0),
|
||||
@@ -562,8 +563,13 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
if let Some(insert_hint) = &self.insert_hint {
|
||||
if let Some(area) = self.insert_hint_area(insert_hint) {
|
||||
self.insert_hint_buffer
|
||||
.update(area.size, self.options.insert_hint.color.to_array_premul());
|
||||
let view_rect = Rectangle::from_loc_and_size(area.loc.upscale(-1.), view_size);
|
||||
self.insert_hint_element.update_render_elements(
|
||||
area.size,
|
||||
view_rect,
|
||||
insert_hint.corner_radius,
|
||||
self.scale.fractional_scale(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -577,6 +583,8 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
data.update(column);
|
||||
}
|
||||
|
||||
self.insert_hint_element.update_config(options.insert_hint);
|
||||
|
||||
self.base_options = base_options;
|
||||
self.options = options;
|
||||
}
|
||||
@@ -587,6 +595,8 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
tile.update_shaders();
|
||||
}
|
||||
}
|
||||
|
||||
self.insert_hint_element.update_shaders();
|
||||
}
|
||||
|
||||
pub fn windows(&self) -> impl Iterator<Item = &W> + '_ {
|
||||
@@ -2672,14 +2682,10 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
// Draw the insert hint.
|
||||
if let Some(insert_hint) = &self.insert_hint {
|
||||
if let Some(area) = self.insert_hint_area(insert_hint) {
|
||||
rv.push(
|
||||
TileRenderElement::SolidColor(SolidColorRenderElement::from_buffer(
|
||||
&self.insert_hint_buffer,
|
||||
area.loc,
|
||||
1.,
|
||||
Kind::Unspecified,
|
||||
))
|
||||
.into(),
|
||||
rv.extend(
|
||||
self.insert_hint_element
|
||||
.render(renderer, area.loc)
|
||||
.map(WorkspaceRenderElement::InsertHint),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ use tracing_subscriber::EnvFilter;
|
||||
|
||||
const DEFAULT_LOG_FILTER: &str = "niri=debug,smithay::backend::renderer::gles=error";
|
||||
|
||||
#[cfg(feature = "profile-with-tracy-allocations")]
|
||||
#[global_allocator]
|
||||
static GLOBAL: tracy_client::ProfiledAllocator<std::alloc::System> =
|
||||
tracy_client::ProfiledAllocator::new(std::alloc::System, 100);
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Set backtrace defaults if not set.
|
||||
if env::var_os("RUST_BACKTRACE").is_none() {
|
||||
|
||||
+100
-65
@@ -110,7 +110,7 @@ use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri};
|
||||
#[cfg(feature = "xdp-gnome-screencast")]
|
||||
use crate::dbus::mutter_screen_cast::{self, ScreenCastToNiri};
|
||||
use crate::frame_clock::FrameClock;
|
||||
use crate::handlers::configure_lock_surface;
|
||||
use crate::handlers::{configure_lock_surface, XDG_ACTIVATION_TOKEN_TIMEOUT};
|
||||
use crate::input::scroll_tracker::ScrollTracker;
|
||||
use crate::input::{
|
||||
apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_wheel_binds, TabletData,
|
||||
@@ -207,6 +207,12 @@ pub struct Niri {
|
||||
// When false, we're idling with monitors powered off.
|
||||
pub monitors_active: bool,
|
||||
|
||||
/// Whether the laptop lid is closed.
|
||||
///
|
||||
/// Libinput guarantees that the lid switch starts in open state, and if it was closed during
|
||||
/// startup, libinput will immediately send a closed event.
|
||||
pub is_lid_closed: bool,
|
||||
|
||||
pub devices: HashSet<input::Device>,
|
||||
pub tablets: HashMap<input::Device, TabletData>,
|
||||
pub touch: HashSet<input::Device>,
|
||||
@@ -262,15 +268,26 @@ pub struct Niri {
|
||||
pub cursor_texture_cache: CursorTextureCache,
|
||||
pub cursor_shape_manager_state: CursorShapeManagerState,
|
||||
pub dnd_icon: Option<DndIcon>,
|
||||
pub pointer_focus: PointerFocus,
|
||||
/// Contents under pointer.
|
||||
///
|
||||
/// Periodically updated: on motion and other events and in the loop callback. If you require
|
||||
/// the real up-to-date contents somewhere, it's better to recompute on the spot.
|
||||
///
|
||||
/// This is not pointer focus. I.e. during a click grab, the pointer focus remains on the
|
||||
/// client with the grab, but this field will keep updating to the latest contents as if no
|
||||
/// grab was active.
|
||||
///
|
||||
/// This is primarily useful for emitting pointer motion events for surfaces that move
|
||||
/// underneath the cursor on their own (i.e. when the tiling layout moves). In this case, not
|
||||
/// taking grabs into account is expected, because we pass the information to pointer.motion()
|
||||
/// which passes it down through grabs, which decide what to do with it as they see fit.
|
||||
pub pointer_contents: PointContents,
|
||||
/// Whether the pointer is hidden, for example due to a previous touch input.
|
||||
///
|
||||
/// When this happens, the pointer also loses any focus. This is so that touch can prevent
|
||||
/// various tooltips from sticking around.
|
||||
pub pointer_hidden: bool,
|
||||
pub pointer_inactivity_timer: Option<RegistrationToken>,
|
||||
// FIXME: this should be able to be removed once PointerFocus takes grabs into account.
|
||||
pub pointer_grab_ongoing: bool,
|
||||
pub tablet_cursor_location: Option<Point<f64, Logical>>,
|
||||
pub gesture_swipe_3f_cumulative: Option<(f64, f64)>,
|
||||
pub vertical_wheel_tracker: ScrollTracker,
|
||||
@@ -386,10 +403,10 @@ pub enum KeyboardFocus {
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub struct PointerFocus {
|
||||
// Output under pointer.
|
||||
pub struct PointContents {
|
||||
// Output under point.
|
||||
pub output: Option<Output>,
|
||||
// Surface under pointer and its location in global coordinate space.
|
||||
// Surface under point and its location in the global coordinate space.
|
||||
pub surface: Option<(WlSurface, Point<f64, Logical>)>,
|
||||
// If surface belongs to a window, this is that window.
|
||||
pub window: Option<Window>,
|
||||
@@ -553,7 +570,7 @@ impl State {
|
||||
self.niri.refresh_pointer_outputs();
|
||||
self.niri.global_space.refresh();
|
||||
self.niri.refresh_idle_inhibit();
|
||||
self.refresh_pointer_focus();
|
||||
self.refresh_pointer_contents();
|
||||
foreign_toplevel::refresh(self);
|
||||
self.niri.refresh_window_rules();
|
||||
self.refresh_ipc_outputs();
|
||||
@@ -574,10 +591,8 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn move_cursor(&mut self, location: Point<f64, Logical>) {
|
||||
let under = self.niri.surface_under_and_global_space(location);
|
||||
self.niri
|
||||
.maybe_activate_pointer_constraint(location, &under);
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
let under = self.niri.contents_under(location);
|
||||
self.niri.pointer_contents.clone_from(&under);
|
||||
|
||||
let pointer = &self.niri.seat.get_pointer().unwrap();
|
||||
pointer.motion(
|
||||
@@ -591,9 +606,9 @@ impl State {
|
||||
);
|
||||
pointer.frame(self);
|
||||
|
||||
// We moved the pointer, show it.
|
||||
self.niri.pointer_hidden = false;
|
||||
self.niri.reset_pointer_inactivity_timer();
|
||||
self.niri.maybe_activate_pointer_constraint();
|
||||
|
||||
// We do not show the pointer on programmatic or keyboard movement.
|
||||
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
@@ -678,8 +693,8 @@ impl State {
|
||||
self.move_cursor_to_focused_tile(CenterCoords::Both)
|
||||
}
|
||||
|
||||
pub fn refresh_pointer_focus(&mut self) {
|
||||
let _span = tracy_client::span!("Niri::refresh_pointer_focus");
|
||||
pub fn refresh_pointer_contents(&mut self) {
|
||||
let _span = tracy_client::span!("Niri::refresh_pointer_contents");
|
||||
|
||||
let pointer = &self.niri.seat.get_pointer().unwrap();
|
||||
let location = pointer.current_location();
|
||||
@@ -694,36 +709,37 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
if !self.update_pointer_focus() {
|
||||
if !self.update_pointer_contents() {
|
||||
return;
|
||||
}
|
||||
|
||||
pointer.frame(self);
|
||||
|
||||
// Pointer motion from a surface to nothing triggers a cursor change to default, which
|
||||
// means we may need to redraw.
|
||||
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
|
||||
pub fn update_pointer_focus(&mut self) -> bool {
|
||||
let _span = tracy_client::span!("Niri::update_pointer_focus");
|
||||
pub fn update_pointer_contents(&mut self) -> bool {
|
||||
let _span = tracy_client::span!("Niri::update_pointer_contents");
|
||||
|
||||
let pointer = &self.niri.seat.get_pointer().unwrap();
|
||||
let location = pointer.current_location();
|
||||
let under = if self.niri.pointer_hidden {
|
||||
PointerFocus::default()
|
||||
PointContents::default()
|
||||
} else {
|
||||
self.niri.surface_under_and_global_space(location)
|
||||
self.niri.contents_under(location)
|
||||
};
|
||||
|
||||
// We're not changing the global cursor location here, so if the focus did not change, then
|
||||
// nothing changed.
|
||||
if self.niri.pointer_focus == under {
|
||||
// We're not changing the global cursor location here, so if the contents did not change,
|
||||
// then nothing changed.
|
||||
if self.niri.pointer_contents == under {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.niri
|
||||
.maybe_activate_pointer_constraint(location, &under);
|
||||
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
self.niri.pointer_contents.clone_from(&under);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
@@ -735,6 +751,8 @@ impl State {
|
||||
},
|
||||
);
|
||||
|
||||
self.niri.maybe_activate_pointer_constraint();
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@@ -1089,6 +1107,12 @@ impl State {
|
||||
|
||||
if config.debug != old_config.debug {
|
||||
debug_config_changed = true;
|
||||
|
||||
if config.debug.keep_laptop_panel_on_when_lid_is_closed
|
||||
!= old_config.debug.keep_laptop_panel_on_when_lid_is_closed
|
||||
{
|
||||
output_config_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
*old_config = config;
|
||||
@@ -1543,7 +1567,7 @@ impl State {
|
||||
to_introspect: &async_channel::Sender<NiriToIntrospect>,
|
||||
msg: IntrospectToNiri,
|
||||
) {
|
||||
use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;
|
||||
use crate::utils::with_toplevel_role;
|
||||
|
||||
let IntrospectToNiri::GetWindows = msg;
|
||||
let _span = tracy_client::span!("GetWindows");
|
||||
@@ -1551,21 +1575,8 @@ impl State {
|
||||
let mut windows = HashMap::new();
|
||||
|
||||
self.niri.layout.with_windows(|mapped, _, _| {
|
||||
let wl_surface = mapped
|
||||
.window
|
||||
.toplevel()
|
||||
.expect("no X11 support")
|
||||
.wl_surface();
|
||||
|
||||
let id = mapped.id().get();
|
||||
let props = with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
let props = with_toplevel_role(mapped.toplevel(), |role| {
|
||||
gnome_shell_introspect::WindowProperties {
|
||||
title: role.title.clone().unwrap_or_default(),
|
||||
app_id: role.app_id.clone().unwrap_or_default(),
|
||||
@@ -1695,6 +1706,18 @@ impl Niri {
|
||||
is_tty && !client.get_data::<ClientState>().unwrap().restricted
|
||||
});
|
||||
let activation_state = XdgActivationState::new::<State>(&display_handle);
|
||||
event_loop
|
||||
.insert_source(
|
||||
Timer::from_duration(XDG_ACTIVATION_TOKEN_TIMEOUT),
|
||||
|_, _, state| {
|
||||
state.niri.activation_state.retain_tokens(|_, token_data| {
|
||||
token_data.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT
|
||||
});
|
||||
TimeoutAction::ToDuration(XDG_ACTIVATION_TOKEN_TIMEOUT)
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mutter_x11_interop_state =
|
||||
MutterX11InteropManagerState::new::<State, _>(&display_handle, move |_| true);
|
||||
|
||||
@@ -1820,6 +1843,7 @@ impl Niri {
|
||||
blocker_cleared_tx,
|
||||
blocker_cleared_rx,
|
||||
monitors_active: true,
|
||||
is_lid_closed: false,
|
||||
|
||||
devices: HashSet::new(),
|
||||
tablets: HashMap::new(),
|
||||
@@ -1873,10 +1897,9 @@ impl Niri {
|
||||
cursor_texture_cache: Default::default(),
|
||||
cursor_shape_manager_state,
|
||||
dnd_icon: None,
|
||||
pointer_focus: PointerFocus::default(),
|
||||
pointer_contents: PointContents::default(),
|
||||
pointer_hidden: false,
|
||||
pointer_inactivity_timer: None,
|
||||
pointer_grab_ongoing: false,
|
||||
tablet_cursor_location: None,
|
||||
gesture_swipe_3f_cumulative: None,
|
||||
vertical_wheel_tracker: ScrollTracker::new(120),
|
||||
@@ -2323,13 +2346,14 @@ impl Niri {
|
||||
self.window_under(pos)
|
||||
}
|
||||
|
||||
/// Returns the surface under cursor and its position in the global space.
|
||||
/// Returns contents under the given point.
|
||||
///
|
||||
/// Pointer needs location in global space, and focused window location compatible with that
|
||||
/// global space. We don't have a global space for all windows, but this function converts the
|
||||
/// window location temporarily to the current global space.
|
||||
pub fn surface_under_and_global_space(&mut self, pos: Point<f64, Logical>) -> PointerFocus {
|
||||
let mut rv = PointerFocus::default();
|
||||
/// We don't have a proper global space for all windows, so this function converts window
|
||||
/// locations to global space according to where they are rendered.
|
||||
///
|
||||
/// This function does not take pointer or touch grabs into account.
|
||||
pub fn contents_under(&mut self, pos: Point<f64, Logical>) -> PointContents {
|
||||
let mut rv = PointContents::default();
|
||||
|
||||
let Some((output, pos_within_output)) = self.output_under(pos) else {
|
||||
return rv;
|
||||
@@ -4486,8 +4510,16 @@ impl Niri {
|
||||
self.cursor_manager
|
||||
.set_cursor_image(CursorImageStatus::default_named());
|
||||
|
||||
self.lock_state = LockState::Locking(confirmation);
|
||||
self.queue_redraw_all();
|
||||
if self.output_state.is_empty() {
|
||||
// There are no outputs, lock the session right away.
|
||||
let lock = confirmation.ext_session_lock().clone();
|
||||
confirmation.lock();
|
||||
self.lock_state = LockState::Locked(lock);
|
||||
} else {
|
||||
// There are outputs, which we need to redraw before locking.
|
||||
self.lock_state = LockState::Locking(confirmation);
|
||||
self.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlock(&mut self) {
|
||||
@@ -4514,17 +4546,20 @@ impl Niri {
|
||||
output_state.lock_surface = Some(surface);
|
||||
}
|
||||
|
||||
pub fn maybe_activate_pointer_constraint(
|
||||
&self,
|
||||
new_pos: Point<f64, Logical>,
|
||||
new_under: &PointerFocus,
|
||||
) {
|
||||
let Some((surface, surface_loc)) = &new_under.surface else {
|
||||
/// Activates the pointer constraint if necessary according to the current pointer contents.
|
||||
///
|
||||
/// Make sure the pointer location and contents are up to date before calling this.
|
||||
pub fn maybe_activate_pointer_constraint(&self) {
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
let pointer_pos = pointer.current_location();
|
||||
|
||||
let Some((surface, surface_loc)) = &self.pointer_contents.surface else {
|
||||
return;
|
||||
};
|
||||
if self.pointer_grab_ongoing {
|
||||
if Some(surface) != pointer.current_focus().as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
let pointer = &self.seat.get_pointer().unwrap();
|
||||
with_pointer_constraint(surface, pointer, |constraint| {
|
||||
let Some(constraint) = constraint else { return };
|
||||
@@ -4535,8 +4570,8 @@ impl Niri {
|
||||
|
||||
// Constraint does not apply if not within region.
|
||||
if let Some(region) = constraint.region() {
|
||||
let new_pos_within_surface = new_pos - *surface_loc;
|
||||
if !region.contains(new_pos_within_surface.to_i32_round()) {
|
||||
let pos_within_surface = pointer_pos - *surface_loc;
|
||||
if !region.contains(pos_within_surface.to_i32_round()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4608,7 +4643,7 @@ impl Niri {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointerFocus) {
|
||||
pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointContents) {
|
||||
let Some(ffm) = self.config.borrow().input.focus_follows_mouse else {
|
||||
return;
|
||||
};
|
||||
@@ -4619,7 +4654,7 @@ impl Niri {
|
||||
}
|
||||
|
||||
// Recompute the current pointer focus because we don't update it during animations.
|
||||
let current_focus = self.surface_under_and_global_space(pointer.current_location());
|
||||
let current_focus = self.contents_under(pointer.current_location());
|
||||
|
||||
if let Some(output) = &new_focus.output {
|
||||
if current_focus.output.as_ref() != Some(output) {
|
||||
|
||||
@@ -11,10 +11,7 @@ use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::reexports::wayland_server::{
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
};
|
||||
use smithay::wayland::compositor::with_states;
|
||||
use smithay::wayland::shell::xdg::{
|
||||
ToplevelStateSet, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
use smithay::wayland::shell::xdg::{ToplevelStateSet, XdgToplevelSurfaceRoleAttributes};
|
||||
use wayland_protocols_wlr::foreign_toplevel::v1::server::{
|
||||
zwlr_foreign_toplevel_handle_v1, zwlr_foreign_toplevel_manager_v1,
|
||||
};
|
||||
@@ -22,6 +19,7 @@ use zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1;
|
||||
use zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1;
|
||||
|
||||
use crate::niri::State;
|
||||
use crate::utils::with_toplevel_role;
|
||||
|
||||
const VERSION: u32 = 3;
|
||||
|
||||
@@ -96,37 +94,23 @@ pub fn refresh(state: &mut State) {
|
||||
// the previous window and only then activate the newly focused window.
|
||||
let mut focused = None;
|
||||
state.niri.layout.with_windows(|mapped, output, _| {
|
||||
let wl_surface = mapped.toplevel().wl_surface();
|
||||
|
||||
with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
let toplevel = mapped.toplevel();
|
||||
let wl_surface = toplevel.wl_surface();
|
||||
with_toplevel_role(toplevel, |role| {
|
||||
if state.niri.keyboard_focus.surface() == Some(wl_surface) {
|
||||
focused = Some((mapped.window.clone(), output.cloned()));
|
||||
} else {
|
||||
refresh_toplevel(protocol_state, wl_surface, &role, output, false);
|
||||
refresh_toplevel(protocol_state, wl_surface, role, output, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Finally, refresh the focused window.
|
||||
if let Some((window, output)) = focused {
|
||||
let wl_surface = window.toplevel().expect("no x11 support").wl_surface();
|
||||
|
||||
with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
refresh_toplevel(protocol_state, wl_surface, &role, output.as_ref(), true);
|
||||
let toplevel = window.toplevel().expect("no X11 support");
|
||||
let wl_surface = toplevel.wl_surface();
|
||||
with_toplevel_role(toplevel, |role| {
|
||||
refresh_toplevel(protocol_state, wl_surface, role, output.as_ref(), true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+24
-1
@@ -17,8 +17,11 @@ use smithay::reexports::rustix::time::{clock_gettime, ClockId};
|
||||
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{Coordinate, Logical, Point, Rectangle, Size, Transform};
|
||||
use smithay::wayland::compositor::{send_surface_state, SurfaceData};
|
||||
use smithay::wayland::compositor::{send_surface_state, with_states, SurfaceData};
|
||||
use smithay::wayland::fractional_scale::with_fractional_scale;
|
||||
use smithay::wayland::shell::xdg::{
|
||||
ToplevelSurface, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
|
||||
pub mod id;
|
||||
pub mod scale;
|
||||
@@ -221,6 +224,26 @@ pub fn output_matches_name(output: &Output, target: &str) -> bool {
|
||||
name.matches(target)
|
||||
}
|
||||
|
||||
pub fn is_laptop_panel(connector: &str) -> bool {
|
||||
matches!(connector.get(..4), Some("eDP-" | "LVDS" | "DSI-"))
|
||||
}
|
||||
|
||||
pub fn with_toplevel_role<T>(
|
||||
toplevel: &ToplevelSurface,
|
||||
f: impl FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
|
||||
) -> T {
|
||||
with_states(toplevel.wl_surface(), |states| {
|
||||
let mut role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
f(&mut role)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "dbus")]
|
||||
pub fn show_screenshot_notification(image_path: Option<PathBuf>) {
|
||||
let mut notification = notify_rust::Notification::new();
|
||||
|
||||
+9
-15
@@ -15,7 +15,7 @@ use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::reexports::wayland_server::Resource as _;
|
||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};
|
||||
use smithay::wayland::compositor::{remove_pre_commit_hook, with_states, HookId};
|
||||
use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface, XdgToplevelSurfaceData};
|
||||
use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface};
|
||||
|
||||
use super::{ResolvedWindowRules, WindowRef};
|
||||
use crate::handlers::KdeDecorationsModeState;
|
||||
@@ -33,7 +33,7 @@ use crate::render_helpers::surface::render_snapshot_from_surface_tree;
|
||||
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements};
|
||||
use crate::utils::id::IdCounter;
|
||||
use crate::utils::transaction::Transaction;
|
||||
use crate::utils::{send_scale_transform, ResizeEdge};
|
||||
use crate::utils::{send_scale_transform, with_toplevel_role, ResizeEdge};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mapped {
|
||||
@@ -571,7 +571,7 @@ impl LayoutElement for Mapped {
|
||||
|
||||
fn has_ssd(&self) -> bool {
|
||||
let toplevel = self.toplevel();
|
||||
let mode = toplevel.current_state().decoration_mode;
|
||||
let mode = with_toplevel_role(self.toplevel(), |role| role.current.decoration_mode);
|
||||
|
||||
match mode {
|
||||
Some(zxdg_toplevel_decoration_v1::Mode::ServerSide) => true,
|
||||
@@ -631,14 +631,7 @@ impl LayoutElement for Mapped {
|
||||
let _span =
|
||||
trace_span!("configure_intent", surface = ?self.toplevel().wl_surface().id()).entered();
|
||||
|
||||
with_states(self.toplevel().wl_surface(), |states| {
|
||||
let attributes = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
with_toplevel_role(self.toplevel(), |attributes| {
|
||||
if let Some(server_pending) = &attributes.server_pending {
|
||||
let current_server = attributes.current_server_state();
|
||||
if server_pending != current_server {
|
||||
@@ -719,10 +712,11 @@ impl LayoutElement for Mapped {
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> bool {
|
||||
self.toplevel()
|
||||
.current_state()
|
||||
.states
|
||||
.contains(xdg_toplevel::State::Fullscreen)
|
||||
with_toplevel_role(self.toplevel(), |role| {
|
||||
role.current
|
||||
.states
|
||||
.contains(xdg_toplevel::State::Fullscreen)
|
||||
})
|
||||
}
|
||||
|
||||
fn is_pending_fullscreen(&self) -> bool {
|
||||
|
||||
+4
-14
@@ -1,11 +1,9 @@
|
||||
use niri_config::{BlockOutFrom, BorderRule, CornerRadius, Match, WindowRule};
|
||||
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||
use smithay::wayland::compositor::with_states;
|
||||
use smithay::wayland::shell::xdg::{
|
||||
ToplevelSurface, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
use smithay::wayland::shell::xdg::{ToplevelSurface, XdgToplevelSurfaceRoleAttributes};
|
||||
|
||||
use crate::layout::workspace::ColumnWidth;
|
||||
use crate::utils::with_toplevel_role;
|
||||
|
||||
pub mod mapped;
|
||||
pub use mapped::Mapped;
|
||||
@@ -144,15 +142,7 @@ impl ResolvedWindowRules {
|
||||
|
||||
let mut resolved = ResolvedWindowRules::empty();
|
||||
|
||||
let toplevel = window.toplevel();
|
||||
with_states(toplevel.wl_surface(), |states| {
|
||||
let mut role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
with_toplevel_role(window.toplevel(), |role| {
|
||||
// Ensure server_pending like in Smithay's with_pending_state().
|
||||
if role.server_pending.is_none() {
|
||||
role.server_pending = Some(role.current_server_state().clone());
|
||||
@@ -169,7 +159,7 @@ impl ResolvedWindowRules {
|
||||
}
|
||||
}
|
||||
|
||||
window_matches(window, &role, m)
|
||||
window_matches(window, role, m)
|
||||
};
|
||||
|
||||
if !(rule.matches.is_empty() || rule.matches.iter().any(matches)) {
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
### VSCode
|
||||
|
||||
There seems to be a bug in VSCode's Wayland backend until 1.86.0 which causes the window to not show up when using server-side decorations. So, to run VSCode:
|
||||
|
||||
1. Make sure VSCode is 1.86.0 or above, or that `prefer-no-csd` is **not set** in the niri config
|
||||
2. Run `code --ozone-platform-hint=auto --enable-features=WaylandWindowDecorations`
|
||||
|
||||
Also, if you're having issues with some VSCode hotkeys, try starting `Xwayland` and setting the `DISPLAY=:0` environment variable for VSCode. That is, still running VSCode with the Wayland backend, but with `DISPLAY` set to a running Xwayland instance. Apparently, VSCode currently unconditionally queries the X server for a keymap.
|
||||
|
||||
### Chromium
|
||||
|
||||
When creating new windows within Chromium (e.g. with <kbd>Ctrl</kbd><kbd>N</kbd>), there's a Chromium bug with sizing:
|
||||
|
||||
- With CSD (`prefer-no-csd` unset), the window will be a bit smaller than needed
|
||||
- With SSD (`prefer-no-csd` set), the window buffer will be offset to the top-left
|
||||
|
||||
Both of these can be fixed by resizing the new Chromium window.
|
||||
If you're having issues with some VSCode hotkeys, try starting `Xwayland` and setting the `DISPLAY=:0` environment variable for VSCode.
|
||||
That is, still running VSCode with the Wayland backend, but with `DISPLAY` set to a running Xwayland instance.
|
||||
Apparently, VSCode currently unconditionally queries the X server for a keymap.
|
||||
|
||||
### WezTerm
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ debug {
|
||||
emulate-zero-presentation-time
|
||||
disable-resize-throttling
|
||||
disable-transactions
|
||||
keep-laptop-panel-on-when-lid-is-closed
|
||||
disable-monitor-names
|
||||
}
|
||||
|
||||
binds {
|
||||
@@ -165,6 +167,33 @@ debug {
|
||||
}
|
||||
```
|
||||
|
||||
### `keep-laptop-panel-on-when-lid-is-closed`
|
||||
|
||||
<sup>Since: 0.1.10</sup>
|
||||
|
||||
By default, niri will disable the internal laptop monitor when the laptop lid is closed.
|
||||
This flag turns off this behavior and will leave the internal laptop monitor on.
|
||||
|
||||
```kdl
|
||||
debug {
|
||||
keep-laptop-panel-on-when-lid-is-closed
|
||||
}
|
||||
```
|
||||
|
||||
### `disable-monitor-names`
|
||||
|
||||
<sup>Since: 0.1.10</sup>
|
||||
|
||||
Disables the make/model/serial monitor names, as if niri fails to read them from the EDID.
|
||||
|
||||
Use this flag to work around a crash present in 0.1.9 and 0.1.10 when connecting two monitors with matching make/model/serial.
|
||||
|
||||
```kdl
|
||||
debug {
|
||||
disable-monitor-names
|
||||
}
|
||||
```
|
||||
|
||||
### Key Bindings
|
||||
|
||||
These are not debug options, but rather key bindings.
|
||||
|
||||
@@ -32,7 +32,9 @@ input {
|
||||
natural-scroll
|
||||
// accel-speed 0.2
|
||||
// accel-profile "flat"
|
||||
// scroll-factor 1.0
|
||||
// scroll-method "two-finger"
|
||||
// scroll-button 273
|
||||
// tap-button-map "left-middle-right"
|
||||
// click-method "clickfinger"
|
||||
// left-handed
|
||||
@@ -45,7 +47,9 @@ input {
|
||||
// natural-scroll
|
||||
// accel-speed 0.2
|
||||
// accel-profile "flat"
|
||||
// scroll-factor 1.0
|
||||
// scroll-method "no-scroll"
|
||||
// scroll-button 273
|
||||
// left-handed
|
||||
// middle-emulation
|
||||
}
|
||||
@@ -60,6 +64,17 @@ input {
|
||||
// middle-emulation
|
||||
}
|
||||
|
||||
trackball {
|
||||
// off
|
||||
// natural-scroll
|
||||
// accel-speed 0.2
|
||||
// accel-profile "flat"
|
||||
// scroll-method "on-button-down"
|
||||
// scroll-button 273
|
||||
// left-handed
|
||||
// middle-emulation
|
||||
}
|
||||
|
||||
tablet {
|
||||
// off
|
||||
map-to-output "eDP-1"
|
||||
@@ -142,7 +157,7 @@ A few settings are common between `touchpad`, `mouse`, `trackpoint`, and `trackb
|
||||
- `accel-profile`: can be `adaptive` (the default) or `flat` (disables pointer acceleration).
|
||||
- `scroll-method`: when to generate scroll events instead of pointer motion events, can be `no-scroll`, `two-finger`, `edge`, or `on-button-down`.
|
||||
The default and supported methods vary depending on the device type.
|
||||
- `scroll-button`: the button code used for the `on-button-down` scroll method. You can find it in `libinput debug-events`.
|
||||
- `scroll-button`: <sup>Since: 0.1.10</sup> the button code used for the `on-button-down` scroll method. You can find it in `libinput debug-events`.
|
||||
- `middle-emulation`: emulate a middle mouse click by pressing left and right mouse buttons at once.
|
||||
|
||||
Settings specific to `touchpad`s:
|
||||
@@ -154,6 +169,10 @@ Settings specific to `touchpad`s:
|
||||
- `click-method`: can be `button-areas` or `clickfinger`, changes the [click method](https://wayland.freedesktop.org/libinput/doc/latest/clickpad-softbuttons.html).
|
||||
- `disabled-on-external-mouse`: do not send events while external pointer device is plugged in.
|
||||
|
||||
Settings specific to `touchpad` and `mouse`:
|
||||
|
||||
- `scroll-factor`: <sup>Since: 0.1.10</sup> scales the scrolling speed by this value.
|
||||
|
||||
Settings specific to `touchpad`, `mouse` and `tablet`:
|
||||
|
||||
- `left-handed`: if set, changes the device to left-handed mode.
|
||||
|
||||
@@ -45,6 +45,7 @@ layout {
|
||||
insert-hint {
|
||||
// off
|
||||
color "#ffc87f80"
|
||||
// gradient from="#ffbb6680" to="#ffc88080" angle=45 relative-to="workspace-view"
|
||||
}
|
||||
|
||||
struts {
|
||||
@@ -316,13 +317,14 @@ Settings for the window insert position hint during an interactive window move.
|
||||
|
||||
`off` disables the insert hint altogether.
|
||||
|
||||
`color` lets you change the color of the hint and has the same syntax as colors in border and focus ring.
|
||||
`color` and `gradient` let you change the color of the hint and have the same syntax as colors and gradients in border and focus ring.
|
||||
|
||||
```kdl
|
||||
layout {
|
||||
insert-hint {
|
||||
// off
|
||||
color "#ffc87f80"
|
||||
gradient from="#ffbb6680" to="#ffc88080" angle=45 relative-to="workspace-view"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ cursor {
|
||||
xcursor-theme "breeze_cursors"
|
||||
xcursor-size 48
|
||||
|
||||
hide-on-key-press
|
||||
hide-when-typing
|
||||
hide-after-inactive-ms 1000
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ cursor {
|
||||
}
|
||||
```
|
||||
|
||||
#### `hide-on-key-press`
|
||||
#### `hide-when-typing`
|
||||
|
||||
<sup>Since: 0.1.10</sup>
|
||||
|
||||
@@ -117,7 +117,7 @@ If set, hides the cursor when pressing a key on the keyboard.
|
||||
|
||||
```kdl
|
||||
cursor {
|
||||
hide-on-key-press
|
||||
hide-when-typing
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
### Overview
|
||||
|
||||
<sup>Since: 0.1.10</sup>
|
||||
|
||||
Switch event bindings are declared in the `switch-events {}` section of the config.
|
||||
|
||||
Here are all the events that you can bind at a glance:
|
||||
|
||||
```kdl
|
||||
switch-events {
|
||||
lid-close { spawn "bash" "-c" "niri msg output \"eDP-1\" off"; }
|
||||
lid-open { spawn "bash" "-c" "niri msg output \"eDP-1\" on"; }
|
||||
lid-close { spawn "notify-send" "The laptop lid is closed!"; }
|
||||
lid-open { spawn "notify-send" "The laptop lid is open!"; }
|
||||
tablet-mode-on { spawn "bash" "-c" "gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true"; }
|
||||
tablet-mode-off { spawn "bash" "-c" "gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false"; }
|
||||
}
|
||||
```
|
||||
|
||||
The syntax is similar to key bindings.
|
||||
Currently only the `spawn` action are supported.
|
||||
Currently, only the `spawn` action are supported.
|
||||
|
||||
> [!NOTE]
|
||||
> In contrast to key bindings, switch event bindings are *always* executed, even when the session is locked.
|
||||
@@ -23,12 +25,12 @@ Currently only the `spawn` action are supported.
|
||||
|
||||
These events correspond to closing and opening of the laptop lid.
|
||||
|
||||
You could use them to turn the laptop internal monitor off and on (until niri gets this functionality built-in).
|
||||
Note that niri will already automatically turn the internal laptop monitor on and off in accordance with the laptop lid.
|
||||
|
||||
```kdl
|
||||
switch-events {
|
||||
lid-close { spawn "bash" "-c" "niri msg output \"eDP-1\" off"; }
|
||||
lid-open { spawn "bash" "-c" "niri msg output \"eDP-1\" on"; }
|
||||
lid-close { spawn "notify-send" "The laptop lid is closed!"; }
|
||||
lid-open { spawn "notify-send" "The laptop lid is open!"; }
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -72,3 +72,5 @@ pub fn some_function() {
|
||||
// Code of the function.
|
||||
}
|
||||
```
|
||||
|
||||
You can also enable Rust memory allocation profiling with `--features=profile-with-tracy-allocations`.
|
||||
|
||||
@@ -46,3 +46,10 @@ hotkey-overlay {
|
||||
skip-at-startup
|
||||
}
|
||||
```
|
||||
|
||||
### How to run X11 apps like Steam or Discord?
|
||||
|
||||
To run X11 apps, you can use [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite).
|
||||
Check [the Xwayland wiki page](./Xwayland.md) for instructions.
|
||||
|
||||
Keep in mind that you can run many Electron apps such as VSCode natively on Wayland by passing the right flags, e.g. `code --ozone-platform-hint=auto`
|
||||
|
||||
@@ -10,6 +10,8 @@ There are several gestures in niri.
|
||||
|
||||
You can move windows by holding <kbd>Mod</kbd> and the left mouse button.
|
||||
|
||||
You can customize the look of the window insertion preview in the `insert-hint` [layout config](./Configuration:-Layout.md) section.
|
||||
|
||||
#### Interactive Resize
|
||||
|
||||
<sup>Since: 0.1.6</sup>
|
||||
|
||||
@@ -10,6 +10,7 @@ To exit niri, press <kbd>Super</kbd><kbd>Shift</kbd><kbd>E</kbd>.
|
||||
|
||||
If you're not using a display manager, you should run `niri-session` (systemd/dinit) or `niri --session` (others) from a TTY.
|
||||
The `--session` flag will make niri import its environment variables globally into the system manager and D-Bus, and start its D-Bus services.
|
||||
The `niri-session` script will additionally start niri as a systemd/dinit service, which starts up a graphical session target required by some services like portals.
|
||||
|
||||
You can also run `niri` inside an existing desktop session.
|
||||
Then it will open as a window, where you can give it a try.
|
||||
@@ -22,21 +23,13 @@ Finally, the [Xwayland](./Xwayland.md) page explains how to run X11 applications
|
||||
|
||||
### NVIDIA
|
||||
|
||||
NVIDIA GPUs tend to have problems running niri (for example, the screen remains black upon starting from a TTY).
|
||||
NVIDIA GPUs can have problems running niri (for example, the screen remains black upon starting from a TTY).
|
||||
Sometimes, the problems can be fixed.
|
||||
You can try the following:
|
||||
|
||||
1. Update NVIDIA drivers. You need a GPU and drivers recent enough to support GBM.
|
||||
2. Make sure kernel modesetting is enabled. This usually involves adding `nvidia-drm.modeset=1` to the kernel command line. Find and follow a guide for your distribution. Guides from other Wayland compositors can help.
|
||||
|
||||
If niri runs but the screen flickers, try adding this into your niri config:
|
||||
|
||||
```
|
||||
debug {
|
||||
wait-for-frame-completion-before-queueing
|
||||
}
|
||||
```
|
||||
|
||||
### Asahi, ARM, and other kmsro devices
|
||||
|
||||
On some of these systems, niri fails to correctly detect the primary render device.
|
||||
@@ -69,6 +62,7 @@ If you still get a black screen, try using each of the `card` devices.
|
||||
### Nix/NixOS
|
||||
|
||||
There's a common problem of mesa drivers going out of sync with niri, so make sure your system mesa version matches the niri mesa version.
|
||||
When this happens, you usually see a black screen when trying to start niri from a TTY.
|
||||
|
||||
Also, on Intel graphics, you may need a workaround described [here](https://nixos.wiki/wiki/Intel_Graphics).
|
||||
|
||||
|
||||
@@ -33,3 +33,10 @@ systemctl --user edit --full plasma-polkit-agent.service
|
||||
```
|
||||
|
||||
Then add `After=graphical-session.target`.
|
||||
|
||||
### Xwayland
|
||||
|
||||
To run X11 apps like Steam or Discord, you can use [xwayland-satellite].
|
||||
Check [the Xwayland wiki page](./Xwayland.md) for instructions.
|
||||
|
||||
[xwayland-satellite]: https://github.com/Supreeeme/xwayland-satellite
|
||||
|
||||
Reference in New Issue
Block a user