anvil-editor: init at 0.4
[NixPkgs.git] / pkgs / build-support / make-desktopitem / default.nix
blob10caac3a935a5c3f86f3463ae157477d76d16bd4
1 { lib, writeTextFile, buildPackages }:
3 # All possible values as defined by the spec, version 1.4.
4 # Please keep in spec order for easier maintenance.
5 # When adding a new value, don't forget to update the Version field below!
6 # See https://specifications.freedesktop.org/desktop-entry-spec/latest
7 lib.makeOverridable ({ name # The name of the desktop file
8 , type ? "Application"
9 # version is hardcoded
10 , desktopName # The name of the application
11 , genericName ? null
12 , noDisplay ? null
13 , comment ? null
14 , icon ? null
15 # we don't support the Hidden key - if you don't need something, just don't install it
16 , onlyShowIn ? []
17 , notShowIn ? []
18 , dbusActivatable ? null
19 , tryExec ? null
20 , exec ? null
21 , path ? null
22 , terminal ? null
23 , actions ? {} # An attrset of [internal name] -> { name, exec?, icon? }
24 , mimeTypes ? [] # The spec uses "MimeType" as singular, use plural here to signify list-ness
25 , categories ? []
26 , implements ? []
27 , keywords ? []
28 , startupNotify ? null
29 , startupWMClass ? null
30 , url ? null
31 , prefersNonDefaultGPU ? null
32 # not supported until version 1.5, which is not supported by our desktop-file-utils as of 2022-02-23
33 # , singleMainWindow ? null
34 , extraConfig ? {} # Additional values to be added literally to the final item, e.g. vendor extensions
36 let
37   # There are multiple places in the FDO spec that make "boolean" values actually tristate,
38   # e.g. StartupNotify, where "unset" is literally defined as "do something reasonable".
39   # So, handle null values separately.
40   boolOrNullToString = value:
41     if value == null then null
42     else if builtins.isBool value then lib.boolToString value
43     else throw "makeDesktopItem: value must be a boolean or null!";
45   # Multiple values are represented as one string, joined by semicolons.
46   # Technically, it's possible to escape semicolons in values with \;, but this is currently not implemented.
47   renderList = key: value:
48     if !builtins.isList value then throw "makeDesktopItem: value for ${key} must be a list!"
49     else if builtins.any (item: lib.hasInfix ";" item) value then throw "makeDesktopItem: values in ${key} list must not contain semicolons!"
50     else if value == [] then null
51     else builtins.concatStringsSep ";" value;
53   # The [Desktop Entry] section of the desktop file, as an attribute set.
54   # Please keep in spec order.
55   mainSection = {
56     "Type" = type;
57     "Version" = "1.4";
58     "Name" = desktopName;
59     "GenericName" = genericName;
60     "NoDisplay" = boolOrNullToString noDisplay;
61     "Comment" = comment;
62     "Icon" = icon;
63     "OnlyShowIn" = renderList "onlyShowIn" onlyShowIn;
64     "NotShowIn" = renderList "notShowIn" notShowIn;
65     "DBusActivatable" = boolOrNullToString dbusActivatable;
66     "TryExec" = tryExec;
67     "Exec" = exec;
68     "Path" = path;
69     "Terminal" = boolOrNullToString terminal;
70     "Actions" = renderList "actions" (builtins.attrNames actions);
71     "MimeType" = renderList "mimeTypes" mimeTypes;
72     "Categories" = renderList "categories" categories;
73     "Implements" = renderList "implements" implements;
74     "Keywords" = renderList "keywords" keywords;
75     "StartupNotify" = boolOrNullToString startupNotify;
76     "StartupWMClass" = startupWMClass;
77     "URL" = url;
78     "PrefersNonDefaultGPU" = boolOrNullToString prefersNonDefaultGPU;
79     # "SingleMainWindow" = boolOrNullToString singleMainWindow;
80   } // extraConfig;
82   # Render a single attribute pair to a Key=Value line.
83   # FIXME: this isn't entirely correct for arbitrary strings, as some characters
84   # need to be escaped. There are currently none in nixpkgs though, so this is OK.
85   renderLine = name: value: if value != null then "${name}=${value}" else null;
87   # Render a full section of the file from an attrset.
88   # Null values are intentionally left out.
89   renderSection = sectionName: attrs:
90     lib.pipe attrs [
91       (lib.mapAttrsToList renderLine)
92       (builtins.filter (v: v != null))
93       (builtins.concatStringsSep "\n")
94       (section: ''
95         [${sectionName}]
96         ${section}
97       '')
98     ];
100   mainSectionRendered = renderSection "Desktop Entry" mainSection;
102   # Convert from javaCase names as used in Nix to PascalCase as used in the spec.
103   preprocessAction = { name, icon ? null, exec ? null }: {
104     "Name" = name;
105     "Icon" = icon;
106     "Exec" = exec;
107   };
108   renderAction = name: attrs: renderSection "Desktop Action ${name}" (preprocessAction attrs);
109   actionsRendered = lib.mapAttrsToList renderAction actions;
111   extension = if type == "Directory" then "directory" else "desktop";
112   content = [ mainSectionRendered ] ++ actionsRendered;
114 writeTextFile {
115   name = "${name}.${extension}";
116   destination = "/share/applications/${name}.${extension}";
117   text = builtins.concatStringsSep "\n" content;
118   checkPhase = ''${buildPackages.desktop-file-utils}/bin/desktop-file-validate "$target"'';