desktop-capturer.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. 'use strict'
  2. const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
  3. const { createDesktopCapturer } = process.electronBinding('desktop_capturer')
  4. const eventBinding = process.electronBinding('event')
  5. const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b)
  6. let currentlyRunning = []
  7. ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, fetchWindowIcons) => {
  8. const customEvent = eventBinding.createWithSender(event.sender)
  9. event.sender.emit('desktop-capturer-get-sources', customEvent)
  10. if (customEvent.defaultPrevented) {
  11. return []
  12. }
  13. const options = {
  14. captureWindow,
  15. captureScreen,
  16. thumbnailSize,
  17. fetchWindowIcons
  18. }
  19. for (const running of currentlyRunning) {
  20. if (deepEqual(running.options, options)) {
  21. // If a request is currently running for the same options
  22. // return that promise
  23. return running.getSources
  24. }
  25. }
  26. const getSources = new Promise((resolve, reject) => {
  27. const stopRunning = () => {
  28. // Remove from currentlyRunning once we resolve or reject
  29. currentlyRunning = currentlyRunning.filter(running => running.options !== options)
  30. }
  31. const request = {
  32. options,
  33. resolve: (value) => {
  34. stopRunning()
  35. resolve(value)
  36. },
  37. reject: (err) => {
  38. stopRunning()
  39. reject(err)
  40. },
  41. capturer: createDesktopCapturer()
  42. }
  43. request.capturer.emit = createCapturerEmitHandler(request.capturer, request)
  44. request.capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons)
  45. // If the WebContents is destroyed before receiving result, just remove the
  46. // reference to resolve, emit and the capturer itself so that it never dispatches
  47. // back to the renderer
  48. event.sender.once('destroyed', () => {
  49. request.resolve = null
  50. delete request.capturer.emit
  51. delete request.capturer
  52. stopRunning()
  53. })
  54. })
  55. currentlyRunning.push({
  56. options,
  57. getSources
  58. })
  59. return getSources
  60. })
  61. const createCapturerEmitHandler = (capturer, request) => {
  62. return function handlEmitOnCapturer (event, name, sources, fetchWindowIcons) {
  63. // Ensure that this capturer instance can only ever receive a single event
  64. // if we get more than one it is a bug but will also cause strange behavior
  65. // if we still try to handle it
  66. delete capturer.emit
  67. if (name === 'error') {
  68. const error = sources
  69. request.reject(error)
  70. return
  71. }
  72. const result = sources.map(source => {
  73. return {
  74. id: source.id,
  75. name: source.name,
  76. thumbnail: source.thumbnail.toDataURL(),
  77. display_id: source.display_id,
  78. appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null
  79. }
  80. })
  81. if (request.resolve) {
  82. request.resolve(result)
  83. }
  84. }
  85. }