mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 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]]
|
||||
name = "input"
|
||||
version = "0.8.3"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059"
|
||||
checksum = "7911ce3db9c10c5ab4a35c49af778a5f9a827bd0f7371d9be56175d8dd2740d0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.2",
|
||||
"input-sys",
|
||||
"io-lifetimes 1.0.11",
|
||||
"libc",
|
||||
"udev 0.7.0",
|
||||
"udev",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "input-sys"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
@@ -1845,7 +1842,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri"
|
||||
version = "0.1.0-beta.1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
@@ -1886,7 +1883,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri-config"
|
||||
version = "0.1.0-beta.1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"knuffel",
|
||||
@@ -1898,7 +1895,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "niri-ipc"
|
||||
version = "0.1.0-beta.1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1974,7 +1971,7 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
dependencies = [
|
||||
"proc-macro-crate 2.0.1",
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
@@ -2125,6 +2122,12 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
@@ -2188,6 +2191,24 @@ dependencies = [
|
||||
"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]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.29"
|
||||
@@ -2301,9 +2322,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2459,13 +2480,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.3",
|
||||
"regex-automata 0.4.4",
|
||||
"regex-syntax 0.8.2",
|
||||
]
|
||||
|
||||
@@ -2480,9 +2501,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -2658,9 +2679,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
@@ -2688,14 +2709,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b187f0231d56fe41bfb12034819dd2bf336422a5866de41bc3fec4b2e3883e8"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/Smithay/smithay.git#b7284bc6ca6afc782bd55a5c34ef3f902005951f"
|
||||
source = "git+https://github.com/Smithay/smithay.git#8854dee7c2f49e9077f10d484b0de9a8e81c587c"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags 2.4.2",
|
||||
@@ -2717,6 +2738,7 @@ dependencies = [
|
||||
"libloading",
|
||||
"libseat",
|
||||
"once_cell",
|
||||
"pixman",
|
||||
"pkg-config",
|
||||
"profiling",
|
||||
"rand",
|
||||
@@ -2726,7 +2748,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"udev 0.8.0",
|
||||
"udev",
|
||||
"wayland-backend",
|
||||
"wayland-egl",
|
||||
"wayland-protocols",
|
||||
@@ -2766,7 +2788,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "smithay-drm-extras"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/Smithay/smithay.git#b7284bc6ca6afc782bd55a5c34ef3f902005951f"
|
||||
source = "git+https://github.com/Smithay/smithay.git#8854dee7c2f49e9077f10d484b0de9a8e81c587c"
|
||||
dependencies = [
|
||||
"drm",
|
||||
"edid-rs",
|
||||
@@ -3062,17 +3084,6 @@ version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "udev"
|
||||
version = "0.8.0"
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
[workspace.package]
|
||||
version = "0.1.0-beta.1"
|
||||
version = "0.1.0"
|
||||
description = "A scrollable-tiling Wayland compositor"
|
||||
authors = ["Ivan Molodetskikh <yalterz@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -49,8 +49,8 @@ keyframe = { version = "1.1.1", default-features = false }
|
||||
libc = "0.2.152"
|
||||
log = { version = "0.4.20", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
logind-zbus = { version = "3.1.2", optional = true }
|
||||
niri-config = { version = "0.1.0-beta.1", path = "niri-config" }
|
||||
niri-ipc = { version = "0.1.0-beta.1", path = "niri-ipc" }
|
||||
niri-config = { version = "0.1.0", path = "niri-config" }
|
||||
niri-ipc = { version = "0.1.0", path = "niri-ipc" }
|
||||
notify-rust = { version = "4.10.0", optional = true }
|
||||
pangocairo = "0.18.0"
|
||||
pipewire = { version = "0.7.2", optional = true }
|
||||
@@ -80,6 +80,7 @@ features = [
|
||||
"backend_winit",
|
||||
"desktop",
|
||||
"renderer_gl",
|
||||
"renderer_pixman",
|
||||
"renderer_multi",
|
||||
"use_system_lib",
|
||||
"wayland_frontend",
|
||||
@@ -108,7 +109,7 @@ lto = "thin"
|
||||
debug = false
|
||||
|
||||
[package.metadata.generate-rpm]
|
||||
version = "0.1.0~beta.1"
|
||||
version = "0.1.0"
|
||||
assets = [
|
||||
{ source = "target/release/niri", 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.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
- Live-reloading config
|
||||
|
||||
## Video Demo
|
||||
|
||||
https://github.com/YaLTeR/niri/assets/1794388/5d355694-7b06-4f00-8920-8dce54a8721c
|
||||
|
||||
## Status
|
||||
|
||||
A lot of the essential functionality is implemented, plus some goodies on top.
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
> [!TIP]
|
||||
> 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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
Next, build niri with `cargo build --release`.
|
||||
Next, get latest stable Rust: https://rustup.rs/
|
||||
|
||||
Then, build niri with `cargo build --release`.
|
||||
|
||||
### 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
|
||||
[mako]: https://github.com/emersion/mako
|
||||
[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.
|
||||
// For example, you can perfectly fit four windows sized "proportion 0.25" on an output.
|
||||
// The default preset widths are 1/3, 1/2 and 2/3 of the output.
|
||||
proportion 0.333
|
||||
proportion 0.33333
|
||||
proportion 0.5
|
||||
proportion 0.667
|
||||
proportion 0.66667
|
||||
|
||||
// Fixed sets the width in logical pixels exactly.
|
||||
// fixed 1920
|
||||
|
||||
+40
-61
@@ -20,7 +20,7 @@ use smithay::backend::drm::{
|
||||
use smithay::backend::egl::context::ContextPriority;
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
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::{GpuManager, MultiFrame, MultiRenderer};
|
||||
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 primary GPU.
|
||||
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>>>,
|
||||
enabled_outputs: Arc<Mutex<HashMap<String, Output>>>,
|
||||
}
|
||||
@@ -222,6 +224,7 @@ impl Tty {
|
||||
devices: HashMap::new(),
|
||||
dmabuf_global: None,
|
||||
primary_allocator: None,
|
||||
update_output_config_on_resume: false,
|
||||
ipc_outputs: Rc::new(RefCell::new(HashMap::new())),
|
||||
enabled_outputs: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
@@ -277,7 +280,7 @@ impl Tty {
|
||||
|
||||
self.libinput.suspend();
|
||||
|
||||
for device in self.devices.values() {
|
||||
for device in self.devices.values_mut() {
|
||||
device.drm.pause();
|
||||
}
|
||||
}
|
||||
@@ -320,47 +323,13 @@ impl Tty {
|
||||
device_list.remove(&node.dev_id());
|
||||
|
||||
// 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.drm_scanner.scan_connectors(&device.drm);
|
||||
let crtcs: Vec<_> = device
|
||||
.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:?}");
|
||||
}
|
||||
if let Err(err) = device.drm.activate(true) {
|
||||
warn!("error activating DRM device: {err:?}");
|
||||
}
|
||||
|
||||
// // Refresh the connectors.
|
||||
// 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();
|
||||
// Refresh the connectors.
|
||||
self.device_changed(node.dev_id(), niri);
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
niri.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,15 +367,9 @@ impl Tty {
|
||||
let (drm, drm_notifier) = DrmDevice::new(device_fd.clone(), true)?;
|
||||
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)?;
|
||||
|
||||
// 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
|
||||
.try_get_render_node()?
|
||||
.context("no render node")?;
|
||||
@@ -735,13 +704,8 @@ impl Tty {
|
||||
let sequence_delta_plot_name =
|
||||
tracy_client::PlotName::new_leak(format!("{output_name} sequence delta"));
|
||||
|
||||
self.enabled_outputs
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(output_name.clone(), output.clone());
|
||||
|
||||
let surface = Surface {
|
||||
name: output_name,
|
||||
name: output_name.clone(),
|
||||
compositor,
|
||||
dmabuf_feedback,
|
||||
vblank_frame: None,
|
||||
@@ -755,6 +719,13 @@ impl Tty {
|
||||
|
||||
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.
|
||||
niri.event_loop.insert_idle(move |state| {
|
||||
state.niri.activate_monitors(&state.backend);
|
||||
@@ -794,6 +765,8 @@ impl Tty {
|
||||
};
|
||||
|
||||
self.enabled_outputs.lock().unwrap().remove(&surface.name);
|
||||
#[cfg(feature = "dbus")]
|
||||
niri.on_enabled_outputs_changed();
|
||||
}
|
||||
|
||||
fn on_vblank(
|
||||
@@ -1039,7 +1012,7 @@ impl Tty {
|
||||
|
||||
// Hand them over to the DRM.
|
||||
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) => {
|
||||
if self
|
||||
.config
|
||||
@@ -1234,6 +1207,13 @@ impl Tty {
|
||||
pub fn on_output_config_changed(&mut self, niri: &mut Niri) {
|
||||
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_connect = vec![];
|
||||
|
||||
@@ -1255,12 +1235,11 @@ impl Tty {
|
||||
}
|
||||
|
||||
// Check if we need to change the mode.
|
||||
let connector = surface
|
||||
.compositor
|
||||
.current_connectors()
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap();
|
||||
let Some(connector) = surface.compositor.pending_connectors().into_iter().next()
|
||||
else {
|
||||
error!("surface pending connectors is empty");
|
||||
continue;
|
||||
};
|
||||
let Some(connector) = device.drm_scanner.connectors().get(&connector) else {
|
||||
error!("missing enabled connector in drm_scanner");
|
||||
continue;
|
||||
@@ -1271,7 +1250,7 @@ impl Tty {
|
||||
continue;
|
||||
};
|
||||
|
||||
if surface.compositor.current_mode() == mode {
|
||||
if surface.compositor.pending_mode() == mode {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ fn render(scale: i32) -> anyhow::Result<MemoryRenderBuffer> {
|
||||
drop(cr);
|
||||
|
||||
let data = surface.take_data().unwrap();
|
||||
let buffer = MemoryRenderBuffer::from_memory(
|
||||
let buffer = MemoryRenderBuffer::from_slice(
|
||||
&data,
|
||||
Fourcc::Argb8888,
|
||||
(width, height),
|
||||
|
||||
+6
-20
@@ -8,8 +8,7 @@ use std::sync::Mutex;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::texture::TextureBuffer;
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
|
||||
use smithay::backend::renderer::element::memory::MemoryRenderBuffer;
|
||||
use smithay::input::pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus};
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
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)]
|
||||
pub struct CursorTextureCache {
|
||||
@@ -238,12 +237,11 @@ impl CursorTextureCache {
|
||||
|
||||
pub fn get(
|
||||
&self,
|
||||
renderer: &mut GlesRenderer,
|
||||
icon: CursorIcon,
|
||||
scale: i32,
|
||||
cursor: &XCursor,
|
||||
idx: usize,
|
||||
) -> Option<TextureBuffer<GlesTexture>> {
|
||||
) -> MemoryRenderBuffer {
|
||||
self.cache
|
||||
.borrow_mut()
|
||||
.entry((icon, scale))
|
||||
@@ -252,26 +250,14 @@ impl CursorTextureCache {
|
||||
.frames()
|
||||
.iter()
|
||||
.map(|frame| {
|
||||
let _span = tracy_client::span!("create TextureBuffer");
|
||||
|
||||
let buffer = TextureBuffer::from_memory(
|
||||
renderer,
|
||||
MemoryRenderBuffer::from_slice(
|
||||
&frame.pixels_rgba,
|
||||
Fourcc::Abgr8888,
|
||||
Fourcc::Argb8888,
|
||||
(frame.width as i32, frame.height as i32),
|
||||
false,
|
||||
scale,
|
||||
Transform::Normal,
|
||||
None,
|
||||
);
|
||||
|
||||
match buffer {
|
||||
Ok(x) => Some(x),
|
||||
Err(err) => {
|
||||
warn!("error creating a cursor texture: {err:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})[idx]
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::Serialize;
|
||||
use smithay::output::Output;
|
||||
use zbus::fdo::RequestNameFlags;
|
||||
use zbus::zvariant::{self, OwnedValue, Type};
|
||||
use zbus::{dbus_interface, fdo};
|
||||
use zbus::{dbus_interface, fdo, SignalContext};
|
||||
|
||||
use super::Start;
|
||||
|
||||
@@ -112,7 +112,8 @@ impl DisplayConfig {
|
||||
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 {
|
||||
|
||||
@@ -7,10 +7,11 @@ use serde::Deserialize;
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::calloop;
|
||||
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 super::Start;
|
||||
use crate::utils::output_size;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScreenCast {
|
||||
@@ -54,6 +55,13 @@ pub struct Stream {
|
||||
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 {
|
||||
StartCast {
|
||||
session_id: usize,
|
||||
@@ -195,6 +203,12 @@ impl Stream {
|
||||
#[dbus_interface(signal)]
|
||||
pub async fn pipe_wire_stream_added(ctxt: &SignalContext<'_>, node_id: u32)
|
||||
-> zbus::Result<()>;
|
||||
|
||||
#[dbus_interface(property)]
|
||||
async fn parameters(&self) -> StreamParameters {
|
||||
let size = output_size(&self.output).into();
|
||||
StreamParameters { size }
|
||||
}
|
||||
}
|
||||
|
||||
impl ScreenCast {
|
||||
|
||||
@@ -149,7 +149,7 @@ fn render(scale: i32) -> anyhow::Result<MemoryRenderBuffer> {
|
||||
drop(cr);
|
||||
|
||||
let data = surface.take_data().unwrap();
|
||||
let buffer = MemoryRenderBuffer::from_memory(
|
||||
let buffer = MemoryRenderBuffer::from_slice(
|
||||
&data,
|
||||
Fourcc::Argb8888,
|
||||
(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::utils::{Logical, Rectangle, Serial};
|
||||
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::wlr_layer::Layer;
|
||||
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) {
|
||||
// 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 Ok(root) = find_popup_root_surface(&popup) else {
|
||||
return;
|
||||
|
||||
@@ -338,7 +338,7 @@ fn render(config: &Config, comp_mod: CompositorMod, scale: i32) -> anyhow::Resul
|
||||
drop(cr);
|
||||
|
||||
let data = surface.take_data().unwrap();
|
||||
let buffer = MemoryRenderBuffer::from_memory(
|
||||
let buffer = MemoryRenderBuffer::from_slice(
|
||||
&data,
|
||||
Fourcc::Argb8888,
|
||||
(width, height),
|
||||
|
||||
@@ -90,6 +90,7 @@ impl State {
|
||||
TouchUp { .. } => (),
|
||||
TouchCancel { .. } => (),
|
||||
TouchFrame { .. } => (),
|
||||
SwitchToggle { .. } => (),
|
||||
Special(_) => (),
|
||||
}
|
||||
|
||||
|
||||
+139
-86
@@ -13,12 +13,12 @@ use anyhow::Context;
|
||||
use calloop::futures::Scheduler;
|
||||
use niri_config::{Config, TrackLayout};
|
||||
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::surface::{
|
||||
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
|
||||
};
|
||||
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
||||
use smithay::backend::renderer::element::utils::select_dmabuf_feedback;
|
||||
use smithay::backend::renderer::element::utils::{select_dmabuf_feedback, RelocateRenderElement};
|
||||
use smithay::backend::renderer::element::{
|
||||
default_primary_scanout_output_compare, AsRenderElements, Element, Id, Kind, RenderElement,
|
||||
RenderElementStates, UnderlyingStorage,
|
||||
@@ -66,7 +66,7 @@ use smithay::wayland::compositor::{
|
||||
};
|
||||
use smithay::wayland::cursor_shape::CursorShapeManagerState;
|
||||
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::pointer_constraints::{with_pointer_constraint, PointerConstraintsState};
|
||||
use smithay::wayland::pointer_gestures::PointerGesturesState;
|
||||
@@ -90,9 +90,7 @@ use smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState;
|
||||
use crate::animation;
|
||||
use crate::backend::tty::{SurfaceDmabufFeedback, TtyFrame, TtyRenderer, TtyRendererError};
|
||||
use crate::backend::{Backend, RenderResult, Tty, Winit};
|
||||
use crate::config_error_notification::{
|
||||
ConfigErrorNotification, ConfigErrorNotificationRenderElement,
|
||||
};
|
||||
use crate::config_error_notification::ConfigErrorNotification;
|
||||
use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};
|
||||
#[cfg(feature = "dbus")]
|
||||
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::layout::{Layout, MonitorRenderElement};
|
||||
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::utils::{
|
||||
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.refresh_pointer_outputs();
|
||||
self.niri.popups.cleanup();
|
||||
self.niri.refresh_popup_grab();
|
||||
self.refresh_popup_grab();
|
||||
self.update_keyboard_focus();
|
||||
self.refresh_pointer_focus();
|
||||
|
||||
@@ -420,6 +418,27 @@ impl State {
|
||||
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) {
|
||||
let focus = if self.niri.is_locked() {
|
||||
self.niri.lock_surface_focus()
|
||||
@@ -1560,14 +1579,6 @@ impl Niri {
|
||||
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.
|
||||
pub fn queue_redraw_all(&mut self) {
|
||||
let outputs: Vec<_> = self.output_state.keys().cloned().collect();
|
||||
@@ -1657,26 +1668,25 @@ impl Niri {
|
||||
let pointer_pos =
|
||||
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
|
||||
|
||||
let texture = self.cursor_texture_cache.get(
|
||||
renderer.as_gles_renderer(),
|
||||
icon,
|
||||
scale,
|
||||
&cursor,
|
||||
idx,
|
||||
);
|
||||
|
||||
let texture = self.cursor_texture_cache.get(icon, scale, &cursor, idx);
|
||||
let mut pointer_elements = vec![];
|
||||
if let Some(texture) = texture {
|
||||
pointer_elements.push(OutputRenderElements::NamedPointer(
|
||||
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
|
||||
pointer_pos.to_f64(),
|
||||
&texture,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Kind::Cursor,
|
||||
)),
|
||||
));
|
||||
let pointer_element = match MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
pointer_pos.to_f64(),
|
||||
&texture,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
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)
|
||||
@@ -2498,7 +2508,11 @@ impl Niri {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -2642,32 +2656,28 @@ impl Niri {
|
||||
include_pointer: bool,
|
||||
on_done: impl FnOnce(PathBuf) + Send + 'static,
|
||||
) -> anyhow::Result<()> {
|
||||
use std::cmp::max;
|
||||
|
||||
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
|
||||
|
||||
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();
|
||||
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);
|
||||
size.h = max(size.h, geom.loc.y + geom.size.h);
|
||||
// FIXME: support multiple outputs, needs fixing multi-scale handling and cropping.
|
||||
anyhow::ensure!(outputs.len() == 1);
|
||||
|
||||
let output_elements = self.render::<GlesRenderer>(renderer, &output, include_pointer);
|
||||
elements.extend(output_elements.into_iter().map(|elem| {
|
||||
RelocateRenderElement::from_element(elem, geom.loc, Relocate::Relative)
|
||||
}));
|
||||
}
|
||||
let output = outputs.into_iter().next().unwrap();
|
||||
let geom = self.global_space.output_geometry(&output).unwrap();
|
||||
|
||||
// FIXME: scale.
|
||||
let pixels = render_to_vec(renderer, size, Scale::from(1.), Fourcc::Abgr8888, &elements)?;
|
||||
let output_scale = output.current_scale().integer_scale();
|
||||
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())
|
||||
.ok()
|
||||
@@ -2763,6 +2773,44 @@ impl Niri {
|
||||
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 {
|
||||
@@ -2804,9 +2852,13 @@ fn render_to_texture(
|
||||
for element in elements.iter().rev() {
|
||||
let src = element.src();
|
||||
let dst = element.geometry(scale);
|
||||
element
|
||||
.draw(&mut frame, src, dst, &[output_rect])
|
||||
.context("error drawing element")?;
|
||||
|
||||
if let Some(mut damage) = output_rect.intersection(dst) {
|
||||
damage.loc -= dst.loc;
|
||||
element
|
||||
.draw(&mut frame, src, dst, &[damage])
|
||||
.context("error drawing element")?;
|
||||
}
|
||||
}
|
||||
|
||||
let sync_point = frame.finish().context("error finishing frame")?;
|
||||
@@ -2869,9 +2921,13 @@ fn render_to_dmabuf(
|
||||
for element in elements.iter().rev() {
|
||||
let src = element.src();
|
||||
let dst = element.geometry(scale);
|
||||
element
|
||||
.draw(&mut frame, src, dst, &[output_rect])
|
||||
.context("error drawing element")?;
|
||||
|
||||
if let Some(mut damage) = output_rect.intersection(dst) {
|
||||
damage.loc -= dst.loc;
|
||||
element
|
||||
.draw(&mut frame, src, dst, &[damage])
|
||||
.context("error drawing element")?;
|
||||
}
|
||||
}
|
||||
|
||||
let _sync_point = frame.finish().context("error finishing frame")?;
|
||||
@@ -2884,10 +2940,11 @@ fn render_to_dmabuf(
|
||||
pub enum OutputRenderElements<R: NiriRenderer> {
|
||||
Monitor(MonitorRenderElement<R>),
|
||||
Wayland(WaylandSurfaceRenderElement<R>),
|
||||
NamedPointer(PrimaryGpuTextureRenderElement),
|
||||
NamedPointer(MemoryRenderBufferRenderElement<R>),
|
||||
SolidColor(SolidColorRenderElement),
|
||||
ScreenshotUi(ScreenshotUiRenderElement),
|
||||
ConfigErrorNotification(ConfigErrorNotificationRenderElement<R>),
|
||||
// Used for the CPU-rendered panels.
|
||||
RelocatedMemoryBuffer(RelocateRenderElement<MemoryRenderBufferRenderElement<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::SolidColor(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::SolidColor(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::SolidColor(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::SolidColor(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::SolidColor(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::SolidColor(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::SolidColor(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::SolidColor(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::SolidColor(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) => {
|
||||
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::SolidColor(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) => {
|
||||
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::SolidColor(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> {
|
||||
fn from(x: SolidColorRenderElement) -> Self {
|
||||
Self::SolidColor(x)
|
||||
@@ -3102,8 +3153,10 @@ impl<R: NiriRenderer> From<ScreenshotUiRenderElement> for OutputRenderElements<R
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: NiriRenderer> From<ConfigErrorNotificationRenderElement<R>> for OutputRenderElements<R> {
|
||||
fn from(x: ConfigErrorNotificationRenderElement<R>) -> Self {
|
||||
Self::ConfigErrorNotification(x)
|
||||
impl<R: NiriRenderer> From<RelocateRenderElement<MemoryRenderBufferRenderElement<R>>>
|
||||
for OutputRenderElements<R>
|
||||
{
|
||||
fn from(x: RelocateRenderElement<MemoryRenderBufferRenderElement<R>>) -> Self {
|
||||
Self::RelocatedMemoryBuffer(x)
|
||||
}
|
||||
}
|
||||
|
||||
+18
-4
@@ -27,7 +27,18 @@ impl Watcher {
|
||||
thread::Builder::new()
|
||||
.name(format!("Filesystem Watcher for {}", path.to_string_lossy()))
|
||||
.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 {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
@@ -36,8 +47,11 @@ impl Watcher {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Ok(mtime) = path.metadata().and_then(|meta| meta.modified()) {
|
||||
if last_mtime != Some(mtime) {
|
||||
if let Ok(new_props) = path
|
||||
.canonicalize()
|
||||
.and_then(|canon| Ok((canon.metadata()?.modified()?, canon)))
|
||||
{
|
||||
if last_props.as_ref() != Some(&new_props) {
|
||||
trace!("file changed: {}", path.to_string_lossy());
|
||||
|
||||
if let Err(err) = changed.send(()) {
|
||||
@@ -45,7 +59,7 @@ impl Watcher {
|
||||
break;
|
||||
}
|
||||
|
||||
last_mtime = Some(mtime);
|
||||
last_props = Some(new_props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user