Fix handling of meta keys in client

This commit is contained in:
Marc Brooks 2025-08-13 16:39:55 -05:00
parent 0c766343e4
commit 0ef60189c0
No known key found for this signature in database
GPG Key ID: 583A6AF2D6AE1DC6
3 changed files with 60 additions and 27 deletions

View File

@ -13,6 +13,7 @@ import (
"time"
"github.com/pion/webrtc/v4"
"github.com/rs/zerolog"
"go.bug.st/serial"
"github.com/jetkvm/kvm/internal/usbgadget"
@ -134,7 +135,7 @@ func onRPCMessage(message webrtc.DataChannelMessage, session *Session) {
}
scopedLogger.Trace().Msg("Calling RPC handler")
result, err := callRPCHandler(handler, request.Params)
result, err := callRPCHandler(scopedLogger, handler, request.Params)
if err != nil {
scopedLogger.Error().Err(err).Msg("Error calling RPC handler")
errorResponse := JSONRPCResponse{
@ -472,7 +473,7 @@ type RPCHandler struct {
}
// call the handler but recover from a panic to ensure our RPC thread doesn't collapse on malformed calls
func callRPCHandler(handler RPCHandler, params map[string]interface{}) (result interface{}, err error) {
func callRPCHandler(logger zerolog.Logger, handler RPCHandler, params map[string]interface{}) (result interface{}, err error) {
// Use defer to recover from a panic
defer func() {
if r := recover(); r != nil {
@ -486,11 +487,11 @@ func callRPCHandler(handler RPCHandler, params map[string]interface{}) (result i
}()
// Call the handler
result, err = riskyCallRPCHandler(handler, params)
return result, err
result, err = riskyCallRPCHandler(logger, handler, params)
return result, err // do not combine these two lines into one, as it breaks the above defer function's setting of err
}
func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (interface{}, error) {
func riskyCallRPCHandler(logger zerolog.Logger, handler RPCHandler, params map[string]interface{}) (interface{}, error) {
handlerValue := reflect.ValueOf(handler.Func)
handlerType := handlerValue.Type()
@ -499,20 +500,24 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
}
numParams := handlerType.NumIn()
args := make([]reflect.Value, numParams)
// Get the parameter names from the RPCHandler
paramNames := handler.Params
paramNames := handler.Params // Get the parameter names from the RPCHandler
if len(paramNames) != numParams {
return nil, errors.New("mismatch between handler parameters and defined parameter names")
err := fmt.Errorf("mismatch between handler parameters (%d) and defined parameter names (%d)", numParams, len(paramNames))
logger.Error().Strs("paramNames", paramNames).Err(err).Msg("Cannot call RPC handler")
return nil, err
}
args := make([]reflect.Value, numParams)
for i := 0; i < numParams; i++ {
paramType := handlerType.In(i)
paramName := paramNames[i]
paramValue, ok := params[paramName]
if !ok {
return nil, errors.New("missing parameter: " + paramName)
err := fmt.Errorf("missing parameter: %s", paramName)
logger.Error().Err(err).Msg("Cannot marshal arguments for RPC handler")
return nil, err
}
convertedValue := reflect.ValueOf(paramValue)
@ -529,7 +534,7 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
if elemValue.Kind() == reflect.Float64 && paramType.Elem().Kind() == reflect.Uint8 {
intValue := int(elemValue.Float())
if intValue < 0 || intValue > 255 {
return nil, fmt.Errorf("value out of range for uint8: %v", intValue)
return nil, fmt.Errorf("value out of range for uint8: %v for parameter %s", intValue, paramName)
}
newSlice.Index(j).SetUint(uint64(intValue))
} else {
@ -545,12 +550,12 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
} else if paramType.Kind() == reflect.Struct && convertedValue.Kind() == reflect.Map {
jsonData, err := json.Marshal(convertedValue.Interface())
if err != nil {
return nil, fmt.Errorf("failed to marshal map to JSON: %v", err)
return nil, fmt.Errorf("failed to marshal map to JSON: %v for parameter %s", err, paramName)
}
newStruct := reflect.New(paramType).Interface()
if err := json.Unmarshal(jsonData, newStruct); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON into struct: %v", err)
return nil, fmt.Errorf("failed to unmarshal JSON into struct: %v for parameter %s", err, paramName)
}
args[i] = reflect.ValueOf(newStruct).Elem()
} else {
@ -561,6 +566,7 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
}
}
logger.Trace().Interface("args", args).Msg("Calling RPC handler")
results := handlerValue.Call(args)
if len(results) == 0 {
@ -568,23 +574,32 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
}
if len(results) == 1 {
if results[0].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
if !results[0].IsNil() {
return nil, results[0].Interface().(error)
if ok, err := asError(results[0]); ok {
return nil, err
}
return results[0].Interface(), nil
}
if len(results) == 2 {
if ok, err := asError(results[1]); ok {
if err != nil {
return nil, err
}
return nil, nil
}
return results[0].Interface(), nil
}
if len(results) == 2 && results[1].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
if !results[1].IsNil() {
return nil, results[1].Interface().(error)
}
return results[0].Interface(), nil
}
return nil, fmt.Errorf("too many return values from handler: %d", len(results))
}
return nil, errors.New("unexpected return values from handler")
func asError(value reflect.Value) (bool, error) {
if value.Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
if value.IsNil() {
return true, nil
}
return true, value.Interface().(error)
}
return false, nil
}
func rpcSetMassStorageMode(mode string) (string, error) {

View File

@ -337,17 +337,21 @@ export default function WebRTCVideo() {
const code = getAdjustedKeyCode(e);
const hidKey = keys[code];
if (hidKey === undefined) {
console.warn(`Key down not mapped: ${code}`);
return;
}
// When pressing the meta key + another key, the key will never trigger a keyup
// event, so we need to clear the keys after a short delay
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
if (e.metaKey) {
if (e.metaKey && hidKey < 0xE0) {
setTimeout(() => {
sendKeypressEvent(hidKey, false);
}, 10);
}
sendKeypressEvent(hidKey, true);
},
[sendKeypressEvent],
);
@ -357,6 +361,12 @@ export default function WebRTCVideo() {
e.preventDefault();
const code = getAdjustedKeyCode(e);
const hidKey = keys[code];
if (hidKey === undefined) {
console.warn(`Key up not mapped: ${code}`);
return;
}
sendKeypressEvent(hidKey, false);
},
[sendKeypressEvent],

View File

@ -14,7 +14,7 @@ export const keys = {
CapsLock: 0x39,
Comma: 0x36,
Compose: 0x65,
ContextMenu: 0,
ContextMenu: 0x65, // same as Compose
Delete: 0x4c,
Digit0: 0x27,
Digit1: 0x1e,
@ -121,6 +121,14 @@ export const keys = {
Space: 0x2c,
SystemRequest: 0x9a,
Tab: 0x2b,
ControlLeft: 0xe0,
ControlRight: 0xe4,
ShiftLeft: 0xe1,
ShiftRight: 0xe5,
AltLeft: 0xe2,
AltRight: 0xe6,
MetaLeft: 0xe3,
MetaRight: 0xe7,
} as Record<string, number>;
export const modifiers = {