diff --git a/packages/app-builder-lib/src/options/metadata.ts b/packages/app-builder-lib/src/options/metadata.ts index 223362255a7..94e3fb86969 100644 --- a/packages/app-builder-lib/src/options/metadata.ts +++ b/packages/app-builder-lib/src/options/metadata.ts @@ -50,6 +50,8 @@ export interface Metadata { readonly productName?: string | null /** @private */ readonly main?: string | null + /** @private */ + readonly desktopName?: string | null } export interface AuthorMetadata { diff --git a/packages/app-builder-lib/src/targets/LinuxTargetHelper.ts b/packages/app-builder-lib/src/targets/LinuxTargetHelper.ts index 14f24257c71..6b43cde445f 100644 --- a/packages/app-builder-lib/src/targets/LinuxTargetHelper.ts +++ b/packages/app-builder-lib/src/targets/LinuxTargetHelper.ts @@ -129,6 +129,17 @@ export class LinuxTargetHelper { } } + // https://github.com/electron-userland/electron-builder/issues/7159 + // Electron uses desktopName from package.json to set the app_id, which should match StartupWMClass. + // https://github.com/electron/electron/blob/9a7b73b5334f1d72c08e2d5e94106706ed751186/lib/browser/init.ts#L128-L133 + const desktopName = packager.info.metadata.desktopName + let wmClass: string + if (!isEmptyOrSpaces(desktopName)) { + wmClass = desktopName!.replace(/\.desktop$/, "") + } else { + wmClass = appInfo.productName + } + const desktopMeta: any = { Name: appInfo.productName, Exec: exec, @@ -140,7 +151,7 @@ export class LinuxTargetHelper { // to get WM_CLASS of running window: xprop WM_CLASS // StartupWMClass doesn't work for unicode // https://github.com/electron/electron/blob/2-0-x/atom/browser/native_window_views.cc#L226 - StartupWMClass: appInfo.productName, + StartupWMClass: wmClass, ...extra, ...(targetSpecificOptions.desktop?.entry ?? {}), } diff --git a/test/snapshots/linux/linuxPackagerTest.js.snap b/test/snapshots/linux/linuxPackagerTest.js.snap index 92d7a6413a1..d7403ad10f9 100644 --- a/test/snapshots/linux/linuxPackagerTest.js.snap +++ b/test/snapshots/linux/linuxPackagerTest.js.snap @@ -42,6 +42,35 @@ exports[`AppImage - default icon, custom executable and custom desktop 2`] = ` exports[`AppImage - deprecated systemIntegration 1`] = `"ERR_ELECTRON_BUILDER_INVALID_CONFIGURATION"`; +exports[`AppImage - desktopName sets StartupWMClass 1`] = ` +"[Desktop Entry] +Name=Signal +Exec=AppRun --no-sandbox %U +Terminal=false +Type=Application +Icon=testapp +StartupWMClass=signal +Comment=Test Application (test quite “ #378) +Categories=Development; +" +`; + +exports[`AppImage - desktopName sets StartupWMClass 2`] = ` +{ + "linux": [ + { + "arch": "x64", + "file": "Signal-1.1.0.AppImage", + "updateInfo": { + "blockMapSize": "@blockMapSize", + "sha512": "@sha512", + "size": "@size", + }, + }, + ], +} +`; + exports[`AppImage 1`] = ` { "linux": [ diff --git a/test/src/linux/linuxPackagerTest.ts b/test/src/linux/linuxPackagerTest.ts index b82cfbfdd32..2c105393928 100644 --- a/test/src/linux/linuxPackagerTest.ts +++ b/test/src/linux/linuxPackagerTest.ts @@ -319,6 +319,34 @@ test.ifNotWindows("no-author-email", ({ expect }) => ) ) +test.ifNotWindows("AppImage - desktopName sets StartupWMClass", ({ expect }) => + app( + expect, + { + targets: appImageTarget, + config: { + productName: "Signal", + }, + effectiveOptionComputed: async it => { + const content: string = it.desktop + expect( + content + .split("\n") + .filter(it => !it.includes("X-AppImage-BuildId") && !it.includes("X-AppImage-Version")) + .join("\n") + ).toMatchSnapshot() + return Promise.resolve(false) + }, + }, + { + projectDirCreated: projectDir => + modifyPackageJson(projectDir, data => { + data.desktopName = "signal.desktop" + }), + } + ) +) + test.ifNotWindows("forbid desktop.Exec", ({ expect }) => appThrows(expect, { targets: appImageTarget,