Skip to content

feat: Linux menubar companion (GTK3 + WebKitGTK)#35

Open
prakersh wants to merge 3 commits intomainfrom
feat/linux-menubar-port
Open

feat: Linux menubar companion (GTK3 + WebKitGTK)#35
prakersh wants to merge 3 commits intomainfrom
feat/linux-menubar-port

Conversation

@prakersh
Copy link
Contributor

Summary

Ports the macOS-only menubar companion to Linux using CGO + GTK3 + WebKitGTK. The existing architecture already uses fyne.io/systray (which supports Linux via libayatana-appindicator3), so the main work is implementing the native popover window and extracting shared code from darwin-only build tags.

  • Extracts platform-agnostic companion logic into shared companion_unix.go (was companion_darwin.go)
  • Implements GTK3 popup window + WebKitGTK WebView for the Linux popover (same HTML/CSS/JS frontend)
  • Produces two Linux binary variants: headless (existing, no CGO) and desktop (with menubar)
  • macOS behavior unchanged - verified by building and running on macOS with all tests passing

Testing on Linux

Prerequisites

Ubuntu 22.04+ or Debian Bookworm+ (for webkit2gtk-4.1).

sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev

On Fedora 38+:

sudo dnf install -y gtk3-devel webkit2gtk4.1-devel libayatana-appindicator-gtk3-devel

Build

# Desktop build (with menubar)
CGO_ENABLED=1 go build -tags menubar,desktop,production \
  -ldflags="-s -w" -o onwatch .

# Headless build (no menubar, no CGO - unchanged)
CGO_ENABLED=0 go build -ldflags="-s -w" -o onwatch .

Or use the build script (auto-detects GTK/WebKit):

./app.sh --build

Run

./onwatch --debug
# Tray icon should appear in GNOME/KDE panel
# Click tray icon -> popover with quota dashboard
# Click outside popover -> dismisses

Run tests

# Headless (xvfb required for GTK tests)
xvfb-run go test -tags menubar -race ./internal/menubar/...

# Or without menubar tag (tests shared code only)
go test -race ./...

Test plan

  • macOS: ./app.sh --build && ./app.sh --test - no regressions
  • macOS: menubar companion starts, popover opens/closes, tray icon updates
  • Linux (GNOME): desktop build produces tray icon in panel
  • Linux (GNOME): clicking tray icon opens popover with dashboard
  • Linux (GNOME): clicking outside popover dismisses it
  • Linux (KDE): same as GNOME checks above
  • Linux: headless build works without GTK libs installed
  • CI: xvfb-run go test -tags menubar -race ./internal/menubar/... passes on ubuntu-latest

Reporting issues

If something doesn't work on your Linux setup, please open an issue with:

  1. Distro and version (cat /etc/os-release)
  2. Desktop environment (GNOME, KDE, etc.)
  3. Build command used
  4. Debug logs: ./onwatch --debug 2>&1 | head -100

Important: Mask any API keys before sharing logs or config files. Replace key values with *** in .env files and log output.

Runtime dependencies (Linux desktop variant)

  • libayatana-appindicator3-1 (system tray)
  • libwebkit2gtk-4.1-0 (WebView rendering)
  • libgtk-3-0 (windowing)

Extract platform-agnostic companion logic from darwin-only build tags
into shared unix files, then implement a native Linux popover using
CGO + GTK3 + WebKitGTK - mirroring the macOS Cocoa/WebKit pattern.

Phase 1 - Shared code extraction:
- Move menubarPopover interface/constants to popover.go (no build tag)
- Rename companion_darwin.go -> companion_unix.go (darwin || linux)
- Extend runtime_state.go build tag to include linux
- Create menubar_linux.go with Init/Stop/IsSupported/IsRunning
- Update menubar_stub.go to exclude linux from no-op builds
- Remove hardcoded runtime.GOOS check in menubar_runtime.go

Phase 2 - Linux native popover:
- popover_linux.c: GTK3 popup window + WebKitGTK WebView
- webview_linux.go: CGO bridge (same API as webview_darwin.go)
- webview_stub_linux.go: browser fallback when CGO disabled
- Focus-out dismissal, top-right workarea positioning
- External URL interception with browser redirect

Phase 3 - Build & release infrastructure:
- app.sh: auto-detect GTK/WebKit and build with menubar tags
- release.yml: add build-linux-desktop job on ubuntu-latest
- Two Linux variants: headless (no CGO) and desktop (with menubar)

Phase 4 - Tests:
- Rename runtime_state_darwin_test.go -> runtime_state_unix_test.go
- Add webview_linux_test.go for popover lifecycle testing
Critical fixes:
- Remove //go:build ignore from popover_linux.c that prevented CGO
  compilation (C symbols would be undefined at link time)
- Extract shared TestMain/runOnMainThread to webview_cgo_test.go
  with //go:build menubar && (darwin || linux) && cgo to eliminate
  duplicate symbol risk between platform test files
- Add gtk_widget_grab_focus after gtk_window_present in do_show
  and do_toggle to ensure focus-out dismissal works on GNOME/KDE
- Fix potential deadlock in onwatch_run_on_main_sync by using
  g_cond_wait_until with g_main_context_iteration fallback when
  GTK main loop is not yet processing idle sources

Other fixes:
- Update stale comment in menubar_darwin.go referencing deleted
  companion_darwin.go (now companion_unix.go)
- Add GTK/WebKit dev lib installation to app.sh --deps for
  Debian/Ubuntu and Fedora/RHEL

Test coverage for new/changed code:
- popover_test.go: dimension constants, error sentinel
- companion_unix_test.go: normalizeRefreshSeconds boundaries,
  trayTooltip nil/zero/normal cases, tray controller URL
  construction with nil/custom config, stopCompanion idempotency
- runtime_state_unix_test.go: PID path construction, readPID
  with valid/empty/malformed/whitespace content, companionPIDEnvValue,
  companionProcessRunning smoke, TriggerRefresh with missing/dead PID
- Fix TrayTitle comment: "macOS tray icon" -> "system tray icon"
- Remove dead Windows branch in defaultCompanionPIDDir (unreachable
  under darwin || linux build tag) and unused runtime import
- Remove unused showMenubar method from companion_unix.go
- Replace no-op TestRefreshStatusNilController with honest t.Skip
- Align menubar_linux.go Init comment with menubar_darwin.go
- Add doc comments to webview stub files explaining browser fallback
@codecov
Copy link

codecov bot commented Mar 18, 2026

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
menubar_runtime.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@xyz-rainbow
Copy link

Starting the review now, gonna send updates soon

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.

2 participants