Skip to content

fix(desktop): on Linux, reveal window on did-finish-load to avoid Wayland deadlock#2217

Closed
mwolson wants to merge 1 commit into
pingdotgg:mainfrom
mwolson:fix/desktop-wayland-ready-to-show-deadlock
Closed

fix(desktop): on Linux, reveal window on did-finish-load to avoid Wayland deadlock#2217
mwolson wants to merge 1 commit into
pingdotgg:mainfrom
mwolson:fix/desktop-wayland-ready-to-show-deadlock

Conversation

@mwolson
Copy link
Copy Markdown
Contributor

@mwolson mwolson commented Apr 20, 2026

Summary

  • On Linux/Wayland with Electron 40, a BrowserWindow created with show: false never receives ready-to-show, because the wl_surface has no role assigned until show() is called and the compositor never reports the surface as ready. Net effect: the renderer loads but the window never appears.
  • Add did-finish-load as a Linux-only fallback reveal trigger so the first event from either source surfaces the window. Other platforms keep the no-flash ready-to-show path, since did-finish-load typically fires before the first paint there.
  • Extract the bind-once logic into bindFirstRevealTrigger (apps/desktop/src/windowReveal.ts) with focused unit tests.

Fixes #2216.

Repro

  1. Linux + Wayland session (reproduced on niri; root cause is in Electron, not the compositor).
  2. Run a recent nightly AppImage (e.g. T3-Code-0.0.20-x86_64.AppImage).
  3. Process starts, renderer loads (network requests fire, React app mounts), no window appears.

Diagnosis

Confirmed via the Electron main-process Node inspector against the running process:

  • webContents.isLoading() transitioned to false and did-finish-load fired.
  • ready-to-show did NOT fire during an 8 s observation window.
  • After forcing window.show() from the inspector, ready-to-show then fired and the window appeared.

This matches Electron's documented behavior: ready-to-show is gated behind the renderer being marked "visible," which on Wayland requires the wl_surface to have a role assigned, which only happens once show() is called.

Why Linux-only (and not Wayland-only)

Per Electron's docs, ready-to-show is "usually emitted after the did-finish-load event." So on macOS / Windows / X11, a dual-listener that fires on whichever comes first would reveal the window before first paint, regressing the no-flash startup behavior the original ready-to-show pattern was buying us.

Gating the fallback to process.platform === "linux" keeps the no-flash path intact on macOS / Windows / X11. X11 strictly doesn't need the fallback either, so this leaves a small avoidable regression for X11 users, a brief flash of the theme-matched backgroundColor (#0a0a0a or #ffffff) on first paint. I went with the broader Linux gate for simplicity.

If you'd prefer to narrow this to actual Wayland sessions, the standard heuristic is:

const isWayland =
  process.platform === "linux" &&
  process.env.XDG_SESSION_TYPE === "wayland" &&
  !!process.env.WAYLAND_DISPLAY;

Since Electron 38 (Sept 2025), the default --ozone-platform-hint=auto makes "Wayland session" effectively equal to "Wayland Ozone backend" unless the user forces --ozone-platform=x11. The two-env-var conjunction matches what Chromium itself uses for auto-detection and what VS Code / Signal use. Footguns: it can be missing in nested/non-systemd/sudo launches (rare for an AppImage launched from a desktop entry), and it cannot detect an explicit --ozone-platform=x11 override. Happy to switch if maintainers prefer.

Test plan

  • bun run test --filter=@t3tools/desktop (4 new tests, 91 total pass)
  • bun typecheck
  • bun lint (0 errors)
  • bun fmt
  • AppImage rebuilt and launched on Linux/Wayland (niri) - window now appears

Note

Low Risk
Small, localized change to window reveal timing in the desktop main process; Linux-only fallback reduces risk of cross-platform behavior regressions.

Overview
Fixes a Linux/Wayland startup deadlock where a BrowserWindow created with show: false may never emit ready-to-show, leaving the app invisible.

main.ts now reveals the first window on whichever happens first: ready-to-show (all platforms) or Linux-only did-finish-load as a fallback, using a new bindFirstRevealTrigger helper with unit tests to ensure the reveal callback fires exactly once.

Reviewed by Cursor Bugbot for commit 32c6bc3. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Wayland deadlock by revealing window on did-finish-load as a fallback on Linux

  • Adds a new bindFirstRevealTrigger utility in windowReveal.ts that accepts multiple event subscribers and fires a reveal callback exactly once on the first event to trigger.
  • On Linux, ready-to-show can never fire when show: false is set under Wayland; a did-finish-load subscriber is added as a fallback so the window still reveals.
  • On other platforms, behavior is unchanged — the window reveals on ready-to-show as before.

Macroscope summarized 32c6bc3.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 62c53172-41f6-41a4-a10c-5396ddf1e26c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 20, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 20, 2026

Approvability

Verdict: Approved

A targeted bug fix for a Linux/Wayland-specific deadlock where the standard Electron window reveal pattern fails. The change adds a fallback event listener only on Linux, with clear documentation and comprehensive unit tests. The logic is simple and the scope is well-contained.

You can customize Macroscope's approvability policy. Learn more.

…and deadlock

On Linux/Wayland, Electron's `ready-to-show` event only fires after
`show()` is called when the BrowserWindow is created with `show: false`,
because the wl_surface has no role assigned until then and the
compositor never reports the surface as ready. The standard "wait for
ready, then show" pattern deadlocks: nothing ever calls `show()`, so the
window never appears.

Add `did-finish-load` as a Linux-only fallback trigger so the first
event from either source reveals the window. Other platforms keep the
no-flash `ready-to-show` path, since `did-finish-load` typically fires
before the first paint there. Extract the bind-once logic into
`bindFirstRevealTrigger` with focused unit tests.
@mwolson mwolson force-pushed the fix/desktop-wayland-ready-to-show-deadlock branch from efd4a55 to 32c6bc3 Compare April 20, 2026 14:43
@mwolson
Copy link
Copy Markdown
Contributor Author

mwolson commented Apr 20, 2026

For anyone hitting this: there's an open Electron tracking issue at electron/electron#48859, and FreeTube landed a did-finish-load fallback for the same bug in FreeTubeApp/FreeTube#8294 which an Electron maintainer pointed to as the sanctioned workaround. This PR is the same shape, gated on process.platform === "linux" rather than ozone-platform=wayland since the ozone switch isn't always set when the compositor is Wayland-native.

@mwolson mwolson changed the title fix(desktop): reveal window on did-finish-load fallback to avoid Wayland deadlock fix(desktop): on Linux, reveal window on did-finish-load to avoid Wayland deadlock Apr 20, 2026
@mwolson
Copy link
Copy Markdown
Contributor Author

mwolson commented Apr 21, 2026

Superseded by #2262, which combines this Wayland fix with the opencode minimum-version fix (#2248) since both are needed together for a usable Linux/Wayland install; discovered during regression testing of the opencode fix.

@mwolson mwolson closed this Apr 21, 2026
@mwolson mwolson deleted the fix/desktop-wayland-ready-to-show-deadlock branch April 21, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop window never appears on Linux/Wayland (Electron ready-to-show deadlock)

1 participant