new isometric mode - build fails

mouse-isometric-mode
nirenjan 2026-04-01 13:51:41 -07:00
parent 6ec133488b
commit 434f64c60f
7 changed files with 232 additions and 68 deletions

View File

@ -82,12 +82,44 @@ LED=128
# Enabled controls whether the virtual mouse is enabled or not. # Enabled controls whether the virtual mouse is enabled or not.
Enabled=yes Enabled=yes
# Speed is proportional to the speed of updates to the virtual mouse # Sensitivity is the sensitivity percentage of the virtual mouse. This
# replaces the old Speed option, and is a percentage value by which to
# scale the input. The sensitivity can vary from 10% to 500%.
Sensitivity=100
# DEPRECATED: Speed is proportional to the speed of updates to the virtual mouse
# This used a calculation with delays and multiplication factors to simulate
# the mouse moves, but it felt choppy at lower speeds.
Speed=0 Speed=0
# ReverseScroll reverses the direction of the virtual scroll wheel # ReverseScroll reverses the direction of the virtual scroll wheel
ReverseScroll=no ReverseScroll=no
# Isometric mode controls if the mouse movement is computed based on
# both X and Y movements. If enabled, the behavior is similar to the
# mouse nubs found on some laptops. Otherwise, the X and Y movements
# are independent of each other.
IsometricMode=no
# Curve factor controls the speed curve in an exponential manner, so
# that the user can get finer control at the lower end of motion, while
# increasing speeds at the upper end. Values range from 1-5, with the
# following descriptions. Values are clamped in this range.
# 1: Linear motion - no curve
# 2: Soft curve: slight dampening in the lower ranges
# 3: Standard: Feels like a Thinkpad
# 4: Precision: heavy dampening in lower ranges, high speed elsewhere
# 5: Aggressive: "sniper" mode in the lower rnages, "flick" elsewhere
CurveFactor=3
# Deadzone is a configurable value from 0-11, with 0 being no deadzone
# and the deadzone size increasing with increasing values. This is useful
# when there is a loose thumbstick and you want to restrict the motion
# when there's no user input. A deadzone of 0 is perfectly fine for a
# new joystick, but keep in mind that the higher values will require
# you to push more to get any motion out of the virtual mouse.
Deadzone=0
###################################################################### ######################################################################
# Profiles - only valid on Linux # Profiles - only valid on Linux
###################################################################### ######################################################################

View File

@ -71,13 +71,26 @@ CFG(Brightness, LED, brightness[1], int, 128)
// Enabled controls whether the virtual mouse is enabled or not. // Enabled controls whether the virtual mouse is enabled or not.
CFG(Mouse, Enabled, mouse_enabled, bool, true) CFG(Mouse, Enabled, mouse_enabled, bool, true)
// Speed is a value that is proportional to the speed of updates to the // DEPRECATED: Speed is a value that is proportional to the speed of updates to
// virtual mouse // the virtual mouse
CFG(Mouse, Speed, mouse_speed, int, 0) CFG(Mouse, Speed, mouse_speed, int, 0)
// Sensitivity is a percentage that is used to scale the speed of the virtual
// mouse. This replaces the old speed value.
CFG(Mouse, Sensitivity, mouse_sensitivity, int, 0)
// ReverseScroll controls the scrolling direction // ReverseScroll controls the scrolling direction
CFG(Mouse, ReverseScroll, mouse_reverse_scroll, bool, false) CFG(Mouse, ReverseScroll, mouse_reverse_scroll, bool, false)
// IsometricMode controls whether to use linear or isometric speed calculations
CFG(Mouse, IsometricMode, mouse_isometric_mode, bool, false)
// CurveFactor controls the speed curve
CFG(Mouse, CurveFactor, mouse_curve_factor, int, 3)
// Deadzone controls the deadzone range for the thumbstick
CFG(Mouse, Deadzone, mouse_deadzone_factor, int, 0)
/********************************************************************** /**********************************************************************
* Profiles - only valid on Linux * Profiles - only valid on Linux
*********************************************************************/ *********************************************************************/

View File

