desktop-capturer.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { BrowserWindow } from 'electron/main';
  2. const { createDesktopCapturer, isDisplayMediaSystemPickerAvailable } = process._linkedBinding('electron_browser_desktop_capturer');
  3. const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
  4. let currentlyRunning: {
  5. options: ElectronInternal.GetSourcesOptions;
  6. getSources: Promise<ElectronInternal.GetSourcesResult[]>;
  7. }[] = [];
  8. // |options.types| can't be empty and must be an array
  9. function isValid (options: Electron.SourcesOptions) {
  10. return Array.isArray(options?.types);
  11. }
  12. export { isDisplayMediaSystemPickerAvailable };
  13. export async function getNativePickerSource () {
  14. if (process.platform !== 'darwin') {
  15. console.error('Native system picker option is currently only supported on MacOS');
  16. }
  17. if (!isDisplayMediaSystemPickerAvailable) {
  18. console.error(`Native system picker unavailable.
  19. Note: This is an experimental API; please check the API documentation for updated restrictions`);
  20. }
  21. // Pass in the needed options for a more native experience
  22. // screen & windows by default, no thumbnails, since the native picker doesn't return them
  23. const options: Electron.SourcesOptions = {
  24. types: ['screen', 'window'],
  25. thumbnailSize: { width: 0, height: 0 },
  26. fetchWindowIcons: false
  27. };
  28. return await getSources(options);
  29. }
  30. export async function getSources (args: Electron.SourcesOptions) {
  31. if (!isValid(args)) throw new Error('Invalid options');
  32. const resizableValues = new Map();
  33. if (process.platform === 'darwin') {
  34. // Fix for bug in ScreenCaptureKit that modifies a window's styleMask the first time
  35. // it captures a non-resizable window. We record each non-resizable window's styleMask,
  36. // and we restore modified styleMasks later, after the screen capture.
  37. for (const win of BrowserWindow.getAllWindows()) {
  38. resizableValues.set([win.id], win.resizable);
  39. }
  40. }
  41. const captureWindow = args.types.includes('window');
  42. const captureScreen = args.types.includes('screen');
  43. const { thumbnailSize = { width: 150, height: 150 } } = args;
  44. const { fetchWindowIcons = false } = args;
  45. const options = {
  46. captureWindow,
  47. captureScreen,
  48. thumbnailSize,
  49. fetchWindowIcons
  50. };
  51. for (const running of currentlyRunning) {
  52. if (deepEqual(running.options, options)) {
  53. // If a request is currently running for the same options
  54. // return that promise
  55. return running.getSources;
  56. }
  57. }
  58. const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
  59. let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
  60. const stopRunning = () => {
  61. if (capturer) {
  62. delete capturer._onerror;
  63. delete capturer._onfinished;
  64. capturer = null;
  65. if (process.platform === 'darwin') {
  66. for (const win of BrowserWindow.getAllWindows()) {
  67. if (resizableValues.has(win.id)) {
  68. win.resizable = resizableValues.get(win.id);
  69. }
  70. };
  71. }
  72. }
  73. // Remove from currentlyRunning once we resolve or reject
  74. currentlyRunning = currentlyRunning.filter(running => running.options !== options);
  75. };
  76. capturer._onerror = (error: string) => {
  77. stopRunning();
  78. reject(error);
  79. };
  80. capturer._onfinished = (sources: Electron.DesktopCapturerSource[]) => {
  81. stopRunning();
  82. resolve(sources);
  83. };
  84. capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
  85. });
  86. currentlyRunning.push({
  87. options,
  88. getSources
  89. });
  90. return getSources;
  91. }