Xlib's default protocol-error handler calls exit() on any async X error, so a tray race (tray manager window dying between find and dock, XMoveWindow on a popup the WM already destroyed) would take the whole UI process down. Install process-global logging no-op handlers via XSetErrorHandler/ XSetIOErrorHandler after every XOpenDisplay. Since the handler slot is process-global and GDK init can clobber it, additionally wrap the popup code's raw Xlib calls on GDK's Display in gdk_x11_display_error_trap_push/ pop_ignored, which is independent of the global handler.
2.1 KiB
Linux tray support (StatusNotifierWatcher + XEmbed)
Minimal WMs (Fluxbox, OpenBox, i3, dwm, vanilla GNOME without the AppIndicator extension) don't ship a StatusNotifierWatcher, so tray icons using libayatana-appindicator / freedesktop StatusNotifier silently fail. main.go calls startStatusNotifierWatcher() before NewTray so the Wails systray's RegisterStatusNotifierItem call hits the in-process watcher we control.
tray_watcher_linux.go— ownsorg.kde.StatusNotifierWatcheron the session bus if no other process has it. Safe to call unconditionally.xembed_host_linux.go+xembed_tray_linux.{c,h}— when an XEmbed tray (_NET_SYSTEM_TRAY_S0) is available, also start an in-process XEmbed host that bridges the SNI icon into the XEmbed tray. ReadsIconPixmapover D-Bus, draws via cairo+X11, polls for clicks, fetchescom.canonical.dbusmenu.GetLayoutfor the popup menu, firescom.canonical.dbusmenu.Eventon click.
X error handling. Xlib's default protocol-error handler calls exit(), so a single async X error from a tray race (tray manager window dying between xembed_find_tray and xembed_dock; XMoveWindow on a popup the WM already destroyed) would take the whole UI process down. xembed_install_error_handlers() (xembed_tray_linux.c) installs process-global logging no-op handlers via XSetErrorHandler/XSetIOErrorHandler; it's idempotent and called from the Go side after every XOpenDisplay (newXembedHost, xembedTrayAvailable). Because the handler slot is process-global, GTK's GDK init can clobber it (whoever installs last wins) — so the popup code's raw Xlib calls on GDK's Display (x11_move_window, the submenu XTranslateCoordinates) are additionally wrapped in GDK's own gdk_x11_display_error_trap_push/_pop_ignored, which is independent of the global handler. (Those GDK X11 backend calls warn -Wdeprecated-declarations in GTK4, like the existing gdk_x11_surface_get_xid/gdk_x11_display_get_xdisplay calls — expected, still functional.)
Build is gated on linux && !386; the 386 build (no cgo) and non-Linux builds use the tray_watcher_other.go no-op.