From 27c460d2ab060e5f8e9a247fcd321791a23a7a4a Mon Sep 17 00:00:00 2001 From: Timon Date: Thu, 3 Sep 2026 14:20:42 +0000 Subject: [PATCH] Desktop: Restructure UI input handling --- desktop/src/app.rs | 24 +- desktop/src/input.rs | 193 +++++++++------ desktop/ui/src/context.rs | 10 +- desktop/ui/src/input.rs | 396 ++++++++++++++---------------- desktop/ui/src/input/event.rs | 245 ++++++++++++++++++ desktop/ui/src/input/state.rs | 256 ------------------- desktop/ui/src/lib.rs | 18 +- desktop/ui/src/remote/host.rs | 2 +- desktop/ui/src/remote/messages.rs | 2 +- 9 files changed, 574 insertions(+), 572 deletions(-) create mode 100644 desktop/ui/src/input/event.rs delete mode 100644 desktop/ui/src/input/state.rs diff --git a/desktop/src/app.rs b/desktop/src/app.rs index f87a09d439..85057aa449 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -17,11 +17,11 @@ use winit::window::WindowId; use crate::dirs; use crate::event::{AppEvent, AppEventScheduler}; -use crate::input::{InputAction, InputState}; +use crate::input::InputState; use crate::persist; use crate::preferences; use crate::render::{RenderError, RenderState}; -use crate::ui::{UiCommand, UiInstance}; +use crate::ui::{InputEvent, UiCommand, UiInstance}; use crate::window::Window; use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, Preferences}; use crate::wrapper::{DesktopWrapper, MmapResourceStorage, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages}; @@ -547,20 +547,16 @@ impl ApplicationHandler for App { if let Some(window) = &self.window { window.end_pointer_lock(); } - self.ui.send(UiCommand::Input(WindowEvent::PointerMoved { - device_id: None, - position: pointer_lock_position, - primary: true, - source: winit::event::PointerSource::Mouse, - })); + self.ui.send(UiCommand::Input( + InputEvent::pointer().position(pointer_lock_position).moved().modifiers(self.input_state.modifiers()).build(), + )); } - for action in self.input_state.process(&event) { - match action { - InputAction::Ui(event) => self.ui.send(UiCommand::Input(event)), - InputAction::Editor(message) => self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message)), - } - } + self.input_state.process( + &event, + |message| self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(DesktopWrapperMessage::Input(message))), + |input| self.ui.send(UiCommand::Input(input)), + ); match event { WindowEvent::CloseRequested => { diff --git a/desktop/src/input.rs b/desktop/src/input.rs index 63ba804981..40b307b822 100644 --- a/desktop/src/input.rs +++ b/desktop/src/input.rs @@ -1,30 +1,20 @@ use std::time::Instant; use winit::dpi::PhysicalPosition; use winit::event::{ButtonSource, ElementState, MouseButton, MouseScrollDelta, PointerSource, TabletToolData, TabletToolKind, WindowEvent}; +use winit::keyboard::ModifiersState; -use crate::ui::{MULTICLICK_ALLOWED_TRAVEL, MULTICLICK_TIMEOUT, PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y}; -use crate::wrapper::messages::{DesktopWrapperMessage, InputMessage, ModifierKeys, MouseKeys, PointerState, ScrollDelta}; +use crate::ui::{InputEvent, MULTICLICK_ALLOWED_TRAVEL, MULTICLICK_TIMEOUT, PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y}; +use crate::wrapper::messages::{InputMessage, ModifierKeys, MouseKeys, PointerState, ScrollDelta}; pub(crate) struct InputState { start: Instant, viewport_info: Option, pointer_lock_position: Option>, - modifier_keys: ModifierKeys, + modifiers: ModifiersState, pointer_position: PhysicalPosition, pointer_keys: MouseKeys, ui_capture: bool, - multiclick: Option, -} - -pub(crate) enum InputAction { - Ui(WindowEvent), - Editor(DesktopWrapperMessage), -} - -impl InputAction { - fn editor(message: InputMessage) -> Self { - Self::Editor(DesktopWrapperMessage::Input(message)) - } + click_tracker: ClickTracker, } impl InputState { @@ -33,11 +23,11 @@ impl InputState { start: Instant::now(), viewport_info: None, pointer_lock_position: None, - modifier_keys: ModifierKeys::empty(), + modifiers: ModifiersState::default(), pointer_position: PhysicalPosition::default(), pointer_keys: MouseKeys::empty(), ui_capture: true, - multiclick: None, + click_tracker: ClickTracker::default(), } } @@ -61,13 +51,18 @@ impl InputState { self.pointer_lock_position.is_some() } - pub(crate) fn process(&mut self, event: &WindowEvent) -> Vec { + pub(crate) fn modifiers(&self) -> ModifiersState { + self.modifiers + } + + pub(crate) fn process(&mut self, event: &WindowEvent, mut editor_callback: impl FnMut(InputMessage), mut ui_callback: impl FnMut(InputEvent)) { match event { WindowEvent::PointerMoved { position, source, .. } => { self.pointer_position = *position; let PointerSource::TabletTool { kind, data } = source else { - return vec![InputAction::Ui(event.clone())]; + ui_callback(InputEvent::pointer().position(*position).moved().modifiers(self.modifiers).build()); + return; }; let ui_capture = if self.pointer_keys.is_empty() { self.pointer_locked() || !self.in_viewport(*position) @@ -75,18 +70,24 @@ impl InputState { self.ui_capture }; if ui_capture { - return vec![InputAction::Ui(event.clone())]; + ui_callback(InputEvent::pointer().position(*position).moved().modifiers(self.modifiers).build()); + return; } - vec![InputAction::editor(InputMessage::PointerMove { + editor_callback(InputMessage::PointerMove { editor_mouse_state: self.tablet_pointer_state(kind, data), - modifier_keys: self.modifier_keys, - })] + modifier_keys: self.modifier_keys(), + }); + } + WindowEvent::PointerEntered { position, .. } => { + self.pointer_position = *position; + ui_callback(InputEvent::pointer().position(*position).entered().modifiers(self.modifiers).build()) } - WindowEvent::PointerEntered { position, .. } | WindowEvent::PointerLeft { position: Some(position), .. } => { + WindowEvent::PointerLeft { position: Some(position), .. } => { self.pointer_position = *position; - vec![InputAction::Ui(event.clone())] + ui_callback(InputEvent::pointer().position(*position).exited().modifiers(self.modifiers).build()) } + WindowEvent::PointerLeft { position: None, .. } => ui_callback(InputEvent::pointer().exited().modifiers(self.modifiers).build()), WindowEvent::PointerButton { state, button, position, .. } => { self.pointer_position = *position; @@ -111,38 +112,47 @@ impl InputState { ElementState::Released => self.pointer_keys.remove(keys), } + let count = mouse_button.map_or(1, |button| self.click_tracker.input(*position, button, *state)); + let back_or_forward = matches!(mouse_button, Some(MouseButton::Back | MouseButton::Forward)); if self.pointer_locked() || !(back_or_forward || (tablet && !self.ui_capture)) { - return vec![InputAction::Ui(event.clone())]; + let pointer = InputEvent::pointer().position(*position); + let input = match state { + ElementState::Pressed => pointer.pressed(button.clone(), count), + ElementState::Released => pointer.released(button.clone(), count), + }; + ui_callback(input.modifiers(self.modifiers).build()); + return; } let editor_mouse_state = match button { ButtonSource::TabletTool { kind, data, .. } => self.tablet_pointer_state(kind, data), _ => self.pointer_state(), }; - let modifier_keys = self.modifier_keys; + let modifier_keys = self.modifier_keys(); match state { - ElementState::Pressed => vec![InputAction::editor(InputMessage::PointerDown { editor_mouse_state, modifier_keys })], - ElementState::Released => { - let mut actions = vec![InputAction::editor(InputMessage::PointerUp { editor_mouse_state, modifier_keys })]; - if let Some(mouse_button) = mouse_button - && self.track_multiclick(mouse_button, *position) - { - actions.push(InputAction::editor(InputMessage::DoubleClick { - editor_mouse_state: PointerState { - mouse_keys: keys, - ..editor_mouse_state - }, - modifier_keys, - })); - } - actions + ElementState::Pressed => editor_callback(InputMessage::PointerDown { editor_mouse_state, modifier_keys }), + ElementState::Released if count % 2 == 0 => { + editor_callback(InputMessage::PointerUp { editor_mouse_state, modifier_keys }); + editor_callback(InputMessage::DoubleClick { + editor_mouse_state: PointerState { + mouse_keys: keys, + ..editor_mouse_state + }, + modifier_keys, + }); } + ElementState::Released => editor_callback(InputMessage::PointerUp { editor_mouse_state, modifier_keys }), } } WindowEvent::MouseWheel { delta, .. } => { if self.pointer_locked() || !self.in_viewport(self.pointer_position) { - return vec![InputAction::Ui(event.clone())]; + let input = match delta { + MouseScrollDelta::LineDelta(x, y) => InputEvent::pointer().scrolled_lines(f64::from(*x), f64::from(*y)), + MouseScrollDelta::PixelDelta(position) => InputEvent::pointer().scrolled_pixels(position.x, position.y), + }; + ui_callback(input.modifiers(self.modifiers).build()); + return; } let (x, y) = match delta { @@ -152,34 +162,29 @@ impl InputState { let scroll_delta = ScrollDelta::new(-x * SCROLL_SPEED_X, -y * SCROLL_SPEED_Y, 0.); - vec![InputAction::editor(InputMessage::WheelScroll { + editor_callback(InputMessage::WheelScroll { editor_mouse_state: PointerState { scroll_delta, ..self.pointer_state() }, - modifier_keys: self.modifier_keys, - })] + modifier_keys: self.modifier_keys(), + }); } WindowEvent::PinchGesture { delta, .. } => { if self.pointer_locked() || !self.in_viewport(self.pointer_position) || !delta.is_normal() { - return vec![InputAction::Ui(event.clone())]; + ui_callback(InputEvent::pointer().zoomed(*delta).modifiers(self.modifiers).build()); + return; } // TODO: This is a temporary solution to handle pinch gestures, we should handle pinch gestures editor-side instead. let scroll_delta = ScrollDelta::new(0., -delta * PINCH_ZOOM_SPEED, 0.); - vec![InputAction::editor(InputMessage::WheelScroll { + editor_callback(InputMessage::WheelScroll { editor_mouse_state: PointerState { scroll_delta, ..self.pointer_state() }, - modifier_keys: self.modifier_keys | ModifierKeys::CONTROL, - })] + modifier_keys: self.modifier_keys() | ModifierKeys::CONTROL, + }); } WindowEvent::ModifiersChanged(modifiers) => { - let modifiers = modifiers.state(); - self.modifier_keys = ModifierKeys::empty(); - self.modifier_keys.set(ModifierKeys::SHIFT, modifiers.shift_key()); - self.modifier_keys.set(ModifierKeys::CONTROL, modifiers.control_key()); - self.modifier_keys.set(ModifierKeys::ALT, modifiers.alt_key()); - self.modifier_keys.set(ModifierKeys::META_OR_COMMAND, modifiers.meta_key()); - - vec![InputAction::Ui(event.clone())] + self.modifiers = modifiers.state(); } - _ => vec![InputAction::Ui(event.clone())], + WindowEvent::KeyboardInput { event, .. } => ui_callback(InputEvent::key(event).modifiers(self.modifiers).build()), + _ => {} } } @@ -211,16 +216,13 @@ impl InputState { } } - fn track_multiclick(&mut self, button: MouseButton, position: PhysicalPosition) -> bool { - let now = Instant::now(); - let travel = MULTICLICK_ALLOWED_TRAVEL as f64; - let double = self.multiclick.take().is_some_and(|click| { - click.button == button && now.duration_since(click.time) <= MULTICLICK_TIMEOUT && (position.x - click.position.x).abs() <= travel && (position.y - click.position.y).abs() <= travel - }); - if !double { - self.multiclick = Some(Multiclick { button, time: now, position }); - } - double + fn modifier_keys(&self) -> ModifierKeys { + let mut keys = ModifierKeys::empty(); + keys.set(ModifierKeys::SHIFT, self.modifiers.shift_key()); + keys.set(ModifierKeys::CONTROL, self.modifiers.control_key()); + keys.set(ModifierKeys::ALT, self.modifiers.alt_key()); + keys.set(ModifierKeys::META_OR_COMMAND, self.modifiers.meta_key()); + keys } } @@ -238,8 +240,55 @@ impl ViewportInfo { } } -struct Multiclick { - button: MouseButton, +#[derive(Default)] +struct ClickTracker { + left: ClickChains, + right: ClickChains, + middle: ClickChains, + back: ClickChains, + forward: ClickChains, +} + +impl ClickTracker { + fn input(&mut self, position: PhysicalPosition, button: MouseButton, state: ElementState) -> u32 { + let position = (position.x as i32, position.y as i32); + let clicks = match button { + MouseButton::Left => &mut self.left, + MouseButton::Right => &mut self.right, + MouseButton::Middle => &mut self.middle, + MouseButton::Back => &mut self.back, + MouseButton::Forward => &mut self.forward, + _ => return 1, + }; + let chain = match state { + ElementState::Pressed => &mut clicks.down, + ElementState::Released => &mut clicks.up, + }; + + let now = Instant::now(); + let count = match chain { + Some(previous) => { + let within_time = now.saturating_duration_since(previous.time) <= MULTICLICK_TIMEOUT; + let dx = position.0.abs_diff(previous.position.0) as usize; + let dy = position.1.abs_diff(previous.position.1) as usize; + let within_distance = dx <= MULTICLICK_ALLOWED_TRAVEL && dy <= MULTICLICK_ALLOWED_TRAVEL; + if within_time && within_distance { previous.count.saturating_add(1) } else { 1 } + } + None => 1, + }; + *chain = Some(Click { time: now, position, count }); + count + } +} + +#[derive(Default)] +struct ClickChains { + down: Option, + up: Option, +} + +struct Click { time: Instant, - position: PhysicalPosition, + position: (i32, i32), + count: u32, } diff --git a/desktop/ui/src/context.rs b/desktop/ui/src/context.rs index c54005c185..c99ee20196 100644 --- a/desktop/ui/src/context.rs +++ b/desktop/ui/src/context.rs @@ -223,6 +223,7 @@ fn create_browser(delegate: BrowserDelegate, frames: FrameStreamer, view_info_se .map(|browser| BrowserContext { delegate, browser, + input: input::InputState::default(), view_info_sender, _instance_dir: instance_dir, }) @@ -252,12 +253,8 @@ pub(crate) enum InitError { pub(crate) struct CefContextHandle; impl CefContextHandle { - pub(crate) fn apply_input(&self, events: Vec) { - with_context(move |context| { - for event in &events { - input::apply(&context.browser, event); - } - }); + pub(crate) fn process_input(&self, event: InputEvent) { + with_context(move |context| input::process(&mut context.input, &context.browser, &event)); } pub(crate) fn update_view_info(&self, update: ViewInfoUpdate) { @@ -276,6 +273,7 @@ impl CefContextHandle { struct BrowserContext { delegate: BrowserDelegate, browser: Browser, + input: input::InputState, view_info_sender: Sender, _instance_dir: TempDir, } diff --git a/desktop/ui/src/input.rs b/desktop/ui/src/input.rs index 84463b0126..bfc52664ba 100644 --- a/desktop/ui/src/input.rs +++ b/desktop/ui/src/input.rs @@ -1,253 +1,235 @@ -use cef::sys::{cef_key_event_type_t, cef_mouse_button_type_t}; -use cef::{Browser, ImplBrowser, ImplBrowserHost, KeyEvent, MouseEvent}; -use winit::event::{ButtonSource, ElementState, MouseButton, MouseScrollDelta, WindowEvent}; +pub(crate) mod event; +pub(crate) use event::InputEvent; mod keymap; use keymap::{ToCharRepresentation, ToNativeKeycode, ToVKBits}; -mod state; -pub(crate) use state::{CefModifiers, InputState}; +use cef::sys::{cef_event_flags_t, cef_key_event_type_t, cef_mouse_button_type_t}; +use cef::{Browser, ImplBrowser, ImplBrowserHost, KeyEvent, MouseEvent}; +use winit::dpi::PhysicalPosition; +use winit::keyboard::{Key, KeyLocation, NamedKey}; use super::consts::{PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y}; +use event::{InputEventKind, KeyAction, Modifiers, MouseButton, PointerAction}; -/// A window input translated into the plain data CEF consumes — no winit types, so it can -/// be applied to a browser living in another process. -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub(crate) enum InputEvent { - MouseMove { data: MouseData, leave: bool }, - MouseClick { data: MouseData, button: MouseButtonKind, up: bool, click_count: i32 }, - MouseWheel { data: MouseData, delta_x: i32, delta_y: i32 }, - Key(KeyData), -} - -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] -pub(crate) struct MouseData { - pub(crate) x: i32, - pub(crate) y: i32, - pub(crate) modifiers: u32, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub(crate) enum MouseButtonKind { - Left, - Right, - Middle, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub(crate) enum KeyEventKind { - RawKeyDown, - KeyUp, - Char, +#[derive(Default)] +pub(crate) struct InputState { + position: PhysicalPosition, + buttons: ButtonStates, } -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] -pub(crate) struct KeyData { - pub(crate) kind: KeyEventKind, - pub(crate) modifiers: u32, - pub(crate) windows_key_code: i32, - pub(crate) native_key_code: i32, - pub(crate) character: u16, - pub(crate) unmodified_character: u16, +impl InputState { + fn pointer_move(&mut self, position: PhysicalPosition) -> bool { + let moved = (position.x as i32, position.y as i32) != (self.position.x as i32, self.position.y as i32); + self.position = position; + moved + } } -/// Turns a winit event into zero or more [`InputEvent`]s, updating the tracked input state -/// (cursor position, click counting, modifiers) along the way. -pub(crate) fn translate(input_state: &mut InputState, event: &WindowEvent) -> Vec { - match event { - WindowEvent::PointerMoved { position, .. } => { - if !input_state.cursor_move(position) { - return Vec::new(); - } - vec![InputEvent::MouseMove { - data: input_state.mouse_data(), - leave: false, - }] - } - WindowEvent::PointerEntered { position, .. } => { - let _ = input_state.cursor_move(position); - vec![InputEvent::MouseMove { - data: input_state.mouse_data(), - leave: false, - }] - } - WindowEvent::PointerLeft { position, .. } => { - if let Some(position) = position { - let _ = input_state.cursor_move(position); +pub(crate) fn process(state: &mut InputState, browser: &Browser, event: &InputEvent) { + let Some(host) = browser.host() else { return }; + match &event.kind { + InputEventKind::Pointer { position, action } => { + let position = position.unwrap_or(state.position); + match *action { + PointerAction::Move => { + if !state.pointer_move(position) { + return; + } + let flags = event_flags(&event.modifiers, &state.buttons); + host.send_mouse_move_event(Some(&mouse_event(position, flags)), 0); + } + PointerAction::Enter => { + state.pointer_move(position); + let flags = event_flags(&event.modifiers, &state.buttons); + host.send_mouse_move_event(Some(&mouse_event(position, flags)), 0); + } + PointerAction::Exit => { + state.pointer_move(position); + let flags = event_flags(&event.modifiers, &state.buttons); + host.send_mouse_move_event(Some(&mouse_event(position, flags)), 1); + } + PointerAction::Press { button, count } | PointerAction::Release { button, count } => { + state.pointer_move(position); + let cef_button = match button { + MouseButton::Left => cef_mouse_button_type_t::MBT_LEFT, + MouseButton::Right => cef_mouse_button_type_t::MBT_RIGHT, + MouseButton::Middle => cef_mouse_button_type_t::MBT_MIDDLE, + MouseButton::Unknown => return, + }; + let up = matches!(action, PointerAction::Release { .. }); + state.buttons.update(button, !up); + + // CEF only understands single, double and triple clicks; further clicks alternate between double and triple. + let count = match count { + 0 | 1 => 1, + count if count % 2 == 0 => 2, + _ => 3, + }; + + let flags = event_flags(&event.modifiers, &state.buttons); + host.send_mouse_click_event(Some(&mouse_event(position, flags)), cef::MouseButtonType::from(cef_button), up as i32, count); + } + PointerAction::ScrollLines { x, y } => { + let flags = event_flags(&event.modifiers, &state.buttons); + let delta_x = (x * SCROLL_LINE_WIDTH * SCROLL_SPEED_X) as i32; + let delta_y = (y * SCROLL_LINE_HEIGHT * SCROLL_SPEED_Y) as i32; + host.send_mouse_wheel_event(Some(&mouse_event(position, flags)), delta_x, delta_y); + } + PointerAction::ScrollPixels { x, y } => { + let flags = event_flags(&event.modifiers, &state.buttons) | PRECISION_SCROLLING_DELTA; + host.send_mouse_wheel_event(Some(&mouse_event(position, flags)), (x * SCROLL_SPEED_X) as i32, (y * SCROLL_SPEED_Y) as i32); + } + PointerAction::Zoom(delta) => { + if !delta.is_normal() { + return; + } + let flags = CONTROL_DOWN | PRECISION_SCROLLING_DELTA; + host.send_mouse_wheel_event(Some(&mouse_event(position, flags)), 0, (delta * PINCH_ZOOM_SPEED).round() as i32); + } } - vec![InputEvent::MouseMove { - data: input_state.mouse_data(), - leave: true, - }] - } - WindowEvent::PointerButton { state, button, position, .. } => { - let mouse_button = match button { - ButtonSource::Mouse(mouse_button) => Some(*mouse_button), - ButtonSource::TabletTool { button, .. } => (*button).into(), - _ => None, // TODO: Handle touch input - }; - let Some(mouse_button) = mouse_button else { - return Vec::new(); - }; - - let _ = input_state.cursor_move(position); - let click_count = input_state.mouse_input(&mouse_button, state).into(); - let up = matches!(state, ElementState::Released); - let button = match mouse_button { - MouseButton::Left => MouseButtonKind::Left, - MouseButton::Right => MouseButtonKind::Right, - MouseButton::Middle => MouseButtonKind::Middle, - _ => return Vec::new(), - }; - - vec![InputEvent::MouseClick { - data: input_state.mouse_data(), - button, - up, - click_count, - }] } - WindowEvent::MouseWheel { delta, phase: _, device_id: _, .. } => { - let (mut delta_x, mut delta_y) = match delta { - MouseScrollDelta::LineDelta(x, y) => (x * SCROLL_LINE_WIDTH as f32, y * SCROLL_LINE_HEIGHT as f32), - MouseScrollDelta::PixelDelta(physical_position) => (physical_position.x as f32, physical_position.y as f32), + InputEventKind::Key { + key, + key_without_modifiers, + physical_key, + location, + text, + action, + } => { + let mut flags = event_flags(&event.modifiers, &state.buttons); + + let own_flag = match key { + Key::Named(NamedKey::Shift) => SHIFT_DOWN, + Key::Named(NamedKey::Control) => CONTROL_DOWN, + Key::Named(NamedKey::Alt) => ALT_DOWN, + Key::Named(NamedKey::AltGraph) => ALTGR_DOWN, + Key::Named(NamedKey::Meta) => COMMAND_DOWN, + _ => 0, }; - delta_x *= SCROLL_SPEED_X as f32; - delta_y *= SCROLL_SPEED_Y as f32; - - vec![InputEvent::MouseWheel { - data: input_state.mouse_data(), - delta_x: delta_x as i32, - delta_y: delta_y as i32, - }] - } - WindowEvent::ModifiersChanged(modifiers) => { - input_state.modifiers_changed(&modifiers.state()); - Vec::new() - } - WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => { - input_state.modifiers_apply_key_event(&event.logical_key, &event.state); + match action { + KeyAction::Press | KeyAction::Repeat => flags |= own_flag, + KeyAction::Release => flags &= !own_flag, + } - let mut kind = match (event.state, &event.logical_key) { - (ElementState::Pressed, winit::keyboard::Key::Character(_)) => KeyEventKind::Char, - (ElementState::Pressed, _) => KeyEventKind::RawKeyDown, - (ElementState::Released, _) => KeyEventKind::KeyUp, + flags |= match location { + KeyLocation::Left => IS_LEFT, + KeyLocation::Right => IS_RIGHT, + KeyLocation::Numpad => IS_KEY_PAD, + KeyLocation::Standard => 0, }; + if *action == KeyAction::Repeat { + flags |= IS_REPEAT; + } - let modifiers = input_state.cef_modifiers(&event.location, event.repeat).into(); - - let windows_key_code = match &event.logical_key { - winit::keyboard::Key::Named(named) => named.to_vk_bits(), - winit::keyboard::Key::Character(char) => char.chars().next().unwrap_or_default().to_vk_bits(), + let windows_key_code = match key { + Key::Named(named) => named.to_vk_bits(), + Key::Character(char) => char.chars().next().unwrap_or_default().to_vk_bits(), _ => 0, }; + let native_key_code = physical_key.to_native_keycode(); - let native_key_code = event.physical_key.to_native_keycode(); - - let char_representation = event.logical_key.to_char_representation(); + let char_representation = key.to_char_representation(); #[allow(unused_mut)] let mut character = char_representation as u16; - - if event.state == ElementState::Pressed && character != 0 { - kind = KeyEventKind::Char; - } - - let unmodified_character = event.key_without_modifiers.to_char_representation() as u16; + let unmodified_character = key_without_modifiers.to_char_representation() as u16; #[cfg(target_os = "macos")] // See https://www.magpcss.org/ceforum/viewtopic.php?start=10&t=11650 - if character == 0 && unmodified_character == 0 && event.text_with_all_modifiers.is_some() { + if character == 0 && unmodified_character == 0 && text.is_some() { character = 1; } + #[cfg(not(target_os = "macos"))] + let _ = text; - let key = KeyData { - kind, - modifiers, + let key_event = |kind: cef_key_event_type_t, windows_key_code: i32| KeyEvent { + type_: kind.into(), + modifiers: flags, windows_key_code, native_key_code, character, unmodified_character, + ..Default::default() }; - if kind == KeyEventKind::Char { - // CEF expects a raw key-down before the character event it produces. - vec![ - InputEvent::Key(KeyData { - kind: KeyEventKind::RawKeyDown, - ..key - }), - InputEvent::Key(KeyData { - windows_key_code: char_representation as i32, - ..key - }), - ] - } else { - vec![InputEvent::Key(key)] + match action { + KeyAction::Press | KeyAction::Repeat if char_representation != '\0' => { + host.send_key_event(Some(&key_event(cef_key_event_type_t::KEYEVENT_RAWKEYDOWN, windows_key_code))); + host.send_key_event(Some(&key_event(cef_key_event_type_t::KEYEVENT_CHAR, char_representation as i32))); + } + KeyAction::Press | KeyAction::Repeat => { + host.send_key_event(Some(&key_event(cef_key_event_type_t::KEYEVENT_RAWKEYDOWN, windows_key_code))); + } + KeyAction::Release => { + host.send_key_event(Some(&key_event(cef_key_event_type_t::KEYEVENT_KEYUP, windows_key_code))); + } } } - WindowEvent::PinchGesture { delta, .. } => { - if !delta.is_normal() { - return Vec::new(); - } + } +} - let data = MouseData { - modifiers: CefModifiers::PINCH_MODIFIERS.into(), - ..input_state.mouse_data() - }; +#[derive(Default)] +struct ButtonStates { + left: bool, + right: bool, + middle: bool, +} - vec![InputEvent::MouseWheel { - data, - delta_x: 0, - delta_y: (delta * PINCH_ZOOM_SPEED).round() as i32, - }] +impl ButtonStates { + fn update(&mut self, button: MouseButton, held: bool) { + match button { + MouseButton::Left => self.left = held, + MouseButton::Right => self.right = held, + MouseButton::Middle => self.middle = held, + MouseButton::Unknown => {} } - _ => Vec::new(), } } -/// Sends a translated [`InputEvent`] to the browser. Must run on the thread owning the browser. -pub(crate) fn apply(browser: &Browser, event: &InputEvent) { - let Some(host) = browser.host() else { return }; - match event { - InputEvent::MouseMove { data, leave } => { - host.send_mouse_move_event(Some(&data.into()), *leave as i32); - } - InputEvent::MouseClick { data, button, up, click_count } => { - let cef_button = cef::MouseButtonType::from(match button { - MouseButtonKind::Left => cef_mouse_button_type_t::MBT_LEFT, - MouseButtonKind::Right => cef_mouse_button_type_t::MBT_RIGHT, - MouseButtonKind::Middle => cef_mouse_button_type_t::MBT_MIDDLE, - }); - host.send_mouse_click_event(Some(&data.into()), cef_button, *up as i32, *click_count); - } - InputEvent::MouseWheel { data, delta_x, delta_y } => { - host.send_mouse_wheel_event(Some(&data.into()), *delta_x, *delta_y); - } - InputEvent::Key(key) => { - let key_event = KeyEvent { - type_: match key.kind { - KeyEventKind::RawKeyDown => cef_key_event_type_t::KEYEVENT_RAWKEYDOWN, - KeyEventKind::KeyUp => cef_key_event_type_t::KEYEVENT_KEYUP, - KeyEventKind::Char => cef_key_event_type_t::KEYEVENT_CHAR, - } - .into(), - modifiers: key.modifiers, - windows_key_code: key.windows_key_code, - native_key_code: key.native_key_code, - character: key.character, - unmodified_character: key.unmodified_character, - ..Default::default() - }; - host.send_key_event(Some(&key_event)); - } +fn mouse_event(position: PhysicalPosition, flags: u32) -> MouseEvent { + MouseEvent { + x: position.x as i32, + y: position.y as i32, + modifiers: flags, } } -impl From<&MouseData> for MouseEvent { - fn from(data: &MouseData) -> Self { - MouseEvent { - x: data.x, - y: data.y, - modifiers: data.modifiers, - } +fn event_flags(modifiers: &Modifiers, buttons: &ButtonStates) -> u32 { + let bit = |condition: bool, flag: u32| if condition { flag } else { 0 }; + bit(modifiers.shift, SHIFT_DOWN) + | bit(modifiers.control, CONTROL_DOWN) + | bit(modifiers.alt, ALT_DOWN) + | bit(modifiers.alt_graph, ALTGR_DOWN) + | bit(modifiers.meta, COMMAND_DOWN) + | bit(modifiers.caps_lock, CAPS_LOCK_ON) + | bit(modifiers.num_lock, NUM_LOCK_ON) + | bit(buttons.left, LEFT_MOUSE_BUTTON) + | bit(buttons.right, RIGHT_MOUSE_BUTTON) + | bit(buttons.middle, MIDDLE_MOUSE_BUTTON) +} + +const fn flag(flag: cef_event_flags_t) -> u32 { + #[cfg(not(target_os = "windows"))] + { + flag.0 + } + #[cfg(target_os = "windows")] + { + flag.0 as u32 } } + +const SHIFT_DOWN: u32 = flag(cef_event_flags_t::EVENTFLAG_SHIFT_DOWN); +const CONTROL_DOWN: u32 = flag(cef_event_flags_t::EVENTFLAG_CONTROL_DOWN); +const ALT_DOWN: u32 = flag(cef_event_flags_t::EVENTFLAG_ALT_DOWN); +const ALTGR_DOWN: u32 = flag(cef_event_flags_t::EVENTFLAG_ALTGR_DOWN); +const COMMAND_DOWN: u32 = flag(cef_event_flags_t::EVENTFLAG_COMMAND_DOWN); +const CAPS_LOCK_ON: u32 = flag(cef_event_flags_t::EVENTFLAG_CAPS_LOCK_ON); +const NUM_LOCK_ON: u32 = flag(cef_event_flags_t::EVENTFLAG_NUM_LOCK_ON); +const LEFT_MOUSE_BUTTON: u32 = flag(cef_event_flags_t::EVENTFLAG_LEFT_MOUSE_BUTTON); +const MIDDLE_MOUSE_BUTTON: u32 = flag(cef_event_flags_t::EVENTFLAG_MIDDLE_MOUSE_BUTTON); +const RIGHT_MOUSE_BUTTON: u32 = flag(cef_event_flags_t::EVENTFLAG_RIGHT_MOUSE_BUTTON); +const IS_LEFT: u32 = flag(cef_event_flags_t::EVENTFLAG_IS_LEFT); +const IS_RIGHT: u32 = flag(cef_event_flags_t::EVENTFLAG_IS_RIGHT); +const IS_KEY_PAD: u32 = flag(cef_event_flags_t::EVENTFLAG_IS_KEY_PAD); +const IS_REPEAT: u32 = flag(cef_event_flags_t::EVENTFLAG_IS_REPEAT); +const PRECISION_SCROLLING_DELTA: u32 = flag(cef_event_flags_t::EVENTFLAG_PRECISION_SCROLLING_DELTA); diff --git a/desktop/ui/src/input/event.rs b/desktop/ui/src/input/event.rs new file mode 100644 index 0000000000..2c985baef7 --- /dev/null +++ b/desktop/ui/src/input/event.rs @@ -0,0 +1,245 @@ +use winit::dpi::PhysicalPosition; +use winit::event::{ElementState, KeyEvent}; +use winit::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey, SmolStr}; + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct InputEvent { + pub(crate) kind: InputEventKind, + pub(crate) modifiers: Modifiers, +} + +impl InputEvent { + pub fn pointer() -> PointerInputEventBuilder { + PointerInputEventBuilder { position: UnknownPosition } + } + + pub fn key(event: &KeyEvent) -> InputEventBuilder { + let action = match (event.state, event.repeat) { + (ElementState::Pressed, false) => KeyAction::Press, + (ElementState::Pressed, true) => KeyAction::Repeat, + (ElementState::Released, _) => KeyAction::Release, + }; + InputEventBuilder::new(InputEventKind::Key { + key: event.logical_key.clone(), + key_without_modifiers: event.key_without_modifiers.clone(), + physical_key: event.physical_key, + location: event.location, + text: event.text_with_all_modifiers.clone(), + action, + }) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum MouseButton { + Left, + Right, + Middle, + Unknown, +} + +impl From for MouseButton { + fn from(button: winit::event::MouseButton) -> Self { + match button { + winit::event::MouseButton::Left => Self::Left, + winit::event::MouseButton::Right => Self::Right, + winit::event::MouseButton::Middle => Self::Middle, + _ => Self::Unknown, + } + } +} + +impl From for MouseButton { + fn from(button: winit::event::ButtonSource) -> Self { + button.mouse_button().map_or(Self::Unknown, Self::from) + } +} + +#[must_use] +#[derive(Clone, Debug)] +pub struct PointerInputEventBuilder

{ + position: P, +} + +impl PointerInputEventBuilder { + pub fn position(self, position: PhysicalPosition) -> PointerInputEventBuilder> { + PointerInputEventBuilder { position } + } +} + +impl PointerInputEventBuilder> { + pub fn moved(self) -> InputEventBuilder { + self.finish(PointerAction::Move) + } + + pub fn entered(self) -> InputEventBuilder { + self.finish(PointerAction::Enter) + } +} + +impl PointerInputEventBuilder

{ + pub fn exited(self) -> InputEventBuilder { + self.finish(PointerAction::Exit) + } + + pub fn pressed(self, button: impl Into, count: u32) -> InputEventBuilder { + self.finish(PointerAction::Press { button: button.into(), count }) + } + + pub fn released(self, button: impl Into, count: u32) -> InputEventBuilder { + self.finish(PointerAction::Release { button: button.into(), count }) + } + + pub fn scrolled_lines(self, x: f64, y: f64) -> InputEventBuilder { + self.finish(PointerAction::ScrollLines { x, y }) + } + + pub fn scrolled_pixels(self, x: f64, y: f64) -> InputEventBuilder { + self.finish(PointerAction::ScrollPixels { x, y }) + } + + pub fn zoomed(self, delta: f64) -> InputEventBuilder { + self.finish(PointerAction::Zoom(delta)) + } + + fn finish(self, action: PointerAction) -> InputEventBuilder { + InputEventBuilder::new(InputEventKind::Pointer { + position: self.position.position(), + action, + }) + } +} + +#[must_use] +#[derive(Clone, Debug)] +pub struct InputEventBuilder { + kind: InputEventKind, + modifiers: Modifiers, +} + +impl InputEventBuilder { + fn new(kind: InputEventKind) -> Self { + Self { + kind, + modifiers: Modifiers::default(), + } + } + + pub fn shift(mut self, on: bool) -> Self { + self.modifiers.shift = on; + self + } + + pub fn control(mut self, on: bool) -> Self { + self.modifiers.control = on; + self + } + + pub fn alt(mut self, on: bool) -> Self { + self.modifiers.alt = on; + self + } + + pub fn alt_graph(mut self, on: bool) -> Self { + self.modifiers.alt_graph = on; + self + } + + pub fn meta(mut self, on: bool) -> Self { + self.modifiers.meta = on; + self + } + + pub fn caps_lock(mut self, on: bool) -> Self { + self.modifiers.caps_lock = on; + self + } + + pub fn num_lock(mut self, on: bool) -> Self { + self.modifiers.num_lock = on; + self + } + + pub fn modifiers(mut self, modifiers: ModifiersState) -> Self { + self.modifiers.shift = modifiers.shift_key(); + self.modifiers.control = modifiers.control_key(); + self.modifiers.alt = modifiers.alt_key(); + self.modifiers.meta = modifiers.meta_key(); + self + } + + pub fn build(self) -> InputEvent { + InputEvent { + kind: self.kind, + modifiers: self.modifiers, + } + } +} + +#[expect(private_bounds)] +pub trait PointerPosition: OptionalPosition {} +impl PointerPosition for UnknownPosition {} +impl PointerPosition for PhysicalPosition {} + +#[derive(Clone, Copy, Debug)] +pub struct UnknownPosition; + +trait OptionalPosition { + fn position(self) -> Option>; +} +impl OptionalPosition for UnknownPosition { + fn position(self) -> Option> { + None + } +} +impl OptionalPosition for PhysicalPosition { + fn position(self) -> Option> { + Some(self) + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub(crate) enum InputEventKind { + Pointer { + position: Option>, + action: PointerAction, + }, + Key { + key: Key, + key_without_modifiers: Key, + physical_key: PhysicalKey, + location: KeyLocation, + text: Option, + action: KeyAction, + }, +} + +#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] +pub(crate) enum PointerAction { + Move, + Enter, + Exit, + Press { button: MouseButton, count: u32 }, + Release { button: MouseButton, count: u32 }, + ScrollLines { x: f64, y: f64 }, + ScrollPixels { x: f64, y: f64 }, + Zoom(f64), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub(crate) enum KeyAction { + Press, + Repeat, + Release, +} + +#[derive(Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] +pub(crate) struct Modifiers { + pub(crate) shift: bool, + pub(crate) control: bool, + pub(crate) alt: bool, + pub(crate) alt_graph: bool, + pub(crate) meta: bool, + pub(crate) caps_lock: bool, + pub(crate) num_lock: bool, +} diff --git a/desktop/ui/src/input/state.rs b/desktop/ui/src/input/state.rs deleted file mode 100644 index aceedd6872..0000000000 --- a/desktop/ui/src/input/state.rs +++ /dev/null @@ -1,256 +0,0 @@ -use cef::sys::cef_event_flags_t; -use std::time::Instant; -use winit::dpi::PhysicalPosition; -use winit::event::{ElementState, MouseButton}; -use winit::keyboard::{Key, KeyLocation, ModifiersState, NamedKey}; - -use super::MouseData; -use crate::consts::{MULTICLICK_ALLOWED_TRAVEL, MULTICLICK_TIMEOUT}; - -#[derive(Default)] -pub(crate) struct InputState { - modifiers: ModifiersState, - mouse_position: MousePosition, - mouse_state: MouseState, - mouse_click_tracker: ClickTracker, -} -impl InputState { - pub(crate) fn modifiers_changed(&mut self, modifiers: &ModifiersState) { - self.modifiers = *modifiers; - } - - pub(crate) fn modifiers_apply_key_event(&mut self, key: &Key, state: &ElementState) { - let bits = match key { - Key::Named(NamedKey::Shift) => ModifiersState::SHIFT, - Key::Named(NamedKey::Control) => ModifiersState::CONTROL, - Key::Named(NamedKey::Alt) => ModifiersState::ALT, - Key::Named(NamedKey::Meta) => ModifiersState::META, - _ => return, - }; - let is_pressed = matches!(state, ElementState::Pressed); - self.modifiers.set(bits, is_pressed); - } - - pub(crate) fn cursor_move(&mut self, position: &PhysicalPosition) -> bool { - let new = position.into(); - if self.mouse_position == new { - return false; - } - self.mouse_position = new; - true - } - - pub(crate) fn mouse_input(&mut self, button: &MouseButton, state: &ElementState) -> ClickCount { - self.mouse_state.update(button, state); - self.mouse_click_tracker.input(button, state, self.mouse_position) - } - - pub(crate) fn cef_modifiers(&self, location: &KeyLocation, is_repeat: bool) -> CefModifiers { - CefModifiers::new(self, location, is_repeat) - } - - pub(crate) fn cef_mouse_modifiers(&self) -> CefModifiers { - self.cef_modifiers(&KeyLocation::Standard, false) - } - - pub(crate) fn mouse_data(&self) -> MouseData { - MouseData { - x: self.mouse_position.x, - y: self.mouse_position.y, - modifiers: self.cef_mouse_modifiers().into(), - } - } -} - -#[derive(Default, Clone, Copy, Eq, PartialEq)] -pub(crate) struct MousePosition { - x: i32, - y: i32, -} -impl From<&PhysicalPosition> for MousePosition { - fn from(position: &PhysicalPosition) -> Self { - Self { - x: position.x as i32, - y: position.y as i32, - } - } -} - -#[derive(Default, Clone)] -pub(crate) struct MouseState { - left: bool, - right: bool, - middle: bool, -} -impl MouseState { - pub(crate) fn update(&mut self, button: &MouseButton, state: &ElementState) { - match state { - ElementState::Pressed => match button { - MouseButton::Left => self.left = true, - MouseButton::Right => self.right = true, - MouseButton::Middle => self.middle = true, - _ => {} - }, - ElementState::Released => match button { - MouseButton::Left => self.left = false, - MouseButton::Right => self.right = false, - MouseButton::Middle => self.middle = false, - _ => {} - }, - } - } -} - -#[derive(Default)] -struct ClickTracker { - left: Option, - middle: Option, - right: Option, -} -impl ClickTracker { - fn input(&mut self, button: &MouseButton, state: &ElementState, position: MousePosition) -> ClickCount { - let record = match button { - MouseButton::Left => &mut self.left, - MouseButton::Right => &mut self.right, - MouseButton::Middle => &mut self.middle, - _ => return ClickCount::Single, - }; - - let Some(record) = record else { - *record = Some(ClickRecord { - down_position: position, - up_position: position, - ..Default::default() - }); - return ClickCount::Single; - }; - - let now = Instant::now(); - let within_time = now.saturating_duration_since(record.time) <= MULTICLICK_TIMEOUT; - - let (prev_count, prev_position) = match state { - ElementState::Pressed => (record.down_count, record.down_position), - ElementState::Released => (record.up_count, record.up_position), - }; - - let dx = position.x.abs_diff(prev_position.x) as usize; - let dy = position.y.abs_diff(prev_position.y) as usize; - let within_dist = dx <= MULTICLICK_ALLOWED_TRAVEL && dy <= MULTICLICK_ALLOWED_TRAVEL; - - let count = match (prev_count, within_time, within_dist) { - (ClickCount::Single, true, true) => ClickCount::Double, - (ClickCount::Double, true, true) => ClickCount::Triple, - (ClickCount::Triple, true, true) => ClickCount::Double, - _ => ClickCount::Single, - }; - - record.time = now; - - match state { - ElementState::Pressed => { - record.down_position = position; - record.down_count = count; - } - ElementState::Released => { - record.up_position = position; - record.up_count = count; - } - } - - count - } -} - -#[derive(Clone, Copy, PartialEq, Default)] -pub(crate) enum ClickCount { - #[default] - Single, - Double, - Triple, -} -impl From for i32 { - fn from(count: ClickCount) -> i32 { - match count { - ClickCount::Single => 1, - ClickCount::Double => 2, - ClickCount::Triple => 3, - } - } -} - -#[derive(Clone, Copy)] -struct ClickRecord { - time: Instant, - down_position: MousePosition, - up_position: MousePosition, - down_count: ClickCount, - up_count: ClickCount, -} - -impl Default for ClickRecord { - fn default() -> Self { - Self { - time: Instant::now(), - down_position: Default::default(), - up_position: Default::default(), - down_count: Default::default(), - up_count: Default::default(), - } - } -} - -pub(crate) struct CefModifiers(cef_event_flags_t); -impl CefModifiers { - fn new(input_state: &InputState, location: &KeyLocation, is_repeat: bool) -> Self { - let mut inner = cef_event_flags_t::EVENTFLAG_NONE; - - if input_state.modifiers.shift_key() { - inner |= cef_event_flags_t::EVENTFLAG_SHIFT_DOWN; - } - if input_state.modifiers.control_key() { - inner |= cef_event_flags_t::EVENTFLAG_CONTROL_DOWN; - } - if input_state.modifiers.alt_key() { - inner |= cef_event_flags_t::EVENTFLAG_ALT_DOWN; - } - if input_state.modifiers.meta_key() { - inner |= cef_event_flags_t::EVENTFLAG_COMMAND_DOWN; - } - - if input_state.mouse_state.left { - inner |= cef_event_flags_t::EVENTFLAG_LEFT_MOUSE_BUTTON; - } - if input_state.mouse_state.right { - inner |= cef_event_flags_t::EVENTFLAG_RIGHT_MOUSE_BUTTON; - } - if input_state.mouse_state.middle { - inner |= cef_event_flags_t::EVENTFLAG_MIDDLE_MOUSE_BUTTON; - } - - if is_repeat { - inner |= cef_event_flags_t::EVENTFLAG_IS_REPEAT; - } - - inner |= match location { - KeyLocation::Left => cef_event_flags_t::EVENTFLAG_IS_LEFT, - KeyLocation::Right => cef_event_flags_t::EVENTFLAG_IS_RIGHT, - KeyLocation::Numpad => cef_event_flags_t::EVENTFLAG_IS_KEY_PAD, - KeyLocation::Standard => cef_event_flags_t::EVENTFLAG_NONE, - }; - - Self(inner) - } - - pub(super) const PINCH_MODIFIERS: Self = Self(cef_event_flags_t( - cef_event_flags_t::EVENTFLAG_CONTROL_DOWN.0 | cef_event_flags_t::EVENTFLAG_PRECISION_SCROLLING_DELTA.0, - )); -} - -impl From for u32 { - fn from(val: CefModifiers) -> Self { - #[cfg(not(target_os = "windows"))] - return val.0.0; - #[cfg(target_os = "windows")] - return val.0.0 as u32; - } -} diff --git a/desktop/ui/src/lib.rs b/desktop/ui/src/lib.rs index cd5bf8b012..13968b5263 100644 --- a/desktop/ui/src/lib.rs +++ b/desktop/ui/src/lib.rs @@ -23,6 +23,7 @@ mod utility; mod view; pub use consts::{MULTICLICK_ALLOWED_TRAVEL, MULTICLICK_TIMEOUT, PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y}; +pub use input::event::{InputEvent, InputEventBuilder, MouseButton, PointerInputEventBuilder, PointerPosition, UnknownPosition}; pub struct UiContext { inner: S::ContextData, @@ -69,7 +70,6 @@ impl UiContext { Ok(UiInstance { inner: Arc::new(UiInstanceInner { host: self.inner.clone(), - input: Mutex::new(input::InputState::default()), events: Mutex::new(events), queue, shutdown_complete: Mutex::new(shutdown_complete), @@ -143,7 +143,6 @@ pub struct UiInstance { pub(crate) struct UiInstanceInner { host: Arc, - input: Mutex, events: Mutex>, queue: events::EventQueue, shutdown_complete: Mutex>, @@ -154,18 +153,7 @@ impl UiInstance { pub fn send(&self, command: UiCommand) { let shared = &self.inner; match command { - UiCommand::Input(event) => { - let events = { - let Ok(mut input) = shared.input.lock() else { - tracing::error!("Failed to lock the input state"); - return; - }; - input::translate(&mut input, &event) - }; - if !events.is_empty() { - shared.host.send(HostControlMessage::Input(events)); - } - } + UiCommand::Input(event) => shared.host.send(HostControlMessage::Input(event)), UiCommand::Resized { width, height } => shared.host.send(HostControlMessage::UpdateViewInfo(view::ViewInfoUpdate::Size { width, height })), UiCommand::ScaleChanged(scale) => shared.host.send(HostControlMessage::UpdateViewInfo(view::ViewInfoUpdate::Scale(scale))), UiCommand::Refresh => shared.host.send(HostControlMessage::RefreshViewInfo), @@ -222,7 +210,7 @@ impl Drop for UiInstanceInner { #[derive(Debug)] pub enum UiCommand { - Input(winit::event::WindowEvent), + Input(InputEvent), Resized { width: u32, height: u32 }, ScaleChanged(f64), Refresh, diff --git a/desktop/ui/src/remote/host.rs b/desktop/ui/src/remote/host.rs index cb3d0c26b7..7db52fdd65 100644 --- a/desktop/ui/src/remote/host.rs +++ b/desktop/ui/src/remote/host.rs @@ -99,7 +99,7 @@ enum ControlOutcome { fn control_loop(receiver: &IpcReceiver, context: &CefContextHandle, sequence: &SequenceState) -> ControlOutcome { loop { match receiver.recv() { - Ok(HostControlMessage::Input(events)) => context.apply_input(events), + Ok(HostControlMessage::Input(event)) => context.process_input(event), Ok(HostControlMessage::UpdateViewInfo(update)) => context.update_view_info(update), Ok(HostControlMessage::RefreshViewInfo) => context.refresh_view_info(), Ok(HostControlMessage::SendWebMessage(message)) => context.send_web_message(message), diff --git a/desktop/ui/src/remote/messages.rs b/desktop/ui/src/remote/messages.rs index 79e12b966e..5afa339c4a 100644 --- a/desktop/ui/src/remote/messages.rs +++ b/desktop/ui/src/remote/messages.rs @@ -8,7 +8,7 @@ use crate::view::ViewInfoUpdate; #[derive(Serialize, Deserialize)] pub(crate) enum HostControlMessage { - Input(Vec), + Input(InputEvent), UpdateViewInfo(ViewInfoUpdate), RefreshViewInfo, SendWebMessage(Vec),