diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend.rs | 15 | ||||
| -rw-r--r-- | src/grabs/move_grab.rs | 24 | ||||
| -rw-r--r-- | src/grabs/resize_grab.rs | 26 | ||||
| -rw-r--r-- | src/handlers/compositor.rs | 14 | ||||
| -rw-r--r-- | src/handlers/mod.rs | 18 | ||||
| -rw-r--r-- | src/handlers/xdg_shell.rs | 10 | ||||
| -rw-r--r-- | src/input.rs | 34 | ||||
| -rw-r--r-- | src/main.rs | 123 | ||||
| -rw-r--r-- | src/niri.rs | 168 | ||||
| -rw-r--r-- | src/state.rs | 165 | ||||
| -rw-r--r-- | src/tty.rs | 423 | ||||
| -rw-r--r-- | src/winit.rs | 225 |
12 files changed, 876 insertions, 369 deletions
diff --git a/src/backend.rs b/src/backend.rs new file mode 100644 index 00000000..e190ed05 --- /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<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>], + ); +} diff --git a/src/grabs/move_grab.rs b/src/grabs/move_grab.rs index 63cdb3bd..699921d1 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<Smallvil>, + pub start_data: PointerGrabStartData<Niri>, pub window: Window, pub initial_window_location: Point<i32, Logical>, } -impl PointerGrab<Smallvil> for MoveSurfaceGrab { +impl PointerGrab<Niri> for MoveSurfaceGrab { fn motion( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, _focus: Option<(WlSurface, Point<i32, Logical>)>, event: &MotionEvent, ) { @@ -33,8 +33,8 @@ impl PointerGrab<Smallvil> 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<i32, Logical>)>, event: &RelativeMotionEvent, ) { @@ -43,8 +43,8 @@ impl PointerGrab<Smallvil> 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<Smallvil> 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<Smallvil> { + fn start_data(&self) -> &PointerGrabStartData<Niri> { &self.start_data } } diff --git a/src/grabs/resize_grab.rs b/src/grabs/resize_grab.rs index ee8aae5d..2bc2f194 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<xdg_toplevel::ResizeEdge> for ResizeEdge { } pub struct ResizeSurfaceGrab { - start_data: PointerGrabStartData<Smallvil>, + start_data: PointerGrabStartData<Niri>, window: Window, edges: ResizeEdge, @@ -48,7 +48,7 @@ pub struct ResizeSurfaceGrab { impl ResizeSurfaceGrab { pub fn start( - start_data: PointerGrabStartData<Smallvil>, + start_data: PointerGrabStartData<Niri>, window: Window, edges: ResizeEdge, initial_window_rect: Rectangle<i32, Logical>, @@ -72,11 +72,11 @@ impl ResizeSurfaceGrab { } } -impl PointerGrab<Smallvil> for ResizeSurfaceGrab { +impl PointerGrab<Niri> for ResizeSurfaceGrab { fn motion( &mut self, - data: &mut Smallvil, - handle: &mut PointerInnerHandle<'_, Smallvil>, + data: &mut Niri, + handle: &mut PointerInnerHandle<'_, Niri>, _focus: Option<(WlSurface, Point<i32, Logical>)>, event: &MotionEvent, ) { @@ -132,8 +132,8 @@ impl PointerGrab<Smallvil> 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<i32, Logical>)>, event: &RelativeMotionEvent, ) { @@ -142,8 +142,8 @@ impl PointerGrab<Smallvil> 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<Smallvil> 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<Smallvil> { + fn start_data(&self) -> &PointerGrabStartData<Niri> { &self.start_data } } diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index 99041f34..b5f6291d 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 58157b27..ab2a1596 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<Smallvil> { + fn seat_state(&mut self) -> &mut SeatState<Niri> { &mut self.seat_state } @@ -29,26 +29,26 @@ impl SeatHandler for Smallvil { fn focus_changed(&mut self, _seat: &smithay::input::Seat<Self>, _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 3b7f53ff..e681d0ef 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<Smallvil>, + seat: &Seat<Niri>, surface: &WlSurface, serial: Serial, -) -> Option<PointerGrabStartData<Smallvil>> { +) -> Option<PointerGrabStartData<Niri>> { let pointer = seat.get_pointer()?; // Check that this surface has a click grab. diff --git a/src/input.rs b/src/input.rs index 9fd894b5..9d167a0a 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<I: InputBackend>(&mut self, event: InputEvent<I>) { + 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 3e285536..7a977bd0 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<Smallvil>, +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(last = true)] + command: Option<OsString>, } -fn main() -> Result<(), Box<dyn std::error::Error>> { - if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() { - tracing_subscriber::fmt() - .compact() - .with_env_filter(env_filter) - .init(); - } else { - tracing_subscriber::fmt().compact().init(); - } +pub struct LoopData { + niri: Niri, + display_handle: DisplayHandle, - let mut event_loop: EventLoop<CalloopData> = EventLoop::try_new()?; + // Last so that it's dropped after the Smithay state in Niri. + display: Display<Niri>, - let mut display: Display<Smallvil> = Display::new()?; - let state = Smallvil::new(&event_loop, &mut display); + tty: Option<Tty>, + winit: Option<Winit>, +} + +fn main() { + env::set_var("RUST_BACKTRACE", "1"); - let mut data = CalloopData { state, display }; + 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(); - crate::winit::init_winit(&event_loop, &mut data)?; + let cli = Cli::parse(); - let mut args = std::env::args().skip(1); - let flag = args.next(); - let arg = args.next(); + let mut event_loop = EventLoop::try_new().unwrap(); - 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 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 { + 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); } - event_loop.run(None, &mut data, move |_| { - // Smallvil is running - })?; + 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}"); + } - 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 00000000..1750ed3d --- /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<Window>, + + // Smithay state. + pub compositor_state: CompositorState, + pub xdg_shell_state: XdgShellState, + pub shm_state: ShmState, + pub output_manager_state: OutputManagerState, + pub seat_state: SeatState<Self>, + pub data_device_state: DataDeviceState, + + pub seat: Seat<Self>, + pub output: Option<Output>, +} + +impl Niri { + pub fn new( + event_loop: LoopHandle<'static, LoopData>, + stop_signal: LoopSignal, + display: &mut Display<Self>, + seat_name: String, + ) -> Self { + let start_time = std::time::Instant::now(); + + let display_handle = display.handle(); + + let compositor_state = CompositorState::new::<Self>(&display_handle); + let xdg_shell_state = XdgShellState::new::<Self>(&display_handle); + let shm_state = ShmState::new::<Self>(&display_handle, vec![]); + let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle); + let mut seat_state = SeatState::new(); + let data_device_state = DataDeviceState::new::<Self>(&display_handle); + + let mut seat: Seat<Self> = 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<Self>, + ) -> Option<(WlSurface, Point<i32, Logical>)> { + 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 15bdf3a0..00000000 --- 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<Window>, - 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<Smallvil>, - pub data_device_state: DataDeviceState, - - pub seat: Seat<Self>, -} - -impl Smallvil { - pub fn new(event_loop: &EventLoop<CalloopData>, display: &mut Display<Self>) -> Self { - let start_time = std::time::Instant::now(); - - let dh = display.handle(); - - let compositor_state = CompositorState::new::<Self>(&dh); - let xdg_shell_state = XdgShellState::new::<Self>(&dh); - let shm_state = ShmState::new::<Self>(&dh, vec![]); - let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&dh); - let mut seat_state = SeatState::new(); - let data_device_state = DataDeviceState::new::<Self>(&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<Self> = 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<Smallvil>, - event_loop: &EventLoop<CalloopData>, - ) -> 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<Self>, - ) -> Option<(WlSurface, Point<i32, Logical>)> { - 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 00000000..1f0b72c3 --- /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<OutputDevice>, +} + +type GbmDrmCompositor = + DrmCompositor<GbmAllocator<DrmDeviceFd>, GbmDevice<DrmDeviceFd>, (), 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<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>], + ) { + 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<LoopData>) -> 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::Activa |
