Compare commits

..

1 Commits

Author SHA1 Message Date
Ivan Molodetskikh 012340c5f4 Freeze view when pointer or touch is grabbed 2024-10-28 21:18:26 +03:00
41 changed files with 545 additions and 1112 deletions
Generated
+90 -324
View File
@@ -133,9 +133,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.93"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
[[package]]
name = "appendlist"
@@ -203,7 +203,7 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand 2.2.0",
"fastrand 2.1.1",
"futures-lite 2.3.0",
"slab",
]
@@ -304,7 +304,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -339,7 +339,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -411,7 +411,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -501,7 +501,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -699,7 +699,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -934,17 +934,6 @@ 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"
@@ -1036,7 +1025,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -1104,9 +1093,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.2.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fdeflate"
@@ -1161,7 +1150,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -1248,7 +1237,7 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
dependencies = [
"fastrand 2.2.0",
"fastrand 2.1.1",
"futures-core",
"futures-io",
"parking",
@@ -1263,7 +1252,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -1474,7 +1463,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -1490,9 +1479,9 @@ dependencies = [
[[package]]
name = "glam"
version = "0.29.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a"
[[package]]
name = "glib"
@@ -1525,7 +1514,7 @@ dependencies = [
"proc-macro-crate 3.2.0",
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -1611,9 +1600,9 @@ dependencies = [
[[package]]
name = "gtk4"
version = "0.9.3"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34465497f5a4c182c9c94a582a187db7d6af0863f28e87ccf4379f21f0e2a22"
checksum = "31e2d105ce672f5cdcb5af2602e91c2901e91c72da15ab76f613ad57ecf04c6d"
dependencies = [
"cairo-rs",
"field-offset",
@@ -1639,7 +1628,7 @@ dependencies = [
"proc-macro-crate 3.2.0",
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -1710,143 +1699,14 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "icu_collections"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
dependencies = [
"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"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
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",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
@@ -1995,7 +1855,7 @@ dependencies = [
"libc",
"proc-macro2",
"regex",
"syn 2.0.86",
"syn 2.0.85",
"terminal_size 0.2.6",
]
@@ -2055,9 +1915,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libadwaita"
version = "0.7.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8611ee9fb85e7606c362b513afcaf5b59853f79e4d98caaaf581d99465014247"
checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878"
dependencies = [
"gdk4",
"gio",
@@ -2086,9 +1946,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.162"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "libdisplay-info"
@@ -2111,7 +1971,7 @@ checksum = "ea1cd31036b732a546d845f9485c56b1b606b5e476b0821c680dd66c8cd6fcee"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -2127,7 +1987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -2221,12 +2081,6 @@ 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"
@@ -2339,7 +2193,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -2390,7 +2244,7 @@ dependencies = [
[[package]]
name = "niri"
version = "0.1.10"
version = "0.1.9"
dependencies = [
"anyhow",
"approx 0.5.1",
@@ -2404,7 +2258,7 @@ dependencies = [
"clap",
"directories",
"drm-ffi",
"fastrand 2.2.0",
"fastrand 2.1.1",
"futures-util",
"git-version",
"glam",
@@ -2444,7 +2298,7 @@ dependencies = [
[[package]]
name = "niri-config"
version = "0.1.10"
version = "0.1.9"
dependencies = [
"bitflags 2.6.0",
"csscolorparser",
@@ -2461,7 +2315,7 @@ dependencies = [
[[package]]
name = "niri-ipc"
version = "0.1.10"
version = "0.1.9"
dependencies = [
"clap",
"schemars",
@@ -2471,7 +2325,7 @@ dependencies = [
[[package]]
name = "niri-visual-tests"
version = "0.1.10"
version = "0.1.9"
dependencies = [
"anyhow",
"gtk4",
@@ -2570,10 +2424,10 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro-crate 3.2.0",
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -2840,9 +2694,9 @@ dependencies = [
[[package]]
name = "ordered-float"
version = "4.5.0"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e"
checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97"
dependencies = [
"num-traits",
]
@@ -2967,7 +2821,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -2996,7 +2850,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3018,7 +2872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
dependencies = [
"atomic-waker",
"fastrand 2.2.0",
"fastrand 2.1.1",
"futures-io",
]
@@ -3216,7 +3070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
dependencies = [
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3247,7 +3101,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3503,7 +3357,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3541,7 +3395,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3552,7 +3406,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3575,7 +3429,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3658,7 +3512,7 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]]
name = "smithay"
version = "0.3.0"
source = "git+https://github.com/Smithay/smithay.git#5e137dcebc9f2de4d026180dfc4ce81282f7f14f"
source = "git+https://github.com/Smithay/smithay.git#521344d21e6c9140ad7884ebf4d7e6b98d979854"
dependencies = [
"appendlist",
"bitflags 2.6.0",
@@ -3730,7 +3584,7 @@ dependencies = [
[[package]]
name = "smithay-drm-extras"
version = "0.1.0"
source = "git+https://github.com/Smithay/smithay.git#5e137dcebc9f2de4d026180dfc4ce81282f7f14f"
source = "git+https://github.com/Smithay/smithay.git#521344d21e6c9140ad7884ebf4d7e6b98d979854"
dependencies = [
"drm",
"libdisplay-info",
@@ -3755,12 +3609,6 @@ 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"
@@ -3814,26 +3662,15 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.86"
version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
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"
@@ -3883,7 +3720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [
"cfg-if",
"fastrand 2.2.0",
"fastrand 2.1.1",
"once_cell",
"rustix 0.38.38",
"windows-sys 0.59.0",
@@ -3937,7 +3774,7 @@ checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -3970,15 +3807,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "tinystr"
version = "0.7.6"
name = "tinyvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
dependencies = [
"displaydoc",
"zerovec",
"tinyvec_macros",
]
[[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"
@@ -4044,7 +3886,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -4141,6 +3983,12 @@ 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"
@@ -4153,6 +4001,15 @@ 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"
@@ -4167,27 +4024,15 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "url"
version = "2.5.3"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
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"
@@ -4265,7 +4110,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
"wasm-bindgen-shared",
]
@@ -4299,7 +4144,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -4561,7 +4406,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -4572,7 +4417,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
"syn 2.0.85",
]
[[package]]
@@ -4877,18 +4722,6 @@ 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"
@@ -5003,30 +4836,6 @@ 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"
@@ -5111,50 +4920,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"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",
"syn 2.0.85",
]
[[package]]
+11 -13
View File
@@ -2,7 +2,7 @@
members = ["niri-visual-tests"]
[workspace.package]
version = "0.1.10"
version = "0.1.9"
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.93"
anyhow = "1.0.90"
bitflags = "2.6.0"
clap = { version = "4.5.20", features = ["derive"] }
k9 = "0.12.0"
serde = { version = "1.0.214", features = ["derive"] }
serde = { version = "1.0.210", 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.2.0"
fastrand = "2.1.1"
futures-util = { version = "0.3.31", default-features = false, features = ["std", "io"] }
git-version = "0.3.9"
glam = "0.29.2"
glam = "0.29.0"
input = { version = "0.9.1", features = ["libinput_1_21"] }
keyframe = { version = "1.1.1", default-features = false }
libc = "0.2.162"
libc = "0.2.161"
libdisplay-info = "0.1.0"
log = { version = "0.4.22", features = ["max_level_trace", "release_max_level_debug"] }
niri-config = { version = "0.1.10", path = "niri-config" }
niri-ipc = { version = "0.1.10", path = "niri-ipc", features = ["clap"] }
niri-config = { version = "0.1.9", path = "niri-config" }
niri-ipc = { version = "0.1.9", path = "niri-ipc", features = ["clap"] }
notify-rust = { version = "~4.10.0", optional = true }
ordered-float = "4.5.0"
ordered-float = "4.4.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.3", optional = true }
url = { version = "2.5.2", optional = true }
wayland-backend = "0.3.7"
wayland-scanner = "0.31.5"
xcursor = "0.3.8"
@@ -124,8 +124,6 @@ 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 = []
@@ -139,7 +137,7 @@ lto = "thin"
debug = false
[package.metadata.generate-rpm]
version = "0.1.10"
version = "0.1.9"
assets = [
{ source = "target/release/niri", dest = "/usr/bin/", mode = "755" },
{ source = "resources/niri-session", dest = "/usr/bin/", mode = "755" },
+4 -4
View File
@@ -32,7 +32,7 @@
libclang,
libdisplay-info,
libinput,
seatd,
libseat,
libxkbcommon,
mesa,
pango,
@@ -90,7 +90,7 @@
libGL
libdisplay-info
libinput
seatd
libseat
libxkbcommon
mesa # libgbm
pango
@@ -123,7 +123,7 @@
# Force linking with libEGL and libwayland-client
# so they can be discovered by `dlopen()`
RUSTFLAGS = toString (
CARGO_BUILD_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"`
CARGO_BUILD_RUSTFLAGS = niri.RUSTFLAGS;
inherit (niri) CARGO_BUILD_RUSTFLAGS;
};
};
}
+2 -2
View File
@@ -12,8 +12,8 @@ bitflags.workspace = true
csscolorparser = "0.7.0"
knuffel = "3.2.0"
miette = "5.10.0"
niri-ipc = { version = "0.1.10", path = "../niri-ipc" }
regex = "1.11.1"
niri-ipc = { version = "0.1.9", path = "../niri-ipc" }
regex = "1.11.0"
smithay = { workspace = true, features = ["backend_libinput"] }
tracing.workspace = true
tracy-client.workspace = true
+8 -38
View File
@@ -188,8 +188,6 @@ 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)]
@@ -210,8 +208,6 @@ 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)]
@@ -601,8 +597,6 @@ 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 {
@@ -610,7 +604,6 @@ impl Default for InsertHint {
Self {
off: false,
color: Color::from_rgba8_unpremul(127, 200, 255, 128),
gradient: None,
}
}
}
@@ -673,7 +666,7 @@ pub struct Cursor {
#[knuffel(child, unwrap(argument), default = 24)]
pub xcursor_size: u8,
#[knuffel(child)]
pub hide_when_typing: bool,
pub hide_on_key_press: bool,
#[knuffel(child, unwrap(argument))]
pub hide_after_inactive_ms: Option<u32>,
}
@@ -683,7 +676,7 @@ impl Default for Cursor {
Self {
xcursor_theme: String::from("default"),
xcursor_size: 24,
hide_when_typing: false,
hide_on_key_press: false,
hide_after_inactive_ms: None,
}
}
@@ -1533,10 +1526,6 @@ 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)]
@@ -1918,17 +1907,13 @@ impl OutputName {
if self.make.is_none() && self.model.is_none() && self.serial.is_none() {
self.connector.to_string()
} else {
self.format_make_model_serial()
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 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) {
@@ -2970,7 +2955,6 @@ mod tests {
scroll-button 272
tap-button-map "left-middle-right"
disabled-on-external-mouse
scroll-factor 0.9
}
mouse {
@@ -2980,7 +2964,6 @@ mod tests {
scroll-method "no-scroll"
scroll-button 273
middle-emulation
scroll-factor 0.2
}
trackpoint {
@@ -3068,7 +3051,6 @@ mod tests {
insert-hint {
color "rgb(255, 200, 127)"
gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view"
}
}
@@ -3079,7 +3061,7 @@ mod tests {
cursor {
xcursor-theme "breeze_cursors"
xcursor-size 16
hide-when-typing
hide-on-key-press
hide-after-inactive-ms 3000
}
@@ -3183,7 +3165,6 @@ mod tests {
left_handed: false,
disabled_on_external_mouse: true,
middle_emulation: false,
scroll_factor: FloatOrInt(0.9),
},
mouse: Mouse {
off: false,
@@ -3194,7 +3175,6 @@ mod tests {
scroll_button: Some(273),
left_handed: false,
middle_emulation: true,
scroll_factor: FloatOrInt(0.2),
},
trackpoint: Trackpoint {
off: true,
@@ -3273,16 +3253,6 @@ 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),
@@ -3316,7 +3286,7 @@ mod tests {
cursor: Cursor {
xcursor_theme: String::from("breeze_cursors"),
xcursor_size: 16,
hide_when_typing: true,
hide_on_key_press: true,
hide_after_inactive_ms: Some(3000),
},
screenshot_path: Some(String::from("~/Screenshots/screenshot.png")),
+1 -5
View File
@@ -1,16 +1,12 @@
[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 }
-16
View File
@@ -1,16 +0,0 @@
# 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"
```
-14
View File
@@ -19,20 +19,6 @@
//!
//! 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;
+4 -4
View File
@@ -8,11 +8,11 @@ edition.workspace = true
repository.workspace = true
[dependencies]
adw = { version = "0.7.1", package = "libadwaita", features = ["v1_4"] }
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_4"] }
anyhow.workspace = true
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" }
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" }
smithay.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
-1
View File
@@ -486,7 +486,6 @@ 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
View File
@@ -1,4 +1,3 @@
[preferred]
default=gnome;gtk;
org.freedesktop.impl.portal.Access=gtk;
org.freedesktop.impl.portal.Secret=gnome-keyring;
+28 -92
View File
@@ -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, is_laptop_panel, logical_output};
use crate::utils::{get_monotonic_time, logical_output};
const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888];
@@ -636,31 +636,22 @@ impl Tty {
connector,
crtc: Some(crtc),
} => {
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());
if let Err(err) = self.connector_connected(niri, node, connector, crtc) {
warn!("error connecting connector: {err:?}");
}
}
DrmScanEvent::Disconnected {
crtc: Some(crtc), ..
} => {
self.connector_disconnected(niri, node, crtc);
removed.push(crtc);
}
_ => (),
}
}
for crtc in &removed {
self.connector_disconnected(niri, node, *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.
let Some(device) = self.devices.get_mut(&node) else {
error!("device disappeared");
return;
@@ -672,12 +663,7 @@ impl Tty {
}
}
// 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);
self.refresh_ipc_outputs(niri);
}
fn device_removed(&mut self, device_id: dev_t, niri: &mut Niri) {
@@ -763,12 +749,7 @@ 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(),
self.config.borrow().debug.disable_monitor_names,
);
let output_name = make_output_name(&device.drm, connector.handle(), connector_name.clone());
let non_desktop = find_drm_property(&device.drm, connector.handle(), "non-desktop")
.and_then(|(_, info, value)| info.value_type().convert_value(value).as_boolean())
@@ -786,6 +767,10 @@ 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()
@@ -794,6 +779,11 @@ 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:?}");
}
@@ -1574,12 +1564,8 @@ 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(),
self.config.borrow().debug.disable_monitor_names,
);
let output_name =
make_output_name(&device.drm, connector.handle(), connector_name.clone());
let surface = device.surfaces.get(&crtc);
let current_crtc_mode = surface.map(|surface| surface.compositor.pending_mode());
@@ -1725,24 +1711,6 @@ 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![];
@@ -1755,7 +1723,7 @@ impl Tty {
.find(&surface.name)
.cloned()
.unwrap_or_default();
if config.off || should_disable(&surface.name.connector) {
if config.off {
to_disconnect.push((node, crtc));
continue;
}
@@ -1851,21 +1819,12 @@ impl Tty {
}
// Check if already enabled.
if device.surfaces.contains_key(&crtc)
|| device
.non_desktop_connectors
.contains(&(connector.handle(), crtc))
{
if device.surfaces.contains_key(&crtc) {
continue;
}
let connector_name = format_connector_name(connector);
let output_name = make_output_name(
&device.drm,
connector.handle(),
connector_name,
self.config.borrow().debug.disable_monitor_names,
);
let output_name = make_output_name(&device.drm, connector.handle(), connector_name);
let config = self
.config
.borrow()
@@ -1874,8 +1833,8 @@ impl Tty {
.cloned()
.unwrap_or_default();
if !(config.off || should_disable(&output_name.connector)) {
to_connect.push((node, connector.clone(), crtc, output_name));
if !config.off {
to_connect.push((node, connector.clone(), crtc));
}
}
}
@@ -1884,11 +1843,7 @@ impl Tty {
self.connector_disconnected(niri, node, crtc);
}
// 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 {
for (node, connector, crtc) in to_connect {
if let Err(err) = self.connector_connected(niri, node, connector, crtc) {
warn!("error connecting connector: {err:?}");
}
@@ -1923,21 +1878,12 @@ impl Tty {
}
// Check if already enabled.
if device.surfaces.contains_key(&crtc)
|| device
.non_desktop_connectors
.contains(&(connector.handle(), crtc))
{
if device.surfaces.contains_key(&crtc) {
continue;
}
let connector_name = format_connector_name(connector);
let output_name = make_output_name(
&device.drm,
connector.handle(),
connector_name,
self.config.borrow().debug.disable_monitor_names,
);
let output_name = make_output_name(&device.drm, connector.handle(), connector_name);
if output_name.matches(target) {
return Some(output_name);
}
@@ -2535,17 +2481,7 @@ 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();
+1 -2
View File
@@ -8,7 +8,6 @@ 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>>,
@@ -64,7 +63,7 @@ impl DisplayConfig {
.map(|output| {
// Loosely matches the check in Mutter.
let c = &output.name;
let is_laptop_panel = is_laptop_panel(c);
let is_laptop_panel = matches!(c.get(..4), Some("eDP-" | "LVDS" | "DSI-"));
let display_name = make_display_name(output, is_laptop_panel);
let mut properties = HashMap::new();
+23 -30
View File
@@ -7,7 +7,6 @@ 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;
@@ -75,14 +74,12 @@ 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, with_toplevel_role};
use crate::utils::{output_size, send_scale_transform};
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;
@@ -140,12 +137,11 @@ impl TabletSeatHandler for State {
delegate_tablet_manager!(State);
impl PointerConstraintsHandler for State {
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 new_constraint(&mut self, _surface: &WlSurface, pointer: &PointerHandle<Self>) {
self.niri.maybe_activate_pointer_constraint(
pointer.current_location(),
&self.niri.pointer_focus,
);
}
fn cursor_position_hint(
@@ -162,20 +158,19 @@ impl PointerConstraintsHandler for State {
return;
}
// 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.
// 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.
//
// 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 {
// 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 {
return;
};
if surface_under_pointer != surface {
if focused_surface != surface {
return;
}
@@ -464,12 +459,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)
{
let has_fullscreen_cap = with_toplevel_role(mapped.toplevel(), |role| {
role.current
.capabilities
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
});
if !has_fullscreen_cap {
if !mapped
.toplevel()
.current_state()
.capabilities
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
{
return;
}
@@ -627,18 +622,16 @@ impl XdgActivationHandler for State {
fn request_activation(
&mut self,
token: XdgActivationToken,
_token: XdgActivationToken,
token_data: XdgActivationTokenData,
surface: WlSurface,
) {
if token_data.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT {
if token_data.timestamp.elapsed().as_secs() < 10 {
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);
}
}
}
+5 -2
View File
@@ -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().is::<DnDGrab<Self>>();
let is_dnd_grab = grab.as_any().downcast_ref::<DnDGrab<Self>>().is_some();
if !is_dnd_grab {
grab_start_data =
@@ -103,7 +103,8 @@ 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().is::<DnDGrab<Self>>();
let is_dnd_grab =
grab.as_any().downcast_ref::<DnDGrab<Self>>().is_some();
if !is_dnd_grab {
grab_start_data =
@@ -148,6 +149,7 @@ 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();
@@ -245,6 +247,7 @@ 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();
+40 -56
View File
@@ -423,7 +423,7 @@ impl State {
}
fn hide_cursor_if_needed(&mut self) {
if !self.niri.config.borrow().cursor.hide_when_typing {
if !self.niri.config.borrow().cursor.hide_on_key_press {
return;
}
@@ -1320,17 +1320,12 @@ 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(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;
if let Some(focus) = &self.niri.pointer_focus.surface {
let pos_within_surface = pos - focus.1;
let mut pointer_locked = false;
with_pointer_constraint(&under.0, &pointer, |constraint| {
with_pointer_constraint(&focus.0, &pointer, |constraint| {
let Some(constraint) = constraint else { return };
if !constraint.is_active() {
return;
@@ -1348,7 +1343,7 @@ impl State {
pointer_locked = true;
}
PointerConstraint::Confined(confine) => {
pointer_confined = Some((under.clone(), confine.region().cloned()));
pointer_confined = Some((focus.clone(), confine.region().cloned()));
}
}
});
@@ -1357,7 +1352,7 @@ impl State {
if pointer_locked {
pointer.relative_motion(
self,
Some(under.clone()),
Some(focus.clone()),
&RelativeMotionEvent {
delta: event.delta(),
delta_unaccel: event.delta_unaccel(),
@@ -1415,7 +1410,7 @@ impl State {
self.niri.screenshot_ui.pointer_motion(point);
}
let under = self.niri.contents_under(new_pos);
let under = self.niri.surface_under_and_global_space(new_pos);
// Handle confined pointer.
if let Some((focus_surface, region)) = pointer_confined {
@@ -1453,7 +1448,10 @@ impl State {
self.niri.handle_focus_follows_mouse(&under);
self.niri.pointer_contents.clone_from(&under);
// Activate a new confinement if necessary.
self.niri.maybe_activate_pointer_constraint(new_pos, &under);
self.niri.pointer_focus.clone_from(&under);
pointer.motion(
self,
@@ -1477,9 +1475,6 @@ 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();
@@ -1514,11 +1509,12 @@ impl State {
self.niri.screenshot_ui.pointer_motion(point);
}
let under = self.niri.contents_under(pos);
let under = self.niri.surface_under_and_global_space(pos);
self.niri.handle_focus_follows_mouse(&under);
self.niri.pointer_contents.clone_from(&under);
self.niri.maybe_activate_pointer_constraint(pos, &under);
self.niri.pointer_focus.clone_from(&under);
pointer.motion(
self,
@@ -1532,8 +1528,6 @@ impl State {
pointer.frame(self);
self.niri.maybe_activate_pointer_constraint();
// We moved the pointer, show it.
self.niri.pointer_hidden = false;
@@ -1588,6 +1582,7 @@ 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));
@@ -1653,6 +1648,7 @@ 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()),
);
@@ -1688,6 +1684,7 @@ 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));
@@ -1696,11 +1693,11 @@ impl State {
}
};
self.update_pointer_contents();
self.update_pointer_focus();
if ButtonState::Pressed == button_state {
let layer_under = self.niri.pointer_contents.layer.clone();
self.niri.focus_layer_surface_if_on_demand(layer_under);
let layer_focus = self.niri.pointer_focus.layer.clone();
self.niri.focus_layer_surface_if_on_demand(layer_focus);
}
if let Some(button) = event.button() {
@@ -1738,6 +1735,8 @@ impl State {
},
);
pointer.frame(self);
self.niri.refresh_layout_is_grabbed();
}
fn on_pointer_axis<I: InputBackend>(&mut self, event: I::PointerAxisEvent) {
@@ -1881,23 +1880,14 @@ 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 {
@@ -1926,7 +1916,7 @@ impl State {
}
}
self.update_pointer_contents();
self.update_pointer_focus();
let pointer = &self.niri.seat.get_pointer().unwrap();
pointer.axis(self, frame);
@@ -1941,7 +1931,7 @@ impl State {
return;
};
let under = self.niri.contents_under(pos);
let under = self.niri.surface_under_and_global_space(pos);
let tablet_seat = self.niri.seat.tablet_seat();
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));
@@ -1993,7 +1983,7 @@ impl State {
tool.tip_down(serial, event.time_msec());
if let Some(pos) = self.niri.tablet_cursor_location {
let under = self.niri.contents_under(pos);
let under = self.niri.surface_under_and_global_space(pos);
if let Some(window) = under.window {
self.niri.layout.activate_window(&window);
@@ -2023,7 +2013,7 @@ impl State {
return;
};
let under = self.niri.contents_under(pos);
let under = self.niri.surface_under_and_global_space(pos);
let tablet_seat = self.niri.seat.tablet_seat();
let display_handle = self.niri.display_handle.clone();
@@ -2055,7 +2045,6 @@ impl State {
self.move_cursor(pos);
}
self.niri.pointer_hidden = false;
self.niri.tablet_cursor_location = None;
}
}
@@ -2089,7 +2078,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial();
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2180,7 +2169,7 @@ impl State {
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2223,7 +2212,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial();
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2241,7 +2230,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial();
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2258,7 +2247,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_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2277,7 +2266,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial();
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2295,7 +2284,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial();
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2313,7 +2302,7 @@ impl State {
let serial = SERIAL_COUNTER.next_serial();
let pointer = self.niri.seat.get_pointer().unwrap();
if self.update_pointer_contents() {
if self.update_pointer_focus() {
pointer.frame(self);
}
@@ -2352,7 +2341,7 @@ impl State {
return;
};
let under = self.niri.contents_under(touch_location);
let under = self.niri.surface_under_and_global_space(touch_location);
if !handle.is_grabbed() {
if let Some(window) = under.window {
@@ -2383,6 +2372,8 @@ impl State {
// We're using touch, hide the pointer.
self.niri.pointer_hidden = true;
self.niri.refresh_layout_is_grabbed();
}
fn on_touch_up<I: InputBackend>(&mut self, evt: I::TouchUpEvent) {
let Some(handle) = self.niri.seat.get_touch() else {
@@ -2405,7 +2396,7 @@ impl State {
let Some(touch_location) = self.compute_touch_location(&evt) else {
return;
};
let under = self.niri.contents_under(touch_location);
let under = self.niri.surface_under_and_global_space(touch_location);
handle.motion(
self,
under.surface,
@@ -2434,13 +2425,6 @@ 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())
+2 -1
View File
@@ -34,6 +34,7 @@ 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
@@ -112,7 +113,7 @@ impl PointerGrab<State> for MoveGrab {
.output_under(handle.current_location())
.map(|(output, _)| output)
.cloned();
// FIXME: workspace switch gesture.
// TODO: workspace switch gesture.
if let Some(output) = output {
self.is_moving = true;
data.niri.layout.view_offset_gesture_begin(&output, false);
+1
View File
@@ -22,6 +22,7 @@ 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
+1
View File
@@ -50,6 +50,7 @@ impl SpatialMovementGrab {
state.niri.queue_redraw(&output);
}
state.niri.pointer_grab_ongoing = false;
state
.niri
.cursor_manager
+28 -8
View File
@@ -19,11 +19,13 @@ 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, with_toplevel_role};
use crate::utils::version;
use crate::window::Mapped;
// If an event stream client fails to read events fast enough that we accumulate more than this
@@ -359,12 +361,22 @@ async fn handle_event_stream_client(client: EventStreamClient) -> anyhow::Result
}
fn make_ipc_window(mapped: &Mapped, workspace_id: Option<WorkspaceId>) -> niri_ipc::Window {
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(),
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(),
}
})
}
@@ -547,7 +559,15 @@ impl State {
let workspace_id = ws_id.map(|id| id.get());
let mut changed = ipc_win.workspace_id != workspace_id;
changed |= with_toplevel_role(mapped.toplevel(), |role| {
let wl_surface = mapped.toplevel().wl_surface();
changed |= with_states(wl_surface, |states| {
let role = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
ipc_win.title != role.title || ipc_win.app_id != role.app_id
});
-61
View File
@@ -1,61 +0,0 @@
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)
}
}
+72 -171
View File
@@ -30,14 +30,12 @@
//! 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, CornerRadius, FloatOrInt, PresetSize, Struts,
Workspace as WorkspaceConfig,
CenterFocusedColumn, Config, FloatOrInt, PresetSize, Struts, Workspace as WorkspaceConfig,
};
use niri_ipc::SizeChange;
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
@@ -66,7 +64,6 @@ 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;
@@ -88,6 +85,59 @@ 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;
@@ -206,14 +256,6 @@ 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.
@@ -289,59 +331,6 @@ 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>,
@@ -441,7 +430,6 @@ 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),
}
@@ -459,7 +447,6 @@ 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,
}
@@ -474,9 +461,6 @@ 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![];
@@ -496,10 +480,6 @@ 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);
}
@@ -517,10 +497,6 @@ 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()));
@@ -528,10 +504,7 @@ impl<W: LayoutElement> Layout<W> {
ws.set_output(Some(output.clone()));
}
let mut monitor = Monitor::new(output, workspaces, self.options.clone());
monitor.active_workspace_idx = active_workspace_idx.unwrap_or(0);
monitors.push(monitor);
monitors.push(Monitor::new(output, workspaces, self.options.clone()));
MonitorSet::Normal {
monitors,
primary_idx,
@@ -542,19 +515,11 @@ 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()));
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() {
for workspace in &mut workspaces {
workspace.set_output(Some(output.clone()));
if Some(workspace.id()) == ws_id_to_activate {
active_workspace_idx = i;
}
}
let mut monitor = Monitor::new(output, workspaces, self.options.clone());
monitor.active_workspace_idx = active_workspace_idx;
let monitor = Monitor::new(output, workspaces, self.options.clone());
MonitorSet::Normal {
monitors: vec![monitor],
@@ -577,12 +542,6 @@ 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 {
@@ -1969,6 +1928,12 @@ impl<W: LayoutElement> Layout<W> {
mon.resize_edges_under(pos_within_output)
}
pub fn set_pointer_grabbed(&mut self, is_grabbed: bool) {
for ws in self.workspaces_mut() {
ws.set_pointer_grabbed(is_grabbed);
}
}
#[cfg(test)]
fn verify_invariants(&self) {
use std::collections::HashSet;
@@ -2299,21 +2264,13 @@ 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,
});
}
}
@@ -2961,6 +2918,13 @@ impl<W: LayoutElement> Layout<W> {
.unwrap();
tile_pos = Some(ws_offset + tile_offset);
// The view offset anim is paused because a grab is active. Since we
// just dragged a window out, cancel the animation, because when we put
// the window back, we will animate to it afresh.
let ws_id = ws.id();
let ws = self.workspaces_mut().find(|ws| ws.id() == ws_id).unwrap();
ws.cancel_view_offset_anim();
}
}
}
@@ -5723,69 +5687,6 @@ 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
View File
@@ -392,7 +392,7 @@ impl<W: LayoutElement> Tile<W> {
}
/// Returns `None` if the border is hidden and `Some(width)` if it should be shown.
pub fn effective_border_width(&self) -> Option<f64> {
fn effective_border_width(&self) -> Option<f64> {
if self.is_fullscreen {
return None;
}
+57 -27
View File
@@ -4,10 +4,11 @@ use std::rc::Rc;
use std::time::Duration;
use niri_config::{
CenterFocusedColumn, CornerRadius, OutputName, PresetSize, Struts, Workspace as WorkspaceConfig,
CenterFocusedColumn, 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;
@@ -16,13 +17,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};
@@ -110,8 +111,13 @@ pub struct Workspace<W: LayoutElement> {
/// Indication where an interactively-moved window is about to be placed.
insert_hint: Option<InsertHint>,
/// Insert hint element for rendering.
insert_hint_element: InsertHintElement,
/// Buffer for the insert hint.
insert_hint_buffer: SolidColorBuffer,
/// Whether a pointer grab is active.
///
/// This means that view offset animations should be temporarily frozen.
is_pointer_grabbed: bool,
/// Configurable properties of the layout as received from the parent monitor.
pub(super) base_options: Rc<Options>,
@@ -137,7 +143,6 @@ pub struct InsertHint {
pub position: InsertPosition,
pub width: ColumnWidth,
pub is_full_width: bool,
pub corner_radius: CornerRadius,
}
#[derive(Debug, Clone)]
@@ -173,7 +178,6 @@ niri_render_elements! {
WorkspaceRenderElement<R> => {
Tile = TileRenderElement<R>,
ClosingWindow = ClosingWindowRenderElement,
InsertHint = InsertHintRenderElement,
}
}
@@ -441,7 +445,8 @@ impl<W: LayoutElement> Workspace<W> {
view_offset_before_fullscreen: None,
closing_windows: vec![],
insert_hint: None,
insert_hint_element: InsertHintElement::new(options.insert_hint),
insert_hint_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
is_pointer_grabbed: false,
base_options,
options,
name: config.map(|c| c.name.0),
@@ -481,7 +486,8 @@ impl<W: LayoutElement> Workspace<W> {
view_offset_before_fullscreen: None,
closing_windows: vec![],
insert_hint: None,
insert_hint_element: InsertHintElement::new(options.insert_hint),
insert_hint_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
is_pointer_grabbed: false,
base_options,
options,
name: config.map(|c| c.name.0),
@@ -516,10 +522,12 @@ impl<W: LayoutElement> Workspace<W> {
pub fn advance_animations(&mut self, current_time: Duration) {
if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj {
anim.set_current_time(current_time);
self.view_offset = anim.value();
if anim.is_done() {
self.view_offset_adj = None;
if !self.is_pointer_grabbed {
anim.set_current_time(current_time);
self.view_offset = anim.value();
if anim.is_done() {
self.view_offset_adj = None;
}
}
} else if let Some(ViewOffsetAdjustment::Gesture(gesture)) = &self.view_offset_adj {
self.view_offset = gesture.current_view_offset;
@@ -563,13 +571,8 @@ impl<W: LayoutElement> Workspace<W> {
if let Some(insert_hint) = &self.insert_hint {
if let Some(area) = self.insert_hint_area(insert_hint) {
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(),
);
self.insert_hint_buffer
.update(area.size, self.options.insert_hint.color.to_array_premul());
}
}
}
@@ -583,8 +586,6 @@ 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;
}
@@ -595,8 +596,6 @@ impl<W: LayoutElement> Workspace<W> {
tile.update_shaders();
}
}
self.insert_hint_element.update_shaders();
}
pub fn windows(&self) -> impl Iterator<Item = &W> + '_ {
@@ -1709,6 +1708,33 @@ impl<W: LayoutElement> Workspace<W> {
}
}
pub fn set_pointer_grabbed(&mut self, is_grabbed: bool) {
if self.is_pointer_grabbed == is_grabbed {
return;
}
self.is_pointer_grabbed = is_grabbed;
// After ungrabbing, restart the view offset animation.
if !is_grabbed {
if let Some(ViewOffsetAdjustment::Animation(anim)) = &self.view_offset_adj {
// FIXME: preserve velocity.
let anim = anim.restarted(anim.value(), anim.to(), 0.);
self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(anim));
}
}
}
/// Stops a view offset animation if one is in progress.
///
/// The view will stop in place. When calling this, make sure that you will move the view to
/// the active window when necessary.
pub fn cancel_view_offset_anim(&mut self) {
if let Some(ViewOffsetAdjustment::Animation(_)) = &self.view_offset_adj {
self.view_offset_adj = None;
}
}
#[cfg(test)]
pub fn verify_invariants(&self, move_win_id: Option<&W::Id>) {
use approx::assert_abs_diff_eq;
@@ -2682,10 +2708,14 @@ 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.extend(
self.insert_hint_element
.render(renderer, area.loc)
.map(WorkspaceRenderElement::InsertHint),
rv.push(
TileRenderElement::SolidColor(SolidColorRenderElement::from_buffer(
&self.insert_hint_buffer,
area.loc,
1.,
Kind::Unspecified,
))
.into(),
);
}
}
-5
View File
@@ -33,11 +33,6 @@ 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() {
+75 -100
View File
@@ -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, XDG_ACTIVATION_TOKEN_TIMEOUT};
use crate::handlers::configure_lock_surface;
use crate::input::scroll_tracker::ScrollTracker;
use crate::input::{
apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_wheel_binds, TabletData,
@@ -207,12 +207,6 @@ 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>,
@@ -268,26 +262,15 @@ pub struct Niri {
pub cursor_texture_cache: CursorTextureCache,
pub cursor_shape_manager_state: CursorShapeManagerState,
pub dnd_icon: Option<DndIcon>,
/// 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,
pub pointer_focus: PointerFocus,
/// 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,
@@ -403,10 +386,10 @@ pub enum KeyboardFocus {
}
#[derive(Default, Clone, PartialEq)]
pub struct PointContents {
// Output under point.
pub struct PointerFocus {
// Output under pointer.
pub output: Option<Output>,
// Surface under point and its location in the global coordinate space.
// Surface under pointer and its location in global coordinate space.
pub surface: Option<(WlSurface, Point<f64, Logical>)>,
// If surface belongs to a window, this is that window.
pub window: Option<Window>,
@@ -570,7 +553,7 @@ impl State {
self.niri.refresh_pointer_outputs();
self.niri.global_space.refresh();
self.niri.refresh_idle_inhibit();
self.refresh_pointer_contents();
self.refresh_pointer_focus();
foreign_toplevel::refresh(self);
self.niri.refresh_window_rules();
self.refresh_ipc_outputs();
@@ -591,8 +574,10 @@ impl State {
}
pub fn move_cursor(&mut self, location: Point<f64, Logical>) {
let under = self.niri.contents_under(location);
self.niri.pointer_contents.clone_from(&under);
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 pointer = &self.niri.seat.get_pointer().unwrap();
pointer.motion(
@@ -606,9 +591,9 @@ impl State {
);
pointer.frame(self);
self.niri.maybe_activate_pointer_constraint();
// We do not show the pointer on programmatic or keyboard movement.
// We moved the pointer, show it.
self.niri.pointer_hidden = false;
self.niri.reset_pointer_inactivity_timer();
// FIXME: granular
self.niri.queue_redraw_all();
@@ -693,8 +678,8 @@ impl State {
self.move_cursor_to_focused_tile(CenterCoords::Both)
}
pub fn refresh_pointer_contents(&mut self) {
let _span = tracy_client::span!("Niri::refresh_pointer_contents");
pub fn refresh_pointer_focus(&mut self) {
let _span = tracy_client::span!("Niri::refresh_pointer_focus");
let pointer = &self.niri.seat.get_pointer().unwrap();
let location = pointer.current_location();
@@ -709,37 +694,36 @@ impl State {
}
}
if !self.update_pointer_contents() {
if !self.update_pointer_focus() {
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_contents(&mut self) -> bool {
let _span = tracy_client::span!("Niri::update_pointer_contents");
pub fn update_pointer_focus(&mut self) -> bool {
let _span = tracy_client::span!("Niri::update_pointer_focus");
let pointer = &self.niri.seat.get_pointer().unwrap();
let location = pointer.current_location();
let under = if self.niri.pointer_hidden {
PointContents::default()
PointerFocus::default()
} else {
self.niri.contents_under(location)
self.niri.surface_under_and_global_space(location)
};
// 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 {
// 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 {
return false;
}
self.niri.pointer_contents.clone_from(&under);
self.niri
.maybe_activate_pointer_constraint(location, &under);
self.niri.pointer_focus.clone_from(&under);
pointer.motion(
self,
@@ -751,8 +735,6 @@ impl State {
},
);
self.niri.maybe_activate_pointer_constraint();
true
}
@@ -1107,12 +1089,6 @@ 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;
@@ -1567,7 +1543,7 @@ impl State {
to_introspect: &async_channel::Sender<NiriToIntrospect>,
msg: IntrospectToNiri,
) {
use crate::utils::with_toplevel_role;
use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;
let IntrospectToNiri::GetWindows = msg;
let _span = tracy_client::span!("GetWindows");
@@ -1575,8 +1551,21 @@ 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_toplevel_role(mapped.toplevel(), |role| {
let props = with_states(wl_surface, |states| {
let role = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
gnome_shell_introspect::WindowProperties {
title: role.title.clone().unwrap_or_default(),
app_id: role.app_id.clone().unwrap_or_default(),
@@ -1706,18 +1695,6 @@ 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);
@@ -1843,7 +1820,6 @@ impl Niri {
blocker_cleared_tx,
blocker_cleared_rx,
monitors_active: true,
is_lid_closed: false,
devices: HashSet::new(),
tablets: HashMap::new(),
@@ -1897,9 +1873,10 @@ impl Niri {
cursor_texture_cache: Default::default(),
cursor_shape_manager_state,
dnd_icon: None,
pointer_contents: PointContents::default(),
pointer_focus: PointerFocus::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),
@@ -2346,14 +2323,13 @@ impl Niri {
self.window_under(pos)
}
/// Returns contents under the given point.
/// Returns the surface under cursor and its position in the global space.
///
/// 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();
/// 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();
let Some((output, pos_within_output)) = self.output_under(pos) else {
return rv;
@@ -2864,7 +2840,17 @@ impl Niri {
}
}
pub fn refresh_layout_is_grabbed(&mut self) {
let pointer = self.seat.get_pointer().unwrap();
let touch = self.seat.get_touch();
let touch_is_grabbed = touch.map_or(false, |touch| touch.is_grabbed());
self.layout
.set_pointer_grabbed(pointer.is_grabbed() || touch_is_grabbed);
}
pub fn refresh_layout(&mut self) {
self.refresh_layout_is_grabbed();
let layout_is_active = match &self.keyboard_focus {
KeyboardFocus::Layout { .. } => true,
KeyboardFocus::LayerShell { .. } => false,
@@ -4510,16 +4496,8 @@ impl Niri {
self.cursor_manager
.set_cursor_image(CursorImageStatus::default_named());
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();
}
self.lock_state = LockState::Locking(confirmation);
self.queue_redraw_all();
}
pub fn unlock(&mut self) {
@@ -4546,20 +4524,17 @@ impl Niri {
output_state.lock_surface = Some(surface);
}
/// 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 {
pub fn maybe_activate_pointer_constraint(
&self,
new_pos: Point<f64, Logical>,
new_under: &PointerFocus,
) {
let Some((surface, surface_loc)) = &new_under.surface else {
return;
};
if Some(surface) != pointer.current_focus().as_ref() {
if self.pointer_grab_ongoing {
return;
}
let pointer = &self.seat.get_pointer().unwrap();
with_pointer_constraint(surface, pointer, |constraint| {
let Some(constraint) = constraint else { return };
@@ -4570,8 +4545,8 @@ impl Niri {
// Constraint does not apply if not within region.
if let Some(region) = constraint.region() {
let pos_within_surface = pointer_pos - *surface_loc;
if !region.contains(pos_within_surface.to_i32_round()) {
let new_pos_within_surface = new_pos - *surface_loc;
if !region.contains(new_pos_within_surface.to_i32_round()) {
return;
}
}
@@ -4643,7 +4618,7 @@ impl Niri {
}
}
pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointContents) {
pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointerFocus) {
let Some(ffm) = self.config.borrow().input.focus_follows_mouse else {
return;
};
@@ -4654,7 +4629,7 @@ impl Niri {
}
// Recompute the current pointer focus because we don't update it during animations.
let current_focus = self.contents_under(pointer.current_location());
let current_focus = self.surface_under_and_global_space(pointer.current_location());
if let Some(output) = &new_focus.output {
if current_focus.output.as_ref() != Some(output) {
+26 -10
View File
@@ -11,7 +11,10 @@ use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
};
use smithay::wayland::shell::xdg::{ToplevelStateSet, XdgToplevelSurfaceRoleAttributes};
use smithay::wayland::compositor::with_states;
use smithay::wayland::shell::xdg::{
ToplevelStateSet, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes,
};
use wayland_protocols_wlr::foreign_toplevel::v1::server::{
zwlr_foreign_toplevel_handle_v1, zwlr_foreign_toplevel_manager_v1,
};
@@ -19,7 +22,6 @@ 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;
@@ -94,23 +96,37 @@ 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 toplevel = mapped.toplevel();
let wl_surface = toplevel.wl_surface();
with_toplevel_role(toplevel, |role| {
let wl_surface = mapped.toplevel().wl_surface();
with_states(wl_surface, |states| {
let role = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
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 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);
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);
});
}
}
+1 -24
View File
@@ -17,11 +17,8 @@ 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, with_states, SurfaceData};
use smithay::wayland::compositor::{send_surface_state, SurfaceData};
use smithay::wayland::fractional_scale::with_fractional_scale;
use smithay::wayland::shell::xdg::{
ToplevelSurface, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes,
};
pub mod id;
pub mod scale;
@@ -224,26 +221,6 @@ 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();
+15 -9
View File
@@ -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};
use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface, XdgToplevelSurfaceData};
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, with_toplevel_role, ResizeEdge};
use crate::utils::{send_scale_transform, 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 = with_toplevel_role(self.toplevel(), |role| role.current.decoration_mode);
let mode = toplevel.current_state().decoration_mode;
match mode {
Some(zxdg_toplevel_decoration_v1::Mode::ServerSide) => true,
@@ -631,7 +631,14 @@ impl LayoutElement for Mapped {
let _span =
trace_span!("configure_intent", surface = ?self.toplevel().wl_surface().id()).entered();
with_toplevel_role(self.toplevel(), |attributes| {
with_states(self.toplevel().wl_surface(), |states| {
let attributes = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
if let Some(server_pending) = &attributes.server_pending {
let current_server = attributes.current_server_state();
if server_pending != current_server {
@@ -712,11 +719,10 @@ impl LayoutElement for Mapped {
}
fn is_fullscreen(&self) -> bool {
with_toplevel_role(self.toplevel(), |role| {
role.current
.states
.contains(xdg_toplevel::State::Fullscreen)
})
self.toplevel()
.current_state()
.states
.contains(xdg_toplevel::State::Fullscreen)
}
fn is_pending_fullscreen(&self) -> bool {
+14 -4
View File
@@ -1,9 +1,11 @@
use niri_config::{BlockOutFrom, BorderRule, CornerRadius, Match, WindowRule};
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::wayland::shell::xdg::{ToplevelSurface, XdgToplevelSurfaceRoleAttributes};
use smithay::wayland::compositor::with_states;
use smithay::wayland::shell::xdg::{
ToplevelSurface, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes,
};
use crate::layout::workspace::ColumnWidth;
use crate::utils::with_toplevel_role;
pub mod mapped;
pub use mapped::Mapped;
@@ -142,7 +144,15 @@ impl ResolvedWindowRules {
let mut resolved = ResolvedWindowRules::empty();
with_toplevel_role(window.toplevel(), |role| {
let toplevel = window.toplevel();
with_states(toplevel.wl_surface(), |states| {
let mut role = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();
// 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());
@@ -159,7 +169,7 @@ impl ResolvedWindowRules {
}
}
window_matches(window, role, m)
window_matches(window, &role, m)
};
if !(rule.matches.is_empty() || rule.matches.iter().any(matches)) {
+15 -3
View File
@@ -1,8 +1,20 @@
### VSCode
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.
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.
### WezTerm
-29
View File
@@ -22,8 +22,6 @@ debug {
emulate-zero-presentation-time
disable-resize-throttling
disable-transactions
keep-laptop-panel-on-when-lid-is-closed
disable-monitor-names
}
binds {
@@ -167,33 +165,6 @@ 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.
+1 -20
View File
@@ -32,9 +32,7 @@ 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
@@ -47,9 +45,7 @@ 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
}
@@ -64,17 +60,6 @@ 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"
@@ -157,7 +142,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`: <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`.
- `scroll-button`: 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:
@@ -169,10 +154,6 @@ 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.
+1 -3
View File
@@ -45,7 +45,6 @@ layout {
insert-hint {
// off
color "#ffc87f80"
// gradient from="#ffbb6680" to="#ffc88080" angle=45 relative-to="workspace-view"
}
struts {
@@ -317,14 +316,13 @@ Settings for the window insert position hint during an interactive window move.
`off` disables the insert hint altogether.
`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.
`color` lets you change the color of the hint and has the same syntax as colors in border and focus ring.
```kdl
layout {
insert-hint {
// off
color "#ffc87f80"
gradient from="#ffbb6680" to="#ffc88080" angle=45 relative-to="workspace-view"
}
}
```
+3 -3
View File
@@ -21,7 +21,7 @@ cursor {
xcursor-theme "breeze_cursors"
xcursor-size 48
hide-when-typing
hide-on-key-press
hide-after-inactive-ms 1000
}
@@ -109,7 +109,7 @@ cursor {
}
```
#### `hide-when-typing`
#### `hide-on-key-press`
<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-when-typing
hide-on-key-press
}
```
+6 -8
View File
@@ -1,22 +1,20 @@
### 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 "notify-send" "The laptop lid is closed!"; }
lid-open { spawn "notify-send" "The laptop lid is open!"; }
lid-close { spawn "bash" "-c" "niri msg output \"eDP-1\" off"; }
lid-open { spawn "bash" "-c" "niri msg output \"eDP-1\" on"; }
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.
@@ -25,12 +23,12 @@ Currently, only the `spawn` action are supported.
These events correspond to closing and opening of the laptop lid.
Note that niri will already automatically turn the internal laptop monitor on and off in accordance with the laptop lid.
You could use them to turn the laptop internal monitor off and on (until niri gets this functionality built-in).
```kdl
switch-events {
lid-close { spawn "notify-send" "The laptop lid is closed!"; }
lid-open { spawn "notify-send" "The laptop lid is open!"; }
lid-close { spawn "bash" "-c" "niri msg output \"eDP-1\" off"; }
lid-open { spawn "bash" "-c" "niri msg output \"eDP-1\" on"; }
}
```
-2
View File
@@ -72,5 +72,3 @@ pub fn some_function() {
// Code of the function.
}
```
You can also enable Rust memory allocation profiling with `--features=profile-with-tracy-allocations`.
-7
View File
@@ -46,10 +46,3 @@ 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`
-2
View File
@@ -10,8 +10,6 @@ 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>
+9 -3
View File
@@ -10,7 +10,6 @@ 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.
@@ -23,13 +22,21 @@ Finally, the [Xwayland](./Xwayland.md) page explains how to run X11 applications
### NVIDIA
NVIDIA GPUs can have problems running niri (for example, the screen remains black upon starting from a TTY).
NVIDIA GPUs tend to 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.
@@ -62,7 +69,6 @@ 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).
-7
View File
@@ -33,10 +33,3 @@ 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