diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index 7281a50..ca7a82e 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -22,14 +22,14 @@ use xkbcommon::xkb; pub mod keysyms; mod state; +mod repeat; use self::state::KbState; pub use self::state::ModifiersState; +pub use self::repeat::{KeyRepeatSource, RepeatMessage}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct KeyEvent { - pub serial: u32, - pub time: u32, pub rawkey: u32, pub keysym: u32, pub state: WEnum, @@ -78,8 +78,6 @@ impl Keyboard { pub fn key( &mut self, - serial: u32, - time: u32, key: u32, key_state: WEnum, ) -> KeyEvent { @@ -120,8 +118,6 @@ impl Keyboard { keysym: sym, state: key_state, modifiers: state.mods_state(), - serial, - time, utf8, repeats, } @@ -133,8 +129,6 @@ impl Keyboard { modifiers: Default::default(), utf8: None, repeats: false, - serial, - time, } } } diff --git a/src/keyboard/repeat.rs b/src/keyboard/repeat.rs new file mode 100644 index 0000000..a08a1c5 --- /dev/null +++ b/src/keyboard/repeat.rs @@ -0,0 +1,169 @@ +use std::time::Duration; + +use calloop::{ + channel::{self, Channel}, + timer::{TimeoutAction, Timer}, + EventSource, Poll, PostAction, Readiness, Token, TokenFactory, +}; +use wayland_client::{ + protocol::wl_keyboard, WEnum, +}; + +use super::KeyEvent; + +#[derive(Debug)] +pub enum RepeatMessage { + StopRepeat, + KeyEvent(KeyEvent), + RepeatInfo((u32, u32)) +} + +#[derive(Debug)] +pub struct KeyRepeatSource { + channel: Channel, + timer: Timer, + rate: Duration, + delay: Duration, + disabled: bool, + key: Option, +} + +impl KeyRepeatSource { + pub fn new(ch: channel::Channel) -> KeyRepeatSource { + KeyRepeatSource{ + channel: ch, + timer: Timer::immediate(), + delay: Duration::ZERO, + rate: Duration::ZERO, + disabled: true, + key: None, + } + } +} + +impl EventSource for KeyRepeatSource { + type Event = KeyEvent; + type Metadata = (); + type Ret = (); + type Error = calloop::Error; + + fn process_events( + &mut self, + readiness: Readiness, + token: Token, + mut callback: F, + ) -> calloop::Result + where + F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, + { + let mut removed = false; + + let timer = &mut self.timer; + let rate = &mut self.rate; + let delay = &mut self.delay; + let key = &mut self.key; + let disabled = &mut self.disabled; + + let mut reregister = false; + + let mut next_deadline = std::time::Instant::now(); + + // Check if the key repeat should stop + let channel_pa = self + .channel + .process_events(readiness, token, |event, _| { + match event { + channel::Event::Msg(message) => { + match message { + RepeatMessage::StopRepeat => { + key.take(); + } + RepeatMessage::KeyEvent(event) => match event.state { + WEnum::Value(wl_keyboard::KeyState::Pressed) => { + key.replace(event); + reregister = true; + + next_deadline = std::time::Instant::now() + *delay; + timer.set_deadline(next_deadline); + } + WEnum::Value(wl_keyboard::KeyState::Released) => match key { + Some(k) if k.keysym == event.keysym => { + key.take(); + } + _ => (), + }, + _ => (), + } + RepeatMessage::RepeatInfo((new_rate, new_delay)) => { + *rate = Duration::from_micros(1_000_000 / new_rate as u64); + *delay = Duration::from_millis(new_delay as u64); + *disabled = false; + if key.is_some() { + next_deadline = std::time::Instant::now() + *delay; + timer.set_deadline(next_deadline); + } + } + } + } + + channel::Event::Closed => { + removed = true; + } + } + }) + .map_err(|err| calloop::Error::OtherError(Box::new(err)))?; + + // Keyboard was destroyed + if removed { + return Ok(PostAction::Remove); + } + + // Re-register the timer to start it again + if reregister { + return Ok(PostAction::Reregister); + } + + let timer_pa = timer.process_events(readiness, token, |_event, _| { + if self.disabled || key.is_none() { + return TimeoutAction::Drop; + } + + // Invoke the event + callback(key.as_ref().unwrap().clone(), &mut ()); + next_deadline += *rate; + TimeoutAction::ToInstant(next_deadline) + })?; + + // Only disable or remove if both want to, otherwise continue or re-register + Ok(match (timer_pa, channel_pa) { + (PostAction::Disable, PostAction::Disable) => PostAction::Disable, + (PostAction::Remove, PostAction::Remove) => PostAction::Remove, + (PostAction::Reregister, _) | (_, PostAction::Reregister) => PostAction::Reregister, + _ => PostAction::Continue, + }) + } + + fn register( + &mut self, + poll: &mut Poll, + token_factory: &mut TokenFactory, + ) -> calloop::Result<()> { + self.channel.register(poll, token_factory)?; + self.timer.register(poll, token_factory) + } + + fn reregister( + &mut self, + poll: &mut Poll, + token_factory: &mut TokenFactory, + ) -> calloop::Result<()> { + self.channel.reregister(poll, token_factory)?; + self.timer.reregister(poll, token_factory) + } + + fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> { + self.channel.unregister(poll)?; + self.timer.unregister(poll) + } +} + diff --git a/src/main.rs b/src/main.rs index ce57e6f..7aed68c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ use event::{Event, Events}; use fonts::{FontMap, MaybeFontMap}; use state::State; use widgets::{Geometry, Widget}; +use keyboard::{KeyRepeatSource, RepeatMessage}; use std::{env, rc::Rc, thread}; @@ -64,6 +65,7 @@ fn main() { display.get_registry(&qhandle, ()); let (ping_sender, ping_source) = calloop::ping::make_ping().unwrap(); + let (keyrepeat_sender, keyrepeat_channel) = calloop::channel::channel::(); let mut event_loop: EventLoop = EventLoop::try_new().expect("Failed to initialize the event loop!"); @@ -109,6 +111,17 @@ fn main() { }) .expect("Failed to insert event source!"); + let keyrepeat_source = KeyRepeatSource::new(keyrepeat_channel); + handle + .insert_source(keyrepeat_source, |event, _metadata, state| { + let ev = Event::KeyEvent(event); + for widget in state.widgets.iter_mut() { + widget.event(&ev); + } + state.dirty = true; + }) + .expect("Failed to insert keyrepeat source!"); + let events = Events::new(ping_sender); let mut fm = FontMap::new(); @@ -143,6 +156,7 @@ fn main() { layout, MaybeFontMap::Waiting(font_thread), events, + keyrepeat_sender, ); let mut damage = Vec::new(); diff --git a/src/state.rs b/src/state.rs index 487f7a8..8c61827 100644 --- a/src/state.rs +++ b/src/state.rs @@ -33,10 +33,12 @@ use crate::{ buffer::BufferManager, event::{Event, Events, PointerButton, PointerEvent}, fonts::{FontMap, MaybeFontMap}, - keyboard::Keyboard, + keyboard::{Keyboard, RepeatMessage}, widgets::{Geometry, Layout, Widget, WidgetUpdater}, }; +use calloop::channel::Sender; + #[derive(Default)] pub struct MainSurface { pub wl_surface: Option, @@ -71,6 +73,7 @@ pub struct State { pub bufmgr: BufferManager, pub widgets: Vec>, pub keyboard: Keyboard, + pub keyrepeat_sender: Sender, pointer: Option<(f64, f64)>, pub fonts: MaybeFontMap, pub events: Arc>, @@ -84,6 +87,7 @@ impl State { layout: Rc>, fonts: MaybeFontMap, events: Arc>, + keyrepeat_sender: Sender, ) -> State { State { protocols: Default::default(), @@ -97,6 +101,7 @@ impl State { bufmgr: BufferManager::new(), keyboard: Keyboard::new(), pointer: None, + keyrepeat_sender, mode, widgets, fonts, @@ -555,21 +560,29 @@ impl Dispatch for State { _: &QueueHandle, ) { match event { + wl_keyboard::Event::Leave { .. }=> { + state.keyrepeat_sender.send(RepeatMessage::StopRepeat).unwrap(); + } wl_keyboard::Event::Key { - serial, - time, key, state: kbstate, + .. } => { if key == 1 { state.running = false; return; } - let k = state.keyboard.key(serial, time, key, kbstate); + let k = state.keyboard.key(key, kbstate); + let repeats = k.repeats; let ev = Event::KeyEvent(k); for widget in state.widgets.iter_mut() { widget.event(&ev); } + if repeats { + if let Event::KeyEvent(k) = ev { + state.keyrepeat_sender.send(RepeatMessage::KeyEvent(k)).unwrap(); + } + } state.dirty = true; } wl_keyboard::Event::Modifiers { @@ -587,6 +600,9 @@ impl Dispatch for State { // TODO: Keymap loading blocks xdg configure, consider delaying state.keyboard.keymap(format, fd, size); } + wl_keyboard::Event::RepeatInfo { rate, delay } => { + state.keyrepeat_sender.send(RepeatMessage::RepeatInfo((rate as u32, delay as u32))).unwrap(); + } _ => (), } }