Skip to content

Commit e4d325d

Browse files
tutman96Nevexo
authored andcommitted
Fix fullscreen video relative mouse movements
1 parent 210bd6a commit e4d325d

File tree

4 files changed

+58
-11
lines changed

4 files changed

+58
-11
lines changed

ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"dev": "vite dev --mode=development",
1111
"build": "npm run build:prod",
1212
"build:device": "tsc && vite build --mode=device --emptyOutDir",
13+
"dev:device": "vite dev --mode=device",
1314
"build:prod": "tsc && vite build --mode=production",
1415
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
1516
},

ui/src/components/ActionBar.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
useMountMediaStore,
55
useUiStore,
66
useSettingsStore,
7+
useVideoStore,
78
} from "@/hooks/stores";
89
import { MdOutlineContentPasteGo } from "react-icons/md";
910
import Container from "@components/Container";
@@ -35,6 +36,7 @@ export default function Actionbar({
3536
state => state.remoteVirtualMediaState,
3637
);
3738
const developerMode = useSettingsStore(state => state.developerMode);
39+
const hdmiState = useVideoStore(state => state.hdmiState);
3840

3941
// This is the only way to get a reliable state change for the popover
4042
// at time of writing this there is no mount, or unmount event for the popover
@@ -268,6 +270,7 @@ export default function Actionbar({
268270
size="XS"
269271
theme="light"
270272
text="Fullscreen"
273+
disabled={hdmiState !== 'ready'}
271274
LeadingIcon={LuMaximize}
272275
onClick={() => requestFullscreen()}
273276
/>

ui/src/components/WebRTCVideo.tsx

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export default function WebRTCVideo() {
3030
const {
3131
setClientSize: setVideoClientSize,
3232
setSize: setVideoSize,
33+
width: videoWidth,
34+
height: videoHeight,
3335
clientWidth: videoClientWidth,
3436
clientHeight: videoClientHeight,
3537
} = useVideoStore();
@@ -102,20 +104,43 @@ export default function WebRTCVideo() {
102104
const mouseMoveHandler = useCallback(
103105
(e: MouseEvent) => {
104106
if (!videoClientWidth || !videoClientHeight) return;
105-
const { buttons } = e;
107+
// Get the aspect ratios of the video element and the video stream
108+
const videoElementAspectRatio = videoClientWidth / videoClientHeight;
109+
const videoStreamAspectRatio = videoWidth / videoHeight;
110+
111+
// Calculate the effective video display area
112+
let effectiveWidth = videoClientWidth;
113+
let effectiveHeight = videoClientHeight;
114+
let offsetX = 0;
115+
let offsetY = 0;
116+
117+
if (videoElementAspectRatio > videoStreamAspectRatio) {
118+
// Pillarboxing: black bars on the left and right
119+
effectiveWidth = videoClientHeight * videoStreamAspectRatio;
120+
offsetX = (videoClientWidth - effectiveWidth) / 2;
121+
} else if (videoElementAspectRatio < videoStreamAspectRatio) {
122+
// Letterboxing: black bars on the top and bottom
123+
effectiveHeight = videoClientWidth / videoStreamAspectRatio;
124+
offsetY = (videoClientHeight - effectiveHeight) / 2;
125+
}
106126

107-
// Clamp mouse position within the video boundaries
108-
const currMouseX = Math.min(Math.max(1, e.offsetX), videoClientWidth);
109-
const currMouseY = Math.min(Math.max(1, e.offsetY), videoClientHeight);
127+
// Clamp mouse position within the effective video boundaries
128+
const clampedX = Math.min(Math.max(offsetX, e.offsetX), offsetX + effectiveWidth);
129+
const clampedY = Math.min(Math.max(offsetY, e.offsetY), offsetY + effectiveHeight);
110130

111-
// Normalize mouse position to 0-32767 range (HID absolute coordinate system)
112-
const x = Math.round((currMouseX / videoClientWidth) * 32767);
113-
const y = Math.round((currMouseY / videoClientHeight) * 32767);
131+
// Map clamped mouse position to the video stream's coordinate system
132+
const relativeX = (clampedX - offsetX) / effectiveWidth;
133+
const relativeY = (clampedY - offsetY) / effectiveHeight;
134+
135+
// Convert to HID absolute coordinate system (0-32767 range)
136+
const x = Math.round(relativeX * 32767);
137+
const y = Math.round(relativeY * 32767);
114138

115139
// Send mouse movement
140+
const { buttons } = e;
116141
sendMouseMovement(x, y, buttons);
117142
},
118-
[sendMouseMovement, videoClientHeight, videoClientWidth],
143+
[sendMouseMovement, videoClientHeight, videoClientWidth, videoWidth, videoHeight],
119144
);
120145

121146
const mouseWheelHandler = useCallback(

ui/vite.config.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,31 @@ import { defineConfig } from "vite";
22
import react from "@vitejs/plugin-react-swc";
33
import tsconfigPaths from "vite-tsconfig-paths";
44

5-
export default defineConfig(({ mode }) => {
5+
declare const process: {
6+
env: {
7+
JETKVM_PROXY_URL: string;
8+
};
9+
};
10+
11+
export default defineConfig(({ mode, command }) => {
612
const isCloud = mode === "production";
713
const onDevice = mode === "device";
14+
const { JETKVM_PROXY_URL } = process.env;
15+
816
return {
917
plugins: [tsconfigPaths(), react()],
1018
build: { outDir: isCloud ? "dist" : "../static" },
11-
server: { host: "0.0.0.0" },
12-
base: onDevice ? "/static" : "/",
19+
server: {
20+
host: "0.0.0.0",
21+
proxy: JETKVM_PROXY_URL ? {
22+
'/me': JETKVM_PROXY_URL,
23+
'/device': JETKVM_PROXY_URL,
24+
'/webrtc': JETKVM_PROXY_URL,
25+
'/auth': JETKVM_PROXY_URL,
26+
'/storage': JETKVM_PROXY_URL,
27+
'/cloud': JETKVM_PROXY_URL,
28+
} : undefined
29+
},
30+
base: onDevice && command === 'build' ? "/static" : "/",
1331
};
1432
});

0 commit comments

Comments
 (0)