mPyKen / ScreenAreaShare

Share selected area of the screen in applications that do not support this, e.g. Teams.
MIT License
148 stars 19 forks source link

App doesn't work on latest Linux Mint #13

Closed joeskeen closed 6 months ago

joeskeen commented 1 year ago

I've used the app on Windows where it works great, but I also wanted to use it on my Linux box. After installing the latest release, I launch it and find it doesn't work. The capture window has a black background.

image

After taking a look, it seems that the version of Electron being used in this project currently doesn't support transparency on my OS. But I did find this project that I was able to quickly run to verify that Electron can do transparency in later versions.

image

So I pulled down the source and started playing around with the Electron version. Currently the version in package.json is 12, and trying all the major versions after 12 I found that version 15 is the minimum version that works with my setup. At the time of writing, the latest version of Electron is 25.

Having solved the transparency issue, I tested the render window, only to find that it isn't getting the capture data from the capture window. Uncommenting the line mainWindow.webContents.openDevTools(); I was able to discover a runtime error:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'id')
    at selectSource (render.js:37)
    at getVideoSources (render.js:25)
    at async EventEmitter.<anonymous> (render.js:65)

After enabling more logging, I was able to determine that the objects returned from desktopCapturer.getSources({types:["screen"]}) did not have a populated display_id property; thus the matching display isn't found in the first parameter of this call:

await selectSource(
    inputSources.filter((is) => is.display_id === display.id.toString())[0],
    display
  );

Investigating further, I tried looking at the documentation for desktopCapturer and it seems that in the latest Electron version, it is only accessible from the main thread (index.js), not from the browser (render.js). Looking at how it is used in the render.js file, it ultimately only needs to know the source.id for the selectSource function. Moving some of the code from render.js to index.js, I was able to finally see the source display ids, detect and handle the error all in the main thread:

find display 1881264395124802 @ {"x":0,"y":546,"width":1920,"height":1080}
  screen:430:0, Screen 1: 3803340867
  screen:429:0, Screen 2: 66
(node:33974) UnhandledPromiseRejectionWarning: Error: Cannot find source matching display 1881264395124802. Candidates: [
  {
    "name": "Screen 1",
    "id": "screen:430:0",
    "thumbnail": {
      "isMacTemplateImage": false
    },
    "display_id": "3803340867",
    "appIcon": null
  },
  {
    "name": "Screen 2",
    "id": "screen:429:0",
    "thumbnail": {
      "isMacTemplateImage": false
    },
    "display_id": "66",
    "appIcon": null
  }
]

Drat. Looks like the display.id and source.display_id don't match. This appears to be an Electron bug? (see https://stackoverflow.com/a/66772210). Yup, and here it is (opened way back in 2018, still open) https://github.com/electron/electron/issues/15111#issuecomment-452346357. Specifically, one user had a clever workaround to fall back on screen name instead of display id. Added that to my changes in index.js and suddenly I am getting some capture! But it's capturing the wrong screen. I had to go into my display settings and make Screen 1 be my primary monitor (screen 2 was previously set to that) and now it works!

image

I'm going to clean up my changes and submit a PR after I have tested it on Windows and Mac as well (to make sure I haven't broken anything).