mirror of https://github.com/jetkvm/kvm.git
217 lines
7.6 KiB
Go
217 lines
7.6 KiB
Go
package kvm
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// Constants for input validation
|
|
const (
|
|
// MaxKeyboardKeys defines the maximum number of simultaneous key presses
|
|
// This matches the USB HID keyboard report specification
|
|
MaxKeyboardKeys = 6
|
|
)
|
|
|
|
// Input RPC Direct Handlers
|
|
// This module provides optimized direct handlers for high-frequency input events,
|
|
// bypassing the reflection-based RPC system for improved performance.
|
|
//
|
|
// Performance benefits:
|
|
// - Eliminates reflection overhead (~2-3ms per call)
|
|
// - Reduces memory allocations
|
|
// - Optimizes parameter parsing and validation
|
|
// - Provides faster code path for input methods
|
|
//
|
|
// The handlers maintain full compatibility with existing RPC interface
|
|
// while providing significant latency improvements for input events.
|
|
|
|
// Common validation helpers for parameter parsing
|
|
// These reduce code duplication and provide consistent error messages
|
|
|
|
// validateFloat64Param extracts and validates a float64 parameter from the params map
|
|
func validateFloat64Param(params map[string]interface{}, paramName, methodName string, min, max float64) (float64, error) {
|
|
value, ok := params[paramName].(float64)
|
|
if !ok {
|
|
return 0, fmt.Errorf("%s: %s parameter must be a number, got %T", methodName, paramName, params[paramName])
|
|
}
|
|
if value < min || value > max {
|
|
return 0, fmt.Errorf("%s: %s value %v out of range [%v to %v]", methodName, paramName, value, min, max)
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// validateKeysArray extracts and validates a keys array parameter
|
|
func validateKeysArray(params map[string]interface{}, methodName string) ([]uint8, error) {
|
|
keysInterface, ok := params["keys"].([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s: keys parameter must be an array, got %T", methodName, params["keys"])
|
|
}
|
|
if len(keysInterface) > MaxKeyboardKeys {
|
|
return nil, fmt.Errorf("%s: too many keys (%d), maximum is %d", methodName, len(keysInterface), MaxKeyboardKeys)
|
|
}
|
|
|
|
keys := make([]uint8, len(keysInterface))
|
|
for i, keyInterface := range keysInterface {
|
|
keyFloat, ok := keyInterface.(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s: key at index %d must be a number, got %T", methodName, i, keyInterface)
|
|
}
|
|
if keyFloat < 0 || keyFloat > 255 {
|
|
return nil, fmt.Errorf("%s: key at index %d value %v out of range [0-255]", methodName, i, keyFloat)
|
|
}
|
|
keys[i] = uint8(keyFloat)
|
|
}
|
|
return keys, nil
|
|
}
|
|
|
|
// Input parameter structures for direct RPC handlers
|
|
// These mirror the original RPC method signatures but provide
|
|
// optimized parsing from JSON map parameters.
|
|
|
|
// KeyboardReportParams represents parameters for keyboard HID report
|
|
// Matches rpcKeyboardReport(modifier uint8, keys []uint8)
|
|
type KeyboardReportParams struct {
|
|
Modifier uint8 `json:"modifier"` // Keyboard modifier keys (Ctrl, Alt, Shift, etc.)
|
|
Keys []uint8 `json:"keys"` // Array of pressed key codes (up to 6 keys)
|
|
}
|
|
|
|
// AbsMouseReportParams represents parameters for absolute mouse positioning
|
|
// Matches rpcAbsMouseReport(x, y int, 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
|
|
}
|
|
|
|
// RelMouseReportParams represents parameters for relative mouse movement
|
|
// Matches rpcRelMouseReport(dx, dy int8, buttons uint8)
|
|
type RelMouseReportParams struct {
|
|
Dx int8 `json:"dx"` // Relative X movement delta (-127 to +127)
|
|
Dy int8 `json:"dy"` // Relative Y movement delta (-127 to +127)
|
|
Buttons uint8 `json:"buttons"` // Mouse button state bitmask
|
|
}
|
|
|
|
// WheelReportParams represents parameters for mouse wheel events
|
|
// Matches rpcWheelReport(wheelY int8)
|
|
type WheelReportParams struct {
|
|
WheelY int8 `json:"wheelY"` // Wheel scroll delta (-127 to +127)
|
|
}
|
|
|
|
// Direct handler for keyboard reports
|
|
// Optimized path that bypasses reflection for keyboard input events
|
|
func handleKeyboardReportDirect(params map[string]interface{}) (interface{}, error) {
|
|
// Extract and validate modifier parameter
|
|
modifierFloat, err := validateFloat64Param(params, "modifier", "keyboardReport", 0, 255)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
modifier := uint8(modifierFloat)
|
|
|
|
// Extract and validate keys array
|
|
keys, err := validateKeysArray(params, "keyboardReport")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return nil, rpcKeyboardReport(modifier, keys)
|
|
}
|
|
|
|
// Direct handler for absolute mouse reports
|
|
// Optimized path that bypasses reflection for absolute mouse positioning
|
|
func handleAbsMouseReportDirect(params map[string]interface{}) (interface{}, error) {
|
|
// Extract and validate x coordinate
|
|
xFloat, err := validateFloat64Param(params, "x", "absMouseReport", 0, 32767)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
x := int(xFloat)
|
|
|
|
// Extract and validate y coordinate
|
|
yFloat, err := validateFloat64Param(params, "y", "absMouseReport", 0, 32767)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
y := int(yFloat)
|
|
|
|
// Extract and validate buttons
|
|
buttonsFloat, err := validateFloat64Param(params, "buttons", "absMouseReport", 0, 255)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buttons := uint8(buttonsFloat)
|
|
|
|
return nil, rpcAbsMouseReport(x, y, buttons)
|
|
}
|
|
|
|
// Direct handler for relative mouse reports
|
|
// Optimized path that bypasses reflection for relative mouse movement
|
|
func handleRelMouseReportDirect(params map[string]interface{}) (interface{}, error) {
|
|
// Extract and validate dx (relative X movement)
|
|
dxFloat, err := validateFloat64Param(params, "dx", "relMouseReport", -127, 127)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dx := int8(dxFloat)
|
|
|
|
// Extract and validate dy (relative Y movement)
|
|
dyFloat, err := validateFloat64Param(params, "dy", "relMouseReport", -127, 127)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dy := int8(dyFloat)
|
|
|
|
// Extract and validate buttons
|
|
buttonsFloat, err := validateFloat64Param(params, "buttons", "relMouseReport", 0, 255)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buttons := uint8(buttonsFloat)
|
|
|
|
return nil, rpcRelMouseReport(dx, dy, buttons)
|
|
}
|
|
|
|
// Direct handler for wheel reports
|
|
// Optimized path that bypasses reflection for mouse wheel events
|
|
func handleWheelReportDirect(params map[string]interface{}) (interface{}, error) {
|
|
// Extract and validate wheelY (scroll delta)
|
|
wheelYFloat, err := validateFloat64Param(params, "wheelY", "wheelReport", -127, 127)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wheelY := int8(wheelYFloat)
|
|
|
|
return nil, rpcWheelReport(wheelY)
|
|
}
|
|
|
|
// handleInputRPCDirect routes input method calls to their optimized direct handlers
|
|
// This is the main entry point for the fast path that bypasses reflection.
|
|
// It provides significant performance improvements for high-frequency input events.
|
|
//
|
|
// Performance monitoring: Consider adding metrics collection here to track
|
|
// latency improvements and call frequency for production monitoring.
|
|
func handleInputRPCDirect(method string, params map[string]interface{}) (interface{}, error) {
|
|
switch method {
|
|
case "keyboardReport":
|
|
return handleKeyboardReportDirect(params)
|
|
case "absMouseReport":
|
|
return handleAbsMouseReportDirect(params)
|
|
case "relMouseReport":
|
|
return handleRelMouseReportDirect(params)
|
|
case "wheelReport":
|
|
return handleWheelReportDirect(params)
|
|
default:
|
|
// This should never happen if isInputMethod is correctly implemented
|
|
return nil, fmt.Errorf("handleInputRPCDirect: unsupported method '%s'", method)
|
|
}
|
|
}
|
|
|
|
// isInputMethod determines if a given RPC method should use the optimized direct path
|
|
// Returns true for input-related methods that have direct handlers implemented.
|
|
// This function must be kept in sync with handleInputRPCDirect.
|
|
func isInputMethod(method string) bool {
|
|
switch method {
|
|
case "keyboardReport", "absMouseReport", "relMouseReport", "wheelReport":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
} |