diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index 33638e7fc86..303e7c354fc 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file ## Added +- Add `LinuxOptions.ApplicationID` to override the GTK application ID, which Flatpak and Snap require to be a domain the publisher controls (#6020) ## Changed diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index 6ecd09c03f4..15adb80a41f 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -233,9 +233,16 @@ func (a *linuxApp) unregisterWindow(window windowPointer) { func newPlatformApp(parent *App) *linuxApp { name := sanitizeAppName(parent.options.Name) + application := pointer(nil) + if id := parent.options.Linux.ApplicationID; id != "" { + application = appNewWithID(id) + } + if application == nil { + application = appNew(name) + } app := &linuxApp{ parent: parent, - application: appNew(name), + application: application, activated: make(chan struct{}), windowMap: map[windowPointer]uint{}, } diff --git a/v3/pkg/application/application_options.go b/v3/pkg/application/application_options.go index 560436a36d8..e13637e2b27 100644 --- a/v3/pkg/application/application_options.go +++ b/v3/pkg/application/application_options.go @@ -329,6 +329,16 @@ type LinuxOptions struct { // //[see the docs]: https://docs.gtk.org/glib/func.set_prgname.html ProgramName string + + // ApplicationID overrides the GTK application ID, which defaults to + // org.wails.. + // + // It is the app ID a Wayland compositor sees, and a desktop entry is found + // by matching it, so a window's icon and its grouping both hang off it. + // Flatpak and Snap go further and require the ID to be a domain the + // publisher controls, which the default cannot be. Must be a valid GTK + // application ID; an invalid one is ignored in favour of the default. + ApplicationID string } /********* iOS Options *********/ diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index e4751c88262..67d3d96ce18 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -144,12 +144,29 @@ func appName() string { } func appNew(name string) pointer { + // A GApplication ID is capped at 255 characters and the prefix takes ten, + // so a long enough app name would otherwise build an ID GTK refuses, + // leaving no application at all. + const maxNameLength = 245 + if len(name) > maxNameLength { + name = name[:maxNameLength] + } + return appNewWithID(fmt.Sprintf("org.wails.%s", name)) +} + +// appNewWithID creates the GtkApplication under an explicit ID. GTK refuses an +// invalid one, so it is checked first and the caller's default used instead — +// an application that fails to construct has no window to report the problem +// in. +func appNewWithID(appID string) pointer { C.install_signal_handlers() - appId := fmt.Sprintf("org.wails.%s", name) - nameC := C.CString(appId) - defer C.free(unsafe.Pointer(nameC)) - return pointer(C.gtk_application_new(nameC, C.APPLICATION_DEFAULT_FLAGS)) + idC := C.CString(appID) + defer C.free(unsafe.Pointer(idC)) + if C.g_application_id_is_valid(idC) == 0 { + return nil + } + return pointer(C.gtk_application_new(idC, C.APPLICATION_DEFAULT_FLAGS)) } func setProgramName(prgName string) {