diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 2e341dabac..f415024c32 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -1,7 +1,10 @@ +use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; +use std::time::Duration; use parking_lot::Mutex; +use smol::Timer; use wayland_backend::client::ObjectId; use wayland_backend::protocol::WEnum; use wayland_client::protocol::wl_callback::WlCallback; @@ -38,7 +41,7 @@ use crate::{ const MIN_KEYCODE: u32 = 8; // used to convert evdev scancode to xkb scancode -pub(crate) struct WaylandClientState { +pub(crate) struct WaylandClientStateInner { compositor: Option, buffer: Option, wm_base: Option, @@ -49,6 +52,7 @@ pub(crate) struct WaylandClientState { platform_inner: Rc, wl_seat: Option, keymap_state: Option, + repeat: KeyRepeat, modifiers: Modifiers, scroll_direction: f64, mouse_location: Option>, @@ -57,17 +61,27 @@ pub(crate) struct WaylandClientState { keyboard_focused_window: Option>, } +#[derive(Clone)] +pub(crate) struct WaylandClientState(Rc>); + +pub(crate) struct KeyRepeat { + rate: i32, + delay: i32, + current_id: u64, + current_keysym: Option, +} + pub(crate) struct WaylandClient { platform_inner: Rc, conn: Arc, - state: Mutex, + state: WaylandClientState, event_queue: Mutex>, qh: Arc>, } impl WaylandClient { pub(crate) fn new(linux_platform_inner: Rc, conn: Arc) -> Self { - let state = WaylandClientState { + let state = WaylandClientState(Rc::new(RefCell::new(WaylandClientStateInner { compositor: None, buffer: None, wm_base: None, @@ -78,6 +92,12 @@ impl WaylandClient { platform_inner: Rc::clone(&linux_platform_inner), wl_seat: None, keymap_state: None, + repeat: KeyRepeat { + rate: 16, + delay: 500, + current_id: 0, + current_keysym: None, + }, modifiers: Modifiers { shift: false, control: false, @@ -90,13 +110,13 @@ impl WaylandClient { button_pressed: None, mouse_focused_window: None, keyboard_focused_window: None, - }; + }))); let event_queue: EventQueue = conn.new_event_queue(); let qh = event_queue.handle(); Self { platform_inner: linux_platform_inner, conn, - state: Mutex::new(state), + state, event_queue: Mutex::new(event_queue), qh: Arc::new(qh), } @@ -109,15 +129,15 @@ impl Client for WaylandClient { let mut eq = self.event_queue.lock(); let _registry = display.get_registry(&self.qh, ()); - eq.roundtrip(&mut self.state.lock()).unwrap(); + eq.roundtrip(&mut self.state.clone()).unwrap(); on_finish_launching(); while !self.platform_inner.state.lock().quit_requested { eq.flush().unwrap(); - eq.dispatch_pending(&mut self.state.lock()).unwrap(); + eq.dispatch_pending(&mut self.state.clone()).unwrap(); if let Some(guard) = self.conn.prepare_read() { guard.read().unwrap(); - eq.dispatch_pending(&mut self.state.lock()).unwrap(); + eq.dispatch_pending(&mut self.state.clone()).unwrap(); } if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() { runnable.run(); @@ -138,7 +158,7 @@ impl Client for WaylandClient { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let mut state = self.state.lock(); + let mut state = self.state.0.borrow_mut(); let wm_base = state.wm_base.as_ref().unwrap(); let compositor = state.compositor.as_ref().unwrap(); @@ -206,6 +226,7 @@ impl Dispatch for WaylandClientState { _: &Connection, qh: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); if let wl_registry::Event::Global { name, interface, .. } = event @@ -275,6 +296,7 @@ impl Dispatch> for WaylandClientState { _: &Connection, qh: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); if let wl_callback::Event::Done { .. } = event { for window in &state.windows { if window.1.surface.id() == surf.id() { @@ -296,6 +318,7 @@ impl Dispatch for WaylandClientState { _: &Connection, _: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); if let xdg_surface::Event::Configure { serial, .. } = event { xdg_surface.ack_configure(serial); for window in &state.windows { @@ -318,6 +341,7 @@ impl Dispatch for WaylandClientState { _: &Connection, _: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); if let xdg_toplevel::Event::Configure { width, height, @@ -388,14 +412,19 @@ impl Dispatch for WaylandClientState { impl Dispatch for WaylandClientState { fn event( - state: &mut Self, + state_container: &mut Self, keyboard: &wl_keyboard::WlKeyboard, event: wl_keyboard::Event, data: &(), conn: &Connection, qh: &QueueHandle, ) { + let mut state = state_container.0.borrow_mut(); match event { + wl_keyboard::Event::RepeatInfo { rate, delay } => { + state.repeat.rate = rate; + state.repeat.delay = delay; + } wl_keyboard::Event::Keymap { format: WEnum::Value(format), fd, @@ -453,47 +482,93 @@ impl Dispatch for WaylandClientState { } => { let keymap_state = state.keymap_state.as_mut().unwrap(); keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group); - state.modifiers.shift = + + let shift = keymap_state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE); - state.modifiers.alt = + let alt = keymap_state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE); - state.modifiers.control = + let control = keymap_state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE); - state.modifiers.command = + let command = keymap_state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE); + + state.modifiers.shift = shift; + state.modifiers.alt = alt; + state.modifiers.control = control; + state.modifiers.command = command; } wl_keyboard::Event::Key { key, state: WEnum::Value(key_state), .. } => { + let focused_window = &state.keyboard_focused_window; + let Some(focused_window) = focused_window else { + return; + }; + let keymap_state = state.keymap_state.as_ref().unwrap(); let keycode = Keycode::from(key + MIN_KEYCODE); + let keysym = keymap_state.key_get_one_sym(keycode); - let focused_window = &state.keyboard_focused_window; - if let Some(focused_window) = focused_window { - match key_state { - wl_keyboard::KeyState::Pressed => { - focused_window.handle_input(PlatformInput::KeyDown(KeyDownEvent { - keystroke: Keystroke::from_xkb( - keymap_state, - state.modifiers, - keycode, - ), - is_held: false, // todo!(linux) - })); + match key_state { + wl_keyboard::KeyState::Pressed => { + let input = PlatformInput::KeyDown(KeyDownEvent { + keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode), + is_held: false, // todo!(linux) + }); + + focused_window.handle_input(input.clone()); + + if !keysym.is_modifier_key() { + state.repeat.current_id += 1; + state.repeat.current_keysym = Some(keysym); + + let rate = state.repeat.rate; + let delay = state.repeat.delay; + let id = state.repeat.current_id; + let keysym = state.repeat.current_keysym; + let state_container = state_container.clone(); + + state + .platform_inner + .foreground_executor + .spawn(async move { + let mut wait_time = Duration::from_millis(delay as u64); + + loop { + Timer::after(wait_time).await; + + let state = state_container.0.borrow_mut(); + let is_repeating = id == state.repeat.current_id + && state.repeat.current_keysym.is_some() + && state.keyboard_focused_window.is_some(); + if !is_repeating { + return; + } + + state + .keyboard_focused_window + .as_ref() + .unwrap() + .handle_input(input.clone()); + + wait_time = Duration::from_millis(1000 / rate as u64); + } + }) + .detach(); } - wl_keyboard::KeyState::Released => { - focused_window.handle_input(PlatformInput::KeyUp(KeyUpEvent { - keystroke: Keystroke::from_xkb( - keymap_state, - state.modifiers, - keycode, - ), - })); - } - _ => {} } + wl_keyboard::KeyState::Released => { + focused_window.handle_input(PlatformInput::KeyUp(KeyUpEvent { + keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode), + })); + + if !keysym.is_modifier_key() { + state.repeat.current_keysym = None; + } + } + _ => {} } } _ => {} @@ -530,6 +605,7 @@ impl Dispatch for WaylandClientState { conn: &Connection, qh: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); match event { wl_pointer::Event::Enter { surface, @@ -537,11 +613,16 @@ impl Dispatch for WaylandClientState { surface_y, .. } => { + let mut mouse_focused_window = None; for window in &state.windows { if window.1.surface.id() == surface.id() { - state.mouse_focused_window = Some(Rc::clone(&window.1)); + mouse_focused_window = Some(Rc::clone(&window.1)); } } + if mouse_focused_window.is_some() { + state.mouse_focused_window = mouse_focused_window; + } + state.mouse_location = Some(Point { x: Pixels::from(surface_x), y: Pixels::from(surface_y), @@ -553,51 +634,55 @@ impl Dispatch for WaylandClientState { surface_y, .. } => { - let focused_window = &state.mouse_focused_window; - if let Some(focused_window) = focused_window { - state.mouse_location = Some(Point { - x: Pixels::from(surface_x), - y: Pixels::from(surface_y), - }); - focused_window.handle_input(PlatformInput::MouseMove(MouseMoveEvent { + if state.mouse_focused_window.is_none() { + return; + } + state.mouse_location = Some(Point { + x: Pixels::from(surface_x), + y: Pixels::from(surface_y), + }); + state.mouse_focused_window.as_ref().unwrap().handle_input( + PlatformInput::MouseMove(MouseMoveEvent { position: state.mouse_location.unwrap(), pressed_button: state.button_pressed, modifiers: state.modifiers, - })) - } + }), + ); } wl_pointer::Event::Button { button, state: WEnum::Value(button_state), .. } => { - let focused_window = &state.mouse_focused_window; - let mouse_location = state.mouse_location; let button = linux_button_to_gpui(button); - if let (Some(focused_window), Some(mouse_location), Some(button)) = - (focused_window, mouse_location, button) - { - match button_state { - wl_pointer::ButtonState::Pressed => { - state.button_pressed = Some(button); - focused_window.handle_input(PlatformInput::MouseDown(MouseDownEvent { + let Some(button) = button else { return }; + if state.mouse_focused_window.is_none() || state.mouse_location.is_none() { + return; + } + match button_state { + wl_pointer::ButtonState::Pressed => { + state.button_pressed = Some(button); + state.mouse_focused_window.as_ref().unwrap().handle_input( + PlatformInput::MouseDown(MouseDownEvent { button, - position: mouse_location, + position: state.mouse_location.unwrap(), modifiers: state.modifiers, click_count: 1, - })); - } - wl_pointer::ButtonState::Released => { - state.button_pressed = None; - focused_window.handle_input(PlatformInput::MouseUp(MouseUpEvent { + }), + ); + } + wl_pointer::ButtonState::Released => { + state.button_pressed = None; + state.mouse_focused_window.as_ref().unwrap().handle_input( + PlatformInput::MouseUp(MouseUpEvent { button, - position: mouse_location, + position: state.mouse_location.unwrap(), modifiers: Modifiers::default(), click_count: 1, - })); - } - _ => {} + }), + ); } + _ => {} } } wl_pointer::Event::AxisRelativeDirection { @@ -664,6 +749,7 @@ impl Dispatch for Wayland _: &Connection, _: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); if let wp_fractional_scale_v1::Event::PreferredScale { scale, .. } = event { for window in &state.windows { if window.0.id() == *id { @@ -686,6 +772,7 @@ impl Dispatch _: &Connection, _: &QueueHandle, ) { + let mut state = state.0.borrow_mut(); if let zxdg_toplevel_decoration_v1::Event::Configure { mode, .. } = event { for window in &state.windows { if window.0.id() == *surface_id {