Skip to content

fix(linux): allow full-screen capture by position if IDs do not match#267

Merged
webadderall merged 1 commit intomainfrom
fix/linux-x11-fullscreen-capture
Apr 18, 2026
Merged

fix(linux): allow full-screen capture by position if IDs do not match#267
webadderall merged 1 commit intomainfrom
fix/linux-x11-fullscreen-capture

Conversation

@webadderall
Copy link
Copy Markdown
Collaborator

@webadderall webadderall commented Apr 18, 2026

Summary

Fixes Linux full-screen capture failing with 'Selected display is not available for browser capture on this system'.

Root cause

On Linux/X11, Electron's desktopCapturer returns screen sources whose display_id values do not match those from screen.getAllDisplays(). This caused all displays to be assigned fallback IDs, which are rejected by the recording logic.

Fix

If the number of Electron screen sources matches the number of displays, pair them by position as a fallback. This allows full-screen capture to work on Linux/X11 even when IDs do not match.

Closes #265

Summary by CodeRabbit

Bug Fixes

  • Improved screen source matching logic on Linux systems for more accurate display detection and screen capture functionality.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

📝 Walkthrough

Walkthrough

Updated Linux screen-source matching logic in the "get-sources" IPC handler. The code now maintains an indexed list of Electron screen sources and falls back to index-based selection when display_id mapping is unavailable, improving source resolution reliability.

Changes

Cohort / File(s) Summary
Linux Screen Source Matching
electron/ipc/register/sources.ts
Added fallback index-based source selection for Linux displays when display_id matching is unavailable. Builds ordered list of "screen:" sources to enable matching by index position when direct ID lookup fails.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

Checked

Poem

🐰 A rabbit hops through Linux screens,
When IDs shift (as happens, it seems),
An index backup saves the day—
No more sources that fade away! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: enabling full-screen capture on Linux by matching sources by position when IDs do not align.
Description check ✅ Passed The PR description provides clear summary, root cause analysis, and fix details, but is missing the optional template sections like Type of Change, Testing Guide, and Screenshots.
Linked Issues check ✅ Passed The code changes directly address issue #265 by implementing fallback position-based source matching when display IDs do not align, enabling full-screen capture on Linux/X11.
Out of Scope Changes check ✅ Passed All changes in electron/ipc/register/sources.ts are directly related to resolving the Linux screen source matching issue; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/linux-x11-fullscreen-capture

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
electron/ipc/register/sources.ts (1)

99-131: ⚠️ Potential issue | 🟠 Major

Index-based fallback can mis-pair screens on multi-monitor Linux setups.

Two concerns in the new mapping when more than one display is present:

  1. Ordering mismatch. displays is sorted by bounds.x, bounds.y, id (lines 90–97), but electronScreenSourcesByIndex preserves whatever order desktopCapturer.getSources returns. Electron does not guarantee that order corresponds to on-screen layout, so electronScreenSourcesByIndex[index] is not actually "by position" as the PR description states — it's purely ordinal. On a 2+ monitor X11 setup with mismatched display_ids, this can bind the wrong capture source to the wrong display (e.g., recording the left monitor when the user picked the right one), which is worse than the current "not available" error because it fails silently.

  2. Possible double-assignment. When counts are equal but only some display_ids line up, a display whose ID did match will consume one source via electronScreenSourcesByDisplayId, while another display whose ID didn't match will fall back to electronScreenSourcesByIndex[index] — which may point to the same already-claimed source. Two screen entries then share one source.id, and recording one effectively records the other.

