mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
164 lines
4.6 KiB
TypeScript
164 lines
4.6 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
import { constants as fsConstants } from "node:fs";
|
|
import fs from "node:fs/promises";
|
|
import { BrowserWindow } from "electron";
|
|
import { ensureNativeCursorMonitorBinary, getCursorMonitorExePath } from "../paths/binaries";
|
|
import {
|
|
currentCursorVisualType,
|
|
nativeCursorMonitorOutputBuffer,
|
|
nativeCursorMonitorProcess,
|
|
setCurrentCursorVisualType,
|
|
setNativeCursorMonitorOutputBuffer,
|
|
setNativeCursorMonitorProcess,
|
|
} from "../state";
|
|
import type { CursorVisualType } from "../types";
|
|
import { recordCursorMouseDown, recordCursorMouseUp } from "./interaction";
|
|
|
|
export function emitCursorStateChanged(cursorType: CursorVisualType) {
|
|
BrowserWindow.getAllWindows().forEach((window) => {
|
|
if (!window.isDestroyed()) {
|
|
window.webContents.send("cursor-state-changed", { cursorType });
|
|
}
|
|
});
|
|
}
|
|
|
|
export function handleCursorMonitorStdout(chunk: Buffer) {
|
|
setNativeCursorMonitorOutputBuffer(nativeCursorMonitorOutputBuffer + chunk.toString());
|
|
const lines = nativeCursorMonitorOutputBuffer.split(/\r?\n/);
|
|
setNativeCursorMonitorOutputBuffer(lines.pop() ?? "");
|
|
|
|
for (const line of lines) {
|
|
const interactionMatch = line.match(/^INTERACTION:(mousedown|mouseup)(?::([123]))?$/);
|
|
if (interactionMatch) {
|
|
if (interactionMatch[1] === "mouseup") {
|
|
recordCursorMouseUp();
|
|
} else {
|
|
const button = Number(interactionMatch[2]);
|
|
recordCursorMouseDown(button === 2 || button === 3 ? button : 1);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const match = line.match(/^STATE:(.+)$/);
|
|
if (!match) continue;
|
|
const next = match[1].trim() as CursorVisualType;
|
|
if (
|
|
next === "arrow" ||
|
|
next === "text" ||
|
|
next === "pointer" ||
|
|
next === "crosshair" ||
|
|
next === "open-hand" ||
|
|
next === "closed-hand" ||
|
|
next === "resize-ew" ||
|
|
next === "resize-ns" ||
|
|
next === "not-allowed"
|
|
) {
|
|
if (currentCursorVisualType !== next) {
|
|
setCurrentCursorVisualType(next);
|
|
// sampleCursorStateChange is called from cursor/telemetry.ts via the handler
|
|
emitCursorStateChanged(next);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function stopNativeCursorMonitor() {
|
|
setCurrentCursorVisualType("arrow");
|
|
|
|
if (!nativeCursorMonitorProcess) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
nativeCursorMonitorProcess.stdin.write("stop\n");
|
|
} catch {
|
|
// ignore stop signal issues
|
|
}
|
|
try {
|
|
nativeCursorMonitorProcess.kill();
|
|
} catch {
|
|
// ignore kill issues
|
|
}
|
|
|
|
setNativeCursorMonitorProcess(null);
|
|
setNativeCursorMonitorOutputBuffer("");
|
|
}
|
|
|
|
export async function startNativeCursorMonitor() {
|
|
stopNativeCursorMonitor();
|
|
|
|
if (process.platform !== "darwin" && process.platform !== "win32") {
|
|
setCurrentCursorVisualType("arrow");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let helperPath: string;
|
|
if (process.platform === "win32") {
|
|
helperPath = getCursorMonitorExePath();
|
|
try {
|
|
// Use F_OK on Windows — X_OK is meaningless and can give false positives
|
|
await fs.access(helperPath, fsConstants.F_OK);
|
|
} catch {
|
|
console.warn("Windows cursor monitor helper missing:", helperPath);
|
|
setCurrentCursorVisualType("arrow");
|
|
return;
|
|
}
|
|
} else {
|
|
helperPath = await ensureNativeCursorMonitorBinary();
|
|
}
|
|
|
|
setNativeCursorMonitorOutputBuffer("");
|
|
setCurrentCursorVisualType("arrow");
|
|
|
|
let proc: ReturnType<typeof spawn> | null;
|
|
try {
|
|
proc = spawn(helperPath, [], {
|
|
stdio: ["pipe", "pipe", "pipe"],
|
|
});
|
|
} catch (spawnError) {
|
|
console.warn("Failed to spawn cursor monitor:", spawnError);
|
|
setNativeCursorMonitorProcess(null);
|
|
setCurrentCursorVisualType("arrow");
|
|
return;
|
|
}
|
|
|
|
setNativeCursorMonitorProcess(proc as Parameters<typeof setNativeCursorMonitorProcess>[0]);
|
|
const spawned = proc;
|
|
if (!spawned) {
|
|
setNativeCursorMonitorProcess(null);
|
|
setCurrentCursorVisualType("arrow");
|
|
return;
|
|
}
|
|
|
|
spawned.once("error", (error) => {
|
|
console.warn("Native cursor monitor process error:", error);
|
|
if (nativeCursorMonitorProcess === spawned) {
|
|
setNativeCursorMonitorProcess(null);
|
|
setNativeCursorMonitorOutputBuffer("");
|
|
setCurrentCursorVisualType("arrow");
|
|
}
|
|
});
|
|
|
|
if (spawned.stdout) spawned.stdout.on("data", handleCursorMonitorStdout);
|
|
if (spawned.stderr) {
|
|
spawned.stderr.on("data", () => {
|
|
// Drain stderr so helper logging cannot block the process.
|
|
});
|
|
}
|
|
|
|
spawned.once("close", () => {
|
|
if (nativeCursorMonitorProcess === spawned) {
|
|
setNativeCursorMonitorProcess(null);
|
|
setNativeCursorMonitorOutputBuffer("");
|
|
setCurrentCursorVisualType("arrow");
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.warn("Failed to start native cursor monitor:", error);
|
|
setNativeCursorMonitorProcess(null);
|
|
setNativeCursorMonitorOutputBuffer("");
|
|
setCurrentCursorVisualType("arrow");
|
|
}
|
|
}
|