2023-12-03 13:50:07 +04:00
|
|
|
use std::any::Any;
|
2023-10-27 16:50:02 +04:00
|
|
|
use std::collections::HashSet;
|
2024-03-02 14:33:22 +04:00
|
|
|
use std::time::Duration;
|
2023-10-27 16:50:02 +04:00
|
|
|
|
2024-03-02 15:01:46 +04:00
|
|
|
use input::event::gesture::GestureEventCoordinates as _;
|
2024-03-22 10:36:19 +04:00
|
|
|
use niri_config::{Action, Binds, Modifiers, Trigger};
|
2024-02-10 09:33:32 +04:00
|
|
|
use niri_ipc::LayoutSwitchTarget;
|
2023-08-07 19:45:55 +04:00
|
|
|
use smithay::backend::input::{
|
2023-08-16 11:43:52 +04:00
|
|
|
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event,
|
2023-09-04 14:28:59 +04:00
|
|
|
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _,
|
|
|
|
|
InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
|
|
|
|
|
PointerMotionEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent,
|
2024-02-24 18:32:13 +01:00
|
|
|
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent,
|
2023-08-07 19:44:40 +04:00
|
|
|
};
|
2023-08-15 18:17:26 +04:00
|
|
|
use smithay::backend::libinput::LibinputInputBackend;
|
2023-10-27 16:50:02 +04:00
|
|
|
use smithay::input::keyboard::{keysyms, FilterResult, Keysym, ModifiersState};
|
2023-09-02 15:09:07 +04:00
|
|
|
use smithay::input::pointer::{
|
2023-10-31 07:16:36 +04:00
|
|
|
AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent,
|
|
|
|
|
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
|
|
|
|
GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, RelativeMotionEvent,
|
2023-09-02 15:09:07 +04:00
|
|
|
};
|
2024-02-24 18:32:13 +01:00
|
|
|
use smithay::input::touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent};
|
2023-12-03 13:50:07 +04:00
|
|
|
use smithay::utils::{Logical, Point, SERIAL_COUNTER};
|
2023-12-21 16:19:16 +04:00
|
|
|
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
|
2023-08-16 11:43:52 +04:00
|
|
|
use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait};
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-09-03 14:10:02 +04:00
|
|
|
use crate::niri::State;
|
2024-02-21 10:50:30 +04:00
|
|
|
use crate::ui::screenshot_ui::ScreenshotUi;
|
2024-02-21 10:42:21 +04:00
|
|
|
use crate::utils::spawning::spawn;
|
|
|
|
|
use crate::utils::{center, get_monotonic_time};
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-10-27 16:50:02 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2023-08-13 07:45:10 +04:00
|
|
|
pub enum CompositorMod {
|
|
|
|
|
Super,
|
|
|
|
|
Alt,
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-03 13:50:07 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct TabletData {
|
|
|
|
|
pub aspect_ratio: f64,
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-03 14:10:02 +04:00
|
|
|
impl State {
|
2024-03-02 15:01:46 +04:00
|
|
|
pub fn process_input_event<I: InputBackend + 'static>(&mut self, event: InputEvent<I>)
|
2023-12-03 13:50:07 +04:00
|
|
|
where
|
|
|
|
|
I::Device: 'static, // Needed for downcasting.
|
|
|
|
|
{
|
2023-08-10 14:12:20 +04:00
|
|
|
let _span = tracy_client::span!("process_input_event");
|
2023-08-09 11:03:38 +04:00
|
|
|
|
2023-08-14 17:40:15 +04:00
|
|
|
// 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.
|
2023-10-05 09:25:07 +04:00
|
|
|
self.niri.layout.advance_animations(get_monotonic_time());
|
2023-09-03 14:10:02 +04:00
|
|
|
|
2024-02-08 13:51:54 +04:00
|
|
|
if self.niri.monitors_active {
|
|
|
|
|
// Notify the idle-notifier of activity.
|
|
|
|
|
if should_notify_activity(&event) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
2023-10-09 18:37:43 +04:00
|
|
|
}
|
|
|
|
|
|
2024-01-18 19:20:46 +04:00
|
|
|
let hide_hotkey_overlay =
|
|
|
|
|
self.niri.hotkey_overlay.is_open() && should_hide_hotkey_overlay(&event);
|
|
|
|
|
|
2024-01-19 08:33:54 +04:00
|
|
|
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);
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
use InputEvent::*;
|
2023-08-07 19:44:40 +04:00
|
|
|
match event {
|
2023-12-09 09:22:58 +04:00
|
|
|
DeviceAdded { device } => self.on_device_added(device),
|
|
|
|
|
DeviceRemoved { device } => self.on_device_removed(device),
|
|
|
|
|
Keyboard { event } => self.on_keyboard::<I>(event),
|
|
|
|
|
PointerMotion { event } => self.on_pointer_motion::<I>(event),
|
|
|
|
|
PointerMotionAbsolute { event } => self.on_pointer_motion_absolute::<I>(event),
|
|
|
|
|
PointerButton { event } => self.on_pointer_button::<I>(event),
|
|
|
|
|
PointerAxis { event } => self.on_pointer_axis::<I>(event),
|
|
|
|
|
TabletToolAxis { event } => self.on_tablet_tool_axis::<I>(event),
|
|
|
|
|
TabletToolTip { event } => self.on_tablet_tool_tip::<I>(event),
|
|
|
|
|
TabletToolProximity { event } => self.on_tablet_tool_proximity::<I>(event),
|
|
|
|
|
TabletToolButton { event } => self.on_tablet_tool_button::<I>(event),
|
|
|
|
|
GestureSwipeBegin { event } => self.on_gesture_swipe_begin::<I>(event),
|
|
|
|
|
GestureSwipeUpdate { event } => self.on_gesture_swipe_update::<I>(event),
|
|
|
|
|
GestureSwipeEnd { event } => self.on_gesture_swipe_end::<I>(event),
|
|
|
|
|
GesturePinchBegin { event } => self.on_gesture_pinch_begin::<I>(event),
|
|
|
|
|
GesturePinchUpdate { event } => self.on_gesture_pinch_update::<I>(event),
|
|
|
|
|
GesturePinchEnd { event } => self.on_gesture_pinch_end::<I>(event),
|
|
|
|
|
GestureHoldBegin { event } => self.on_gesture_hold_begin::<I>(event),
|
|
|
|
|
GestureHoldEnd { event } => self.on_gesture_hold_end::<I>(event),
|
2024-02-24 18:32:13 +01:00
|
|
|
TouchDown { event } => self.on_touch_down::<I>(event),
|
|
|
|
|
TouchMotion { event } => self.on_touch_motion::<I>(event),
|
|
|
|
|
TouchUp { event } => self.on_touch_up::<I>(event),
|
|
|
|
|
TouchCancel { event } => self.on_touch_cancel::<I>(event),
|
|
|
|
|
TouchFrame { event } => self.on_touch_frame::<I>(event),
|
2024-01-22 16:08:51 +04:00
|
|
|
SwitchToggle { .. } => (),
|
2023-12-09 09:22:58 +04:00
|
|
|
Special(_) => (),
|
|
|
|
|
}
|
2024-01-18 19:20:46 +04:00
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
}
|
2024-01-19 08:33:54 +04:00
|
|
|
|
|
|
|
|
if let Some(dialog) = &mut self.niri.exit_confirm_dialog {
|
|
|
|
|
if hide_exit_confirm_dialog && dialog.hide() {
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pub fn process_libinput_event(&mut self, event: &mut InputEvent<LibinputInputBackend>) {
|
2023-12-09 09:23:41 +04:00
|
|
|
let _span = tracy_client::span!("process_libinput_event");
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
match event {
|
|
|
|
|
InputEvent::DeviceAdded { device } => {
|
2024-01-16 20:28:46 +04:00
|
|
|
self.niri.devices.insert(device.clone());
|
2023-08-09 11:03:38 +04:00
|
|
|
|
2023-12-09 09:23:25 +04:00
|
|
|
if device.has_capability(input::DeviceCapability::TabletTool) {
|
2023-12-09 09:22:58 +04:00
|
|
|
match device.size() {
|
|
|
|
|
Some((w, h)) => {
|
2023-12-09 09:25:27 +04:00
|
|
|
let aspect_ratio = w / h;
|
|
|
|
|
let data = TabletData { aspect_ratio };
|
|
|
|
|
self.niri.tablets.insert(device.clone(), data);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
warn!("tablet tool device has no size");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-30 14:28:04 +04:00
|
|
|
}
|
2024-01-08 11:53:34 +04:00
|
|
|
|
2024-01-26 15:13:54 +01:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 18:32:13 +01:00
|
|
|
if device.has_capability(input::DeviceCapability::Touch) {
|
|
|
|
|
self.niri.touch.insert(device.clone());
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-16 20:21:36 +04:00
|
|
|
apply_libinput_settings(&self.niri.config.borrow().input, device);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
InputEvent::DeviceRemoved { device } => {
|
2024-02-24 18:32:13 +01:00
|
|
|
self.niri.touch.remove(device);
|
2023-12-09 09:22:58 +04:00
|
|
|
self.niri.tablets.remove(device);
|
2024-01-16 20:28:46 +04:00
|
|
|
self.niri.devices.remove(device);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-30 14:28:04 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_device_added(&mut self, device: impl Device) {
|
|
|
|
|
if device.has_capability(DeviceCapability::TabletTool) {
|
2023-12-09 09:28:41 +04:00
|
|
|
let tablet_seat = self.niri.seat.tablet_seat();
|
|
|
|
|
|
|
|
|
|
let desc = TabletDescriptor::from(&device);
|
|
|
|
|
tablet_seat.add_tablet::<Self>(&self.niri.display_handle, &desc);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2024-02-24 18:32:13 +01:00
|
|
|
if device.has_capability(DeviceCapability::Touch) && self.niri.seat.get_touch().is_none() {
|
|
|
|
|
self.niri.seat.add_touch();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_device_removed(&mut self, device: impl Device) {
|
|
|
|
|
if device.has_capability(DeviceCapability::TabletTool) {
|
|
|
|
|
let tablet_seat = self.niri.seat.tablet_seat();
|
|
|
|
|
|
2023-12-09 09:28:41 +04:00
|
|
|
let desc = TabletDescriptor::from(&device);
|
|
|
|
|
tablet_seat.remove_tablet(&desc);
|
2023-12-09 09:22:58 +04:00
|
|
|
|
|
|
|
|
// If there are no tablets in seat we can remove all tools
|
|
|
|
|
if tablet_seat.count_tablets() == 0 {
|
|
|
|
|
tablet_seat.clear_tools();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-24 18:32:13 +01:00
|
|
|
if device.has_capability(DeviceCapability::Touch) && self.niri.touch.is_empty() {
|
|
|
|
|
self.niri.seat.remove_touch();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
|
2023-12-09 09:30:56 +04:00
|
|
|
/// Computes the cursor position for the tablet event.
|
|
|
|
|
///
|
|
|
|
|
/// This function handles the tablet output mapping, as well as coordinate clamping and aspect
|
|
|
|
|
/// ratio correction.
|
2023-12-09 09:22:58 +04:00
|
|
|
fn compute_tablet_position<I: InputBackend>(
|
2023-12-09 09:30:56 +04:00
|
|
|
&self,
|
2023-12-09 09:22:58 +04:00
|
|
|
event: &(impl Event<I> + TabletToolEvent<I>),
|
|
|
|
|
) -> Option<Point<f64, Logical>>
|
|
|
|
|
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::<input::Device>() {
|
|
|
|
|
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;
|
2023-10-13 13:30:11 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
};
|
2023-10-13 13:30:11 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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())
|
|
|
|
|
}
|
2023-10-30 20:29:03 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_keyboard<I: InputBackend>(&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(action)) = 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();
|
2024-01-19 08:33:54 +04:00
|
|
|
|
|
|
|
|
if let Some(dialog) = &this.niri.exit_confirm_dialog {
|
|
|
|
|
if dialog.is_open() && pressed && raw == Some(Keysym::Return) {
|
2024-02-12 07:48:05 +04:00
|
|
|
info!("quitting after confirming exit dialog");
|
2024-01-19 08:33:54 +04:00
|
|
|
this.niri.stop_signal.stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
should_intercept_key(
|
|
|
|
|
&mut this.niri.suppressed_keys,
|
|
|
|
|
bindings,
|
|
|
|
|
comp_mod,
|
|
|
|
|
key_code,
|
|
|
|
|
modified,
|
|
|
|
|
raw,
|
|
|
|
|
pressed,
|
|
|
|
|
*mods,
|
|
|
|
|
&this.niri.screenshot_ui,
|
2023-12-28 09:36:10 +04:00
|
|
|
this.niri.config.borrow().input.disable_power_key_handling,
|
2023-12-09 09:22:58 +04:00
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Filter actions when the key is released or the session is locked.
|
|
|
|
|
if !pressed {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-10 09:33:32 +04:00
|
|
|
self.do_action(action);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn do_action(&mut self, action: Action) {
|
2023-12-09 09:43:26 +04:00
|
|
|
if self.niri.is_locked() && !allowed_when_locked(&action) {
|
2023-12-09 09:22:58 +04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 18:32:13 +01:00
|
|
|
if let Some(touch) = self.niri.seat.get_touch() {
|
|
|
|
|
touch.cancel(self);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
match action {
|
2024-02-12 07:53:06 +04:00
|
|
|
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;
|
2024-01-19 08:33:54 +04:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-12 07:53:06 +04:00
|
|
|
|
|
|
|
|
info!("quitting as requested");
|
|
|
|
|
self.niri.stop_signal.stop()
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::ChangeVt(vt) => {
|
|
|
|
|
self.backend.change_vt(vt);
|
2024-02-10 09:19:08 +04:00
|
|
|
// Changing VT may not deliver the key releases, so clear the state.
|
2023-12-09 09:22:58 +04:00
|
|
|
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 => {
|
2024-01-28 16:18:52 +01:00
|
|
|
self.niri.deactivate_monitors(&mut self.backend);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::ToggleDebugTint => {
|
|
|
|
|
self.backend.toggle_debug_tint();
|
2024-01-06 08:28:12 +04:00
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::Spawn(command) => {
|
|
|
|
|
spawn(command);
|
|
|
|
|
}
|
|
|
|
|
Action::ScreenshotScreen => {
|
|
|
|
|
let active = self.niri.layout.active_output().cloned();
|
|
|
|
|
if let Some(active) = active {
|
2024-01-03 11:33:24 +04:00
|
|
|
self.backend.with_primary_renderer(|renderer| {
|
2023-12-09 09:22:58 +04:00
|
|
|
if let Err(err) = self.niri.screenshot(renderer, &active) {
|
|
|
|
|
warn!("error taking screenshot: {err:?}");
|
2023-10-30 20:29:03 +04:00
|
|
|
}
|
2024-01-03 11:33:24 +04:00
|
|
|
});
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::ConfirmScreenshot => {
|
2024-01-03 11:33:24 +04:00
|
|
|
self.backend.with_primary_renderer(|renderer| {
|
2023-12-09 09:22:58 +04:00
|
|
|
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:?}");
|
2023-10-10 12:42:24 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Err(err) => {
|
|
|
|
|
warn!("error capturing screenshot: {err:?}");
|
2023-08-16 08:03:20 +04:00
|
|
|
}
|
2023-10-27 16:50:02 +04:00
|
|
|
}
|
2024-01-03 11:33:24 +04:00
|
|
|
});
|
2023-12-09 09:22:58 +04:00
|
|
|
|
|
|
|
|
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();
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::Screenshot => {
|
2024-01-03 11:33:24 +04:00
|
|
|
self.backend.with_primary_renderer(|renderer| {
|
2023-12-09 09:22:58 +04:00
|
|
|
self.niri.open_screenshot_ui(renderer);
|
2024-01-03 11:33:24 +04:00
|
|
|
});
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::ScreenshotWindow => {
|
|
|
|
|
let active = self.niri.layout.active_window();
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some((mapped, output)) = active {
|
2024-01-03 11:33:24 +04:00
|
|
|
self.backend.with_primary_renderer(|renderer| {
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Err(err) =
|
|
|
|
|
self.niri
|
|
|
|
|
.screenshot_window(renderer, output, &mapped.window)
|
|
|
|
|
{
|
2023-12-09 09:22:58 +04:00
|
|
|
warn!("error taking screenshot: {err:?}");
|
|
|
|
|
}
|
2024-01-03 11:33:24 +04:00
|
|
|
});
|
2023-09-05 18:02:05 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::CloseWindow => {
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some(mapped) = self.niri.layout.focus() {
|
|
|
|
|
mapped.toplevel().send_close();
|
2023-10-30 20:29:03 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::FullscreenWindow => {
|
2024-03-19 14:41:17 +04:00
|
|
|
let focus = self.niri.layout.focus().map(|m| m.window.clone());
|
2023-12-09 09:22:58 +04:00
|
|
|
if let Some(window) = focus {
|
|
|
|
|
self.niri.layout.toggle_fullscreen(&window);
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::SwitchLayout(action) => {
|
|
|
|
|
self.niri.seat.get_keyboard().unwrap().with_xkb_state(
|
2023-08-09 15:15:06 +04:00
|
|
|
self,
|
2023-12-09 09:22:58 +04:00
|
|
|
|mut state| match action {
|
2024-02-10 09:33:32 +04:00
|
|
|
LayoutSwitchTarget::Next => state.cycle_next_layout(),
|
|
|
|
|
LayoutSwitchTarget::Prev => state.cycle_prev_layout(),
|
2023-08-09 15:15:06 +04:00
|
|
|
},
|
|
|
|
|
);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::MoveColumnLeft => {
|
|
|
|
|
self.niri.layout.move_left();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::MoveColumnRight => {
|
|
|
|
|
self.niri.layout.move_right();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-29 08:01:02 +04:00
|
|
|
Action::MoveColumnToFirst => {
|
|
|
|
|
self.niri.layout.move_column_to_first();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-29 08:01:02 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::MoveColumnToLast => {
|
|
|
|
|
self.niri.layout.move_column_to_last();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-29 08:01:02 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::MoveWindowDown => {
|
|
|
|
|
self.niri.layout.move_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowUp => {
|
|
|
|
|
self.niri.layout.move_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-19 19:25:05 +11:00
|
|
|
Action::MoveWindowDownOrToWorkspaceDown => {
|
|
|
|
|
self.niri.layout.move_down_or_to_workspace_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-19 19:25:05 +11:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowUpOrToWorkspaceUp => {
|
|
|
|
|
self.niri.layout.move_up_or_to_workspace_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-19 19:25:05 +11:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2024-02-03 20:25:08 +01:00
|
|
|
Action::ConsumeOrExpelWindowLeft => {
|
|
|
|
|
self.niri.layout.consume_or_expel_window_left();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-02-03 20:25:08 +01:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::ConsumeOrExpelWindowRight => {
|
|
|
|
|
self.niri.layout.consume_or_expel_window_right();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-02-03 20:25:08 +01:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::FocusColumnLeft => {
|
|
|
|
|
self.niri.layout.focus_left();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::FocusColumnRight => {
|
|
|
|
|
self.niri.layout.focus_right();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2023-12-29 07:51:14 +04:00
|
|
|
Action::FocusColumnFirst => {
|
|
|
|
|
self.niri.layout.focus_column_first();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-29 07:51:14 +04:00
|
|
|
}
|
|
|
|
|
Action::FocusColumnLast => {
|
|
|
|
|
self.niri.layout.focus_column_last();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-29 07:51:14 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::FocusWindowDown => {
|
|
|
|
|
self.niri.layout.focus_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
Action::FocusWindowUp => {
|
|
|
|
|
self.niri.layout.focus_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-13 08:16:09 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2023-12-19 19:25:05 +11:00
|
|
|
Action::FocusWindowOrWorkspaceDown => {
|
|
|
|
|
self.niri.layout.focus_window_or_workspace_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-19 19:25:05 +11:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::FocusWindowOrWorkspaceUp => {
|
|
|
|
|
self.niri.layout.focus_window_or_workspace_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-19 19:25:05 +11:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::MoveWindowToWorkspaceDown => {
|
|
|
|
|
self.niri.layout.move_to_workspace_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowToWorkspaceUp => {
|
|
|
|
|
self.niri.layout.move_to_workspace_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// 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);
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2024-01-15 10:31:44 +04:00
|
|
|
Action::MoveColumnToWorkspaceDown => {
|
|
|
|
|
self.niri.layout.move_column_to_workspace_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-15 10:31:44 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::MoveColumnToWorkspaceUp => {
|
|
|
|
|
self.niri.layout.move_column_to_workspace_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-15 10:31:44 +04:00
|
|
|
// 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);
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2024-01-15 10:31:44 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::FocusWorkspaceDown => {
|
|
|
|
|
self.niri.layout.switch_workspace_down();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::FocusWorkspaceUp => {
|
|
|
|
|
self.niri.layout.switch_workspace_up();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::FocusWorkspace(idx) => {
|
|
|
|
|
let idx = idx.saturating_sub(1) as usize;
|
2024-03-19 14:27:52 +00:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2024-03-19 14:27:52 +00:00
|
|
|
Action::FocusWorkspacePrevious => {
|
|
|
|
|
self.niri.layout.switch_workspace_previous();
|
|
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
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();
|
2024-02-26 18:47:46 +01:00
|
|
|
// This does not cause immediate focus or window size change, so warping mouse to
|
|
|
|
|
// focus won't do anything here.
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
Action::ExpelWindowFromColumn => {
|
|
|
|
|
self.niri.layout.expel_from_column();
|
2024-02-26 18:47:46 +01:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
2023-12-09 09:22:58 +04:00
|
|
|
// 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);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::FocusMonitorRight => {
|
|
|
|
|
if let Some(output) = self.niri.output_right() {
|
|
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::FocusMonitorDown => {
|
|
|
|
|
if let Some(output) = self.niri.output_down() {
|
|
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::FocusMonitorUp => {
|
|
|
|
|
if let Some(output) = self.niri.output_up() {
|
|
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowToMonitorLeft => {
|
|
|
|
|
if let Some(output) = self.niri.output_left() {
|
|
|
|
|
self.niri.layout.move_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowToMonitorRight => {
|
|
|
|
|
if let Some(output) = self.niri.output_right() {
|
|
|
|
|
self.niri.layout.move_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowToMonitorDown => {
|
|
|
|
|
if let Some(output) = self.niri.output_down() {
|
|
|
|
|
self.niri.layout.move_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWindowToMonitorUp => {
|
|
|
|
|
if let Some(output) = self.niri.output_up() {
|
|
|
|
|
self.niri.layout.move_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-15 10:36:59 +04:00
|
|
|
Action::MoveColumnToMonitorLeft => {
|
|
|
|
|
if let Some(output) = self.niri.output_left() {
|
|
|
|
|
self.niri.layout.move_column_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2024-01-15 10:36:59 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveColumnToMonitorRight => {
|
|
|
|
|
if let Some(output) = self.niri.output_right() {
|
|
|
|
|
self.niri.layout.move_column_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2024-01-15 10:36:59 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveColumnToMonitorDown => {
|
|
|
|
|
if let Some(output) = self.niri.output_down() {
|
|
|
|
|
self.niri.layout.move_column_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2024-01-15 10:36:59 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveColumnToMonitorUp => {
|
|
|
|
|
if let Some(output) = self.niri.output_up() {
|
|
|
|
|
self.niri.layout.move_column_to_output(&output);
|
2024-02-14 09:06:13 +04:00
|
|
|
self.niri.layout.focus_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2024-01-15 10:36:59 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
Action::SetColumnWidth(change) => {
|
|
|
|
|
self.niri.layout.set_column_width(change);
|
|
|
|
|
}
|
|
|
|
|
Action::SetWindowHeight(change) => {
|
|
|
|
|
self.niri.layout.set_window_height(change);
|
|
|
|
|
}
|
2024-01-18 19:20:46 +04:00
|
|
|
Action::ShowHotkeyOverlay => {
|
|
|
|
|
if self.niri.hotkey_overlay.show() {
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-02 23:57:01 +01:00
|
|
|
Action::MoveWorkspaceToMonitorLeft => {
|
|
|
|
|
if let Some(output) = self.niri.output_left() {
|
|
|
|
|
self.niri.layout.move_workspace_to_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-02 23:57:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWorkspaceToMonitorRight => {
|
|
|
|
|
if let Some(output) = self.niri.output_right() {
|
|
|
|
|
self.niri.layout.move_workspace_to_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-02 23:57:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWorkspaceToMonitorDown => {
|
|
|
|
|
if let Some(output) = self.niri.output_down() {
|
|
|
|
|
self.niri.layout.move_workspace_to_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-02 23:57:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Action::MoveWorkspaceToMonitorUp => {
|
|
|
|
|
if let Some(output) = self.niri.output_up() {
|
|
|
|
|
self.niri.layout.move_workspace_to_output(&output);
|
2024-02-26 18:47:46 +01:00
|
|
|
if !self.maybe_warp_cursor_to_focus_centered() {
|
|
|
|
|
self.move_cursor_to_output(&output);
|
|
|
|
|
}
|
2023-12-02 23:57:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-10 09:58:26 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_pointer_motion<I: InputBackend>(&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;
|
|
|
|
|
}
|
2023-12-05 10:24:41 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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();
|
|
|
|
|
|
2023-12-21 16:17:19 +04:00
|
|
|
// We received an event for the regular pointer, so show it now.
|
|
|
|
|
self.niri.tablet_cursor_location = None;
|
|
|
|
|
|
2023-12-21 16:19:16 +04:00
|
|
|
// Check if we have an active pointer constraint.
|
|
|
|
|
let mut pointer_confined = None;
|
2024-03-18 17:50:31 +04:00
|
|
|
if let Some(focus) = &self.niri.pointer_focus.surface {
|
|
|
|
|
let pos_within_surface = pos.to_i32_round() - focus.1;
|
2023-12-21 16:19:16 +04:00
|
|
|
|
|
|
|
|
let mut pointer_locked = false;
|
2024-03-18 17:50:31 +04:00
|
|
|
with_pointer_constraint(&focus.0, &pointer, |constraint| {
|
2023-12-21 16:19:16 +04:00
|
|
|
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) => {
|
2024-03-18 17:50:31 +04:00
|
|
|
pointer_confined = Some((focus.clone(), confine.region().cloned()));
|
2023-12-21 16:19:16 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// If the pointer is locked, only send relative motion.
|
|
|
|
|
if pointer_locked {
|
|
|
|
|
pointer.relative_motion(
|
|
|
|
|
self,
|
2024-03-18 17:50:31 +04:00
|
|
|
Some(focus.clone()),
|
2023-12-21 16:19:16 +04:00
|
|
|
&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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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();
|
2023-08-09 15:15:06 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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);
|
|
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let under = self.niri.surface_under_and_global_space(new_pos);
|
2023-12-21 16:19:16 +04:00
|
|
|
|
|
|
|
|
// Handle confined pointer.
|
|
|
|
|
if let Some((focus_surface, region)) = pointer_confined {
|
|
|
|
|
let mut prevent = false;
|
|
|
|
|
|
|
|
|
|
// Prevent the pointer from leaving the focused surface.
|
2024-03-18 17:50:31 +04:00
|
|
|
if Some(&focus_surface.0) != under.surface.as_ref().map(|(s, _)| s) {
|
2023-12-21 16:19:16 +04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-18 18:17:04 +04:00
|
|
|
self.niri.handle_focus_follows_mouse(&under);
|
|
|
|
|
|
2023-12-21 16:19:16 +04:00
|
|
|
// Activate a new confinement if necessary.
|
|
|
|
|
self.niri.maybe_activate_pointer_constraint(new_pos, &under);
|
|
|
|
|
|
2024-03-12 09:07:59 +04:00
|
|
|
self.niri.pointer_focus.clone_from(&under);
|
2023-12-09 09:22:58 +04:00
|
|
|
|
|
|
|
|
pointer.motion(
|
|
|
|
|
self,
|
2024-03-18 17:50:31 +04:00
|
|
|
under.surface.clone(),
|
2023-12-09 09:22:58 +04:00
|
|
|
&MotionEvent {
|
|
|
|
|
location: new_pos,
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
pointer.relative_motion(
|
|
|
|
|
self,
|
2024-03-18 17:50:31 +04:00
|
|
|
under.surface,
|
2023-12-09 09:22:58 +04:00
|
|
|
&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();
|
|
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_pointer_motion_absolute<I: InputBackend>(
|
|
|
|
|
&mut self,
|
|
|
|
|
event: I::PointerMotionAbsoluteEvent,
|
|
|
|
|
) {
|
|
|
|
|
let Some(output) = self.niri.global_space.outputs().next() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let output_geo = self.niri.global_space.output_geometry(output).unwrap();
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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);
|
|
|
|
|
}
|
2023-10-30 20:29:03 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let under = self.niri.surface_under_and_global_space(pos);
|
2024-03-18 18:17:04 +04:00
|
|
|
|
|
|
|
|
self.niri.handle_focus_follows_mouse(&under);
|
|
|
|
|
|
2023-12-21 16:19:16 +04:00
|
|
|
self.niri.maybe_activate_pointer_constraint(pos, &under);
|
2024-03-12 09:07:59 +04:00
|
|
|
self.niri.pointer_focus.clone_from(&under);
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.motion(
|
|
|
|
|
self,
|
2024-03-18 17:50:31 +04:00
|
|
|
under.surface,
|
2023-12-09 09:22:58 +04:00
|
|
|
&MotionEvent {
|
|
|
|
|
location: pos,
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
},
|
|
|
|
|
);
|
2023-08-10 09:58:26 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.frame(self);
|
2023-09-24 11:04:30 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
// We moved the regular pointer, so show it now.
|
|
|
|
|
self.niri.tablet_cursor_location = None;
|
2023-12-05 10:24:41 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
// Redraw to update the cursor position.
|
|
|
|
|
// FIXME: redraw only outputs overlapping the cursor.
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_pointer_button<I: InputBackend>(&mut self, event: I::PointerButtonEvent) {
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let button = event.button_code();
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let button_state = event.state();
|
2023-10-30 20:29:03 +04:00
|
|
|
|
2024-01-13 08:16:30 +04:00
|
|
|
if ButtonState::Pressed == button_state {
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some(mapped) = self.niri.window_under_cursor() {
|
|
|
|
|
let window = mapped.window.clone();
|
2023-12-09 09:22:58 +04:00
|
|
|
self.niri.layout.activate_window(&window);
|
2024-01-12 08:48:22 +04:00
|
|
|
|
|
|
|
|
// FIXME: granular.
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
} else if let Some(output) = self.niri.output_under_cursor() {
|
|
|
|
|
self.niri.layout.activate_output(&output);
|
2024-01-12 08:48:22 +04:00
|
|
|
|
|
|
|
|
// FIXME: granular.
|
|
|
|
|
self.niri.queue_redraw_all();
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
};
|
2023-09-06 14:51:37 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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();
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.button(
|
|
|
|
|
self,
|
|
|
|
|
&ButtonEvent {
|
|
|
|
|
button,
|
|
|
|
|
state: button_state,
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_pointer_axis<I: InputBackend>(&mut self, event: I::PointerAxisEvent) {
|
|
|
|
|
let source = event.source();
|
|
|
|
|
|
2024-03-22 09:35:17 +04:00
|
|
|
let horizontal_amount_v120 = event.amount_v120(Axis::Horizontal);
|
|
|
|
|
let vertical_amount_v120 = event.amount_v120(Axis::Vertical);
|
2023-12-09 09:22:58 +04:00
|
|
|
|
2024-03-22 09:41:10 +04:00
|
|
|
let horizontal_amount = event.amount(Axis::Horizontal).unwrap_or_else(|| {
|
|
|
|
|
// Winit backend, discrete scrolling.
|
|
|
|
|
horizontal_amount_v120.unwrap_or(0.0) / 120. * 15.
|
|
|
|
|
});
|
|
|
|
|
let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
|
|
|
|
|
// Winit backend, discrete scrolling.
|
|
|
|
|
vertical_amount_v120.unwrap_or(0.0) / 120. * 15.
|
|
|
|
|
});
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let mut frame = AxisFrame::new(event.time_msec()).source(source);
|
|
|
|
|
if horizontal_amount != 0.0 {
|
2024-01-05 17:05:35 +04:00
|
|
|
frame = frame
|
|
|
|
|
.relative_direction(Axis::Horizontal, event.relative_direction(Axis::Horizontal));
|
2023-12-09 09:22:58 +04:00
|
|
|
frame = frame.value(Axis::Horizontal, horizontal_amount);
|
2024-03-22 09:35:17 +04:00
|
|
|
if let Some(v120) = horizontal_amount_v120 {
|
|
|
|
|
frame = frame.v120(Axis::Horizontal, v120 as i32);
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if vertical_amount != 0.0 {
|
2024-01-05 17:05:35 +04:00
|
|
|
frame =
|
|
|
|
|
frame.relative_direction(Axis::Vertical, event.relative_direction(Axis::Vertical));
|
2023-12-09 09:22:58 +04:00
|
|
|
frame = frame.value(Axis::Vertical, vertical_amount);
|
2024-03-22 09:35:17 +04:00
|
|
|
if let Some(v120) = vertical_amount_v120 {
|
|
|
|
|
frame = frame.v120(Axis::Vertical, v120 as i32);
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2023-08-15 15:00:26 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-15 15:00:26 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
self.update_pointer_focus();
|
2023-08-16 11:43:52 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let pointer = &self.niri.seat.get_pointer().unwrap();
|
|
|
|
|
pointer.axis(self, frame);
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-12-05 10:24:41 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_tablet_tool_axis<I: InputBackend>(&mut self, event: I::TabletToolAxisEvent)
|
|
|
|
|
where
|
|
|
|
|
I::Device: 'static, // Needed for downcasting.
|
|
|
|
|
{
|
|
|
|
|
let Some(pos) = self.compute_tablet_position(&event) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2023-08-16 11:43:52 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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());
|
2023-08-15 15:00:26 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
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());
|
2023-08-16 11:43:52 +04:00
|
|
|
}
|
2023-12-08 08:32:42 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
tool.motion(
|
|
|
|
|
pos,
|
2024-03-18 17:50:31 +04:00
|
|
|
under.surface,
|
2023-12-09 09:22:58 +04:00
|
|
|
&tablet,
|
|
|
|
|
SERIAL_COUNTER.next_serial(),
|
|
|
|
|
event.time_msec(),
|
|
|
|
|
);
|
2023-12-05 10:24:41 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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<I: InputBackend>(&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 {
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some(mapped) = self.niri.window_under(pos) {
|
|
|
|
|
let window = mapped.window.clone();
|
2023-12-09 09:22:58 +04:00
|
|
|
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);
|
2023-08-16 11:43:52 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular.
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-16 11:43:52 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
TabletToolTipState::Up => {
|
|
|
|
|
tool.tip_up(event.time_msec());
|
2023-08-16 11:43:52 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-16 11:43:52 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_tablet_tool_proximity<I: InputBackend>(&mut self, event: I::TabletToolProximityEvent)
|
|
|
|
|
where
|
|
|
|
|
I::Device: 'static, // Needed for downcasting.
|
|
|
|
|
{
|
|
|
|
|
let Some(pos) = self.compute_tablet_position(&event) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2023-08-16 11:43:52 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
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>(&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 => {
|
2024-03-18 17:50:31 +04:00
|
|
|
if let Some(under) = under.surface {
|
2023-12-09 09:22:58 +04:00
|
|
|
tool.proximity_in(
|
|
|
|
|
pos,
|
|
|
|
|
under,
|
|
|
|
|
&tablet,
|
|
|
|
|
SERIAL_COUNTER.next_serial(),
|
|
|
|
|
event.time_msec(),
|
|
|
|
|
);
|
2023-08-16 11:43:52 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
self.niri.tablet_cursor_location = Some(pos);
|
2023-08-16 11:43:52 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
ProximityState::Out => {
|
|
|
|
|
tool.proximity_out(event.time_msec());
|
2023-10-08 09:57:59 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
// Move the mouse pointer here to avoid discontinuity.
|
|
|
|
|
//
|
|
|
|
|
// Plus, Wayland SDL2 currently warps the pointer into some weird
|
|
|
|
|
// location on proximity out, so this shuold help it a little.
|
|
|
|
|
if let Some(pos) = self.niri.tablet_cursor_location {
|
|
|
|
|
self.move_cursor(pos);
|
2023-10-08 09:57:59 +04:00
|
|
|
}
|
|
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
self.niri.tablet_cursor_location = None;
|
2023-10-08 09:57:59 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
}
|
2023-10-08 09:57:59 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
// FIXME: granular.
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_tablet_tool_button<I: InputBackend>(&mut self, event: I::TabletToolButtonEvent) {
|
|
|
|
|
let tool = self.niri.seat.tablet_seat().get_tool(&event.tool());
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if let Some(tool) = tool {
|
|
|
|
|
tool.button(
|
|
|
|
|
event.button(),
|
|
|
|
|
event.button_state(),
|
|
|
|
|
SERIAL_COUNTER.next_serial(),
|
|
|
|
|
event.time_msec(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_gesture_swipe_begin<I: InputBackend>(&mut self, event: I::GestureSwipeBeginEvent) {
|
|
|
|
|
if event.fingers() == 3 {
|
2024-02-29 08:56:20 +04:00
|
|
|
self.niri.gesture_swipe_3f_cumulative = Some((0., 0.));
|
2023-10-08 09:57:59 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
// We handled this event.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-08 09:57:59 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.gesture_swipe_begin(
|
|
|
|
|
self,
|
|
|
|
|
&GestureSwipeBeginEvent {
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
fingers: event.fingers(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 15:01:46 +04:00
|
|
|
fn on_gesture_swipe_update<I: InputBackend + 'static>(
|
|
|
|
|
&mut self,
|
|
|
|
|
event: I::GestureSwipeUpdateEvent,
|
|
|
|
|
) where
|
2024-02-03 09:00:08 +04:00
|
|
|
I::Device: 'static,
|
|
|
|
|
{
|
2024-02-29 08:56:20 +04:00
|
|
|
let mut delta_x = event.delta_x();
|
2024-02-03 09:00:08 +04:00
|
|
|
let mut delta_y = event.delta_y();
|
|
|
|
|
|
2024-03-02 15:01:46 +04:00
|
|
|
if let Some(libinput_event) =
|
|
|
|
|
(&event as &dyn Any).downcast_ref::<input::event::gesture::GestureSwipeUpdateEvent>()
|
|
|
|
|
{
|
2024-03-03 09:26:29 +04:00
|
|
|
delta_x = libinput_event.dx_unaccelerated();
|
2024-03-02 15:01:46 +04:00
|
|
|
delta_y = libinput_event.dy_unaccelerated();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-03 09:00:08 +04:00
|
|
|
let device = event.device();
|
|
|
|
|
if let Some(device) = (&device as &dyn Any).downcast_ref::<input::Device>() {
|
|
|
|
|
if device.config_scroll_natural_scroll_enabled() {
|
2024-02-29 08:56:20 +04:00
|
|
|
delta_x = -delta_x;
|
2024-02-03 09:00:08 +04:00
|
|
|
delta_y = -delta_y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-29 08:56:20 +04:00
|
|
|
if let Some((cx, cy)) = &mut self.niri.gesture_swipe_3f_cumulative {
|
|
|
|
|
*cx += delta_x;
|
2024-03-03 09:26:29 +04:00
|
|
|
*cy += delta_y;
|
2024-02-29 08:56:20 +04:00
|
|
|
|
|
|
|
|
// Check if the gesture moved far enough to decide. Threshold copied from GNOME Shell.
|
|
|
|
|
let (cx, cy) = (*cx, *cy);
|
|
|
|
|
if cx * cx + cy * cy >= 16. * 16. {
|
|
|
|
|
self.niri.gesture_swipe_3f_cumulative = None;
|
|
|
|
|
|
|
|
|
|
if let Some(output) = self.niri.output_under_cursor() {
|
|
|
|
|
if cx.abs() > cy.abs() {
|
|
|
|
|
self.niri.layout.view_offset_gesture_begin(&output);
|
|
|
|
|
} else {
|
|
|
|
|
self.niri.layout.workspace_switch_gesture_begin(&output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 14:33:22 +04:00
|
|
|
let timestamp = Duration::from_micros(event.time());
|
|
|
|
|
|
2024-02-29 08:56:20 +04:00
|
|
|
let mut handled = false;
|
2024-03-02 14:33:22 +04:00
|
|
|
let res = self
|
|
|
|
|
.niri
|
|
|
|
|
.layout
|
|
|
|
|
.workspace_switch_gesture_update(delta_y, timestamp);
|
2023-12-09 09:22:58 +04:00
|
|
|
if let Some(output) = res {
|
|
|
|
|
if let Some(output) = output {
|
|
|
|
|
self.niri.queue_redraw(output);
|
2023-09-02 15:09:07 +04:00
|
|
|
}
|
2024-02-29 08:56:20 +04:00
|
|
|
handled = true;
|
|
|
|
|
}
|
2023-10-08 09:57:59 +04:00
|
|
|
|
2024-03-02 15:23:40 +04:00
|
|
|
let res = self
|
|
|
|
|
.niri
|
|
|
|
|
.layout
|
|
|
|
|
.view_offset_gesture_update(delta_x, timestamp);
|
2024-02-29 08:56:20 +04:00
|
|
|
if let Some(output) = res {
|
|
|
|
|
if let Some(output) = output {
|
|
|
|
|
self.niri.queue_redraw(output);
|
|
|
|
|
}
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if handled {
|
2023-12-09 09:22:58 +04:00
|
|
|
// We handled this event.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-08 09:57:59 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.gesture_swipe_update(
|
|
|
|
|
self,
|
|
|
|
|
&GestureSwipeUpdateEvent {
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
delta: event.delta(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_gesture_swipe_end<I: InputBackend>(&mut self, event: I::GestureSwipeEndEvent) {
|
2024-02-29 08:56:20 +04:00
|
|
|
self.niri.gesture_swipe_3f_cumulative = None;
|
|
|
|
|
|
|
|
|
|
let mut handled = false;
|
2023-12-09 09:22:58 +04:00
|
|
|
let res = self
|
|
|
|
|
.niri
|
|
|
|
|
.layout
|
|
|
|
|
.workspace_switch_gesture_end(event.cancelled());
|
|
|
|
|
if let Some(output) = res {
|
|
|
|
|
self.niri.queue_redraw(output);
|
2024-02-29 08:56:20 +04:00
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let res = self.niri.layout.view_offset_gesture_end(event.cancelled());
|
|
|
|
|
if let Some(output) = res {
|
|
|
|
|
self.niri.queue_redraw(output);
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2024-02-29 08:56:20 +04:00
|
|
|
if handled {
|
2023-12-09 09:22:58 +04:00
|
|
|
// We handled this event.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.gesture_swipe_end(
|
|
|
|
|
self,
|
|
|
|
|
&GestureSwipeEndEvent {
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
cancelled: event.cancelled(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_gesture_pinch_begin<I: InputBackend>(&mut self, event: I::GesturePinchBeginEvent) {
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.gesture_pinch_begin(
|
|
|
|
|
self,
|
|
|
|
|
&GesturePinchBeginEvent {
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
fingers: event.fingers(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_gesture_pinch_update<I: InputBackend>(&mut self, event: I::GesturePinchUpdateEvent) {
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-10-21 20:48:48 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
|
|
|
|
|
pointer.gesture_pinch_update(
|
|
|
|
|
self,
|
|
|
|
|
&GesturePinchUpdateEvent {
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
delta: event.delta(),
|
|
|
|
|
scale: event.scale(),
|
|
|
|
|
rotation: event.rotation(),
|
|
|
|
|
},
|
|
|
|
|
);
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-08-15 18:17:26 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_gesture_pinch_end<I: InputBackend>(&mut self, event: I::GesturePinchEndEvent) {
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-12-03 13:50:07 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
2023-08-15 18:17:26 +04:00
|
|
|
}
|
2023-12-09 09:22:58 +04:00
|
|
|
|
|
|
|
|
pointer.gesture_pinch_end(
|
|
|
|
|
self,
|
|
|
|
|
&GesturePinchEndEvent {
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
cancelled: event.cancelled(),
|
|
|
|
|
},
|
|
|
|
|
);
|
2023-08-15 18:17:26 +04:00
|
|
|
}
|
2023-12-03 13:50:07 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_gesture_hold_begin<I: InputBackend>(&mut self, event: I::GestureHoldBeginEvent) {
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-12-03 13:50:07 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
2023-12-03 13:50:07 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
pointer.gesture_hold_begin(
|
|
|
|
|
self,
|
|
|
|
|
&GestureHoldBeginEvent {
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
fingers: event.fingers(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-12-03 13:50:07 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
fn on_gesture_hold_end<I: InputBackend>(&mut self, event: I::GestureHoldEndEvent) {
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
let pointer = self.niri.seat.get_pointer().unwrap();
|
2023-12-03 13:50:07 +04:00
|
|
|
|
2023-12-09 09:22:58 +04:00
|
|
|
if self.update_pointer_focus() {
|
|
|
|
|
pointer.frame(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pointer.gesture_hold_end(
|
|
|
|
|
self,
|
|
|
|
|
&GestureHoldEndEvent {
|
|
|
|
|
serial,
|
|
|
|
|
time: event.time_msec(),
|
|
|
|
|
cancelled: event.cancelled(),
|
|
|
|
|
},
|
|
|
|
|
);
|
2023-12-03 13:50:07 +04:00
|
|
|
}
|
2024-02-24 18:32:13 +01:00
|
|
|
|
|
|
|
|
/// Computes the cursor position for the touch event.
|
|
|
|
|
///
|
|
|
|
|
/// This function handles the touch output mapping, as well as coordinate transform
|
|
|
|
|
fn compute_touch_location<I: InputBackend, E: AbsolutePositionEvent<I>>(
|
|
|
|
|
&self,
|
|
|
|
|
evt: &E,
|
|
|
|
|
) -> Option<Point<f64, Logical>> {
|
|
|
|
|
let output = self.niri.output_for_touch()?;
|
|
|
|
|
let output_geo = self.niri.global_space.output_geometry(output).unwrap();
|
|
|
|
|
let transform = output.current_transform();
|
|
|
|
|
let size = transform.invert().transform_size(output_geo.size);
|
|
|
|
|
Some(
|
|
|
|
|
transform.transform_point_in(evt.position_transformed(size), &size.to_f64())
|
|
|
|
|
+ output_geo.loc.to_f64(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_touch_down<I: InputBackend>(&mut self, evt: I::TouchDownEvent) {
|
|
|
|
|
let Some(handle) = self.niri.seat.get_touch() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let Some(touch_location) = self.compute_touch_location(&evt) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if !handle.is_grabbed() {
|
|
|
|
|
let output_under_touch = self
|
|
|
|
|
.niri
|
|
|
|
|
.global_space
|
|
|
|
|
.output_under(touch_location)
|
|
|
|
|
.next()
|
|
|
|
|
.cloned();
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some(mapped) = self.niri.window_under(touch_location) {
|
|
|
|
|
let window = mapped.window.clone();
|
2024-02-24 18:32:13 +01:00
|
|
|
self.niri.layout.activate_window(&window);
|
|
|
|
|
|
|
|
|
|
// FIXME: granular.
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
} else if let Some(output) = output_under_touch {
|
|
|
|
|
self.niri.layout.activate_output(&output);
|
|
|
|
|
|
|
|
|
|
// FIXME: granular.
|
|
|
|
|
self.niri.queue_redraw_all();
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
2024-03-18 17:50:31 +04:00
|
|
|
let under = self.niri.surface_under_and_global_space(touch_location);
|
2024-02-24 18:32:13 +01:00
|
|
|
handle.down(
|
|
|
|
|
self,
|
2024-03-18 17:50:31 +04:00
|
|
|
under.surface,
|
2024-02-24 18:32:13 +01:00
|
|
|
&DownEvent {
|
|
|
|
|
slot: evt.slot(),
|
|
|
|
|
location: touch_location,
|
|
|
|
|
serial,
|
|
|
|
|
time: evt.time_msec(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
fn on_touch_up<I: InputBackend>(&mut self, evt: I::TouchUpEvent) {
|
|
|
|
|
let Some(handle) = self.niri.seat.get_touch() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
handle.up(
|
|
|
|
|
self,
|
|
|
|
|
&UpEvent {
|
|
|
|
|
slot: evt.slot(),
|
|
|
|
|
serial,
|
|
|
|
|
time: evt.time_msec(),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
fn on_touch_motion<I: InputBackend>(&mut self, evt: I::TouchMotionEvent) {
|
|
|
|
|
let Some(handle) = self.niri.seat.get_touch() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let Some(touch_location) = self.compute_touch_location(&evt) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2024-03-18 17:50:31 +04:00
|
|
|
let under = self.niri.surface_under_and_global_space(touch_location);
|
2024-02-24 18:32:13 +01:00
|
|
|
handle.motion(
|
|
|
|
|
self,
|
2024-03-18 17:50:31 +04:00
|
|
|
under.surface,
|
2024-02-24 18:32:13 +01:00
|
|
|
&TouchMotionEvent {
|
|
|
|
|
slot: evt.slot(),
|
|
|
|
|
location: touch_location,
|
|
|
|
|
time: evt.time_msec(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
fn on_touch_frame<I: InputBackend>(&mut self, _evt: I::TouchFrameEvent) {
|
|
|
|
|
let Some(handle) = self.niri.seat.get_touch() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
handle.frame(self);
|
|
|
|
|
}
|
|
|
|
|
fn on_touch_cancel<I: InputBackend>(&mut self, _evt: I::TouchCancelEvent) {
|
|
|
|
|
let Some(handle) = self.niri.seat.get_touch() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
handle.cancel(self);
|
|
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-10-27 16:50:02 +04:00
|
|
|
|
|
|
|
|
/// Check whether the key should be intercepted and mark intercepted
|
|
|
|
|
/// pressed keys as `suppressed`, thus preventing `releases` corresponding
|
|
|
|
|
/// to them from being delivered.
|
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
|
fn should_intercept_key(
|
|
|
|
|
suppressed_keys: &mut HashSet<u32>,
|
|
|
|
|
bindings: &Binds,
|
|
|
|
|
comp_mod: CompositorMod,
|
|
|
|
|
key_code: u32,
|
|
|
|
|
modified: Keysym,
|
|
|
|
|
raw: Option<Keysym>,
|
|
|
|
|
pressed: bool,
|
|
|
|
|
mods: ModifiersState,
|
2023-10-30 20:29:03 +04:00
|
|
|
screenshot_ui: &ScreenshotUi,
|
2023-12-28 09:36:10 +04:00
|
|
|
disable_power_key_handling: bool,
|
2023-10-27 16:50:02 +04:00
|
|
|
) -> FilterResult<Option<Action>> {
|
|
|
|
|
// Actions are only triggered on presses, release of the key
|
|
|
|
|
// shouldn't try to intercept anything unless we have marked
|
|
|
|
|
// the key to suppress.
|
|
|
|
|
if !pressed && !suppressed_keys.contains(&key_code) {
|
|
|
|
|
return FilterResult::Forward;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-28 09:36:10 +04:00
|
|
|
let mut final_action = action(
|
|
|
|
|
bindings,
|
|
|
|
|
comp_mod,
|
|
|
|
|
modified,
|
|
|
|
|
raw,
|
|
|
|
|
mods,
|
|
|
|
|
disable_power_key_handling,
|
|
|
|
|
);
|
2023-12-09 09:43:26 +04:00
|
|
|
|
|
|
|
|
// Allow only a subset of compositor actions while the screenshot UI is open, since the user
|
|
|
|
|
// cannot see the screen.
|
|
|
|
|
if screenshot_ui.is_open() {
|
|
|
|
|
let mut use_screenshot_ui_action = true;
|
|
|
|
|
|
|
|
|
|
if let Some(action) = &final_action {
|
|
|
|
|
if allowed_during_screenshot(action) {
|
|
|
|
|
use_screenshot_ui_action = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if use_screenshot_ui_action {
|
|
|
|
|
final_action = screenshot_ui.action(raw, mods);
|
|
|
|
|
}
|
2023-10-30 20:29:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match (final_action, pressed) {
|
2023-10-27 16:50:02 +04:00
|
|
|
(Some(action), true) => {
|
|
|
|
|
suppressed_keys.insert(key_code);
|
|
|
|
|
FilterResult::Intercept(Some(action))
|
|
|
|
|
}
|
|
|
|
|
(_, false) => {
|
|
|
|
|
suppressed_keys.remove(&key_code);
|
|
|
|
|
FilterResult::Intercept(None)
|
|
|
|
|
}
|
|
|
|
|
(None, true) => FilterResult::Forward,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn action(
|
|
|
|
|
bindings: &Binds,
|
|
|
|
|
comp_mod: CompositorMod,
|
|
|
|
|
modified: Keysym,
|
|
|
|
|
raw: Option<Keysym>,
|
|
|
|
|
mods: ModifiersState,
|
2023-12-28 09:36:10 +04:00
|
|
|
disable_power_key_handling: bool,
|
2023-10-27 16:50:02 +04:00
|
|
|
) -> Option<Action> {
|
|
|
|
|
use keysyms::*;
|
|
|
|
|
|
|
|
|
|
// Handle hardcoded binds.
|
|
|
|
|
#[allow(non_upper_case_globals)] // wat
|
|
|
|
|
match modified.raw() {
|
|
|
|
|
modified @ KEY_XF86Switch_VT_1..=KEY_XF86Switch_VT_12 => {
|
|
|
|
|
let vt = (modified - KEY_XF86Switch_VT_1 + 1) as i32;
|
|
|
|
|
return Some(Action::ChangeVt(vt));
|
|
|
|
|
}
|
2023-12-28 09:36:10 +04:00
|
|
|
KEY_XF86PowerOff if !disable_power_key_handling => return Some(Action::Suspend),
|
2023-10-27 16:50:02 +04:00
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-09 16:16:18 +04:00
|
|
|
bound_action(bindings, comp_mod, raw, mods)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn bound_action(
|
|
|
|
|
bindings: &Binds,
|
|
|
|
|
comp_mod: CompositorMod,
|
|
|
|
|
raw: Option<Keysym>,
|
|
|
|
|
mods: ModifiersState,
|
|
|
|
|
) -> Option<Action> {
|
2023-10-27 16:50:02 +04:00
|
|
|
// Handle configured binds.
|
|
|
|
|
let mut modifiers = Modifiers::empty();
|
|
|
|
|
if mods.ctrl {
|
|
|
|
|
modifiers |= Modifiers::CTRL;
|
|
|
|
|
}
|
|
|
|
|
if mods.shift {
|
|
|
|
|
modifiers |= Modifiers::SHIFT;
|
|
|
|
|
}
|
|
|
|
|
if mods.alt {
|
|
|
|
|
modifiers |= Modifiers::ALT;
|
|
|
|
|
}
|
|
|
|
|
if mods.logo {
|
|
|
|
|
modifiers |= Modifiers::SUPER;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-09 16:23:05 +04:00
|
|
|
let (mod_down, comp_mod) = match comp_mod {
|
2023-10-27 16:50:02 +04:00
|
|
|
CompositorMod::Super => (mods.logo, Modifiers::SUPER),
|
|
|
|
|
CompositorMod::Alt => (mods.alt, Modifiers::ALT),
|
|
|
|
|
};
|
|
|
|
|
if mod_down {
|
|
|
|
|
modifiers |= Modifiers::COMPOSITOR;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 21:42:00 +04:00
|
|
|
let raw = raw?;
|
2023-10-27 16:50:02 +04:00
|
|
|
|
|
|
|
|
for bind in &bindings.0 {
|
2024-03-22 10:36:19 +04:00
|
|
|
if bind.key.trigger != Trigger::Keysym(raw) {
|
2023-10-27 16:50:02 +04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-09 16:23:05 +04:00
|
|
|
let mut bind_modifiers = bind.key.modifiers;
|
|
|
|
|
if bind_modifiers.contains(Modifiers::COMPOSITOR) {
|
|
|
|
|
bind_modifiers |= comp_mod;
|
|
|
|
|
} else if bind_modifiers.contains(comp_mod) {
|
|
|
|
|
bind_modifiers |= Modifiers::COMPOSITOR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if bind_modifiers == modifiers {
|
2024-03-01 12:50:49 +01:00
|
|
|
return Some(bind.action.clone());
|
2023-10-27 16:50:02 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn should_activate_monitors<I: InputBackend>(event: &InputEvent<I>) -> bool {
|
|
|
|
|
match event {
|
|
|
|
|
InputEvent::Keyboard { event } if event.state() == KeyState::Pressed => true,
|
2024-02-17 14:07:51 +04:00
|
|
|
InputEvent::PointerButton { event } if event.state() == ButtonState::Pressed => true,
|
2023-10-27 16:50:02 +04:00
|
|
|
InputEvent::PointerMotion { .. }
|
|
|
|
|
| InputEvent::PointerMotionAbsolute { .. }
|
|
|
|
|
| InputEvent::PointerAxis { .. }
|
|
|
|
|
| InputEvent::GestureSwipeBegin { .. }
|
|
|
|
|
| InputEvent::GesturePinchBegin { .. }
|
|
|
|
|
| InputEvent::GestureHoldBegin { .. }
|
|
|
|
|
| InputEvent::TouchDown { .. }
|
|
|
|
|
| InputEvent::TouchMotion { .. }
|
|
|
|
|
| InputEvent::TabletToolAxis { .. }
|
|
|
|
|
| InputEvent::TabletToolProximity { .. }
|
|
|
|
|
| InputEvent::TabletToolTip { .. }
|
|
|
|
|
| InputEvent::TabletToolButton { .. } => true,
|
|
|
|
|
// Ignore events like device additions and removals, key releases, gesture ends.
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 19:20:46 +04:00
|
|
|
fn should_hide_hotkey_overlay<I: InputBackend>(event: &InputEvent<I>) -> bool {
|
|
|
|
|
match event {
|
|
|
|
|
InputEvent::Keyboard { event } if event.state() == KeyState::Pressed => true,
|
2024-02-17 14:07:51 +04:00
|
|
|
InputEvent::PointerButton { event } if event.state() == ButtonState::Pressed => true,
|
|
|
|
|
InputEvent::PointerAxis { .. }
|
2024-01-19 08:33:54 +04:00
|
|
|
| InputEvent::GestureSwipeBegin { .. }
|
|
|
|
|
| InputEvent::GesturePinchBegin { .. }
|
|
|
|
|
| InputEvent::TouchDown { .. }
|
|
|
|
|
| InputEvent::TouchMotion { .. }
|
|
|
|
|
| InputEvent::TabletToolTip { .. }
|
|
|
|
|
| InputEvent::TabletToolButton { .. } => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn should_hide_exit_confirm_dialog<I: InputBackend>(event: &InputEvent<I>) -> bool {
|
|
|
|
|
match event {
|
|
|
|
|
InputEvent::Keyboard { event } if event.state() == KeyState::Pressed => true,
|
2024-02-17 14:07:51 +04:00
|
|
|
InputEvent::PointerButton { event } if event.state() == ButtonState::Pressed => true,
|
|
|
|
|
InputEvent::PointerAxis { .. }
|
2024-01-18 19:20:46 +04:00
|
|
|
| InputEvent::GestureSwipeBegin { .. }
|
|
|
|
|
| InputEvent::GesturePinchBegin { .. }
|
|
|
|
|
| InputEvent::TouchDown { .. }
|
|
|
|
|
| InputEvent::TouchMotion { .. }
|
|
|
|
|
| InputEvent::TabletToolTip { .. }
|
|
|
|
|
| InputEvent::TabletToolButton { .. } => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-08 13:51:54 +04:00
|
|
|
fn should_notify_activity<I: InputBackend>(event: &InputEvent<I>) -> bool {
|
2024-02-10 08:38:19 +04:00
|
|
|
!matches!(
|
|
|
|
|
event,
|
|
|
|
|
InputEvent::DeviceAdded { .. } | InputEvent::DeviceRemoved { .. }
|
|
|
|
|
)
|
2024-02-08 13:51:54 +04:00
|
|
|
}
|
|
|
|
|
|
2023-12-09 09:43:26 +04:00
|
|
|
fn allowed_when_locked(action: &Action) -> bool {
|
|
|
|
|
matches!(
|
|
|
|
|
action,
|
2024-02-12 07:53:06 +04:00
|
|
|
Action::Quit(_)
|
2023-12-09 09:43:26 +04:00
|
|
|
| Action::ChangeVt(_)
|
|
|
|
|
| Action::Suspend
|
|
|
|
|
| Action::PowerOffMonitors
|
|
|
|
|
| Action::SwitchLayout(_)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn allowed_during_screenshot(action: &Action) -> bool {
|
|
|
|
|
matches!(
|
|
|
|
|
action,
|
2024-02-12 07:53:06 +04:00
|
|
|
Action::Quit(_) | Action::ChangeVt(_) | Action::Suspend | Action::PowerOffMonitors
|
2023-12-09 09:43:26 +04:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-16 20:21:36 +04:00
|
|
|
pub fn apply_libinput_settings(config: &niri_config::Input, device: &mut input::Device) {
|
|
|
|
|
// According to Mutter code, this setting is specific to touchpads.
|
|
|
|
|
let is_touchpad = device.config_tap_finger_count() > 0;
|
|
|
|
|
if is_touchpad {
|
|
|
|
|
let c = &config.touchpad;
|
|
|
|
|
let _ = device.config_tap_set_enabled(c.tap);
|
|
|
|
|
let _ = device.config_dwt_set_enabled(c.dwt);
|
2024-02-03 08:20:15 +04:00
|
|
|
let _ = device.config_dwtp_set_enabled(c.dwtp);
|
2024-01-16 20:21:36 +04:00
|
|
|
let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);
|
|
|
|
|
let _ = device.config_accel_set_speed(c.accel_speed);
|
|
|
|
|
|
|
|
|
|
if let Some(accel_profile) = c.accel_profile {
|
|
|
|
|
let _ = device.config_accel_set_profile(accel_profile.into());
|
|
|
|
|
} else if let Some(default) = device.config_accel_default_profile() {
|
|
|
|
|
let _ = device.config_accel_set_profile(default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(tap_button_map) = c.tap_button_map {
|
|
|
|
|
let _ = device.config_tap_set_button_map(tap_button_map.into());
|
|
|
|
|
} else if let Some(default) = device.config_tap_default_button_map() {
|
|
|
|
|
let _ = device.config_tap_set_button_map(default);
|
|
|
|
|
}
|
2024-03-13 21:26:03 -07:00
|
|
|
|
|
|
|
|
if let Some(method) = c.click_method {
|
|
|
|
|
let _ = device.config_click_set_method(method.into());
|
|
|
|
|
} else if let Some(default) = device.config_click_default_method() {
|
|
|
|
|
let _ = device.config_click_set_method(default);
|
|
|
|
|
}
|
2024-01-16 20:21:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is how Mutter tells apart mice.
|
|
|
|
|
let mut is_trackball = false;
|
|
|
|
|
let mut is_trackpoint = false;
|
|
|
|
|
if let Some(udev_device) = unsafe { device.udev_device() } {
|
|
|
|
|
if udev_device.property_value("ID_INPUT_TRACKBALL").is_some() {
|
|
|
|
|
is_trackball = true;
|
|
|
|
|
}
|
|
|
|
|
if udev_device
|
|
|
|
|
.property_value("ID_INPUT_POINTINGSTICK")
|
|
|
|
|
.is_some()
|
|
|
|
|
{
|
|
|
|
|
is_trackpoint = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let is_mouse = device.has_capability(input::DeviceCapability::Pointer)
|
|
|
|
|
&& !is_touchpad
|
|
|
|
|
&& !is_trackball
|
|
|
|
|
&& !is_trackpoint;
|
|
|
|
|
if is_mouse {
|
|
|
|
|
let c = &config.mouse;
|
|
|
|
|
let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);
|
|
|
|
|
let _ = device.config_accel_set_speed(c.accel_speed);
|
|
|
|
|
|
|
|
|
|
if let Some(accel_profile) = c.accel_profile {
|
|
|
|
|
let _ = device.config_accel_set_profile(accel_profile.into());
|
|
|
|
|
} else if let Some(default) = device.config_accel_default_profile() {
|
|
|
|
|
let _ = device.config_accel_set_profile(default);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-14 16:24:46 +01:00
|
|
|
|
|
|
|
|
if is_trackpoint {
|
|
|
|
|
let c = &config.trackpoint;
|
|
|
|
|
let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);
|
|
|
|
|
let _ = device.config_accel_set_speed(c.accel_speed);
|
|
|
|
|
|
|
|
|
|
if let Some(accel_profile) = c.accel_profile {
|
|
|
|
|
let _ = device.config_accel_set_profile(accel_profile.into());
|
|
|
|
|
} else if let Some(default) = device.config_accel_default_profile() {
|
|
|
|
|
let _ = device.config_accel_set_profile(default);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-16 20:21:36 +04:00
|
|
|
}
|
|
|
|
|
|
2023-10-27 16:50:02 +04:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2024-02-22 14:04:18 +04:00
|
|
|
use niri_config::{Bind, Key};
|
2024-01-07 09:07:22 +04:00
|
|
|
|
2023-10-30 14:21:07 +04:00
|
|
|
use super::*;
|
2023-10-27 16:50:02 +04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn bindings_suppress_keys() {
|
|
|
|
|
let close_keysym = Keysym::q;
|
|
|
|
|
let bindings = Binds(vec![Bind {
|
|
|
|
|
key: Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
trigger: Trigger::Keysym(close_keysym),
|
2023-10-27 16:50:02 +04:00
|
|
|
modifiers: Modifiers::COMPOSITOR | Modifiers::CTRL,
|
|
|
|
|
},
|
2024-03-01 12:50:49 +01:00
|
|
|
action: Action::CloseWindow,
|
2023-10-27 16:50:02 +04:00
|
|
|
}]);
|
|
|
|
|
|
|
|
|
|
let comp_mod = CompositorMod::Super;
|
|
|
|
|
let mut suppressed_keys = HashSet::new();
|
|
|
|
|
|
2023-10-30 20:29:03 +04:00
|
|
|
let screenshot_ui = ScreenshotUi::new();
|
2023-12-28 09:36:10 +04:00
|
|
|
let disable_power_key_handling = false;
|
2023-10-30 20:29:03 +04:00
|
|
|
|
2023-10-27 16:50:02 +04:00
|
|
|
// The key_code we pick is arbitrary, the only thing
|
|
|
|
|
// that matters is that they are different between cases.
|
|
|
|
|
|
|
|
|
|
let close_key_code = close_keysym.into();
|
|
|
|
|
let close_key_event = |suppr: &mut HashSet<u32>, mods: ModifiersState, pressed| {
|
|
|
|
|
should_intercept_key(
|
|
|
|
|
suppr,
|
|
|
|
|
&bindings,
|
|
|
|
|
comp_mod,
|
|
|
|
|
close_key_code,
|
|
|
|
|
close_keysym,
|
|
|
|
|
Some(close_keysym),
|
|
|
|
|
pressed,
|
|
|
|
|
mods,
|
2023-10-30 20:29:03 +04:00
|
|
|
&screenshot_ui,
|
2023-12-28 09:36:10 +04:00
|
|
|
disable_power_key_handling,
|
2023-10-27 16:50:02 +04:00
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Key event with the code which can't trigger any action.
|
|
|
|
|
let none_key_event = |suppr: &mut HashSet<u32>, mods: ModifiersState, pressed| {
|
|
|
|
|
should_intercept_key(
|
|
|
|
|
suppr,
|
|
|
|
|
&bindings,
|
|
|
|
|
comp_mod,
|
|
|
|
|
Keysym::l.into(),
|
|
|
|
|
Keysym::l,
|
|
|
|
|
Some(Keysym::l),
|
|
|
|
|
pressed,
|
|
|
|
|
mods,
|
2023-10-30 20:29:03 +04:00
|
|
|
&screenshot_ui,
|
2023-12-28 09:36:10 +04:00
|
|
|
disable_power_key_handling,
|
2023-10-27 16:50:02 +04:00
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-30 14:21:25 +04:00
|
|
|
let mut mods = ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
ctrl: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
2023-10-27 16:50:02 +04:00
|
|
|
|
|
|
|
|
// Action press/release.
|
|
|
|
|
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, true);
|
|
|
|
|
assert!(matches!(
|
|
|
|
|
filter,
|
|
|
|
|
FilterResult::Intercept(Some(Action::CloseWindow))
|
|
|
|
|
));
|
|
|
|
|
assert!(suppressed_keys.contains(&close_key_code));
|
|
|
|
|
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, false);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Intercept(None)));
|
|
|
|
|
assert!(suppressed_keys.is_empty());
|
|
|
|
|
|
|
|
|
|
// Remove mod to make it for a binding.
|
|
|
|
|
|
|
|
|
|
mods.shift = true;
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, true);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Forward));
|
|
|
|
|
|
|
|
|
|
mods.shift = false;
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, false);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Forward));
|
|
|
|
|
|
|
|
|
|
// Just none press/release.
|
|
|
|
|
|
|
|
|
|
let filter = none_key_event(&mut suppressed_keys, mods, true);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Forward));
|
|
|
|
|
|
|
|
|
|
let filter = none_key_event(&mut suppressed_keys, mods, false);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Forward));
|
|
|
|
|
|
|
|
|
|
// Press action, press arbitrary, release action, release arbitrary.
|
|
|
|
|
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, true);
|
|
|
|
|
assert!(matches!(
|
|
|
|
|
filter,
|
|
|
|
|
FilterResult::Intercept(Some(Action::CloseWindow))
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
let filter = none_key_event(&mut suppressed_keys, mods, true);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Forward));
|
|
|
|
|
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, false);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Intercept(None)));
|
|
|
|
|
|
|
|
|
|
let filter = none_key_event(&mut suppressed_keys, mods, false);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Forward));
|
|
|
|
|
|
|
|
|
|
// Trigger and remove all mods.
|
|
|
|
|
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, true);
|
|
|
|
|
assert!(matches!(
|
|
|
|
|
filter,
|
|
|
|
|
FilterResult::Intercept(Some(Action::CloseWindow))
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
mods = Default::default();
|
|
|
|
|
let filter = close_key_event(&mut suppressed_keys, mods, false);
|
|
|
|
|
assert!(matches!(filter, FilterResult::Intercept(None)));
|
|
|
|
|
|
|
|
|
|
// Ensure that no keys are being suppressed.
|
|
|
|
|
assert!(suppressed_keys.is_empty());
|
|
|
|
|
}
|
2024-02-09 16:16:18 +04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn comp_mod_handling() {
|
|
|
|
|
let bindings = Binds(vec![
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
trigger: Trigger::Keysym(Keysym::q),
|
2024-02-09 16:16:18 +04:00
|
|
|
modifiers: Modifiers::COMPOSITOR,
|
|
|
|
|
},
|
2024-03-01 12:50:49 +01:00
|
|
|
action: Action::CloseWindow,
|
2024-02-09 16:16:18 +04:00
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
trigger: Trigger::Keysym(Keysym::h),
|
2024-02-09 16:16:18 +04:00
|
|
|
modifiers: Modifiers::SUPER,
|
|
|
|
|
},
|
2024-03-01 12:50:49 +01:00
|
|
|
action: Action::FocusColumnLeft,
|
2024-02-09 16:16:18 +04:00
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
trigger: Trigger::Keysym(Keysym::j),
|
2024-02-09 16:16:18 +04:00
|
|
|
modifiers: Modifiers::empty(),
|
|
|
|
|
},
|
2024-03-01 12:50:49 +01:00
|
|
|
action: Action::FocusWindowDown,
|
2024-02-09 16:16:18 +04:00
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
trigger: Trigger::Keysym(Keysym::k),
|
2024-02-09 16:16:18 +04:00
|
|
|
modifiers: Modifiers::COMPOSITOR | Modifiers::SUPER,
|
|
|
|
|
},
|
2024-03-01 12:50:49 +01:00
|
|
|
action: Action::FocusWindowUp,
|
2024-02-09 16:16:18 +04:00
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
trigger: Trigger::Keysym(Keysym::l),
|
2024-02-09 16:16:18 +04:00
|
|
|
modifiers: Modifiers::SUPER | Modifiers::ALT,
|
|
|
|
|
},
|
2024-03-01 12:50:49 +01:00
|
|
|
action: Action::FocusColumnRight,
|
2024-02-09 16:16:18 +04:00
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::q),
|
|
|
|
|
ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
Some(Action::CloseWindow)
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::q),
|
|
|
|
|
ModifiersState::default(),
|
|
|
|
|
),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
|
2024-02-09 16:23:05 +04:00
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::h),
|
|
|
|
|
ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
Some(Action::FocusColumnLeft)
|
|
|
|
|
);
|
2024-02-09 16:16:18 +04:00
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::h),
|
|
|
|
|
ModifiersState::default(),
|
|
|
|
|
),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::j),
|
|
|
|
|
ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::j),
|
|
|
|
|
ModifiersState::default(),
|
|
|
|
|
),
|
|
|
|
|
Some(Action::FocusWindowDown)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::k),
|
|
|
|
|
ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
Some(Action::FocusWindowUp)
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::k),
|
|
|
|
|
ModifiersState::default(),
|
|
|
|
|
),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
|
2024-02-09 16:23:05 +04:00
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::l),
|
|
|
|
|
ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
alt: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
Some(Action::FocusColumnRight)
|
|
|
|
|
);
|
2024-02-09 16:16:18 +04:00
|
|
|
assert_eq!(
|
|
|
|
|
bound_action(
|
|
|
|
|
&bindings,
|
|
|
|
|
CompositorMod::Super,
|
|
|
|
|
Some(Keysym::l),
|
|
|
|
|
ModifiersState {
|
|
|
|
|
logo: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-10-27 16:50:02 +04:00
|
|
|
}
|