Compare commits

..

19 Commits

Author SHA1 Message Date
HigherOrderLogic 49fc6117fd nix: bump inputs 2026-06-18 14:11:04 +03:00
HigherOrderLogic 165a6d1ee8 nix: remove rust-overlay input 2026-06-18 14:11:04 +03:00
Ivan Molodetskikh fdb6d85fc7 Upgrade accesskit_unix to 0.22
Seems the regression is fixed so we can now upgrade it.
2026-06-16 08:37:58 +03:00
Ivan Molodetskikh 188c5300f7 wiki: Update dynamic cast target description 2026-06-15 19:47:58 +03:00
11backslashes a4b5539baa wiki: Add instructions for working around JetBrains Idea crash when
opening the settings window under Wayland.
2026-06-15 12:52:03 +03:00
Noratrieb 6f1a2c5f0e Use RUSTFLAGS instead of CARGO_BUILD_RUSTFLAGS in dev shell
`CARGO_BUILD_RUSTFLAGS` has a very low precedence and is overruled by
global `target.<tuple>.rustflags`.
Since these rustflags are very important, ensure that they have a higher
precedence. We could even go for `CARGO_ENCODED_RUSTFLAGS` which has the
highest precedence, but is slightly annoying to construct and probably
not worth it.
2026-06-08 20:06:55 +03:00
Ivan Molodetskikh f717ae030f Add comment to updating kb focus in confirm MRU 2026-06-05 08:28:52 +03:00
NSPC911 f3696081d1 fix: warp mouse when switching between recent windows 2026-06-05 08:28:19 +03:00
J. Adly 4b60cbe537 input: add tablet stylus button triggers and binds (#3745)
* input: add tablet stylus button triggers and binds

add tablet stylus button3 and fix stylus bind event flow

* add missing allowed during screenshot check

---------

Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
2026-06-05 05:22:59 +00:00
Ivan Molodetskikh f9f43d826a Remove deprecated keep-max-bpc-unchanged debug flag
It's been a stub for two releases now.
2026-05-29 15:01:50 +03:00
Ivan Molodetskikh 3d49db3870 wiki: Clarify max-bpc docs
As discussed: https://oftc.catirclogs.org/wayland/2026-05-29
2026-05-29 14:59:31 +03:00
Michael Yang 9bd6c2cadd feat: add 10-bit framebuffer pixel format
Enable true 10-bit content on supported outputs (tested).
2026-05-28 19:50:25 +03:00
Michael Yang c5253968b4 feat: add per output max-bpc config and ipc action
List current max-bpc values in outputs.

fix: remove 16 bpc and change default behaviour

feat(ipc): add max_bpc and format to output

fix: bpc on output config change

docs: add bpc to Outputs

feat: use atomic commits for connector properties

fix: drm `value_type` breaking change

fix: minor changes based on PR review

Rename bpc to max-bpc.

Add max-bpc output action.

refactor: add set_connector_properties

Fix niri-config parse test.

fix: bail when outside valid max bpc range
2026-05-28 19:50:25 +03:00
Jakob Hellermann 9a6f31012d Fix clippy lints 2026-05-28 08:08:18 +03:00
KarimAlaswad 4294948cf1 Add XF86AudioPause media key mapping
Some Bluetooth earbuds send alternating XF86AudioPlay/XF86AudioPause
keycodes on the same physical press. Without a binding for XF86AudioPause,
every other press is dropped, making playback toggle unreliable.
2026-05-21 17:47:43 +03:00
bokicoder cd5ac3e5e0 nix: add systemd units to the right location 2026-05-15 18:25:56 +03:00
Ivan Molodetskikh 38191826cb Optimize and move .png files from LFS into repo
We want to include them in the tarballs alongside the wiki. I tried
toggling the include LFS in archives option, but it quickly used up most
of our free LFS traffic. So let's move these files in-repo, they are not
that big.

I optimized the files with:

oxipng -o max --strip safe docs/wiki/img/*.png

Need to rememeber to do that for any new .png files.
2026-05-10 08:04:05 +03:00
HigherOrderLogic 0200670d9e Fix typo in issue type name 2026-05-07 23:30:58 -07:00
HigherOrderLogic 90366886b2 Assign issue with type instead of label 2026-05-07 12:36:33 -07:00
52 changed files with 465 additions and 217 deletions
-2
View File
@@ -1,2 +0,0 @@
# LFS configuration for images from the wiki
*.png filter=lfs diff=lfs merge=lfs -text
+1 -1
View File
@@ -2,7 +2,7 @@
name: Bug report
about: Report a bug or a crash
title: ''
labels: bug
type: Bug
assignees: ''
---
Generated
+67 -53
View File
@@ -4,39 +4,42 @@ version = 4
[[package]]
name = "accesskit"
version = "0.21.1"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99"
checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320"
dependencies = [
"uuid",
]
[[package]]
name = "accesskit_atspi_common"
version = "0.14.2"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "890d241cf51fc784f0ac5ac34dfc847421f8d39da6c7c91a0fcc987db62a8267"
checksum = "7e98018dbef3583d751dbb96e07b8728fb99581360e1c3df408af16f4a80b821"
dependencies = [
"accesskit",
"accesskit_consumer",
"atspi-common",
"phf",
"serde",
"thiserror 1.0.69",
"zvariant",
]
[[package]]
name = "accesskit_consumer"
version = "0.31.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db81010a6895d8707f9072e6ce98070579b43b717193d2614014abd5cb17dd43"
checksum = "f950720ce064757a1b629caad3a408e8d2c63bb01f29b8a3ff8daa331053ffeb"
dependencies = [
"accesskit",
"hashbrown 0.15.5",
"hashbrown 0.16.1",
]
[[package]]
name = "accesskit_unix"
version = "0.17.2"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301e55b39cfc15d9c48943ce5f572204a551646700d0e8efa424585f94fec528"
checksum = "5376ba4cc23312587634abb5250b1ce8618f01a55915608209aafd01efb4bf8c"
dependencies = [
"accesskit",
"accesskit_atspi_common",
@@ -371,20 +374,19 @@ checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a"
[[package]]
name = "atspi"
version = "0.25.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c"
checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d"
dependencies = [
"atspi-common",
"atspi-connection",
"atspi-proxies",
]
[[package]]
name = "atspi-common"
version = "0.9.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb"
checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d"
dependencies = [
"enumflags2",
"serde",
@@ -396,23 +398,11 @@ dependencies = [
"zvariant",
]
[[package]]
name = "atspi-connection"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938"
dependencies = [
"atspi-common",
"atspi-proxies",
"futures-lite",
"zbus",
]
[[package]]
name = "atspi-proxies"
version = "0.9.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c"
checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc"
dependencies = [
"atspi-common",
"serde",
@@ -714,9 +704,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.6.3"
version = "4.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "660c0520455b1013b9bcb0393d5f643d7e4454fb69c915b8d6d2aa0e9a45acc3"
checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb"
dependencies = [
"clap",
]
@@ -1150,6 +1140,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "foreign-types"
version = "0.5.0"
@@ -1656,7 +1652,16 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
]
[[package]]
@@ -1963,9 +1968,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.186"
version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
[[package]]
name = "libdisplay-info"
@@ -3764,7 +3769,7 @@ dependencies = [
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
"winnow 1.0.1",
]
[[package]]
@@ -3785,7 +3790,7 @@ dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
"winnow 1.0.1",
]
[[package]]
@@ -3794,7 +3799,7 @@ version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
"winnow 1.0.1",
]
[[package]]
@@ -4556,6 +4561,15 @@ dependencies = [
"xkbcommon-dl",
]
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "1.0.1"
@@ -4756,9 +4770,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "zbus"
version = "5.15.0"
version = "5.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1"
checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1"
dependencies = [
"async-broadcast",
"async-executor",
@@ -4783,7 +4797,7 @@ dependencies = [
"uds_windows",
"uuid",
"windows-sys 0.61.2",
"winnow",
"winnow 0.7.15",
"zbus_macros",
"zbus_names",
"zvariant",
@@ -4815,9 +4829,9 @@ dependencies = [
[[package]]
name = "zbus_macros"
version = "5.15.0"
version = "5.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff"
checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@@ -4830,12 +4844,12 @@ dependencies = [
[[package]]
name = "zbus_names"
version = "4.3.2"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f"
dependencies = [
"serde",
"winnow",
"winnow 0.7.15",
"zvariant",
]
@@ -4879,23 +4893,23 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zvariant"
version = "5.11.0"
version = "5.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee"
checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4"
dependencies = [
"endi",
"enumflags2",
"serde",
"winnow",
"winnow 0.7.15",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "5.11.0"
version = "5.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda"
checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@@ -4906,13 +4920,13 @@ dependencies = [
[[package]]
name = "zvariant_utils"
version = "3.3.1"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691"
checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9"
dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.117",
"winnow",
"winnow 0.7.15",
]
+2 -5
View File
@@ -51,11 +51,8 @@ readme = "README.md"
keywords = ["wayland", "compositor", "tiling", "smithay", "wm"]
[dependencies]
# accesskit_unix 0.18 has a regression where it doesn't work in normal configurations.
# accesskit 0.21 is its correct dependent version.
# https://github.com/niri-wm/niri/issues/3594
accesskit = { version = "0.21", optional = true }
accesskit_unix = { version = "0.17", optional = true }
accesskit = { version = "0.24", optional = true }
accesskit_unix = { version = "0.22", optional = true }
anyhow.workspace = true
arrayvec = "0.7.6"
async-channel = "2.5.0"
+2
View File
@@ -33,6 +33,8 @@ JetBrains IDEs can run directly on Wayland, but it's not the default.
For JetBrainsRuntime > 17, you can set the flag `-Dawt.toolkit.name=WLToolkit` inside of `help -> edit custom vm options -> add`.
If the settings window fails to load under Wayland, and the UI becomes unresponsive afterwards, also set the flag `-Dsun.awt.wl.WindowDecorationStyle=builtin` in the custom vm options. This gives the settings window a titlebar, but it at least makes the IDE functional.
### WezTerm
> [!NOTE]
-21
View File
@@ -324,27 +324,6 @@ debug {
}
```
### `keep-max-bpc-unchanged`
<sup>Since: 25.08</sup>
When connecting monitors, niri sets their max bpc to 8 in order to reduce display bandwidth and to potentially allow more monitors to be connected at once.
Restricting bpc to 8 is not a problem since we don't support HDR or color management yet and can't really make use of higher bpc.
Apparently, setting max bpc to 8 breaks some displays driven by AMDGPU.
If this happens to you, set this debug flag, which will prevent niri from changing max bpc.
AMDGPU bug report: https://gitlab.freedesktop.org/drm/amd/-/issues/4487.
<sup>Since: 25.11</sup>
This setting is deprecated and does nothing: niri no longer sets max bpc.
The old niri behavior with this setting enabled matches the new behavior.
```kdl
debug {
keep-max-bpc-unchanged
}
```
### Key Bindings
These are not debug options, but rather key bindings.
+22
View File
@@ -15,6 +15,7 @@ output "eDP-1" {
variable-refresh-rate // on-demand=true
focus-at-startup
backdrop-color "#001100"
// max-bpc 8
hot-corners {
// off
@@ -279,6 +280,27 @@ output "HDMI-A-1" {
}
```
### `max-bpc`
<sup>Since: next release</sup>
Set the maximum bits per channel (BPC) for this output.
You *do not* need to set this option normally.
It influences the encoding of the display signal on the wire and *is not* directly related to the color bitness or framebuffer format.
Setting `max-bpc` to a low value may help if you hit a bandwidth issue (can't set a monitor configuration that works on other compositor).
Otherwise, you're advised to leave it unset (keeping a default, usually high value) and let the GPU driver figure things out automatically.
Valid values are `6`, `8`, `10`, `12`, `14`, `16`.
```kdl
// Set 8 max-bpc on HDMI-A-1 to lower the bandwidth.
output "HDMI-A-1" {
max-bpc 8
}
```
### `hot-corners`
<sup>Since: 25.11</sup>
+3 -3
View File
@@ -53,12 +53,12 @@ It shows up as "niri Dynamic Cast Target" in the screencast window dialog.
![Screencast dialog showing niri Dynamic Cast Target.](https://github.com/user-attachments/assets/e236ce74-98ec-4f3a-a99b-29ac1ff324dd)
When you select it, it will start as an empty, transparent video stream.
Then, you can use the following binds to change what it shows:
Choose it, then use the following binds to change what it shows.
The stream won't start until you make your first target selection.
- `set-dynamic-cast-window` to cast the focused window.
- `set-dynamic-cast-monitor` to cast the focused monitor.
- `clear-dynamic-cast-target` to go back to an empty stream.
- `clear-dynamic-cast-target` to reset to an empty video stream.
You can also use these actions from the command line, for example to interactively pick which window to cast:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 9.3 KiB

Generated
+4 -25
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1757967192,
"narHash": "sha256-/aA9A/OBmnuOMgwfzdsXRusqzUpd8rQnQY8jtrHK+To=",
"lastModified": 1781607440,
"narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0d7c15863b251a7a50265e57c1dca1a7add2e291",
"rev": "3e41b24abd260e8f71dbe2f5737d24122f972158",
"type": "github"
},
"original": {
@@ -18,28 +18,7 @@
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1757989933,
"narHash": "sha256-9cpKYWWPCFhgwQTww8S94rTXgg8Q8ydFv9fXM6I8xQM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "8249aa3442fb9b45e615a35f39eca2fe5510d7c3",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
"nixpkgs": "nixpkgs"
}
}
},
+16 -39
View File
@@ -2,22 +2,12 @@
{
description = "Niri: A scrollable-tiling Wayland compositor.";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# NOTE: This is not necessary for end users
# You can omit it with `inputs.rust-overlay.follows = ""`
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
outputs =
{
self,
nixpkgs,
rust-overlay,
}:
let
revision = self.shortRev or self.dirtyShortRev or "unknown";
@@ -135,12 +125,12 @@
''
+ lib.optionalString withSystemd ''
install -Dm755 resources/niri-session $out/bin/niri-session
install -Dm644 resources/niri{.service,-shutdown.target} -t $out/share/systemd/user
install -Dm644 resources/niri{.service,-shutdown.target} -t $out/lib/systemd/user
'';
env = {
# Force linking with libEGL and libwayland-client
# so they can be discovered by `dlopen()`
# Force linking with libEGL and libwayland-client so they end up in RPATH and
# can be discovered by `dlopen()`
RUSTFLAGS = toString (
map (arg: "-C link-arg=" + arg) [
"-Wl,--push-state,--no-as-needed"
@@ -182,33 +172,20 @@
system:
let
pkgs = nixpkgsFor.${system};
rust-bin = rust-overlay.lib.mkRustBin { } pkgs;
rustfmt' = pkgs.rustfmt.override { asNightly = true; };
inherit (self.packages.${system}) niri;
in
{
default = pkgs.mkShell {
packages = [
# We don't use the toolchain from nixpkgs
# because we prefer a nightly toolchain
# and we *require* a nightly rustfmt
(rust-bin.selectLatestNightlyWith (
toolchain:
toolchain.default.override {
extensions = [
# includes already:
# rustc
# cargo
# rust-std
# rust-docs
# rustfmt-preview
# clippy-preview
"rust-analyzer"
"rust-src"
];
}
))
pkgs.cargo-insta
];
packages = builtins.attrValues {
inherit (pkgs)
rustc
cargo
clippy
cargo-insta
;
inherit rustfmt';
};
nativeBuildInputs = [
pkgs.rustPlatform.bindgenHook
@@ -225,8 +202,8 @@
# It is required for `dlopen()` to work on some libraries; see the comment
# in the package expression
#
# This should only be set with `CARGO_BUILD_RUSTFLAGS="$CARGO_BUILD_RUSTFLAGS -C your-flags"`
CARGO_BUILD_RUSTFLAGS = niri.RUSTFLAGS;
# This should only be set with `RUSTFLAGS="$RUSTFLAGS -C your-flags"`
RUSTFLAGS = niri.RUSTFLAGS;
};
};
}
+9
View File
@@ -52,6 +52,9 @@ pub enum Trigger {
TouchpadScrollUp,
TouchpadScrollLeft,
TouchpadScrollRight,
TabletStylusButton1,
TabletStylusButton2,
TabletStylusButton3,
}
bitflags! {
@@ -1000,6 +1003,12 @@ impl FromStr for Key {
Trigger::TouchpadScrollLeft
} else if key.eq_ignore_ascii_case("TouchpadScrollRight") {
Trigger::TouchpadScrollRight
} else if key.eq_ignore_ascii_case("TabletStylusButton1") {
Trigger::TabletStylusButton1
} else if key.eq_ignore_ascii_case("TabletStylusButton2") {
Trigger::TabletStylusButton2
} else if key.eq_ignore_ascii_case("TabletStylusButton3") {
Trigger::TabletStylusButton3
} else {
let mut keysym = keysym_from_name(key, KEYSYM_CASE_INSENSITIVE);
// The keyboard event handling code can receive either
-4
View File
@@ -10,7 +10,6 @@ pub struct Debug {
pub enable_overlay_planes: bool,
pub disable_cursor_plane: bool,
pub disable_direct_scanout: bool,
pub keep_max_bpc_unchanged: bool,
pub restrict_primary_scanout_to_matching_format: bool,
pub force_disable_connectors_on_resume: bool,
pub render_drm_device: Option<PathBuf>,
@@ -42,8 +41,6 @@ pub struct DebugPart {
#[knuffel(child)]
pub disable_direct_scanout: Option<Flag>,
#[knuffel(child)]
pub keep_max_bpc_unchanged: Option<Flag>,
#[knuffel(child)]
pub restrict_primary_scanout_to_matching_format: Option<Flag>,
#[knuffel(child)]
pub force_disable_connectors_on_resume: Option<Flag>,
@@ -82,7 +79,6 @@ impl MergeWith<DebugPart> for Debug {
enable_overlay_planes,
disable_cursor_plane,
disable_direct_scanout,
keep_max_bpc_unchanged,
restrict_primary_scanout_to_matching_format,
force_disable_connectors_on_resume,
force_pipewire_invalid_modifier,
+9 -2
View File
@@ -745,6 +745,7 @@ mod tests {
transform "flipped-90"
position x=10 y=20
mode "1920x1080@144"
max-bpc 10
variable-refresh-rate on-demand=true
background-color "rgba(25, 25, 102, 1.0)"
hot-corners {
@@ -857,7 +858,7 @@ mod tests {
window-open { off; }
window-close {
curve "cubic-bezier" 0.05 0.7 0.1 1
curve "cubic-bezier" 0.05 0.7 0.1 1
}
recent-windows-close {
@@ -1160,6 +1161,11 @@ mod tests {
y: 20,
},
),
max_bpc: Some(
MaxBpc(
_10,
),
),
mode: Some(
Mode {
custom: false,
@@ -1205,6 +1211,7 @@ mod tests {
scale: None,
transform: Normal,
position: None,
max_bpc: None,
mode: Some(
Mode {
custom: true,
@@ -1231,6 +1238,7 @@ mod tests {
scale: None,
transform: Normal,
position: None,
max_bpc: None,
mode: None,
modeline: Some(
Modeline {
@@ -2244,7 +2252,6 @@ mod tests {
enable_overlay_planes: false,
disable_cursor_plane: false,
disable_direct_scanout: false,
keep_max_bpc_unchanged: false,
restrict_primary_scanout_to_matching_format: false,
force_disable_connectors_on_resume: false,
render_drm_device: Some(
+42
View File
@@ -59,6 +59,8 @@ pub struct Output {
pub transform: Transform,
#[knuffel(child)]
pub position: Option<Position>,
#[knuffel(child, unwrap(argument))]
pub max_bpc: Option<MaxBpc>,
#[knuffel(child)]
pub mode: Option<Mode>,
#[knuffel(child)]
@@ -101,6 +103,7 @@ impl Default for Output {
scale: None,
transform: Transform::Normal,
position: None,
max_bpc: None,
mode: None,
modeline: None,
variable_refresh_rate: None,
@@ -128,6 +131,9 @@ pub struct Position {
pub y: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct MaxBpc(pub niri_ipc::MaxBpc);
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Default)]
pub struct Vrr {
#[knuffel(property, default = false)]
@@ -257,6 +263,42 @@ impl OutputName {
}
}
impl<S: ErrorSpan> knuffel::DecodeScalar<S> for MaxBpc {
fn type_check(
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
ctx: &mut Context<S>,
) {
if let Some(type_name) = &type_name {
ctx.emit_error(DecodeError::unexpected(
type_name,
"type name",
"no type name expected for this node",
));
}
}
fn raw_decode(
value: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
ctx: &mut Context<S>,
) -> Result<Self, DecodeError<S>> {
match &**value {
knuffel::ast::Literal::Int(ref val) => match u8::try_from(val) {
Ok(v) => niri_ipc::MaxBpc::try_from(v)
.map(MaxBpc)
.map_err(|e| DecodeError::conversion(value, e)),
Err(e) => {
ctx.emit_error(DecodeError::conversion(value, e));
Ok(Self::default())
}
},
_ => {
ctx.emit_error(DecodeError::scalar_kind(knuffel::decode::Kind::Int, value));
Ok(Self::default())
}
}
}
}
impl<S: ErrorSpan> knuffel::Decode<S> for Mode {
fn decode_node(node: &SpannedNode<S>, ctx: &mut Context<S>) -> Result<Self, DecodeError<S>> {
if let Some(type_name) = &node.type_name {
+58
View File
@@ -1097,6 +1097,12 @@ pub enum OutputAction {
#[cfg_attr(feature = "clap", command(flatten))]
vrr: VrrToSet,
},
/// Set the maximum bits per channel (bit depth).
MaxBpc {
/// Maximum bits per channel to set.
#[cfg_attr(feature = "clap", arg())]
max_bpc: MaxBpc,
},
}
/// Output mode to set.
@@ -1228,6 +1234,8 @@ pub struct Output {
///
/// `None` if the output is not mapped to any logical output (for example, if it is disabled).
pub logical: Option<LogicalOutput>,
/// Maximum bits per channel (bit depth), if known.
pub max_bpc: Option<u8>,
}
/// Output mode.
@@ -1291,6 +1299,32 @@ pub enum Transform {
Flipped270,
}
/// Output maximum bits per channel.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum MaxBpc {
/// 6-bit.
#[serde(rename = "6")]
_6 = 6,
/// 8-bit.
#[default]
#[serde(rename = "8")]
_8 = 8,
/// 10-bit.
#[serde(rename = "10")]
_10 = 10,
/// 12-bit.
#[serde(rename = "12")]
_12 = 12,
/// 14-bit.
#[serde(rename = "14")]
_14 = 14,
/// 16-bit.
#[serde(rename = "16")]
_16 = 16,
}
/// Toplevel window.
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
@@ -1868,6 +1902,30 @@ impl FromStr for Transform {
}
}
impl TryFrom<u8> for MaxBpc {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
6 => Ok(MaxBpc::_6),
8 => Ok(MaxBpc::_8),
10 => Ok(MaxBpc::_10),
12 => Ok(MaxBpc::_12),
14 => Ok(MaxBpc::_14),
16 => Ok(MaxBpc::_16),
_ => Err("invalid max-bpc, can be 6, 8, 10, 12, 14, 16"),
}
}
}
impl FromStr for MaxBpc {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s.parse::<u8>().unwrap_or_default())
}
}
impl FromStr for Layer {
type Err = &'static str;
+1
View File
@@ -383,6 +383,7 @@ binds {
// Example media keys mapping using playerctl.
// This will work with any MPRIS-enabled media player.
XF86AudioPlay allow-when-locked=true { spawn-sh "playerctl play-pause"; }
XF86AudioPause allow-when-locked=true { spawn-sh "playerctl play-pause"; }
XF86AudioStop allow-when-locked=true { spawn-sh "playerctl stop"; }
XF86AudioPrev allow-when-locked=true { spawn-sh "playerctl previous"; }
XF86AudioNext allow-when-locked=true { spawn-sh "playerctl next"; }
+4 -1
View File
@@ -3,7 +3,7 @@ use std::thread;
use accesskit::{
ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Live, Node, NodeId, Role,
Tree, TreeUpdate,
Tree, TreeId, TreeUpdate,
};
use accesskit_unix::Adapter;
use calloop::LoopHandle;
@@ -220,6 +220,7 @@ impl Niri {
let update = TreeUpdate {
nodes,
tree: None,
tree_id: TreeId::ROOT,
focus,
};
@@ -246,6 +247,7 @@ impl Niri {
let update = TreeUpdate {
nodes: vec![(ID_ANNOUNCEMENT, node)],
tree: None,
tree_id: TreeId::ROOT,
focus: self.a11y.focus,
};
@@ -339,6 +341,7 @@ impl Niri {
(ID_MRU, mru),
],
tree: Some(tree),
tree_id: TreeId::ROOT,
focus,
}
}
+1
View File
@@ -109,6 +109,7 @@ impl Headless {
vrr_supported: false,
vrr_enabled: false,
logical: Some(logical_output(&output)),
max_bpc: None,
},
);
+143 -56
View File
@@ -14,7 +14,7 @@ use anyhow::{anyhow, bail, ensure, Context};
use bytemuck::cast_slice_mut;
use drm_ffi::drm_mode_modeinfo;
use libc::dev_t;
use niri_config::output::Modeline;
use niri_config::output::{MaxBpc, Modeline};
use niri_config::{Config, OutputName};
use niri_ipc::{HSyncPolarity, VSyncPolarity};
use smithay::backend::allocator::dmabuf::Dmabuf;
@@ -70,7 +70,11 @@ use crate::render_helpers::renderer::AsGlesRenderer;
use crate::render_helpers::{resources, shaders, RenderCtx, RenderTarget};
use crate::utils::{get_monotonic_time, is_laptop_panel, logical_output, PanelOrientation};
const SUPPORTED_COLOR_FORMATS: [Fourcc; 4] = [
const SUPPORTED_COLOR_FORMATS: [Fourcc; 8] = [
Fourcc::Xrgb2101010,
Fourcc::Xbgr2101010,
Fourcc::Argb2101010,
Fourcc::Abgr2101010,
Fourcc::Xrgb8888,
Fourcc::Xbgr8888,
Fourcc::Argb8888,
@@ -405,6 +409,8 @@ struct ConnectorProperties<'a> {
device: &'a DrmDevice,
connector: connector::Handle,
properties: Vec<(property::Info, property::RawValue)>,
has_change: bool,
requests: AtomicModeReq,
}
impl Tty {
@@ -676,16 +682,19 @@ impl Tty {
// Apply pending gamma changes and restore our existing gamma.
let device = self.devices.get_mut(&node).unwrap();
for (crtc, surface) in device.surfaces.iter_mut() {
if let Ok(props) =
if let Ok(mut props) =
ConnectorProperties::try_new(&device.drm, surface.connector)
{
match reset_hdr(&props) {
Ok(()) => (),
Err(err) => debug!("couldn't reset HDR properties: {err:?}"),
}
let max_bpc = self
.config
.borrow()
.outputs
.find(&surface.name)
.and_then(|o| o.max_bpc);
set_connector_properties(&mut props, max_bpc, true);
} else {
warn!("failed to get connector properties");
};
}
if let Some(ramp) = surface.pending_gamma_change.take() {
let ramp = ramp.as_deref();
@@ -1302,13 +1311,10 @@ impl Tty {
debug!("picking mode: {mode:?}");
let mut orientation = None;
if let Ok(props) = ConnectorProperties::try_new(&device.drm, connector.handle()) {
match reset_hdr(&props) {
Ok(()) => (),
Err(err) => debug!("couldn't reset HDR properties: {err:?}"),
}
if let Ok(mut props) = ConnectorProperties::try_new(&device.drm, connector.handle()) {
set_connector_properties(&mut props, config.max_bpc, true);
match get_panel_orientation(&props) {
match props.get_panel_orientation() {
Ok(x) => orientation = Some(x),
Err(err) => {
trace!("couldn't get panel orientation: {err:?}");
@@ -1316,7 +1322,7 @@ impl Tty {
}
} else {
warn!("failed to get connector properties");
};
}
let mut gamma_props = GammaProps::new(&device.drm, crtc)
.map_err(|err| debug!("couldn't get gamma properties: {err:?}"))
@@ -2194,6 +2200,15 @@ impl Tty {
OutputId::next()
});
let props = ConnectorProperties::try_new(&device.drm, connector.handle()).ok();
let max_bpc = props.as_ref().and_then(|p| p.find(c"max bpc").ok());
let max_bpc = max_bpc.and_then(|(info, value)| {
info.value_type()
.convert_value(*value)
.as_unsigned_range()
.map(|v| v as u8)
});
let ipc_output = niri_ipc::Output {
name: connector_name,
make: output_name.make.unwrap_or_else(|| "Unknown".into()),
@@ -2206,6 +2221,7 @@ impl Tty {
vrr_supported,
vrr_enabled,
logical,
max_bpc,
};
ipc_outputs.insert(id, ipc_output);
@@ -2422,6 +2438,13 @@ impl Tty {
},
};
if let Ok(mut props) = ConnectorProperties::try_new(&device.drm, surface.connector)
{
set_connector_properties(&mut props, config.max_bpc, false);
} else {
warn!("failed to get connector properties");
}
let change_mode = surface.compositor.pending_mode() != mode;
let vrr_enabled = surface.compositor.vrr_enabled();
@@ -3250,6 +3273,8 @@ impl<'a> ConnectorProperties<'a> {
device,
connector,
properties,
has_change: false,
requests: AtomicModeReq::new(),
})
}
@@ -3262,35 +3287,115 @@ impl<'a> ConnectorProperties<'a> {
Err(anyhow!("couldn't find property: {name:?}"))
}
fn get_panel_orientation(&self) -> anyhow::Result<Transform> {
let (info, value) = self.find(c"panel orientation")?;
match info.value_type().convert_value(*value) {
property::Value::Enum(Some(val)) => match val.value() {
// "Normal"
0 => Ok(Transform::Normal),
// "Upside Down"
1 => Ok(Transform::_180),
// "Left Side Up"
2 => Ok(Transform::_90),
// "Right Side Up"
3 => Ok(Transform::_270),
_ => bail!("panel orientation has invalid value: {:?}", val),
},
_ => bail!("panel orientation has wrong value type"),
}
}
fn reset_hdr(&mut self) -> anyhow::Result<()> {
const DRM_MODE_COLORIMETRY_DEFAULT: u64 = 0;
let (info, value) = self.find(c"HDR_OUTPUT_METADATA")?;
let property::ValueType::Blob = info.value_type() else {
bail!("wrong property type")
};
if *value != 0 {
self.requests
.add_raw_property(self.connector.into(), info.handle(), 0);
self.has_change = true;
}
let (info, value) = self.find(c"Colorspace")?;
let property::ValueType::Enum(_) = info.value_type() else {
bail!("wrong property type")
};
if *value != DRM_MODE_COLORIMETRY_DEFAULT {
self.requests.add_raw_property(
self.connector.into(),
info.handle(),
DRM_MODE_COLORIMETRY_DEFAULT,
);
self.has_change = true;
}
Ok(())
}
fn set_max_bpc(&mut self, max_bpc: MaxBpc) -> anyhow::Result<u64> {
let (info, value) = self.find(c"max bpc")?;
let property::ValueType::UnsignedRange(min, max) = info.value_type() else {
bail!("wrong property type")
};
let max_bpc = max_bpc.0 as u64;
if !(min..=max).contains(&max_bpc) {
bail!("max-bpc {max_bpc} outside valid range of [{min}, {max}]");
}
let property::Value::UnsignedRange(value) = info.value_type().convert_value(*value) else {
bail!("wrong property type")
};
if value != max_bpc {
self.requests.add_raw_property(
self.connector.into(),
info.handle(),
property::Value::UnsignedRange(max_bpc).into(),
);
self.has_change = true;
}
Ok(max_bpc)
}
fn commit(&mut self) -> anyhow::Result<()> {
if self.has_change {
self.device.atomic_commit(
AtomicCommitFlags::ALLOW_MODESET,
std::mem::take(&mut self.requests),
)?;
}
Ok(())
}
}
const DRM_MODE_COLORIMETRY_DEFAULT: u64 = 0;
fn reset_hdr(props: &ConnectorProperties) -> anyhow::Result<()> {
let (info, value) = props.find(c"HDR_OUTPUT_METADATA")?;
let property::ValueType::Blob = info.value_type() else {
bail!("wrong property type")
};
if *value != 0 {
props
.device
.set_property(props.connector, info.handle(), 0)
.context("error setting property")?;
fn set_connector_properties(
props: &mut ConnectorProperties,
max_bpc: Option<MaxBpc>,
reset_hdr: bool,
) {
if let Some(max_bpc) = max_bpc {
if let Err(err) = props.set_max_bpc(max_bpc) {
debug!("failed to set `max bpc` property: {err}");
}
}
let (info, value) = props.find(c"Colorspace")?;
let property::ValueType::Enum(_) = info.value_type() else {
bail!("wrong property type")
};
if *value != DRM_MODE_COLORIMETRY_DEFAULT {
props
.device
.set_property(props.connector, info.handle(), DRM_MODE_COLORIMETRY_DEFAULT)
.context("error setting property")?;
if reset_hdr {
if let Err(err) = props.reset_hdr() {
debug!("failed to set HDR properties: {err}");
}
}
Ok(())
if let Err(err) = props.commit() {
warn!("failed to atomically commit properties: {err}");
}
}
fn is_vrr_capable(device: &DrmDevice, connector: connector::Handle) -> Option<bool> {
@@ -3298,24 +3403,6 @@ fn is_vrr_capable(device: &DrmDevice, connector: connector::Handle) -> Option<bo
info.value_type().convert_value(value).as_boolean()
}
fn get_panel_orientation(props: &ConnectorProperties) -> anyhow::Result<Transform> {
let (info, value) = props.find(c"panel orientation")?;
match info.value_type().convert_value(*value) {
property::Value::Enum(Some(val)) => match val.value() {
// "Normal"
0 => Ok(Transform::Normal),
// "Upside Down"
1 => Ok(Transform::_180),
// "Left Side Up"
2 => Ok(Transform::_90),
// "Right Side Up"
3 => Ok(Transform::_270),
_ => bail!("panel orientation has invalid value: {:?}", val),
},
_ => bail!("panel orientation has wrong value type"),
}
}
pub fn set_gamma_for_crtc(
device: &DrmDevice,
crtc: crtc::Handle,
+1
View File
@@ -95,6 +95,7 @@ impl Winit {
vrr_supported: false,
vrr_enabled: false,
logical: Some(logical_output(&output)),
max_bpc: None,
},
)])));
+55 -1
View File
@@ -3778,11 +3778,53 @@ impl State {
}
fn on_tablet_tool_button<I: InputBackend>(&mut self, event: I::TabletToolButtonEvent) {
const BTN_STYLUS3: u32 = 0x149;
const BTN_STYLUS: u32 = 0x14b;
const BTN_STYLUS2: u32 = 0x14c;
let tool = self.niri.seat.tablet_seat().get_tool(&event.tool());
if let Some(tool) = tool {
let button = event.button();
if self.niri.suppressed_buttons.remove(&button) {
return;
}
let trigger = match button {
BTN_STYLUS => Some(Trigger::TabletStylusButton1),
BTN_STYLUS2 => Some(Trigger::TabletStylusButton2),
BTN_STYLUS3 => Some(Trigger::TabletStylusButton3),
_ => None,
};
if let Some(trigger) = trigger {
if event.button_state() == ButtonState::Pressed {
let mod_key = self.backend.mod_key(&self.niri.config.borrow());
let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();
let modifiers = modifiers_from_state(mods);
if self.niri.mods_with_tablet_stylus_binds.contains(&modifiers) {
let bind = {
let config = self.niri.config.borrow();
let bindings = config.binds.0.iter();
find_configured_bind(bindings, mod_key, trigger, mods)
}
.filter(|bind| {
!self.niri.screenshot_ui.is_open()
|| allowed_during_screenshot(&bind.action)
});
if let Some(bind) = bind {
self.niri.suppressed_buttons.insert(button);
self.handle_bind(bind.clone());
return;
}
}
}
}
tool.button(
event.button(),
button,
event.button_state(),
SERIAL_COUNTER.next_serial(),
event.time_msec(),
@@ -5069,6 +5111,18 @@ pub fn mods_with_finger_scroll_binds(mod_key: ModKey, binds: &Binds) -> HashSet<
)
}
pub fn mods_with_tablet_stylus_binds(mod_key: ModKey, binds: &Binds) -> HashSet<Modifiers> {
mods_with_binds(
mod_key,
binds,
&[
Trigger::TabletStylusButton1,
Trigger::TabletStylusButton2,
Trigger::TabletStylusButton3,
],
)
}
fn grab_allows_hot_corner(grab: &(dyn PointerGrab<State> + 'static)) -> bool {
let grab = grab.as_any();
+6 -1
View File
@@ -220,7 +220,7 @@ pub fn handle_msg(mut msg: Msg, json: bool) -> anyhow::Result<()> {
let print = |surface: &niri_ipc::LayerSurface| {
println!(" Surface:");
println!(" Namespace: \"{}\"", &surface.namespace);
println!(" Namespace: \"{}\"", surface.namespace);
let interactivity = match surface.keyboard_interactivity {
niri_ipc::LayerSurfaceKeyboardInteractivity::None => "none",
@@ -568,6 +568,7 @@ fn print_output(output: Output) -> anyhow::Result<()> {
vrr_supported,
vrr_enabled,
logical,
max_bpc,
} = output;
let serial = serial.as_deref().unwrap_or("Unknown");
@@ -651,6 +652,10 @@ fn print_output(output: Output) -> anyhow::Result<()> {
println!(" Transform: {transform}");
}
if let Some(max_bpc) = max_bpc {
println!(" Max bits per channel: {max_bpc}");
}
println!(" Available modes:");
for (idx, mode) in modes.into_iter().enumerate() {
let Mode {
+14 -1
View File
@@ -14,6 +14,7 @@ use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as
use anyhow::{bail, ensure, Context};
use calloop::futures::Scheduler;
use niri_config::debug::PreviewRender;
use niri_config::output::MaxBpc;
use niri_config::{
Config, FloatOrInt, Key, Modifiers, OutputName, TrackLayout, WarpMouseToFocusMode,
WorkspaceReference, Xkb,
@@ -133,7 +134,7 @@ use crate::input::scroll_swipe_gesture::ScrollSwipeGesture;
use crate::input::scroll_tracker::ScrollTracker;
use crate::input::{
apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_mouse_binds,
mods_with_wheel_binds, TabletData,
mods_with_tablet_stylus_binds, mods_with_wheel_binds, TabletData,
};
use crate::ipc::server::IpcServer;
use crate::layer::mapped::LayerSurfaceRenderElement;
@@ -375,6 +376,7 @@ pub struct Niri {
pub horizontal_wheel_tracker: ScrollTracker,
pub mods_with_mouse_binds: HashSet<Modifiers>,
pub mods_with_wheel_binds: HashSet<Modifiers>,
pub mods_with_tablet_stylus_binds: HashSet<Modifiers>,
pub vertical_finger_scroll_tracker: ScrollTracker,
pub horizontal_finger_scroll_tracker: ScrollTracker,
pub mods_with_finger_scroll_binds: HashSet<Modifiers>,
@@ -991,6 +993,12 @@ impl State {
pub fn confirm_mru(&mut self) {
if let Some(window) = self.niri.close_mru(MruCloseRequest::Confirm) {
// focus_window() will warp the cursor to the window only when the keyboard focus is on
// the layout. However, right now the keyboard focus is still on the MRU (that we had
// just closed) since it's only updated at the end of the event loop cycle. Force-update
// the keyboard focus here to make cursor warping work.
self.update_keyboard_focus();
self.focus_window(&window);
}
}
@@ -1533,6 +1541,8 @@ impl State {
.on_hotkey_config_updated(new_mod_key);
self.niri.mods_with_mouse_binds = mods_with_mouse_binds(new_mod_key, &config.binds);
self.niri.mods_with_wheel_binds = mods_with_wheel_binds(new_mod_key, &config.binds);
self.niri.mods_with_tablet_stylus_binds =
mods_with_tablet_stylus_binds(new_mod_key, &config.binds);
self.niri.mods_with_finger_scroll_binds =
mods_with_finger_scroll_binds(new_mod_key, &config.binds);
}
@@ -1925,6 +1935,7 @@ impl State {
None
}
}
niri_ipc::OutputAction::MaxBpc { max_bpc } => config.max_bpc = Some(MaxBpc(max_bpc)),
});
self.reload_output_config();
@@ -2405,6 +2416,7 @@ impl Niri {
let mods_with_mouse_binds = mods_with_mouse_binds(mod_key, &config_.binds);
let mods_with_wheel_binds = mods_with_wheel_binds(mod_key, &config_.binds);
let mods_with_finger_scroll_binds = mods_with_finger_scroll_binds(mod_key, &config_.binds);
let mods_with_tablet_stylus_binds = mods_with_tablet_stylus_binds(mod_key, &config_.binds);
let screenshot_ui = ScreenshotUi::new(animation_clock.clone(), config.clone());
let window_mru_ui = WindowMruUi::new(config.clone());
@@ -2586,6 +2598,7 @@ impl Niri {
horizontal_wheel_tracker: ScrollTracker::new(120),
mods_with_mouse_binds,
mods_with_wheel_binds,
mods_with_tablet_stylus_binds,
// 10 is copied from Clutter: DISCRETE_SCROLL_STEP.
vertical_finger_scroll_tracker: ScrollTracker::new(10),
+1 -1
View File
@@ -200,7 +200,7 @@ fn refresh_workspace_group(protocol_state: &mut ExtWorkspaceManagerState, output
// Send workspace_enter for all existing workspaces on this output.
for group in &data.instances {
let manager: &ExtWorkspaceManagerV1 = group.data().unwrap();
for (_, ws) in protocol_state.workspaces.iter() {
for ws in protocol_state.workspaces.values() {
if ws.output.as_ref() != Some(output) {
continue;
}
+1 -1
View File
@@ -250,7 +250,7 @@ impl OutputManagementManagerState {
notify_new_head(self, output, conf);
}
}
for (old, _) in self.current_state.iter() {
for old in self.current_state.keys() {
if !new_state.contains_key(old) {
changed = true;
notify_removed_head(&mut self.clients, old);
+3
View File
@@ -560,6 +560,9 @@ fn key_name(screen_reader: bool, mod_key: ModKey, key: &Key) -> String {
Trigger::TouchpadScrollUp => String::from("Touchpad Scroll Up"),
Trigger::TouchpadScrollLeft => String::from("Touchpad Scroll Left"),
Trigger::TouchpadScrollRight => String::from("Touchpad Scroll Right"),
Trigger::TabletStylusButton1 => String::from("Tablet Stylus Button 1"),
Trigger::TabletStylusButton2 => String::from("Tablet Stylus Button 2"),
Trigger::TabletStylusButton3 => String::from("Tablet Stylus Button 3"),
};
name.push_str(&pretty);