From 87e7637f8d242d3e8e06d0ba76ecf86bbd8af3c7 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 27 Sep 2026 19:09:46 +0200 Subject: properly implement msmouse-like sensitivity, separate from speed --- int33.h | 36 +++++++++++++++++++++++++++++++----- mousetsr.c | 53 ++++++++++++++++++++++++++++++++++++++++++----------- mousetsr.h | 6 +++++- mousmain.c | 6 +++--- moustest.c | 39 ++++++++++++++++++++++++++++++++++++--- utils.h | 19 ++++++++++++++----- 6 files changed, 131 insertions(+), 28 deletions(-) diff --git a/int33.h b/int33.h index df870be..f9191ce 100644 --- a/int33.h +++ b/int33.h @@ -114,12 +114,38 @@ enum INT33_API { INT33_SAVE_MOUSE_STATUS = 0x16, INT33_LOAD_MOUSE_STATUS = 0x17, - /** Sets both speed and speed-doubling threshold in one call. - * @param bx horizontal speed, cx vertical speed - * @param dx doubling threshold (mickeys per second). */ + /** Sets both sensitivity and speed-doubling threshold in one call. + * @param bx horizontal sensitivity, cx vertical sensitivity + * these are values from 5..100. + * @param dx speed doubling threshold (mickeys per second?). + * Unlike set_mouse_speed, this value is persistent, and survives a reset. + * MS doesn't define what "sensitivity" means here, however MSD does print + * a table of common sensitivity values and their equivalent "factor", + * which is applied to the mouse deltas: + * 100 - 3.5 (i.e. mouse is 3.5x faster) + * 95 - 3.25 + * 90 - 3 + * 85 - 2.75 + * 80 - 2.5 + * 75 - 2.25 + * 70 - 2 + * 65 - 1.75 + * 60 - 1.5 + * 55 - 1.25 + * 50 - 1 (default value, mouse is 1x) + * 45 - 0.875 + * 40 - 0.75 + * 35 - 0.625 + * 30 - 0.5 + * 25 - 0.375 + * 20 - 0.25 + * 15 - 0.125 + * 10 - 0.062 + * 5 - 0.031 (MSD prints 0.31 though? looks buggy as hell). + * @todo verify if the doubling threshold here is in the same units. */ INT33_SET_MOUSE_SENSITIVITY = 0x1A, - /** Gets current speed and speed-doubling threshold. - * @return bx horizontal speed, cx vertical speed + /** Gets current sensitivity and speed-doubling threshold. + * @return bx horizontal sensitivity, cx vertical sensitivity * @return dx doubling threshold (mickeys per second). */ INT33_GET_MOUSE_SENSITIVITY = 0x1B, diff --git a/mousetsr.c b/mousetsr.c index 16c84df..f5caf59 100644 --- a/mousetsr.c +++ b/mousetsr.c @@ -60,6 +60,30 @@ static void bound_position_to_window(void) if (data.pos.y > data.max.y) data.pos.y = data.max.y; } +/** Apply sensitivity to a raw mouse delta. + * @param sens value [5..100] given to set_mouse_sensitivity. + * @param x mouse delta in mickeys + * @return mouse delta scaled by sensitivity + * + * Note this is not acceleration, we are still multiplying the delta by a + * linear factor supposedly derived from the sensivitity [5..100] value. + * Every mouse driver seems to use their own formula for computing this factor. + * I am going with f(x) = x(x+32)/4096 since it looks round and fits decently, + * even with integer math (we don't divide by 4096 until after multiplying by delta). + * @see INT33_SET_MOUSE_SENSITIVITY for what the factor should look like. */ +int16_t apply_sensitivity(int16_t x, uint8_t sens) +{ + uint16_t factor = (sens * (sens + 32)); +#if 1 + // use shift by 12 rather than divide by 4096 + // unsigned math, otherwise negative values would round down when shifted + uint16_t scaled = scaleu_shift(abs(x), 12 /*4096*/, factor); + return x < 0 ? -scaled : scaled; +#else + return scalei(x, 4096, factor); +#endif +} + /** Constraints coordinate value to the desired granularity, * which must be a power of two. */ static inline int16_t snap_to_grid(int16_t val, int16_t granularity) @@ -646,6 +670,10 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, ch } data.total_motion += ax * ax + ay * ay; + // Apply sensitivity now, since unlike acceleration/speed, it affects the raw mickey counters + x = apply_sensitivity(x, data.sensitivity_x); + y = apply_sensitivity(y, data.sensitivity_y); + // Update the raw mickey counters data.delta.x += x; data.delta.y += y; @@ -654,11 +682,11 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, ch data.abs_pos.x = -1; data.abs_pos.y = -1; - // Convert mickeys into pixels, applying speed, sensitivity and acceleration + // Convert mickeys into pixels, applying speed and acceleration // If more than the double speed threshold has been moved in the last second, // double the speed - if (data.cur_speed > data.doubleSpeedThreshold * data.doubleSpeedThreshold) { + if (data.cur_speed > data.double_speed_threshold * data.double_speed_threshold) { x *= 2; y *= 2; } @@ -1172,7 +1200,7 @@ static void reset_mouse_settings() data.mickeysPerLine.x = 8; data.mickeysPerLine.y = 16; - data.doubleSpeedThreshold = 64; + data.double_speed_threshold = 64; data.min.x = 0; data.max.x = data.screen_max.x; data.min.y = 0; @@ -1185,6 +1213,10 @@ static void reset_mouse_settings() data.cursor_hotspot.y = 0; memcpy(data.cursor_graphic, default_cursor_graphic, sizeof(data.cursor_graphic)); + // Sensitivity is persisted through resets, but ensure we init it with 50 if 0. + if (data.sensitivity_x == 0) data.sensitivity_x = 50; + if (data.sensitivity_y == 0) data.sensitivity_y = 50; + refresh_cursor(); // This will hide the cursor and update data.cursor_visible } @@ -1414,7 +1446,7 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_SPEED_DOUBLE_THRESHOLD: dprintf("Mouse set speed double threshold=%d\n", r.w.dx); - data.doubleSpeedThreshold = r.w.dx; + data.double_speed_threshold = r.w.dx; break; case INT33_EXCHANGE_EVENT_HANDLER: dputs("Mouse exchange event handler"); @@ -1446,15 +1478,14 @@ static void int33_handler(union INTPACK r) case INT33_SET_MOUSE_SENSITIVITY: dprintf("Mouse set sensitivity x=%d y=%d threshold=%d\n", r.w.bx, r.w.cx, r.w.dx); - // TODO According to cutemouse, sensitivity != mickeysPerLine - data.mickeysPerLine.x = r.w.bx; - data.mickeysPerLine.y = r.w.cx; - data.doubleSpeedThreshold = r.w.dx; + data.sensitivity_x = BOUND(r.w.bx, 1, 100); + data.sensitivity_y = BOUND(r.w.cx, 1, 100); + data.double_speed_threshold = r.w.dx; break; case INT33_GET_MOUSE_SENSITIVITY: - r.w.bx = data.mickeysPerLine.x; - r.w.cx = data.mickeysPerLine.y; - r.w.dx = data.doubleSpeedThreshold; + r.w.bx = data.sensitivity_x; + r.w.cx = data.sensitivity_y; + r.w.dx = data.double_speed_threshold; break; case INT33_RESET_SETTINGS: dputs("Mouse reset settings"); diff --git a/mousetsr.h b/mousetsr.h index 9b8fdf8..93503c5 100644 --- a/mousetsr.h +++ b/mousetsr.h @@ -161,8 +161,12 @@ typedef struct tsrdata { // Current mouse settings /** Mouse speed, in mickeys per "line" (aka 8 pixels). */ struct point mickeysPerLine; + /** Mouse sensitivity, in 1..100. */ + uint8_t sensitivity_x, sensitivity_y; + /** Mouse sensitivity factor, precalculated. */ + struct point sensitivity_factor; /** Mouse acceleration "double-speed threshold", in mickeys per second. */ - uint16_t doubleSpeedThreshold; + uint16_t double_speed_threshold; /** Current window min coordinates. */ struct point min; /** Current window max coordinates. */ diff --git a/mousmain.c b/mousmain.c index 24801b7..dcc9de9 100644 --- a/mousmain.c +++ b/mousmain.c @@ -354,8 +354,8 @@ static int configure_driver_serial(LPTSRDATA data, int port, unsigned iobase) serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS); dputs("DTR ON, RTS ON"); - // Wait up to 10 ticks (550ms) for the mouse to send something we understand - for (delay = 10; delay > 0; delay--) { + // Wait up to 11 ticks (~600ms) for the mouse to send something we understand + for (delay = 11; delay > 0; delay--) { while (serial_data_ready(iobase)) { uint8_t car = serial_read_data(iobase); @@ -429,7 +429,7 @@ static int configure_driver_serial(LPTSRDATA data, int port, unsigned iobase) } #endif -/// Auto-configure the TSR. Try to detect which mouse we have connected and enable all possible integrations. +/// Configure the TSR. Try to detect which mouse we have connected and enable all possible integrations. static int configure_driver(LPTSRDATA data, unsigned num_ports, int ports[]) { int errs[MAX_PORTS+1] = {0}; diff --git a/moustest.c b/moustest.c index 3270c9c..0d71921 100644 --- a/moustest.c +++ b/moustest.c @@ -142,7 +142,7 @@ void gui_draw_title() const int cols = vidconf.numtextcols; gui_draw_textwindow(1, 1, 1, cols, GUI_COLOR_BAR); - gui_print(GUI_COLOR_BAR, "MOUSTEST ESC to exit, 'm'ode, 'r'eset, 's'how, 'h'ide, s'p'eed"); + gui_print(GUI_COLOR_BAR, "MOUSTEST ESC to exit, 'm'ode, 'r'eset, 's'how, 'h'ide, s'p'eed, sensi't'"); } void gui_draw_status() @@ -584,6 +584,35 @@ void mouse_report() } void mouse_speed() +{ + uint16_t speed_x = 8, speed_y = 16; + bool update = false; + char buffer[16] = {'\0'}; + int ret; + + console_printf("X speed [default=8] ? "); + ret = console_getline(buffer, sizeof(buffer)-1); + if (ret > 0) { + speed_x = atoi(buffer); + update = true; + } + + console_printf("Y speed [default=16] ? "); + ret = console_getline(buffer, sizeof(buffer)-1); + if (ret > 0) { + speed_y = atoi(buffer); + update = true; + } + + if (update) { + int33_set_mouse_speed(speed_x, speed_y); + console_printf("Values updated: %u %u\n", speed_x, speed_y); + } else { + console_printf("No values changed\n"); + } +} + +void mouse_sensitivity() { uint16_t speed_x, speed_y, double_speed_threshold; bool update = false; @@ -592,14 +621,14 @@ void mouse_speed() int33_get_sensitivity(&speed_x, &speed_y, &double_speed_threshold); - console_printf("X speed [default=8, current=%u] ? ", speed_x); + console_printf("X sensitivity [default=50, current=%u] ? ", speed_x); ret = console_getline(buffer, sizeof(buffer)-1); if (ret > 0) { speed_x = atoi(buffer); update = true; } - console_printf("Y speed [default=16, current=%u] ? ", speed_y); + console_printf("Y sensitivity [default=50, current=%u] ? ", speed_y); ret = console_getline(buffer, sizeof(buffer)-1); if (ret > 0) { speed_y = atoi(buffer); @@ -797,6 +826,10 @@ int main(int argc, const char *argv[]) break; case 'p': mouse_speed(); + break; + case 't': + mouse_sensitivity(); + break; case 'd': mouse_debug(); break; diff --git a/utils.h b/utils.h index 9b9db2a..e1258c9 100644 --- a/utils.h +++ b/utils.h @@ -60,7 +60,17 @@ static unsigned scaleu(unsigned x, unsigned srcmax, unsigned dstmax); "div bx" /* ax = dx:ax / srcmax */\ __parm [ax] [bx] [cx] \ __value [ax] \ - __modify [ax dx] + __modify exact [ax dx] + +/** Map x linearly from range [0, srcmax] to [0, 2^dstmax]. + * Equivalent to above, but uses shift instead of division. */ +static unsigned scaleu_shift(unsigned x, unsigned char srcmax, unsigned dstmax); +#pragma aux scaleu_shift = \ + "mul dx" /* dx:ax = x * dstmax */ \ + "shrd ax, dx, cl" /* ax = dx:ax / 2^srcmax */ \ + __parm [ax] [cl] [dx] \ + __value [ax] \ + __modify exact [ax dx] /** Map x linearly from range [0, srcmax] to [0, dstmax]. * Equivalent of (x * dstmax) / srcmax but with 32-bit signed precision. */ @@ -70,12 +80,11 @@ static int scalei(int x, int srcmax, int dstmax); "idiv bx" /* ax = dx:ax / srcmax */ \ __parm [ax] [bx] [cx] \ __value [ax] \ - __modify [ax dx] + __modify exact [ax dx] /** Map x linearly from range [0, srcmax] to [0, dstmax]. - * Equivalent of (x * dstmax) / srcmax but with 32-bit signed precision. - * Division remainder is returned in rem, which should be reused - * in future calls to reduce rounding error. */ + * Equivalent to scalei, but the division remainder is returned in rem, + * which can be reused in future calls to reduce rounding error. */ static int scalei_rem(int x, int srcmax, int dstmax, short *rem); #pragma aux scalei_rem = \ "imul cx" /* dx:ax = x * dstmax */ \ -- cgit v1.2.3