mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 22:55:48 +01:00
fix(linux): add mutex protection for menuActive flag
Address data race in menuActive flag access: - Add menuMutex sync.RWMutex to WindowAttachConfig struct - Protect menuActive read in WindowLostFocus handler with RLock - Protect menuActive writes in AboutToShow and Event handlers with Lock This fixes concurrent access between DBus callbacks (writes) and the WindowLostFocus handler (reads) that could cause data races. Addresses CodeRabbit review comments from PR #4775. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4fbe0353f7
commit
304f958640
7 changed files with 174 additions and 2 deletions
|
|
@ -17,20 +17,22 @@ After processing, the content will be moved to the main changelog and this file
|
|||
|
||||
## Added
|
||||
- Add `XDG_SESSION_TYPE` to `wails3 doctor` output on Linux by @leaanthony
|
||||
- Add additional WebKit2 load-change events for Linux: `WindowLoadStarted`, `WindowLoadRedirected`, `WindowLoadCommitted`, `WindowLoadFinished` (#3896) by @leaanthony
|
||||
<!-- New features, capabilities, or enhancements -->
|
||||
|
||||
## Changed
|
||||
<!-- Changes in existing functionality -->
|
||||
|
||||
## Fixed
|
||||
- Fix window menu crash on Wayland caused by appmenu-gtk-module accessing unrealized window (#4769) by @leaanthony
|
||||
- Fix GTK application crash when app name contains invalid characters (spaces, parentheses, etc.) by @leaanthony
|
||||
- Fix "not enough memory" error when initializing drag and drop on Windows (#4701) by @overlordtm
|
||||
- Fix systray context menu hiding attached window on Linux (#4494) by @leaanthony
|
||||
<!-- Bug fixes -->
|
||||
|
||||
## Deprecated
|
||||
<!-- Soon-to-be removed features -->
|
||||
|
||||
## Removed
|
||||
- **BREAKING**: Remove `linux:WindowLoadChanged` event - use `linux:WindowLoadFinished` instead for detecting when WebView has finished loading (#3896) by @leaanthony
|
||||
<!-- Features removed in this release -->
|
||||
|
||||
## Security
|
||||
|
|
|
|||
|
|
@ -101,6 +101,14 @@ func (s *SystemTray) Run() {
|
|||
if s.attachedWindow.Window != nil {
|
||||
// Setup listener
|
||||
s.attachedWindow.Window.OnWindowEvent(events.Common.WindowLostFocus, func(event *WindowEvent) {
|
||||
// On Linux, don't hide when the systray menu is active (opening or open).
|
||||
// The menu steals focus from the window, but we don't want to hide in this case.
|
||||
s.attachedWindow.menuMutex.RLock()
|
||||
menuActive := s.attachedWindow.menuActive
|
||||
s.attachedWindow.menuMutex.RUnlock()
|
||||
if menuActive {
|
||||
return
|
||||
}
|
||||
s.attachedWindow.Window.Hide()
|
||||
// Special handler for Windows
|
||||
if runtime.GOOS == "windows" {
|
||||
|
|
@ -274,6 +282,13 @@ type WindowAttachConfig struct {
|
|||
|
||||
// Used to ensure that the window state is read on first click
|
||||
initialClick sync.Once
|
||||
|
||||
// Protects menuActive from concurrent access
|
||||
menuMutex sync.RWMutex
|
||||
|
||||
// Indicates that the systray menu is currently open or about to open.
|
||||
// Used on Linux to prevent hiding the attached window when the menu steals focus.
|
||||
menuActive bool
|
||||
}
|
||||
|
||||
// AttachWindow attaches a window to the system tray. The window will be shown when the system tray icon is clicked.
|
||||
|
|
|
|||
|
|
@ -620,6 +620,11 @@ func iconToPX(icon []byte) (PX, error) {
|
|||
|
||||
// AboutToShow is an implementation of the com.canonical.dbusmenu.AboutToShow method.
|
||||
func (s *linuxSystemTray) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
|
||||
// Mark menu as active to prevent hiding attached window when menu steals focus.
|
||||
// This is called by the desktop environment before showing the menu.
|
||||
s.parent.attachedWindow.menuMutex.Lock()
|
||||
s.parent.attachedWindow.menuActive = true
|
||||
s.parent.attachedWindow.menuMutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -646,6 +651,10 @@ func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, tim
|
|||
InvokeAsync(item.menuItem.handleClick)
|
||||
}
|
||||
case "opened":
|
||||
// Menu is now open - keep the flag active
|
||||
s.parent.attachedWindow.menuMutex.Lock()
|
||||
s.parent.attachedWindow.menuActive = true
|
||||
s.parent.attachedWindow.menuMutex.Unlock()
|
||||
if s.parent.clickHandler != nil {
|
||||
s.parent.clickHandler()
|
||||
}
|
||||
|
|
@ -653,6 +662,10 @@ func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, tim
|
|||
s.parent.onMenuOpen()
|
||||
}
|
||||
case "closed":
|
||||
// Menu is closed - clear the flag so normal focus behavior resumes
|
||||
s.parent.attachedWindow.menuMutex.Lock()
|
||||
s.parent.attachedWindow.menuActive = false
|
||||
s.parent.attachedWindow.menuMutex.Unlock()
|
||||
if s.parent.onMenuClose != nil {
|
||||
s.parent.onMenuClose()
|
||||
}
|
||||
|
|
|
|||
22
v3/test/4494-systray/Taskfile.yml
Normal file
22
v3/test/4494-systray/Taskfile.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
version: '3'
|
||||
|
||||
vars:
|
||||
APP_NAME: "4494-systray{{exeExt}}"
|
||||
|
||||
tasks:
|
||||
build:
|
||||
summary: Builds the test application
|
||||
cmds:
|
||||
- go build -o bin/{{.APP_NAME}} .
|
||||
|
||||
run:
|
||||
summary: Runs the test application
|
||||
deps:
|
||||
- build
|
||||
cmds:
|
||||
- ./bin/{{.APP_NAME}}
|
||||
|
||||
dev:
|
||||
summary: Builds and runs the test application
|
||||
cmds:
|
||||
- go run .
|
||||
BIN
v3/test/4494-systray/assets/icon.png
Normal file
BIN
v3/test/4494-systray/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
56
v3/test/4494-systray/assets/index.html
Normal file
56
v3/test/4494-systray/assets/index.html
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Systray Test (#4494)</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
min-height: calc(100vh - 40px);
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.test-info {
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.bug {
|
||||
color: #ffcccc;
|
||||
font-weight: bold;
|
||||
}
|
||||
.expected {
|
||||
color: #ccffcc;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Systray Test (#4494)</h1>
|
||||
|
||||
<div class="test-info">
|
||||
<h3>Test Steps:</h3>
|
||||
<ol>
|
||||
<li>Look for the systray icon in your system tray</li>
|
||||
<li>Right-click the systray icon to open the context menu</li>
|
||||
</ol>
|
||||
|
||||
<p class="expected">Expected: This window should stay visible when the context menu opens</p>
|
||||
<p class="bug">Bug: On Linux (Wayland/KDE), this window hides when right-clicking the systray icon</p>
|
||||
</div>
|
||||
|
||||
<div class="test-info">
|
||||
<h3>Issue Details:</h3>
|
||||
<p>When an attached window loses focus (which happens when the context menu opens),
|
||||
the WindowLostFocus event triggers and hides the window.</p>
|
||||
<p>This is problematic because the user is still interacting with the systray,
|
||||
not clicking away from it.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
64
v3/test/4494-systray/main.go
Normal file
64
v3/test/4494-systray/main.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
//go:embed assets/*
|
||||
var assets embed.FS
|
||||
|
||||
//go:embed assets/icon.png
|
||||
var icon []byte
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Name: "Systray Test (#4494)",
|
||||
Description: "Test for systray context menu hiding attached window",
|
||||
Assets: application.AssetOptions{
|
||||
Handler: application.BundledAssetFileServer(assets),
|
||||
},
|
||||
})
|
||||
|
||||
// Create the main window
|
||||
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
|
||||
Title: "Systray Test (#4494)",
|
||||
Width: 400,
|
||||
Height: 300,
|
||||
})
|
||||
|
||||
// Create a systray menu
|
||||
menu := app.NewMenu()
|
||||
menu.Add("Show Window").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Show Window clicked")
|
||||
window.Show()
|
||||
})
|
||||
menu.Add("Hide Window").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Hide Window clicked")
|
||||
window.Hide()
|
||||
})
|
||||
menu.AddSeparator()
|
||||
menu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Quit clicked")
|
||||
app.Quit()
|
||||
})
|
||||
|
||||
// Create system tray with attached window
|
||||
// Issue #4494: Right-clicking to open context menu hides the attached window
|
||||
systemTray := app.SystemTray.New()
|
||||
systemTray.SetIcon(icon)
|
||||
systemTray.SetMenu(menu)
|
||||
systemTray.AttachWindow(window)
|
||||
|
||||
log.Println("Starting application...")
|
||||
log.Println("TEST: Right-click the systray icon. The window should NOT hide when the context menu opens.")
|
||||
log.Println("BUG: On Linux (especially Wayland/KDE), the window hides when right-clicking the systray icon.")
|
||||
|
||||
err := app.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue