nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 writeTextFile,
4 buildPackages,
5}:
6
7/**
8 A utility builder to create a desktop entry file at a predetermined location (by default, $out/share/applications).
9
10 # Examples
11
12 ```nix
13 makeDesktopItem {
14 name = "Eclipse";
15 exec = "eclipse";
16 icon = "eclipse";
17 comment = "Integrated Development Environment";
18 desktopName = "Eclipse IDE";
19 genericName = "Integrated Development Environment";
20 categories = [ "Development" ];
21 }
22 => «derivation /nix/store/sh017x5n50i2qwns2na1sxdp7zb2zgcw-Eclipse.desktop.drv»
23 ```
24
25 # Type
26
27 ```
28 makeDesktopItem :: AttrSet -> Derivation
29 ```
30
31 # Input
32
33 `attrs`
34
35 : An AttrSet with the following definitions. See https://specifications.freedesktop.org/desktop-entry-spec/1.4/recognized-keys.html#id-1.7.6 for definitions.
36
37 - `name` (string): The name of the desktop file (excluding the .desktop or .directory file extensions)
38 - `destination` (string): The directory that will contain the desktop entry file (Default: "/share/applications")
39 - `type` ("Application" | "Link" | "Directory"): The `Type` of the desktop entry
40 - `desktopName` (string): The `Name` of the desktop entry
41 - `genericName` (string): The `GenericName` of the desktop entry
42 - `noDisplay` (bool): The `NoDisplay` of the desktop entry
43 - `comment` (string): The `Comment` of the desktop entry
44 - `icon` (string): The `Icon` of the desktop entry
45 - `onlyShowIn` (string[]): The `OnlyShowIn` of the desktop entry
46 - `notShowIn` (string[]): The `NotShowIn` of the desktop entry
47 - `dbusActivatable` (bool): The `DBusActivatable` of the desktop entry
48 - `tryExec` (string): The `TryExec` of the desktop entry
49 - `exec` (string): The `Exec` of the desktop entry
50 - `path` (string): The `Path` of the desktop entry
51 - `terminal` (bool): The `Terminal` of the desktop entry
52 - `actions` (AttrSet): An attrset of [internal name] -> { name, exec?, icon? } representing the `Actions` of the desktop entry
53 - `mimeTypes` (string[]): The `MimeType` of the desktop entry
54 - `categories` (string[]): The `Categories` of the desktop entry; see https://specifications.freedesktop.org/menu-spec/1.0/category-registry.html for possible values
55 - `implements` (string[]): The `Implements` of the desktop entry
56 - `keywords` (string[]): The `Keywords` of the desktop entry
57 - `startupNotify` (bool): The `StartupNotify` of the desktop entry
58 - `startupWMClass` (string): The `StartupWMClass` of the desktop entry
59 - `url` (string): The `URL` of the Link-type desktop entry
60 - `prefersNonDefaultGPU` (bool): The `PrefersNonDefaultGPU` (non-standard) of the desktop entry
61 - `extraConfig` (AttrSet): Additional values to be added literally to the final item, e.g. vendor extensions
62
63 # Output
64
65 A derivation that contains the output desktop entry file.
66
67 # Developer Note
68
69 All possible values are as defined by the spec, version 1.4.
70 Please keep in spec order for easier maintenance.
71 When adding a new value, don't forget to update the Version field below!
72 See https://specifications.freedesktop.org/desktop-entry-spec/latest
73*/
74lib.makeOverridable (
75 {
76 name, # The name of the desktop file
77 destination ? "/share/applications",
78 type ? "Application",
79 # version is hardcoded
80 desktopName, # The name of the application
81 genericName ? null,
82 noDisplay ? null,
83 comment ? null,
84 icon ? null,
85 # we don't support the Hidden key - if you don't need something, just don't install it
86 onlyShowIn ? [ ],
87 notShowIn ? [ ],
88 dbusActivatable ? null,
89 tryExec ? null,
90 exec ? null,
91 path ? null,
92 terminal ? null,
93 actions ? { }, # An attrset of [internal name] -> { name, exec?, icon? }
94 mimeTypes ? [ ], # The spec uses "MimeType" as singular, use plural here to signify list-ness
95 categories ? [ ],
96 implements ? [ ],
97 keywords ? [ ],
98 startupNotify ? null,
99 startupWMClass ? null,
100 url ? null,
101 prefersNonDefaultGPU ? null,
102 # not supported until version 1.5, which is not supported by our desktop-file-utils as of 2022-02-23
103 # singleMainWindow ? null,
104 extraConfig ? { }, # Additional values to be added literally to the final item, e.g. vendor extensions
105 }:
106 let
107 # There are multiple places in the FDO spec that make "boolean" values actually tristate,
108 # e.g. StartupNotify, where "unset" is literally defined as "do something reasonable".
109 # So, handle null values separately.
110 boolOrNullToString =
111 value:
112 if value == null then
113 null
114 else if builtins.isBool value then
115 lib.boolToString value
116 else
117 throw "makeDesktopItem: value must be a boolean or null!";
118
119 # Multiple values are represented as one string, joined by semicolons.
120 # Technically, it's possible to escape semicolons in values with \;, but this is currently not implemented.
121 renderList =
122 key: value:
123 if !builtins.isList value then
124 throw "makeDesktopItem: value for ${key} must be a list!"
125 else if builtins.any (item: lib.hasInfix ";" item) value then
126 throw "makeDesktopItem: values in ${key} list must not contain semicolons!"
127 else if value == [ ] then
128 null
129 else
130 builtins.concatStringsSep ";" value;
131
132 # The [Desktop Entry] section of the desktop file, as an attribute set.
133 # Please keep in spec order.
134 mainSection = {
135 "Type" = type;
136 "Version" = "1.4";
137 "Name" = desktopName;
138 "GenericName" = genericName;
139 "NoDisplay" = boolOrNullToString noDisplay;
140 "Comment" = comment;
141 "Icon" = icon;
142 "OnlyShowIn" = renderList "onlyShowIn" onlyShowIn;
143 "NotShowIn" = renderList "notShowIn" notShowIn;
144 "DBusActivatable" = boolOrNullToString dbusActivatable;
145 "TryExec" = tryExec;
146 "Exec" = exec;
147 "Path" = path;
148 "Terminal" = boolOrNullToString terminal;
149 "Actions" = renderList "actions" (builtins.attrNames actions);
150 "MimeType" = renderList "mimeTypes" mimeTypes;
151 "Categories" = renderList "categories" categories;
152 "Implements" = renderList "implements" implements;
153 "Keywords" = renderList "keywords" keywords;
154 "StartupNotify" = boolOrNullToString startupNotify;
155 "StartupWMClass" = startupWMClass;
156 "URL" = url;
157 "PrefersNonDefaultGPU" = boolOrNullToString prefersNonDefaultGPU;
158 # "SingleMainWindow" = boolOrNullToString singleMainWindow;
159 }
160 // extraConfig;
161
162 # Render a single attribute pair to a Key=Value line.
163 # FIXME: this isn't entirely correct for arbitrary strings, as some characters
164 # need to be escaped. There are currently none in nixpkgs though, so this is OK.
165 renderLine = name: value: if value != null then "${name}=${value}" else null;
166
167 # Render a full section of the file from an attrset.
168 # Null values are intentionally left out.
169 renderSection =
170 sectionName: attrs:
171 lib.pipe attrs [
172 (lib.mapAttrsToList renderLine)
173 (builtins.filter (v: v != null))
174 (builtins.concatStringsSep "\n")
175 (section: ''
176 [${sectionName}]
177 ${section}
178 '')
179 ];
180
181 mainSectionRendered = renderSection "Desktop Entry" mainSection;
182
183 # Convert from javaCase names as used in Nix to PascalCase as used in the spec.
184 preprocessAction =
185 {
186 name,
187 icon ? null,
188 exec ? null,
189 }:
190 {
191 "Name" = name;
192 "Icon" = icon;
193 "Exec" = exec;
194 };
195 renderAction = name: attrs: renderSection "Desktop Action ${name}" (preprocessAction attrs);
196 actionsRendered = lib.mapAttrsToList renderAction actions;
197
198 extension = if type == "Directory" then "directory" else "desktop";
199 content = [ mainSectionRendered ] ++ actionsRendered;
200 in
201 writeTextFile {
202 name = "${name}.${extension}";
203 destination = "${destination}/${name}.${extension}";
204 text = builtins.concatStringsSep "\n" content;
205 checkPhase = ''${buildPackages.desktop-file-utils}/bin/desktop-file-validate "$target"'';
206 }
207)