fix(v3/linux): wire common events + dbus monitors on GTK4 default path (#5474)#5477
Conversation
#5474) The GTK4 default build's `linuxApp.run()` (the post-#5463 default in alpha.93) never called `setupCommonEvents()`, `monitorThemeChanges()`, or `monitorPowerEvents()`. As a result, listeners registered for `events.Common.ApplicationStarted`, `Common.ThemeChanged`, `Common.SystemWillSleep` and `Common.SystemDidWake` never fired, since nothing forwarded the underlying `events.Linux.*` events to their common counterparts. Reverting to alpha.92 worked because the gtk3 default already wired these in `application_linux_gtk3.go` (renamed from the previous `application_linux.go`). Restore parity by calling all three from the gtk4 `run()`, and extract the two dbus monitor helpers to a build-tag-shared `application_linux_dbus.go` so both stacks use the same implementation. Verified with the existing `pkg/application/internal/tests/services/*` suite on lin-node1: the startup/shutdown tests hang under the pre-fix GTK4 default (`Common.ApplicationStarted` never fires, listener never calls `app.Quit()`) and pass after the fix on both default and `-tags gtk3` builds.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughExtracts DBus theme and power monitoring into a Linux+cgo file, wires common event setup and power monitoring into linuxApp startup, removes GTK3 duplicate DBus implementations, and adds an unreleased changelog entry documenting the fix. ChangesLinux Event Monitoring Refactor
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewersatterpac Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" 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.
Actionable comments posted: 3
🤖 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.
Inline comments:
In `@v3/pkg/application/application_linux_dbus.go`:
- Around line 32-50: The getTheme parser currently only accepts the
GNOME-specific "org.gnome.desktop.interface" + "color-scheme" and expects a
string; update getTheme to also accept the standardized portal namespace
"org.freedesktop.appearance" with key "color-scheme" and to handle the D-Bus
unsigned integer payload (dbus.Variant containing a uint32 for 'u'). Keep
backward compatibility by still accepting the GNOME form and string variants: if
variant.Value() is a uint32 map 0->"" (no preference), 1->"light", 2->"dark"
(returning the corresponding string), and if variant.Value() is a string return
it as before; return false for any other shapes/types. Reference: the getTheme
closure defined in application_linux_dbus.go that inspects
body[0]/body[1]/body[2].
In `@v3/pkg/application/application_linux.go`:
- Line 96: The GTK4 path is registering two theme-change watchers which
double-fires events: remove the duplicate watcher by either deleting the
a.monitorThemeChanges() call from the GTK4 init path or gating one path;
specifically, in the init() flow remove (or conditionalize) the
a.monitorThemeChanges() invocation so only listenForSystemThemeChanges(a) emits
events.Linux.SystemThemeChanged for GTK4, or alternatively wrap
a.monitorThemeChanges() with a runtime/feature check that prevents it from
running when listenForSystemThemeChanges(a) is active.
In `@v3/UNRELEASED_CHANGELOG.md`:
- Line 26: Update the changelog entry to use correct capitalization for project
names: change "dbus" to "DBus" (or "D-Bus"), and "gtk3"/"gtk4" to "GTK3"/"GTK4"
in the sentence about Fix `events.Common.ApplicationStarted`,
`Common.ThemeChanged`, `Common.SystemWillSleep` and `Common.SystemDidWake` not
firing on Linux after the GTK4 + WebKitGTK 6.0 stack was promoted to the default
in alpha.93; ensure the sentence still references that the new default
`application_linux.go` `run()` wasn't calling `setupCommonEvents()` /
`monitorThemeChanges()` / `monitorPowerEvents()` and that the two DBus monitor
helpers are now shared via `application_linux_dbus.go`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 08baf259-7ff5-4fca-9d76-c2eac048b6aa
📒 Files selected for processing (4)
v3/UNRELEASED_CHANGELOG.mdv3/pkg/application/application_linux.gov3/pkg/application/application_linux_dbus.gov3/pkg/application/application_linux_gtk3.go
💤 Files with no reviewable changes (1)
- v3/pkg/application/application_linux_gtk3.go
| getTheme := func(body []interface{}) (string, bool) { | ||
| if len(body) < 3 { | ||
| return "", false | ||
| } | ||
| if entry, ok := body[0].(string); !ok || entry != "org.gnome.desktop.interface" { | ||
| return "", false | ||
| } | ||
| if entry, ok := body[1].(string); !ok || entry != "color-scheme" { | ||
| return "", false | ||
| } | ||
| variant, ok := body[2].(dbus.Variant) | ||
| if !ok { | ||
| return "", false | ||
| } | ||
| value, ok := variant.Value().(string) | ||
| if !ok { | ||
| return "", false | ||
| } | ||
| return value, true |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== XDG Desktop Portal Settings docs =="
curl -fsSL https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html \
| rg -n "SettingChanged|org\.freedesktop\.appearance|color-scheme|implementation details"
echo
echo "== Current Wails Linux theme-monitor code paths =="
fd 'application_linux.*\.go$' v3/pkg/application -t f \
| xargs rg -n -C2 'org\.freedesktop\.appearance|org\.gnome\.desktop\.interface|SystemThemeChanged|color-scheme'Repository: wailsapp/wails
Length of output: 4349
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Build tags on relevant files ==="
head -1 v3/pkg/application/application_linux*.go
echo
echo "=== File list with build tags ==="
ls -la v3/pkg/application/application_linux*.go
echo
echo "=== Check if both files are included in same build ==="
grep -r "monitorThemeChanges\|monitorPowerEvents" v3/pkg/application/*.go | head -20Repository: wailsapp/wails
Length of output: 1344
🏁 Script executed:
#!/bin/bash
set -euo unpipefail
echo "=== Full context: where monitorThemeChanges is called ==="
rg -n "monitorThemeChanges\|monitorPowerEvents" v3/pkg/application -A2 -B2
echo
echo "=== Check the linuxApp type and its definition ==="
rg -n "type linuxApp struct" v3/pkg/application -A20Repository: wailsapp/wails
Length of output: 114
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Context: where monitorThemeChanges is called and how linuxApp is initialized ==="
rg -n "monitorThemeChanges\|monitorPowerEvents" v3/pkg/application -B5 -A1 | head -40
echo
echo "=== linuxApp struct definition and init ==="
rg -n "type linuxApp struct" v3/pkg/application -A15 | head -25Repository: wailsapp/wails
Length of output: 143
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check if monitorThemeChanges subscribes to portal or dbus directly ==="
sed -n '10,70p' v3/pkg/application/application_linux_dbus.go
echo
echo "=== Check what namespace the subscription is listening to ==="
rg -n "MatchRule|AddMatch" v3/pkg/application/application_linux_dbus.go -B2 -A2Repository: wailsapp/wails
Length of output: 2324
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check portal documentation for color-scheme type and signal structure ==="
curl -fsSL https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html \
| rg -n "color-scheme|SettingChanged" -A8 -B2 | grep -A8 "SettingChanged"
echo
echo "=== Check if application_linux.go shows how it parses the portal value ==="
sed -n '180,195p' v3/pkg/application/application_linux.go
echo
echo "=== Check the isDarkMode logic to see what value types are expected ==="
rg -n "isDarkMode" v3/pkg/application -A5 | head -30Repository: wailsapp/wails
Length of output: 3774
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Get full portal Settings spec for color-scheme including the value type ==="
curl -fsSL https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html \
| rg -n "org\.freedesktop\.appearance.*color-scheme" -B2 -A5
echo
echo "=== Check if there's any fallback or secondary theme detection path ==="
rg -n "color-scheme\|monitorTheme" v3/pkg/application/application_linux.go -B3 -A3Repository: wailsapp/wails
Length of output: 823
Fix the portal namespace and value type in the theme signal parser.
The parser on lines 32–50 hard-codes org.gnome.desktop.interface and expects a string value, but the XDG Desktop Portal Settings spec standardizes org.freedesktop.appearance/color-scheme with an unsigned integer type (u, where 0=no preference, 1=light, 2=dark). In GTK4 builds, both application_linux.go and application_linux_dbus.go register theme monitors, so signals from the portal (which uses the standardized namespace) are silently rejected by this parser. While xdg-desktop-portal-gtk mirrors the GNOME key as well, non-GNOME portal backends only guarantee the standardized namespace, causing missed theme changes.
Accept the standardized payload namespace and parse the unsigned integer value correctly, or support both shapes for backwards compatibility with GNOME-specific backends.
🤖 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 `@v3/pkg/application/application_linux_dbus.go` around lines 32 - 50, The
getTheme parser currently only accepts the GNOME-specific
"org.gnome.desktop.interface" + "color-scheme" and expects a string; update
getTheme to also accept the standardized portal namespace
"org.freedesktop.appearance" with key "color-scheme" and to handle the D-Bus
unsigned integer payload (dbus.Variant containing a uint32 for 'u'). Keep
backward compatibility by still accepting the GNOME form and string variants: if
variant.Value() is a uint32 map 0->"" (no preference), 1->"light", 2->"dark"
(returning the corresponding string), and if variant.Value() is a string return
it as before; return false for any other shapes/types. Reference: the getTheme
closure defined in application_linux_dbus.go that inspects
body[0]/body[1]/body[2].
There was a problem hiding this comment.
Skipping in this PR — this is pre-existing behaviour that I just moved (the parser was already in application_linux_gtk3.go before the file rename in #5463). It targets the GNOME-mirrored portal payload; on the GTK4 default path, the canonical portal listener (listenForSystemThemeChanges in application_linux.go) already uses the standardized org.freedesktop.appearance namespace, so the regression in #5474 isn't this. Worth doing as a separate change — happy to file a follow-up if you want it tracked.
There was a problem hiding this comment.
@leaanthony, that makes sense — since listenForSystemThemeChanges in application_linux.go already handles the standardized org.freedesktop.appearance namespace on the GTK4 path, this parser's GNOME-specific behavior is redundant rather than a regression blocker. The concern is still worth tracking for non-GNOME portal backends (KDE, COSMIC, etc.) where the GNOME mirror isn't guaranteed.
Would you like me to open a follow-up GitHub issue to track adding org.freedesktop.appearance + uint32 support to getTheme in application_linux_dbus.go? I can file it now and link back to this PR.
🧠 Learnings used
Learnt from: symball
Repo: wailsapp/wails PR: 4853
File: v2/internal/system/system.go:128-152
Timestamp: 2026-01-04T08:01:00.038Z
Learning: In v2/internal/system/system.go, shared functions like checkLibrary are defined without build tags but are only invoked from platform-specific files (system_linux.go, system_windows.go, system_darwin.go) that have build constraints. Reviewers should ensure there are no runtime OS checks in system.go and that platform-specific behavior is controlled via build tags. If runtime switches exist, remove them in favor of compile-time platform constraints to reduce overhead and improve correctness.
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4906
File: v3/internal/setupwizard/wizard_windows.go:93-94
Timestamp: 2026-01-26T01:56:49.314Z
Learning: Ensure the Go toolchain minimum version for Wails v3 is 1.25+. Update go.mod to reflect 1.25+ and validate that setup wizard checks enforce Go 1.25+ at release time. Align any CI/go-version checks, documentation, and release notes with this requirement; this guideline applies to all Go files under the v3 module, not just the wizard.
There was a problem hiding this comment.
Pull request overview
This PR restores Linux common application event wiring after the GTK4/WebKitGTK 6.0 path became the default, and shares D-Bus-based event monitors across GTK3 and GTK4 builds.
Changes:
- Wires common event forwarding and D-Bus theme/power monitors into the default Linux GTK4 run path.
- Moves Linux theme and power D-Bus monitor implementations into a shared Linux cgo file.
- Updates the unreleased changelog and removes the now-unused GTK3 D-Bus import.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
v3/UNRELEASED_CHANGELOG.md |
Adds a fixed-entry for restored Linux common events. |
v3/pkg/application/application_linux.go |
Calls common event setup and D-Bus monitors in the GTK4 default run path. |
v3/pkg/application/application_linux_gtk3.go |
Removes duplicated monitor implementations and unused import from GTK3 path. |
v3/pkg/application/application_linux_dbus.go |
Adds shared Linux D-Bus theme and power monitor helpers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if err = conn.AddMatchSignal( | ||
| dbus.WithMatchObjectPath("/org/freedesktop/portal/desktop"), | ||
| ); err != nil { | ||
| panic(err) |
There was a problem hiding this comment.
Fixed in 9114d91 — replaced panic(err) with the same warning-and-return pattern used by the surrounding session-bus setup, so a session-bus policy or portal error now disables theme monitoring instead of taking down the app via handlePanic.
| } | ||
| }) | ||
| a.setupCommonEvents() | ||
| a.monitorThemeChanges() |
There was a problem hiding this comment.
Addressed in 9114d91 by dropping a.monitorThemeChanges() from the GTK4 run() so listenForSystemThemeChanges remains the sole GTK4 theme listener — same change CodeRabbit flagged. I left listenForSystemThemeChanges in place because it uses the portal-standard org.freedesktop.appearance namespace and the cross-stack helper in application_linux_dbus.go is still needed for GTK3. Consolidating the two implementations entirely (porting listenForSystemThemeChanges's portal-namespace approach back into the shared helper, then deleting it from application_linux.go) is worth doing, but is broader than this regression fix and I'd rather land that as a follow-up.
| event := newApplicationEvent(events.Linux.SystemThemeChanged) | ||
| event.Context().setIsDarkMode(a.isDarkMode()) | ||
| applicationEvents <- event |
There was a problem hiding this comment.
Partially addressed in 9114d91: I tightened the variant assertion in isDarkMode (innerVariant, ok := result.Value().(dbus.Variant) instead of an unchecked type assertion) so an unexpected portal payload shape now returns false rather than panicking.
Note: with the same commit, monitorThemeChanges is no longer called on the GTK4 path (it duplicated the existing listenForSystemThemeChanges), so the original cross-file panic-chain you flagged no longer applies here on the default path — isDarkMode is still callable but no longer the trigger point.
- application_linux.go run(): drop a.monitorThemeChanges() — the GTK4 default already has go listenForSystemThemeChanges(a) running from init(), which uses the portal-standard org.freedesktop.appearance namespace, so calling both would double-fire Common.ThemeChanged. monitorThemeChanges() stays in the shared dbus file because GTK3 still uses it. Updates the changelog to reflect that only the power-monitor helper is newly shared. - application_linux.go isDarkMode(): type-check result.Value().(dbus.Variant) instead of an unchecked assertion that would panic on unexpected portal payload shapes. - application_linux_dbus.go monitorThemeChanges(): replace panic(err) on AddMatchSignal failure with the same warning-and-return pattern used by the surrounding dbus setup, so a session-bus policy error disables theme monitoring instead of killing the app.
Summary
Fixes #5474 — on alpha.93,
events.Common.ApplicationStarted(andCommon.ThemeChanged,Common.SystemWillSleep,Common.SystemDidWake) silently stopped firing on Linux for default builds.Root cause: the GTK4 + WebKitGTK 6.0 default flip (#5463) renamed
*_linux.go→*_linux_gtk3.goand promoted*_linux_gtk4.go→*_linux.go. The new defaultlinuxApp.run()was missing three calls that the gtk3 path had:Without
setupCommonEvents()the activate signal firesevents.Linux.ApplicationStartupas before, but nothing re-emits it asCommon.ApplicationStarted, so user listeners never run. The reporter's hypothesis (#4424 / #5192) was a red herring — those event-store locking changes don't affect this wiring.Changes
application_linux.gorun()so the GTK4 default has parity with the gtk3 path.monitorThemeChangesandmonitorPowerEventsinto a new build-tag-shared fileapplication_linux_dbus.go(linux && cgo && !android && !server). The implementations are pure dbus, GTK-agnostic, and were previously living only in the gtk3-tagged file. Now they're defined once and used by both stacks.godbus/dbus/v5import fromapplication_linux_gtk3.go(no longer used there).Fixed.Test plan
Verified on Ubuntu 26.04 + GTK 4.22.2 + WebKitGTK 2.52.3 (default GTK4 build path), using the existing
pkg/application/internal/tests/services/*suite, which usesevents.Common.ApplicationStartedas the signal toapp.Quit():startup_test.gohangs and fails with goroutine panic after the 50s test timeout —Common.ApplicationStartednever fires, listener never callsapp.Quit().go test ./pkg/application/internal/tests/services/...→ all 6 service tests pass.-tags gtk3:startup_test.gopasses — refactor didn't regress the legacy stack.go build ./pkg/application/...andgo build -tags gtk3 ./pkg/application/...both clean (only pre-existinggdk_x11_*deprecation warnings).Reporter's original repro program now creates the window as expected on the default GTK4 build.
Summary by CodeRabbit