From 2a81497d34967cbca610bf52ab31a0ccfce81c60 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 9 Sep 2025 20:58:34 +0000 Subject: [PATCH] Improvements: input performance --- hidrpc.go | 2 +- input_rpc.go | 141 +++++++++++++++++++++++---------------------------- usb.go | 4 +- 3 files changed, 66 insertions(+), 81 deletions(-) diff --git a/hidrpc.go b/hidrpc.go index 74fe687f..c5597096 100644 --- a/hidrpc.go +++ b/hidrpc.go @@ -35,7 +35,7 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) { logger.Warn().Err(err).Msg("failed to get pointer report") return } - rpcErr = rpcAbsMouseReport(pointerReport.X, pointerReport.Y, pointerReport.Button) + rpcErr = rpcAbsMouseReport(uint16(pointerReport.X), uint16(pointerReport.Y), pointerReport.Button) case hidrpc.TypeMouseReport: mouseReport, err := message.MouseReport() if err != nil { diff --git a/input_rpc.go b/input_rpc.go index dabc405a..3e7b52ab 100644 --- a/input_rpc.go +++ b/input_rpc.go @@ -35,18 +35,19 @@ type InputRPCRequest struct { Method string `json:"method"` ID any `json:"id,omitempty"` // Union of all possible input parameters - only relevant fields are populated + // Fields ordered for optimal 32-bit alignment: slice pointers, 2-byte fields, 1-byte fields Params struct { - // Keyboard parameters - Modifier *uint8 `json:"modifier,omitempty"` - Keys *[]uint8 `json:"keys,omitempty"` - // Mouse parameters - X *int `json:"x,omitempty"` - Y *int `json:"y,omitempty"` - Dx *int8 `json:"dx,omitempty"` - Dy *int8 `json:"dy,omitempty"` - Buttons *uint8 `json:"buttons,omitempty"` - // Wheel parameters - WheelY *int8 `json:"wheelY,omitempty"` + // Slice pointers (4 bytes on 32-bit ARM) + Keys *[]uint8 `json:"keys,omitempty"` + // 2-byte fields grouped together + X *uint16 `json:"x,omitempty"` + Y *uint16 `json:"y,omitempty"` + // 1-byte fields grouped together for optimal packing + Modifier *uint8 `json:"modifier,omitempty"` + Dx *int8 `json:"dx,omitempty"` + Dy *int8 `json:"dy,omitempty"` + Buttons *uint8 `json:"buttons,omitempty"` + WheelY *int8 `json:"wheelY,omitempty"` } `json:"params,omitempty"` } @@ -77,15 +78,7 @@ func validateKeysArray(params map[string]interface{}, methodName string) ([]uint keys[i] = uint8(intVal) continue } - // Fallback to float64 for compatibility with existing clients - if floatVal, ok := keyInterface.(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < 0 || intVal > 255 { - return nil, fmt.Errorf("%s: key at index %d value %v invalid for uint8 (must be integer 0-255)", methodName, i, floatVal) - } - keys[i] = uint8(intVal) - continue - } + return nil, fmt.Errorf("%s: key at index %d must be a number, got %T", methodName, i, keyInterface) } return keys, nil @@ -103,11 +96,11 @@ type KeyboardReportParams struct { } // AbsMouseReportParams represents parameters for absolute mouse positioning -// Matches rpcAbsMouseReport(x, y int, buttons uint8) +// Matches rpcAbsMouseReport(x, y uint16, buttons uint8) type AbsMouseReportParams struct { - X int `json:"x"` // Absolute X coordinate (0-32767) - Y int `json:"y"` // Absolute Y coordinate (0-32767) - Buttons uint8 `json:"buttons"` // Mouse button state bitmask + X uint16 `json:"x"` // Absolute X coordinate (0-32767) + Y uint16 `json:"y"` // Absolute Y coordinate (0-32767) + Buttons uint8 `json:"buttons"` // Mouse button state bitmask } // RelMouseReportParams represents parameters for relative mouse movement @@ -150,7 +143,7 @@ func handleInputRPCUltraFast(data []byte) (interface{}, error) { return nil, fmt.Errorf("absMouseReport: missing required parameters") } x, y, buttons := *request.Params.X, *request.Params.Y, *request.Params.Buttons - if x < 0 || x > 32767 || y < 0 || y > 32767 { + if x > 32767 || y > 32767 { return nil, fmt.Errorf("absMouseReport: coordinates out of range") } return nil, rpcAbsMouseReport(x, y, buttons) @@ -183,11 +176,10 @@ func handleKeyboardReportDirect(params map[string]interface{}) (interface{}, err } modifier = uint8(intVal) } else if floatVal, ok := params["modifier"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < 0 || intVal > 255 { + if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 { return nil, fmt.Errorf("keyboardReport: modifier value %v invalid", floatVal) } - modifier = uint8(intVal) + modifier = uint8(floatVal) } else { return nil, fmt.Errorf("keyboardReport: modifier must be a number") } @@ -205,36 +197,34 @@ func handleKeyboardReportDirect(params map[string]interface{}) (interface{}, err // Direct handler for absolute mouse reports // Ultra-optimized path with inlined validation for maximum performance func handleAbsMouseReportDirect(params map[string]interface{}) (interface{}, error) { - // Inline x coordinate validation - var x int - if intVal, ok := params["x"].(int); ok { + // Inline x coordinate validation - check float64 first (most common JSON number type) + var x uint16 + if floatVal, ok := params["x"].(float64); ok { + if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 32767 { + return nil, fmt.Errorf("absMouseReport: x value %v invalid", floatVal) + } + x = uint16(floatVal) + } else if intVal, ok := params["x"].(int); ok { if intVal < 0 || intVal > 32767 { return nil, fmt.Errorf("absMouseReport: x value %d out of range [0-32767]", intVal) } - x = intVal - } else if floatVal, ok := params["x"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < 0 || intVal > 32767 { - return nil, fmt.Errorf("absMouseReport: x value %v invalid", floatVal) - } - x = intVal + x = uint16(intVal) } else { return nil, fmt.Errorf("absMouseReport: x must be a number") } - // Inline y coordinate validation - var y int - if intVal, ok := params["y"].(int); ok { + // Inline y coordinate validation - check float64 first (most common JSON number type) + var y uint16 + if floatVal, ok := params["y"].(float64); ok { + if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 32767 { + return nil, fmt.Errorf("absMouseReport: y value %v invalid", floatVal) + } + y = uint16(floatVal) + } else if intVal, ok := params["y"].(int); ok { if intVal < 0 || intVal > 32767 { return nil, fmt.Errorf("absMouseReport: y value %d out of range [0-32767]", intVal) } - y = intVal - } else if floatVal, ok := params["y"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < 0 || intVal > 32767 { - return nil, fmt.Errorf("absMouseReport: y value %v invalid", floatVal) - } - y = intVal + y = uint16(intVal) } else { return nil, fmt.Errorf("absMouseReport: y must be a number") } @@ -247,11 +237,10 @@ func handleAbsMouseReportDirect(params map[string]interface{}) (interface{}, err } buttons = uint8(intVal) } else if floatVal, ok := params["buttons"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < 0 || intVal > 255 { + if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 { return nil, fmt.Errorf("absMouseReport: buttons value %v invalid", floatVal) } - buttons = uint8(intVal) + buttons = uint8(floatVal) } else { return nil, fmt.Errorf("absMouseReport: buttons must be a number") } @@ -262,53 +251,50 @@ func handleAbsMouseReportDirect(params map[string]interface{}) (interface{}, err // Direct handler for relative mouse reports // Ultra-optimized path with inlined validation for maximum performance func handleRelMouseReportDirect(params map[string]interface{}) (interface{}, error) { - // Inline dx validation + // Inline dx validation - check float64 first (most common JSON number type) var dx int8 - if intVal, ok := params["dx"].(int); ok { + if floatVal, ok := params["dx"].(float64); ok { + if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 { + return nil, fmt.Errorf("relMouseReport: dx value %v invalid", floatVal) + } + dx = int8(floatVal) + } else if intVal, ok := params["dx"].(int); ok { if intVal < -128 || intVal > 127 { return nil, fmt.Errorf("relMouseReport: dx value %d out of range [-128 to 127]", intVal) } dx = int8(intVal) - } else if floatVal, ok := params["dx"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < -128 || intVal > 127 { - return nil, fmt.Errorf("relMouseReport: dx value %v invalid", floatVal) - } - dx = int8(intVal) } else { return nil, fmt.Errorf("relMouseReport: dx must be a number") } - // Inline dy validation + // Inline dy validation - check float64 first (most common JSON number type) var dy int8 - if intVal, ok := params["dy"].(int); ok { + if floatVal, ok := params["dy"].(float64); ok { + if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 { + return nil, fmt.Errorf("relMouseReport: dy value %v invalid", floatVal) + } + dy = int8(floatVal) + } else if intVal, ok := params["dy"].(int); ok { if intVal < -128 || intVal > 127 { return nil, fmt.Errorf("relMouseReport: dy value %d out of range [-128 to 127]", intVal) } dy = int8(intVal) - } else if floatVal, ok := params["dy"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < -128 || intVal > 127 { - return nil, fmt.Errorf("relMouseReport: dy value %v invalid", floatVal) - } - dy = int8(intVal) } else { return nil, fmt.Errorf("relMouseReport: dy must be a number") } - // Inline buttons validation + // Inline buttons validation - check float64 first (most common JSON number type) var buttons uint8 - if intVal, ok := params["buttons"].(int); ok { + if floatVal, ok := params["buttons"].(float64); ok { + if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 { + return nil, fmt.Errorf("relMouseReport: buttons value %v invalid", floatVal) + } + buttons = uint8(floatVal) + } else if intVal, ok := params["buttons"].(int); ok { if intVal < 0 || intVal > 255 { return nil, fmt.Errorf("relMouseReport: buttons value %d out of range [0-255]", intVal) } buttons = uint8(intVal) - } else if floatVal, ok := params["buttons"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < 0 || intVal > 255 { - return nil, fmt.Errorf("relMouseReport: buttons value %v invalid", floatVal) - } - buttons = uint8(intVal) } else { return nil, fmt.Errorf("relMouseReport: buttons must be a number") } @@ -327,11 +313,10 @@ func handleWheelReportDirect(params map[string]interface{}) (interface{}, error) } wheelY = int8(intVal) } else if floatVal, ok := params["wheelY"].(float64); ok { - intVal := int(floatVal) - if floatVal != float64(intVal) || intVal < -128 || intVal > 127 { + if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 { return nil, fmt.Errorf("wheelReport: wheelY value %v invalid", floatVal) } - wheelY = int8(intVal) + wheelY = int8(floatVal) } else { return nil, fmt.Errorf("wheelReport: wheelY must be a number") } diff --git a/usb.go b/usb.go index 8038de89..f0b2b924 100644 --- a/usb.go +++ b/usb.go @@ -51,8 +51,8 @@ func rpcKeypressReport(key byte, press bool) (usbgadget.KeysDownState, error) { return gadget.KeypressReport(key, press) } -func rpcAbsMouseReport(x int, y int, buttons uint8) error { - return gadget.AbsMouseReport(x, y, buttons) +func rpcAbsMouseReport(x uint16, y uint16, buttons uint8) error { + return gadget.AbsMouseReport(int(x), int(y), buttons) } func rpcRelMouseReport(dx int8, dy int8, buttons uint8) error {