mirror of https://github.com/jetkvm/kvm.git
[WIP] Cleanup: Remove hid optimization code, as it is out of scope
This commit is contained in:
parent
eab0261344
commit
5da357ba01
|
@ -35,7 +35,7 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) {
|
||||||
logger.Warn().Err(err).Msg("failed to get pointer report")
|
logger.Warn().Err(err).Msg("failed to get pointer report")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rpcErr = rpcAbsMouseReport(uint16(pointerReport.X), uint16(pointerReport.Y), pointerReport.Button)
|
rpcErr = rpcAbsMouseReport(pointerReport.X, pointerReport.Y, pointerReport.Button)
|
||||||
case hidrpc.TypeMouseReport:
|
case hidrpc.TypeMouseReport:
|
||||||
mouseReport, err := message.MouseReport()
|
mouseReport, err := message.MouseReport()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
359
input_rpc.go
359
input_rpc.go
|
@ -1,359 +0,0 @@
|
||||||
package kvm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"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.
|
|
||||||
|
|
||||||
// Ultra-fast input RPC structures for zero-allocation parsing
|
|
||||||
// Bypasses float64 conversion by using typed JSON unmarshaling
|
|
||||||
|
|
||||||
// InputRPCRequest represents a specialized JSON-RPC request for input methods
|
|
||||||
// This eliminates the map[string]interface{} overhead and float64 conversions
|
|
||||||
type InputRPCRequest struct {
|
|
||||||
JSONRPC string `json:"jsonrpc"`
|
|
||||||
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 {
|
|
||||||
// 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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common validation helpers for parameter parsing
|
|
||||||
// These reduce code duplication and provide consistent error messages
|
|
||||||
|
|
||||||
// Ultra-fast inline validation macros - no function call overhead
|
|
||||||
// These prioritize the happy path (direct int parsing) for maximum performance
|
|
||||||
|
|
||||||
// validateKeysArray extracts and validates a keys array parameter
|
|
||||||
// Ultra-optimized inline validation for maximum performance
|
|
||||||
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 {
|
|
||||||
// Try int first (most common case for small integers)
|
|
||||||
if intVal, ok := keyInterface.(int); ok {
|
|
||||||
if intVal < 0 || intVal > 255 {
|
|
||||||
return nil, fmt.Errorf("%s: key at index %d value %d out of range [0-255]", methodName, i, intVal)
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 uint16, buttons uint8)
|
|
||||||
type AbsMouseReportParams struct {
|
|
||||||
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
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ultra-fast typed input handler - completely bypasses float64 conversions
|
|
||||||
// Uses direct JSON unmarshaling to target types for maximum performance
|
|
||||||
func handleInputRPCUltraFast(data []byte) (interface{}, error) {
|
|
||||||
var request InputRPCRequest
|
|
||||||
err := json.Unmarshal(data, &request)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse input request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch request.Method {
|
|
||||||
case "keyboardReport":
|
|
||||||
if request.Params.Modifier == nil || request.Params.Keys == nil {
|
|
||||||
return nil, fmt.Errorf("keyboardReport: missing required parameters")
|
|
||||||
}
|
|
||||||
keys := *request.Params.Keys
|
|
||||||
if len(keys) > MaxKeyboardKeys {
|
|
||||||
return nil, fmt.Errorf("keyboardReport: too many keys (max %d)", MaxKeyboardKeys)
|
|
||||||
}
|
|
||||||
_, err = rpcKeyboardReport(*request.Params.Modifier, keys)
|
|
||||||
return nil, err
|
|
||||||
|
|
||||||
case "absMouseReport":
|
|
||||||
if request.Params.X == nil || request.Params.Y == nil || request.Params.Buttons == nil {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: missing required parameters")
|
|
||||||
}
|
|
||||||
x, y, buttons := *request.Params.X, *request.Params.Y, *request.Params.Buttons
|
|
||||||
if x > 32767 || y > 32767 {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: coordinates out of range")
|
|
||||||
}
|
|
||||||
return nil, rpcAbsMouseReport(x, y, buttons)
|
|
||||||
|
|
||||||
case "relMouseReport":
|
|
||||||
if request.Params.Dx == nil || request.Params.Dy == nil || request.Params.Buttons == nil {
|
|
||||||
return nil, fmt.Errorf("relMouseReport: missing required parameters")
|
|
||||||
}
|
|
||||||
return nil, rpcRelMouseReport(*request.Params.Dx, *request.Params.Dy, *request.Params.Buttons)
|
|
||||||
|
|
||||||
case "wheelReport":
|
|
||||||
if request.Params.WheelY == nil {
|
|
||||||
return nil, fmt.Errorf("wheelReport: missing wheelY parameter")
|
|
||||||
}
|
|
||||||
return nil, rpcWheelReport(*request.Params.WheelY)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown input method: %s", request.Method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct handler for keyboard reports
|
|
||||||
// Ultra-optimized path with inlined validation for maximum performance
|
|
||||||
func handleKeyboardReportDirect(params map[string]interface{}) (interface{}, error) {
|
|
||||||
// Inline modifier validation - prioritize int path
|
|
||||||
var modifier uint8
|
|
||||||
if intVal, ok := params["modifier"].(int); ok {
|
|
||||||
if intVal < 0 || intVal > 255 {
|
|
||||||
return nil, fmt.Errorf("keyboardReport: modifier value %d out of range [0-255]", intVal)
|
|
||||||
}
|
|
||||||
modifier = uint8(intVal)
|
|
||||||
} else if floatVal, ok := params["modifier"].(float64); ok {
|
|
||||||
if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 {
|
|
||||||
return nil, fmt.Errorf("keyboardReport: modifier value %v invalid", floatVal)
|
|
||||||
}
|
|
||||||
modifier = uint8(floatVal)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("keyboardReport: modifier must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract and validate keys array
|
|
||||||
keys, err := validateKeysArray(params, "keyboardReport")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = rpcKeyboardReport(modifier, keys)
|
|
||||||
return nil, 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 - 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 = uint16(intVal)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: x must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = uint16(intVal)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: y must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inline buttons validation
|
|
||||||
var buttons uint8
|
|
||||||
if intVal, ok := params["buttons"].(int); ok {
|
|
||||||
if intVal < 0 || intVal > 255 {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: buttons value %d out of range [0-255]", intVal)
|
|
||||||
}
|
|
||||||
buttons = uint8(intVal)
|
|
||||||
} else if floatVal, ok := params["buttons"].(float64); ok {
|
|
||||||
if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: buttons value %v invalid", floatVal)
|
|
||||||
}
|
|
||||||
buttons = uint8(floatVal)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("absMouseReport: buttons must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, rpcAbsMouseReport(x, y, buttons)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 - check float64 first (most common JSON number type)
|
|
||||||
var dx int8
|
|
||||||
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 {
|
|
||||||
return nil, fmt.Errorf("relMouseReport: dx must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inline dy validation - check float64 first (most common JSON number type)
|
|
||||||
var dy int8
|
|
||||||
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 {
|
|
||||||
return nil, fmt.Errorf("relMouseReport: dy must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inline buttons validation - check float64 first (most common JSON number type)
|
|
||||||
var buttons uint8
|
|
||||||
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 {
|
|
||||||
return nil, fmt.Errorf("relMouseReport: buttons must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, rpcRelMouseReport(dx, dy, buttons)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct handler for wheel reports
|
|
||||||
// Ultra-optimized path with inlined validation for maximum performance
|
|
||||||
func handleWheelReportDirect(params map[string]interface{}) (interface{}, error) {
|
|
||||||
// Inline wheelY validation
|
|
||||||
var wheelY int8
|
|
||||||
if intVal, ok := params["wheelY"].(int); ok {
|
|
||||||
if intVal < -128 || intVal > 127 {
|
|
||||||
return nil, fmt.Errorf("wheelReport: wheelY value %d out of range [-128 to 127]", intVal)
|
|
||||||
}
|
|
||||||
wheelY = int8(intVal)
|
|
||||||
} else if floatVal, ok := params["wheelY"].(float64); ok {
|
|
||||||
if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 {
|
|
||||||
return nil, fmt.Errorf("wheelReport: wheelY value %v invalid", floatVal)
|
|
||||||
}
|
|
||||||
wheelY = int8(floatVal)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("wheelReport: wheelY must be a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -87,9 +87,9 @@ static volatile int playback_initialized = 0;
|
||||||
// Function to dynamically update Opus encoder parameters
|
// Function to dynamically update Opus encoder parameters
|
||||||
int update_opus_encoder_params(int bitrate, int complexity, int vbr, int vbr_constraint,
|
int update_opus_encoder_params(int bitrate, int complexity, int vbr, int vbr_constraint,
|
||||||
int signal_type, int bandwidth, int dtx) {
|
int signal_type, int bandwidth, int dtx) {
|
||||||
// This function works for both audio input and output encoder parameters
|
// This function updates encoder parameters for audio input (capture)
|
||||||
// Require either capture (output) or playback (input) initialization
|
// Only capture uses the encoder; playback uses a separate decoder
|
||||||
if (!encoder || (!capture_initialized && !playback_initialized)) {
|
if (!encoder || !capture_initialized) {
|
||||||
return -1; // Audio encoder not initialized
|
return -1; // Audio encoder not initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -532,12 +532,9 @@ func (ais *AudioInputServer) processOpusConfig(data []byte) error {
|
||||||
|
|
||||||
logger.Info().Interface("config", config).Msg("applying dynamic Opus encoder configuration")
|
logger.Info().Interface("config", config).Msg("applying dynamic Opus encoder configuration")
|
||||||
|
|
||||||
// Ensure capture is initialized before updating encoder parameters
|
// Note: We don't call CGOAudioInit() here as it would destroy and recreate the encoder,
|
||||||
// The C function requires both encoder and capture_initialized to be true
|
// causing temporary unavailability. The encoder should already be initialized when
|
||||||
if err := CGOAudioInit(); err != nil {
|
// the audio input server starts.
|
||||||
logger.Debug().Err(err).Msg("Audio capture already initialized or initialization failed")
|
|
||||||
// Continue anyway - capture may already be initialized
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the Opus encoder configuration dynamically with retry logic
|
// Apply the Opus encoder configuration dynamically with retry logic
|
||||||
var err error
|
var err error
|
||||||
|
|
87
jsonrpc.go
87
jsonrpc.go
|
@ -1,7 +1,6 @@
|
||||||
package kvm
|
package kvm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -21,8 +20,6 @@ import (
|
||||||
"github.com/jetkvm/kvm/internal/usbgadget"
|
"github.com/jetkvm/kvm/internal/usbgadget"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Direct RPC message handling for optimal input responsiveness
|
|
||||||
|
|
||||||
type JSONRPCRequest struct {
|
type JSONRPCRequest struct {
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
|
@ -97,62 +94,6 @@ func writeJSONRPCEvent(event string, params any, session *Session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func onRPCMessage(message webrtc.DataChannelMessage, session *Session) {
|
func onRPCMessage(message webrtc.DataChannelMessage, session *Session) {
|
||||||
// Ultra-fast path for input methods - completely bypass float64 conversions
|
|
||||||
// This optimization reduces latency by 5-10ms per input event by:
|
|
||||||
// - Eliminating float64 conversion overhead entirely
|
|
||||||
// - Using direct JSON unmarshaling to target types
|
|
||||||
// - Removing map[string]interface{} allocations
|
|
||||||
// - Bypassing reflection completely
|
|
||||||
if len(message.Data) > 0 {
|
|
||||||
// Quick method detection without full JSON parsing
|
|
||||||
data := message.Data
|
|
||||||
if bytes.Contains(data, []byte(`"keyboardReport"`)) ||
|
|
||||||
bytes.Contains(data, []byte(`"absMouseReport"`)) ||
|
|
||||||
bytes.Contains(data, []byte(`"relMouseReport"`)) ||
|
|
||||||
bytes.Contains(data, []byte(`"wheelReport"`)) {
|
|
||||||
result, err := handleInputRPCUltraFast(data)
|
|
||||||
if err != nil {
|
|
||||||
jsonRpcLogger.Error().Err(err).Msg("Error in ultra-fast input handler")
|
|
||||||
errorResponse := JSONRPCResponse{
|
|
||||||
JSONRPC: "2.0",
|
|
||||||
Error: map[string]interface{}{
|
|
||||||
"code": -32603,
|
|
||||||
"message": "Internal error",
|
|
||||||
"data": err.Error(),
|
|
||||||
},
|
|
||||||
ID: nil, // Will be extracted if needed
|
|
||||||
}
|
|
||||||
writeJSONRPCResponse(errorResponse, session)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract ID for response (minimal parsing)
|
|
||||||
var requestID interface{}
|
|
||||||
if idStart := bytes.Index(data, []byte(`"id":`)); idStart != -1 {
|
|
||||||
// Simple ID extraction - assumes numeric ID
|
|
||||||
idStart += 5
|
|
||||||
for i := idStart; i < len(data); i++ {
|
|
||||||
if data[i] >= '0' && data[i] <= '9' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if id, err := strconv.Atoi(string(data[idStart:i])); err == nil {
|
|
||||||
requestID = id
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response := JSONRPCResponse{
|
|
||||||
JSONRPC: "2.0",
|
|
||||||
Result: result,
|
|
||||||
ID: requestID,
|
|
||||||
}
|
|
||||||
writeJSONRPCResponse(response, session)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to standard JSON parsing for non-input methods
|
|
||||||
var request JSONRPCRequest
|
var request JSONRPCRequest
|
||||||
err := json.Unmarshal(message.Data, &request)
|
err := json.Unmarshal(message.Data, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -180,34 +121,6 @@ func onRPCMessage(message webrtc.DataChannelMessage, session *Session) {
|
||||||
|
|
||||||
scopedLogger.Trace().Msg("Received RPC request")
|
scopedLogger.Trace().Msg("Received RPC request")
|
||||||
|
|
||||||
// Legacy fast path for input methods (kept as fallback)
|
|
||||||
if isInputMethod(request.Method) {
|
|
||||||
result, err := handleInputRPCDirect(request.Method, request.Params)
|
|
||||||
if err != nil {
|
|
||||||
scopedLogger.Error().Err(err).Msg("Error calling direct input handler")
|
|
||||||
errorResponse := JSONRPCResponse{
|
|
||||||
JSONRPC: "2.0",
|
|
||||||
Error: map[string]interface{}{
|
|
||||||
"code": -32603,
|
|
||||||
"message": "Internal error",
|
|
||||||
"data": err.Error(),
|
|
||||||
},
|
|
||||||
ID: request.ID,
|
|
||||||
}
|
|
||||||
writeJSONRPCResponse(errorResponse, session)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response := JSONRPCResponse{
|
|
||||||
JSONRPC: "2.0",
|
|
||||||
Result: result,
|
|
||||||
ID: request.ID,
|
|
||||||
}
|
|
||||||
writeJSONRPCResponse(response, session)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to reflection-based handler for non-input methods
|
|
||||||
handler, ok := rpcHandlers[request.Method]
|
handler, ok := rpcHandlers[request.Method]
|
||||||
if !ok {
|
if !ok {
|
||||||
errorResponse := JSONRPCResponse{
|
errorResponse := JSONRPCResponse{
|
||||||
|
|
10
usb.go
10
usb.go
|
@ -51,8 +51,8 @@ func rpcKeypressReport(key byte, press bool) (usbgadget.KeysDownState, error) {
|
||||||
return gadget.KeypressReport(key, press)
|
return gadget.KeypressReport(key, press)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcAbsMouseReport(x uint16, y uint16, buttons uint8) error {
|
func rpcAbsMouseReport(x int, y int, buttons uint8) error {
|
||||||
return gadget.AbsMouseReport(int(x), int(y), buttons)
|
return gadget.AbsMouseReport(x, y, buttons)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcRelMouseReport(dx int8, dy int8, buttons uint8) error {
|
func rpcRelMouseReport(dx int8, dy int8, buttons uint8) error {
|
||||||
|
@ -60,16 +60,10 @@ func rpcRelMouseReport(dx int8, dy int8, buttons uint8) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcWheelReport(wheelY int8) error {
|
func rpcWheelReport(wheelY int8) error {
|
||||||
if gadget == nil {
|
|
||||||
return nil // Gracefully handle uninitialized gadget (e.g., in tests)
|
|
||||||
}
|
|
||||||
return gadget.AbsMouseWheelReport(wheelY)
|
return gadget.AbsMouseWheelReport(wheelY)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetKeyboardLedState() (state usbgadget.KeyboardState) {
|
func rpcGetKeyboardLedState() (state usbgadget.KeyboardState) {
|
||||||
if gadget == nil {
|
|
||||||
return usbgadget.KeyboardState{} // Return empty state for uninitialized gadget
|
|
||||||
}
|
|
||||||
return gadget.GetKeyboardState()
|
return gadget.GetKeyboardState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue