diff --git a/Cargo.lock b/Cargo.lock index 471a0c7..295b1cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.0.2" @@ -35,6 +50,64 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +dependencies = [ + "backtrace", +] + [[package]] name = "appendlist" version = "1.4.0" @@ -56,6 +129,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -93,6 +181,26 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "calloop" version = "0.10.6" @@ -139,6 +247,53 @@ dependencies = [ "num-traits", ] +[[package]] +name = "clap" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "core-foundation" version = "0.9.3" @@ -200,18 +355,67 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "drm" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf9159ef4bcecd0c5e4cbeb573b8d0037493403d542780dba5d840bbf9df56f" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "nix 0.26.2", +] + +[[package]] +name = "drm-ffi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1352481b7b90e27a8a1bf8ef6b33cf18b98dba7c410e75c24bb3eef2f0d8d525" +dependencies = [ + "drm-sys", + "nix 0.26.2", +] + [[package]] name = "drm-fourcc" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" +[[package]] +name = "drm-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1369f1679d6b706d234c4c1e0613c415c2c74b598a09ad28080ba2474b72e42d" +dependencies = [ + "libc", +] + +[[package]] +name = "edid-rs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab5fa33485cd85ac354df485819a63360fefa312fe04cffe65e6f175be1522c" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + [[package]] name = "errno" version = "0.3.2" @@ -254,6 +458,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "gbm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ec389cda876966cf824111bf6e533fb934c711d473498279964a990853b3c6" +dependencies = [ + "bitflags 1.3.2", + "drm", + "drm-fourcc", + "gbm-sys", + "libc", + "wayland-backend", + "wayland-server", +] + +[[package]] +name = "gbm-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63eba9b9b7a231514482deb08759301c9f9f049ac6869403f381834ebfeaf67" +dependencies = [ + "libc", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -265,6 +493,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "gl_generator" version = "0.14.0" @@ -288,6 +522,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.2" @@ -314,6 +554,28 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "input" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" +dependencies = [ + "bitflags 1.3.2", + "input-sys", + "io-lifetimes", + "libc", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -337,6 +599,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -389,6 +662,36 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libseat" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845e5c255462c9bc7c71c17b996766b76e3c66f2ddd5846bfbc83f18382aa648" +dependencies = [ + "errno 0.2.8", + "libseat-sys", + "slog", +] + +[[package]] +name = "libseat-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3671cb5e03871f1d6bf0b3b5daa9275549e348fa6359e0f9adb910ca163d4c34" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "linux-raw-sys" version = "0.4.5" @@ -458,6 +761,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.8" @@ -503,8 +815,12 @@ dependencies = [ name = "niri" version = "0.1.0" dependencies = [ + "anyhow", "bitflags 2.3.3", + "clap", "smithay", + "smithay-drm-extras", + "tracing", "tracing-subscriber", ] @@ -644,6 +960,15 @@ dependencies = [ "objc-sys", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -840,6 +1165,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" version = "0.38.7" @@ -847,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399" dependencies = [ "bitflags 2.3.3", - "errno", + "errno 0.3.2", "libc", "linux-raw-sys", "windows-sys 0.48.0", @@ -874,6 +1205,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + [[package]] name = "slotmap" version = "1.0.6" @@ -897,27 +1234,37 @@ dependencies = [ "appendlist", "bitflags 2.3.3", "calloop", + "cc", "cgmath", "downcast-rs", + "drm", + "drm-ffi", "drm-fourcc", + "gbm", "gl_generator", "indexmap 1.9.3", + "input", "lazy_static", "libc", "libloading", + "libseat", "nix 0.26.2", "once_cell", + "pkg-config", "profiling", "rand", "scan_fmt", "tempfile", "thiserror", "tracing", + "udev", + "wayland-backend", "wayland-egl", "wayland-protocols 0.30.1", "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-server", + "wayland-sys 0.30.1", "winit", "xkbcommon", ] @@ -941,12 +1288,26 @@ dependencies = [ "wayland-protocols 0.29.5", ] +[[package]] +name = "smithay-drm-extras" +version = "0.1.0" +dependencies = [ + "drm", + "edid-rs", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.109" @@ -1091,12 +1452,29 @@ dependencies = [ "tracing-log", ] +[[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 = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" @@ -1343,7 +1721,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" dependencies = [ "dlib", + "libc", "log", + "memoffset 0.7.1", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 767d40f..5011956 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,33 @@ [package] name = "niri" version = "0.1.0" -edition = "2021" +description = "A Wayland compositor" +authors = ["Ivan Molodetskikh "] license = "GPL-3.0-or-later" +edition = "2021" [dependencies] -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -bitflags = "2.2.1" +anyhow = { version = "1.0.72", features = ["backtrace"] } +bitflags = "2.3.3" +clap = { version = "4.3.21", features = ["derive"] } +smithay-drm-extras = { version = "0.1.0", path = "../smithay/smithay-drm-extras" } +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } [dependencies.smithay] git = "https://github.com/Smithay/smithay" default-features = false features = [ + "backend_drm", + "backend_egl", + "backend_gbm", + "backend_libinput", + "backend_session_libseat", + "backend_udev", "backend_winit", - "wayland_frontend", "desktop", + "renderer_gl", + "renderer_multi", + "use_system_lib", + "wayland_frontend", ] diff --git a/src/backend.rs b/src/backend.rs new file mode 100644 index 0000000..e190ed0 --- /dev/null +++ b/src/backend.rs @@ -0,0 +1,15 @@ +use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; +use smithay::backend::renderer::gles::GlesRenderer; +use smithay::desktop::space::SpaceRenderElements; + +use crate::Niri; + +pub trait Backend { + fn seat_name(&self) -> String; + fn renderer(&mut self) -> &mut GlesRenderer; + fn render( + &mut self, + niri: &mut Niri, + elements: &[SpaceRenderElements>], + ); +} diff --git a/src/grabs/move_grab.rs b/src/grabs/move_grab.rs index 63cdb3b..699921d 100644 --- a/src/grabs/move_grab.rs +++ b/src/grabs/move_grab.rs @@ -6,19 +6,19 @@ use smithay::input::pointer::{ use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::utils::{Logical, Point}; -use crate::Smallvil; +use crate::Niri; pub struct MoveSurfaceGrab { - pub start_data: PointerGrabStartData, + pub start_data: PointerGrabStartData, pub window: Window, pub initial_window_location: Point, } -impl PointerGrab for MoveSurfaceGrab { +impl PointerGrab for MoveSurfaceGrab { fn motion( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, _focus: Option<(WlSurface, Point)>, event: &MotionEvent, ) { @@ -33,8 +33,8 @@ impl PointerGrab for MoveSurfaceGrab { fn relative_motion( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, focus: Option<(WlSurface, Point)>, event: &RelativeMotionEvent, ) { @@ -43,8 +43,8 @@ impl PointerGrab for MoveSurfaceGrab { fn button( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, event: &ButtonEvent, ) { handle.button(data, event); @@ -61,14 +61,14 @@ impl PointerGrab for MoveSurfaceGrab { fn axis( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, details: AxisFrame, ) { handle.axis(data, details) } - fn start_data(&self) -> &PointerGrabStartData { + fn start_data(&self) -> &PointerGrabStartData { &self.start_data } } diff --git a/src/grabs/resize_grab.rs b/src/grabs/resize_grab.rs index ee8aae5..2bc2f19 100644 --- a/src/grabs/resize_grab.rs +++ b/src/grabs/resize_grab.rs @@ -11,7 +11,7 @@ use smithay::utils::{Logical, Point, Rectangle, Size}; use smithay::wayland::compositor; use smithay::wayland::shell::xdg::SurfaceCachedState; -use crate::Smallvil; +use crate::Niri; bitflags::bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -37,7 +37,7 @@ impl From for ResizeEdge { } pub struct ResizeSurfaceGrab { - start_data: PointerGrabStartData, + start_data: PointerGrabStartData, window: Window, edges: ResizeEdge, @@ -48,7 +48,7 @@ pub struct ResizeSurfaceGrab { impl ResizeSurfaceGrab { pub fn start( - start_data: PointerGrabStartData, + start_data: PointerGrabStartData, window: Window, edges: ResizeEdge, initial_window_rect: Rectangle, @@ -72,11 +72,11 @@ impl ResizeSurfaceGrab { } } -impl PointerGrab for ResizeSurfaceGrab { +impl PointerGrab for ResizeSurfaceGrab { fn motion( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, _focus: Option<(WlSurface, Point)>, event: &MotionEvent, ) { @@ -132,8 +132,8 @@ impl PointerGrab for ResizeSurfaceGrab { fn relative_motion( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, focus: Option<(WlSurface, Point)>, event: &RelativeMotionEvent, ) { @@ -142,8 +142,8 @@ impl PointerGrab for ResizeSurfaceGrab { fn button( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, event: &ButtonEvent, ) { handle.button(data, event); @@ -175,14 +175,14 @@ impl PointerGrab for ResizeSurfaceGrab { fn axis( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, details: AxisFrame, ) { handle.axis(data, details) } - fn start_data(&self) -> &PointerGrabStartData { + fn start_data(&self) -> &PointerGrabStartData { &self.start_data } } diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index 99041f3..b5f6291 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -11,10 +11,10 @@ use smithay::{delegate_compositor, delegate_shm}; use super::xdg_shell; use crate::grabs::resize_grab; -use crate::state::ClientState; -use crate::Smallvil; +use crate::niri::ClientState; +use crate::Niri; -impl CompositorHandler for Smallvil { +impl CompositorHandler for Niri { fn compositor_state(&mut self) -> &mut CompositorState { &mut self.compositor_state } @@ -44,15 +44,15 @@ impl CompositorHandler for Smallvil { } } -impl BufferHandler for Smallvil { +impl BufferHandler for Niri { fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {} } -impl ShmHandler for Smallvil { +impl ShmHandler for Niri { fn shm_state(&self) -> &ShmState { &self.shm_state } } -delegate_compositor!(Smallvil); -delegate_shm!(Smallvil); +delegate_compositor!(Niri); +delegate_shm!(Niri); diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 58157b2..ab2a159 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -10,13 +10,13 @@ use smithay::wayland::data_device::{ }; use smithay::{delegate_data_device, delegate_output, delegate_seat}; -use crate::Smallvil; +use crate::Niri; -impl SeatHandler for Smallvil { +impl SeatHandler for Niri { type KeyboardFocus = WlSurface; type PointerFocus = WlSurface; - fn seat_state(&mut self) -> &mut SeatState { + fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state } @@ -29,26 +29,26 @@ impl SeatHandler for Smallvil { fn focus_changed(&mut self, _seat: &smithay::input::Seat, _focused: Option<&WlSurface>) {} } -delegate_seat!(Smallvil); +delegate_seat!(Niri); // // Wl Data Device // -impl DataDeviceHandler for Smallvil { +impl DataDeviceHandler for Niri { type SelectionUserData = (); fn data_device_state(&self) -> &smithay::wayland::data_device::DataDeviceState { &self.data_device_state } } -impl ClientDndGrabHandler for Smallvil {} -impl ServerDndGrabHandler for Smallvil {} +impl ClientDndGrabHandler for Niri {} +impl ServerDndGrabHandler for Niri {} -delegate_data_device!(Smallvil); +delegate_data_device!(Niri); // // Wl Output & Xdg Output // -delegate_output!(Smallvil); +delegate_output!(Niri); diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 3b7f53f..e681d0e 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -14,9 +14,9 @@ use smithay::wayland::shell::xdg::{ }; use crate::grabs::{MoveSurfaceGrab, ResizeSurfaceGrab}; -use crate::Smallvil; +use crate::Niri; -impl XdgShellHandler for Smallvil { +impl XdgShellHandler for Niri { fn xdg_shell_state(&mut self) -> &mut XdgShellState { &mut self.xdg_shell_state } @@ -102,13 +102,13 @@ impl XdgShellHandler for Smallvil { } // Xdg Shell -delegate_xdg_shell!(Smallvil); +delegate_xdg_shell!(Niri); fn check_grab( - seat: &Seat, + seat: &Seat, surface: &WlSurface, serial: Serial, -) -> Option> { +) -> Option> { let pointer = seat.get_pointer()?; // Check that this surface has a click grab. diff --git a/src/input.rs b/src/input.rs index 9fd894b..9d167a0 100644 --- a/src/input.rs +++ b/src/input.rs @@ -2,28 +2,52 @@ use smithay::backend::input::{ AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, }; -use smithay::input::keyboard::FilterResult; +use smithay::input::keyboard::{keysyms, FilterResult}; use smithay::input::pointer::{AxisFrame, ButtonEvent, MotionEvent}; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::utils::SERIAL_COUNTER; -use crate::state::Smallvil; +use crate::niri::Niri; -impl Smallvil { +enum InputAction { + Quit, + ChangeVt(i32), +} + +impl Niri { pub fn process_input_event(&mut self, event: InputEvent) { + trace!("process_input_event"); + match event { InputEvent::Keyboard { event, .. } => { let serial = SERIAL_COUNTER.next_serial(); let time = Event::time_msec(&event); - self.seat.get_keyboard().unwrap().input::<(), _>( + let action = self.seat.get_keyboard().unwrap().input( self, event.key_code(), event.state(), serial, time, - |_, _, _| FilterResult::Forward, + |_, _, keysym| match keysym.modified_sym() { + keysyms::KEY_Escape => FilterResult::Intercept(InputAction::Quit), + keysym @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 => { + let vt = (keysym - keysyms::KEY_XF86Switch_VT_1 + 1) as i32; + FilterResult::Intercept(InputAction::ChangeVt(vt)) + } + _ => FilterResult::Forward, + }, ); + + if let Some(action) = action { + match action { + InputAction::Quit => { + info!("quitting because Esc was pressed"); + self.stop_signal.stop() + } + InputAction::ChangeVt(vt) => todo!(), + } + } } InputEvent::PointerMotion { .. } => {} InputEvent::PointerMotionAbsolute { event, .. } => { diff --git a/src/main.rs b/src/main.rs index 3e28553..7a977bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,54 +1,109 @@ +#[macro_use] +extern crate tracing; + mod handlers; +mod backend; mod grabs; mod input; -mod state; +mod niri; +mod tty; mod winit; +use std::env; +use std::ffi::OsString; + +use backend::Backend; +use clap::Parser; +use niri::Niri; use smithay::reexports::calloop::EventLoop; -use smithay::reexports::wayland_server::Display; -pub use state::Smallvil; +use smithay::reexports::wayland_server::{Display, DisplayHandle}; +use tracing_subscriber::EnvFilter; +use tty::Tty; +use winit::Winit; -pub struct CalloopData { - state: Smallvil, - display: Display, +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(last = true)] + command: Option, } -fn main() -> Result<(), Box> { - if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() { - tracing_subscriber::fmt() - .compact() - .with_env_filter(env_filter) - .init(); +pub struct LoopData { + niri: Niri, + display_handle: DisplayHandle, + + // Last so that it's dropped after the Smithay state in Niri. + display: Display, + + tty: Option, + winit: Option, +} + +fn main() { + env::set_var("RUST_BACKTRACE", "1"); + + let directives = env::var("RUST_LOG").unwrap_or_else(|_| "niri=debug,info".to_owned()); + let env_filter = EnvFilter::builder().parse_lossy(directives); + tracing_subscriber::fmt() + .compact() + .with_env_filter(env_filter) + .init(); + + let cli = Cli::parse(); + + let mut event_loop = EventLoop::try_new().unwrap(); + + let has_display = env::var_os("WAYLAND_DISPLAY").is_some() || env::var_os("DISPLAY").is_some(); + + let mut winit = None; + let mut tty = None; + let backend: &mut dyn Backend = if has_display { + winit = Some(Winit::new(event_loop.handle())); + winit.as_mut().unwrap() } else { - tracing_subscriber::fmt().compact().init(); + tty = Some(Tty::new(event_loop.handle())); + tty.as_mut().unwrap() + }; + + let mut display = Display::new().unwrap(); + let display_handle = display.handle(); + let niri = Niri::new( + event_loop.handle(), + event_loop.get_signal(), + &mut display, + backend.seat_name(), + ); + + let mut data = LoopData { + niri, + display_handle, + display, + + tty, + winit, + }; + + if let Some(tty) = data.tty.as_mut() { + tty.init(&mut data.niri); + } + if let Some(winit) = data.winit.as_mut() { + winit.init(&mut data.niri); } - let mut event_loop: EventLoop = EventLoop::try_new()?; - - let mut display: Display = Display::new()?; - let state = Smallvil::new(&event_loop, &mut display); - - let mut data = CalloopData { state, display }; - - crate::winit::init_winit(&event_loop, &mut data)?; - - let mut args = std::env::args().skip(1); - let flag = args.next(); - let arg = args.next(); - - match (flag.as_deref(), arg) { - (Some("-c") | Some("--command"), Some(command)) => { - std::process::Command::new(command).spawn().ok(); - } - _ => { - std::process::Command::new("weston-terminal").spawn().ok(); - } + let res = if let Some(command) = &cli.command { + std::process::Command::new(command).spawn() + } else { + std::process::Command::new("weston-terminal").spawn() + }; + if let Err(err) = res { + warn!("error spawning command: {err}"); } - event_loop.run(None, &mut data, move |_| { - // Smallvil is running - })?; - - Ok(()) + event_loop + .run(None, &mut data, move |data| { + // niri is running. + data.display.flush_clients().unwrap(); + }) + .unwrap(); } diff --git a/src/niri.rs b/src/niri.rs new file mode 100644 index 0000000..1750ed3 --- /dev/null +++ b/src/niri.rs @@ -0,0 +1,168 @@ +use std::os::unix::io::AsRawFd; +use std::sync::Arc; +use std::time::Duration; + +use smithay::desktop::space::space_render_elements; +use smithay::desktop::{Space, Window, WindowSurfaceType}; +use smithay::input::keyboard::XkbConfig; +use smithay::input::pointer::PointerHandle; +use smithay::input::{Seat, SeatState}; +use smithay::output::Output; +use smithay::reexports::calloop::generic::Generic; +use smithay::reexports::calloop::{Interest, LoopHandle, LoopSignal, Mode, PostAction}; +use smithay::reexports::wayland_server::backend::{ClientData, ClientId, DisconnectReason}; +use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; +use smithay::reexports::wayland_server::{Display, DisplayHandle}; +use smithay::utils::{Logical, Point}; +use smithay::wayland::compositor::{CompositorClientState, CompositorState}; +use smithay::wayland::data_device::DataDeviceState; +use smithay::wayland::output::OutputManagerState; +use smithay::wayland::shell::xdg::XdgShellState; +use smithay::wayland::shm::ShmState; +use smithay::wayland::socket::ListeningSocketSource; + +use crate::backend::Backend; +use crate::LoopData; + +pub struct Niri { + pub start_time: std::time::Instant, + pub event_loop: LoopHandle<'static, LoopData>, + pub stop_signal: LoopSignal, + pub display_handle: DisplayHandle, + + pub space: Space, + + // Smithay state. + pub compositor_state: CompositorState, + pub xdg_shell_state: XdgShellState, + pub shm_state: ShmState, + pub output_manager_state: OutputManagerState, + pub seat_state: SeatState, + pub data_device_state: DataDeviceState, + + pub seat: Seat, + pub output: Option, +} + +impl Niri { + pub fn new( + event_loop: LoopHandle<'static, LoopData>, + stop_signal: LoopSignal, + display: &mut Display, + seat_name: String, + ) -> Self { + let start_time = std::time::Instant::now(); + + let display_handle = display.handle(); + + let compositor_state = CompositorState::new::(&display_handle); + let xdg_shell_state = XdgShellState::new::(&display_handle); + let shm_state = ShmState::new::(&display_handle, vec![]); + let output_manager_state = OutputManagerState::new_with_xdg_output::(&display_handle); + let mut seat_state = SeatState::new(); + let data_device_state = DataDeviceState::new::(&display_handle); + + let mut seat: Seat = seat_state.new_wl_seat(&display_handle, seat_name); + // FIXME: get Xkb and repeat interval from GNOME dconf. + seat.add_keyboard(XkbConfig::default(), 400, 30).unwrap(); + seat.add_pointer(); + + let space = Space::default(); + + let socket_source = ListeningSocketSource::new_auto().unwrap(); + let socket_name = socket_source.socket_name().to_os_string(); + event_loop + .insert_source(socket_source, move |client, _, data| { + if let Err(err) = data + .display_handle + .insert_client(client, Arc::new(ClientState::default())) + { + error!("error inserting client: {err}"); + } + }) + .unwrap(); + std::env::set_var("WAYLAND_DISPLAY", &socket_name); + info!( + "listening on Wayland socket: {}", + socket_name.to_string_lossy() + ); + + let display_source = Generic::new( + display.backend().poll_fd().as_raw_fd(), + Interest::READ, + Mode::Level, + ); + event_loop + .insert_source(display_source, |_, _, data| { + data.display.dispatch_clients(&mut data.niri).unwrap(); + Ok(PostAction::Continue) + }) + .unwrap(); + + Self { + start_time, + event_loop, + stop_signal, + display_handle, + + space, + + compositor_state, + xdg_shell_state, + shm_state, + output_manager_state, + seat_state, + data_device_state, + + seat, + output: None, + } + } + + pub fn surface_under_pointer( + &self, + pointer: &PointerHandle, + ) -> Option<(WlSurface, Point)> { + let pos = pointer.current_location(); + self.space + .element_under(pos) + .and_then(|(window, location)| { + window + .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL) + .map(|(s, p)| (s, p + location)) + }) + } + + pub fn redraw(&mut self, backend: &mut dyn Backend) { + let elements = space_render_elements( + backend.renderer(), + [&self.space], + self.output.as_ref().unwrap(), + 1., + ) + .unwrap(); + backend.render(self, &elements); + + let output = self.output.as_ref().unwrap(); + self.space.elements().for_each(|window| { + window.send_frame( + output, + self.start_time.elapsed(), + Some(Duration::ZERO), + |_, _| Some(output.clone()), + ) + }); + + self.space.refresh(); + } +} + +#[derive(Default)] +pub struct ClientState { + pub compositor_state: CompositorClientState, +} + +impl ClientData for ClientState { + fn initialized(&self, _client_id: ClientId) {} + fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {} +} diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index 15bdf3a..0000000 --- a/src/state.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::ffi::OsString; -use std::os::unix::io::AsRawFd; -use std::sync::Arc; - -use smithay::desktop::{Space, Window, WindowSurfaceType}; -use smithay::input::pointer::PointerHandle; -use smithay::input::{Seat, SeatState}; -use smithay::reexports::calloop::generic::Generic; -use smithay::reexports::calloop::{EventLoop, Interest, LoopSignal, Mode, PostAction}; -use smithay::reexports::wayland_server::backend::{ClientData, ClientId, DisconnectReason}; -use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; -use smithay::reexports::wayland_server::Display; -use smithay::utils::{Logical, Point}; -use smithay::wayland::compositor::{CompositorClientState, CompositorState}; -use smithay::wayland::data_device::DataDeviceState; -use smithay::wayland::output::OutputManagerState; -use smithay::wayland::shell::xdg::XdgShellState; -use smithay::wayland::shm::ShmState; -use smithay::wayland::socket::ListeningSocketSource; - -use crate::CalloopData; - -pub struct Smallvil { - pub start_time: std::time::Instant, - pub socket_name: OsString, - - pub space: Space, - pub loop_signal: LoopSignal, - - // Smithay State - pub compositor_state: CompositorState, - pub xdg_shell_state: XdgShellState, - pub shm_state: ShmState, - pub output_manager_state: OutputManagerState, - pub seat_state: SeatState, - pub data_device_state: DataDeviceState, - - pub seat: Seat, -} - -impl Smallvil { - pub fn new(event_loop: &EventLoop, display: &mut Display) -> Self { - let start_time = std::time::Instant::now(); - - let dh = display.handle(); - - let compositor_state = CompositorState::new::(&dh); - let xdg_shell_state = XdgShellState::new::(&dh); - let shm_state = ShmState::new::(&dh, vec![]); - let output_manager_state = OutputManagerState::new_with_xdg_output::(&dh); - let mut seat_state = SeatState::new(); - let data_device_state = DataDeviceState::new::(&dh); - - // A seat is a group of keyboards, pointer and touch devices. - // A seat typically has a pointer and maintains a keyboard focus and a pointer focus. - let mut seat: Seat = seat_state.new_wl_seat(&dh, "winit"); - - // Notify clients that we have a keyboard, for the sake of the example we assume that - // keyboard is always present. You may want to track keyboard hot-plug in real - // compositor. - seat.add_keyboard(Default::default(), 200, 200).unwrap(); - - // Notify clients that we have a pointer (mouse) - // Here we assume that there is always pointer plugged in - seat.add_pointer(); - - // A space represents a two-dimensional plane. Windows and Outputs can be mapped onto it. - // - // Windows get a position and stacking order through mapping. - // Outputs become views of a part of the Space and can be rendered via Space::render_output. - let space = Space::default(); - - let socket_name = Self::init_wayland_listener(display, event_loop); - - // Get the loop signal, used to stop the event loop - let loop_signal = event_loop.get_signal(); - - Self { - start_time, - - space, - loop_signal, - socket_name, - - compositor_state, - xdg_shell_state, - shm_state, - output_manager_state, - seat_state, - data_device_state, - seat, - } - } - - fn init_wayland_listener( - display: &mut Display, - event_loop: &EventLoop, - ) -> OsString { - // Creates a new listening socket, automatically choosing the next available `wayland` - // socket name. - let listening_socket = ListeningSocketSource::new_auto().unwrap(); - - // Get the name of the listening socket. - // Clients will connect to this socket. - let socket_name = listening_socket.socket_name().to_os_string(); - - let handle = event_loop.handle(); - - event_loop - .handle() - .insert_source(listening_socket, move |client_stream, _, state| { - // Inside the callback, you should insert the client into the display. - // - // You may also associate some data with the client when inserting the client. - state - .display - .handle() - .insert_client(client_stream, Arc::new(ClientState::default())) - .unwrap(); - }) - .expect("Failed to init the wayland event source."); - - // You also need to add the display itself to the event loop, so that client events will be - // processed by wayland-server. - handle - .insert_source( - Generic::new( - display.backend().poll_fd().as_raw_fd(), - Interest::READ, - Mode::Level, - ), - |_, _, state| { - state.display.dispatch_clients(&mut state.state).unwrap(); - Ok(PostAction::Continue) - }, - ) - .unwrap(); - - socket_name - } - - pub fn surface_under_pointer( - &self, - pointer: &PointerHandle, - ) -> Option<(WlSurface, Point)> { - let pos = pointer.current_location(); - self.space - .element_under(pos) - .and_then(|(window, location)| { - window - .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL) - .map(|(s, p)| (s, p + location)) - }) - } -} - -#[derive(Default)] -pub struct ClientState { - pub compositor_state: CompositorClientState, -} - -impl ClientData for ClientState { - fn initialized(&self, _client_id: ClientId) {} - fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {} -} diff --git a/src/tty.rs b/src/tty.rs new file mode 100644 index 0000000..1f0b72c --- /dev/null +++ b/src/tty.rs @@ -0,0 +1,423 @@ +use std::os::fd::FromRawFd; +use std::path::PathBuf; +use std::time::Duration; + +use anyhow::anyhow; +use smithay::backend::allocator::dmabuf::Dmabuf; +use smithay::backend::allocator::gbm::{GbmAllocator, GbmBufferFlags, GbmDevice}; +use smithay::backend::allocator::Fourcc; +use smithay::backend::drm::compositor::DrmCompositor; +use smithay::backend::drm::{DrmDevice, DrmDeviceFd, DrmEvent}; +use smithay::backend::egl::{EGLContext, EGLDisplay}; +use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface}; +use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; +use smithay::backend::renderer::gles::{GlesRenderbuffer, GlesRenderer}; +use smithay::backend::renderer::{Bind, ImportEgl}; +use smithay::backend::session::libseat::LibSeatSession; +use smithay::backend::session::{Event as SessionEvent, Session}; +use smithay::backend::udev::{self, UdevBackend, UdevEvent}; +use smithay::desktop::space::SpaceRenderElements; +use smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties, Subpixel}; +use smithay::reexports::calloop::timer::{TimeoutAction, Timer}; +use smithay::reexports::calloop::{LoopHandle, RegistrationToken}; +use smithay::reexports::drm::control::connector::{ + Interface as ConnectorInterface, State as ConnectorState, +}; +use smithay::reexports::drm::control::{Device, ModeTypeFlags}; +use smithay::reexports::input::Libinput; +use smithay::reexports::nix::fcntl::OFlag; +use smithay::reexports::nix::libc::dev_t; +use smithay::utils::DeviceFd; +use smithay_drm_extras::edid::EdidInfo; + +use crate::backend::Backend; +use crate::{LoopData, Niri}; + +const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888]; + +pub struct Tty { + session: LibSeatSession, + primary_gpu_path: PathBuf, + output_device: Option, +} + +type GbmDrmCompositor = + DrmCompositor, GbmDevice, (), DrmDeviceFd>; + +struct OutputDevice { + id: dev_t, + path: PathBuf, + token: RegistrationToken, + drm: DrmDevice, + gles: GlesRenderer, + drm_compositor: GbmDrmCompositor, +} + +impl Backend for Tty { + fn seat_name(&self) -> String { + self.session.seat() + } + + fn renderer(&mut self) -> &mut GlesRenderer { + &mut self.output_device.as_mut().unwrap().gles + } + + fn render( + &mut self, + niri: &mut Niri, + elements: &[SpaceRenderElements>], + ) { + let output_device = self.output_device.as_mut().unwrap(); + let res = output_device + .drm_compositor + .render_frame::<_, _, GlesRenderbuffer>( + &mut output_device.gles, + elements, + [0.1, 0.1, 0.1, 1.], + ) + .unwrap(); + assert!(!res.needs_sync()); + if res.damage.is_some() { + output_device.drm_compositor.queue_frame(()).unwrap(); + } else { + niri.event_loop + .insert_source( + Timer::from_duration(Duration::from_millis(6)), + |_, _, data| { + data.niri.redraw(data.tty.as_mut().unwrap()); + TimeoutAction::Drop + }, + ) + .unwrap(); + } + } +} + +impl Tty { + pub fn new(event_loop: LoopHandle) -> Self { + let (session, notifier) = LibSeatSession::new().unwrap(); + let seat_name = session.seat(); + + let mut libinput = Libinput::new_with_udev(LibinputSessionInterface::from(session.clone())); + libinput.udev_assign_seat(&seat_name).unwrap(); + + let input_backend = LibinputInputBackend::new(libinput.clone()); + event_loop + .insert_source(input_backend, |event, _, data| { + data.niri.process_input_event(event) + }) + .unwrap(); + + event_loop + .insert_source(notifier, move |event, _, data| { + let tty = data.tty.as_mut().unwrap(); + let niri = &mut data.niri; + + match event { + SessionEvent::PauseSession => { + libinput.suspend(); + + if let Some(output_device) = &tty.output_device { + output_device.drm.pause(); + } + } + SessionEvent::ActivateSession => { + if libinput.resume().is_err() { + error!("error resuming libinput"); + } + + if let Some(output_device) = &tty.output_device { + // FIXME: according to Catacomb, resetting DRM+Compositor is preferrable + // here, but currently not possible due to a bug somewhere. + tty.device_changed(output_device.id, niri); + } + + niri.redraw(tty); + } + } + }) + .unwrap(); + + let primary_gpu_path = udev::primary_gpu(&seat_name).unwrap().unwrap(); + + Self { + session, + primary_gpu_path, + output_device: None, + } + } + + pub fn init(&mut self, niri: &mut Niri) { + let backend = UdevBackend::new(&self.session.seat()).unwrap(); + for (device_id, path) in backend.device_list() { + if let Err(err) = self.device_added(device_id, path.to_owned(), niri) { + warn!("error adding device: {err:?}"); + } + } + + niri.event_loop + .insert_source(backend, move |event, _, data| { + let tty = data.tty.as_mut().unwrap(); + let niri = &mut data.niri; + + match event { + UdevEvent::Added { device_id, path } => { + if let Err(err) = tty.device_added(device_id, path, niri) { + warn!("error adding device: {err:?}"); + } + niri.redraw(tty); + } + UdevEvent::Changed { device_id } => tty.device_changed(device_id, niri), + UdevEvent::Removed { device_id } => tty.device_removed(device_id, niri), + } + }) + .unwrap(); + + niri.redraw(self); + } + + fn device_added( + &mut self, + device_id: dev_t, + path: PathBuf, + niri: &mut Niri, + ) -> anyhow::Result<()> { + if path != self.primary_gpu_path { + debug!("skipping non-primary device {path:?}"); + return Ok(()); + } + + debug!("adding device {path:?}"); + assert!(self.output_device.is_none()); + + let open_flags = OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK; + let fd = self.session.open(&path, open_flags)?; + let device_fd = unsafe { DrmDeviceFd::new(DeviceFd::from_raw_fd(fd)) }; + + let (drm, drm_notifier) = DrmDevice::new(device_fd.clone(), true)?; + let gbm = GbmDevice::new(device_fd)?; + + let display = EGLDisplay::new(gbm.clone())?; + let egl_context = EGLContext::new(&display)?; + + let mut gles = unsafe { GlesRenderer::new(egl_context)? }; + gles.bind_wl_display(&niri.display_handle)?; + + let drm_compositor = self.create_drm_compositor(&drm, &gbm, &gles, niri)?; + + let token = niri + .event_loop + .insert_source(drm_notifier, move |event, metadata, data| { + let tty = data.tty.as_mut().unwrap(); + match event { + DrmEvent::VBlank(_crtc) => { + info!("vblank {metadata:?}"); + + let output_device = tty.output_device.as_mut().unwrap(); + + // Mark the last frame as submitted. + if let Err(err) = output_device.drm_compositor.frame_submitted() { + error!("error marking frame as submitted: {err}"); + } + + // Send presentation time feedback. + // catacomb + // .windows + // .mark_presented(&output_device.last_render_states, metadata); + + // Request redraw before the next VBlank. + // let frame_interval = catacomb.windows.output().frame_interval(); + // let duration = frame_interval - RENDER_TIME_OFFSET; + // catacomb.backend.schedule_redraw(duration); + data.niri.redraw(tty); + } + DrmEvent::Error(error) => error!("DRM error: {error}"), + }; + }) + .unwrap(); + + self.output_device = Some(OutputDevice { + id: device_id, + path, + token, + drm, + gles, + drm_compositor, + }); + + Ok(()) + } + + fn device_changed(&mut self, device_id: dev_t, niri: &mut Niri) { + if let Some(output_device) = &self.output_device { + if output_device.id == device_id { + debug!("output device changed"); + + let path = output_device.path.clone(); + self.device_removed(device_id, niri); + if let Err(err) = self.device_added(device_id, path, niri) { + warn!("error adding device: {err:?}"); + } + } + } + } + + fn device_removed(&mut self, device_id: dev_t, niri: &mut Niri) { + if let Some(mut output_device) = self.output_device.take() { + if output_device.id != device_id { + self.output_device = Some(output_device); + return; + } + + // FIXME: remove wl_output. + niri.event_loop.remove(output_device.token); + niri.output = None; + output_device.gles.unbind_wl_display(); + } + } + + fn create_drm_compositor( + &mut self, + drm: &DrmDevice, + gbm: &GbmDevice, + gles: &GlesRenderer, + niri: &mut Niri, + ) -> anyhow::Result { + let formats = Bind::::supported_formats(gles) + .ok_or_else(|| anyhow!("no supported formats"))?; + let resources = drm.resource_handles()?; + + let mut connector = None; + resources + .connectors() + .iter() + .filter_map(|conn| match drm.get_connector(*conn, true) { + Ok(info) => Some(info), + Err(err) => { + warn!("error probing connector: {err}"); + None + } + }) + .inspect(|conn| { + debug!( + "connector: {}-{}, {:?}, {} modes", + conn.interface().as_str(), + conn.interface_id(), + conn.state(), + conn.modes().len(), + ); + }) + .filter(|conn| conn.state() == ConnectorState::Connected) + // FIXME: don't hardcode eDP. + .filter(|conn| conn.interface() == ConnectorInterface::EmbeddedDisplayPort) + .for_each(|conn| connector = Some(conn)); + let connector = connector.ok_or_else(|| anyhow!("no compatible connector"))?; + info!( + "picking connector: {}-{}", + connector.interface().as_str(), + connector.interface_id(), + ); + + let mut mode = connector.modes().get(0); + connector.modes().iter().for_each(|m| { + debug!("mode: {m:?}"); + + if m.mode_type().contains(ModeTypeFlags::PREFERRED) { + // Pick the highest refresh rate. + if mode + .map(|curr| curr.vrefresh() < m.vrefresh()) + .unwrap_or(true) + { + mode = Some(m); + } + } + }); + let mode = mode.ok_or_else(|| anyhow!("no mode"))?; + info!("picking mode: {mode:?}"); + + let surface = connector + .encoders() + .iter() + .filter_map(|enc| match drm.get_encoder(*enc) { + Ok(info) => Some(info), + Err(err) => { + warn!("error probing encoder: {err}"); + None + } + }) + .flat_map(|enc| { + // Get all CRTCs compatible with the encoder. + let mut crtcs = resources.filter_crtcs(enc.possible_crtcs()); + + // Sort by maximum number of overlay planes. + crtcs.sort_by_cached_key(|crtc| match drm.planes(crtc) { + Ok(planes) => -(planes.overlay.len() as isize), + Err(err) => { + warn!("error probing planes for CRTC: {err}"); + 0 + } + }); + + crtcs + }) + .find_map( + |crtc| match drm.create_surface(crtc, *mode, &[connector.handle()]) { + Ok(surface) => Some(surface), + Err(err) => { + warn!("error creating DRM surface: {err}"); + None + } + }, + ); + let surface = surface.ok_or_else(|| anyhow!("no surface"))?; + + // Create GBM allocator. + let gbm_flags = GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT; + let allocator = GbmAllocator::new(gbm.clone(), gbm_flags); + + // Update the output mode. + let (physical_width, physical_height) = connector.size().unwrap_or((0, 0)); + let output_name = format!( + "{}-{}", + connector.interface().as_str(), + connector.interface_id(), + ); + + let (make, model) = EdidInfo::for_connector(drm, connector.handle()) + .map(|info| (info.manufacturer, info.model)) + .unwrap_or_else(|| ("Unknown".into(), "Unknown".into())); + + let output = Output::new( + output_name, + PhysicalProperties { + size: (physical_width as i32, physical_height as i32).into(), + subpixel: Subpixel::Unknown, + model, + make, + }, + ); + let wl_mode = Mode::from(*mode); + output.change_current_state(Some(wl_mode), None, None, Some((0, 0).into())); + output.set_preferred(wl_mode); + + // FIXME: store this somewhere to remove on disconnect, etc. + let _global = output.create_global::(&niri.display_handle); + niri.space.map_output(&output, (0, 0)); + niri.output = Some(output.clone()); + // windows.set_output(); + + // Create the compositor. + let compositor = DrmCompositor::new( + OutputModeSource::Auto(output), + surface, + None, + allocator, + gbm.clone(), + SUPPORTED_COLOR_FORMATS, + formats, + drm.cursor_size(), + Some(gbm.clone()), + )?; + Ok(compositor) + } +} diff --git a/src/winit.rs b/src/winit.rs index d0bb1a2..d0994f7 100644 --- a/src/winit.rs +++ b/src/winit.rs @@ -4,137 +4,124 @@ use smithay::backend::renderer::damage::OutputDamageTracker; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::backend::winit::{self, WinitError, WinitEvent, WinitEventLoop, WinitGraphicsBackend}; +use smithay::desktop::space::SpaceRenderElements; use smithay::output::{Mode, Output, PhysicalProperties, Subpixel}; use smithay::reexports::calloop::timer::{TimeoutAction, Timer}; -use smithay::reexports::calloop::EventLoop; +use smithay::reexports::calloop::LoopHandle; use smithay::utils::{Rectangle, Transform}; -use crate::{CalloopData, Smallvil}; +use crate::backend::Backend; +use crate::{LoopData, Niri}; -pub fn init_winit( - event_loop: &EventLoop, - data: &mut CalloopData, -) -> Result<(), Box> { - let display = &mut data.display; - let state = &mut data.state; - - let (mut backend, mut winit) = winit::init()?; - - let mode = Mode { - size: backend.window_size().physical_size, - refresh: 60_000, - }; - - let output = Output::new( - "winit".to_string(), - PhysicalProperties { - size: (0, 0).into(), - subpixel: Subpixel::Unknown, - make: "Smithay".into(), - model: "Winit".into(), - }, - ); - let _global = output.create_global::(&display.handle()); - output.change_current_state( - Some(mode), - Some(Transform::Flipped180), - None, - Some((0, 0).into()), - ); - output.set_preferred(mode); - - state.space.map_output(&output, (0, 0)); - - let mut damage_tracker = OutputDamageTracker::from_output(&output); - - std::env::set_var("WAYLAND_DISPLAY", &state.socket_name); - - let mut full_redraw = 0u8; - - let timer = Timer::immediate(); - event_loop - .handle() - .insert_source(timer, move |_, _, data| { - winit_dispatch( - &mut backend, - &mut winit, - data, - &output, - &mut damage_tracker, - &mut full_redraw, - ) - .unwrap(); - TimeoutAction::ToDuration(Duration::from_millis(16)) - })?; - - Ok(()) +pub struct Winit { + output: Output, + backend: WinitGraphicsBackend, + winit_event_loop: WinitEventLoop, + damage_tracker: OutputDamageTracker, } -pub fn winit_dispatch( - backend: &mut WinitGraphicsBackend, - winit: &mut WinitEventLoop, - data: &mut CalloopData, - output: &Output, - damage_tracker: &mut OutputDamageTracker, - full_redraw: &mut u8, -) -> Result<(), Box> { - let display = &mut data.display; - let state = &mut data.state; - - let res = winit.dispatch_new_events(|event| match event { - WinitEvent::Resized { size, .. } => { - output.change_current_state( - Some(Mode { - size, - refresh: 60_000, - }), - None, - None, - None, - ); - } - WinitEvent::Input(event) => state.process_input_event(event), - _ => (), - }); - - if let Err(WinitError::WindowClosed) = res { - // Stop the loop - state.loop_signal.stop(); - - return Ok(()); - } else { - res?; +impl Backend for Winit { + fn seat_name(&self) -> String { + "winit".to_owned() } - *full_redraw = full_redraw.saturating_sub(1); + fn renderer(&mut self) -> &mut GlesRenderer { + self.backend.renderer() + } - let size = backend.window_size().physical_size; - let damage = Rectangle::from_loc_and_size((0, 0), size); + fn render( + &mut self, + _niri: &mut Niri, + elements: &[SpaceRenderElements>], + ) { + let size = self.backend.window_size().physical_size; + let damage = Rectangle::from_loc_and_size((0, 0), size); - backend.bind()?; - smithay::desktop::space::render_output::<_, WaylandSurfaceRenderElement, _, _>( - output, - backend.renderer(), - 1.0, - 0, - [&state.space], - &[], - damage_tracker, - [0.1, 0.1, 0.1, 1.0], - )?; - backend.submit(Some(&[damage]))?; - - state.space.elements().for_each(|window| { - window.send_frame( - output, - state.start_time.elapsed(), - Some(Duration::ZERO), - |_, _| Some(output.clone()), - ) - }); - - state.space.refresh(); - display.flush_clients()?; - - Ok(()) + self.damage_tracker + .render_output(self.backend.renderer(), 0, elements, [0.1, 0.1, 0.1, 1.0]) + .unwrap(); + self.backend.submit(Some(&[damage])).unwrap(); + } +} + +impl Winit { + pub fn new(event_loop: LoopHandle) -> Self { + let (backend, winit_event_loop) = winit::init().unwrap(); + + let mode = Mode { + size: backend.window_size().physical_size, + refresh: 60_000, + }; + + let output = Output::new( + "winit".to_string(), + PhysicalProperties { + size: (0, 0).into(), + subpixel: Subpixel::Unknown, + make: "Smithay".into(), + model: "Winit".into(), + }, + ); + output.change_current_state( + Some(mode), + Some(Transform::Flipped180), + None, + Some((0, 0).into()), + ); + output.set_preferred(mode); + + let damage_tracker = OutputDamageTracker::from_output(&output); + + let timer = Timer::immediate(); + event_loop + .insert_source(timer, move |_, _, data| { + let winit = data.winit.as_mut().unwrap(); + winit.dispatch(&mut data.niri); + TimeoutAction::ToDuration(Duration::from_millis(16)) + }) + .unwrap(); + + Self { + output, + backend, + winit_event_loop, + damage_tracker, + } + } + + pub fn init(&mut self, niri: &mut Niri) { + let _global = self.output.create_global::(&niri.display_handle); + niri.space.map_output(&self.output, (0, 0)); + niri.output = Some(self.output.clone()); + } + + fn dispatch(&mut self, niri: &mut Niri) { + let res = self + .winit_event_loop + .dispatch_new_events(|event| match event { + WinitEvent::Resized { size, .. } => { + niri.output.as_ref().unwrap().change_current_state( + Some(Mode { + size, + refresh: 60_000, + }), + None, + None, + None, + ); + } + WinitEvent::Input(event) => niri.process_input_event(event), + _ => (), + }); + + if let Err(WinitError::WindowClosed) = res { + niri.stop_signal.stop(); + return; + } else { + res.unwrap(); + } + + self.backend.bind().unwrap(); + niri.redraw(self); + } }