diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index 7bbf534a..5bf2abc6 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -163,7 +163,10 @@ "WL_VIRTUAL": "Wayland virtual input", "WL_VIRTUAL_HELP": "Send events to the Wayland compositor using\nzwlr_virtual_pointer and zwp_virtual_keyboard.\nOnly works reliably on some desktops.\nDoes NOT work on KDE, GNOME or COSMIC." }, + "POINTER_ACCELERATION": "Mouse acceleration", + "POINTER_ACCELERATION_HELP": "On: Adaptive mouse acceleration\nOff: Constant speed", "POINTER_LERP_FACTOR": "Pointer smoothing", + "POINTER_SPEED": "Mouse speed", "REQUIRES_RESTART": "Requires restart", "RESET_PLAYSPACE": "Reset playspace", "RESET_PLAYSPACE_HELP": "Clear the stage space offset.", diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs index 36472a7b..0ba65fde 100644 --- a/dash-frontend/src/tab/settings/mod.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -300,6 +300,7 @@ pub(crate) enum SettingType { ClickFreezeTimeMs, Clock12h, DoubleCursorFix, + EnableWatch, FocusFollowsMouseMode, GridOpacity, HandsfreePointer, @@ -317,11 +318,12 @@ pub(crate) enum SettingType { NotificationsSoundEnabled, DefaultOverlayScale, OpaqueBackground, + MouseAcceleration, PointerLerpFactor, + MouseSpeed, ScreenRenderDown, ScrollSpeed, SnapAngleDeg, - EnableWatch, SetsOnWatch, SpaceDragMultiplier, SpaceDragUnlocked, @@ -346,6 +348,7 @@ pub(crate) enum SettingType { impl SettingType { pub fn change_kind(self) -> ConfigChangeKind { match self { + Self::MouseAcceleration | Self::MouseSpeed => ConfigChangeKind::WvrServerConfig, Self::UseSkybox | Self::UsePassthrough => ConfigChangeKind::EnvironmentBlend, _ => ConfigChangeKind::OverlayConfig, } @@ -370,6 +373,7 @@ impl SettingType { Self::NotificationsEnabled => &mut config.notifications_enabled, Self::NotificationsSoundEnabled => &mut config.notifications_sound_enabled, Self::OpaqueBackground => &mut config.opaque_background, + Self::MouseAcceleration => &mut config.wvr_mouse_acceleration, Self::ScreenRenderDown => &mut config.screen_render_down, Self::SetsOnWatch => &mut config.sets_on_watch, Self::SpaceDragUnlocked => &mut config.space_drag_unlocked, @@ -389,6 +393,7 @@ impl SettingType { Self::GridOpacity => &mut config.grid_opacity, Self::LongPressDuration => &mut config.long_press_duration, Self::PointerLerpFactor => &mut config.pointer_lerp_factor, + Self::MouseSpeed => &mut config.wvr_mouse_speed, Self::ScrollSpeed => &mut config.scroll_speed, Self::SnapAngleDeg => &mut config.snap_angle_deg, Self::SpaceDragMultiplier => &mut config.space_drag_multiplier, @@ -497,6 +502,8 @@ impl SettingType { Self::NotificationsEnabled => Ok("APP_SETTINGS.NOTIFICATIONS_ENABLED"), Self::NotificationsSoundEnabled => Ok("APP_SETTINGS.NOTIFICATIONS_SOUND_ENABLED"), Self::OpaqueBackground => Ok("APP_SETTINGS.OPAQUE_BACKGROUND"), + Self::MouseSpeed => Ok("APP_SETTINGS.POINTER_SPEED"), + Self::MouseAcceleration => Ok("APP_SETTINGS.POINTER_ACCELERATION"), Self::PointerLerpFactor => Ok("APP_SETTINGS.POINTER_LERP_FACTOR"), Self::ScreenRenderDown => Ok("APP_SETTINGS.SCREEN_RENDER_DOWN"), Self::ScrollSpeed => Ok("APP_SETTINGS.SCROLL_SPEED"), @@ -538,6 +545,7 @@ impl SettingType { Self::InputEmulationMethod => Some("APP_SETTINGS.INPUT_EMULATION_METHOD_HELP"), Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"), Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"), + Self::MouseAcceleration => Some("APP_SETTINGS.POINTER_ACCELERATION_HELP"), Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"), Self::SnapAngleDeg => Some("APP_SETTINGS.SNAP_ANGLE_DEG_HELP"), Self::SpaceGravityDamping => Some("APP_SETTINGS.SPACE_GRAVITY_DAMPING_HELP"), diff --git a/dash-frontend/src/tab/settings/tab_controls.rs b/dash-frontend/src/tab/settings/tab_controls.rs index d241e0bb..6a9ad1bc 100644 --- a/dash-frontend/src/tab/settings/tab_controls.rs +++ b/dash-frontend/src/tab/settings/tab_controls.rs @@ -112,6 +112,9 @@ impl State { options_slider_i32(par.mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50)?; + options_slider_f32(par.mp, c, SettingType::MouseSpeed, -1., 1., 0.05)?; + options_checkbox(par.mp, c, SettingType::MouseAcceleration)?; + Ok(State { popup_input_profiles: popup, frontend_tasks: par.frontend_tasks.clone(), diff --git a/wayvr/src/backend/wayvr/input_capture.rs b/wayvr/src/backend/wayvr/input_capture.rs index 911fee32..deccd471 100644 --- a/wayvr/src/backend/wayvr/input_capture.rs +++ b/wayvr/src/backend/wayvr/input_capture.rs @@ -51,31 +51,16 @@ pub enum CapturedEvent { UngrabbedAll, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PointerAccelProfile { - Adaptive, - Flat, -} - -impl PointerAccelProfile { - fn to_libinput(self) -> AccelProfile { - match self { - Self::Adaptive => AccelProfile::Adaptive, - Self::Flat => AccelProfile::Flat, - } - } -} - #[derive(Debug, Clone, Copy)] struct PointerAccelConfig { - profile: PointerAccelProfile, - speed: f64, + accel: bool, + speed: f32, } impl Default for PointerAccelConfig { fn default() -> Self { Self { - profile: PointerAccelProfile::Adaptive, + accel: true, speed: 0.0, } } @@ -143,17 +128,13 @@ impl InputCapture { } /// Set acceleration profile for current and future mice - pub fn set_pointer_accel( - &self, - profile: PointerAccelProfile, - speed: f64, - ) -> anyhow::Result<()> { + pub fn set_pointer_accel(&self, accel: bool, speed: f32) -> anyhow::Result<()> { if !speed.is_finite() || !(-1.0..=1.0).contains(&speed) { anyhow::bail!("pointer acceleration speed must be within -1.0..=1.0"); } self.command_tx - .send(Command::SetPointerAccel { profile, speed }) + .send(Command::SetPointerAccel { accel, speed }) .map_err(|error| anyhow::anyhow!("worker thread unreachable: {error}"))?; Ok(()) @@ -176,8 +157,8 @@ enum Command { response_tx: SyncSender>, }, SetPointerAccel { - profile: PointerAccelProfile, - speed: f64, + accel: bool, + speed: f32, }, Shutdown, } @@ -400,8 +381,8 @@ fn worker_main( let _ = response_tx.send(result); } - Ok(Command::SetPointerAccel { profile, speed }) => { - pointer_accel = PointerAccelConfig { profile, speed }; + Ok(Command::SetPointerAccel { accel, speed }) => { + pointer_accel = PointerAccelConfig { accel, speed }; for mut device in pointer_devices.iter().cloned() { apply_pointer_accel(&mut device, pointer_accel); } @@ -640,11 +621,15 @@ fn apply_pointer_accel(device: &mut LibinputDevice, config: PointerAccelConfig) return; } - let profile = config.profile.to_libinput(); + let profile = if config.accel { + AccelProfile::Adaptive + } else { + AccelProfile::Flat + }; if device.config_accel_profiles().contains(&profile) { let _ = device.config_accel_set_profile(profile); } - let _ = device.config_accel_set_speed(config.speed); + let _ = device.config_accel_set_speed(config.speed as _); } fn drain_pending_libinput_events( diff --git a/wayvr/src/backend/wayvr/mod.rs b/wayvr/src/backend/wayvr/mod.rs index cd209e5a..ad94520d 100644 --- a/wayvr/src/backend/wayvr/mod.rs +++ b/wayvr/src/backend/wayvr/mod.rs @@ -10,7 +10,7 @@ pub mod window; use anyhow::Context; use comp::Application; -use glam::Vec2; +use glam::{DVec2, Vec2}; use process::ProcessVec; use slotmap::SecondaryMap; use smallvec::SmallVec; @@ -57,7 +57,7 @@ use wayland_protocols::xdg::shell::server::xdg_toplevel; use wayvr_ipc::{packet_client::PositionMode, packet_server}; use wgui::{gfx::WGfx, log::LogErr}; use wlx_capture::frame::Transform; -use wlx_common::desktop_finder::DesktopFinder; +use wlx_common::{config::GeneralConfig, desktop_finder::DesktopFinder}; use xkbcommon::xkb; use crate::{ @@ -615,6 +615,14 @@ impl WvrServerState { Ok(tasks) } + pub fn config_changed(&mut self, config: &GeneralConfig) { + if let Some(cap) = self.input_capture.as_mut() { + let _ = cap + .set_pointer_accel(config.wvr_mouse_acceleration, config.wvr_mouse_speed) + .log_err("Could not set mouse accel/speed"); + } + } + pub fn terminate_process( &mut self, process_handle: process::ProcessHandle, @@ -719,6 +727,8 @@ impl WvrServerState { return; }; + let mut mouse_delta = DVec2::ZERO; + for ev in input_capture.drain_events() { match ev { input_capture::CapturedEvent::Key { code, pressed } => { @@ -765,36 +775,7 @@ impl WvrServerState { ); } input_capture::CapturedEvent::PointerMotion { dx, dy } => { - let Some(ref hover) = self.wm.mouse else { - continue; - }; - let new_x = hover.x as f64 + dx; - let new_y = hover.y as f64 + dy; - let new_pos = Vec2::new(new_x as f32, new_y as f32); - - let Some(window) = self.wm.windows.get(&hover.hover_window) else { - continue; - }; - let toplevel = window.toplevel.wl_surface().clone(); - let inner_extent = with_states(&toplevel, |states| { - SurfaceBufWithImage::get_from_surface(states) - .map(|s| s.image.extent_u32arr()) - .unwrap_or([1, 1]) - }); - let Some(hit_ctx) = build_hit_context( - &toplevel, - &self.manager.state.popup_manager, - inner_extent, - ) else { - continue; - }; - let target = hit_ctx.hit_target_at(new_pos); - let focus_target = self.hit_target_to_focus( - target.unwrap_or(WvrHitTarget::Toplevel { pos: new_pos }), - hover.hover_window, - new_pos, - ); - self.send_mouse_move(focus_target, new_pos, hover.hover_window); + mouse_delta += DVec2 { x: dx, y: dy }; } input_capture::CapturedEvent::PointerAxis { horizontal_v120, @@ -819,6 +800,40 @@ impl WvrServerState { } } } + + if mouse_delta.length_squared() > 1e-6 { + 'mouse_update: { + let Some(ref hover) = self.wm.mouse else { + break 'mouse_update; + }; + + let new_x = hover.x as f64 + mouse_delta.x; + let new_y = hover.y as f64 + mouse_delta.y; + let new_pos = Vec2::new(new_x as f32, new_y as f32); + + let Some(window) = self.wm.windows.get(&hover.hover_window) else { + break 'mouse_update; + }; + let toplevel = window.toplevel.wl_surface().clone(); + let inner_extent = with_states(&toplevel, |states| { + SurfaceBufWithImage::get_from_surface(states) + .map(|s| s.image.extent_u32arr()) + .unwrap_or([1, 1]) + }); + let Some(hit_ctx) = + build_hit_context(&toplevel, &self.manager.state.popup_manager, inner_extent) + else { + break 'mouse_update; + }; + let target = hit_ctx.hit_target_at(new_pos); + let focus_target = self.hit_target_to_focus( + target.unwrap_or(WvrHitTarget::Toplevel { pos: new_pos }), + hover.hover_window, + new_pos, + ); + self.send_mouse_move(focus_target, new_pos, hover.hover_window); + } + } } fn button_to_mouse_index(button: u32) -> Option { diff --git a/wayvr/src/config.rs b/wayvr/src/config.rs index b9c9386d..10b4d22d 100644 --- a/wayvr/src/config.rs +++ b/wayvr/src/config.rs @@ -188,6 +188,8 @@ pub struct AutoSettings { pub default_overlay_scale: f32, pub color_palette: Arc, pub snap_angle_deg: f32, + pub wvr_mouse_acceleration: bool, + pub wvr_mouse_speed: f32, } fn get_settings_path() -> PathBuf { @@ -254,6 +256,8 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> { default_overlay_scale: config.default_overlay_scale, color_palette: config.color_palette.clone(), snap_angle_deg: config.snap_angle_deg, + wvr_mouse_acceleration: config.wvr_mouse_acceleration, + wvr_mouse_speed: config.wvr_mouse_speed, }; let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic diff --git a/wayvr/src/overlays/dashboard.rs b/wayvr/src/overlays/dashboard.rs index d8518522..69b54197 100644 --- a/wayvr/src/overlays/dashboard.rs +++ b/wayvr/src/overlays/dashboard.rs @@ -543,6 +543,11 @@ impl DashInterface for DashInterfaceLive { .enqueue(TaskType::OpenXR(OpenXrTask::EnvironmentChanged)); } } + ConfigChangeKind::WvrServerConfig => { + if let Some(wvr_server) = data.wvr_server.as_mut() { + wvr_server.config_changed(&data.session.config); + } + } _ => {} } } diff --git a/wlx-common/src/config.rs b/wlx-common/src/config.rs index 289ba055..29b8295c 100644 --- a/wlx-common/src/config.rs +++ b/wlx-common/src/config.rs @@ -205,10 +205,6 @@ const fn def_true() -> bool { true } -const fn def_false() -> bool { - false -} - const fn def_one() -> f32 { 1.0 } @@ -225,10 +221,6 @@ const fn def_point3() -> f32 { 0.3 } -const fn def_zero() -> f32 { - 0.0 -} - const fn def_osc_port() -> u16 { 9000 } @@ -245,10 +237,6 @@ fn def_timezones() -> Vec { } } -fn def_empty() -> Arc { - "".into() -} - fn def_default() -> Arc { "Default".into() } @@ -287,10 +275,10 @@ pub struct GeneralConfig { #[serde(default = "def_click_freeze_time_ms")] pub click_freeze_time_ms: i32, - #[serde(default = "def_false")] + #[serde(default)] pub invert_scroll_direction_x: bool, - #[serde(default = "def_false")] + #[serde(default)] pub invert_scroll_direction_y: bool, #[serde(default = "def_one")] @@ -329,19 +317,19 @@ pub struct GeneralConfig { #[serde(default = "def_osc_port")] pub osc_out_port: u16, - #[serde(default = "def_false")] + #[serde(default)] pub upright_screen_fix: bool, - #[serde(default = "def_false")] + #[serde(default)] pub double_cursor_fix: bool, #[serde(default = "def_true")] pub enable_watch: bool, - #[serde(default = "def_false")] + #[serde(default)] pub sets_on_watch: bool, - #[serde(default = "def_false")] + #[serde(default)] pub hide_grab_help: bool, #[serde(default)] @@ -356,13 +344,13 @@ pub struct GeneralConfig { #[serde(default = "def_true")] pub allow_sliding: bool, - #[serde(default = "def_false")] + #[serde(default)] pub focus_follows_mouse_mode: bool, - #[serde(default = "def_false")] + #[serde(default)] pub left_handed_mouse: bool, - #[serde(default = "def_false")] + #[serde(default)] pub block_game_input: bool, #[serde(default = "def_true")] @@ -374,7 +362,7 @@ pub struct GeneralConfig { #[serde(default = "def_one")] pub space_drag_multiplier: f32, - #[serde(default = "def_empty")] + #[serde(default)] pub skybox_texture: Arc, #[serde(default = "def_true")] @@ -386,7 +374,7 @@ pub struct GeneralConfig { #[serde(default = "def_max_height")] pub screen_max_height: u16, - #[serde(default = "def_false")] + #[serde(default)] pub screen_render_down: bool, #[serde(default = "def_point3")] @@ -395,10 +383,10 @@ pub struct GeneralConfig { #[serde(default = "def_true")] pub space_drag_unlocked: bool, - #[serde(default = "def_false")] + #[serde(default)] pub space_rotate_unlocked: bool, - #[serde(default = "def_false")] + #[serde(default)] pub space_gravity_enabled: bool, #[serde(default = "def_one")] @@ -413,7 +401,7 @@ pub struct GeneralConfig { #[serde(default = "def_one")] pub space_gravity_ground_friction: f32, - #[serde(default = "def_zero")] + #[serde(default)] pub space_gravity_floor_height: f32, #[serde(default)] @@ -425,7 +413,7 @@ pub struct GeneralConfig { #[serde(default = "def_timezones")] pub timezones: Vec, - #[serde(default = "def_false")] + #[serde(default)] pub clock_12h: bool, #[serde(default)] @@ -470,7 +458,7 @@ pub struct GeneralConfig { #[serde(default)] pub tutorial_graduated: bool, - #[serde(default = "def_empty")] + #[serde(default)] pub whisper_model: Arc, #[serde(default = "def_default")] @@ -478,4 +466,10 @@ pub struct GeneralConfig { #[serde(default)] pub snap_angle_deg: f32, + + #[serde(default = "def_true")] + pub wvr_mouse_acceleration: bool, + + #[serde(default)] + pub wvr_mouse_speed: f32, } diff --git a/wlx-common/src/dash_interface.rs b/wlx-common/src/dash_interface.rs index 5741e78c..e707f867 100644 --- a/wlx-common/src/dash_interface.rs +++ b/wlx-common/src/dash_interface.rs @@ -83,6 +83,7 @@ pub enum ConfigChangeKind { OverlayConfig, EnvironmentBlend, WguiThemeChange, // TODO: does not do anything right now + WvrServerConfig, /// Marks the config for saving but doesn't notify any components #[default] Other,