For the single-monitor case (the main scenario in #265) this logic is fine. For the multi-monitor case, consider matching by actual on-screen geometry instead, e.g. parsing the X11 source id (screen:<xrandr-output-id>:<index> / bounds), or pairing remaining sources to remaining displays after removing those already matched by display_id, and only falling back when exactly one of each is left.

♻️ Sketch: consume sources left-to-right and skip ones already matched by display_id
-		const electronScreenSourcesByDisplayId = new Map(
-			electronSources
-				.filter((source) => source.id.startsWith("screen:"))
-				.map((source) => [String(source.display_id ?? ""), source] as const),
-		);
-		// On Linux, desktopCapturer display_id values may not match screen.getAllDisplays() IDs.
-		// Keep an ordered list so we can fall back to position-based matching.
-		const electronScreenSourcesByIndex = electronSources.filter((source) =>
-			source.id.startsWith("screen:"),
-		);
+		const electronScreenSources = electronSources.filter((source) =>
+			source.id.startsWith("screen:"),
+		);
+		const electronScreenSourcesByDisplayId = new Map(
+			electronScreenSources.map(
+				(source) => [String(source.display_id ?? ""), source] as const,
+			),
+		);
+		const claimedSourceIds = new Set<string>();
+		for (const display of displays) {
+			const s = electronScreenSourcesByDisplayId.get(String(display.id));
+			if (s) claimedSourceIds.add(s.id);
+		}
+		const unmatchedSources = electronScreenSources.filter(
+			(s) => !claimedSourceIds.has(s.id),
+		);
+		const unmatchedDisplayCount = displays.filter(
+			(d) => !electronScreenSourcesByDisplayId.has(String(d.id)),
+		).length;
+		const canFallbackByIndex =
+			unmatchedSources.length === unmatchedDisplayCount && unmatchedDisplayCount > 0;
+		let fallbackCursor = 0;

		const screenSources = displays.map((display, index) => {
			const displayId = String(display.id);
-			const matchedSource =
-				electronScreenSourcesByDisplayId.get(displayId) ??
-				(electronScreenSourcesByIndex.length === displays.length
-					? electronScreenSourcesByIndex[index]
-					: undefined);
+			const matchedSource =
+				electronScreenSourcesByDisplayId.get(displayId) ??
+				(canFallbackByIndex ? unmatchedSources[fallbackCursor++] : undefined);

Note: this still uses ordinal pairing for the leftover set; if you want genuine position-based matching, compare display.bounds against source-provided geometry (where available) rather than relying on array order.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@electron/ipc/register/sources.ts` around lines 99 - 131, The current fallback
uses electronScreenSourcesByIndex[index] which can mis-pair and double-assign
sources; change the logic in the screenSources construction so you first build
electronScreenSourcesByDisplayId, then compute two lists of remainingDisplays
and remainingSources (filter out displays already matched and sources already
used), and pair those leftovers one-to-one (consuming sources left-to-right and
skipping any already-matched source ids) only when counts align; reference the
variables electronScreenSourcesByDisplayId, electronScreenSourcesByIndex,
screenSources, displays and matchedSource and ensure the fallback never returns
a source id that was already assigned to another display (alternatively parse
X11 source ids/geometry for precise matching if available).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@electron/ipc/register/sources.ts`:
- Around line 99-131: The current fallback uses
electronScreenSourcesByIndex[index] which can mis-pair and double-assign
sources; change the logic in the screenSources construction so you first build
electronScreenSourcesByDisplayId, then compute two lists of remainingDisplays
and remainingSources (filter out displays already matched and sources already
used), and pair those leftovers one-to-one (consuming sources left-to-right and
skipping any already-matched source ids) only when counts align; reference the
variables electronScreenSourcesByDisplayId, electronScreenSourcesByIndex,
screenSources, displays and matchedSource and ensure the fallback never returns
a source id that was already assigned to another display (alternatively parse
X11 source ids/geometry for precise matching if available).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2832c8cb-1d49-4fae-b9eb-23823de6303f

📥 Commits

Reviewing files that changed from the base of the PR and between c8db3f8 and fa97c94.

📒 Files selected for processing (1)
  • electron/ipc/register/sources.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux: Full-screen capture fails with 'Selected display is not available for browser capture on this system'

1 participant