mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
* fix: prevent window menu crash on Wayland by realizing window before showing On Wayland with GTK3, the appmenu-gtk-module tries to set DBus properties for global menu integration before the window is fully realized, causing a crash with the error "GDK_IS_WAYLAND_WINDOW (window) assertion failed". The fix calls gtk_widget_realize() before gtk_widget_show_all() to ensure the window has a valid GdkWindow when the menu system accesses it. This fix is applied to both the CGO and purego implementations. Fixes wailsapp/wails#4769 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add Wayland detection to wails3 doctor and test for #4769 - Add Wayland session detection row to `wails3 doctor` output on Linux - Create test project v3/test/4769-menu to manually verify the menu fix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: sanitize GTK application name to prevent crashes with invalid characters Application names containing spaces, parentheses, hash symbols, or other invalid characters would cause GTK to fail with an assertion error. Added sanitizeAppName() function that: - Replaces invalid characters with underscores - Handles leading digits - Removes consecutive underscores - Defaults to "wailsapp" if empty 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
726c7546ce
commit
e80cf28578
8 changed files with 205 additions and 17 deletions
|
|
@ -16,12 +16,15 @@ 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
|
||||
<!-- 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
|
||||
<!-- Bug fixes -->
|
||||
|
||||
## Deprecated
|
||||
|
|
|
|||
|
|
@ -13,12 +13,23 @@ import (
|
|||
func getInfo() (map[string]string, bool) {
|
||||
result := make(map[string]string)
|
||||
|
||||
// Check session type (X11/Wayland)
|
||||
result["XDG_SESSION_TYPE"] = getSessionType()
|
||||
|
||||
// Check for NVIDIA driver
|
||||
result["NVIDIA Driver"] = getNvidiaDriverInfo()
|
||||
|
||||
return result, true
|
||||
}
|
||||
|
||||
func getSessionType() string {
|
||||
sessionType := os.Getenv("XDG_SESSION_TYPE")
|
||||
if sessionType == "" {
|
||||
return "unset"
|
||||
}
|
||||
return sessionType
|
||||
}
|
||||
|
||||
func getNvidiaDriverInfo() string {
|
||||
version, err := os.ReadFile("/sys/module/nvidia/version")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import "C"
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -27,6 +28,29 @@ import (
|
|||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// sanitizeAppName sanitizes the application name to be a valid GTK/D-Bus application ID.
|
||||
// Valid IDs contain only alphanumeric characters, hyphens, and underscores.
|
||||
// They must not start with a digit.
|
||||
var invalidAppNameChars = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
|
||||
var leadingDigits = regexp.MustCompile(`^[0-9]+`)
|
||||
|
||||
func sanitizeAppName(name string) string {
|
||||
// Replace invalid characters with underscores
|
||||
name = invalidAppNameChars.ReplaceAllString(name, "_")
|
||||
// Prefix with underscore if starts with digit
|
||||
name = leadingDigits.ReplaceAllString(name, "_$0")
|
||||
// Remove consecutive underscores
|
||||
for strings.Contains(name, "__") {
|
||||
name = strings.ReplaceAll(name, "__", "_")
|
||||
}
|
||||
// Trim leading/trailing underscores
|
||||
name = strings.Trim(name, "_")
|
||||
if name == "" {
|
||||
name = "wailsapp"
|
||||
}
|
||||
return strings.ToLower(name)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// FIXME: This should be handled appropriately in the individual files most likely.
|
||||
// Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings
|
||||
|
|
@ -250,11 +274,7 @@ func (a *linuxApp) monitorThemeChanges() {
|
|||
}
|
||||
|
||||
func newPlatformApp(parent *App) *linuxApp {
|
||||
|
||||
name := strings.ToLower(strings.Replace(parent.options.Name, " ", "", -1))
|
||||
if name == "" {
|
||||
name = "undefined"
|
||||
}
|
||||
name := sanitizeAppName(parent.options.Name)
|
||||
app := &linuxApp{
|
||||
parent: parent,
|
||||
application: appNew(name),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package application
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -385,12 +384,7 @@ func appName() string {
|
|||
func appNew(name string) pointer {
|
||||
C.install_signal_handlers()
|
||||
|
||||
// prevent leading number
|
||||
if matched, _ := regexp.MatchString(`^\d+`, name); matched {
|
||||
name = fmt.Sprintf("_%s", name)
|
||||
}
|
||||
name = strings.Replace(name, "(", "_", -1)
|
||||
name = strings.Replace(name, ")", "_", -1)
|
||||
// Name is already sanitized by sanitizeAppName() in application_linux.go
|
||||
appId := fmt.Sprintf("org.wails.%s", name)
|
||||
nameC := C.CString(appId)
|
||||
defer C.free(unsafe.Pointer(nameC))
|
||||
|
|
@ -1169,6 +1163,11 @@ func (w *linuxWebviewWindow) windowShow() {
|
|||
if w.gtkWidget() == nil {
|
||||
return
|
||||
}
|
||||
// Realize the window first to ensure it has a valid GdkWindow.
|
||||
// This prevents crashes on Wayland when appmenu-gtk-module tries to
|
||||
// set DBus properties for global menu integration before the window
|
||||
// is fully realized. See: https://github.com/wailsapp/wails/issues/4769
|
||||
C.gtk_widget_realize(w.gtkWidget())
|
||||
C.gtk_widget_show_all(w.gtkWidget())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ var (
|
|||
gtkWidgetGetWindow func(pointer) pointer
|
||||
gtkWidgetHide func(pointer)
|
||||
gtkWidgetIsVisible func(pointer) bool
|
||||
gtkWidgetRealize func(pointer)
|
||||
gtkWidgetShow func(pointer)
|
||||
gtkWidgetShowAll func(pointer)
|
||||
gtkWidgetSetAppPaintable func(pointer, int)
|
||||
|
|
@ -330,6 +331,7 @@ func init() {
|
|||
purego.RegisterLibFunc(>kWidgetGetWindow, gtk, "gtk_widget_get_window")
|
||||
purego.RegisterLibFunc(>kWidgetHide, gtk, "gtk_widget_hide")
|
||||
purego.RegisterLibFunc(>kWidgetIsVisible, gtk, "gtk_widget_is_visible")
|
||||
purego.RegisterLibFunc(>kWidgetRealize, gtk, "gtk_widget_realize")
|
||||
purego.RegisterLibFunc(>kWidgetSetAppPaintable, gtk, "gtk_widget_set_app_paintable")
|
||||
purego.RegisterLibFunc(>kWidgetSetName, gtk, "gtk_widget_set_name")
|
||||
purego.RegisterLibFunc(>kWidgetSetSensitive, gtk, "gtk_widget_set_sensitive")
|
||||
|
|
@ -393,11 +395,8 @@ func appName() string {
|
|||
func appNew(name string) pointer {
|
||||
GApplicationDefaultFlags := uint(0)
|
||||
|
||||
name = strings.ToLower(name)
|
||||
if name == "" {
|
||||
name = "undefined"
|
||||
}
|
||||
identifier := fmt.Sprintf("org.wails.%s", strings.Replace(name, " ", "-", -1))
|
||||
// Name is already sanitized by sanitizeAppName() in application_linux.go
|
||||
identifier := fmt.Sprintf("org.wails.%s", name)
|
||||
|
||||
return pointer(gtkApplicationNew(identifier, GApplicationDefaultFlags))
|
||||
}
|
||||
|
|
@ -863,6 +862,11 @@ func windowResize(window pointer, width, height int) {
|
|||
}
|
||||
|
||||
func windowShow(window pointer) {
|
||||
// Realize the window first to ensure it has a valid GdkWindow.
|
||||
// This prevents crashes on Wayland when appmenu-gtk-module tries to
|
||||
// set DBus properties for global menu integration before the window
|
||||
// is fully realized. See: https://github.com/wailsapp/wails/issues/4769
|
||||
gtkWidgetRealize(pointer(window))
|
||||
gtkWidgetShowAll(pointer(window))
|
||||
}
|
||||
|
||||
|
|
|
|||
22
v3/test/4769-menu/Taskfile.yml
Normal file
22
v3/test/4769-menu/Taskfile.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
version: '3'
|
||||
|
||||
vars:
|
||||
APP_NAME: "4769-menu{{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 .
|
||||
59
v3/test/4769-menu/assets/index.html
Normal file
59
v3/test/4769-menu/assets/index.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Menu Wayland Test (#4769)</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
.container {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 1rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
h1 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.success {
|
||||
color: #90EE90;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Menu Wayland Test</h1>
|
||||
<p>GitHub Issue: <code>#4769</code></p>
|
||||
<p>This tests the window menu on Wayland.</p>
|
||||
<p class="success">If you can see this window with the menu bar above, the fix works!</p>
|
||||
<p style="margin-top: 1.5rem; font-size: 0.9rem; opacity: 0.7;">
|
||||
Try clicking the menu items (File, Edit, Help) to verify they work.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
70
v3/test/4769-menu/main.go
Normal file
70
v3/test/4769-menu/main.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
//go:embed assets/*
|
||||
var assets embed.FS
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Name: "Menu Wayland Test (#4769)",
|
||||
Description: "Test for window menu crash on Wayland",
|
||||
Assets: application.AssetOptions{
|
||||
Handler: application.BundledAssetFileServer(assets),
|
||||
},
|
||||
})
|
||||
|
||||
// Create a menu - this would crash on Wayland before the fix
|
||||
menu := app.NewMenu()
|
||||
|
||||
fileMenu := menu.AddSubmenu("File")
|
||||
fileMenu.Add("New").OnClick(func(ctx *application.Context) {
|
||||
log.Println("New clicked")
|
||||
})
|
||||
fileMenu.Add("Open").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Open clicked")
|
||||
})
|
||||
fileMenu.AddSeparator()
|
||||
fileMenu.Add("Exit").OnClick(func(ctx *application.Context) {
|
||||
app.Quit()
|
||||
})
|
||||
|
||||
editMenu := menu.AddSubmenu("Edit")
|
||||
editMenu.Add("Cut").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Cut clicked")
|
||||
})
|
||||
editMenu.Add("Copy").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Copy clicked")
|
||||
})
|
||||
editMenu.Add("Paste").OnClick(func(ctx *application.Context) {
|
||||
log.Println("Paste clicked")
|
||||
})
|
||||
|
||||
helpMenu := menu.AddSubmenu("Help")
|
||||
helpMenu.Add("About").OnClick(func(ctx *application.Context) {
|
||||
log.Println("About clicked")
|
||||
})
|
||||
|
||||
// Create window with menu attached via Linux options
|
||||
// This tests the fix for issue #4769
|
||||
app.Window.NewWithOptions(application.WebviewWindowOptions{
|
||||
Title: "Menu Wayland Test (#4769)",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
Linux: application.LinuxWindow{
|
||||
Menu: menu,
|
||||
},
|
||||
})
|
||||
|
||||
log.Println("Starting application - if you see this on Wayland, the fix works!")
|
||||
err := app.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue