Fix: Linux startup panic from unrealized dictate window (#680)#837
Fix: Linux startup panic from unrealized dictate window (#680)#837PhamBit wants to merge 1 commit into
Conversation
On Linux (GTK3 + tao 0.34.5), the transparent dictate pill was built with .visible(false), which leaves its GDK window unrealized. The first set_ignore_cursor_events() dispatched over the glib main-context channel then unwraps a None GDK window and aborts the whole app with a non-unwinding panic at tao event_loop.rs:449. Build the pill visible so GTK realizes the GDK backing, then immediately move it off-screen and hide it. The pill remains invisible to the user until a speak, so behaviour is unchanged, but the window now exists when cursor-ignore is first toggled. Fixes jamiepine#680 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe ChangesDictate Window Realization Fix
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant App
participant WindowBuilder
participant GdkWindow
App->>WindowBuilder: build_dictate_window()
WindowBuilder->>GdkWindow: create with visible(true)
GdkWindow-->>WindowBuilder: window realized
WindowBuilder->>GdkWindow: set_position(-10000, -10000)
WindowBuilder->>GdkWindow: hide()
GdkWindow-->>App: parked, non-interactive
Related issues: Suggested labels: bug, linux, tauri Suggested reviewers: jamiepine 🐰 A window once hidden, now blinks awake, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) 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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tauri/src-tauri/src/main.rs (1)
49-58: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winScope the visibility workaround to Linux only.
The rationale comment explains this is a Linux/GTK3/tao-specific realization issue, but
.visible(true)is applied unconditionally on all platforms. macOS/Windows previously built the window hidden with no crash; forcing it visible there now introduces an unnecessary (if brief) on-screen flash/focus-steal of the transparent pill before it's parked off-screen at line 70-71.Consider gating this behind
cfg(target_os = "linux")so macOS/Windows retain the original hidden-at-create behavior while Linux gets the realization fix.♻️ Suggested platform-gated builder
- .shadow(false) - // On Linux (GTK3 + tao 0.34.5), building the transparent dictate pill with - // `.visible(false)` leaves its GDK window unrealized. The first - // `set_ignore_cursor_events()` dispatched over the glib main-context channel - // then unwraps a `None` GDK window and aborts the whole app with a - // non-unwinding panic at tao event_loop.rs:449 (see `#680`). Build the window - // visible so GTK realizes the GDK backing, then immediately park it - // off-screen and hide it (below). Net user-visible effect is unchanged — the - // pill stays hidden until a speak — but the window now actually exists. - .visible(true) + .shadow(false) + // On Linux (GTK3 + tao 0.34.5), building the transparent dictate pill with + // `.visible(false)` leaves its GDK window unrealized. The first + // `set_ignore_cursor_events()` dispatched over the glib main-context channel + // then unwraps a `None` GDK window and aborts the whole app with a + // non-unwinding panic at tao event_loop.rs:449 (see `#680`). Build the window + // visible so GTK realizes the GDK backing, then immediately park it + // off-screen and hide it (below). Net user-visible effect is unchanged — the + // pill stays hidden until a speak — but the window now actually exists. + // Scoped to Linux since macOS/Windows never hit this GDK realization gap. + .visible(cfg!(target_os = "linux"))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tauri/src-tauri/src/main.rs` around lines 49 - 58, Scope the window-visibility workaround to Linux only in the builder chain around the transparent pill creation. The current `.visible(true)` change in `main` is Linux/GTK3/tao-specific, so gate it with `cfg(target_os = "linux")` (or equivalent platform branching) and keep macOS/Windows on the original hidden-at-create path. Preserve the later off-screen parking and hide logic, but ensure only Linux forces realization before `set_ignore_cursor_events()` is used.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tauri/src-tauri/src/main.rs`:
- Around line 49-58: Scope the window-visibility workaround to Linux only in the
builder chain around the transparent pill creation. The current `.visible(true)`
change in `main` is Linux/GTK3/tao-specific, so gate it with `cfg(target_os =
"linux")` (or equivalent platform branching) and keep macOS/Windows on the
original hidden-at-create path. Preserve the later off-screen parking and hide
logic, but ensure only Linux forces realization before
`set_ignore_cursor_events()` is used.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a65602f3-1134-4249-bf39-5031fbee42e2
📒 Files selected for processing (1)
tauri/src-tauri/src/main.rs
…| fix(linux): 修复胶囊窗口因 GDK 未实例化导致的启动崩溃 (#792) * fix(linux): prevent startup panic from unrealized capsule GDK window | fix(linux): 修复胶囊窗口因 GDK 未实例化导致的启动崩溃 On Linux X11, when the capsule window is created with `visible: false` + `transparent: true` in tauri.conf.json, its underlying GDK window is never realized. The subsequent call to `capsule.set_ignore_cursor_events(true)` triggers a panic inside tao's Linux event loop (event_loop.rs:457) because `window.window().unwrap()` returns `None` for an unrealized GDK window. The fix follows the same approach as voicebox PR#837 (PhamBit): force GDK window realization before calling set_ignore_cursor_events, by first moving the window off-screen and calling show(). The window is then repositioned correctly by the existing position_capsule_bottom_center() call and hidden as normal. Refs: - jamiepine/voicebox#837 - tauri-apps/tao#635 在 Linux X11 上,tauri.conf.json 中定义的 capsule 窗口设置了 visible: false + transparent: true,导致其底层 GDK 窗口从未被实例化。 随后调用 capsule.set_ignore_cursor_events(true) 时,tao 的 Linux 事件循环 (event_loop.rs:457) 因 window.window().unwrap() 返回 None 而 panic 崩溃。 修复方法参考 voicebox PR#837:在设置鼠标穿透前先将窗口移出屏幕并 调用 show() 强制 GDK 实例化,后续由原有的 position_capsule_bottom_center() 重新定位并隐藏。 * fix(linux): realize hidden capsule without showing it --------- Co-authored-by: Chris233 <h-chris233@outlook.com>
Summary
Fixes a startup crash on Linux where the app opens the main window and then immediately aborts with a non-unwinding panic from
tao:This is the crash reported in #680.
Root cause
build_dictate_window(intauri/src-tauri/src/main.rs) constructs the transparent "dictate pill" overlay with.visible(false). On the Linux GTK3 backend, a window that is never shown never has its underlying GDK window realized. Shortly after startup, the firstset_ignore_cursor_events()toggle is dispatched over the glib main-context channel and reaches into that unrealized window —taocalls.unwrap()on theNoneGDK window (event_loop.rs:449) and, because it happens in a non-unwinding context, the panic takes the whole process down before the UI is usable.Fix
Build the pill visible so GTK realizes the GDK backing, then immediately move it off-screen (
-10000, -10000) andhide()it. The pill is still not visible to the user until a speak, so behaviour is unchanged — but the window now actually exists when cursor-ignore is first toggled, so there is noNoneto unwrap.The change is confined to
build_dictate_window; no public API or other call sites are touched.Testing
just devcrashed on startup withOption::unwrap()panic in tao event loop #680.event_loop.rs:449.xwininfothat both the main window (1200x800) and the dictate pill are realized, with the pill parked off-screen and hidden as intended.cargo check --no-default-featurespasses on currentmain(only pre-existingkeyboard_layout.rsdead-code warnings).Happy to add a
CHANGELOG.mdentry in whatever format you prefer.Fixes #680
Summary by CodeRabbit