Soumyajyotii Ssarkar <[email protected]> writes:
> Implement multiplier-based scaling with residual accumulation for
> fine-grained mouse wheel and pan sensitivity control in report mode.
>
> Applies divisor based scaling (1, 4, 8, 16) based on negotiated
> multiplier values.
> Then uses residual based approach to prevent precision loss:
>
> residual += delta
> output = residual / divisor
> residual -= output * divisor
>
> Residual state preserved across VM migrations via VMState.
>
> Signed-off-by: Soumyajyotii Ssarkar <[email protected]>
> ---
> hw/input/hid.c | 60 ++++++++++++++++++++++++++++++++++++++++++
> include/hw/input/hid.h | 2 ++
> 2 files changed, 62 insertions(+)
>
> diff --git a/hw/input/hid.c b/hw/input/hid.c
> index d0b7af7c13..9b800faeb0 100644
> --- a/hw/input/hid.c
> +++ b/hw/input/hid.c
> @@ -365,6 +365,7 @@ void hid_pointer_activate(HIDState *hs)
> int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
> {
> int dx, dy, dz, pan, l;
> + int wheel_divisor, pan_divisor;
> int index;
> HIDPointerEvent *e;
>
> @@ -392,6 +393,54 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
> e->pan -= pan;
>
>
> + if (hs->kind == HID_MOUSE && hs->protocol != 0) {
> + switch (hs->ptr.wheel_multiplier & 0x03) {
> + case 0:
> + wheel_divisor = 1;
> + break;
> + case 1:
> + wheel_divisor = 4;
> + break;
> + case 2:
> + wheel_divisor = 8;
> + break;
> + default:
> + wheel_divisor = 16;
> + break;
> + }
> +
> + switch (hs->ptr.pan_multiplier & 0x03) {
> + case 0:
> + pan_divisor = 1;
> + break;
> + case 1:
> + pan_divisor = 4;
> + break;
> + case 2:
> + pan_divisor = 8;
> + break;
> + default:
> + pan_divisor = 16;
> + break;
> + }
> +
> + if (wheel_divisor > 1) {
> + hs->ptr.wheel_residual += dz;
> + dz = hs->ptr.wheel_residual / wheel_divisor;
> + hs->ptr.wheel_residual -= dz * wheel_divisor;
> + } else {
> + hs->ptr.wheel_residual = 0;
> + }
> +
> + if (pan_divisor > 1) {
> + hs->ptr.pan_residual += pan;
> + pan = hs->ptr.pan_residual / pan_divisor;
> + hs->ptr.pan_residual -= pan * pan_divisor;
> + } else {
> + hs->ptr.pan_residual = 0;
> + }
> + }
> +
> if (hs->n &&
> !e->dz &&
> !e->pan &&
> @@ -405,6 +454,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
> dz = 0 - dz;
> pan = 0 - pan;
> l = 0;
> +
> switch (hs->kind) {
> case HID_MOUSE:
> if (hs->protocol == 0) {
> @@ -528,6 +578,11 @@ void hid_reset(HIDState *hs)
> case HID_MOUSE:
> case HID_TABLET:
> memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
> + hs->ptr.mouse_grabbed = 0;
> + hs->ptr.wheel_multiplier = 0;
> + hs->ptr.pan_multiplier = 0;
> + hs->ptr.wheel_residual = 0;
> + hs->ptr.pan_residual = 0;
> break;
> }
> hs->head = 0;
> @@ -619,6 +674,7 @@ static const VMStateDescription vmstate_hid_ptr_queue = {
> VMSTATE_INT32(xdx, HIDPointerEvent),
> VMSTATE_INT32(ydy, HIDPointerEvent),
> VMSTATE_INT32(dz, HIDPointerEvent),
> + VMSTATE_INT32(pan, HIDPointerEvent),
> VMSTATE_INT32(buttons_state, HIDPointerEvent),
> VMSTATE_END_OF_LIST()
> }
> @@ -636,6 +692,10 @@ const VMStateDescription vmstate_hid_ptr_device = {
> VMSTATE_UINT32(n, HIDState),
> VMSTATE_INT32(protocol, HIDState),
> VMSTATE_UINT8(idle, HIDState),
> + VMSTATE_UINT8(ptr.wheel_multiplier, HIDState),
> + VMSTATE_UINT8(ptr.pan_multiplier, HIDState),
> + VMSTATE_INT32(ptr.wheel_residual, HIDState),
> + VMSTATE_INT32(ptr.pan_residual, HIDState),
> VMSTATE_END_OF_LIST(),
If you update migration state you also need to ensure that older systems
can be migrated to newer ones. There are some docs in docs/devel that
give some guidance on handling these cases.
> }
> };
> diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
> index 1a1941d371..db902fa023 100644
> --- a/include/hw/input/hid.h
> +++ b/include/hw/input/hid.h
> @@ -25,6 +25,8 @@ typedef struct HIDMouseState {
> int mouse_grabbed;
> uint8_t wheel_multiplier;
> uint8_t pan_multiplier;
> + int32_t wheel_residual;
> + int32_t pan_residual;
> } HIDMouseState;
>
> typedef struct HIDKeyboardState {
--
Alex Bennée
Virtualisation Tech Lead @ Linaro