@@ -2,6 +2,7 @@ const GhosttyResources = @This();
22
33const std = @import ("std" );
44const builtin = @import ("builtin" );
5+ const assert = std .debug .assert ;
56const buildpkg = @import ("main.zig" );
67const Config = @import ("Config.zig" );
78const config_vim = @import ("../config/vim.zig" );
@@ -220,83 +221,178 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
220221 }
221222
222223 // App (Linux)
223- if (cfg .target .result .os .tag == .linux ) {
224- // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
224+ if (cfg .target .result .os .tag == .linux ) try addLinuxAppResources (
225+ b ,
226+ cfg ,
227+ & steps ,
228+ );
229+
230+ return .{ .steps = steps .items };
231+ }
232+
233+ /// Add the resource files needed to make Ghostty a proper
234+ /// Linux desktop application (for various desktop environments).
235+ fn addLinuxAppResources (
236+ b : * std.Build ,
237+ cfg : * const Config ,
238+ steps : * std .ArrayList (* std.Build.Step ),
239+ ) ! void {
240+ assert (cfg .target .result .os .tag == .linux );
241+
242+ // Background:
243+ // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
244+
245+ const name = b .fmt ("Ghostty{s}" , .{
246+ switch (cfg .optimize ) {
247+ .Debug , .ReleaseSafe = > " (Debug)" ,
248+ .ReleaseFast , .ReleaseSmall = > "" ,
249+ },
250+ });
251+
252+ const app_id = b .fmt ("com.mitchellh.ghostty{s}" , .{
253+ switch (cfg .optimize ) {
254+ .Debug , .ReleaseSafe = > "-debug" ,
255+ .ReleaseFast , .ReleaseSmall = > "" ,
256+ },
257+ });
258+
259+ const exe_abs_path = b .fmt (
260+ "{s}/bin/ghostty" ,
261+ .{b .install_prefix },
262+ );
263+
264+ // The templates that we will process. The templates are in
265+ // cmake format and will be processed and saved to the
266+ // second element of the tuple.
267+ const Template = struct { std .Build .LazyPath , []const u8 };
268+ const templates : []const Template = templates : {
269+ var ts : std .ArrayList (Template ) = .init (b .allocator );
225270
226271 // Desktop file so that we have an icon and other metadata
227- try steps .append (& b . addInstallFile (
228- b .path ("dist/linux/app.desktop" ),
229- "share/applications/com.mitchellh.ghostty. desktop" ,
230- ). step );
272+ try ts .append (.{
273+ b .path ("dist/linux/app.desktop.in " ),
274+ b . fmt ( "share/applications/{s}. desktop" , .{ app_id }) ,
275+ } );
231276
232- // AppStream metainfo so that application has rich metadata within app stores
233- try steps .append (& b .addInstallFile (
234- b .path ("dist/linux/com.mitchellh.ghostty.metainfo.xml" ),
235- "share/metainfo/com.mitchellh.ghostty.metainfo.xml" ,
236- ).step );
277+ // Service for DBus activation.
278+ try ts .append (.{
279+ if (cfg .flatpak )
280+ b .path ("dist/linux/dbus.service.flatpak.in" )
281+ else
282+ b .path ("dist/linux/dbus.service.in" ),
283+ b .fmt ("share/dbus-1/services/{s}.service" , .{app_id }),
284+ });
237285
238- // Right click menu action for Plasma desktop
239- try steps .append (& b .addInstallFile (
240- b .path ("dist/linux/ghostty_dolphin.desktop" ),
241- "share/kio/servicemenus/com.mitchellh.ghostty.desktop" ,
242- ).step );
286+ // systemd user service. This is kind of nasty but systemd
287+ // looks for user services in different paths depending on
288+ // if we are installed as a system package or not (lib vs.
289+ // share) so we have to handle that here. We might be able
290+ // to get away with always installing to both because it
291+ // only ever searches in one... but I don't want to do that hack
292+ // until we have to.
293+ if (! cfg .flatpak ) try ts .append (.{
294+ b .path ("dist/linux/systemd.service.in" ),
295+ b .fmt (
296+ "{s}/systemd/user/{s}.service" ,
297+ .{
298+ if (b .graph .system_package_mode ) "lib" else "share" ,
299+ app_id ,
300+ },
301+ ),
302+ });
243303
244- // Right click menu action for Nautilus. Note that this _must_ be named
245- // `ghostty.py`. Using the full app id causes problems (see #5468).
246- try steps .append (& b . addInstallFile (
247- b .path ("dist/linux/ghostty_nautilus.py " ),
248- "share/nautilus-python/extensions/ghostty.py" ,
249- ). step );
304+ // AppStream metainfo so that application has rich metadata
305+ // within app stores
306+ try ts .append (.{
307+ b .path ("dist/linux/com.mitchellh.ghostty.metainfo.xml.in " ),
308+ b . fmt ( "share/metainfo/{s}.metainfo.xml" , .{ app_id }) ,
309+ } );
250310
251- // Various icons that our application can use, including the icon
252- // that will be used for the desktop.
253- try steps .append (& b .addInstallFile (
254- b .path ("images/icons/icon_16.png" ),
255- "share/icons/hicolor/16x16/apps/com.mitchellh.ghostty.png" ,
256- ).step );
257- try steps .append (& b .addInstallFile (
258- b .path ("images/icons/icon_32.png" ),
259- "share/icons/hicolor/32x32/apps/com.mitchellh.ghostty.png" ,
260- ).step );
261- try steps .append (& b .addInstallFile (
262- b .path ("images/icons/icon_128.png" ),
263- "share/icons/hicolor/128x128/apps/com.mitchellh.ghostty.png" ,
264- ).step );
265- try steps .append (& b .addInstallFile (
266- b .path ("images/icons/icon_256.png" ),
267- "share/icons/hicolor/256x256/apps/com.mitchellh.ghostty.png" ,
268- ).step );
269- try steps .append (& b .addInstallFile (
270- b .path ("images/icons/icon_512.png" ),
271- "share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png" ,
272- ).step );
273- // Flatpaks only support icons up to 512x512.
274- if (! cfg .flatpak ) {
275- try steps .append (& b .addInstallFile (
276- b .path ("images/icons/icon_1024.png" ),
277- "share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png" ,
278- ).step );
279- }
311+ break :templates ts .items ;
312+ };
313+
314+ // Process all our templates
315+ for (templates ) | template | {
316+ const tpl = b .addConfigHeader (.{
317+ .style = .{ .cmake = template [0 ] },
318+ }, .{
319+ .NAME = name ,
320+ .APPID = app_id ,
321+ .GHOSTTY = exe_abs_path ,
322+ });
280323
324+ // Template output has a single header line we want to remove.
325+ // We use `tail` to do it since its part of the POSIX standard.
326+ const tail = b .addSystemCommand (&.{ "tail" , "-n" , "+2" });
327+ tail .setStdIn (.{ .lazy_path = tpl .getOutput () });
328+
329+ const copy = b .addInstallFile (
330+ tail .captureStdOut (),
331+ template [1 ],
332+ );
333+
334+ try steps .append (& copy .step );
335+ }
336+
337+ // Right click menu action for Plasma desktop
338+ try steps .append (& b .addInstallFile (
339+ b .path ("dist/linux/ghostty_dolphin.desktop" ),
340+ "share/kio/servicemenus/com.mitchellh.ghostty.desktop" ,
341+ ).step );
342+
343+ // Right click menu action for Nautilus. Note that this _must_ be named
344+ // `ghostty.py`. Using the full app id causes problems (see #5468).
345+ try steps .append (& b .addInstallFile (
346+ b .path ("dist/linux/ghostty_nautilus.py" ),
347+ "share/nautilus-python/extensions/ghostty.py" ,
348+ ).step );
349+
350+ // Various icons that our application can use, including the icon
351+ // that will be used for the desktop.
352+ try steps .append (& b .addInstallFile (
353+ b .path ("images/icons/icon_16.png" ),
354+ "share/icons/hicolor/16x16/apps/com.mitchellh.ghostty.png" ,
355+ ).step );
356+ try steps .append (& b .addInstallFile (
357+ b .path ("images/icons/icon_32.png" ),
358+ "share/icons/hicolor/32x32/apps/com.mitchellh.ghostty.png" ,
359+ ).step );
360+ try steps .append (& b .addInstallFile (
361+ b .path ("images/icons/icon_128.png" ),
362+ "share/icons/hicolor/128x128/apps/com.mitchellh.ghostty.png" ,
363+ ).step );
364+ try steps .append (& b .addInstallFile (
365+ b .path ("images/icons/icon_256.png" ),
366+ "share/icons/hicolor/256x256/apps/com.mitchellh.ghostty.png" ,
367+ ).step );
368+ try steps .append (& b .addInstallFile (
369+ b .path ("images/icons/icon_512.png" ),
370+ "share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png" ,
371+ ).step );
372+ // Flatpaks only support icons up to 512x512.
373+ if (! cfg .flatpak ) {
281374 try steps .append (& b .addInstallFile (
282- b .path ("images/icons/icon_16@2x.png" ),
283- "share/icons/hicolor/16x16@2/apps/com.mitchellh.ghostty.png" ,
284- ).step );
285- try steps .append (& b .addInstallFile (
286- b .path ("images/icons/icon_32@2x.png" ),
287- "share/icons/hicolor/32x32@2/apps/com.mitchellh.ghostty.png" ,
288- ).step );
289- try steps .append (& b .addInstallFile (
290- b .path ("images/icons/icon_128@2x.png" ),
291- "share/icons/hicolor/128x128@2/apps/com.mitchellh.ghostty.png" ,
292- ).step );
293- try steps .append (& b .addInstallFile (
294- b .path ("images/icons/icon_256@2x.png" ),
295- "share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png" ,
375+ b .path ("images/icons/icon_1024.png" ),
376+ "share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png" ,
296377 ).step );
297378 }
298379
299- return .{ .steps = steps .items };
380+ try steps .append (& b .addInstallFile (
381+ b .path ("images/icons/icon_16@2x.png" ),
382+ "share/icons/hicolor/16x16@2/apps/com.mitchellh.ghostty.png" ,
383+ ).step );
384+ try steps .append (& b .addInstallFile (
385+ b .path ("images/icons/icon_32@2x.png" ),
386+ "share/icons/hicolor/32x32@2/apps/com.mitchellh.ghostty.png" ,
387+ ).step );
388+ try steps .append (& b .addInstallFile (
389+ b .path ("images/icons/icon_128@2x.png" ),
390+ "share/icons/hicolor/128x128@2/apps/com.mitchellh.ghostty.png" ,
391+ ).step );
392+ try steps .append (& b .addInstallFile (
393+ b .path ("images/icons/icon_256@2x.png" ),
394+ "share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png" ,
395+ ).step );
300396}
301397
302398pub fn install (self : * const GhosttyResources ) void {
0 commit comments