From 03a9fd8af3bbd8a0e6dbc33516dce83cce564166 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sun, 3 Sep 2023 14:10:02 +0400 Subject: Move all traits one layer up --- src/backend/mod.rs | 41 ++++++-- src/backend/tty.rs | 120 +++++++++++------------ src/backend/winit.rs | 103 +++++++++----------- src/handlers/compositor.rs | 51 +++++----- src/handlers/layer_shell.rs | 19 ++-- src/handlers/mod.rs | 42 ++++---- src/handlers/xdg_shell.rs | 45 +++++---- src/input.rs | 230 ++++++++++++++++++++++---------------------- src/main.rs | 18 +++- src/niri.rs | 126 ++++++++++++------------ 10 files changed, 407 insertions(+), 388 deletions(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 5c0f62a5..d70ee2aa 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,6 +1,7 @@ use smithay::backend::renderer::gles::GlesRenderer; use smithay::output::Output; +use crate::input::CompositorMod; use crate::niri::OutputRenderElements; use crate::Niri; @@ -49,19 +50,47 @@ impl Backend { } } - pub fn tty(&mut self) -> Option<&mut Tty> { + pub fn mod_key(&self) -> CompositorMod { + match self { + Backend::Tty(_) => CompositorMod::Super, + Backend::Winit(_) => CompositorMod::Alt, + } + } + + pub fn change_vt(&mut self, vt: i32) { + match self { + Backend::Tty(tty) => tty.change_vt(vt), + Backend::Winit(_) => (), + } + } + + pub fn suspend(&mut self) { + match self { + Backend::Tty(tty) => tty.suspend(), + Backend::Winit(_) => (), + } + } + + pub fn toggle_debug_tint(&mut self) { + match self { + Backend::Tty(tty) => tty.toggle_debug_tint(), + Backend::Winit(winit) => winit.toggle_debug_tint(), + } + } + + pub fn tty(&mut self) -> &mut Tty { if let Self::Tty(v) = self { - Some(v) + v } else { - None + panic!("backend is not Tty"); } } - pub fn winit(&mut self) -> Option<&mut Winit> { + pub fn winit(&mut self) -> &mut Winit { if let Self::Winit(v) = self { - Some(v) + v } else { - None + panic!("backend is not Winit") } } } diff --git a/src/backend/tty.rs b/src/backend/tty.rs index e5ee4551..eec796f1 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -30,16 +30,15 @@ use smithay::utils::DeviceFd; use smithay_drm_extras::drm_scanner::{DrmScanEvent, DrmScanner}; use smithay_drm_extras::edid::EdidInfo; -use crate::input::{BackendAction, CompositorMod}; -use crate::niri::{Data, OutputRenderElements}; -use crate::Niri; +use crate::niri::OutputRenderElements; +use crate::{LoopData, Niri}; const BACKGROUND_COLOR: [f32; 4] = [0.1, 0.1, 0.1, 1.]; const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888]; pub struct Tty { session: LibSeatSession, - udev_dispatcher: Dispatcher<'static, UdevBackend, Data>, + udev_dispatcher: Dispatcher<'static, UdevBackend, LoopData>, primary_gpu_path: PathBuf, output_device: Option, } @@ -69,44 +68,45 @@ struct TtyOutputState { } impl Tty { - pub fn new(event_loop: LoopHandle<'static, Data>) -> Self { + pub fn new(event_loop: LoopHandle<'static, LoopData>) -> Self { let (session, notifier) = LibSeatSession::new().unwrap(); let seat_name = session.seat(); let udev_backend = UdevBackend::new(session.seat()).unwrap(); - let udev_dispatcher = Dispatcher::new(udev_backend, move |event, _, data: &mut Data| { - let tty = data.backend.tty().unwrap(); - let niri = &mut data.niri; + let udev_dispatcher = + Dispatcher::new(udev_backend, move |event, _, data: &mut LoopData| { + let tty = data.state.backend.tty(); + let niri = &mut data.state.niri; - match event { - UdevEvent::Added { device_id, path } => { - if !tty.session.is_active() { - debug!("skipping UdevEvent::Added as session is inactive"); - return; - } + match event { + UdevEvent::Added { device_id, path } => { + if !tty.session.is_active() { + debug!("skipping UdevEvent::Added as session is inactive"); + return; + } - if let Err(err) = tty.device_added(device_id, &path, niri) { - warn!("error adding device: {err:?}"); - } - } - UdevEvent::Changed { device_id } => { - if !tty.session.is_active() { - debug!("skipping UdevEvent::Changed as session is inactive"); - return; + if let Err(err) = tty.device_added(device_id, &path, niri) { + warn!("error adding device: {err:?}"); + } } + UdevEvent::Changed { device_id } => { + if !tty.session.is_active() { + debug!("skipping UdevEvent::Changed as session is inactive"); + return; + } - tty.device_changed(device_id, niri) - } - UdevEvent::Removed { device_id } => { - if !tty.session.is_active() { - debug!("skipping UdevEvent::Removed as session is inactive"); - return; + tty.device_changed(device_id, niri) } + UdevEvent::Removed { device_id } => { + if !tty.session.is_active() { + debug!("skipping UdevEvent::Removed as session is inactive"); + return; + } - tty.device_removed(device_id, niri) + tty.device_removed(device_id, niri) + } } - } - }); + }); event_loop .register_dispatcher(udev_dispatcher.clone()) .unwrap(); @@ -117,43 +117,16 @@ impl Tty { let input_backend = LibinputInputBackend::new(libinput.clone()); event_loop .insert_source(input_backend, |mut event, _, data| { - let tty = data.backend.tty().unwrap(); - let niri = &mut data.niri; - - niri.process_libinput_event(&mut event); - match niri.process_input_event(CompositorMod::Super, event) { - BackendAction::None => (), - BackendAction::ChangeVt(vt) => tty.change_vt(vt), - BackendAction::Suspend => { - if let Err(err) = suspend() { - warn!("error suspending: {err:?}"); - } - } - BackendAction::Screenshot => { - let active = niri.monitor_set.active_output().cloned(); - if let Some(active) = active { - if let Err(err) = niri.screenshot(tty.renderer(), &active) { - warn!("error taking screenshot: {err:?}"); - } - } - } - BackendAction::ToggleDebugTint => { - if let Some(device) = tty.output_device.as_mut() { - for (_, compositor) in &mut device.surfaces { - compositor - .set_debug_flags(compositor.debug_flags() ^ DebugFlags::TINT); - } - } - } - }; + data.state.process_libinput_event(&mut event); + data.state.process_input_event(event); }) .unwrap(); let udev_dispatcher_c = udev_dispatcher.clone(); event_loop .insert_source(notifier, move |event, _, data| { - let tty = data.backend.tty().unwrap(); - let niri = &mut data.niri; + let tty = data.state.backend.tty(); + let niri = &mut data.state.niri; match event { SessionEvent::PauseSession => { @@ -289,7 +262,7 @@ impl Tty { let token = niri .event_loop .insert_source(drm_notifier, move |event, metadata, data| { - let tty = data.backend.tty().unwrap(); + let tty = data.state.backend.tty(); match event { DrmEvent::VBlank(crtc) => { tracy_client::Client::running() @@ -336,6 +309,7 @@ impl Tty { } let output = data + .state .niri .global_space .outputs() @@ -345,10 +319,10 @@ impl Tty { }) .unwrap() .clone(); - let output_state = data.niri.output_state.get_mut(&output).unwrap(); + let output_state = data.state.niri.output_state.get_mut(&output).unwrap(); output_state.waiting_for_vblank = false; output_state.frame_clock.presented(presentation_time); - data.niri.queue_redraw(output); + data.state.niri.queue_redraw(output); } DrmEvent::Error(error) => error!("DRM error: {error}"), }; @@ -589,11 +563,25 @@ impl Tty { } } - fn change_vt(&mut self, vt: i32) { + pub fn change_vt(&mut self, vt: i32) { if let Err(err) = self.session.change_vt(vt) { error!("error changing VT: {err}"); } } + + pub fn suspend(&self) { + if let Err(err) = suspend() { + warn!("error suspending: {err:?}"); + } + } + + pub fn toggle_debug_tint(&mut self) { + if let Some(device) = self.output_device.as_mut() { + for (_, compositor) in &mut device.surfaces { + compositor.set_debug_flags(compositor.debug_flags() ^ DebugFlags::TINT); + } + } + } } fn refresh_interval(mode: DrmMode) -> Duration { diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 74485469..767bf7f4 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -3,7 +3,7 @@ use std::time::Duration; use smithay::backend::renderer::damage::OutputDamageTracker; use smithay::backend::renderer::gles::GlesRenderer; use smithay::backend::renderer::{DebugFlags, Renderer}; -use smithay::backend::winit::{self, WinitError, WinitEvent, WinitEventLoop, WinitGraphicsBackend}; +use smithay::backend::winit::{self, WinitError, WinitEvent, WinitGraphicsBackend}; use smithay::output::{Mode, Output, PhysicalProperties, Subpixel}; use smithay::reexports::calloop::timer::{TimeoutAction, Timer}; use smithay::reexports::calloop::LoopHandle; @@ -12,25 +12,23 @@ use smithay::reexports::winit::dpi::LogicalSize; use smithay::reexports::winit::window::WindowBuilder; use smithay::utils::Transform; -use crate::input::{BackendAction, CompositorMod}; -use crate::niri::{Data, OutputRenderElements}; +use crate::niri::OutputRenderElements; use crate::utils::get_monotonic_time; -use crate::Niri; +use crate::{LoopData, Niri}; pub struct Winit { output: Output, backend: WinitGraphicsBackend, - winit_event_loop: WinitEventLoop, damage_tracker: OutputDamageTracker, } impl Winit { - pub fn new(event_loop: LoopHandle) -> Self { + pub fn new(event_loop: LoopHandle) -> Self { let builder = WindowBuilder::new() .with_inner_size(LogicalSize::new(1280.0, 800.0)) // .with_resizable(false) .with_title("niri"); - let (backend, winit_event_loop) = winit::init_from_builder(builder).unwrap(); + let (backend, mut winit_event_loop) = winit::init_from_builder(builder).unwrap(); let mode = Mode { size: backend.window_size().physical_size, @@ -59,8 +57,39 @@ impl Winit { let timer = Timer::immediate(); event_loop .insert_source(timer, move |_, _, data| { - let winit = data.backend.winit().unwrap(); - winit.dispatch(&mut data.niri); + let res = winit_event_loop.dispatch_new_events(|event| match event { + WinitEvent::Resized { size, .. } => { + let winit = data.state.backend.winit(); + winit.output.change_current_state( + Some(Mode { + size, + refresh: 60_000, + }), + None, + None, + None, + ); + data.state.niri.output_resized(winit.output.clone()); + } + WinitEvent::Input(event) => data.state.process_input_event(event), + WinitEvent::Focus(_) => (), + WinitEvent::Refresh => data + .state + .niri + .queue_redraw(data.state.backend.winit().output.clone()), + }); + + // I want this to stop compiling if more errors are added. + #[allow(clippy::single_match)] + match res { + Err(WinitError::WindowClosed) => { + data.state.niri.stop_signal.stop(); + data.state + .niri + .remove_output(&data.state.backend.winit().output); + } + Ok(()) => (), + } TimeoutAction::ToDuration(Duration::from_micros(16667)) }) .unwrap(); @@ -68,7 +97,6 @@ impl Winit { Self { output, backend, - winit_event_loop, damage_tracker, } } @@ -88,56 +116,6 @@ impl Winit { niri.add_output(self.output.clone(), None); } - fn dispatch(&mut self, niri: &mut Niri) { - let renderer = self.backend.renderer(); - let res = self - .winit_event_loop - .dispatch_new_events(|event| match event { - WinitEvent::Resized { size, .. } => { - self.output.change_current_state( - Some(Mode { - size, - refresh: 60_000, - }), - None, - None, - None, - ); - niri.output_resized(self.output.clone()); - } - WinitEvent::Input(event) => { - match niri.process_input_event(CompositorMod::Alt, event) { - BackendAction::None => (), - BackendAction::ChangeVt(_) => (), - BackendAction::Suspend => (), - BackendAction::Screenshot => { - let active = niri.monitor_set.active_output().cloned(); - if let Some(active) = active { - if let Err(err) = niri.screenshot(renderer, &active) { - warn!("error taking screenshot: {err:?}"); - } - } - } - BackendAction::ToggleDebugTint => { - renderer.set_debug_flags(renderer.debug_flags() ^ DebugFlags::TINT); - } - } - } - WinitEvent::Focus(_) => (), - WinitEvent::Refresh => niri.queue_redraw(self.output.clone()), - }); - - // I want this to stop compiling if more errors are added. - #[allow(clippy::single_match)] - match res { - Err(WinitError::WindowClosed) => { - niri.stop_signal.stop(); - niri.remove_output(&self.output); - } - Ok(()) => (), - } - } - pub fn seat_name(&self) -> String { "winit".to_owned() } @@ -175,4 +153,9 @@ impl Winit { self.backend.window().request_redraw(); } } + + pub fn toggle_debug_tint(&mut self) { + let renderer = self.backend.renderer(); + renderer.set_debug_flags(renderer.debug_flags() ^ DebugFlags::TINT); + } } diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index e126f9df..035daafc 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -13,12 +13,11 @@ use smithay::wayland::shm::{ShmHandler, ShmState}; use smithay::{delegate_compositor, delegate_shm}; use super::xdg_shell; -use crate::niri::ClientState; -use crate::Niri; +use crate::niri::{ClientState, State}; -impl CompositorHandler for Niri { +impl CompositorHandler for State { fn compositor_state(&mut self) -> &mut CompositorState { - &mut self.compositor_state + &mut self.niri.compositor_state } fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { @@ -41,7 +40,7 @@ impl CompositorHandler for Niri { if surface == &root_surface { // This is a root surface commit. It might have mapped a previously-unmapped toplevel. - if let Entry::Occupied(entry) = self.unmapped_windows.entry(surface.clone()) { + if let Entry::Occupied(entry) = self.niri.unmapped_windows.entry(surface.clone()) { let is_mapped = with_renderer_surface_state(surface, |state| state.buffer().is_some()); @@ -50,9 +49,11 @@ impl CompositorHandler for Niri { let window = entry.remove(); window.on_commit(); - let output = self.monitor_set.active_output().unwrap().clone(); - self.monitor_set.add_window_to_output(&output, window, true); - self.queue_redraw(output); + let output = self.niri.monitor_set.active_output().unwrap().clone(); + self.niri + .monitor_set + .add_window_to_output(&output, window, true); + self.niri.queue_redraw(output); return; } @@ -63,7 +64,7 @@ impl CompositorHandler for Niri { } // This is a commit of a previously-mapped root or a non-toplevel root. - if let Some((window, output)) = self.monitor_set.find_window_and_output(surface) { + if let Some((window, output)) = self.niri.monitor_set.find_window_and_output(surface) { // This is a commit of a previously-mapped toplevel. window.on_commit(); @@ -73,16 +74,16 @@ impl CompositorHandler for Niri { if !is_mapped { // The toplevel got unmapped. - self.monitor_set.remove_window(&window); - self.unmapped_windows.insert(surface.clone(), window); - self.queue_redraw(output); + self.niri.monitor_set.remove_window(&window); + self.niri.unmapped_windows.insert(surface.clone(), window); + self.niri.queue_redraw(output); return; } // The toplevel remains mapped. - self.monitor_set.update_window(&window); + self.niri.monitor_set.update_window(&window); - self.queue_redraw(output); + self.niri.queue_redraw(output); return; } @@ -90,21 +91,21 @@ impl CompositorHandler for Niri { } // This is a commit of a non-root or a non-toplevel root. - let root_window_output = self.monitor_set.find_window_and_output(&root_surface); + let root_window_output = self.niri.monitor_set.find_window_and_output(&root_surface); if let Some((window, output)) = root_window_output { window.on_commit(); - self.monitor_set.update_window(&window); - self.queue_redraw(output); + self.niri.monitor_set.update_window(&window); + self.niri.queue_redraw(output); return; } // This might be a popup. self.popups_handle_commit(surface); - if let Some(popup) = self.popups.find_popup(surface) { + if let Some(popup) = self.niri.popups.find_popup(surface) { if let Ok(root) = find_popup_root_surface(&popup) { - let root_window_output = self.monitor_set.find_window_and_output(&root); + let root_window_output = self.niri.monitor_set.find_window_and_output(&root); if let Some((_window, output)) = root_window_output { - self.queue_redraw(output); + self.niri.queue_redraw(output); } } } @@ -114,15 +115,15 @@ impl CompositorHandler for Niri { } } -impl BufferHandler for Niri { +impl BufferHandler for State { fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {} } -impl ShmHandler for Niri { +impl ShmHandler for State { fn shm_state(&self) -> &ShmState { - &self.shm_state + &self.niri.shm_state } } -delegate_compositor!(Niri); -delegate_shm!(Niri); +delegate_compositor!(State); +delegate_shm!(State); diff --git a/src/handlers/layer_shell.rs b/src/handlers/layer_shell.rs index 236ae7ab..39be2345 100644 --- a/src/handlers/layer_shell.rs +++ b/src/handlers/layer_shell.rs @@ -9,11 +9,11 @@ use smithay::wayland::shell::wlr_layer::{ WlrLayerShellState, }; -use crate::niri::Niri; +use crate::niri::State; -impl WlrLayerShellHandler for Niri { +impl WlrLayerShellHandler for State { fn shell_state(&mut self) -> &mut WlrLayerShellState { - &mut self.layer_shell_state + &mut self.niri.layer_shell_state } fn new_layer_surface( @@ -26,7 +26,7 @@ impl WlrLayerShellHandler for Niri { let output = wl_output .as_ref() .and_then(Output::from_resource) - .or_else(|| self.monitor_set.active_output().cloned()) + .or_else(|| self.niri.monitor_set.active_output().cloned()) .unwrap(); let mut map = layer_map_for_output(&output); map.map_layer(&LayerSurface::new(surface, namespace)) @@ -35,7 +35,7 @@ impl WlrLayerShellHandler for Niri { fn layer_destroyed(&mut self, surface: WlrLayerSurface) { let output = if let Some((output, mut map, layer)) = - self.monitor_set.outputs().find_map(|o| { + self.niri.monitor_set.outputs().find_map(|o| { let map = layer_map_for_output(o); let layer = map .layers() @@ -49,15 +49,16 @@ impl WlrLayerShellHandler for Niri { None }; if let Some(output) = output { - self.queue_redraw(output); + self.niri.queue_redraw(output); } } } -delegate_layer_shell!(Niri); +delegate_layer_shell!(State); -impl Niri { +impl State { pub fn layer_shell_handle_commit(&mut self, surface: &WlSurface) { let Some(output) = self + .niri .monitor_set .outputs() .find(|o| { @@ -95,6 +96,6 @@ impl Niri { } drop(map); - self.queue_redraw(output); + self.niri.queue_redraw(output); } } diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 782b7b37..7e643d5d 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -16,62 +16,62 @@ use smithay::{ delegate_seat, delegate_tablet_manager, }; -use crate::Niri; +use crate::niri::State; -impl SeatHandler for Niri { +impl SeatHandler for State { type KeyboardFocus = WlSurface; type PointerFocus = WlSurface; - fn seat_state(&mut self) -> &mut SeatState { - &mut self.seat_state + fn seat_state(&mut self) -> &mut SeatState { + &mut self.niri.seat_state } fn cursor_image(&mut self, _seat: &Seat, image: CursorImageStatus) { - self.cursor_image = image; + self.niri.cursor_image = image; // FIXME: more granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { - let dh = &self.display_handle; + let dh = &self.niri.display_handle; let client = focused.and_then(|s| dh.get_client(s.id()).ok()); set_data_device_focus(dh, seat, client); } } -delegate_seat!(Niri); -delegate_tablet_manager!(Niri); -delegate_pointer_gestures!(Niri); +delegate_seat!(State); +delegate_tablet_manager!(State); +delegate_pointer_gestures!(State); -impl DataDeviceHandler for Niri { +impl DataDeviceHandler for State { type SelectionUserData = (); fn data_device_state(&self) -> &DataDeviceState { - &self.data_device_state + &self.niri.data_device_state } } -impl ClientDndGrabHandler for Niri { +impl ClientDndGrabHandler for State { fn started( &mut self, _source: Option, icon: Option, _seat: Seat, ) { - self.dnd_icon = icon; + self.niri.dnd_icon = icon; // FIXME: more granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } fn dropped(&mut self, _seat: Seat) { - self.dnd_icon = None; + self.niri.dnd_icon = None; // FIXME: more granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } } -impl ServerDndGrabHandler for Niri {} +impl ServerDndGrabHandler for State {} -delegate_data_device!(Niri); +delegate_data_device!(State); -delegate_output!(Niri); +delegate_output!(State); -delegate_presentation!(Niri); +delegate_presentation!(State); diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 2f4448a4..b1dd9d4f 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -13,11 +13,11 @@ use smithay::wayland::shell::xdg::{ }; use crate::layout::{configure_new_window, output_size}; -use crate::Niri; +use crate::niri::State; -impl XdgShellHandler for Niri { +impl XdgShellHandler for State { fn xdg_shell_state(&mut self) -> &mut XdgShellState { - &mut self.xdg_shell_state + &mut self.niri.xdg_shell_state } fn new_toplevel(&mut self, surface: ToplevelSurface) { @@ -25,18 +25,18 @@ impl XdgShellHandler for Niri { let window = Window::new(surface); // Tell the surface the preferred size and bounds for its likely output. - let output = self.monitor_set.active_output().unwrap(); + let output = self.niri.monitor_set.active_output().unwrap(); configure_new_window(output_size(output), &window); // At the moment of creation, xdg toplevels must have no buffer. - let existing = self.unmapped_windows.insert(wl_surface, window); + let existing = self.niri.unmapped_windows.insert(wl_surface, window); assert!(existing.is_none()); } fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) { // FIXME: adjust the geometry so the popup doesn't overflow at least off the top and bottom // screen edges, and ideally off the view size. - if let Err(err) = self.popups.track_popup(PopupKind::Xdg(surface)) { + if let Err(err) = self.niri.popups.track_popup(PopupKind::Xdg(surface)) { warn!("error tracking popup: {err:?}"); } } @@ -101,17 +101,19 @@ impl XdgShellHandler for Niri { // location and configure size here, but the surface should be rendered fullscreen // independently from its buffer size if let Some((window, current_output)) = self + .niri .monitor_set .find_window_and_output(surface.wl_surface()) { if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) { if requested_output != current_output { - self.monitor_set + self.niri + .monitor_set .move_window_to_output(window.clone(), &requested_output); } } - self.monitor_set.set_fullscreen(&window, true); + self.niri.monitor_set.set_fullscreen(&window, true); } } @@ -122,38 +124,45 @@ impl XdgShellHandler for Niri { fn unfullscreen_request(&mut self, surface: ToplevelSurface) { if let Some((window, _)) = self + .niri .monitor_set .find_window_and_output(surface.wl_surface()) { - self.monitor_set.set_fullscreen(&window, false); + self.niri.monitor_set.set_fullscreen(&window, false); } } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { - if self.unmapped_windows.remove(surface.wl_surface()).is_some() { + if self + .niri + .unmapped_windows + .remove(surface.wl_surface()) + .is_some() + { // An unmapped toplevel got destroyed. return; } let (window, output) = self + .niri .monitor_set .find_window_and_output(surface.wl_surface()) .unwrap(); - self.monitor_set.remove_window(&window); - self.queue_redraw(output); + self.niri.monitor_set.remove_window(&window); + self.niri.queue_redraw(output); } fn popup_destroyed(&mut self, surface: PopupSurface) { if let Ok(root) = find_popup_root_surface(&surface.into()) { - let root_window_output = self.monitor_set.find_window_and_output(&root); + let root_window_output = self.niri.monitor_set.find_window_and_output(&root); if let Some((_window, output)) = root_window_output { - self.queue_redraw(output); + self.niri.queue_redraw(output); } } } } -delegate_xdg_shell!(Niri); +delegate_xdg_shell!(State); pub fn send_initial_configure_if_needed(window: &Window) { let initial_configure_sent = with_states(window.toplevel().wl_surface(), |states| { @@ -171,12 +180,12 @@ pub fn send_initial_configure_if_needed(window: &Window) { } } -impl Niri { +impl State { /// Should be called on `WlSurface::commit` pub fn popups_handle_commit(&mut self, surface: &WlSurface) { - self.popups.commit(surface); + self.niri.popups.commit(surface); - if let Some(popup) = self.popups.find_popup(surface) { + if let Some(popup) = self.niri.popups.find_popup(surface) { let PopupKind::Xdg(ref popup) = popup; let initial_configure_sent = with_states(surface, |states| { states diff --git a/src/input.rs b/src/input.rs index f392ddf4..57037403 100644 --- a/src/input.rs +++ b/src/input.rs @@ -17,7 +17,7 @@ use smithay::input::pointer::{ use smithay::utils::SERIAL_COUNTER; use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait}; -use crate::niri::Niri; +use crate::niri::State; use crate::utils::get_monotonic_time; enum Action { @@ -56,14 +56,6 @@ enum Action { ToggleFullWidth, } -pub enum BackendAction { - None, - ChangeVt(i32), - Suspend, - Screenshot, - ToggleDebugTint, -} - pub enum CompositorMod { Super, Alt, @@ -148,12 +140,8 @@ fn action(comp_mod: CompositorMod, keysym: KeysymHandle, mods: ModifiersState) - } } -impl Niri { - pub fn process_input_event( - &mut self, - comp_mod: CompositorMod, - event: InputEvent, - ) -> BackendAction { +impl State { + pub fn process_input_event(&mut self, event: InputEvent) { let _span = tracy_client::span!("process_input_event"); trace!("process_input_event"); @@ -161,14 +149,18 @@ impl Niri { // doesn't always trigger due to damage, etc. So run it here right before it might prove // important. Besides, animations affect the input, so it's best to have up-to-date values // here. - self.monitor_set.advance_animations(get_monotonic_time()); + self.niri + .monitor_set + .advance_animations(get_monotonic_time()); + + let comp_mod = self.backend.mod_key(); match event { InputEvent::Keyboard { event, .. } => { let serial = SERIAL_COUNTER.next_serial(); let time = Event::time_msec(&event); - let action = self.seat.get_keyboard().unwrap().input( + let action = self.niri.seat.get_keyboard().unwrap().input( self, event.key_code(), event.state(), @@ -188,16 +180,16 @@ impl Niri { Action::None => unreachable!(), Action::Quit => { info!("quitting because quit bind was pressed"); - self.stop_signal.stop() + self.niri.stop_signal.stop() } Action::ChangeVt(vt) => { - return BackendAction::ChangeVt(vt); + self.backend.change_vt(vt); } Action::Suspend => { - return BackendAction::Suspend; + self.backend.suspend(); } Action::ToggleDebugTint => { - return BackendAction::ToggleDebugTint; + self.backend.toggle_debug_tint(); } Action::Spawn(command) => { if let Err(err) = Command::new(command).spawn() { @@ -205,132 +197,139 @@ impl Niri { } } Action::Screenshot => { - return BackendAction::Screenshot; + let active = self.niri.monitor_set.active_output().cloned(); + if let Some(active) = active { + if let Err(err) = + self.niri.screenshot(self.backend.renderer(), &active) + { + warn!("error taking screenshot: {err:?}"); + } + } } Action::CloseWindow => { - if let Some(window) = self.monitor_set.focus() { + if let Some(window) = self.niri.monitor_set.focus() { window.toplevel().send_close(); } } Action::ToggleFullscreen => { - let focus = self.monitor_set.focus().cloned(); + let focus = self.niri.monitor_set.focus().cloned(); if let Some(window) = focus { - self.monitor_set.toggle_fullscreen(&window); + self.niri.monitor_set.toggle_fullscreen(&window); } } Action::MoveLeft => { - self.monitor_set.move_left(); + self.niri.monitor_set.move_left(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::MoveRight => { - self.monitor_set.move_right(); + self.niri.monitor_set.move_right(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::MoveDown => { - self.monitor_set.move_down(); + self.niri.monitor_set.move_down(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::MoveUp => { - self.monitor_set.move_up(); + self.niri.monitor_set.move_up(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::FocusLeft => { - self.monitor_set.focus_left(); + self.niri.monitor_set.focus_left(); } Action::FocusRight => { - self.monitor_set.focus_right(); + self.niri.monitor_set.focus_right(); } Action::FocusDown => { - self.monitor_set.focus_down(); + self.niri.monitor_set.focus_down(); } Action::FocusUp => { - self.monitor_set.focus_up(); + self.niri.monitor_set.focus_up(); } Action::MoveToWorkspaceDown => { - self.monitor_set.move_to_workspace_down(); + self.niri.monitor_set.move_to_workspace_down(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::MoveToWorkspaceUp => { - self.monitor_set.move_to_workspace_up(); + self.niri.monitor_set.move_to_workspace_up(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::SwitchWorkspaceDown => { - self.monitor_set.switch_workspace_down(); + self.niri.monitor_set.switch_workspace_down(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::SwitchWorkspaceUp => { - self.monitor_set.switch_workspace_up(); + self.niri.monitor_set.switch_workspace_up(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::ConsumeIntoColumn => { - self.monitor_set.consume_into_column(); + self.niri.monitor_set.consume_into_column(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::ExpelFromColumn => { - self.monitor_set.expel_from_column(); + self.niri.monitor_set.expel_from_column(); // FIXME: granular - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } Action::ToggleWidth => { - self.monitor_set.toggle_width(); + self.niri.monitor_set.toggle_width(); } Action::ToggleFullWidth => { - self.monitor_set.toggle_full_width(); + self.niri.monitor_set.toggle_full_width(); } Action::FocusMonitorLeft => { - if let Some(output) = self.output_left() { - self.monitor_set.focus_output(&output); + if let Some(output) = self.niri.output_left() { + self.niri.monitor_set.focus_output(&output); self.move_cursor_to_output(&output); } } Action::FocusMonitorRight => { - if let Some(output) = self.output_right() { - self.monitor_set.focus_output(&output); + if let Some(output) = self.niri.output_right() { + self.niri.monitor_set.focus_output(&output); self.move_cursor_to_output(&output); } } Action::FocusMonitorDown => { - if let Some(output) = self.output_down() { - self.monitor_set.focus_output(&output); + if let Some(output) = self.niri.output_down() { + self.niri.monitor_set.focus_output(&output); self.move_cursor_to_output(&output); } } Action::FocusMonitorUp => { - if let Some(output) = self.output_up() { - self.monitor_set.focus_output(&output); + if let Some(output) = self.niri.output_up() { + self.niri.monitor_set.focus_output(&output); self.move_cursor_to_output(&output); } } Action::MoveToMonitorLeft => { - if let Some(output) = self.output_left() { - self.monitor_set.move_to_output(&output); + if let Some(output) = self.niri.output_left() { + self.niri.monitor_set.move_to_output(&output); self.move_cursor_to_output(&output); } } Action::MoveToMonitorRight => { - if let Some(output) = self.output_right() { - self.monitor_set.move_to_output(&output); + if let Some(output) = self.niri.output_right() { + self.niri.monitor_set.move_to_output(&output); self.move_cursor_to_output(&output); } } Action::MoveToMonitorDown => { - if let Some(output) = self.output_down() { - self.monitor_set.move_to_output(&output); + if let Some(output) = self.niri.output_down() { + self.niri.monitor_set.move_to_output(&output); self.move_cursor_to_output(&output); } } Action::MoveToMonitorUp => { - if let Some(output) = self.output_up() { - self.monitor_set.move_to_output(&output); + if let Some(output) = self.niri.output_up() { + self.niri.monitor_set.move_to_output(&output); self.move_cursor_to_output(&output); } } @@ -340,7 +339,7 @@ impl Niri { InputEvent::PointerMotion { event, .. } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); let mut pos = pointer.current_location(); pos += event.delta(); @@ -349,9 +348,9 @@ impl Niri { let mut min_y = i32::MAX; let mut max_x = 0; let mut max_y = 0; - for output in self.global_space.outputs() { + for output in self.niri.global_space.outputs() { // FIXME: smarter clamping. - let geom = self.global_space.output_geometry(output).unwrap(); + let geom = self.niri.global_space.output_geometry(output).unwrap(); min_x = min_x.min(geom.loc.x); min_y = min_y.min(geom.loc.y); max_x = max_x.max(geom.loc.x + geom.size.w); @@ -361,7 +360,7 @@ impl Niri { pos.x = pos.x.clamp(min_x as f64, max_x as f64); pos.y = pos.y.clamp(min_y as f64, max_y as f64); - let under = self.surface_under_and_global_space(pos); + let under = self.niri.surface_under_and_global_space(pos); pointer.motion( self, @@ -385,20 +384,20 @@ impl Niri { // Redraw to update the cursor position. // FIXME: redraw only outputs overlapping the cursor. - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } InputEvent::PointerMotionAbsolute { event, .. } => { - let output = self.global_space.outputs().next().unwrap(); + let output = self.niri.global_space.outputs().next().unwrap(); - let output_geo = self.global_space.output_geometry(output).unwrap(); + let output_geo = self.niri.global_space.output_geometry(output).unwrap(); let pos = event.position_transformed(output_geo.size) + output_geo.loc.to_f64(); let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); - let under = self.surface_under_and_global_space(pos); + let under = self.niri.surface_under_and_global_space(pos); pointer.motion( self, @@ -412,10 +411,10 @@ impl Niri { // Redraw to update the cursor position. // FIXME: redraw only outputs overlapping the cursor. - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } InputEvent::PointerButton { event, .. } => { - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); let serial = SERIAL_COUNTER.next_serial(); @@ -424,12 +423,12 @@ impl Niri { let button_state = event.state(); if ButtonState::Pressed == button_state && !pointer.is_grabbed() { - if let Some(window) = self.window_under_cursor() { + if let Some(window) = self.niri.window_under_cursor() { let window = window.clone(); - self.monitor_set.activate_window(&window); + self.niri.monitor_set.activate_window(&window); } else { - let output = self.output_under_cursor().unwrap(); - self.monitor_set.activate_output(&output); + let output = self.niri.output_under_cursor().unwrap(); + self.niri.monitor_set.activate_output(&output); } }; @@ -473,21 +472,21 @@ impl Niri { frame = frame.stop(Axis::Vertical); } - self.seat.get_pointer().unwrap().axis(self, frame); + self.niri.seat.get_pointer().unwrap().axis(self, frame); } InputEvent::TabletToolAxis { event, .. } => { // FIXME: allow mapping tablet to different outputs. - let output = self.global_space.outputs().next().unwrap(); + let output = self.niri.global_space.outputs().next().unwrap(); - let output_geo = self.global_space.output_geometry(output).unwrap(); + let output_geo = self.niri.global_space.output_geometry(output).unwrap(); let pos = event.position_transformed(output_geo.size) + output_geo.loc.to_f64(); let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); - let under = self.surface_under_and_global_space(pos); + let under = self.niri.surface_under_and_global_space(pos); pointer.motion( self, @@ -499,7 +498,7 @@ impl Niri { }, ); - let tablet_seat = self.seat.tablet_seat(); + let tablet_seat = self.niri.seat.tablet_seat(); let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); let tool = tablet_seat.get_tool(&event.tool()); if let (Some(tablet), Some(tool)) = (tablet, tool) { @@ -533,10 +532,10 @@ impl Niri { // Redraw to update the cursor position. // FIXME: redraw only outputs overlapping the cursor. - self.queue_redraw_all(); + self.niri.queue_redraw_all(); } InputEvent::TabletToolTip { event, .. } => { - let tool = self.seat.tablet_seat().get_tool(&event.tool()); + let tool = self.niri.seat.tablet_seat().get_tool(&event.tool()); if let Some(tool) = tool { match event.tip_state() { @@ -544,14 +543,14 @@ impl Niri { let serial = SERIAL_COUNTER.next_serial(); tool.tip_down(serial, event.time_msec()); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); if !pointer.is_grabbed() { - if let Some(window) = self.window_under_cursor() { + if let Some(window) = self.niri.window_under_cursor() { let window = window.clone(); - self.monitor_set.activate_window(&window); + self.niri.monitor_set.activate_window(&window); } else { - let output = self.output_under_cursor().unwrap(); - self.monitor_set.activate_output(&output); + let output = self.niri.output_under_cursor().unwrap(); + self.niri.monitor_set.activate_output(&output); } }; } @@ -563,17 +562,17 @@ impl Niri { } InputEvent::TabletToolProximity { event, .. } => { // FIXME: allow mapping tablet to different outputs. - let output = self.global_space.outputs().next().unwrap(); + let output = self.niri.global_space.outputs().next().unwrap(); - let output_geo = self.global_space.output_geometry(output).unwrap(); + let output_geo = self.niri.global_space.output_geometry(output).unwrap(); let pos = event.position_transformed(output_geo.size) + output_geo.loc.to_f64(); let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); - let under = self.surface_under_and_global_space(pos); + let under = self.niri.surface_under_and_global_space(pos); pointer.motion( self, @@ -585,8 +584,8 @@ impl Niri { }, ); - let tablet_seat = self.seat.tablet_seat(); - let tool = tablet_seat.add_tool::(&self.display_handle, &event.tool()); + let tablet_seat = self.niri.seat.tablet_seat(); + let tool = tablet_seat.add_tool::(&self.niri.display_handle, &event.tool()); let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); if let (Some(under), Some(tablet)) = (under, tablet) { match event.state() { @@ -602,7 +601,7 @@ impl Niri { } } InputEvent::TabletToolButton { event, .. } => { - let tool = self.seat.tablet_seat().get_tool(&event.tool()); + let tool = self.niri.seat.tablet_seat().get_tool(&event.tool()); if let Some(tool) = tool { tool.button( @@ -615,14 +614,15 @@ impl Niri { } InputEvent::DeviceAdded { device } => { if device.has_capability(DeviceCapability::TabletTool) { - self.seat - .tablet_seat() - .add_tablet::(&self.display_handle, &TabletDescriptor::from(&device)); + self.niri.seat.tablet_seat().add_tablet::( + &self.niri.display_handle, + &TabletDescriptor::from(&device), + ); } } InputEvent::DeviceRemoved { device } => { if device.has_capability(DeviceCapability::TabletTool) { - let tablet_seat = self.seat.tablet_seat(); + let tablet_seat = self.niri.seat.tablet_seat(); tablet_seat.remove_tablet(&TabletDescriptor::from(&device)); @@ -634,7 +634,7 @@ impl Niri { } InputEvent::GestureSwipeBegin { event } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_swipe_begin( self, &GestureSwipeBeginEvent { @@ -645,7 +645,7 @@ impl Niri { ); } InputEvent::GestureSwipeUpdate { event } => { - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_swipe_update( self, &GestureSwipeUpdateEvent { @@ -656,7 +656,7 @@ impl Niri { } InputEvent::GestureSwipeEnd { event } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_swipe_end( self, &GestureSwipeEndEvent { @@ -668,7 +668,7 @@ impl Niri { } InputEvent::GesturePinchBegin { event } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_pinch_begin( self, &GesturePinchBeginEvent { @@ -679,7 +679,7 @@ impl Niri { ); } InputEvent::GesturePinchUpdate { event } => { - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_pinch_update( self, &GesturePinchUpdateEvent { @@ -694,7 +694,7 @@ impl Niri { } InputEvent::GesturePinchEnd { event } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_pinch_end( self, &GesturePinchEndEvent { @@ -706,7 +706,7 @@ impl Niri { } InputEvent::GestureHoldBegin { event } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_hold_begin( self, &GestureHoldBeginEvent { @@ -718,7 +718,7 @@ impl Niri { } InputEvent::GestureHoldEnd { event } => { let serial = SERIAL_COUNTER.next_serial(); - let pointer = self.seat.get_pointer().unwrap(); + let pointer = self.niri.seat.get_pointer().unwrap(); pointer.gesture_hold_end( self, &GestureHoldEndEvent { @@ -735,8 +735,6 @@ impl Niri { InputEvent::TouchFrame { .. } => (), InputEvent::Special(_) => (), } - - BackendAction::None } pub fn process_libinput_event(&mut self, event: &mut InputEvent) { diff --git a/src/main.rs b/src/main.rs index c5912c6f..3b9014c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,8 +16,9 @@ use std::env; use std::ffi::OsString; use clap::Parser; -use niri::{Data, Niri}; +use niri::{Niri, State}; use smithay::reexports::calloop::EventLoop; +use smithay::reexports::wayland_server::Display; use tracing_subscriber::EnvFilter; #[derive(Parser)] @@ -28,6 +29,11 @@ struct Cli { command: Vec, } +struct LoopData { + display: Display, + state: State, +} + fn main() { env::set_var("RUST_BACKTRACE", "1"); @@ -43,7 +49,9 @@ fn main() { let _client = tracy_client::Client::start(); let mut event_loop = EventLoop::try_new().unwrap(); - let mut data = Data::new(event_loop.handle(), event_loop.get_signal()); + let mut display = Display::new().unwrap(); + let state = State::new(event_loop.handle(), event_loop.get_signal(), &mut display); + let mut data = LoopData { display, state }; if let Some((command, args)) = cli.command.split_first() { if let Err(err) = std::process::Command::new(command).args(args).spawn() { @@ -56,9 +64,9 @@ fn main() { let _span = tracy_client::span!("loop callback"); // These should be called periodically, before flushing the clients. - data.niri.monitor_set.refresh(); - data.niri.popups.cleanup(); - data.niri.update_focus(); + data.state.niri.monitor_set.refresh(); + data.state.niri.popups.cleanup(); + data.state.update_focus(); { let _span = tracy_client::span!("flush_clients"); diff --git a/src/niri.rs b/src/niri.rs index adc97b1e..364acf1d 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -58,10 +58,11 @@ use crate::dbus::mutter_service_channel::ServiceChannel; use crate::frame_clock::FrameClock; use crate::layout::{MonitorRenderElement, MonitorSet}; use crate::utils::{center, get_monotonic_time, load_default_cursor}; +use crate::LoopData; pub struct Niri { pub start_time: std::time::Instant, - pub event_loop: LoopHandle<'static, Data>, + pub event_loop: LoopHandle<'static, LoopData>, pub stop_signal: LoopSignal, pub display_handle: DisplayHandle, @@ -84,14 +85,14 @@ pub struct Niri { pub layer_shell_state: WlrLayerShellState, pub shm_state: ShmState, pub output_manager_state: OutputManagerState, - pub seat_state: SeatState, + pub seat_state: SeatState, pub tablet_state: TabletManagerState, pub pointer_gestures_state: PointerGesturesState, pub data_device_state: DataDeviceState, pub popups: PopupManager, pub presentation_state: PresentationState, - pub seat: Seat, + pub seat: Seat, pub pointer_buffer: Option<(TextureBuffer, Point)>, pub cursor_image: CursorImageStatus, @@ -112,14 +113,17 @@ pub struct OutputState { pub frame_clock: FrameClock, } -pub struct Data { - pub display: Display, +pub struct State { pub backend: Backend, pub niri: Niri, } -impl Data { - pub fn new(event_loop: LoopHandle<'static, Self>, stop_signal: LoopSignal) -> Self { +impl State { + pub fn new( + event_loop: LoopHandle<'static, LoopData>, + stop_signal: LoopSignal, + display: &mut Display, + ) -> Self { let has_display = env::var_os("WAYLAND_DISPLAY").is_some() || env::var_os("DISPLAY").is_some(); @@ -129,45 +133,76 @@ impl Data { Backend::Tty(Tty::new(event_loop.clone())) }; - let mut display = Display::new().unwrap(); - let mut niri = Niri::new(event_loop, stop_signal, &mut display, backend.seat_name()); + let mut niri = Niri::new(event_loop, stop_signal, display, backend.seat_name()); backend.init(&mut niri); - Self { - display, - backend, - niri, + Self { backend, niri } + } + + pub fn move_cursor(&mut self, location: Point) { + let under = self.niri.surface_under_and_global_space(location); + self.niri.seat.get_pointer().unwrap().motion( + self, + under, + &MotionEvent { + location, + serial: SERIAL_COUNTER.next_serial(), + time: get_monotonic_time().as_millis() as u32, + }, + ); + // FIXME: granular + self.niri.queue_redraw_all(); + } + + pub fn move_cursor_to_output(&mut self, output: &Output) { + let geo = self.niri.global_space.output_geometry(output).unwrap(); + self.move_cursor(center(geo).to_f64()); + } + + pub fn update_focus(&mut self) { + let focus = self.niri.layer_surface_focus().or_else(|| { + self.niri + .monitor_set + .focus() + .map(|win| win.toplevel().wl_surface().clone()) + }); + let keyboard = self.niri.seat.get_keyboard().unwrap(); + if keyboard.current_focus() != focus { + keyboard.set_focus(self, focus, SERIAL_COUNTER.next_serial()); + // FIXME: can be more granular. + self.niri.queue_redraw_all(); } } } impl Niri { pub fn new( - event_loop: LoopHandle<'static, Data>, + event_loop: LoopHandle<'static, LoopData>, stop_signal: LoopSignal, - display: &mut Display, +