@ -39,7 +39,11 @@ struct x52d_config {
bool mouse_enabled; bool mouse_enabled;
int mouse_speed; int mouse_speed;
int mouse_sensitivity;
bool mouse_reverse_scroll; bool mouse_reverse_scroll;
bool mouse_isometric_mode;
int mouse_curve_factor;
int mouse_deadzone_factor;
bool clutch_enabled; bool clutch_enabled;
bool clutch_latched; bool clutch_latched;
@ -72,7 +76,11 @@ void x52d_cfg_set_Brightness_MFD(uint16_t param);
void x52d_cfg_set_Brightness_LED(uint16_t param); void x52d_cfg_set_Brightness_LED(uint16_t param);
void x52d_cfg_set_Mouse_Enabled(bool param); void x52d_cfg_set_Mouse_Enabled(bool param);
void x52d_cfg_set_Mouse_Speed(int param); void x52d_cfg_set_Mouse_Speed(int param);
void x52d_cfg_set_Mouse_Sensitivity(int param);
void x52d_cfg_set_Mouse_ReverseScroll(bool param); void x52d_cfg_set_Mouse_ReverseScroll(bool param);
void x52d_cfg_set_Mouse_IsometricMode(bool param);
void x52d_cfg_set_Mouse_CurveFactor(int param);
void x52d_cfg_set_Mouse_Deadzone(int param);
void x52d_cfg_set_Profiles_Directory(char* param); void x52d_cfg_set_Profiles_Directory(char* param);
void x52d_cfg_set_Profiles_ClutchEnabled(bool param); void x52d_cfg_set_Profiles_ClutchEnabled(bool param);
void x52d_cfg_set_Profiles_ClutchLatched(bool param); void x52d_cfg_set_Profiles_ClutchLatched(bool param);

View File

@ -9,6 +9,7 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <math.h>
#define PINELOG_MODULE X52D_MOD_MOUSE #define PINELOG_MODULE X52D_MOD_MOUSE
#include "pinelog.h" #include "pinelog.h"
@ -20,11 +21,41 @@
#define DEFAULT_MOUSE_DELAY 70000 #define DEFAULT_MOUSE_DELAY 70000
#define MOUSE_DELAY_DELTA 5000 #define MOUSE_DELAY_DELTA 5000
#define MOUSE_DELAY_MIN 10000 #define MOUSE_DELAY_MIN 10000
#define MOUSE_MULT_FACTOR 4
#define MAX_MOUSE_MULT 5 #define MAX_MOUSE_MULT 5
#define MIN_SENSITIVITY 10
#define MAX_SENSITIVITY 500
static const double MOUSE_CURVE_FACTORS[5] = {
1.0, 1.2, 1.5, 1.8, 2.2
};
static const double MOUSE_DEADZONES[12] = {
0.0, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5
};
volatile int mouse_delay = DEFAULT_MOUSE_DELAY;
volatile int mouse_mult = MOUSE_MULT_FACTOR;
volatile int mouse_scroll_dir = 1; volatile int mouse_scroll_dir = 1;
volatile bool mouse_isometric_mode = false;
volatile double mouse_curve_factor = MOUSE_CURVE_FACTORS[2]; // default
volatile double mouse_deadzone = MOUSE_DEADZONES[0];
volatile int mouse_sensitivity = 0;
static int clamp_int(const char *description, int value, int min, int max)
{
if (value < min) {
PINELOG_DEBUG(_("Clamping %s value %d to range [%d..%d]"),
description, value, min, max);
return min;
}
if (value > max) {
PINELOG_DEBUG(_("Clamping %s value %d to range [%d..%d]"),
description, value, min, max);
return max;
}
return value;
}
void x52d_cfg_set_Mouse_Enabled(bool enabled) void x52d_cfg_set_Mouse_Enabled(bool enabled)
{ {
@ -35,17 +66,21 @@ void x52d_cfg_set_Mouse_Enabled(bool enabled)
void x52d_cfg_set_Mouse_Speed(int speed) void x52d_cfg_set_Mouse_Speed(int speed)
{ {
// DEPRECATED, calculate the sensitivity instead
int new_delay; int new_delay;
int new_mult; int new_mult;
int max_base_speed = (DEFAULT_MOUSE_DELAY - MOUSE_DELAY_MIN) / MOUSE_DELAY_DELTA; int max_base_speed = (DEFAULT_MOUSE_DELAY - MOUSE_DELAY_MIN) / MOUSE_DELAY_DELTA;
int max_speed = max_base_speed + MAX_MOUSE_MULT * MOUSE_MULT_FACTOR; int max_speed = max_base_speed + MAX_MOUSE_MULT * MOUSE_MULT_FACTOR;
if (speed < 0 || speed > max_speed) { double sensitivity;
PINELOG_INFO(_("Ignoring mouse speed %d outside supported range (0-%d)"),
speed, max_speed); if (mouse_sensitivity == 0) {
return; PINELOG_WARN(_("Config option 'mouse.speed' is DEPRECATED. Please use 'mouse.sensitivity' instead"));
} else if (speed <= max_base_speed) { }
speed = clamp_int("mouse speed", speed, 0, max_speed);
if (speed <= max_base_speed) {
new_delay = DEFAULT_MOUSE_DELAY - speed * MOUSE_DELAY_DELTA; new_delay = DEFAULT_MOUSE_DELAY - speed * MOUSE_DELAY_DELTA;
new_mult = MOUSE_MULT_FACTOR; new_mult = MOUSE_MULT_FACTOR;
} else { } else {
@ -54,10 +89,12 @@ void x52d_cfg_set_Mouse_Speed(int speed)
new_mult = MOUSE_MULT_FACTOR + (speed - max_base_speed); new_mult = MOUSE_MULT_FACTOR + (speed - max_base_speed);
} }
PINELOG_DEBUG(_("Setting mouse speed to %d (delay %d ms, multiplier %f)"), sensitivity = round(10000000.0 / new_delay * new_mult / (double)MOUSE_MULT_FACTOR);
speed, new_delay / 1000, new_mult / (double)MOUSE_MULT_FACTOR);
mouse_delay = new_delay; PINELOG_INFO(_("Migrating legacy mouse speed '%d' to sensitivity '%d%%'"),
mouse_mult = new_mult; speed, (int)sensitivity);
mouse_sensitivity = clamp_int(_("speed -> sensitivity"), (int)sensitivity,
MIN_SENSITIVITY, MAX_SENSITIVITY);
} }
void x52d_cfg_set_Mouse_ReverseScroll(bool enabled) void x52d_cfg_set_Mouse_ReverseScroll(bool enabled)
@ -71,3 +108,36 @@ void x52d_cfg_set_Mouse_ReverseScroll(bool enabled)
mouse_scroll_dir = 1; mouse_scroll_dir = 1;
} }
} }
void x52d_config_set_Mouse_IsometricMode(bool enabled)
{
PINELOG_DEBUG(_("Setting mouse isometric mode to %s"),
enabled ? _("on") : _("off"));
mouse_isometric_mode = enabled;
}
void x52d_config_set_Mouse_Sensitivity(int factor)
{
mouse_sensitivity = clamp_int(_("sensitivity"), factor,
MIN_SENSITIVITY, MAX_SENSITIVITY);
PINELOG_DEBUG(_("Setting mouse sensitivity to %d%%"), mouse_sensitivity);
}
void x52d_config_set_Mouse_CurveFactor(int factor)
{
// Factor ranges from 1-5, clamp it in this range
factor = clamp_int(_("curve factor"), factor, 1, 5);
mouse_curve_factor = MOUSE_CURVE_FACTORS[factor-1];
PINELOG_DEBUG(_("Setting mouse curve factor to %f"), mouse_curve_factor);
}
void x52d_config_set_Mouse_Deadzone(int factor)
{
// Factor ranges from 0-12, clamp it in this range
factor = clamp_int(_("deadzone factor"), factor, 0, 11);
mouse_deadzone = MOUSE_DEADZONES[factor];
PINELOG_DEBUG(_("Setting mouse deadzone to %f"), mouse_deadzone);
}

