Repository: electron/electron
Pull request: fix: `desktopCapturer` and `screen` display ids should match

#### Description of Change

Follow-up to #42781:

> Closes https://github.com/electron/electron/issues/42747.
>
> Fixes an issue where sources returned by the `desktopCapturer` API and by `screen.getAllDisplays()` no longer match on Windows. This happened initially as a result of https://chromium-review.googlesource.com/c/chromium/src/+/5362362, which removed the static helper we were previously using and required that the id be derived from `HMONITOREX`. The ids are specifically intended and expected to match, so this created a problem for developers needing this information to be kept consistent. To fix this, we need to do a bit more work unfortunately and get that `HMONITOREX` information ourselves in order to derive the correct id.
>
> Confirmed to produce expected results via https://gist.github.com/bukharin/7e4c4d61c9569e57de2183076d2808f9

This PR completes the work in #42781 by setting the `source.display_id` to the derived ID value.

#### Checklist

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed an issue where sources returned by the `desktopCapturer` API and `screen.getAllDisplays()` no longer matched on Windows.


Changed files:
filename: shell/browser/api/electron_api_desktop_capturer.cc
patch: @@ -399,28 +399,37 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
     if (using_directx_capturer_) {
       std::vector<std::string> device_names;
       // Crucially, this list of device names will be in the same order as
-      // |media_list_sources|.
+      // |screen_sources|.
       if (!webrtc::DxgiDuplicatorController::Instance()->GetDeviceNames(
               &device_names)) {
         HandleFailure();
         return;
       }
+      DCHECK_EQ(device_names.size(), screen_sources.size());
 
       std::vector<HMONITOR> monitors;
       EnumDisplayMonitors(nullptr, nullptr, EnumDisplayMonitorsCallback,
                           reinterpret_cast<LPARAM>(&monitors));
 
-      std::vector<std::pair<std::string, MONITORINFOEX>> pairs;
-      for (const auto& device_name : device_names) {
-        std::wstring wide_device_name;
-        base::UTF8ToWide(device_name.c_str(), device_name.size(),
-                         &wide_device_name);
-        for (const auto monitor : monitors) {
-          MONITORINFOEX monitorInfo{{sizeof(MONITORINFOEX)}};
-          if (GetMonitorInfo(monitor, &monitorInfo)) {
-            if (wide_device_name == monitorInfo.szDevice)
-              pairs.push_back(std::make_pair(device_name, monitorInfo));
-          }
+      base::flat_map<std::string, int64_t> device_name_to_id;
+      device_name_to_id.reserve(monitors.size());
+      for (auto* monitor : monitors) {
+        MONITORINFOEX monitorInfo{{sizeof(MONITORINFOEX)}};
+        if (!GetMonitorInfo(monitor, &monitorInfo)) {
+          continue;
+        }
+
+        device_name_to_id[base::WideToUTF8(monitorInfo.szDevice)] =
+            display::win::internal::DisplayInfo::DisplayIdFromMonitorInfo(
+                monitorInfo);
+      }
+
+      int device_name_index = 0;
+      for (auto& source : screen_sources) {
+        const auto& device_name = device_names[device_name_index++];
+        if (auto id_iter = device_name_to_id.find(device_name);
+            id_iter != device_name_to_id.end()) {
+          source.display_id = base::NumberToString(id_iter->second);
         }
       }
     }

filename: spec/api-screen-spec.ts
patch: @@ -1,6 +1,5 @@
 import { expect } from 'chai';
 import { Display, screen, desktopCapturer } from 'electron/main';
-import { ifit } from './lib/spec-helpers';
 
 describe('screen module', () => {
   describe('methods reassignment', () => {
@@ -24,14 +23,14 @@ describe('screen module', () => {
       }
     });
 
-    // desktopCapturer.getSources does not work as expected in Windows CI.
-    ifit(process.platform !== 'win32')('returns displays with IDs matching desktopCapturer source display IDs', async () => {
+    it('returns displays with IDs matching desktopCapturer source display IDs', async () => {
       const displayIds = screen.getAllDisplays().map(d => `${d.id}`);
 
       const sources = await desktopCapturer.getSources({ types: ['screen'] });
       const sourceIds = sources.map(s => s.display_id);
 
-      expect(displayIds).to.have.members(sourceIds);
+      expect(displayIds).to.have.length(sources.length);
+      expect(displayIds).to.have.same.members(sourceIds);
     });
   });