mirror of https://github.com/jetkvm/kvm.git
Fix handling of meta keys in client
This commit is contained in:
parent
0c766343e4
commit
0ef60189c0
63
jsonrpc.go
63
jsonrpc.go
|
@ -13,6 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/webrtc/v4"
|
"github.com/pion/webrtc/v4"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
|
|
||||||
"github.com/jetkvm/kvm/internal/usbgadget"
|
"github.com/jetkvm/kvm/internal/usbgadget"
|
||||||
|
@ -134,7 +135,7 @@ func onRPCMessage(message webrtc.DataChannelMessage, session *Session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
scopedLogger.Trace().Msg("Calling RPC handler")
|
scopedLogger.Trace().Msg("Calling RPC handler")
|
||||||
result, err := callRPCHandler(handler, request.Params)
|
result, err := callRPCHandler(scopedLogger, handler, request.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scopedLogger.Error().Err(err).Msg("Error calling RPC handler")
|
scopedLogger.Error().Err(err).Msg("Error calling RPC handler")
|
||||||
errorResponse := JSONRPCResponse{
|
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
|
// 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
|
// Use defer to recover from a panic
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
|
@ -486,11 +487,11 @@ func callRPCHandler(handler RPCHandler, params map[string]interface{}) (result i
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Call the handler
|
// Call the handler
|
||||||
result, err = riskyCallRPCHandler(handler, params)
|
result, err = riskyCallRPCHandler(logger, handler, params)
|
||||||
return result, err
|
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)
|
handlerValue := reflect.ValueOf(handler.Func)
|
||||||
handlerType := handlerValue.Type()
|
handlerType := handlerValue.Type()
|
||||||
|
|
||||||
|
@ -499,20 +500,24 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
|
||||||
}
|
}
|
||||||
|
|
||||||
numParams := handlerType.NumIn()
|
numParams := handlerType.NumIn()
|
||||||
args := make([]reflect.Value, numParams)
|
paramNames := handler.Params // Get the parameter names from the RPCHandler
|
||||||
// Get the parameter names from the RPCHandler
|
|
||||||
paramNames := handler.Params
|
|
||||||
|
|
||||||
if len(paramNames) != numParams {
|
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++ {
|
for i := 0; i < numParams; i++ {
|
||||||
paramType := handlerType.In(i)
|
paramType := handlerType.In(i)
|
||||||
paramName := paramNames[i]
|
paramName := paramNames[i]
|
||||||
paramValue, ok := params[paramName]
|
paramValue, ok := params[paramName]
|
||||||
if !ok {
|
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)
|
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 {
|
if elemValue.Kind() == reflect.Float64 && paramType.Elem().Kind() == reflect.Uint8 {
|
||||||
intValue := int(elemValue.Float())
|
intValue := int(elemValue.Float())
|
||||||
if intValue < 0 || intValue > 255 {
|
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))
|
newSlice.Index(j).SetUint(uint64(intValue))
|
||||||
} else {
|
} else {
|
||||||
|
@ -545,12 +550,12 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
|
||||||
} else if paramType.Kind() == reflect.Struct && convertedValue.Kind() == reflect.Map {
|
} else if paramType.Kind() == reflect.Struct && convertedValue.Kind() == reflect.Map {
|
||||||
jsonData, err := json.Marshal(convertedValue.Interface())
|
jsonData, err := json.Marshal(convertedValue.Interface())
|
||||||
if err != nil {
|
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()
|
newStruct := reflect.New(paramType).Interface()
|
||||||
if err := json.Unmarshal(jsonData, newStruct); err != nil {
|
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()
|
args[i] = reflect.ValueOf(newStruct).Elem()
|
||||||
} else {
|
} 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)
|
results := handlerValue.Call(args)
|
||||||
|
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
|
@ -568,23 +574,32 @@ func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (int
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(results) == 1 {
|
if len(results) == 1 {
|
||||||
if results[0].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
if ok, err := asError(results[0]); ok {
|
||||||
if !results[0].IsNil() {
|
return nil, err
|
||||||
return nil, results[0].Interface().(error)
|
}
|
||||||
|
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
|
return results[0].Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(results) == 2 && results[1].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
return nil, fmt.Errorf("too many return values from handler: %d", len(results))
|
||||||
if !results[1].IsNil() {
|
}
|
||||||
return nil, results[1].Interface().(error)
|
|
||||||
}
|
|
||||||
return results[0].Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
func rpcSetMassStorageMode(mode string) (string, error) {
|
||||||
|
|
|
@ -337,17 +337,21 @@ export default function WebRTCVideo() {
|
||||||
const code = getAdjustedKeyCode(e);
|
const code = getAdjustedKeyCode(e);
|
||||||
const hidKey = keys[code];
|
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
|
// 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
|
// event, so we need to clear the keys after a short delay
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
||||||
if (e.metaKey) {
|
if (e.metaKey && hidKey < 0xE0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
sendKeypressEvent(hidKey, false);
|
sendKeypressEvent(hidKey, false);
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
sendKeypressEvent(hidKey, true);
|
sendKeypressEvent(hidKey, true);
|
||||||
|
|
||||||
},
|
},
|
||||||
[sendKeypressEvent],
|
[sendKeypressEvent],
|
||||||
);
|
);
|
||||||
|
@ -357,6 +361,12 @@ export default function WebRTCVideo() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const code = getAdjustedKeyCode(e);
|
const code = getAdjustedKeyCode(e);
|
||||||
const hidKey = keys[code];
|
const hidKey = keys[code];
|
||||||
|
|
||||||
|
if (hidKey === undefined) {
|
||||||
|
console.warn(`Key up not mapped: ${code}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sendKeypressEvent(hidKey, false);
|
sendKeypressEvent(hidKey, false);
|
||||||
},
|
},
|
||||||
[sendKeypressEvent],
|
[sendKeypressEvent],
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const keys = {
|
||||||
CapsLock: 0x39,
|
CapsLock: 0x39,
|
||||||
Comma: 0x36,
|
Comma: 0x36,
|
||||||
Compose: 0x65,
|
Compose: 0x65,
|
||||||
ContextMenu: 0,
|
ContextMenu: 0x65, // same as Compose
|
||||||
Delete: 0x4c,
|
Delete: 0x4c,
|
||||||
Digit0: 0x27,
|
Digit0: 0x27,
|
||||||
Digit1: 0x1e,
|
Digit1: 0x1e,
|
||||||
|
@ -121,6 +121,14 @@ export const keys = {
|
||||||
Space: 0x2c,
|
Space: 0x2c,
|
||||||
SystemRequest: 0x9a,
|
SystemRequest: 0x9a,
|
||||||
Tab: 0x2b,
|
Tab: 0x2b,
|
||||||
|
ControlLeft: 0xe0,
|
||||||
|
ControlRight: 0xe4,
|
||||||
|
ShiftLeft: 0xe1,
|
||||||
|
ShiftRight: 0xe5,
|
||||||
|
AltLeft: 0xe2,
|
||||||
|
AltRight: 0xe6,
|
||||||
|
MetaLeft: 0xe3,
|
||||||
|
MetaRight: 0xe7,
|
||||||
} as Record<string, number>;
|
} as Record<string, number>;
|
||||||
|
|
||||||
export const modifiers = {
|
export const modifiers = {
|
||||||
|
|
Loading…
Reference in New Issue