mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Compare commits
21 Commits
v0.1.0-beta.1
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| dd967554d1 | |||
| 6d7c220137 | |||
| d77aac1afa | |||
| 837a0a20fb | |||
| ecdf756b55 | |||
| 73f3c160b2 | |||
| 5f99eb13ab | |||
| 20326b093c | |||
| 467d92a4b4 | |||
| 15bb69c0b9 | |||
| adfbfdffb3 | |||
| 087ed260c5 | |||
| f5642ab733 | |||
| ab9706cb30 | |||
| 05f2a3709b | |||
| 743173ef64 | |||
| cbbb7a26fc | |||
| 18566e3366 | |||
| df48337d83 | |||
| f5e9b40140 | |||
| 5cacd03e85 |
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Report a bug or a crash
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Please describe the issue here at the top, then fill in the system information below. -->
|
||||||
|
|
||||||
|
### System Information
|
||||||
|
|
||||||
|
<!-- Paste the output of `niri -V`, e.g. niri 0.1.0-beta.1 (v0.1.0-beta.1) -->
|
||||||
|
* niri version:
|
||||||
|
|
||||||
|
<!-- Write your GPU vendor and model, e.g. AMD RX 6700M -->
|
||||||
|
* GPU:
|
||||||
|
|
||||||
|
<!-- Write your CPU vendor and model, e.g. AMD Ryzen 7 6800H -->
|
||||||
|
* CPU:
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
contact_links:
|
||||||
|
- name: Feature request
|
||||||
|
url: https://github.com/YaLTeR/niri/discussions/new?category=ideas
|
||||||
|
about: Ideas for new features and functionality (start a Discussion)
|
||||||
Generated
+49
-38
@@ -1406,25 +1406,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "input"
|
name = "input"
|
||||||
version = "0.8.3"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059"
|
checksum = "7911ce3db9c10c5ab4a35c49af778a5f9a827bd0f7371d9be56175d8dd2740d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 2.4.2",
|
||||||
"input-sys",
|
"input-sys",
|
||||||
"io-lifetimes 1.0.11",
|
"io-lifetimes 1.0.11",
|
||||||
"libc",
|
"libc",
|
||||||
"udev 0.7.0",
|
"udev",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "input-sys"
|
name = "input-sys"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2"
|
checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0"
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
@@ -1845,7 +1842,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "niri"
|
name = "niri"
|
||||||
version = "0.1.0-beta.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
@@ -1886,7 +1883,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "niri-config"
|
name = "niri-config"
|
||||||
version = "0.1.0-beta.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"knuffel",
|
"knuffel",
|
||||||
@@ -1898,7 +1895,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "niri-ipc"
|
name = "niri-ipc"
|
||||||
version = "0.1.0-beta.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -1974,7 +1971,7 @@ version = "0.7.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate 2.0.1",
|
"proc-macro-crate 1.3.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
@@ -2125,6 +2122,12 @@ version = "2.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -2188,6 +2191,24 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pixman"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d24a24da0bec14f4e43a495c1837a3c358b87532e7fe66bd75c348b89f0451b6"
|
||||||
|
dependencies = [
|
||||||
|
"drm-fourcc",
|
||||||
|
"paste",
|
||||||
|
"pixman-sys",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pixman-sys"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1a0483e89e81d7915defe83c51f23f6800594d64f6f4a21253ce87fd8444ada"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.29"
|
version = "0.3.29"
|
||||||
@@ -2301,9 +2322,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.76"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -2459,13 +2480,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.2"
|
version = "1.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.3",
|
"regex-automata 0.4.4",
|
||||||
"regex-syntax 0.8.2",
|
"regex-syntax 0.8.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2480,9 +2501,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -2658,9 +2679,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
@@ -2688,14 +2709,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.0"
|
version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b187f0231d56fe41bfb12034819dd2bf336422a5866de41bc3fec4b2e3883e8"
|
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/Smithay/smithay.git#b7284bc6ca6afc782bd55a5c34ef3f902005951f"
|
source = "git+https://github.com/Smithay/smithay.git#8854dee7c2f49e9077f10d484b0de9a8e81c587c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
@@ -2717,6 +2738,7 @@ dependencies = [
|
|||||||
"libloading",
|
"libloading",
|
||||||
"libseat",
|
"libseat",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"pixman",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"profiling",
|
"profiling",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -2726,7 +2748,7 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"udev 0.8.0",
|
"udev",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
"wayland-egl",
|
"wayland-egl",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
@@ -2766,7 +2788,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay-drm-extras"
|
name = "smithay-drm-extras"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/Smithay/smithay.git#b7284bc6ca6afc782bd55a5c34ef3f902005951f"
|
source = "git+https://github.com/Smithay/smithay.git#8854dee7c2f49e9077f10d484b0de9a8e81c587c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"drm",
|
"drm",
|
||||||
"edid-rs",
|
"edid-rs",
|
||||||
@@ -3062,17 +3084,6 @@ version = "1.17.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "udev"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"libudev-sys",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "udev"
|
name = "udev"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|||||||
+5
-4
@@ -1,5 +1,5 @@
|
|||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0-beta.1"
|
version = "0.1.0"
|
||||||
description = "A scrollable-tiling Wayland compositor"
|
description = "A scrollable-tiling Wayland compositor"
|
||||||
authors = ["Ivan Molodetskikh <yalterz@gmail.com>"]
|
authors = ["Ivan Molodetskikh <yalterz@gmail.com>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
@@ -49,8 +49,8 @@ keyframe = { version = "1.1.1", default-features = false }
|
|||||||
libc = "0.2.152"
|
libc = "0.2.152"
|
||||||
log = { version = "0.4.20", features = ["max_level_trace", "release_max_level_debug"] }
|
log = { version = "0.4.20", features = ["max_level_trace", "release_max_level_debug"] }
|
||||||
logind-zbus = { version = "3.1.2", optional = true }
|
logind-zbus = { version = "3.1.2", optional = true }
|
||||||
niri-config = { version = "0.1.0-beta.1", path = "niri-config" }
|
niri-config = { version = "0.1.0", path = "niri-config" }
|
||||||
niri-ipc = { version = "0.1.0-beta.1", path = "niri-ipc" }
|
niri-ipc = { version = "0.1.0", path = "niri-ipc" }
|
||||||
notify-rust = { version = "4.10.0", optional = true }
|
notify-rust = { version = "4.10.0", optional = true }
|
||||||
pangocairo = "0.18.0"
|
pangocairo = "0.18.0"
|
||||||
pipewire = { version = "0.7.2", optional = true }
|
pipewire = { version = "0.7.2", optional = true }
|
||||||
@@ -80,6 +80,7 @@ features = [
|
|||||||
"backend_winit",
|
"backend_winit",
|
||||||
"desktop",
|
"desktop",
|
||||||
"renderer_gl",
|
"renderer_gl",
|
||||||
|
"renderer_pixman",
|
||||||
"renderer_multi",
|
"renderer_multi",
|
||||||
"use_system_lib",
|
"use_system_lib",
|
||||||
"wayland_frontend",
|
"wayland_frontend",
|
||||||
@@ -108,7 +109,7 @@ lto = "thin"
|
|||||||
debug = false
|
debug = false
|
||||||
|
|
||||||
[package.metadata.generate-rpm]
|
[package.metadata.generate-rpm]
|
||||||
version = "0.1.0~beta.1"
|
version = "0.1.0"
|
||||||
assets = [
|
assets = [
|
||||||
{ source = "target/release/niri", dest = "/usr/bin/", mode = "755" },
|
{ source = "target/release/niri", dest = "/usr/bin/", mode = "755" },
|
||||||
{ source = "resources/niri-session", dest = "/usr/bin/", mode = "755" },
|
{ source = "resources/niri-session", dest = "/usr/bin/", mode = "755" },
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ Opening a new window never causes existing windows to resize.
|
|||||||
Every monitor has its own separate window strip.
|
Every monitor has its own separate window strip.
|
||||||
Windows can never "overflow" onto an adjacent monitor.
|
Windows can never "overflow" onto an adjacent monitor.
|
||||||
|
|
||||||
Since windows go left-to-right horizontally, workspaces are arranged vertically.
|
Workspaces are dynamic and arranged vertically.
|
||||||
Every monitor has an independent set of workspaces, and there's always one empty workspace present all the way down.
|
Every monitor has an independent set of workspaces, and there's always one empty workspace present all the way down.
|
||||||
|
|
||||||
|
The workspace arrangement is preserved across disconnecting and connecting monitors where it makes sense.
|
||||||
|
When a monitor disconnects, its workspaces will move to another monitor, but upon reconnection they will move back to the original monitor.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Scrollable tiling
|
- Scrollable tiling
|
||||||
@@ -29,13 +32,17 @@ Every monitor has an independent set of workspaces, and there's always one empty
|
|||||||
- Configurable layout: gaps, borders, struts, window sizes
|
- Configurable layout: gaps, borders, struts, window sizes
|
||||||
- Live-reloading config
|
- Live-reloading config
|
||||||
|
|
||||||
|
## Video Demo
|
||||||
|
|
||||||
|
https://github.com/YaLTeR/niri/assets/1794388/5d355694-7b06-4f00-8920-8dce54a8721c
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
A lot of the essential functionality is implemented, plus some goodies on top.
|
A lot of the essential functionality is implemented, plus some goodies on top.
|
||||||
Feel free to give niri a try.
|
Feel free to give niri a try.
|
||||||
Have your waybars and fuzzels ready: niri is not a complete desktop environment.
|
Have your [waybar]s and [fuzzel]s ready: niri is not a complete desktop environment.
|
||||||
|
|
||||||
https://github.com/YaLTeR/niri/assets/1794388/5d355694-7b06-4f00-8920-8dce54a8721c
|
Note that NVIDIA GPUs might have rendering issues.
|
||||||
|
|
||||||
## Inspiration
|
## Inspiration
|
||||||
|
|
||||||
@@ -44,15 +51,14 @@ Niri is heavily inspired by [PaperWM] which implements scrollable tiling on top
|
|||||||
One of the reasons that prompted me to try writing my own compositor is being able to properly separate the monitors.
|
One of the reasons that prompted me to try writing my own compositor is being able to properly separate the monitors.
|
||||||
Being a GNOME Shell extension, PaperWM has to work against Shell's global window coordinate space to prevent windows from overflowing.
|
Being a GNOME Shell extension, PaperWM has to work against Shell's global window coordinate space to prevent windows from overflowing.
|
||||||
|
|
||||||
Niri tries to preserve the workspace arrangement as much as possible upon disconnecting and connecting monitors.
|
|
||||||
When a monitor disconnects, its workspaces will move to another monitor, but upon reconnection they will move back to the original monitor.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> For Fedora users, there's a COPR with built and packaged niri: https://copr.fedorainfracloud.org/coprs/yalter/niri/
|
> For Fedora users, there's a COPR with built and packaged niri: https://copr.fedorainfracloud.org/coprs/yalter/niri/
|
||||||
>
|
>
|
||||||
> For NixOS users, check out https://github.com/sodiboo/niri-flake
|
> NixOS users, check out https://github.com/sodiboo/niri-flake
|
||||||
|
>
|
||||||
|
> For Arch users, there's an AUR package: https://aur.archlinux.org/packages/niri
|
||||||
|
|
||||||
First, install the dependencies for your distribution.
|
First, install the dependencies for your distribution.
|
||||||
|
|
||||||
@@ -71,7 +77,9 @@ First, install the dependencies for your distribution.
|
|||||||
sudo dnf install gcc libudev-devel libgbm-devel libxkbcommon-devel wayland-devel libinput-devel dbus-devel systemd-devel libseat-devel pipewire-devel pango-devel cairo-gobject-devel clang
|
sudo dnf install gcc libudev-devel libgbm-devel libxkbcommon-devel wayland-devel libinput-devel dbus-devel systemd-devel libseat-devel pipewire-devel pango-devel cairo-gobject-devel clang
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, build niri with `cargo build --release`.
|
Next, get latest stable Rust: https://rustup.rs/
|
||||||
|
|
||||||
|
Then, build niri with `cargo build --release`.
|
||||||
|
|
||||||
### NixOS/Nix
|
### NixOS/Nix
|
||||||
|
|
||||||
@@ -202,4 +210,6 @@ We have a Matrix chat, feel free to join and ask a question: https://matrix.to/#
|
|||||||
[PaperWM]: https://github.com/paperwm/PaperWM
|
[PaperWM]: https://github.com/paperwm/PaperWM
|
||||||
[mako]: https://github.com/emersion/mako
|
[mako]: https://github.com/emersion/mako
|
||||||
[OBS]: https://flathub.org/apps/com.obsproject.Studio
|
[OBS]: https://flathub.org/apps/com.obsproject.Studio
|
||||||
|
[waybar]: https://github.com/Alexays/Waybar
|
||||||
|
[fuzzel]: https://codeberg.org/dnkl/fuzzel
|
||||||
|
|
||||||
|
|||||||
@@ -117,9 +117,9 @@ layout {
|
|||||||
// Proportion sets the width as a fraction of the output width, taking gaps into account.
|
// 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.
|
// 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.
|
// The default preset widths are 1/3, 1/2 and 2/3 of the output.
|
||||||
proportion 0.333
|
proportion 0.33333
|
||||||
proportion 0.5
|
proportion 0.5
|
||||||
proportion 0.667
|
proportion 0.66667
|
||||||
|
|
||||||
// Fixed sets the width in logical pixels exactly.
|
// Fixed sets the width in logical pixels exactly.
|
||||||
// fixed 1920
|
// fixed 1920
|
||||||
|
|||||||
+40
-61
@@ -20,7 +20,7 @@ use smithay::backend::drm::{
|
|||||||
use smithay::backend::egl::context::ContextPriority;
|
use smithay::backend::egl::context::ContextPriority;
|
||||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||||
use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};
|
use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};
|
||||||
use smithay::backend::renderer::gles::{Capability, GlesRenderer, GlesTexture};
|
use smithay::backend::renderer::gles::{Capability, GlesRenderer};
|
||||||
use smithay::backend::renderer::multigpu::gbm::GbmGlesBackend;
|
use smithay::backend::renderer::multigpu::gbm::GbmGlesBackend;
|
||||||
use smithay::backend::renderer::multigpu::{GpuManager, MultiFrame, MultiRenderer};
|
use smithay::backend::renderer::multigpu::{GpuManager, MultiFrame, MultiRenderer};
|
||||||
use smithay::backend::renderer::{DebugFlags, ImportDma, ImportEgl, Renderer};
|
use smithay::backend::renderer::{DebugFlags, ImportDma, ImportEgl, Renderer};
|
||||||
@@ -74,6 +74,8 @@ pub struct Tty {
|
|||||||
// The allocator for the primary GPU. It is only `Some()` if we have a device corresponding to
|
// The allocator for the primary GPU. It is only `Some()` if we have a device corresponding to
|
||||||
// the primary GPU.
|
// the primary GPU.
|
||||||
primary_allocator: Option<DmabufAllocator<GbmAllocator<DrmDeviceFd>>>,
|
primary_allocator: Option<DmabufAllocator<GbmAllocator<DrmDeviceFd>>>,
|
||||||
|
// The output config had changed, but the session is paused, so we need to update it on resume.
|
||||||
|
update_output_config_on_resume: bool,
|
||||||
ipc_outputs: Rc<RefCell<HashMap<String, niri_ipc::Output>>>,
|
ipc_outputs: Rc<RefCell<HashMap<String, niri_ipc::Output>>>,
|
||||||
enabled_outputs: Arc<Mutex<HashMap<String, Output>>>,
|
enabled_outputs: Arc<Mutex<HashMap<String, Output>>>,
|
||||||
}
|
}
|
||||||
@@ -222,6 +224,7 @@ impl Tty {
|
|||||||
devices: HashMap::new(),
|
devices: HashMap::new(),
|
||||||
dmabuf_global: None,
|
dmabuf_global: None,
|
||||||
primary_allocator: None,
|
primary_allocator: None,
|
||||||
|
update_output_config_on_resume: false,
|
||||||
ipc_outputs: Rc::new(RefCell::new(HashMap::new())),
|
ipc_outputs: Rc::new(RefCell::new(HashMap::new())),
|
||||||
enabled_outputs: Arc::new(Mutex::new(HashMap::new())),
|
enabled_outputs: Arc::new(Mutex::new(HashMap::new())),
|
||||||
}
|
}
|
||||||
@@ -277,7 +280,7 @@ impl Tty {
|
|||||||
|
|
||||||
self.libinput.suspend();
|
self.libinput.suspend();
|
||||||
|
|
||||||
for device in self.devices.values() {
|
for device in self.devices.values_mut() {
|
||||||
device.drm.pause();
|
device.drm.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,47 +323,13 @@ impl Tty {
|
|||||||
device_list.remove(&node.dev_id());
|
device_list.remove(&node.dev_id());
|
||||||
|
|
||||||
// It hasn't been removed, update its state as usual.
|
// It hasn't been removed, update its state as usual.
|
||||||
let device = &self.devices[&node];
|
|
||||||
device.drm.activate();
|
|
||||||
|
|
||||||
// HACK: force reset the connectors to make resuming work across sleep.
|
|
||||||
let device = &self.devices[&node];
|
|
||||||
let crtcs: Vec<_> = device
|
|
||||||
.drm_scanner
|
|
||||||
.crtcs()
|
|
||||||
.map(|(_conn, crtc)| crtc)
|
|
||||||
.collect();
|
|
||||||
for crtc in crtcs {
|
|
||||||
self.connector_disconnected(niri, node, crtc);
|
|
||||||
}
|
|
||||||
|
|
||||||
let device = self.devices.get_mut(&node).unwrap();
|
let device = self.devices.get_mut(&node).unwrap();
|
||||||
let _ = device.drm_scanner.scan_connectors(&device.drm);
|
if let Err(err) = device.drm.activate(true) {
|
||||||
let crtcs: Vec<_> = device
|
warn!("error activating DRM device: {err:?}");
|
||||||
.drm_scanner
|
|
||||||
.crtcs()
|
|
||||||
.map(|(conn, crtc)| (conn.clone(), crtc))
|
|
||||||
.collect();
|
|
||||||
for (conn, crtc) in crtcs {
|
|
||||||
if let Err(err) = self.connector_connected(niri, node, conn, crtc) {
|
|
||||||
warn!("error connecting connector: {err:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Refresh the connectors.
|
// Refresh the connectors.
|
||||||
// self.device_changed(node.dev_id(), niri);
|
self.device_changed(node.dev_id(), niri);
|
||||||
|
|
||||||
// // Refresh the state on unchanged connectors.
|
|
||||||
// let device = self.devices.get_mut(&node).unwrap();
|
|
||||||
// for surface in device.surfaces.values_mut() {
|
|
||||||
// let compositor = &mut surface.compositor;
|
|
||||||
// if let Err(err) = compositor.surface().reset_state() {
|
|
||||||
// warn!("error resetting DRM surface state: {err}");
|
|
||||||
// }
|
|
||||||
// compositor.reset_buffers();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// niri.queue_redraw_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new devices.
|
// Add new devices.
|
||||||
@@ -370,7 +339,13 @@ impl Tty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.update_output_config_on_resume {
|
||||||
|
self.on_output_config_changed(niri);
|
||||||
|
}
|
||||||
|
|
||||||
self.refresh_ipc_outputs();
|
self.refresh_ipc_outputs();
|
||||||
|
|
||||||
|
niri.queue_redraw_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,15 +367,9 @@ impl Tty {
|
|||||||
let (drm, drm_notifier) = DrmDevice::new(device_fd.clone(), true)?;
|
let (drm, drm_notifier) = DrmDevice::new(device_fd.clone(), true)?;
|
||||||
let gbm = GbmDevice::new(device_fd)?;
|
let gbm = GbmDevice::new(device_fd)?;
|
||||||
|
|
||||||
let display = EGLDisplay::new(gbm.clone())?;
|
let display = unsafe { EGLDisplay::new(gbm.clone())? };
|
||||||
let egl_device = EGLDevice::device_for_display(&display)?;
|
let egl_device = EGLDevice::device_for_display(&display)?;
|
||||||
|
|
||||||
// HACK: There's an issue in Smithay where the display created by GpuManager will be the
|
|
||||||
// same as the one we just created here, so when ours is dropped at the end of the scope,
|
|
||||||
// it will also close the long-lived display in GpuManager. Thus, we need to drop ours
|
|
||||||
// beforehand.
|
|
||||||
drop(display);
|
|
||||||
|
|
||||||
let render_node = egl_device
|
let render_node = egl_device
|
||||||
.try_get_render_node()?
|
.try_get_render_node()?
|
||||||
.context("no render node")?;
|
.context("no render node")?;
|
||||||
@@ -735,13 +704,8 @@ impl Tty {
|
|||||||
let sequence_delta_plot_name =
|
let sequence_delta_plot_name =
|
||||||
tracy_client::PlotName::new_leak(format!("{output_name} sequence delta"));
|
tracy_client::PlotName::new_leak(format!("{output_name} sequence delta"));
|
||||||
|
|
||||||
self.enabled_outputs
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.insert(output_name.clone(), output.clone());
|
|
||||||
|
|
||||||
let surface = Surface {
|
let surface = Surface {
|
||||||
name: output_name,
|
name: output_name.clone(),
|
||||||
compositor,
|
compositor,
|
||||||
dmabuf_feedback,
|
dmabuf_feedback,
|
||||||
vblank_frame: None,
|
vblank_frame: None,
|
||||||
@@ -755,6 +719,13 @@ impl Tty {
|
|||||||
|
|
||||||
niri.add_output(output.clone(), Some(refresh_interval(mode)));
|
niri.add_output(output.clone(), Some(refresh_interval(mode)));
|
||||||
|
|
||||||
|
self.enabled_outputs
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(output_name, output.clone());
|
||||||
|
#[cfg(feature = "dbus")]
|
||||||
|
niri.on_enabled_outputs_changed();
|
||||||
|
|
||||||
// Power on all monitors if necessary and queue a redraw on the new one.
|
// Power on all monitors if necessary and queue a redraw on the new one.
|
||||||
niri.event_loop.insert_idle(move |state| {
|
niri.event_loop.insert_idle(move |state| {
|
||||||
state.niri.activate_monitors(&state.backend);
|
state.niri.activate_monitors(&state.backend);
|
||||||
@@ -794,6 +765,8 @@ impl Tty {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.enabled_outputs.lock().unwrap().remove(&surface.name);
|
self.enabled_outputs.lock().unwrap().remove(&surface.name);
|
||||||
|
#[cfg(feature = "dbus")]
|
||||||
|
niri.on_enabled_outputs_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_vblank(
|
fn on_vblank(
|
||||||
@@ -1039,7 +1012,7 @@ impl Tty {
|
|||||||
|
|
||||||
// Hand them over to the DRM.
|
// Hand them over to the DRM.
|
||||||
let drm_compositor = &mut surface.compositor;
|
let drm_compositor = &mut surface.compositor;
|
||||||
match drm_compositor.render_frame::<_, _, GlesTexture>(&mut renderer, &elements, [0.; 4]) {
|
match drm_compositor.render_frame::<_, _>(&mut renderer, &elements, [0.; 4]) {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
if self
|
if self
|
||||||
.config
|
.config
|
||||||
@@ -1234,6 +1207,13 @@ impl Tty {
|
|||||||
pub fn on_output_config_changed(&mut self, niri: &mut Niri) {
|
pub fn on_output_config_changed(&mut self, niri: &mut Niri) {
|
||||||
let _span = tracy_client::span!("Tty::on_output_config_changed");
|
let _span = tracy_client::span!("Tty::on_output_config_changed");
|
||||||
|
|
||||||
|
// If we're inactive, we can't do anything, so just set a flag for later.
|
||||||
|
if !self.session.is_active() {
|
||||||
|
self.update_output_config_on_resume = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.update_output_config_on_resume = false;
|
||||||
|
|
||||||
let mut to_disconnect = vec![];
|
let mut to_disconnect = vec![];
|
||||||
let mut to_connect = vec![];
|
let mut to_connect = vec![];
|
||||||
|
|
||||||
@@ -1255,12 +1235,11 @@ impl Tty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to change the mode.
|
// Check if we need to change the mode.
|
||||||
let connector = surface
|
let Some(connector) = surface.compositor.pending_connectors().into_iter().next()
|
||||||
.compositor
|
else {
|
||||||
.current_connectors()
|
error!("surface pending connectors is empty");
|
||||||
.into_iter()
|
continue;
|
||||||
.next()
|
};
|
||||||
.unwrap();
|
|
||||||
let Some(connector) = device.drm_scanner.connectors().get(&connector) else {
|
let Some(connector) = device.drm_scanner.connectors().get(&connector) else {
|
||||||
error!("missing enabled connector in drm_scanner");
|
error!("missing enabled connector in drm_scanner");
|
||||||
continue;
|
continue;
|
||||||
@@ -1271,7 +1250,7 @@ impl Tty {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if surface.compositor.current_mode() == mode {
|
if surface.compositor.pending_mode() == mode {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ fn render(scale: i32) -> anyhow::Result<MemoryRenderBuffer> {
|
|||||||
drop(cr);
|
drop(cr);
|
||||||
|
|
||||||
let data = surface.take_data().unwrap();
|
let data = surface.take_data().unwrap();
|
||||||
let buffer = MemoryRenderBuffer::from_memory(
|
let buffer = MemoryRenderBuffer::from_slice(
|
||||||
&data,
|
&data,
|
||||||
Fourcc::Argb8888,
|
Fourcc::Argb8888,
|
||||||
(width, height),
|
(width, height),
|
||||||
|
|||||||
+6
-20
@@ -8,8 +8,7 @@ use std::sync::Mutex;
|
|||||||
|
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use smithay::backend::allocator::Fourcc;
|
use smithay::backend::allocator::Fourcc;
|
||||||
use smithay::backend::renderer::element::texture::TextureBuffer;
|
use smithay::backend::renderer::element::memory::MemoryRenderBuffer;
|
||||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
|
|
||||||
use smithay::input::pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus};
|
use smithay::input::pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus};
|
||||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
use smithay::utils::{IsAlive, Logical, Physical, Point, Transform};
|
use smithay::utils::{IsAlive, Logical, Physical, Point, Transform};
|
||||||
@@ -224,7 +223,7 @@ pub enum RenderCursor {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextureCache = HashMap<(CursorIcon, i32), Vec<Option<TextureBuffer<GlesTexture>>>>;
|
type TextureCache = HashMap<(CursorIcon, i32), Vec<MemoryRenderBuffer>>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CursorTextureCache {
|
pub struct CursorTextureCache {
|
||||||
@@ -238,12 +237,11 @@ impl CursorTextureCache {
|
|||||||
|
|
||||||
pub fn get(
|
pub fn get(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut GlesRenderer,
|
|
||||||
icon: CursorIcon,
|
icon: CursorIcon,
|
||||||
scale: i32,
|
scale: i32,
|
||||||
cursor: &XCursor,
|
cursor: &XCursor,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> Option<TextureBuffer<GlesTexture>> {
|
) -> MemoryRenderBuffer {
|
||||||
self.cache
|
self.cache
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.entry((icon, scale))
|
.entry((icon, scale))
|
||||||
@@ -252,26 +250,14 @@ impl CursorTextureCache {
|
|||||||
.frames()
|
.frames()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|frame| {
|
.map(|frame| {
|
||||||
let _span = tracy_client::span!("create TextureBuffer");
|
MemoryRenderBuffer::from_slice(
|
||||||
|
|
||||||
let buffer = TextureBuffer::from_memory(
|
|
||||||
renderer,
|
|
||||||
&frame.pixels_rgba,
|
&frame.pixels_rgba,
|
||||||
Fourcc::Abgr8888,
|
Fourcc::Argb8888,
|
||||||
(frame.width as i32, frame.height as i32),
|
(frame.width as i32, frame.height as i32),
|
||||||
false,
|
|
||||||
scale,
|
scale,
|
||||||
Transform::Normal,
|
Transform::Normal,
|
||||||
None,
|
None,
|
||||||
);
|
)
|
||||||
|
|
||||||
match buffer {
|
|
||||||
Ok(x) => Some(x),
|
|
||||||
Err(err) => {
|
|
||||||
warn!("error creating a cursor texture: {err:?}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})[idx]
|
})[idx]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use serde::Serialize;
|
|||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
use zbus::fdo::RequestNameFlags;
|
use zbus::fdo::RequestNameFlags;
|
||||||
use zbus::zvariant::{self, OwnedValue, Type};
|
use zbus::zvariant::{self, OwnedValue, Type};
|
||||||
use zbus::{dbus_interface, fdo};
|
use zbus::{dbus_interface, fdo, SignalContext};
|
||||||
|
|
||||||
use super::Start;
|
use super::Start;
|
||||||
|
|
||||||
@@ -112,7 +112,8 @@ impl DisplayConfig {
|
|||||||
Ok((0, monitors, logical_monitors, HashMap::new()))
|
Ok((0, monitors, logical_monitors, HashMap::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: monitors-changed signal.
|
#[dbus_interface(signal)]
|
||||||
|
pub async fn monitors_changed(ctxt: &SignalContext<'_>) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayConfig {
|
impl DisplayConfig {
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ use serde::Deserialize;
|
|||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
use smithay::reexports::calloop;
|
use smithay::reexports::calloop;
|
||||||
use zbus::fdo::RequestNameFlags;
|
use zbus::fdo::RequestNameFlags;
|
||||||
use zbus::zvariant::{DeserializeDict, OwnedObjectPath, Type, Value};
|
use zbus::zvariant::{DeserializeDict, OwnedObjectPath, SerializeDict, Type, Value};
|
||||||
use zbus::{dbus_interface, fdo, InterfaceRef, ObjectServer, SignalContext};
|
use zbus::{dbus_interface, fdo, InterfaceRef, ObjectServer, SignalContext};
|
||||||
|
|
||||||
use super::Start;
|
use super::Start;
|
||||||
|
use crate::utils::output_size;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ScreenCast {
|
pub struct ScreenCast {
|
||||||
@@ -54,6 +55,13 @@ pub struct Stream {
|
|||||||
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
|
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, SerializeDict, Type, Value)]
|
||||||
|
#[zvariant(signature = "dict")]
|
||||||
|
struct StreamParameters {
|
||||||
|
/// Size of the stream in logical coordinates.
|
||||||
|
size: (i32, i32),
|
||||||
|
}
|
||||||
|
|
||||||
pub enum ScreenCastToNiri {
|
pub enum ScreenCastToNiri {
|
||||||
StartCast {
|
StartCast {
|
||||||
session_id: usize,
|
session_id: usize,
|
||||||
@@ -195,6 +203,12 @@ impl Stream {
|
|||||||
#[dbus_interface(signal)]
|
#[dbus_interface(signal)]
|
||||||
pub async fn pipe_wire_stream_added(ctxt: &SignalContext<'_>, node_id: u32)
|
pub async fn pipe_wire_stream_added(ctxt: &SignalContext<'_>, node_id: u32)
|
||||||
-> zbus::Result<()>;
|
-> zbus::Result<()>;
|
||||||
|
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
async fn parameters(&self) -> StreamParameters {
|
||||||
|
let size = output_size(&self.output).into();
|
||||||
|
StreamParameters { size }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenCast {
|
impl ScreenCast {
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ fn render(scale: i32) -> anyhow::Result<MemoryRenderBuffer> {
|
|||||||
drop(cr);
|
drop(cr);
|
||||||
|
|
||||||
let data = surface.take_data().unwrap();
|
let data = surface.take_data().unwrap();
|
||||||
let buffer = MemoryRenderBuffer::from_memory(
|
let buffer = MemoryRenderBuffer::from_slice(
|
||||||
&data,
|
&data,
|
||||||
Fourcc::Argb8888,
|
Fourcc::Argb8888,
|
||||||
(width, height),
|
(width, height),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;
|
|||||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
use smithay::utils::{Logical, Rectangle, Serial};
|
use smithay::utils::{Logical, Rectangle, Serial};
|
||||||
use smithay::wayland::compositor::{send_surface_state, with_states};
|
use smithay::wayland::compositor::{send_surface_state, with_states};
|
||||||
|
use smithay::wayland::input_method::InputMethodSeat;
|
||||||
use smithay::wayland::shell::kde::decoration::{KdeDecorationHandler, KdeDecorationState};
|
use smithay::wayland::shell::kde::decoration::{KdeDecorationHandler, KdeDecorationState};
|
||||||
use smithay::wayland::shell::wlr_layer::Layer;
|
use smithay::wayland::shell::wlr_layer::Layer;
|
||||||
use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
|
use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
|
||||||
@@ -94,6 +95,15 @@ impl XdgShellHandler for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn grab(&mut self, surface: PopupSurface, _seat: WlSeat, serial: Serial) {
|
fn grab(&mut self, surface: PopupSurface, _seat: WlSeat, serial: Serial) {
|
||||||
|
// HACK: ignore grabs (pretend they work without actually grabbing) if the input method has
|
||||||
|
// a grab. It will likely need refactors in Smithay to support properly since grabs just
|
||||||
|
// replace each other.
|
||||||
|
// FIXME: do this properly.
|
||||||
|
if self.niri.seat.input_method().keyboard_grabbed() {
|
||||||
|
trace!("ignoring popup grab because IME has keyboard grabbed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let popup = PopupKind::Xdg(surface);
|
let popup = PopupKind::Xdg(surface);
|
||||||
let Ok(root) = find_popup_root_surface(&popup) else {
|
let Ok(root) = find_popup_root_surface(&popup) else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ fn render(config: &Config, comp_mod: CompositorMod, scale: i32) -> anyhow::Resul
|
|||||||
drop(cr);
|
drop(cr);
|
||||||
|
|
||||||
let data = surface.take_data().unwrap();
|
let data = surface.take_data().unwrap();
|
||||||
let buffer = MemoryRenderBuffer::from_memory(
|
let buffer = MemoryRenderBuffer::from_slice(
|
||||||
&data,
|
&data,
|
||||||
Fourcc::Argb8888,
|
Fourcc::Argb8888,
|
||||||
(width, height),
|
(width, height),
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ impl State {
|
|||||||
TouchUp { .. } => (),
|
TouchUp { .. } => (),
|
||||||
TouchCancel { .. } => (),
|
TouchCancel { .. } => (),
|
||||||
TouchFrame { .. } => (),
|
TouchFrame { .. } => (),
|
||||||
|
SwitchToggle { .. } => (),
|
||||||
Special(_) => (),
|
Special(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+139
-86
@@ -13,12 +13,12 @@ use anyhow::Context;
|
|||||||
use calloop::futures::Scheduler;
|
use calloop::futures::Scheduler;
|
||||||
use niri_config::{Config, TrackLayout};
|
use niri_config::{Config, TrackLayout};
|
||||||
use smithay::backend::allocator::Fourcc;
|
use smithay::backend::allocator::Fourcc;
|
||||||
|
use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
|
||||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||||
use smithay::backend::renderer::element::surface::{
|
use smithay::backend::renderer::element::surface::{
|
||||||
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
|
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
|
||||||
};
|
};
|
||||||
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
use smithay::backend::renderer::element::utils::{select_dmabuf_feedback, RelocateRenderElement};
|
||||||
use smithay::backend::renderer::element::utils::select_dmabuf_feedback;
|
|
||||||
use smithay::backend::renderer::element::{
|
use smithay::backend::renderer::element::{
|
||||||
default_primary_scanout_output_compare, AsRenderElements, Element, Id, Kind, RenderElement,
|
default_primary_scanout_output_compare, AsRenderElements, Element, Id, Kind, RenderElement,
|
||||||
RenderElementStates, UnderlyingStorage,
|
RenderElementStates, UnderlyingStorage,
|
||||||
@@ -66,7 +66,7 @@ use smithay::wayland::compositor::{
|
|||||||
};
|
};
|
||||||
use smithay::wayland::cursor_shape::CursorShapeManagerState;
|
use smithay::wayland::cursor_shape::CursorShapeManagerState;
|
||||||
use smithay::wayland::dmabuf::DmabufState;
|
use smithay::wayland::dmabuf::DmabufState;
|
||||||
use smithay::wayland::input_method::InputMethodManagerState;
|
use smithay::wayland::input_method::{InputMethodManagerState, InputMethodSeat};
|
||||||
use smithay::wayland::output::OutputManagerState;
|
use smithay::wayland::output::OutputManagerState;
|
||||||
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsState};
|
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsState};
|
||||||
use smithay::wayland::pointer_gestures::PointerGesturesState;
|
use smithay::wayland::pointer_gestures::PointerGesturesState;
|
||||||
@@ -90,9 +90,7 @@ use smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState;
|
|||||||
use crate::animation;
|
use crate::animation;
|
||||||
use crate::backend::tty::{SurfaceDmabufFeedback, TtyFrame, TtyRenderer, TtyRendererError};
|
use crate::backend::tty::{SurfaceDmabufFeedback, TtyFrame, TtyRenderer, TtyRendererError};
|
||||||
use crate::backend::{Backend, RenderResult, Tty, Winit};
|
use crate::backend::{Backend, RenderResult, Tty, Winit};
|
||||||
use crate::config_error_notification::{
|
use crate::config_error_notification::ConfigErrorNotification;
|
||||||
ConfigErrorNotification, ConfigErrorNotificationRenderElement,
|
|
||||||
};
|
|
||||||
use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};
|
use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};
|
||||||
#[cfg(feature = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri};
|
use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri};
|
||||||
@@ -106,7 +104,7 @@ use crate::input::{apply_libinput_settings, TabletData};
|
|||||||
use crate::ipc::server::IpcServer;
|
use crate::ipc::server::IpcServer;
|
||||||
use crate::layout::{Layout, MonitorRenderElement};
|
use crate::layout::{Layout, MonitorRenderElement};
|
||||||
use crate::pw_utils::{Cast, PipeWire};
|
use crate::pw_utils::{Cast, PipeWire};
|
||||||
use crate::render_helpers::{NiriRenderer, PrimaryGpuTextureRenderElement};
|
use crate::render_helpers::NiriRenderer;
|
||||||
use crate::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
use crate::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
center, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
|
center, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
|
||||||
@@ -326,7 +324,7 @@ impl State {
|
|||||||
self.niri.cursor_manager.check_cursor_image_surface_alive();
|
self.niri.cursor_manager.check_cursor_image_surface_alive();
|
||||||
self.niri.refresh_pointer_outputs();
|
self.niri.refresh_pointer_outputs();
|
||||||
self.niri.popups.cleanup();
|
self.niri.popups.cleanup();
|
||||||
self.niri.refresh_popup_grab();
|
self.refresh_popup_grab();
|
||||||
self.update_keyboard_focus();
|
self.update_keyboard_focus();
|
||||||
self.refresh_pointer_focus();
|
self.refresh_pointer_focus();
|
||||||
|
|
||||||
@@ -420,6 +418,27 @@ impl State {
|
|||||||
self.move_cursor(center(geo).to_f64());
|
self.move_cursor(center(geo).to_f64());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn refresh_popup_grab(&mut self) {
|
||||||
|
let keyboard_grabbed = self.niri.seat.input_method().keyboard_grabbed();
|
||||||
|
|
||||||
|
if let Some(grab) = &mut self.niri.popup_grab {
|
||||||
|
if grab.grab.has_ended() {
|
||||||
|
self.niri.popup_grab = None;
|
||||||
|
} else if keyboard_grabbed {
|
||||||
|
// HACK: remove popup grab if IME grabbed the keyboard, because we can't yet do
|
||||||
|
// popup grabs together with an IME grab.
|
||||||
|
// FIXME: do this properly.
|
||||||
|
grab.grab.ungrab(PopupUngrabStrategy::All);
|
||||||
|
self.niri.seat.get_pointer().unwrap().unset_grab(
|
||||||
|
self,
|
||||||
|
SERIAL_COUNTER.next_serial(),
|
||||||
|
get_monotonic_time().as_millis() as u32,
|
||||||
|
);
|
||||||
|
self.niri.popup_grab = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_keyboard_focus(&mut self) {
|
pub fn update_keyboard_focus(&mut self) {
|
||||||
let focus = if self.niri.is_locked() {
|
let focus = if self.niri.is_locked() {
|
||||||
self.niri.lock_surface_focus()
|
self.niri.lock_surface_focus()
|
||||||
@@ -1560,14 +1579,6 @@ impl Niri {
|
|||||||
state.lock_surface.as_ref().map(|s| s.wl_surface()).cloned()
|
state.lock_surface.as_ref().map(|s| s.wl_surface()).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_popup_grab(&mut self) {
|
|
||||||
if let Some(grab) = &self.popup_grab {
|
|
||||||
if grab.grab.has_ended() {
|
|
||||||
self.popup_grab = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedules an immediate redraw on all outputs if one is not already scheduled.
|
/// Schedules an immediate redraw on all outputs if one is not already scheduled.
|
||||||
pub fn queue_redraw_all(&mut self) {
|
pub fn queue_redraw_all(&mut self) {
|
||||||
let outputs: Vec<_> = self.output_state.keys().cloned().collect();
|
let outputs: Vec<_> = self.output_state.keys().cloned().collect();
|
||||||
@@ -1657,26 +1668,25 @@ impl Niri {
|
|||||||
let pointer_pos =
|
let pointer_pos =
|
||||||
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
|
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
|
||||||
|
|
||||||
let texture = self.cursor_texture_cache.get(
|
let texture = self.cursor_texture_cache.get(icon, scale, &cursor, idx);
|
||||||
renderer.as_gles_renderer(),
|
|
||||||
icon,
|
|
||||||
scale,
|
|
||||||
&cursor,
|
|
||||||
idx,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut pointer_elements = vec![];
|
let mut pointer_elements = vec![];
|
||||||
if let Some(texture) = texture {
|
let pointer_element = match MemoryRenderBufferRenderElement::from_buffer(
|
||||||
pointer_elements.push(OutputRenderElements::NamedPointer(
|
renderer,
|
||||||
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
|
pointer_pos.to_f64(),
|
||||||
pointer_pos.to_f64(),
|
&texture,
|
||||||
&texture,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
Kind::Cursor,
|
||||||
Kind::Cursor,
|
) {
|
||||||
)),
|
Ok(element) => Some(element),
|
||||||
));
|
Err(err) => {
|
||||||
|
warn!("error importing a cursor texture: {err:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(element) = pointer_element {
|
||||||
|
pointer_elements.push(OutputRenderElements::NamedPointer(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
(pointer_elements, pointer_pos)
|
(pointer_elements, pointer_pos)
|
||||||
@@ -2498,7 +2508,11 @@ impl Niri {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(default_output) = self.output_under_cursor() else {
|
let default_output = self
|
||||||
|
.output_under_cursor()
|
||||||
|
.or_else(|| self.layout.active_output().cloned())
|
||||||
|
.or_else(|| self.global_space.outputs().next().cloned());
|
||||||
|
let Some(default_output) = default_output else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2642,32 +2656,28 @@ impl Niri {
|
|||||||
include_pointer: bool,
|
include_pointer: bool,
|
||||||
on_done: impl FnOnce(PathBuf) + Send + 'static,
|
on_done: impl FnOnce(PathBuf) + Send + 'static,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
use std::cmp::max;
|
|
||||||
|
|
||||||
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
|
|
||||||
|
|
||||||
let _span = tracy_client::span!("Niri::screenshot_all_outputs");
|
let _span = tracy_client::span!("Niri::screenshot_all_outputs");
|
||||||
|
|
||||||
let mut elements = vec![];
|
|
||||||
let mut size = Size::from((0, 0));
|
|
||||||
|
|
||||||
let outputs: Vec<_> = self.global_space.outputs().cloned().collect();
|
let outputs: Vec<_> = self.global_space.outputs().cloned().collect();
|
||||||
for output in outputs {
|
|
||||||
let geom = self.global_space.output_geometry(&output).unwrap();
|
|
||||||
// FIXME: this does not work when outputs can have non-1 scale.
|
|
||||||
let geom = geom.to_physical(1);
|
|
||||||
|
|
||||||
size.w = max(size.w, geom.loc.x + geom.size.w);
|
// FIXME: support multiple outputs, needs fixing multi-scale handling and cropping.
|
||||||
size.h = max(size.h, geom.loc.y + geom.size.h);
|
anyhow::ensure!(outputs.len() == 1);
|
||||||
|
|
||||||
let output_elements = self.render::<GlesRenderer>(renderer, &output, include_pointer);
|
let output = outputs.into_iter().next().unwrap();
|
||||||
elements.extend(output_elements.into_iter().map(|elem| {
|
let geom = self.global_space.output_geometry(&output).unwrap();
|
||||||
RelocateRenderElement::from_element(elem, geom.loc, Relocate::Relative)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: scale.
|
let output_scale = output.current_scale().integer_scale();
|
||||||
let pixels = render_to_vec(renderer, size, Scale::from(1.), Fourcc::Abgr8888, &elements)?;
|
let geom = geom.to_physical(output_scale);
|
||||||
|
|
||||||
|
let size = geom.size;
|
||||||
|
let elements = self.render::<GlesRenderer>(renderer, &output, include_pointer);
|
||||||
|
let pixels = render_to_vec(
|
||||||
|
renderer,
|
||||||
|
size,
|
||||||
|
Scale::from(f64::from(output_scale)),
|
||||||
|
Fourcc::Abgr8888,
|
||||||
|
&elements,
|
||||||
|
)?;
|
||||||
|
|
||||||
let path = make_screenshot_path(&self.config.borrow())
|
let path = make_screenshot_path(&self.config.borrow())
|
||||||
.ok()
|
.ok()
|
||||||
@@ -2763,6 +2773,44 @@ impl Niri {
|
|||||||
constraint.activate();
|
constraint.activate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dbus")]
|
||||||
|
pub fn on_enabled_outputs_changed(&self) {
|
||||||
|
let _span = tracy_client::span!("Niri::on_enabled_outputs_changed");
|
||||||
|
|
||||||
|
let Some(dbus) = &self.dbus else { return };
|
||||||
|
let Some(conn_display_config) = dbus.conn_display_config.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = thread::Builder::new()
|
||||||
|
.name("DisplayConfig MonitorsChanged Emitter".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
|
use crate::dbus::mutter_display_config::DisplayConfig;
|
||||||
|
let _span = tracy_client::span!("MonitorsChanged");
|
||||||
|
let iface = match conn_display_config
|
||||||
|
.object_server()
|
||||||
|
.interface::<_, DisplayConfig>("/org/gnome/Mutter/DisplayConfig")
|
||||||
|
{
|
||||||
|
Ok(iface) => iface,
|
||||||
|
Err(err) => {
|
||||||
|
warn!("error getting DisplayConfig interface: {err:?}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async_io::block_on(async move {
|
||||||
|
if let Err(err) = DisplayConfig::monitors_changed(iface.signal_context()).await
|
||||||
|
{
|
||||||
|
warn!("error emitting MonitorsChanged: {err:?}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(err) = res {
|
||||||
|
warn!("error spawning a thread to send MonitorsChanged: {err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
@@ -2804,9 +2852,13 @@ fn render_to_texture(
|
|||||||
for element in elements.iter().rev() {
|
for element in elements.iter().rev() {
|
||||||
let src = element.src();
|
let src = element.src();
|
||||||
let dst = element.geometry(scale);
|
let dst = element.geometry(scale);
|
||||||
element
|
|
||||||
.draw(&mut frame, src, dst, &[output_rect])
|
if let Some(mut damage) = output_rect.intersection(dst) {
|
||||||
.context("error drawing element")?;
|
damage.loc -= dst.loc;
|
||||||
|
element
|
||||||
|
.draw(&mut frame, src, dst, &[damage])
|
||||||
|
.context("error drawing element")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sync_point = frame.finish().context("error finishing frame")?;
|
let sync_point = frame.finish().context("error finishing frame")?;
|
||||||
@@ -2869,9 +2921,13 @@ fn render_to_dmabuf(
|
|||||||
for element in elements.iter().rev() {
|
for element in elements.iter().rev() {
|
||||||
let src = element.src();
|
let src = element.src();
|
||||||
let dst = element.geometry(scale);
|
let dst = element.geometry(scale);
|
||||||
element
|
|
||||||
.draw(&mut frame, src, dst, &[output_rect])
|
if let Some(mut damage) = output_rect.intersection(dst) {
|
||||||
.context("error drawing element")?;
|
damage.loc -= dst.loc;
|
||||||
|
element
|
||||||
|
.draw(&mut frame, src, dst, &[damage])
|
||||||
|
.context("error drawing element")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _sync_point = frame.finish().context("error finishing frame")?;
|
let _sync_point = frame.finish().context("error finishing frame")?;
|
||||||
@@ -2884,10 +2940,11 @@ fn render_to_dmabuf(
|
|||||||
pub enum OutputRenderElements<R: NiriRenderer> {
|
pub enum OutputRenderElements<R: NiriRenderer> {
|
||||||
Monitor(MonitorRenderElement<R>),
|
Monitor(MonitorRenderElement<R>),
|
||||||
Wayland(WaylandSurfaceRenderElement<R>),
|
Wayland(WaylandSurfaceRenderElement<R>),
|
||||||
NamedPointer(PrimaryGpuTextureRenderElement),
|
NamedPointer(MemoryRenderBufferRenderElement<R>),
|
||||||
SolidColor(SolidColorRenderElement),
|
SolidColor(SolidColorRenderElement),
|
||||||
ScreenshotUi(ScreenshotUiRenderElement),
|
ScreenshotUi(ScreenshotUiRenderElement),
|
||||||
ConfigErrorNotification(ConfigErrorNotificationRenderElement<R>),
|
// Used for the CPU-rendered panels.
|
||||||
|
RelocatedMemoryBuffer(RelocateRenderElement<MemoryRenderBufferRenderElement<R>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
||||||
@@ -2898,7 +2955,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.id(),
|
Self::NamedPointer(elem) => elem.id(),
|
||||||
Self::SolidColor(elem) => elem.id(),
|
Self::SolidColor(elem) => elem.id(),
|
||||||
Self::ScreenshotUi(elem) => elem.id(),
|
Self::ScreenshotUi(elem) => elem.id(),
|
||||||
Self::ConfigErrorNotification(elem) => elem.id(),
|
Self::RelocatedMemoryBuffer(elem) => elem.id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2909,7 +2966,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.current_commit(),
|
Self::NamedPointer(elem) => elem.current_commit(),
|
||||||
Self::SolidColor(elem) => elem.current_commit(),
|
Self::SolidColor(elem) => elem.current_commit(),
|
||||||
Self::ScreenshotUi(elem) => elem.current_commit(),
|
Self::ScreenshotUi(elem) => elem.current_commit(),
|
||||||
Self::ConfigErrorNotification(elem) => elem.current_commit(),
|
Self::RelocatedMemoryBuffer(elem) => elem.current_commit(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2920,7 +2977,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.geometry(scale),
|
Self::NamedPointer(elem) => elem.geometry(scale),
|
||||||
Self::SolidColor(elem) => elem.geometry(scale),
|
Self::SolidColor(elem) => elem.geometry(scale),
|
||||||
Self::ScreenshotUi(elem) => elem.geometry(scale),
|
Self::ScreenshotUi(elem) => elem.geometry(scale),
|
||||||
Self::ConfigErrorNotification(elem) => elem.geometry(scale),
|
Self::RelocatedMemoryBuffer(elem) => elem.geometry(scale),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2931,7 +2988,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.transform(),
|
Self::NamedPointer(elem) => elem.transform(),
|
||||||
Self::SolidColor(elem) => elem.transform(),
|
Self::SolidColor(elem) => elem.transform(),
|
||||||
Self::ScreenshotUi(elem) => elem.transform(),
|
Self::ScreenshotUi(elem) => elem.transform(),
|
||||||
Self::ConfigErrorNotification(elem) => elem.transform(),
|
Self::RelocatedMemoryBuffer(elem) => elem.transform(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2942,7 +2999,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.src(),
|
Self::NamedPointer(elem) => elem.src(),
|
||||||
Self::SolidColor(elem) => elem.src(),
|
Self::SolidColor(elem) => elem.src(),
|
||||||
Self::ScreenshotUi(elem) => elem.src(),
|
Self::ScreenshotUi(elem) => elem.src(),
|
||||||
Self::ConfigErrorNotification(elem) => elem.src(),
|
Self::RelocatedMemoryBuffer(elem) => elem.src(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2957,7 +3014,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.damage_since(scale, commit),
|
Self::NamedPointer(elem) => elem.damage_since(scale, commit),
|
||||||
Self::SolidColor(elem) => elem.damage_since(scale, commit),
|
Self::SolidColor(elem) => elem.damage_since(scale, commit),
|
||||||
Self::ScreenshotUi(elem) => elem.damage_since(scale, commit),
|
Self::ScreenshotUi(elem) => elem.damage_since(scale, commit),
|
||||||
Self::ConfigErrorNotification(elem) => elem.damage_since(scale, commit),
|
Self::RelocatedMemoryBuffer(elem) => elem.damage_since(scale, commit),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2968,7 +3025,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.opaque_regions(scale),
|
Self::NamedPointer(elem) => elem.opaque_regions(scale),
|
||||||
Self::SolidColor(elem) => elem.opaque_regions(scale),
|
Self::SolidColor(elem) => elem.opaque_regions(scale),
|
||||||
Self::ScreenshotUi(elem) => elem.opaque_regions(scale),
|
Self::ScreenshotUi(elem) => elem.opaque_regions(scale),
|
||||||
Self::ConfigErrorNotification(elem) => elem.opaque_regions(scale),
|
Self::RelocatedMemoryBuffer(elem) => elem.opaque_regions(scale),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2979,7 +3036,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.alpha(),
|
Self::NamedPointer(elem) => elem.alpha(),
|
||||||
Self::SolidColor(elem) => elem.alpha(),
|
Self::SolidColor(elem) => elem.alpha(),
|
||||||
Self::ScreenshotUi(elem) => elem.alpha(),
|
Self::ScreenshotUi(elem) => elem.alpha(),
|
||||||
Self::ConfigErrorNotification(elem) => elem.alpha(),
|
Self::RelocatedMemoryBuffer(elem) => elem.alpha(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2990,7 +3047,7 @@ impl<R: NiriRenderer> Element for OutputRenderElements<R> {
|
|||||||
Self::NamedPointer(elem) => elem.kind(),
|
Self::NamedPointer(elem) => elem.kind(),
|
||||||
Self::SolidColor(elem) => elem.kind(),
|
Self::SolidColor(elem) => elem.kind(),
|
||||||
Self::ScreenshotUi(elem) => elem.kind(),
|
Self::ScreenshotUi(elem) => elem.kind(),
|
||||||
Self::ConfigErrorNotification(elem) => elem.kind(),
|
Self::RelocatedMemoryBuffer(elem) => elem.kind(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3015,7 +3072,7 @@ impl RenderElement<GlesRenderer> for OutputRenderElements<GlesRenderer> {
|
|||||||
Self::ScreenshotUi(elem) => {
|
Self::ScreenshotUi(elem) => {
|
||||||
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
|
||||||
}
|
}
|
||||||
Self::ConfigErrorNotification(elem) => elem.draw(frame, src, dst, damage),
|
Self::RelocatedMemoryBuffer(elem) => elem.draw(frame, src, dst, damage),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3026,7 +3083,7 @@ impl RenderElement<GlesRenderer> for OutputRenderElements<GlesRenderer> {
|
|||||||
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
|
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
|
||||||
Self::SolidColor(elem) => elem.underlying_storage(renderer),
|
Self::SolidColor(elem) => elem.underlying_storage(renderer),
|
||||||
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
|
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
|
||||||
Self::ConfigErrorNotification(elem) => elem.underlying_storage(renderer),
|
Self::RelocatedMemoryBuffer(elem) => elem.underlying_storage(renderer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3053,7 +3110,7 @@ impl<'render, 'alloc> RenderElement<TtyRenderer<'render, 'alloc>>
|
|||||||
Self::ScreenshotUi(elem) => {
|
Self::ScreenshotUi(elem) => {
|
||||||
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
RenderElement::<TtyRenderer<'render, 'alloc>>::draw(&elem, frame, src, dst, damage)
|
||||||
}
|
}
|
||||||
Self::ConfigErrorNotification(elem) => elem.draw(frame, src, dst, damage),
|
Self::RelocatedMemoryBuffer(elem) => elem.draw(frame, src, dst, damage),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3067,7 +3124,7 @@ impl<'render, 'alloc> RenderElement<TtyRenderer<'render, 'alloc>>
|
|||||||
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
|
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
|
||||||
Self::SolidColor(elem) => elem.underlying_storage(renderer),
|
Self::SolidColor(elem) => elem.underlying_storage(renderer),
|
||||||
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
|
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
|
||||||
Self::ConfigErrorNotification(elem) => elem.underlying_storage(renderer),
|
Self::RelocatedMemoryBuffer(elem) => elem.underlying_storage(renderer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3084,12 +3141,6 @@ impl<R: NiriRenderer> From<WaylandSurfaceRenderElement<R>> for OutputRenderEleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: NiriRenderer> From<PrimaryGpuTextureRenderElement> for OutputRenderElements<R> {
|
|
||||||
fn from(x: PrimaryGpuTextureRenderElement) -> Self {
|
|
||||||
Self::NamedPointer(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: NiriRenderer> From<SolidColorRenderElement> for OutputRenderElements<R> {
|
impl<R: NiriRenderer> From<SolidColorRenderElement> for OutputRenderElements<R> {
|
||||||
fn from(x: SolidColorRenderElement) -> Self {
|
fn from(x: SolidColorRenderElement) -> Self {
|
||||||
Self::SolidColor(x)
|
Self::SolidColor(x)
|
||||||
@@ -3102,8 +3153,10 @@ impl<R: NiriRenderer> From<ScreenshotUiRenderElement> for OutputRenderElements<R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: NiriRenderer> From<ConfigErrorNotificationRenderElement<R>> for OutputRenderElements<R> {
|
impl<R: NiriRenderer> From<RelocateRenderElement<MemoryRenderBufferRenderElement<R>>>
|
||||||
fn from(x: ConfigErrorNotificationRenderElement<R>) -> Self {
|
for OutputRenderElements<R>
|
||||||
Self::ConfigErrorNotification(x)
|
{
|
||||||
|
fn from(x: RelocateRenderElement<MemoryRenderBufferRenderElement<R>>) -> Self {
|
||||||
|
Self::RelocatedMemoryBuffer(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-4
@@ -27,7 +27,18 @@ impl Watcher {
|
|||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(format!("Filesystem Watcher for {}", path.to_string_lossy()))
|
.name(format!("Filesystem Watcher for {}", path.to_string_lossy()))
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let mut last_mtime = path.metadata().and_then(|meta| meta.modified()).ok();
|
// this "should" be as simple as mtime, but it does not quite work in practice;
|
||||||
|
// it doesn't work if the config is a symlink, and its target changes but the
|
||||||
|
// new target and old target have identical mtimes.
|
||||||
|
//
|
||||||
|
// in practice, this does not occur on any systems other than nix.
|
||||||
|
// because, on nix practically everything is a symlink to /nix/store
|
||||||
|
// and due to reproducibility, /nix/store keeps no mtime (= 1970-01-01)
|
||||||
|
// so, symlink targets change frequently when mtime doesn't.
|
||||||
|
let mut last_props = path
|
||||||
|
.canonicalize()
|
||||||
|
.and_then(|canon| Ok((canon.metadata()?.modified()?, canon)))
|
||||||
|
.ok();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(500));
|
||||||
@@ -36,8 +47,11 @@ impl Watcher {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(mtime) = path.metadata().and_then(|meta| meta.modified()) {
|
if let Ok(new_props) = path
|
||||||
if last_mtime != Some(mtime) {
|
.canonicalize()
|
||||||
|
.and_then(|canon| Ok((canon.metadata()?.modified()?, canon)))
|
||||||
|
{
|
||||||
|
if last_props.as_ref() != Some(&new_props) {
|
||||||
trace!("file changed: {}", path.to_string_lossy());
|
trace!("file changed: {}", path.to_string_lossy());
|
||||||
|
|
||||||
if let Err(err) = changed.send(()) {
|
if let Err(err) = changed.send(()) {
|
||||||
@@ -45,7 +59,7 @@ impl Watcher {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_mtime = Some(mtime);
|
last_props = Some(new_props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user