input capture #4 - pointer accel config

This commit is contained in:
galister 2026-07-15 13:33:40 +09:00
parent 8e87e8cc2a
commit 2633022304
9 changed files with 109 additions and 91 deletions

View File

@ -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.",

View File

@ -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"),

View File

@ -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(),

View File

@ -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<io::Result<()>>,
},
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(

View File

@ -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<MouseIndex> {

View File

@ -188,6 +188,8 @@ pub struct AutoSettings {
pub default_overlay_scale: f32,
pub color_palette: Arc<str>,
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

View File

@ -543,6 +543,11 @@ impl DashInterface<AppState> 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);
}
}
_ => {}
}
}

View File

@ -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<String> {
}
}
fn def_empty() -> Arc<str> {
"".into()
}
fn def_default() -> Arc<str> {
"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<str>,
#[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<String>,
#[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<str>,
#[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,
}

View File

@ -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,