Repository navigation
Conversation
Wayland compositors pair a toplevel with its launcher entry, and so show
the app's icon in the dock, only when the xdg-shell app_id equals the
.desktop file's basename. GTK 3 (Tao registers no GApplication id) sends
g_get_prgname(), i.e. the launcher binary's name. For the JVM packages
that is the jpackage launcher bin/<packageName>, which the
/opt/<app>/<executableName> alias execs, while electron-builder names the
entry after executableName = linux.packageName ?: packageName. Whenever
linux.packageName differs from packageName the two never match: GNOME
shows a generic icon and a separate dock item.
Repro (GNOME 50, Wayland, Ubuntu): the Lab with
linux { packageName = "nucleus-lab" }, WAYLAND_DEBUG=1:
- deb (/opt/nucleus-lab/nucleus-lab): nucleus-lab.desktop,
before xdg_toplevel.set_app_id("NucleusLab"), after ("nucleus-lab")
- AppImage (APPIMAGE_EXTRACT_AND_RUN=1): nucleus-lab.desktop in
$APPDIR, before "NucleusLab", after "nucleus-lab"
- X11 (GDK_BACKEND=x11): WM_CLASS "NucleusLab", "NucleusLab" both
before and after
The plugin bakes linux.desktop.entry.id into nucleus-app.properties
(NucleusApp.linuxDesktopEntryId), and TaoApplication hands it to the
bridge, which calls g_set_prgname once GTK is up and only if its display
is Wayland: on X11 the prgname is also WM_CLASS, left as it is. Flatpak
sends FLATPAK_ID; snaps and native images keep GTK's default (a native
image's binary is already named after its entry, graalvm.imageName).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sproctor
marked this pull request as ready for review
October 19, 2026 19:20
…guard It covers the pure app_id selection in TaoWaylandAppId.resolve, with the native-image case passed in as a parameter: no ComposeScene to run in the native-image battery, like the other pure-function tests listed there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sproctor
marked this pull request as draft
October 20, 2026 02:18
X11 desktops pair a window with its launcher entry by matching WM_CLASS against the entry's StartupWMClass. GTK 3 names Tao windows after the launcher binary (NucleusLab / NucleusLab), while the plugin writes StartupWMClass as linux.startupWMClass or the main class with dots replaced by dashes (the AWT XToolkit convention), so the two never agreed unless the app set startupWMClass to the binary's name. The plugin already bakes that value as startup.wm.class; NucleusApp now exposes it as linuxStartupWmClass, and the bridge sets both halves of WM_CLASS (g_set_prgname for the instance, gdk_set_program_class for the class) on an X11 display, next to the Wayland app_id. JVM packages and native images write the same entry, so both get it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sproctor
marked this pull request as ready for review
October 20, 2026 02:40
Collaborator
|
Could you check if this breaks hot reload? |
Contributor
Author
|
It seems fine with hot reload. Did you have a problem with it? For me, hot reload is running the app through XWayland so just the X11 part would apply anyway. |
Keeps both sides of the TaoApplication / NativeTaoBridge conflicts, and moves nativeSetWaylandAppId / nativeSetX11WmClass into NativeTaoLinuxIdentityBridge: with upstream's new natives NativeTaoBridge crossed detekt's LargeClass limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linux desktops pair a window with its launcher entry, and so show the app's icon in the dock, by a name the window carries: the xdg-shell
app_idon Wayland,WM_CLASSon X11. Tao windows got both wrong in common configurations. This PR fixes both halves.Reproduction
Setup:
nucleus-3.0, Ubuntu 26.04 with GNOME 50. The Lab's deb (packageName = "NucleusLab",mainClass = "dev.nucleusframework.lab.app.MainKt"), run from/opt/<app>/bin/NucleusLab.Wayland, with
linux { packageName = "nucleus-lab" }, run withWAYLAND_DEBUG=1and looking forxdg_toplevel.set_app_id:/opt/nucleus-lab/nucleus-lab)nucleus-lab.desktopset_app_id("NucleusLab")set_app_id("nucleus-lab")APPIMAGE_EXTRACT_AND_RUN=1)nucleus-lab.desktopin$APPDIR"NucleusLab""nucleus-lab"Without
linux.packageNamethe entry isNucleusLab.desktopand the app_id staysNucleusLab(re-checked after the X11 change).X11, default Lab config,
GDK_BACKEND=x11, readingxprop WM_CLASSon the toplevel:NucleusLab.desktop:StartupWMClass=dev-nucleusframework-lab-app-MainKtWM_CLASS "NucleusLab", "NucleusLab"WM_CLASS "dev-nucleusframework-lab-app-MainKt", "dev-nucleusframework-lab-app-MainKt"So on X11 the window never matched its entry's
StartupWMClassunless the app setlinux.startupWMClassto the launcher's name. A native image shows the same thing: an app withgraalvm { imageName = "warlock" }getsStartupWMClass=warlockfe-warlock3-app-MainKtin its deb, while its window isWM_CLASS "warlock", "Warlock".GNOME Shell hides this in the default setup: when
StartupWMClassdoes not match, it also looks for a desktop file named after theWM_CLASS, and the Lab'sNucleusLab.desktopis. The icon goes missing whenever the entry is named otherwise, which is the samelinux.packageNamecase as on Wayland. Checked by eye in the dock, GNOME 50 on X11 (XWayland), Lab run withGDK_BACKEND=x11against a user-level entry:StartupWMClassWM_CLASSnucleus-lab-demo.desktopdev-nucleusframework-lab-app-MainKtNucleusLabnucleus-lab-demo.desktopNucleusLab(edited to match)NucleusLabNucleusLab.desktopdev-nucleusframework-lab-app-MainKtdev-nucleusframework-lab-app-MainKtThe second row confirms the mismatch is the cause. In the third, the filename cannot be what matched, since the window is no longer named
NucleusLab.Root cause
GTK 3 (Tao registers no
GApplicationid) derives both names fromg_get_prgname(), the launcher binary's name:bin/<packageName>, which the/opt/<app>/<executableName>alias execs, while electron-builder names the entry afterexecutableName = linux.packageName ?: packageName.WM_CLASSis the prgname (instance) andgdk_get_program_class()(class, the prgname capitalised). The plugin writesStartupWMClassaslinux.startupWMClass, else the main class with dots replaced by dashes, the name AWT's XToolkit gives its windows. That default was right for AWT apps; a Tao window is never named that way.Fix
linux.desktop.entry.idintonucleus-app.properties, read back asNucleusApp.linuxDesktopEntryId. It already bakedstartup.wm.class(the exact value it writes asStartupWMClass, for JVM and native packages alike), now read back asNucleusApp.linuxStartupWmClass.TaoApplicationhands both to the native bridge. Once GTK is up and its display chosen, before any window exists, the bridge applies the one for that display:g_set_prgname(app_id).g_set_prgname(wm_class)andgdk_set_program_class(wm_class), so both halves ofWM_CLASSequalStartupWMClass(GNOME Shell matches either).FLATPAK_ID. Snaps and native images keep GTK's default, since a native image's binary is already named after its entry (graalvm.imageName).StartupWMClass.The default
StartupWMClassderivation is unchanged, so AWT apps packaged by the plugin (which XToolkit names after the main class) keep matching.Alternative considered: fixing it in the packager instead, by writing the launcher's name as
StartupWMClass(the second row above shows that works too). But the plugin packages AWT apps as well, whose windows XToolkit names after the main class, and it cannot tell which backend an app uses. Applying the baked value at run time keeps the entry authoritative for both, and an explicitlinux.startupWMClasskeeps working.This is the window-matching half of AppImage desktop integration (NucleusFramework/Nucleus#847): once the entry is installed, the desktop can only pair the windows with it when the app_id or
WM_CLASSmatches.Testing
TaoWaylandAppIdTest: which app_id is chosen for each packaging case.TaoX11WmClassTest(new): the bakedStartupWMClassis used as written, and nothing baked keeps GTK's default.WAYLAND_DEBUGandxpropbefore/after above, from the packaged Lab deb, and the X11 dock-icon table../gradlew :core-runtime:check :decorated-window-tao:check: 135 + 584 tests (1 skipped), apiCheck, detekt and ktlint clean.Not tested: the dock icon on Wayland and on KDE (GNOME blocks the calls that would read the dock from a script, so it was checked by eye on X11 only), Flatpak and snap packages, a native image built from this branch (the native
WM_CLASSmismatch above was observed on a native build of the published 3.0.0-dev artifacts), and macOS/Windows (the change is Linux-only).Known gap: GDK's hidden 10×10 client-leader window keeps the launcher's name in its
WM_CLASS. GDK creates it when the display opens, before the display type is known to the bridge. It is never mapped, so docks do not match against it.🤖 Generated with Claude Code