real cursor shit

This commit is contained in:
KingKDot
2026-07-19 01:22:42 -05:00
parent 06e8a2921e
commit 1a68b5ddf1
17 changed files with 889 additions and 146 deletions
+2
View File
@@ -93,6 +93,7 @@ import {
handleConsoleOutput,
handleDesktopEncoderCapabilities,
handleDesktopStreamStats,
handleDesktopCursor,
handleConsoleViewerMessage,
handleConsoleViewerOpen,
handlebackstageViewerMessage,
@@ -658,6 +659,7 @@ async function startServer() {
handleConsoleOutput: (clientId: string, payload: any) => handleConsoleOutput(clientId, payload),
handleDesktopEncoderCapabilities: (clientId: string, payload: any) => handleDesktopEncoderCapabilities(clientId, payload),
handleDesktopStreamStats: (clientId: string, payload: any) => handleDesktopStreamStats(clientId, payload),
handleDesktopCursor: (clientId: string, payload: unknown) => handleDesktopCursor(clientId, payload),
handleFileBrowserMessage: (clientId: string, payload: any) =>
forwardFileBrowserMessage(clientId, payload, {
pendingHttpDownloads,
+16
View File
@@ -9,6 +9,7 @@ export type MessageKind =
| "command_result"
| "desktop_encoder_capabilities"
| "desktop_stream_stats"
| "desktop_cursor"
| "client_logs_result"
| "screenshot_result"
| "frame"
@@ -224,6 +225,20 @@ export type FrameHeader = {
export type Frame = { type: "frame"; header: FrameHeader; data: Uint8Array };
export type FrameAck = { type: "frame_ack" };
export type DesktopCursor = {
type: "desktop_cursor";
x: number;
y: number;
width: number;
height: number;
visible: boolean;
cursorWidth?: number;
cursorHeight?: number;
hotspotX?: number;
hotspotY?: number;
image?: Uint8Array;
};
export type DesktopStreamStats = {
type: "desktop_stream_stats";
fps: number;
@@ -547,6 +562,7 @@ export type WireMessage =
| Frame
| FrameAck
| DesktopStreamStats
| DesktopCursor
| Status
| ConsoleOutput
| FileListResult
@@ -229,6 +229,7 @@ type WsLifecycleDeps = {
handleConsoleOutput: (clientId: string, payload: any) => void;
handleDesktopEncoderCapabilities: (clientId: string, payload: any) => void;
handleDesktopStreamStats: (clientId: string, payload: any) => void;
handleDesktopCursor: (clientId: string, payload: unknown) => void;
handleFileBrowserMessage: (clientId: string, payload: any) => void;
handleProxyTunnelData: (clientId: string, connectionId: string, data: Uint8Array) => void;
handleProxyTunnelClose: (clientId: string, connectionId: string) => void;
@@ -808,6 +809,9 @@ export async function handleWebSocketMessage(
case "desktop_encoder_capabilities":
deps.handleDesktopEncoderCapabilities(client.id, payload);
break;
case "desktop_cursor":
deps.handleDesktopCursor(client.id, payload);
break;
case "desktop_stream_stats":
deps.handleDesktopStreamStats(client.id, payload);
break;
@@ -10,6 +10,7 @@ import {
handleRemoteDesktopViewerMessage,
handleRemoteDesktopViewerOpen,
handleDesktopStreamStats,
handleDesktopCursor,
handleDesktopEncoderCapabilities,
backstageStreamingState,
rdStreamingState,
@@ -218,6 +219,41 @@ describe("remote desktop viewer control", () => {
expect(message.transport).toBe("webrtc");
});
test("forwards cursor metadata without modifying video frames", () => {
const clientId = `rd-cursor-${Date.now().toString(36)}`;
createClient(clientId);
const viewer = createMockWs({ clientId });
handleRemoteDesktopViewerOpen(viewer as unknown as Parameters<typeof handleRemoteDesktopViewerOpen>[0]);
handleDesktopCursor(clientId, {
type: "desktop_cursor",
x: 640,
y: 360,
width: 1280,
height: 720,
visible: true,
cursorWidth: 32,
cursorHeight: 32,
hotspotX: 3,
hotspotY: 4,
image: new Uint8Array([137, 80, 78, 71]),
});
expect(decodeMessage(viewer.sent.at(-1) as Uint8Array)).toEqual({
type: "desktop_cursor",
x: 640,
y: 360,
width: 1280,
height: 720,
visible: true,
cursorWidth: 32,
cursorHeight: 32,
hotspotX: 3,
hotspotY: 4,
image: new Uint8Array([137, 80, 78, 71]),
});
});
test("selects one mutually compatible codec across all viewer transports", () => {
const clientId = `rd-codecs-${Date.now().toString(36)}`;
createClient(clientId);
@@ -1568,6 +1568,33 @@ function releaseCanvasFrameAck(clientId: string, sessionId?: string): boolean {
}
}
export function handleDesktopCursor(clientId: string, payload: unknown) {
const data = payload && typeof payload === "object" ? payload as Record<string, unknown> : {};
const image = data.image instanceof Uint8Array && data.image.byteLength <= 256 * 1024
? data.image
: undefined;
const cursor = {
type: "desktop_cursor",
x: Math.max(0, Math.floor(Number(data.x) || 0)),
y: Math.max(0, Math.floor(Number(data.y) || 0)),
width: Math.max(0, Math.floor(Number(data.width) || 0)),
height: Math.max(0, Math.floor(Number(data.height) || 0)),
visible: data.visible === true,
...(image
? {
cursorWidth: Math.max(1, Math.floor(Number(data.cursorWidth) || 1)),
cursorHeight: Math.max(1, Math.floor(Number(data.cursorHeight) || 1)),
hotspotX: Math.max(0, Math.floor(Number(data.hotspotX) || 0)),
hotspotY: Math.max(0, Math.floor(Number(data.hotspotY) || 0)),
image,
}
: {}),
};
for (const session of sessionManager.getRdSessionsForClient(clientId)) {
safeSendViewer(session.viewer, cursor, "rd-cursor");
}
}
export function handleDesktopStreamStats(clientId: string, payload: any) {
const stats = {
type: "desktop_stream_stats",
+1
View File
@@ -11,6 +11,7 @@ describe("wsValidation", () => {
expect(ALLOWED_CLIENT_MESSAGE_TYPES.has("hello")).toBe(true);
expect(ALLOWED_CLIENT_MESSAGE_TYPES.has("ping")).toBe(true);
expect(ALLOWED_CLIENT_MESSAGE_TYPES.has("frame")).toBe(true);
expect(ALLOWED_CLIENT_MESSAGE_TYPES.has("desktop_cursor")).toBe(true);
});
test("unknown client message types are rejected", () => {
+1
View File
@@ -22,6 +22,7 @@ export const ALLOWED_CLIENT_MESSAGE_TYPES = new Set([
"command_result",
"desktop_encoder_capabilities",
"desktop_stream_stats",
"desktop_cursor",
"client_logs_result",
"screenshot_result",
"command_progress",