View File

@ -12,11 +12,11 @@
#include <stdbool.h> #include <stdbool.h>
#include "libx52io.h" #include "libx52io.h"
extern volatile int mouse_delay; extern volatile bool mouse_isometric_mode;
extern volatile int mouse_mult;
extern volatile int mouse_scroll_dir; extern volatile int mouse_scroll_dir;
extern volatile double mouse_curve_factor;
#define MOUSE_MULT_FACTOR 4 extern volatile double mouse_deadzone;
extern volatile int mouse_sensitivity;
void x52d_mouse_thread_control(bool enabled); void x52d_mouse_thread_control(bool enabled);
void x52d_mouse_handler_init(void); void x52d_mouse_handler_init(void);

View File

@ -11,6 +11,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
#include <math.h>
#include "libx52io.h" #include "libx52io.h"
#include "vkm.h" #include "vkm.h"
@ -85,40 +86,94 @@ static int report_wheel(void)
return (rc == VKM_SUCCESS); return (rc == VKM_SUCCESS);
} }
static int get_axis_val(int index) static inline int fsgn(double f)
{ {
int axis_val = new_report.axis[index]; return (f >= 0 ? 1 : -1);
/*
* Axis value ranges from 0 to 15, with the default midpoint at 8.
* We need to translate this to a range of -7 to +7. Since the midpoint
* is slightly off-center, we will shift the values left, and subtract
* 15, effectively, giving us a range of -15 to +15. Shifting right again
* will reduce the range to -7 to +7, and effectively ignore the reported
* values of 7 and 8.
*/
axis_val = ((axis_val << 1) - 15) >> 1;
/*
* Factor in the multiplicative factor for the axis. This deliberately
* uses integer division, since the uinput event only accepts integers.
* For the speed purposes, this should be good enough.
*/
axis_val = (axis_val * mouse_mult) / MOUSE_MULT_FACTOR;
return axis_val;
} }
static int report_axis(void) static int report_axis(void)
{ {
vkm_result rc; #define MAX_TICK_SPEED 250.0
int dx = get_axis_val(LIBX52IO_AXIS_THUMBX);
int dy = get_axis_val(LIBX52IO_AXIS_THUMBY);
rc = vkm_mouse_move(mouse_context, dx, dy); static double accum_x = 0.0;
static double accum_y = 0.0;
/* Center raw HID values (0,15) => (-8, 7) */
int dx = new_report.axis[LIBX52IO_AXIS_THUMBX] - 8;
int dy = new_report.axis[LIBX52IO_AXIS_THUMBY] - 8;
/* Calculate radial magnitude */
double mag = sqrt((double)(dx * dx + dy * dy));
/* Radial deadzone check */
if (mag <= mouse_deadzone) {
accum_x = 0.0;
accum_y = 0.0;
return 0;
}
/* Calculate gain */
double gain = (double)mouse_sensitivity / 100.0;
/* Normalize magnitude */
double adj_mag = mag - mouse_deadzone;
double out_x = 0.0;
double out_y = 0.0;
if (mouse_isometric_mode) {
/* Isometric mode: speed is a function of total distance */
double speed = gain * pow(adj_mag, mouse_curve_factor);
/* Clamp total speed before breaking into components */
if (speed > MAX_TICK_SPEED) {
speed = MAX_TICK_SPEED;
}
/* Unit vector * speed */
out_x = (dx / mag) * speed;
out_y = (dy / mag) * speed;
} else {
/* Linear mode: speed is independently calculated for X & Y axes */
double ratio = adj_mag / mag;
double cur_x = dx * ratio;
double cur_y = dy * ratio;
out_x = fsgn(cur_x) * gain * pow(fabs(cur_x), mouse_curve_factor);
out_y = fsgn(cur_y) * gain * pow(fabs(cur_y), mouse_curve_factor);
/* Clamp individual axis speeds */
if (fabs(out_x) > MAX_TICK_SPEED) {
out_x = fsgn(out_x) * MAX_TICK_SPEED;
}
if (fabs(out_y) > MAX_TICK_SPEED) {
out_y = fsgn(out_y) * MAX_TICK_SPEED;
}
}
/* Accumulate movement and independent resets */
accum_x += out_x;
accum_y += out_y;
if (dx == 0) {
accum_x = 0.0;
}
if (dy == 0) {
accum_y = 0.0;
}
/* Extract integer values for VKM injection */
int move_x = (int)accum_x;
int move_y = (int)accum_y;
accum_x -= move_x;
accum_y -= move_y;
vkm_result rc;
rc = vkm_mouse_move(mouse_context, move_x, move_y);
if (rc != VKM_SUCCESS && rc != VKM_ERROR_NO_CHANGE) { if (rc != VKM_SUCCESS && rc != VKM_ERROR_NO_CHANGE) {
PINELOG_ERROR(_("Error %d writing mouse axis event (dx %d, dy %d)"), PINELOG_ERROR(_("Error %d writing mouse axis event (dx %d, dy %d)"),
rc, dx, dy); rc, move_x, move_y);
} }
return (rc == VKM_SUCCESS); return (rc == VKM_SUCCESS);
@ -154,7 +209,7 @@ static void * x52_mouse_thr(void *param)
report_sync(); report_sync();
} }
usleep(mouse_delay); usleep(10000);
} }
return NULL; return NULL;

