Skip to content

Commit

Permalink
Adding support for Windows to use navigator.mediaDevices.getDisplayMedia
Browse files Browse the repository at this point in the history
  • Loading branch information
axeleriksson147 committed Jan 29, 2025
1 parent 599a2f3 commit eb73215
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 51 deletions.
98 changes: 89 additions & 9 deletions src/app/display-media-request-handler.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,99 @@
import { session } from 'electron';
import { desktopCapturer, ipcMain, session } from 'electron';
import { NOTIFICATION_WINDOW_TITLE } from '../common/api-interface';
import { isDevEnv, isMac } from '../common/env';
import { logger } from '../common/logger';
import {
ICustomBrowserWindowConstructorOpts,
windowHandler,
} from './window-handler';
import { createComponentWindow, windowExists } from './window-utils';

/**
* This is currently supported only on macOS 15+.
* setDisplayMediaRequestHandler injects into navigator.mediaDevices.getDisplayMedia().
* With the macOS-only option { useSystemPicker: true },
* everyting is handled natively by the OS.
* For MacOS 15+ the { useSystemPicker: true } overrides the code set in the handler,
* and uses the native implementation.
*
* For all other OSes and versions, the regular screen share flow will be used.
* But for other versions and OSes, the code is executed.
*/
export const setDisplayMediaRequestHandler = () => {
const { defaultSession } = session;

defaultSession.setDisplayMediaRequestHandler(
async (_request, _callback) => {
// TODO - Add support for Windows.
async (_request, callback) => {
logger.info('display-media-request-handler: getting sources');
const sources = await desktopCapturer.getSources({
types: ['screen', 'window'],
thumbnailSize: {
height: 150,
width: 150,
},
});

const updatedSources = sources.filter(
(source) => source.name !== NOTIFICATION_WINDOW_TITLE,
);

const browserWindowOptions: ICustomBrowserWindowConstructorOpts =
windowHandler.getWindowOpts(
{
alwaysOnTop: true,
autoHideMenuBar: true,
frame: false,
modal: false,
height: isMac ? 519 : 523,
width: 580,
show: false,
fullscreenable: false,
},
{
devTools: isDevEnv,
},
);
const screenPickerWindow = createComponentWindow(
'screen-picker',
browserWindowOptions,
);

screenPickerWindow.webContents.once('did-finish-load', () => {
if (!screenPickerWindow || !windowExists(screenPickerWindow)) {
return;
}
screenPickerWindow.webContents.send('screen-picker-data', {
sources: updatedSources,
});
});

const mainWebContents = windowHandler.getMainWebContents();
if (!mainWebContents) {
return;
}
mainWebContents.send('screen-picker-data', updatedSources);

ipcMain.on(
'screen-source-select',
(_event, source: Electron.DesktopCapturerSource) => {
logger.info('display-media-request-handler: source selected', source);
},
);

ipcMain.once(
'screen-source-selected',
(_event, source: Electron.DesktopCapturerSource) => {
screenPickerWindow.close();
logger.info(
'display-media-request-handler: source to be shared',
source,
);
if (!source) {
/**
* Passing the empty stream crashes the main process,
* but passing an empty callback throws an AbortError.
*/
// @ts-ignore
callback();
} else {
callback({ video: source });
}
},
);
},
{ useSystemPicker: true },
);
Expand Down
60 changes: 30 additions & 30 deletions src/app/window-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2419,6 +2419,36 @@ export class WindowHandler {
app.exit();
};

/**
* Returns constructor opts for the browser window
*
* @param windowOpts {Electron.BrowserWindowConstructorOptions}
* @param webPreferences {Electron.WebPreferences}
*/
public getWindowOpts(
windowOpts: Electron.BrowserWindowConstructorOptions,
webPreferences: Electron.WebPreferences,
): ICustomBrowserWindowConstructorOpts {
const defaultPreferencesOpts = {
...{
sandbox: IS_SAND_BOXED,
nodeIntegration: IS_NODE_INTEGRATION_ENABLED,
contextIsolation: this.contextIsolation,
backgroundThrottling: this.backgroundThrottling,
enableRemoteModule: true,
disableBlinkFeatures: AUX_CLICK,
},
...webPreferences,
};
const defaultWindowOpts = {
alwaysOnTop: false,
webPreferences: defaultPreferencesOpts,
winKey: getGuid(),
};

return { ...defaultWindowOpts, ...windowOpts };
}

/**
* Listens for app load timeouts and reloads if required
*/
Expand Down Expand Up @@ -2507,36 +2537,6 @@ export class WindowHandler {
}
}

/**
* Returns constructor opts for the browser window
*
* @param windowOpts {Electron.BrowserWindowConstructorOptions}
* @param webPreferences {Electron.WebPreferences}
*/
private getWindowOpts(
windowOpts: Electron.BrowserWindowConstructorOptions,
webPreferences: Electron.WebPreferences,
): ICustomBrowserWindowConstructorOpts {
const defaultPreferencesOpts = {
...{
sandbox: IS_SAND_BOXED,
nodeIntegration: IS_NODE_INTEGRATION_ENABLED,
contextIsolation: this.contextIsolation,
backgroundThrottling: this.backgroundThrottling,
enableRemoteModule: true,
disableBlinkFeatures: AUX_CLICK,
},
...webPreferences,
};
const defaultWindowOpts = {
alwaysOnTop: false,
webPreferences: defaultPreferencesOpts,
winKey: getGuid(),
};

return { ...defaultWindowOpts, ...windowOpts };
}

/**
* getUserAgent retrieves current window user-agent and updates it
* depending on global config setup
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/screen-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
<div className='ScreenPicker-screen-section-box'>
<img
className='ScreenPicker-img-wrapper'
src={source.thumbnail as any}
src={source.thumbnail.toDataURL()}
alt='thumbnail image'
/>
</div>
Expand All @@ -190,7 +190,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
<div className='ScreenPicker-screen-section-box'>
<img
className='ScreenPicker-img-wrapper'
src={source.thumbnail as any}
src={source.thumbnail.toDataURL()}
alt='thumbnail image'
/>
</div>
Expand Down
13 changes: 3 additions & 10 deletions src/renderer/desktop-capturer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,9 @@ export const getSource = async (
}
}

const updatedSources = sources
.filter((source) => source.name !== NOTIFICATION_WINDOW_TITLE)
.map((source) => {
return {
...source,
...{
thumbnail: source.thumbnail.toDataURL(),
},
};
});
const updatedSources = sources.filter(
(source) => source.name !== NOTIFICATION_WINDOW_TITLE,
);

ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.openScreenPickerWindow,
Expand Down

0 comments on commit eb73215

Please sign in to comment.