Address HUD protection review feedback

This commit is contained in:
young
2026-09-03 11:51:48 +10:00
parent ae36ac8a88
commit 7899d8fce0
7 changed files with 77 additions and 33 deletions
+13 -21
View File
@@ -1,29 +1,21 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const windowsSource = readFileSync(fileURLToPath(new URL("./windows.ts", import.meta.url)), "utf8");
const mainSource = readFileSync(fileURLToPath(new URL("./main.ts", import.meta.url)), "utf8");
const recordingSource = readFileSync(
fileURLToPath(new URL("./ipc/register/recording.ts", import.meta.url)),
"utf8",
);
import {
getHudCaptureExcludedProcessIds,
supportsHudCaptureProtection,
} from "../src/lib/hudCaptureProtection";
describe("HUD capture protection lifecycle", () => {
it("applies protection without disabling Linux", () => {
expect(windowsSource).not.toContain(
"function isHudOverlayCaptureProtectionSupported(): boolean",
);
expect(windowsSource).toContain("hud.setContentProtection(enabled)");
expect(windowsSource).toContain('win.on("show"');
it("uses window protection on Windows and macOS only", () => {
expect(supportsHudCaptureProtection("win32")).toBe(true);
expect(supportsHudCaptureProtection("darwin")).toBe(true);
expect(supportsHudCaptureProtection("linux")).toBe(false);
});
it("reasserts protection at native and browser capture boundaries", () => {
expect(recordingSource).toMatch(
/"start-native-screen-recording"[\s\S]*?reassertHudOverlayCaptureProtection\(\)/,
);
expect(mainSource).toMatch(
/setDisplayMediaRequestHandler[\s\S]*?reassertHudOverlayCaptureProtection\(\)/,
);
it("only builds a macOS process exclusion when protection is enabled", () => {
expect(getHudCaptureExcludedProcessIds("darwin", true, 734)).toEqual([734]);
expect(getHudCaptureExcludedProcessIds("darwin", false, 734)).toEqual([]);
expect(getHudCaptureExcludedProcessIds("win32", true, 734)).toEqual([]);
expect(getHudCaptureExcludedProcessIds("linux", true, 734)).toEqual([]);
});
});
+8 -2
View File
@@ -12,6 +12,7 @@ import {
shell,
systemPreferences,
} from "electron";
import { getHudCaptureExcludedProcessIds } from "../../../src/lib/hudCaptureProtection";
import { showCursor } from "../../cursorHider";
import {
getHudOverlayCaptureProtectionEnabled,
@@ -734,8 +735,13 @@ export function registerRecordingHandlers(
capturesMicrophone,
};
if (getHudOverlayCaptureProtectionEnabled()) {
config.excludedProcessIds = [process.pid];
const excludedProcessIds = getHudCaptureExcludedProcessIds(
process.platform,
getHudOverlayCaptureProtectionEnabled(),
process.pid,
);
if (excludedProcessIds.length > 0) {
config.excludedProcessIds = excludedProcessIds;
}
if (options?.microphoneDeviceId) {
@@ -64,11 +64,3 @@ describe("ScreenCaptureKitRecorder colour metadata", () => {
expect(recorderSource).toContain("AVVideoYCbCrMatrix_ITU_R_709_2");
});
});
describe("ScreenCaptureKitRecorder HUD capture protection", () => {
it("excludes protected Recordly applications from display capture", () => {
expect(recorderSource).toContain("let excludedProcessIds: [Int32]?");
expect(recorderSource).toContain("excludedProcessIds.contains($0.processID)");
expect(recorderSource).toContain("excludingApplications: excludedApplications");
});
});
+5
View File
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { app, BrowserWindow, ipcMain } from "electron";
import { supportsHudCaptureProtection } from "../src/lib/hudCaptureProtection";
import { USER_DATA_PATH } from "./appPaths";
import {
getHudOverlayWindowBounds,
@@ -147,6 +148,10 @@ export function getHudOverlayCaptureProtectionEnabled(): boolean {
}
function applyHudOverlayCaptureProtectionToWindow(hud: BrowserWindow, enabled: boolean): void {
if (!supportsHudCaptureProtection(process.platform)) {
return;
}
try {
hud.setContentProtection(enabled);
} catch (error) {
+3 -2
View File
@@ -19,6 +19,7 @@ import { useScopedT } from "../../contexts/I18nContext";
import { useMicrophoneDevices } from "../../hooks/useMicrophoneDevices";
import { useScreenRecorder } from "../../hooks/useScreenRecorder";
import { useVideoDevices } from "../../hooks/useVideoDevices";
import { supportsHudCaptureProtection } from "../../lib/hudCaptureProtection";
import { Button } from "../ui/button";
import { HudInteractionContext } from "./contexts/HudInteractionContext";
import { canToggleFloatingWebcamPreview } from "./floatingWebcamPreview";
@@ -115,7 +116,7 @@ function LaunchWindowContent() {
toggleHudCaptureProtection,
} = useLaunchWindowSystemState(preparePermissions);
const supportsHudCaptureProtection = true;
const hudCaptureProtectionSupported = supportsHudCaptureProtection(platform ?? "");
useEffect(() => {
if (!selectedDeviceId) {
@@ -372,7 +373,7 @@ function LaunchWindowContent() {
</div>
<MorePopover
supportsHudCaptureProtection={supportsHudCaptureProtection}
supportsHudCaptureProtection={hudCaptureProtectionSupported}
hideHudFromCapture={hideHudFromCapture}
onToggleHudCaptureProtection={() => {
void toggleHudCaptureProtection();
+33
View File
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import {
getHudCaptureExcludedProcessIds,
supportsHudCaptureProtection,
} from "./hudCaptureProtection";
describe("supportsHudCaptureProtection", () => {
it.each([
["win32", true],
["darwin", true],
["linux", false],
["freebsd", false],
])("reports support for %s as %s", (platform, expected) => {
expect(supportsHudCaptureProtection(platform)).toBe(expected);
});
});
describe("getHudCaptureExcludedProcessIds", () => {
it("passes the current process to macOS capture when protection is enabled", () => {
expect(getHudCaptureExcludedProcessIds("darwin", true, 4512)).toEqual([4512]);
});
it.each([
["darwin", false, 4512],
["win32", true, 4512],
["linux", true, 4512],
["darwin", true, 0],
["darwin", true, Number.NaN],
])("returns no native exclusions for %s, enabled=%s, pid=%s", (platform, enabled, pid) => {
expect(getHudCaptureExcludedProcessIds(platform, enabled, pid as number)).toEqual([]);
});
});
+15
View File
@@ -0,0 +1,15 @@
export function supportsHudCaptureProtection(platform: string): boolean {
return platform === "darwin" || platform === "win32";
}
export function getHudCaptureExcludedProcessIds(
platform: string,
enabled: boolean,
processId: number,
): number[] {
if (platform !== "darwin" || !enabled || !Number.isSafeInteger(processId) || processId <= 0) {
return [];
}
return [processId];
}