View File

@ -44,75 +44,61 @@ static void test_mouse_thread_disabled(void **state)
x52d_cfg_set_Mouse_Enabled(false); x52d_cfg_set_Mouse_Enabled(false);
} }
/* The following tests are dependent on the values in x52d_mouse.c */
static void test_mouse_speed_negative(void **state) static void test_mouse_speed_negative(void **state)
{ {
(void)state; (void)state;
int orig_mouse_delay = mouse_delay;
int orig_mouse_mult = mouse_mult;
x52d_cfg_set_Mouse_Speed(-1); x52d_cfg_set_Mouse_Speed(-1);
assert_int_equal(mouse_delay, orig_mouse_delay); assert_int_equal(mouse_sensitivity, 14);
assert_int_equal(mouse_mult, orig_mouse_mult);
} }
/* The following tests are dependent on the values in x52d_mouse.c */
static void test_mouse_speed_0(void **state) static void test_mouse_speed_0(void **state)
{ {
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(0); x52d_cfg_set_Mouse_Speed(0);
assert_int_equal(mouse_delay, 70000); assert_int_equal(mouse_sensitivity, 14);
assert_int_equal(mouse_mult, 4);
} }
static void test_mouse_speed_mid_base(void **state) static void test_mouse_speed_mid_base(void **state)
{ {
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(6); x52d_cfg_set_Mouse_Speed(6);
assert_int_equal(mouse_delay, 40000); assert_int_equal(mouse_sensitivity, 25);
assert_int_equal(mouse_mult, 4);
} }
static void test_mouse_speed_max_base(void **state) static void test_mouse_speed_max_base(void **state)
{ {
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(12); x52d_cfg_set_Mouse_Speed(12);
assert_int_equal(mouse_delay, 10000); assert_int_equal(mouse_sensitivity, 100);
assert_int_equal(mouse_mult, 4);
} }
static void test_mouse_speed_min_hyper(void **state) static void test_mouse_speed_min_hyper(void **state)
{ {
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(13); x52d_cfg_set_Mouse_Speed(13);
assert_int_equal(mouse_delay, 10000); assert_int_equal(mouse_sensitivity, 125);
assert_int_equal(mouse_mult, 5);
} }
static void test_mouse_speed_mid_hyper(void **state) static void test_mouse_speed_mid_hyper(void **state)
{ {
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(22); x52d_cfg_set_Mouse_Speed(22);
assert_int_equal(mouse_delay, 10000); assert_int_equal(mouse_sensitivity, 350);
assert_int_equal(mouse_mult, 14);
} }
static void test_mouse_speed_max_hyper(void **state) static void test_mouse_speed_max_hyper(void **state)
{ {
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(32); x52d_cfg_set_Mouse_Speed(32);
assert_int_equal(mouse_delay, 10000); assert_int_equal(mouse_sensitivity, 600);
assert_int_equal(mouse_mult, 24);
} }
static void test_mouse_speed_above_max(void **state) static void test_mouse_speed_above_max(void **state)
{ {
int orig_mouse_delay = mouse_delay;
int orig_mouse_mult = mouse_mult;
(void)state; (void)state;
x52d_cfg_set_Mouse_Speed(33); x52d_cfg_set_Mouse_Speed(33);
assert_int_equal(mouse_delay, orig_mouse_delay); assert_int_equal(mouse_sensitivity, 600);
assert_int_equal(mouse_mult, orig_mouse_mult);
} }
static void test_mouse_reverse_scroll_enabled(void **state) static void test_mouse_reverse_scroll_enabled(void **state)