diff --git a/jsonrpc.go b/jsonrpc.go index b1b36c5..e94a950 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -771,6 +771,7 @@ var rpcHandlers = map[string]RPCHandler{ "getCloudState": {Func: rpcGetCloudState}, "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}}, "absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}}, + "relMouseReport": {Func: rpcRelMouseReport, Params: []string{"mx", "my", "buttons"}}, "wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}}, "getVideoState": {Func: rpcGetVideoState}, "getUSBState": {Func: rpcGetUSBState}, diff --git a/ui/src/components/InfoBar.tsx b/ui/src/components/InfoBar.tsx index be94043..7c002f1 100644 --- a/ui/src/components/InfoBar.tsx +++ b/ui/src/components/InfoBar.tsx @@ -14,6 +14,7 @@ export default function InfoBar() { const activeModifiers = useHidStore(state => state.activeModifiers); const mouseX = useMouseStore(state => state.mouseX); const mouseY = useMouseStore(state => state.mouseY); + const mouseMove = useMouseStore(state => state.mouseMove); const videoClientSize = useVideoStore( state => `${Math.round(state.clientWidth)}x${Math.round(state.clientHeight)}`, @@ -62,7 +63,7 @@ export default function InfoBar() { ) : null} - {settings.debugMode ? ( + {(settings.debugMode && settings.mouseMode == "absolute") ? (
Pointer: @@ -71,6 +72,17 @@ export default function InfoBar() {
) : null} + {(settings.debugMode && settings.mouseMode == "relative") ? ( +
+ Last Move: + + {mouseMove ? + `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : + "N/A"} + +
+ ) : null} + {settings.debugMode && (
USB State: diff --git a/ui/src/components/WebRTCVideo.tsx b/ui/src/components/WebRTCVideo.tsx index bb17e39..7b75141 100644 --- a/ui/src/components/WebRTCVideo.tsx +++ b/ui/src/components/WebRTCVideo.tsx @@ -28,6 +28,7 @@ export default function WebRTCVideo() { const settings = useSettingsStore(); const { sendKeyboardEvent, resetKeyboardState } = useKeyboard(); const setMousePosition = useMouseStore(state => state.setMousePosition); + const setMouseMove = useMouseStore(state => state.setMouseMove); const { setClientSize: setVideoClientSize, setSize: setVideoSize, @@ -92,14 +93,22 @@ export default function WebRTCVideo() { ); // Mouse-related + const calcDelta = (pos: number) => Math.abs(pos) < 10 ? pos * 2 : pos; + const sendMouseMovement = useCallback( (x: number, y: number, buttons: number) => { - send("absMouseReport", { x, y, buttons }); - - // We set that for the debug info bar - setMousePosition(x, y); + if (settings.mouseMode === "relative") { + // if we ignore the event, double-click will not work + // if (x === 0 && y === 0 && buttons === 0) return; + send("relMouseReport", { mx: calcDelta(x), my: calcDelta(y), buttons }); + setMouseMove({ x, y, buttons }); + } else if (settings.mouseMode === "absolute") { + send("absMouseReport", { x, y, buttons }); + // We set that for the debug info bar + setMousePosition(x, y); + } }, - [send, setMousePosition], + [send, setMousePosition, setMouseMove, settings.mouseMode], ); const mouseMoveHandler = useCallback( @@ -109,6 +118,13 @@ export default function WebRTCVideo() { const videoElementAspectRatio = videoClientWidth / videoClientHeight; const videoStreamAspectRatio = videoWidth / videoHeight; + const { buttons } = e; + // Send mouse movement events to the server + if (settings.mouseMode == "relative") { + sendMouseMovement(e.movementX, e.movementY, buttons); + return; + } + // Calculate the effective video display area let effectiveWidth = videoClientWidth; let effectiveHeight = videoClientHeight; @@ -137,11 +153,9 @@ export default function WebRTCVideo() { const x = Math.round(relativeX * 32767); const y = Math.round(relativeY * 32767); - // Send mouse movement - const { buttons } = e; sendMouseMovement(x, y, buttons); }, - [sendMouseMovement, videoClientHeight, videoClientWidth, videoWidth, videoHeight], + [sendMouseMovement, videoClientHeight, videoClientWidth, videoWidth, videoHeight, settings.mouseMode], ); const mouseWheelHandler = useCallback( diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index 3dfc96c..24cd408 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -197,15 +197,23 @@ export const useRTCStore = create(set => ({ setTerminalChannel: channel => set({ terminalChannel: channel }), })); +interface MouseMove { + x: number; + y: number; + buttons: number; +} interface MouseState { mouseX: number; mouseY: number; + mouseMove?: MouseMove; + setMouseMove: (move?: MouseMove) => void; setMousePosition: (x: number, y: number) => void; } export const useMouseStore = create(set => ({ mouseX: 0, mouseY: 0, + setMouseMove: (move?: MouseMove) => set({ mouseMove: move }), setMousePosition: (x, y) => set({ mouseX: x, mouseY: y }), })); diff --git a/ui/src/routes/devices.$id.settings.mouse.tsx b/ui/src/routes/devices.$id.settings.mouse.tsx index e7dc4b8..4dc172e 100644 --- a/ui/src/routes/devices.$id.settings.mouse.tsx +++ b/ui/src/routes/devices.$id.settings.mouse.tsx @@ -16,6 +16,9 @@ export default function SettingsKeyboardMouseRoute() { const [jiggler, setJiggler] = useState(false); + const mouseMode = useSettingsStore(state => state.mouseMode); + const setMouseMode = useSettingsStore(state => state.setMouseMode); + const [send] = useJsonRpc(); useEffect(() => { @@ -68,7 +71,7 @@ export default function SettingsKeyboardMouseRoute() {