Compare commits

..

60 Commits

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