diff --git a/src/main/crossover.js b/src/main/crossover.js index b710441..1efeb63 100644 --- a/src/main/crossover.js +++ b/src/main/crossover.js @@ -409,6 +409,9 @@ const syncSettings = ( options = preferences.preferences ) => { } ) + // Sync screen-capture exclusion across all windows (#359) + windows.syncContentProtection() + set.startOnBoot() // Reset all custom shortcuts diff --git a/src/main/preferences.js b/src/main/preferences.js index a866e3d..c13cd8b 100644 --- a/src/main/preferences.js +++ b/src/main/preferences.js @@ -55,6 +55,7 @@ const getDefaults = () => ( { updates: [ 'updates' ], sounds: [ 'sounds' ], gpu: [ 'gpu' ], + hideFromScreenCapture: [], startUnlocked: [ 'startUnlocked' ], boot: [], appSize: 'normal', @@ -571,6 +572,13 @@ const preferencesConfig = { options: [ { label: 'This switch runs the GPU process in the same process as the browser', value: 'gpuprocess' } ], help: 'This can help avoid issues with transparency.', }, + { + label: 'Hide From Screen Recording', + key: 'hideFromScreenCapture', + type: 'checkbox', + options: [ { label: 'Exclude the crosshair from screen captures and recordings', value: 'hideFromScreenCapture' } ], + help: 'Prevents the crosshair from showing up in screen recordings (e.g. GeForce Instant Replay, OBS, screenshots). The crosshair remains visible on-screen. Requires Windows 10 or later for full effect.', + }, { label: 'Run App On System Start', key: 'boot', diff --git a/src/main/windows.js b/src/main/windows.js index a784af1..43cf0ed 100644 --- a/src/main/windows.js +++ b/src/main/windows.js @@ -4,6 +4,7 @@ const { app, BrowserWindow, screen } = require( 'electron' ) const { activeWindow, centerWindow, is } = require( './util' ) const { APP_HEIGHT, APP_WIDTH, MAX_SHADOW_WINDOWS, APP_ASPECT_RATIO } = require( '../config/config.js' ) +const { checkboxTrue } = require( '../config/utils.js' ) const { productName } = require( '../../package.json' ) const dock = require( './dock.js' ) const log = require( './log.js' ) @@ -77,6 +78,43 @@ const getActiveWindow = () => { } +// Toggle whether a window is excluded from screen captures / recordings (#359) +// Uses Electron's setContentProtection, which maps to WDA_MONITOR / WDA_EXCLUDEFROMCAPTURE +// on Windows and NSWindowSharingNone on macOS. +const applyContentProtection = ( win, enabled = checkboxTrue( preferences.value( 'app.hideFromScreenCapture' ), 'hideFromScreenCapture' ) ) => { + + if ( !win || win.isDestroyed() ) { + + return + + } + + try { + + win.setContentProtection( Boolean( enabled ) ) + + } catch ( error ) { + + log.error( `setContentProtection failed: ${error?.message}` ) + + } + +} + +// Apply the current hideFromScreenCapture preference to every crosshair window +const syncContentProtection = () => { + + const enabled = checkboxTrue( preferences.value( 'app.hideFromScreenCapture' ), 'hideFromScreenCapture' ) + applyContentProtection( windows.win, enabled ) + + for ( const currentWindow of windows.shadowWindows ) { + + applyContentProtection( currentWindow, enabled ) + + } + +} + // Prevent window from being garbage collected // Default no shadow window const create = ( { isShadowWindow } = { isShadowWindow: false } ) => { @@ -141,6 +179,9 @@ const create = ( { isShadowWindow } = { isShadowWindow: false } ) => { win.setAlwaysOnTop( true, 'screen-saver', 1 ) win.setFullScreenable( false ) + // Exclude the crosshair from screen recordings / captures when enabled (#359) + applyContentProtection( win ) + win.once( 'ready-to-show', () => { log.info( 'Event: Ready to show' ) @@ -594,6 +635,8 @@ const unregister = () => { const windows = { + applyContentProtection, + syncContentProtection, init, load, each,