From beba87354a1fd30a95eaaf6c98eec72797e4baa7 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 11 May 2024 13:21:05 +0400 Subject: Group input-related things in a subfolder --- src/handlers/xdg_shell.rs | 2 +- src/input.rs | 2561 ------------------------------------------ src/input/mod.rs | 2565 +++++++++++++++++++++++++++++++++++++++++++ src/input/resize_grab.rs | 176 +++ src/input/scroll_tracker.rs | 40 + src/input/swipe_tracker.rs | 87 ++ src/layout/monitor.rs | 2 +- src/layout/workspace.rs | 2 +- src/lib.rs | 3 - src/niri.rs | 2 +- src/resize_grab.rs | 176 --- src/scroll_tracker.rs | 40 - src/swipe_tracker.rs | 87 -- 13 files changed, 2872 insertions(+), 2871 deletions(-) delete mode 100644 src/input.rs create mode 100644 src/input/mod.rs create mode 100644 src/input/resize_grab.rs create mode 100644 src/input/scroll_tracker.rs create mode 100644 src/input/swipe_tracker.rs delete mode 100644 src/resize_grab.rs delete mode 100644 src/scroll_tracker.rs delete mode 100644 src/swipe_tracker.rs (limited to 'src') diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index efaa6dfa..6f36fe8d 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -30,10 +30,10 @@ use smithay::{ delegate_kde_decoration, delegate_xdg_decoration, delegate_xdg_foreign, delegate_xdg_shell, }; +use crate::input::resize_grab::ResizeGrab; use crate::input::DOUBLE_CLICK_TIME; use crate::layout::workspace::ColumnWidth; use crate::niri::{PopupGrabState, State}; -use crate::resize_grab::ResizeGrab; use crate::utils::{get_monotonic_time, ResizeEdge}; use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped, WindowRef}; diff --git a/src/input.rs b/src/input.rs deleted file mode 100644 index ee618068..00000000 --- a/src/input.rs +++ /dev/null @@ -1,2561 +0,0 @@ -use std::any::Any; -use std::collections::hash_map::Entry; -use std::collections::HashSet; -use std::time::Duration; - -use calloop::timer::{TimeoutAction, Timer}; -use input::event::gesture::GestureEventCoordinates as _; -use niri_config::{Action, Bind, Binds, Key, Modifiers, Trigger}; -use niri_ipc::LayoutSwitchTarget; -use smithay::backend::input::{ - AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event, - GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, - InputBackend, InputEvent, KeyState, KeyboardKeyEvent, MouseButton, PointerAxisEvent, - PointerButtonEvent, PointerMotionEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, - TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, -}; -use smithay::backend::libinput::LibinputInputBackend; -use smithay::input::keyboard::{keysyms, FilterResult, Keysym, ModifiersState}; -use smithay::input::pointer::{ - AxisFrame, ButtonEvent, CursorImageStatus, Focus, GestureHoldBeginEvent, GestureHoldEndEvent, - GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, - GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, - MotionEvent, RelativeMotionEvent, -}; -use smithay::input::touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent}; -use smithay::utils::{Logical, Point, SERIAL_COUNTER}; -use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint}; -use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait}; - -use crate::niri::State; -use crate::resize_grab::ResizeGrab; -use crate::ui::screenshot_ui::ScreenshotUi; -use crate::utils::spawning::spawn; -use crate::utils::{center, get_monotonic_time, ResizeEdge}; - -pub const DOUBLE_CLICK_TIME: Duration = Duration::from_millis(400); - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CompositorMod { - Super, - Alt, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct TabletData { - pub aspect_ratio: f64, -} - -impl State { - pub fn process_input_event(&mut self, event: InputEvent) - where - I::Device: 'static, // Needed for downcasting. - { - let _span = tracy_client::span!("process_input_event"); - - // A bit of a hack, but animation end runs some logic (i.e. workspace clean-up) and it - // 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.niri.layout.advance_animations(get_monotonic_time()); - - if self.niri.monitors_active { - // Notify the idle-notifier of activity. - if should_notify_activity(&event) { - let _span = tracy_client::span!("IdleNotifierState::notify_activity"); - self.niri - .idle_notifier_state - .notify_activity(&self.niri.seat); - } - } else { - // Power on monitors if they were off. - if should_activate_monitors(&event) { - self.niri.activate_monitors(&mut self.backend); - - // Notify the idle-notifier of activity only if we're also powering on the - // monitors. - self.niri - .idle_notifier_state - .notify_activity(&self.niri.seat); - } - } - - let hide_hotkey_overlay = - self.niri.hotkey_overlay.is_open() && should_hide_hotkey_overlay(&event); - - let hide_exit_confirm_dialog = self - .niri - .exit_confirm_dialog - .as_ref() - .map_or(false, |d| d.is_open()) - && should_hide_exit_confirm_dialog(&event); - - use InputEvent::*; - match event { - DeviceAdded { device } => self.on_device_added(device), - DeviceRemoved { device } => self.on_device_removed(device), - Keyboard { event } => self.on_keyboard::(event), - PointerMotion { event } => self.on_pointer_motion::(event), - PointerMotionAbsolute { event } => self.on_pointer_motion_absolute::(event), - PointerButton { event } => self.on_pointer_button::(event), - PointerAxis { event } => self.on_pointer_axis::(event), - TabletToolAxis { event } => self.on_tablet_tool_axis::(event), - TabletToolTip { event } => self.on_tablet_tool_tip::(event), - TabletToolProximity { event } => self.on_tablet_tool_proximity::(event), - TabletToolButton { event } => self.on_tablet_tool_button::(event), - GestureSwipeBegin { event } => self.on_gesture_swipe_begin::(event), - GestureSwipeUpdate { event } => self.on_gesture_swipe_update::(event), - GestureSwipeEnd { event } => self.on_gesture_swipe_end::(event), - GesturePinchBegin { event } => self.on_gesture_pinch_begin::(event), - GesturePinchUpdate { event } => self.on_gesture_pinch_update::(event), - GesturePinchEnd { event } => self.on_gesture_pinch_end::(event), - GestureHoldBegin { event } => self.on_gesture_hold_begin::(event), - GestureHoldEnd { event } => self.on_gesture_hold_end::(event), - TouchDown { event } => self.on_touch_down::(event), - TouchMotion { event } => self.on_touch_motion::(event), - TouchUp { event } => self.on_touch_up::(event), - TouchCancel { event } => self.on_touch_cancel::(event), - TouchFrame { event } => self.on_touch_frame::(event), - SwitchToggle { .. } => (), - Special(_) => (), - } - - // Do this last so that screenshot still gets it. - // FIXME: do this in a less cursed fashion somehow. - if hide_hotkey_overlay && self.niri.hotkey_overlay.hide() { - self.niri.queue_redraw_all(); - } - - if let Some(dialog) = &mut self.niri.exit_confirm_dialog { - if hide_exit_confirm_dialog && dialog.hide() { - self.niri.queue_redraw_all(); - } - } - } - - pub fn process_libinput_event(&mut self, event: &mut InputEvent) { - let _span = tracy_client::span!("process_libinput_event"); - - match event { - InputEvent::DeviceAdded { device } => { - self.niri.devices.insert(device.clone()); - - if device.has_capability(input::DeviceCapability::TabletTool) { - match device.size() { - Some((w, h)) => { - let aspect_ratio = w / h; - let data = TabletData { aspect_ratio }; - self.niri.tablets.insert(device.clone(), data); - } - None => { - warn!("tablet tool device has no size"); - } - } - } - - if device.has_capability(input::DeviceCapability::Keyboard) { - if let Some(led_state) = self - .niri - .seat - .get_keyboard() - .map(|keyboard| keyboard.led_state()) - { - device.led_update(led_state.into()); - } - } - - if device.has_capability(input::DeviceCapability::Touch) { - self.niri.touch.insert(device.clone()); - } - - apply_libinput_settings(&self.niri.config.borrow().input, device); - } - InputEvent::DeviceRemoved { device } => { - self.niri.touch.remove(device); - self.niri.tablets.remove(device); - self.niri.devices.remove(device); - } - _ => (), - } - } - - fn on_device_added(&mut self, device: impl Device) { - if device.has_capability(DeviceCapability::TabletTool) { - let tablet_seat = self.niri.seat.tablet_seat(); - - let desc = TabletDescriptor::from(&device); - tablet_seat.add_tablet::(&self.niri.display_handle, &desc); - } - if device.has_capability(DeviceCapability::Touch) && self.niri.seat.get_touch().is_none() { - self.niri.seat.add_touch(); - } - } - - fn on_device_removed(&mut self, device: impl Device) { - if device.has_capability(DeviceCapability::TabletTool) { - let tablet_seat = self.niri.seat.tablet_seat(); - - let desc = TabletDescriptor::from(&device); - tablet_seat.remove_tablet(&desc); - - // If there are no tablets in seat we can remove all tools - if tablet_seat.count_tablets() == 0 { - tablet_seat.clear_tools(); - } - } - if device.has_capability(DeviceCapability::Touch) && self.niri.touch.is_empty() { - self.niri.seat.remove_touch(); - } - } - - /// Computes the cursor position for the tablet event. - /// - /// This function handles the tablet output mapping, as well as coordinate clamping and aspect - /// ratio correction. - fn compute_tablet_position( - &self, - event: &(impl Event + TabletToolEvent), - ) -> Option> - where - I::Device: 'static, - { - let output = self.niri.output_for_tablet()?; - let output_geo = self.niri.global_space.output_geometry(output).unwrap(); - - let mut pos = event.position_transformed(output_geo.size); - pos.x /= output_geo.size.w as f64; - pos.y /= output_geo.size.h as f64; - - let device = event.device(); - if let Some(device) = (&device as &dyn Any).downcast_ref::() { - if let Some(data) = self.niri.tablets.get(device) { - // This code does the same thing as mutter with "keep aspect ratio" enabled. - let output_aspect_ratio = output_geo.size.w as f64 / output_geo.size.h as f64; - let ratio = data.aspect_ratio / output_aspect_ratio; - - if ratio > 1. { - pos.x *= ratio; - } else { - pos.y /= ratio; - } - } - }; - - pos.x *= output_geo.size.w as f64; - pos.y *= output_geo.size.h as f64; - pos.x = pos.x.clamp(0.0, output_geo.size.w as f64 - 1.); - pos.y = pos.y.clamp(0.0, output_geo.size.h as f64 - 1.); - Some(pos + output_geo.loc.to_f64()) - } - - fn on_keyboard(&mut self, event: I::KeyboardKeyEvent) { - let comp_mod = self.backend.mod_key(); - - let serial = SERIAL_COUNTER.next_serial(); - let time = Event::time_msec(&event); - let pressed = event.state() == KeyState::Pressed; - - let Some(Some(bind)) = self.niri.seat.get_keyboard().unwrap().input( - self, - event.key_code(), - event.state(), - serial, - time, - |this, mods, keysym| { - let bindings = &this.niri.config.borrow().binds; - let key_code = event.key_code(); - let modified = keysym.modified_sym(); - let raw = keysym.raw_latin_sym_or_raw_current_sym(); - - if let Some(dialog) = &this.niri.exit_confirm_dialog { - if dialog.is_open() && pressed && raw == Some(Keysym::Return) { - info!("quitting after confirming exit dialog"); - this.niri.stop_signal.stop(); - } - } - - should_intercept_key( - &mut this.niri.suppressed_keys, - bindings, - comp_mod, - key_code, - modified, - raw, - pressed, - *mods, - &this.niri.screenshot_ui, - this.niri.config.borrow().input.disable_power_key_handling, - ) - }, - ) else { - return; - }; - - // Filter actions when the key is released or the session is locked. - if !pressed { - return; - } - - self.handle_bind(bind); - } - - pub fn handle_bind(&mut self, bind: Bind) { - let Some(cooldown) = bind.cooldown else { - self.do_action(bind.action, bind.allow_when_locked); - return; - }; - - // Check this first so that it doesn't trigger the cooldown. - if self.niri.is_locked() && !(bind.allow_when_locked || allowed_when_locked(&bind.action)) { - return; - } - - match self.niri.bind_cooldown_timers.entry(bind.key) { - // The bind is on cooldown. - Entry::Occupied(_) => (), - Entry::Vacant(entry) => { - let timer = Timer::from_duration(cooldown); - let token = self - .niri - .event_loop - .insert_source(timer, move |_, _, state| { - if state.niri.bind_cooldown_timers.remove(&bind.key).is_none() { - error!("bind cooldown timer entry disappeared"); - } - TimeoutAction::Drop - }) - .unwrap(); - entry.insert(token); - - self.do_action(bind.action, bind.allow_when_locked); - } - } - } - - pub fn do_action(&mut self, action: Action, allow_when_locked: bool) { - if self.niri.is_locked() && !(allow_when_locked || allowed_when_locked(&action)) { - return; - } - - if let Some(touch) = self.niri.seat.get_touch() { - touch.cancel(self); - } - - match action { - Action::Quit(skip_confirmation) => { - if !skip_confirmation { - if let Some(dialog) = &mut self.niri.exit_confirm_dialog { - if dialog.show() { - self.niri.queue_redraw_all(); - } - return; - } - } - - info!("quitting as requested"); - self.niri.stop_signal.stop() - } - Action::ChangeVt(vt) => { - self.backend.change_vt(vt); - // Changing VT may not deliver the key releases, so clear the state. - self.niri.suppressed_keys.clear(); - } - Action::Suspend => { - self.backend.suspend(); - // Suspend may not deliver the key releases, so clear the state. - self.niri.suppressed_keys.clear(); - } - Action::PowerOffMonitors => { - self.niri.deactivate_monitors(&mut self.backend); - } - Action::ToggleDebugTint => { - self.backend.toggle_debug_tint(); - self.niri.queue_redraw_all(); - } - Action::DebugToggleOpaqueRegions => { - self.niri.debug_draw_opaque_regions = !self.niri.debug_draw_opaque_regions; - self.niri.queue_redraw_all(); - } - Action::DebugToggleDamage => { - self.niri.debug_toggle_damage(); - } - Action::Spawn(command) => { - spawn(command); - } - Action::DoScreenTransition(delay_ms) => { - self.backend.with_primary_renderer(|renderer| { - self.niri.do_screen_transition(renderer, delay_ms); - }); - } - Action::ScreenshotScreen => { - let active = self.niri.layout.active_output().cloned(); - if let Some(active) = active { - self.backend.with_primary_renderer(|renderer| { - if let Err(err) = self.niri.screenshot(renderer, &active) { - warn!("error taking screenshot: {err:?}"); - } - }); - } - } - Action::ConfirmScreenshot => { - self.backend.with_primary_renderer(|renderer| { - match self.niri.screenshot_ui.capture(renderer) { - Ok((size, pixels)) => { - if let Err(err) = self.niri.save_screenshot(size, pixels) { - warn!("error saving screenshot: {err:?}"); - } - } - Err(err) => { - warn!("error capturing screenshot: {err:?}"); - } - } - }); - - self.niri.screenshot_ui.close(); - self.niri - .cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); - self.niri.queue_redraw_all(); - } - Action::CancelScreenshot => { - self.niri.screenshot_ui.close(); - self.niri - .cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); - self.niri.queue_redraw_all(); - } - Action::Screenshot => { - self.backend.with_primary_renderer(|renderer| { - self.niri.open_screenshot_ui(renderer); - }); - } - Action::ScreenshotWindow => { - let active = self.niri.layout.active_window(); - if let Some((mapped, output)) = active { - self.backend.with_primary_renderer(|renderer| { - if let Err(err) = self.niri.screenshot_window(renderer, output, mapped) { - warn!("error taking screenshot: {err:?}"); - } - }); - } - } - Action::CloseWindow => { - if let Some(mapped) = self.niri.layout.focus() { - mapped.toplevel().send_close(); - } - } - Action::FullscreenWindow => { - let focus = self.niri.layout.focus().map(|m| m.window.clone()); - if let Some(window) = focus { - self.niri.layout.toggle_fullscreen(&window); - // FIXME: granular - self.niri.queue_redraw_all(); - } - } - Action::SwitchLayout(action) => { - self.niri.seat.get_keyboard().unwrap().with_xkb_state( - self, - |mut state| match action { - LayoutSwitchTarget::Next => state.cycle_next_layout(), - LayoutSwitchTarget::Prev => state.cycle_prev_layout(), - }, - ); - } - Action::MoveColumnLeft => { - self.niri.layout.move_left(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveColumnRight => { - self.niri.layout.move_right(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveColumnToFirst => { - self.niri.layout.move_column_to_first(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveColumnToLast => { - self.niri.layout.move_column_to_last(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowDown => { - self.niri.layout.move_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowUp => { - self.niri.layout.move_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowDownOrToWorkspaceDown => { - self.niri.layout.move_down_or_to_workspace_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowUpOrToWorkspaceUp => { - self.niri.layout.move_up_or_to_workspace_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::ConsumeOrExpelWindowLeft => { - self.niri.layout.consume_or_expel_window_left(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::ConsumeOrExpelWindowRight => { - self.niri.layout.consume_or_expel_window_right(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusColumnLeft => { - self.niri.layout.focus_left(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusColumnRight => { - self.niri.layout.focus_right(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusColumnFirst => { - self.niri.layout.focus_column_first(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusColumnLast => { - self.niri.layout.focus_column_last(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWindowDown => { - self.niri.layout.focus_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWindowUp => { - self.niri.layout.focus_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWindowOrWorkspaceDown => { - self.niri.layout.focus_window_or_workspace_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWindowOrWorkspaceUp => { - self.niri.layout.focus_window_or_workspace_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowToWorkspaceDown => { - self.niri.layout.move_to_workspace_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowToWorkspaceUp => { - self.niri.layout.move_to_workspace_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWindowToWorkspace(idx) => { - let idx = idx.saturating_sub(1) as usize; - self.niri.layout.move_to_workspace(idx); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveColumnToWorkspaceDown => { - self.niri.layout.move_column_to_workspace_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveColumnToWorkspaceUp => { - self.niri.layout.move_column_to_workspace_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveColumnToWorkspace(idx) => { - let idx = idx.saturating_sub(1) as usize; - self.niri.layout.move_column_to_workspace(idx); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWorkspaceDown => { - self.niri.layout.switch_workspace_down(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWorkspaceUp => { - self.niri.layout.switch_workspace_up(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWorkspace(idx) => { - let idx = idx.saturating_sub(1) as usize; - - let config = &self.niri.config; - if config.borrow().input.workspace_auto_back_and_forth { - self.niri.layout.switch_workspace_auto_back_and_forth(idx); - } else { - self.niri.layout.switch_workspace(idx); - } - - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::FocusWorkspacePrevious => { - self.niri.layout.switch_workspace_previous(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWorkspaceDown => { - self.niri.layout.move_workspace_down(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MoveWorkspaceUp => { - self.niri.layout.move_workspace_up(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::ConsumeWindowIntoColumn => { - self.niri.layout.consume_into_column(); - // This does not cause immediate focus or window size change, so warping mouse to - // focus won't do anything here. - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::ExpelWindowFromColumn => { - self.niri.layout.expel_from_column(); - self.maybe_warp_cursor_to_focus(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::SwitchPresetColumnWidth => { - self.niri.layout.toggle_width(); - } - Action::CenterColumn => { - self.niri.layout.center_column(); - // FIXME: granular - self.niri.queue_redraw_all(); - } - Action::MaximizeColumn => { - self.niri.layout.toggle_full_width(); - } - Action::FocusMonitorLeft => { - if let Some(output) = self.niri.output_left() { - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::FocusMonitorRight => { - if let Some(output) = self.niri.output_right() { - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::FocusMonitorDown => { - if let Some(output) = self.niri.output_down() { - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::FocusMonitorUp => { - if let Some(output) = self.niri.output_up() { - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWindowToMonitorLeft => { - if let Some(output) = self.niri.output_left() { - self.niri.layout.move_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWindowToMonitorRight => { - if let Some(output) = self.niri.output_right() { - self.niri.layout.move_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWindowToMonitorDown => { - if let Some(output) = self.niri.output_down() { - self.niri.layout.move_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWindowToMonitorUp => { - if let Some(output) = self.niri.output_up() { - self.niri.layout.move_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveColumnToMonitorLeft => { - if let Some(output) = self.niri.output_left() { - self.niri.layout.move_column_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveColumnToMonitorRight => { - if let Some(output) = self.niri.output_right() { - self.niri.layout.move_column_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveColumnToMonitorDown => { - if let Some(output) = self.niri.output_down() { - self.niri.layout.move_column_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveColumnToMonitorUp => { - if let Some(output) = self.niri.output_up() { - self.niri.layout.move_column_to_output(&output); - self.niri.layout.focus_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::SetColumnWidth(change) => { - self.niri.layout.set_column_width(change); - } - Action::SetWindowHeight(change) => { - self.niri.layout.set_window_height(change); - } - Action::ResetWindowHeight => { - self.niri.layout.reset_window_height(); - } - Action::ShowHotkeyOverlay => { - if self.niri.hotkey_overlay.show() { - self.niri.queue_redraw_all(); - } - } - Action::MoveWorkspaceToMonitorLeft => { - if let Some(output) = self.niri.output_left() { - self.niri.layout.move_workspace_to_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWorkspaceToMonitorRight => { - if let Some(output) = self.niri.output_right() { - self.niri.layout.move_workspace_to_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWorkspaceToMonitorDown => { - if let Some(output) = self.niri.output_down() { - self.niri.layout.move_workspace_to_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - Action::MoveWorkspaceToMonitorUp => { - if let Some(output) = self.niri.output_up() { - self.niri.layout.move_workspace_to_output(&output); - if !self.maybe_warp_cursor_to_focus_centered() { - self.move_cursor_to_output(&output); - } - } - } - } - } - - fn on_pointer_motion(&mut self, event: I::PointerMotionEvent) { - // We need an output to be able to move the pointer. - if self.niri.global_space.outputs().next().is_none() { - return; - } - - let serial = SERIAL_COUNTER.next_serial(); - - let pointer = self.niri.seat.get_pointer().unwrap(); - - let pos = pointer.current_location(); - - // We have an output, so we can compute the new location and focus. - let mut new_pos = pos + event.delta(); - - // We received an event for the regular pointer, so show it now. - self.niri.pointer_hidden = false; - self.niri.tablet_cursor_location = None; - - // Check if we have an active pointer constraint. - let mut pointer_confined = None; - if let Some(focus) = &self.niri.pointer_focus.surface { - let pos_within_surface = pos.to_i32_round() - focus.1; - - let mut pointer_locked = false; - with_pointer_constraint(&focus.0, &pointer, |constraint| { - let Some(constraint) = constraint else { return }; - if !constraint.is_active() { - return; - } - - // Constraint does not apply if not within region. - if let Some(region) = constraint.region() { - if !region.contains(pos_within_surface) { - return; - } - } - - match &*constraint { - PointerConstraint::Locked(_locked) => { - pointer_locked = true; - } - PointerConstraint::Confined(confine) => { - pointer_confined = Some((focus.clone(), confine.region().cloned())); - } - } - }); - - // If the pointer is locked, only send relative motion. - if pointer_locked { - pointer.relative_motion( - self, - Some(focus.clone()), - &RelativeMotionEvent { - delta: event.delta(), - delta_unaccel: event.delta_unaccel(), - utime: event.time(), - }, - ); - - pointer.frame(self); - - // I guess a redraw to hide the tablet cursor could be nice? Doesn't matter too - // much here I think. - return; - } - } - - if self - .niri - .global_space - .output_under(new_pos) - .next() - .is_none() - { - // We ended up outside the outputs and need to clip the movement. - if let Some(output) = self.niri.global_space.output_under(pos).next() { - // The pointer was previously on some output. Clip the movement against its - // boundaries. - let geom = self.niri.global_space.output_geometry(output).unwrap(); - new_pos.x = new_pos - .x - .clamp(geom.loc.x as f64, (geom.loc.x + geom.size.w - 1) as f64); - new_pos.y = new_pos - .y - .clamp(geom.loc.y as f64, (geom.loc.y + geom.size.h - 1) as f64); - } else { - // The pointer was not on any output in the first place. Find one for it. - // Let's do the simple thing and just put it on the first output. - let output = self.niri.global_space.outputs().next().unwrap(); - let geom = self.niri.global_space.output_geometry(output).unwrap(); - new_pos = center(geom).to_f64(); - } - } - - if let Some(output) = self.niri.screenshot_ui.selection_output() { - let geom = self.niri.global_space.output_geometry(output).unwrap(); - let mut point = new_pos; - point.x = point - .x - .clamp(geom.loc.x as f64, (geom.loc.x + geom.size.w - 1) as f64); - point.y = point - .y - .clamp(geom.loc.y as f64, (geom.loc.y + geom.size.h - 1) as f64); - let point = (point - geom.loc.to_f64()) - .to_physical(output.current_scale().fractional_scale()) - .to_i32_round(); - self.niri.screenshot_ui.pointer_motion(point); - } - - let under = self.niri.surface_under_and_global_space(new_pos); - - // Handle confined pointer. - if let Some((focus_surface, region)) = pointer_confined { - let mut prevent = false; - - // Prevent the pointer from leaving the focused surface. - if Some(&focus_surface.0) != under.surface.as_ref().map(|(s, _)| s) { - prevent = true; - } - - // Prevent the pointer from leaving the confine region, if any. - if let Some(region) = region { - let new_pos_within_surface = new_pos.to_i32_round() - focus_surface.1; - if !region.contains(new_pos_within_surface) { - prevent = true; - } - } - - if prevent { - pointer.relative_motion( - self, - Some(focus_surface), - &RelativeMotionEvent { - delta: event.delta(), - delta_unaccel: event.delta_unaccel(), - utime: event.time(), - }, - ); - - pointer.frame(self); - - return; - } - } - - self.niri.handle_focus_follows_mouse(&under); - - // Activate a new confinement if necessary. - self.niri.maybe_activate_pointer_constraint(new_pos, &under); - - self.niri.pointer_focus.clone_from(&under); - - pointer.motion( - self, - under.surface.clone(), - &MotionEvent { - location: new_pos, - serial, - time: event.time_msec(), - }, - ); - - pointer.relative_motion( - self, - under.surface, - &RelativeMotionEvent { - delta: event.delta(), - delta_unaccel: event.delta_unaccel(), - utime: event.time(), - }, - ); - - pointer.frame(self); - - // Redraw to update the cursor position. - // FIXME: redraw only outputs overlapping the cursor. - self.niri.queue_redraw_all(); - } - - fn on_pointer_motion_absolute( - &mut self, - event: I::PointerMotionAbsoluteEvent, - ) { - let Some(output) = self.niri.global_space.outputs().next() else { - return; - }; - - 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.niri.seat.get_pointer().unwrap(); - - if let Some(output) = self.niri.screenshot_ui.selection_output() { - let geom = self.niri.global_space.output_geometry(output).unwrap(); - let mut point = pos; - point.x = point - .x - .clamp(geom.loc.x as f64, (geom.loc.x + geom.size.w - 1) as f64); - point.y = point - .y - .clamp(geom.loc.y as f64, (geom.loc.y + geom.size.h - 1) as f64); - let point = (point - geom.loc.to_f64()) - .to_physical(output.current_scale().fractional_scale()) - .to_i32_round(); - self.niri.screenshot_ui.pointer_motion(point); - } - - let under = self.niri.surface_under_and_global_space(pos); - - self.niri.handle_focus_follows_mouse(&under); - - self.niri.maybe_activate_pointer_constraint(pos, &under); - self.niri.pointer_focus.clone_from(&under); - - pointer.motion( - self, - under.surface, - &MotionEvent { - location: pos, - serial, - time: event.time_msec(), - }, - ); - - pointer.frame(self); - - // We moved the pointer, show it. - self.niri.pointer_hidden = false; - - // We moved the regular pointer, so show it now. - self.niri.tablet_cursor_location = None; - - // Redraw to update the cursor position. - // FIXME: redraw only outputs overlapping the cursor. - self.niri.queue_redraw_all(); - } - - fn on_pointer_button(&mut self, event: I::PointerButtonEvent) { - let pointer = self.niri.seat.get_pointer().unwrap(); - - let serial = SERIAL_COUNTER.next_serial(); - - let button = event.button_code(); - - let button_state = event.state(); - - if ButtonState::Pressed == button_state { - if let Some(mapped) = self.niri.window_under_cursor() { - let window = mapped.window.clone(); - - // Check if we need to start an interactive resize. - if event.button() == Some(MouseButton::Right) && !pointer.is_grabbed() { - let mods = self.niri.seat.get_keyboard().unwrap().modifier_state(); - let mod_down = match self.backend.mod_key() { - CompositorMod::Super => mods.logo, - CompositorMod::Alt => mods.alt, - }; - if mod_down { - let location = pointer.current_location(); - let (output, pos_within_output) = self.niri.output_under(location).unwrap(); - let edges = self - .niri - .layout - .resize_edges_under(output, pos_within_output) - .unwrap(); - - if !edges.is_empty() { - // See if we got a double resize-click gesture. - // FIXME: deduplicate with resize_request in xdg-shell somehow. - let time = get_monotonic_time(); - let last_cell = mapped.last_interactive_resize_start(); - let last = last_cell.get(); - last_cell.set(Some((time, edges))); - if let Some((last_time, last_edges)) = last { - if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME { - // Allow quick resize after a triple click. - last_cell.set(None); - - let intersection = edges.intersection(last_edges); - if intersection.intersects(ResizeEdge::LEFT_RIGHT) { - // FIXME: don't activate once we can pass specific windows - // to actions. - self.niri.layout.activate_window(&window); - self.niri.layout.toggle_full_width(); - } - if intersection.intersects(ResizeEdge::TOP_BOTTOM) { - // FIXME: don't activate once we can pass specific windows - // to actions. - self.niri.layout.activate_window(&window); - self.niri.layout.reset_window_height(); - } - // FIXME: granular. - self.niri.queue_redraw_all(); - return; - } - } - - self.niri.layout.activate_window(&window); - - if self - .niri - .layout - .interactive_resize_begin(window.clone(), edges) - { - let start_data = PointerGrabStartData { - focus: None, - button: event.button_code(), - location, - }; - let grab = ResizeGrab::new(start_data, window.clone()); - pointer.set_grab(self, grab, serial, Focus::Clear); - self.niri.interactive_resize_ongoing = true; - self.niri.cursor_manager.set_cursor_image( - CursorImageStatus::Named(edges.cursor_icon()), - ); - } - } - } - } - - self.niri.layout.activate_window(&window); - - // FIXME: granular. - self.niri.queue_redraw_all(); - } else if let Some(output) = self.niri.output_under_cursor() { - self.niri.layout.activate_output(&output); - - // FIXME: granular. - self.niri.queue_redraw_all(); - } - }; - - self.update_pointer_focus(); - - if let Some(button) = event.button() { - let pos = pointer.current_location(); - if let Some((output, _)) = self.niri.output_under(pos) { - let output = output.clone(); - let geom = self.niri.global_space.output_geometry(&output).unwrap(); - let mut point = pos; - // Re-clamp as pointer can be within 0.5 from the limit which will round up - // to a wrong value. - point.x = point - .x - .clamp(geom.loc.x as f64, (geom.loc.x + geom.size.w - 1) as f64); - point.y = point - .y - .clamp(geom.loc.y as f64, (geom.loc.y + geom.size.h - 1) as f64); - let point = (point - geom.loc.to_f64()) - .to_physical(output.current_scale().fractional_scale()) - .to_i32_round(); - if self - .niri - .screenshot_ui - .pointer_button(output, point, button, button_state) - { - self.niri.queue_redraw_all(); - } - } - } - - pointer.button( - self, - &ButtonEvent { - button, - state: button_state, - serial, - time: event.time_msec(), - }, - ); - pointer.frame(self); - } - - fn on_pointer_axis(&mut self, event: I::PointerAxisEvent) { - let source = event.source(); - - let horizontal_amount_v120 = event.amount_v120(Axis::Horizontal); - let vertical_amount_v120 = event.amount_v120(Axis::Vertical); - - // Handle wheel scroll bindings. - if source == AxisSource::Wheel { - // If we have a scroll bind with current modifiers, then accumulate and don't pass to - // Wayland. If there's no bind, reset the accumulator. - let mods = self.niri.seat.get_keyboard().unwrap().modifier_state(); - let modifiers = modifiers_from_state(mods); - if self.niri.mods_with_wheel_binds.contains(&modifiers) { - let comp_mod = self.backend.mod_key(); - - let horizontal = horizontal_amount_v120.unwrap_or(0.); - let ticks = self.niri.horizontal_wheel_tracker.accumulate(horizontal); - if ticks != 0 { - let config = self.niri.config.borrow(); - let bindings = &config.binds; - let bind_left = - find_configured_bind(bindings, comp_mod, Trigger::WheelScrollLeft, mods); - let bind_right = - find_configured_bind(bindings, comp_mod, Trigger::WheelScrollRight, mods); - drop(config); - - if let Some(right) = bind_right { - for _ in 0..ticks { - self.handle_bind(right.clone()); - } - } - if let Some(left) = bind_left { - for _ in ticks..0 { - self.handle_bind(left.clone()); - } - } - } - - let vertical = vertical_amount_v120.unwrap_or(0.); - let ticks = self.niri.vertical_wheel_tracker.accumulate(vertical); - if ticks != 0 { - let config = self.niri.config.borrow(); - let bindings = &config.binds; - let bind_up = - find_configured_bind(bindings, comp_mod, Trigger::WheelScrollUp, mods); - let bind_down = - find_configured_bind(bindings, comp_mod, Trigger::WheelScrollDown, mods); - drop(config); - - if let Some(down) = bind_down { - for _ in 0..ticks { - self.handle_bind(down.clone()); - } - } - if let Some(up) = bind_up { - for _ in ticks..0 { - self.handle_bind(up.clone()); - } - } - } - - return; - } else { - self.niri.horizontal_wheel_tracker.reset(); - self.niri.vertical_wheel_tracker.reset(); - } - } - - let horizontal_amount = event.amount(Axis::Horizontal); - let vertical_amount = event.amount(Axis::Vertical); - - // Handle touchpad scroll bindings. - if source == AxisSource::Finger { - let mods = self.niri.seat.get_keyboard().unwrap().modifier_state(); - let modifiers = modifiers_from_state(mods); - if self.niri.mods_with_finger_scroll_binds.contains(&modifiers) { - let comp_mod = self.backend.mod_key(); - - let horizontal = horizontal_amount.unwrap_or(0.); - let ticks = self - .niri - .horizontal_finger_scroll_tracker - .accumulate(horizontal); - if ticks != 0 { - let config = self.niri.config.borrow(); - let bindings = &config.binds; - let bind_left = - find_configured_bind(bindings, comp_mod, Trigger::TouchpadScrollLeft, mods); - let bind_right = find_configured_bind( - bindings, - comp_mod, - Trigger::TouchpadScrollRight, - mods, - ); - drop(config); - - if let Some(right) = bind_right { - for _ in 0..ticks { - self.handle_bind(right.clone()); - } - } - if let Some(left) = bind_left { - for _ in ticks..0 { - self.handle_bind(left.clone()); - } - } - } - - let vertical = vertical_amount.unwrap_or(0.); - let ticks = self - .niri - .vertical_finger_scroll_tracker - .accumulate(vertical); - if ticks != 0 { - let config = self.niri.config.borrow(); - let bindings = &config.binds; - let bind_up = - find_configured_bind(bindings, comp_mod, Trigger::TouchpadScrollUp, mods); - let bind_down = - find_configured_bind(bindings, comp_mod, Trigger::TouchpadScrollDown, mods); - drop(config); - - if let Some(down) = bind_down { - for _ in 0..ticks { - self.handle_bind(down.clone()); - } - } - if let Some(up) = bind_up { - for _ in ticks..0 { - self.handle_bind(up.clone()); - } - } - } - - return; - } else { - self.niri.horizontal_finger_scroll_tracker.reset(); - self.niri.vertical_finger_scroll_tracker.reset(); - } - } - - let horizontal_amount = horizontal_amount.unwrap_or_else(|| { - // Winit backend, discrete scrolling. - horizontal_amount_v120.unwrap_or(0.0) / 120. * 15. - }); - let vertical_amount = vertical_amount.unwrap_or_else(|| { - // Winit backend, discrete scrolling. - vertical_amount_v120.unwrap_or(0.0) / 120. * 15. - }); - - let mut frame = AxisFrame::new(event.time_msec()).source(source); - if horizontal_amount != 0.0 { - frame = frame - .relative_direction(Axis::Horizontal, event.relative_direction(Axis::Horizontal)); - frame = frame.value(Axis::Horizontal, horizontal_amount); - if let Some(v120) = horizontal_amount_v120 { - frame = frame.v120(Axis::Horizontal, v120 as i32); - } - } - if vertical_amount != 0.0 { - frame = - frame.relative_direction(Axis::Vertical, event.relative_direction(Axis::Vertical)); - frame = frame.value(Axis::Vertical, vertical_amount); - if let Some(v120) = vertical_amount_v120 { - frame = frame.v120(Axis::Vertical, v120 as i32); - } - } - - if source == AxisSource::Finger { - if event.amount(Axis::Horizontal) == Some(0.0) { - frame = frame.stop(Axis::Horizontal); - } - if event.amount(Axis::Vertical) == Some(0.0) { - frame = frame.stop(Axis::Vertical); - } - } - - self.update_pointer_focus(); - - let pointer = &self.niri.seat.get_pointer().unwrap(); - pointer.axis(self, frame); - pointer.frame(self); - } - - fn on_tablet_tool_axis(&mut self, event: I::TabletToolAxisEvent) - where - I::Device: 'static, // Needed for downcasting. - { - let Some(pos) = self.compute_tablet_position(&event) else { - return; - }; - - let under = self.niri.surface_under_and_global_space(pos); - - 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) { - if event.pressure_has_changed() { - tool.pressure(event.pressure()); - } - if event.distance_has_changed() { - tool.distance(event.distance()); - } - if event.tilt_has_changed() { - tool.tilt(event.tilt()); - } - if event.slider_has_changed() { - tool.slider_position(event.slider_position()); - } - if event.rotation_has_changed() { - tool.rotation(event.rotation()); - } - if event.wheel_has_changed() { - tool.wheel(event.wheel_delta(), event.wheel_delta_discrete()); - } - - tool.motion( - pos, - under.surface, - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), - ); - - self.niri.pointer_hidden = false; - self.niri.tablet_cursor_location = Some(pos); - } - - // Redraw to update the cursor position. - // FIXME: redraw only outputs overlapping the cursor. - self.niri.queue_redraw_all(); - } - - fn on_tablet_tool_tip(&mut self, event: I::TabletToolTipEvent) { - let tool = self.niri.seat.tablet_seat().get_tool(&event.tool()); - - if let Some(tool) = tool { - match event.tip_state() { - TabletToolTipState::Down => { - let serial = SERIAL_COUNTER.next_serial(); - tool.tip_down(serial, event.time_msec()); - - if let Some(pos) = self.niri.tablet_cursor_location { - if let Some(mapped) = self.niri.window_under(pos) { - let window = mapped.window.clone(); - self.niri.layout.activate_window(&window); - - // FIXME: granular. - self.niri.queue_redraw_all(); - } else if let Some((output, _)) = self.niri.output_under(pos) { - let output = output.clone(); - self.niri.layout.activate_output(&output); - - // FIXME: granular. - self.niri.queue_redraw_all(); - } - } - } - TabletToolTipState::Up => { - tool.tip_up(event.time_msec()); - } - } - } - } - - fn on_tablet_tool_proximity(&mut self, event: I::TabletToolProximityEvent) - where - I::Device: 'static, // Needed for downcasting. - { - let Some(pos) = self.compute_tablet_position(&event) else { - return; - }; - - let under = self.niri.surface_under_and_global_space(pos); - - 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(tablet) = tablet { - match event.state() { - ProximityState::In => { - if let Some(under) = under.surface { - tool.proximity_in( - pos, - under, - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), - ); - } - self.niri.pointer_hidden = false; - self.niri.tablet_cursor_location = Some(pos); - } - ProximityState::Out => { - tool.proximity_out(event.time_msec()); - - // Move the mouse pointer here to avoid discontinuity. - // - // Plus, Wayland SDL2 currently warps the pointer into some weird - // location on proximity out, so this should help it a little. - if let Some