diff --git a/Cargo.toml b/Cargo.toml index e24c120826ad..1ee96c910edd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ wayland-egl = { version = "0.32.7", optional = true } wayland-protocols = { version = "0.32.8", features = ["unstable", "staging", "server"], optional = true } wayland-protocols-wlr = { version = "0.3.8", features = ["server"], optional = true } wayland-protocols-misc = { version = "0.3.8", features = ["server"], optional = true } -wayland-server = { version = "0.31.9", optional = true } +wayland-server = { version = "0.31.10", optional = true } wayland-sys = { version = "0.31.6", optional = true } wayland-backend = { version = "0.3.10", optional = true } winit = { version = "0.30.0", default-features = false, features = ["wayland", "wayland-dlopen", "x11", "rwh_06"], optional = true } diff --git a/anvil/src/focus.rs b/anvil/src/focus.rs index 13ca8bb65ac3..0503253789e1 100644 --- a/anvil/src/focus.rs +++ b/anvil/src/focus.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; #[cfg(feature = "xwayland")] use smithay::xwayland::X11Surface; pub use smithay::{ - backend::input::KeyState, + backend::input::KeyEvent, desktop::{LayerSurface, PopupKind}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, @@ -311,7 +311,7 @@ impl KeyboardTarget> for KeyboardF seat: &Seat>, data: &mut AnvilState, key: KeysymHandle<'_>, - state: KeyState, + state: KeyEvent, serial: Serial, time: u32, ) { diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 14a113fbc4a9..f36cf6672441 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -9,8 +9,8 @@ use smithay::backend::renderer::DebugFlags; use smithay::{ backend::input::{ - self, Axis, AxisSource, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, - PointerAxisEvent, PointerButtonEvent, + self, Axis, AxisSource, Event, InputBackend, InputEvent, KeyEvent, Keycode, PointerAxisEvent, + PointerButtonEvent, }, desktop::{layer_map_for_output, WindowSurfaceType}, input::{ @@ -65,6 +65,13 @@ use smithay::{ }, }; +#[derive(Clone, Debug, PartialEq, Eq)] +struct KeyboardEvent { + kind: KeyEvent, + code: Keycode, + time_msec: u32, +} + impl AnvilState { // Allow in this method because of existing usage #[allow(clippy::uninlined_format_args)] @@ -136,12 +143,12 @@ impl AnvilState { } } - fn keyboard_key_to_action(&mut self, evt: B::KeyboardKeyEvent) -> KeyAction { - let keycode = evt.key_code(); - let state = evt.state(); + fn keyboard_event_to_action(&mut self, evt: KeyboardEvent) -> KeyAction { + let keycode = evt.code; + let state = evt.kind; debug!(?keycode, ?state, "key"); let serial = SCOUNTER.next_serial(); - let time = Event::time_msec(&evt); + let time = evt.time_msec; let mut suppressed_keys = self.suppressed_keys.clone(); let keyboard = self.seat.get_keyboard().unwrap(); @@ -193,27 +200,36 @@ impl AnvilState { // Additionally add the key to the suppressed keys // so that we can decide on a release if the key // should be forwarded to the client or not. - if let KeyState::Pressed = state { - if !inhibited { - let action = process_keyboard_shortcut(*modifiers, keysym); + match state { + KeyEvent::Pressed | KeyEvent::Repeated => { + let filter = if !inhibited { + let action = process_keyboard_shortcut(*modifiers, keysym); - if action.is_some() { - suppressed_keys.push(keysym); - } + if action.is_some() { + suppressed_keys.push(keysym); + } - action - .map(FilterResult::Intercept) - .unwrap_or(FilterResult::Forward) - } else { - FilterResult::Forward + action + .map(FilterResult::Intercept) + .unwrap_or(FilterResult::Forward) + } else { + FilterResult::Forward + }; + + // If the user keeps holding the key long enough to trigger a repeat that gets forwarded to the client, then the client needs to receive the release event too. + if let (&FilterResult::Forward, KeyEvent::Repeated) = (&filter, state) { + suppressed_keys.retain(|k| *k != keysym); + } + filter } - } else { - let suppressed = suppressed_keys.contains(&keysym); - if suppressed { - suppressed_keys.retain(|k| *k != keysym); - FilterResult::Intercept(KeyAction::None) - } else { - FilterResult::Forward + KeyEvent::Released => { + let suppressed = suppressed_keys.contains(&keysym); + if suppressed { + suppressed_keys.retain(|k| *k != keysym); + FilterResult::Intercept(KeyAction::None) + } else { + FilterResult::Forward + } } } }) @@ -223,6 +239,19 @@ impl AnvilState { action } + fn on_keyboard_event( + &mut self, + evt: B::KeyboardKeyEvent, + on_event: impl Fn(&mut Self, KeyEvent, u32, Keycode) + Clone + 'static, + ) { + self.seat.get_keyboard().unwrap().key_register_repeat::( + self, + |data: &Self| &data.handle, + evt, + on_event, + ) + } + fn on_pointer_button(&mut self, evt: B::PointerButtonEvent) { let serial = SCOUNTER.next_serial(); let button = evt.button_code(); @@ -445,91 +474,111 @@ impl AnvilState { impl AnvilState { pub fn process_input_event_windowed(&mut self, event: InputEvent, output_name: &str) { match event { - InputEvent::Keyboard { event } => match self.keyboard_key_to_action::(event) { - KeyAction::ScaleUp => { - let output = self - .space - .outputs() - .find(|o| o.name() == output_name) - .unwrap() - .clone(); - - let current_scale = output.current_scale().fractional_scale(); - let new_scale = current_scale + 0.25; - output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); + InputEvent::Keyboard { event } => { + let output_name = output_name.to_owned(); + self.on_keyboard_event::(event, move |state: &mut Self, event, time_ms, keycode| { + state.process_key_event_windowed(event, time_ms, keycode, &output_name) + }) + } - crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); - self.backend_data.reset_buffers(&output); - } + InputEvent::PointerMotionAbsolute { event } => { + let output = self + .space + .outputs() + .find(|o| o.name() == output_name) + .unwrap() + .clone(); + self.on_pointer_move_absolute_windowed::(event, &output) + } + InputEvent::PointerButton { event } => self.on_pointer_button::(event), + InputEvent::PointerAxis { event } => self.on_pointer_axis::(event), + _ => (), // other events are not handled in anvil (yet) + } + } - KeyAction::ScaleDown => { - let output = self - .space - .outputs() - .find(|o| o.name() == output_name) - .unwrap() - .clone(); + fn process_key_event_windowed( + &mut self, + event: KeyEvent, + time_ms: u32, + keycode: Keycode, + output_name: &str, + ) { + let event = KeyboardEvent { + kind: event, + code: keycode, + time_msec: time_ms, + }; + match self.keyboard_event_to_action(event) { + KeyAction::ScaleUp => { + let output = self + .space + .outputs() + .find(|o| o.name() == output_name) + .unwrap() + .clone(); - let current_scale = output.current_scale().fractional_scale(); - let new_scale = f64::max(1.0, current_scale - 0.25); - output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); + let current_scale = output.current_scale().fractional_scale(); + let new_scale = current_scale + 0.25; + output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); - crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); - self.backend_data.reset_buffers(&output); - } + crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); + self.backend_data.reset_buffers(&output); + } - KeyAction::RotateOutput => { - let output = self - .space - .outputs() - .find(|o| o.name() == output_name) - .unwrap() - .clone(); + KeyAction::ScaleDown => { + let output = self + .space + .outputs() + .find(|o| o.name() == output_name) + .unwrap() + .clone(); - let current_transform = output.current_transform(); - let new_transform = match current_transform { - Transform::Normal => Transform::_90, - Transform::_90 => Transform::_180, - Transform::_180 => Transform::_270, - Transform::_270 => Transform::Flipped, - Transform::Flipped => Transform::Flipped90, - Transform::Flipped90 => Transform::Flipped180, - Transform::Flipped180 => Transform::Flipped270, - Transform::Flipped270 => Transform::Normal, - }; - tracing::info!(?current_transform, ?new_transform, output = ?output.name(), "changing output transform"); - output.change_current_state(None, Some(new_transform), None, None); - crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); - self.backend_data.reset_buffers(&output); - } + let current_scale = output.current_scale().fractional_scale(); + let new_scale = f64::max(1.0, current_scale - 0.25); + output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); - action => match action { - KeyAction::None - | KeyAction::Quit - | KeyAction::Run(_) - | KeyAction::TogglePreview - | KeyAction::ToggleDecorations => self.process_common_key_action(action), - - _ => tracing::warn!( - ?action, - output_name, - "Key action unsupported on on output backend.", - ), - }, - }, + crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); + self.backend_data.reset_buffers(&output); + } - InputEvent::PointerMotionAbsolute { event } => { + KeyAction::RotateOutput => { let output = self .space .outputs() .find(|o| o.name() == output_name) .unwrap() .clone(); - self.on_pointer_move_absolute_windowed::(event, &output) + + let current_transform = output.current_transform(); + let new_transform = match current_transform { + Transform::Normal => Transform::_90, + Transform::_90 => Transform::_180, + Transform::_180 => Transform::_270, + Transform::_270 => Transform::Flipped, + Transform::Flipped => Transform::Flipped90, + Transform::Flipped90 => Transform::Flipped180, + Transform::Flipped180 => Transform::Flipped270, + Transform::Flipped270 => Transform::Normal, + }; + tracing::info!(?current_transform, ?new_transform, output = ?output.name(), "changing output transform"); + output.change_current_state(None, Some(new_transform), None, None); + crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); + self.backend_data.reset_buffers(&output); } - InputEvent::PointerButton { event } => self.on_pointer_button::(event), - InputEvent::PointerAxis { event } => self.on_pointer_axis::(event), - _ => (), // other events are not handled in anvil (yet) + + action => match action { + KeyAction::None + | KeyAction::Quit + | KeyAction::Run(_) + | KeyAction::TogglePreview + | KeyAction::ToggleDecorations => self.process_common_key_action(action), + + _ => tracing::warn!( + ?action, + output_name, + "Key action unsupported on on output backend.", + ), + }, } } @@ -559,11 +608,12 @@ impl AnvilState { pub fn release_all_keys(&mut self) { let keyboard = self.seat.get_keyboard().unwrap(); + keyboard.key_stop_repeat(self, |data| &data.handle); for keycode in keyboard.pressed_keys() { keyboard.input( self, keycode, - KeyState::Released, + KeyEvent::Released, SCOUNTER.next_serial(), 0, |_, _, _| FilterResult::Forward::, @@ -576,158 +626,11 @@ impl AnvilState { impl AnvilState { pub fn process_input_event(&mut self, dh: &DisplayHandle, event: InputEvent) { match event { - InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::(event) { - #[cfg(feature = "udev")] - KeyAction::VtSwitch(vt) => { - info!(to = vt, "Trying to switch vt"); - if let Err(err) = self.backend_data.session.change_vt(vt) { - error!(vt, "Error switching vt: {}", err); - } - } - KeyAction::Screen(num) => { - let geometry = self - .space - .outputs() - .nth(num) - .map(|o| self.space.output_geometry(o).unwrap()); - - if let Some(geometry) = geometry { - let x = geometry.loc.x as f64 + geometry.size.w as f64 / 2.0; - let y = geometry.size.h as f64 / 2.0; - let location = (x, y).into(); - let pointer = self.pointer.clone(); - let under = self.surface_under(location); - pointer.motion( - self, - under, - &MotionEvent { - location, - serial: SCOUNTER.next_serial(), - time: self.clock.now().as_millis(), - }, - ); - pointer.frame(self); - } - } - KeyAction::ScaleUp => { - let pos = self.pointer.current_location().to_i32_round(); - let output = self - .space - .outputs() - .find(|o| self.space.output_geometry(o).unwrap().contains(pos)) - .cloned(); - - if let Some(output) = output { - let (output_location, scale) = ( - self.space.output_geometry(&output).unwrap().loc, - output.current_scale().fractional_scale(), - ); - let new_scale = scale + 0.25; - output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); - - let rescale = scale / new_scale; - let output_location = output_location.to_f64(); - let mut pointer_output_location = self.pointer.current_location() - output_location; - pointer_output_location.x *= rescale; - pointer_output_location.y *= rescale; - let pointer_location = output_location + pointer_output_location; - - crate::shell::fixup_positions(&mut self.space, pointer_location); - let pointer = self.pointer.clone(); - let under = self.surface_under(pointer_location); - pointer.motion( - self, - under, - &MotionEvent { - location: pointer_location, - serial: SCOUNTER.next_serial(), - time: self.clock.now().as_millis(), - }, - ); - pointer.frame(self); - self.backend_data.reset_buffers(&output); - } - } - KeyAction::ScaleDown => { - let pos = self.pointer.current_location().to_i32_round(); - let output = self - .space - .outputs() - .find(|o| self.space.output_geometry(o).unwrap().contains(pos)) - .cloned(); - - if let Some(output) = output { - let (output_location, scale) = ( - self.space.output_geometry(&output).unwrap().loc, - output.current_scale().fractional_scale(), - ); - let new_scale = f64::max(1.0, scale - 0.25); - output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); - - let rescale = scale / new_scale; - let output_location = output_location.to_f64(); - let mut pointer_output_location = self.pointer.current_location() - output_location; - pointer_output_location.x *= rescale; - pointer_output_location.y *= rescale; - let pointer_location = output_location + pointer_output_location; - - crate::shell::fixup_positions(&mut self.space, pointer_location); - let pointer = self.pointer.clone(); - let under = self.surface_under(pointer_location); - pointer.motion( - self, - under, - &MotionEvent { - location: pointer_location, - serial: SCOUNTER.next_serial(), - time: self.clock.now().as_millis(), - }, - ); - pointer.frame(self); - self.backend_data.reset_buffers(&output); - } - } - KeyAction::RotateOutput => { - let pos = self.pointer.current_location().to_i32_round(); - let output = self - .space - .outputs() - .find(|o| self.space.output_geometry(o).unwrap().contains(pos)) - .cloned(); - - if let Some(output) = output { - let current_transform = output.current_transform(); - let new_transform = match current_transform { - Transform::Normal => Transform::_90, - Transform::_90 => Transform::_180, - Transform::_180 => Transform::_270, - Transform::_270 => Transform::Flipped, - Transform::Flipped => Transform::Flipped90, - Transform::Flipped90 => Transform::Flipped180, - Transform::Flipped180 => Transform::Flipped270, - Transform::Flipped270 => Transform::Normal, - }; - output.change_current_state(None, Some(new_transform), None, None); - crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); - self.backend_data.reset_buffers(&output); - } - } - KeyAction::ToggleTint => { - let mut debug_flags = self.backend_data.debug_flags(); - debug_flags.toggle(DebugFlags::TINT); - self.backend_data.set_debug_flags(debug_flags); - } - - action => match action { - KeyAction::None - | KeyAction::Quit - | KeyAction::Run(_) - | KeyAction::TogglePreview - | KeyAction::ToggleDecorations => self.process_common_key_action(action), - - _ => unreachable!(), - }, - }, + InputEvent::Keyboard { event, .. } => { + self.on_keyboard_event::(event, move |state: &mut Self, event, time_ms, keycode| { + state.process_key_event(event, time_ms, keycode) + }) + } InputEvent::PointerMotion { event, .. } => self.on_pointer_move::(dh, event), InputEvent::PointerMotionAbsolute { event, .. } => self.on_pointer_move_absolute::(dh, event), InputEvent::PointerButton { event, .. } => self.on_pointer_button::(event), @@ -779,6 +682,166 @@ impl AnvilState { } } + fn process_key_event(&mut self, event: KeyEvent, time_ms: u32, keycode: Keycode) { + let event = KeyboardEvent { + kind: event, + code: keycode, + time_msec: time_ms, + }; + match self.keyboard_event_to_action(event) { + #[cfg(feature = "udev")] + KeyAction::VtSwitch(vt) => { + info!(to = vt, "Trying to switch vt"); + if let Err(err) = self.backend_data.session.change_vt(vt) { + error!(vt, "Error switching vt: {}", err); + } + } + KeyAction::Screen(num) => { + let geometry = self + .space + .outputs() + .nth(num) + .map(|o| self.space.output_geometry(o).unwrap()); + + if let Some(geometry) = geometry { + let x = geometry.loc.x as f64 + geometry.size.w as f64 / 2.0; + let y = geometry.size.h as f64 / 2.0; + let location = (x, y).into(); + let pointer = self.pointer.clone(); + let under = self.surface_under(location); + pointer.motion( + self, + under, + &MotionEvent { + location, + serial: SCOUNTER.next_serial(), + time: self.clock.now().as_millis(), + }, + ); + pointer.frame(self); + } + } + KeyAction::ScaleUp => { + let pos = self.pointer.current_location().to_i32_round(); + let output = self + .space + .outputs() + .find(|o| self.space.output_geometry(o).unwrap().contains(pos)) + .cloned(); + + if let Some(output) = output { + let (output_location, scale) = ( + self.space.output_geometry(&output).unwrap().loc, + output.current_scale().fractional_scale(), + ); + let new_scale = scale + 0.25; + output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); + + let rescale = scale / new_scale; + let output_location = output_location.to_f64(); + let mut pointer_output_location = self.pointer.current_location() - output_location; + pointer_output_location.x *= rescale; + pointer_output_location.y *= rescale; + let pointer_location = output_location + pointer_output_location; + + crate::shell::fixup_positions(&mut self.space, pointer_location); + let pointer = self.pointer.clone(); + let under = self.surface_under(pointer_location); + pointer.motion( + self, + under, + &MotionEvent { + location: pointer_location, + serial: SCOUNTER.next_serial(), + time: self.clock.now().as_millis(), + }, + ); + pointer.frame(self); + self.backend_data.reset_buffers(&output); + } + } + KeyAction::ScaleDown => { + let pos = self.pointer.current_location().to_i32_round(); + let output = self + .space + .outputs() + .find(|o| self.space.output_geometry(o).unwrap().contains(pos)) + .cloned(); + + if let Some(output) = output { + let (output_location, scale) = ( + self.space.output_geometry(&output).unwrap().loc, + output.current_scale().fractional_scale(), + ); + let new_scale = f64::max(1.0, scale - 0.25); + output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None); + + let rescale = scale / new_scale; + let output_location = output_location.to_f64(); + let mut pointer_output_location = self.pointer.current_location() - output_location; + pointer_output_location.x *= rescale; + pointer_output_location.y *= rescale; + let pointer_location = output_location + pointer_output_location; + + crate::shell::fixup_positions(&mut self.space, pointer_location); + let pointer = self.pointer.clone(); + let under = self.surface_under(pointer_location); + pointer.motion( + self, + under, + &MotionEvent { + location: pointer_location, + serial: SCOUNTER.next_serial(), + time: self.clock.now().as_millis(), + }, + ); + pointer.frame(self); + self.backend_data.reset_buffers(&output); + } + } + KeyAction::RotateOutput => { + let pos = self.pointer.current_location().to_i32_round(); + let output = self + .space + .outputs() + .find(|o| self.space.output_geometry(o).unwrap().contains(pos)) + .cloned(); + + if let Some(output) = output { + let current_transform = output.current_transform(); + let new_transform = match current_transform { + Transform::Normal => Transform::_90, + Transform::_90 => Transform::_180, + Transform::_180 => Transform::_270, + Transform::_270 => Transform::Flipped, + Transform::Flipped => Transform::Flipped90, + Transform::Flipped90 => Transform::Flipped180, + Transform::Flipped180 => Transform::Flipped270, + Transform::Flipped270 => Transform::Normal, + }; + output.change_current_state(None, Some(new_transform), None, None); + crate::shell::fixup_positions(&mut self.space, self.pointer.current_location()); + self.backend_data.reset_buffers(&output); + } + } + KeyAction::ToggleTint => { + let mut debug_flags = self.backend_data.debug_flags(); + debug_flags.toggle(DebugFlags::TINT); + self.backend_data.set_debug_flags(debug_flags); + } + + action => match action { + KeyAction::None + | KeyAction::Quit + | KeyAction::Run(_) + | KeyAction::TogglePreview + | KeyAction::ToggleDecorations => self.process_common_key_action(action), + + _ => unreachable!(), + }, + } + } + fn on_pointer_move(&mut self, _dh: &DisplayHandle, evt: B::PointerMotionEvent) { let mut pointer_location = self.pointer.current_location(); let serial = SCOUNTER.next_serial(); diff --git a/examples/minimal.rs b/examples/minimal.rs index e9c6a07c016e..18f4e0892d5a 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -180,7 +180,7 @@ pub fn run_winit() -> Result<(), Box> { keyboard.input::<(), _>( &mut state, event.key_code(), - event.state(), + event.state().into(), 0.into(), 0, |_, _, _| { diff --git a/examples/seat.rs b/examples/seat.rs index 9bc89a68e827..04db3c152fa5 100644 --- a/examples/seat.rs +++ b/examples/seat.rs @@ -62,7 +62,7 @@ fn main() -> Result<(), Box> { keyboard.input( &mut state, smithay::backend::input::Keycode::from(9u32), - smithay::backend::input::KeyState::Pressed, + smithay::backend::input::KeyEvent::Pressed, 0.into(), 0, |_, _, _| { diff --git a/smallvil/src/input.rs b/smallvil/src/input.rs index 3f38b26943a7..85ed51433bc6 100644 --- a/smallvil/src/input.rs +++ b/smallvil/src/input.rs @@ -23,7 +23,7 @@ impl Smallvil { self.seat.get_keyboard().unwrap().input::<(), _>( self, event.key_code(), - event.state(), + event.state().into(), serial, time, |_, _, _| FilterResult::Forward, diff --git a/src/backend/input/mod.rs b/src/backend/input/mod.rs index 17c60ac6f676..0a26206f0761 100644 --- a/src/backend/input/mod.rs +++ b/src/backend/input/mod.rs @@ -96,6 +96,26 @@ pub enum KeyState { Pressed, } +/// Keyboard key event. Modelled on the wl_keyboard KeyState. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum KeyEvent { + /// Key was released + Released, + /// Key was pressed + Pressed, + /// Key is being held and repetition was triggered. + Repeated, +} + +impl From for KeyEvent { + fn from(k: KeyState) -> Self { + match k { + KeyState::Released => Self::Released, + KeyState::Pressed => Self::Pressed, + } + } +} + /// Trait for keyboard event pub trait KeyboardKeyEvent: Event { /// Returns the numerical button code of the keyboard button. diff --git a/src/desktop/wayland/popup/grab.rs b/src/desktop/wayland/popup/grab.rs index 36cfd73451bf..a2e0a754e7ad 100644 --- a/src/desktop/wayland/popup/grab.rs +++ b/src/desktop/wayland/popup/grab.rs @@ -6,7 +6,7 @@ use std::{ use wayland_server::{protocol::wl_surface::WlSurface, Resource}; use crate::{ - backend::input::{ButtonState, KeyState, Keycode}, + backend::input::{ButtonState, KeyEvent, Keycode}, input::{ keyboard::{ GrabStartData as KeyboardGrabStartData, KeyboardGrab, KeyboardHandle, KeyboardInnerHandle, @@ -434,7 +434,7 @@ where data: &mut D, handle: &mut KeyboardInnerHandle<'_, D>, keycode: Keycode, - state: KeyState, + state: KeyEvent, modifiers: Option, serial: Serial, time: u32, diff --git a/src/input/keyboard/mod.rs b/src/input/keyboard/mod.rs index 2caddc1eb17b..62b959706b8c 100644 --- a/src/input/keyboard/mod.rs +++ b/src/input/keyboard/mod.rs @@ -1,18 +1,21 @@ //! Keyboard-related types for smithay's input abstraction -use crate::backend::input::KeyState; +use crate::backend::input::{Event, InputBackend, KeyEvent, KeyState, KeyboardKeyEvent}; +use crate::reexports::calloop::LoopHandle; use crate::utils::{IsAlive, Serial, SERIAL_COUNTER}; +use calloop::RegistrationToken; use downcast_rs::{impl_downcast, Downcast}; use std::collections::HashSet; #[cfg(feature = "wayland_frontend")] use std::sync::RwLock; +use std::time::Duration; use std::{ default::Default, fmt, io, sync::{Arc, Mutex}, }; use thiserror::Error; -use tracing::{debug, error, info, info_span, instrument, trace}; +use tracing::{debug, error, info, info_span, instrument, trace, warn}; use xkbcommon::xkb::ffi::XKB_STATE_LAYOUT_EFFECTIVE; pub use xkbcommon::xkb::{self, keysyms, Keycode, Keysym}; @@ -47,7 +50,7 @@ where seat: &Seat, data: &mut D, key: KeysymHandle<'_>, - state: KeyState, + state: KeyEvent, serial: Serial, time: u32, ); @@ -215,6 +218,19 @@ pub(crate) struct KbdInternal { led_mapping: LedMapping, pub(crate) led_state: LedState, grab: GrabStatus>, + /// Holds the token to cancel key repeat. + /// The token gets cleared when the keyboard is dropped, to neutralize the repeat callback. + pub(crate) key_repeat_timer: Arc>>, +} + +#[cfg(feature = "wayland_frontend")] +impl Drop for KbdInternal { + fn drop(&mut self) { + let timer = self.key_repeat_timer.lock().unwrap().take(); + if timer.is_some() { + error!("A keyboard was dropped without unregistering a repeat handler. This is a bug in smithay or in the compositor."); + } + } } // focus_hook does not implement debug, so we have to impl Debug manually @@ -229,6 +245,7 @@ impl fmt::Debug for KbdInternal { .field("xkb", &self.xkb) .field("repeat_rate", &self.repeat_rate) .field("repeat_delay", &self.repeat_delay) + .field("key_repeat_timer", &self.key_repeat_timer) .finish() } } @@ -266,21 +283,25 @@ impl KbdInternal { led_mapping, led_state, grab: GrabStatus::None, + key_repeat_timer: Arc::new(Mutex::new(None)), }) } // returns whether the modifiers or led state has changed - fn key_input(&mut self, keycode: Keycode, state: KeyState) -> (bool, bool) { + fn key_input(&mut self, keycode: Keycode, state: KeyEvent) -> (bool, bool) { // track pressed keys as xkbcommon does not seem to expose it :( let direction = match state { - KeyState::Pressed => { + KeyEvent::Pressed => { self.pressed_keys.insert(keycode); xkb::KeyDirection::Down } - KeyState::Released => { + KeyEvent::Released => { self.pressed_keys.remove(&keycode); xkb::KeyDirection::Up } + KeyEvent::Repeated => { + return (false, false); + } }; // update state @@ -602,10 +623,10 @@ pub trait KeyboardGrab: Downcast { data: &mut D, handle: &mut KeyboardInnerHandle<'_, D>, keycode: Keycode, - state: KeyState, + event: KeyEvent, modifiers: Option, serial: Serial, - time: u32, + time_ms: u32, ); /// A focus change was requested. @@ -931,6 +952,106 @@ impl KeyboardHandle { } } + /// Processes the keyboard event, starting or stopping key repeat. + /// If this method is used, it must receive all keyboard events. + /// The `on_event` argument will be called with all events: the ones received directly and the generated repeats. + pub fn key_register_repeat( + &self, + data: &mut D, + get_handle: impl Fn(&D) -> &LoopHandle<'static, D> + 'static, + event: B::KeyboardKeyEvent, + // event, timeout, code. + // This is Clone because there are two closures in here... + on_event: impl Fn(&mut D, KeyEvent, u32, Keycode) + Clone + 'static, + ) { + let time_ms = event.time_msec(); + let keycode = event.key_code(); + let state = event.state(); + + // Forward initial hardware event as logical event + on_event(data, state.into(), time_ms, keycode); + + // Unregister preexisting repeating + // Releasing a key press obviously stops the repeat. + // But also, pressing another key stops the repeat of the previous key and starts it for the newly pressed key. + // TODO: this may had odd consequences when a modifier is pressed as the second key. But is that worth worrying about? + self.key_stop_repeat(data, &get_handle); + + // Register repeating + match event.state() { + KeyState::Pressed => { + let mut guard = self.arc.internal.lock().unwrap(); + let delay = guard.repeat_delay; + let rate = guard.repeat_rate; + let mut time_ms = time_ms; + + // This closure-in-closure business is somewhat ugly. + // The reason is that there are two timers needed: first, the delay timer, and after the delay, the repeat timer. Both of them receive different tokens for cancelling, so we have to swap the token after the delay. + // The only comparable alternative I can think of is to wrap the key_repeat_timer in an Mutex> and change the token when delay turns into repeat. But locks are worse than nesting. + let kbd = self.arc.clone(); + let duration = Duration::from_millis(delay as _); + let handle = get_handle(data); + let token = handle.insert_source( + calloop::timer::Timer::from_duration(duration), + move |_, _, data| { + time_ms += delay as u32; + on_event(data, KeyEvent::Repeated, time_ms, keycode); + let mut guard = kbd.internal.lock().unwrap(); + + let handle = get_handle(data); + { + let timer = guard.key_repeat_timer.lock().unwrap(); + match *timer { + Some(token) => handle.remove(token), + None => debug!("Key starts repeating but there is no delay timer. Was repeat already cancelled?"), + }; + } + + // This implementation doesn't take into account changes to the repeat rate after repeating begins. + let kbd = kbd.clone(); + let on_event = on_event.clone(); + let duration = Duration::from_millis(rate as _); + let token = handle.insert_source( + calloop::timer::Timer::from_duration(duration), + move |_, _, data| { + time_ms += rate as u32; + let guard = kbd.internal.lock().unwrap(); + let timer = guard.key_repeat_timer.lock().unwrap(); + + // If the timer has been orphaned by dropping the keyboard, don't actually send the event, don't register a repeat. + if timer.is_some() { + drop(timer); + drop(guard); + on_event(data, KeyEvent::Repeated, time_ms, keycode); + calloop::timer::TimeoutAction::ToDuration(duration) + } else { + debug!("Cancelling an orphaned keyboard repeat."); + calloop::timer::TimeoutAction::Drop + } + }, + ).unwrap(); + guard.key_repeat_timer = Arc::new(Mutex::new(Some(token))); + calloop::timer::TimeoutAction::Drop + } + ).unwrap(); + guard.key_repeat_timer = Arc::new(Mutex::new(Some(token))); + } + KeyState::Released => { + // Nothing to do; timer is released for both in the common path. + } + } + } + + /// Cancels any ongoing key repeat + pub fn key_stop_repeat(&self, data: &mut D, get_handle: impl Fn(&D) -> &LoopHandle<'static, D>) { + let guard = self.arc.internal.lock().unwrap(); + let mut timer = guard.key_repeat_timer.lock().unwrap(); + if let Some(token) = timer.take() { + let handle = get_handle(data); + handle.remove(token); + }; + } + /// Handle a keystroke /// /// All keystrokes from the input backend should be fed _in order_ to this method of the @@ -949,7 +1070,7 @@ impl KeyboardHandle { &self, data: &mut D, keycode: Keycode, - state: KeyState, + state: KeyEvent, serial: Serial, time: u32, filter: F, @@ -979,13 +1100,13 @@ impl KeyboardHandle { &self, data: &mut D, keycode: Keycode, - state: KeyState, + state: KeyEvent, filter: F, ) -> (T, bool) where F: FnOnce(&mut D, &ModifiersState, KeysymHandle<'_>) -> T, { - trace!("Handling keystroke"); + trace!("Handling key event"); let mut guard = self.arc.internal.lock().unwrap(); let (mods_changed, leds_changed) = guard.key_input(keycode, state); @@ -1014,26 +1135,18 @@ impl KeyboardHandle { &self, data: &mut D, keycode: Keycode, - state: KeyState, + event: KeyEvent, serial: Serial, - time: u32, + time_ms: u32, mods_changed: bool, ) { let mut guard = self.arc.internal.lock().unwrap(); - match state { - KeyState::Pressed => { - guard.forwarded_pressed_keys.insert(keycode); - } - KeyState::Released => { - guard.forwarded_pressed_keys.remove(&keycode); - } - }; // forward to client if no keybinding is triggered let seat = self.get_seat(data); let modifiers = mods_changed.then_some(guard.mods_state); guard.with_grab(data, &seat, |data, handle, grab| { - grab.input(data, handle, keycode, state, modifiers, serial, time); + grab.input(data, handle, keycode, event, modifiers, serial, time_ms); }); if guard.focus.is_some() { trace!("Input forwarded to client"); @@ -1151,6 +1264,11 @@ impl KeyboardHandle { continue; }; if kbd.version() >= 4 { + let rate = if kbd.version() >= 10 { + 0 // Enables compositor-side key repeat. See wl_keyboard key event + } else { + rate + }; kbd.repeat_info(rate, delay); } } @@ -1266,7 +1384,7 @@ impl KeyboardInnerHandle<'_, D> { &mut self, data: &mut D, keycode: Keycode, - key_state: KeyState, + key_state: KeyEvent, modifiers: Option, serial: Serial, time: u32, @@ -1377,7 +1495,7 @@ impl KeyboardGrab for DefaultGrab { data: &mut D, handle: &mut KeyboardInnerHandle<'_, D>, keycode: Keycode, - state: KeyState, + state: KeyEvent, modifiers: Option, serial: Serial, time: u32, diff --git a/src/input/mod.rs b/src/input/mod.rs index 7d4896cc11ff..7e0b9b0c0f2b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -17,7 +17,7 @@ //! //! ``` //! use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; -//! # use smithay::backend::input::KeyState; +//! # use smithay::backend::input::KeyEvent; //! # use smithay::input::{ //! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent, //! # GestureSwipeBeginEvent, GestureSwipeUpdateEvent, GestureSwipeEndEvent, @@ -70,7 +70,7 @@ //! # seat: &Seat, //! # data: &mut State, //! # key: KeysymHandle<'_>, -//! # state: KeyState, +//! # state: KeyEvent, //! # serial: Serial, //! # time: u32, //! # ) {} @@ -362,7 +362,7 @@ impl Seat { /// /// ```no_run /// # use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; - /// # use smithay::backend::input::KeyState; + /// # use smithay::backend::input::KeyEvent; /// # use smithay::input::{ /// # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent, /// # GestureSwipeBeginEvent, GestureSwipeUpdateEvent, GestureSwipeEndEvent, @@ -403,7 +403,7 @@ impl Seat { /// # seat: &Seat, /// # data: &mut State, /// # key: KeysymHandle<'_>, - /// # state: KeyState, + /// # state: KeyEvent, /// # serial: Serial, /// # time: u32, /// # ) {} @@ -483,7 +483,7 @@ impl Seat { /// /// ```no_run /// # use smithay::input::{Seat, SeatState, SeatHandler, keyboard::XkbConfig, pointer::CursorImageStatus}; - /// # use smithay::backend::input::KeyState; + /// # use smithay::backend::input::KeyEvent; /// # use smithay::input::{ /// # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent, /// # GestureSwipeBeginEvent, GestureSwipeUpdateEvent, GestureSwipeEndEvent, @@ -524,7 +524,7 @@ impl Seat { /// # seat: &Seat, /// # data: &mut State, /// # key: KeysymHandle<'_>, - /// # state: KeyState, + /// # state: KeyEvent, /// # serial: Serial, /// # time: u32, /// # ) {} diff --git a/src/wayland/cursor_shape.rs b/src/wayland/cursor_shape.rs index df1f3f739cc7..d10e5aaffd66 100644 --- a/src/wayland/cursor_shape.rs +++ b/src/wayland/cursor_shape.rs @@ -12,7 +12,7 @@ //! use smithay::wayland::cursor_shape::CursorShapeManagerState; //! use smithay::delegate_cursor_shape; //! -//! # use smithay::backend::input::KeyState; +//! # use smithay::backend::input::KeyEvent; //! # use smithay::input::{ //! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent, //! # GestureSwipeBeginEvent, GestureSwipeUpdateEvent, GestureSwipeEndEvent, @@ -63,7 +63,7 @@ //! # seat: &Seat, //! # data: &mut State, //! # key: KeysymHandle<'_>, -//! # state: KeyState, +//! # state: KeyEvent, //! # serial: Serial, //! # time: u32, //! # ) {} diff --git a/src/wayland/input_method/input_method_keyboard_grab.rs b/src/wayland/input_method/input_method_keyboard_grab.rs index 24ac2a727296..02f93e869a9b 100644 --- a/src/wayland/input_method/input_method_keyboard_grab.rs +++ b/src/wayland/input_method/input_method_keyboard_grab.rs @@ -18,7 +18,7 @@ use crate::input::{ }; use crate::wayland::text_input::TextInputHandle; use crate::{ - backend::input::{KeyState, Keycode}, + backend::input::{KeyEvent, Keycode}, utils::Serial, }; @@ -45,7 +45,7 @@ where _data: &mut D, _handle: &mut KeyboardInnerHandle<'_, D>, keycode: Keycode, - key_state: KeyState, + key_event: KeyEvent, modifiers: Option, serial: Serial, time: u32, @@ -55,7 +55,7 @@ where inner .text_input_handle .active_text_input_serial_or_default(serial.0, |serial| { - keyboard.key(serial, time, keycode.raw() - 8, key_state.into()); + keyboard.key(serial, time, keycode.raw() - 8, key_event.into()); if let Some(serialized) = modifiers.map(|m| m.serialized) { keyboard.modifiers( serial, diff --git a/src/wayland/pointer_gestures.rs b/src/wayland/pointer_gestures.rs index 320ceccc3470..fd93a71b5915 100644 --- a/src/wayland/pointer_gestures.rs +++ b/src/wayland/pointer_gestures.rs @@ -21,7 +21,7 @@ //! //! use smithay::wayland::pointer_gestures::PointerGesturesState; //! use smithay::delegate_pointer_gestures; -//! # use smithay::backend::input::KeyState; +//! # use smithay::backend::input::KeyEvent; //! # use smithay::input::{ //! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent, //! # GestureSwipeBeginEvent, GestureSwipeUpdateEvent, GestureSwipeEndEvent, @@ -63,7 +63,7 @@ //! # seat: &Seat, //! # data: &mut State, //! # key: KeysymHandle<'_>, -//! # state: KeyState, +//! # state: KeyEvent, //! # serial: Serial, //! # time: u32, //! # ) {} diff --git a/src/wayland/relative_pointer.rs b/src/wayland/relative_pointer.rs index 169af13b62ac..df872efefc70 100644 --- a/src/wayland/relative_pointer.rs +++ b/src/wayland/relative_pointer.rs @@ -9,7 +9,7 @@ //! //! use smithay::wayland::relative_pointer::RelativePointerManagerState; //! use smithay::delegate_relative_pointer; -//! # use smithay::backend::input::KeyState; +//! # use smithay::backend::input::KeyEvent; //! # use smithay::input::{ //! # pointer::{PointerTarget, AxisFrame, MotionEvent, ButtonEvent, RelativeMotionEvent, //! # GestureSwipeBeginEvent, GestureSwipeUpdateEvent, GestureSwipeEndEvent, @@ -51,7 +51,7 @@ //! # seat: &Seat, //! # data: &mut State, //! # key: KeysymHandle<'_>, -//! # state: KeyState, +//! # state: KeyEvent, //! # serial: Serial, //! # time: u32, //! # ) {} diff --git a/src/wayland/seat/keyboard.rs b/src/wayland/seat/keyboard.rs index 71ade8650c89..8dc783bdb8ec 100644 --- a/src/wayland/seat/keyboard.rs +++ b/src/wayland/seat/keyboard.rs @@ -12,7 +12,7 @@ use wayland_server::{ use super::WaylandFocus; use crate::{ - backend::input::{KeyState, Keycode}, + backend::input::{KeyEvent, Keycode}, input::{ keyboard::{KeyboardHandle, KeyboardTarget, KeysymHandle, ModifiersState}, Seat, SeatHandler, SeatState, @@ -68,7 +68,12 @@ where let guard = self.arc.internal.lock().unwrap(); if kbd.version() >= 4 { - kbd.repeat_info(guard.repeat_rate, guard.repeat_delay); + let rate = if kbd.version() >= 10 { + 0 // Enables compositor-side key repeat. See wl_keyboard key event + } else { + guard.repeat_rate + }; + kbd.repeat_info(rate, guard.repeat_delay); } if let Some((focused, serial)) = guard.focus.as_ref() { if focused.same_client_as(&kbd.id()) { @@ -211,12 +216,12 @@ impl KeyboardTarget for WlSurface { seat: &Seat, _data: &mut D, key: KeysymHandle<'_>, - state: KeyState, + event: KeyEvent, serial: Serial, time: u32, ) { for_each_focused_kbds(seat, self, |kbd| { - kbd.key(serial.into(), time, key.raw_code().raw() - 8, state.into()) + kbd.key(serial.into(), time, key.raw_code().raw() - 8, event.into()) }) } @@ -234,12 +239,13 @@ impl KeyboardTarget for WlSurface { } } -impl From for WlKeyState { +impl From for WlKeyState { #[inline] - fn from(state: KeyState) -> WlKeyState { + fn from(state: KeyEvent) -> WlKeyState { match state { - KeyState::Pressed => WlKeyState::Pressed, - KeyState::Released => WlKeyState::Released, + KeyEvent::Pressed => WlKeyState::Pressed, + KeyEvent::Released => WlKeyState::Released, + KeyEvent::Repeated => WlKeyState::Repeated, } } } @@ -248,13 +254,14 @@ impl From for WlKeyState { #[error("Unknown KeyState {0:?}")] pub struct UnknownKeyState(WlKeyState); -impl TryFrom for KeyState { +impl TryFrom for KeyEvent { type Error = UnknownKeyState; #[inline] fn try_from(state: WlKeyState) -> Result { match state { - WlKeyState::Pressed => Ok(KeyState::Pressed), - WlKeyState::Released => Ok(KeyState::Released), + WlKeyState::Pressed => Ok(KeyEvent::Pressed), + WlKeyState::Released => Ok(KeyEvent::Released), + WlKeyState::Repeated => Ok(KeyEvent::Repeated), x => Err(UnknownKeyState(x)), } } diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 0e33ec0e5df6..fda2547e69c1 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -173,7 +173,7 @@ impl SeatState { { let Seat { arc } = self.new_seat(name); - let global_id = display.create_global::(9, SeatGlobalData { arc: arc.clone() }); + let global_id = display.create_global::(10, SeatGlobalData { arc: arc.clone() }); arc.inner.lock().unwrap().global = Some(global_id); Seat { arc } diff --git a/src/wayland/xwayland_keyboard_grab.rs b/src/wayland/xwayland_keyboard_grab.rs index 8a9232ab87eb..f1071f504a62 100644 --- a/src/wayland/xwayland_keyboard_grab.rs +++ b/src/wayland/xwayland_keyboard_grab.rs @@ -47,7 +47,7 @@ use wayland_server::{ }; use crate::{ - backend::input::{KeyState, Keycode}, + backend::input::{KeyEvent, Keycode}, input::{ keyboard::{self, KeyboardGrab, KeyboardInnerHandle}, Seat, SeatHandler, @@ -109,7 +109,7 @@ impl KeyboardGrab for XWaylandKeybo data: &mut D, handle: &mut KeyboardInnerHandle<'_, D>, keycode: Keycode, - state: KeyState, + state: KeyEvent, modifiers: Option, serial: Serial, time: u32, diff --git a/src/xwayland/xwm/surface.rs b/src/xwayland/xwm/surface.rs index 564034f0ed47..7cb6ab9c7d2f 100644 --- a/src/xwayland/xwm/surface.rs +++ b/src/xwayland/xwm/surface.rs @@ -1,5 +1,5 @@ use crate::{ - backend::input::KeyState, + backend::input::KeyEvent, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ @@ -1081,7 +1081,7 @@ impl KeyboardTarget for X11Surface { seat: &Seat, data: &mut D, key: KeysymHandle<'_>, - state: KeyState, + state: KeyEvent, serial: Serial, time: u32, ) {