grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / x11 / desktop-managers / phosh.nix
blobe16eace5407caead5f2e11b6fd7259d5aa621ed7
1 { config, lib, pkgs, ... }:
2 let
3   cfg = config.services.xserver.desktopManager.phosh;
5   # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop
6   oskItem = pkgs.makeDesktopItem {
7     name = "sm.puri.OSK0";
8     desktopName = "On-screen keyboard";
9     exec = "${pkgs.squeekboard}/bin/squeekboard";
10     categories = [ "GNOME" "Core" ];
11     onlyShowIn = [ "GNOME" ];
12     noDisplay = true;
13     extraConfig = {
14       X-GNOME-Autostart-Phase = "Panel";
15       X-GNOME-Provides = "inputmethod";
16       X-GNOME-Autostart-Notify = "true";
17       X-GNOME-AutoRestart = "true";
18     };
19   };
21   phocConfigType = lib.types.submodule {
22     options = {
23       xwayland = lib.mkOption {
24         description = ''
25           Whether to enable XWayland support.
27           To start XWayland immediately, use `immediate`.
28         '';
29         type = lib.types.enum [ "true" "false" "immediate" ];
30         default = "false";
31       };
32       cursorTheme = lib.mkOption {
33         description = ''
34           Cursor theme to use in Phosh.
35         '';
36         type = lib.types.str;
37         default = "default";
38       };
39       outputs = lib.mkOption {
40         description = ''
41           Output configurations.
42         '';
43         type = lib.types.attrsOf phocOutputType;
44         default = {
45           DSI-1 = {
46             scale = 2;
47           };
48         };
49       };
50     };
51   };
53   phocOutputType = lib.types.submodule {
54     options = {
55       modeline = lib.mkOption {
56         description = ''
57           One or more modelines.
58         '';
59         type = lib.types.either lib.types.str (lib.types.listOf lib.types.str);
60         default = [];
61         example = [
62           "87.25 720 776 848  976 1440 1443 1453 1493 -hsync +vsync"
63           "65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync"
64         ];
65       };
66       mode = lib.mkOption {
67         description = ''
68           Default video mode.
69         '';
70         type = lib.types.nullOr lib.types.str;
71         default = null;
72         example = "768x1024";
73       };
74       scale = lib.mkOption {
75         description = ''
76           Display scaling factor.
77         '';
78         type = lib.types.nullOr (
79           lib.types.addCheck
80           (lib.types.either lib.types.int lib.types.float)
81           (x : x > 0)
82         ) // {
83           description = "null or positive integer or float";
84         };
85         default = null;
86         example = 2;
87       };
88       rotate = lib.mkOption {
89         description = ''
90           Screen transformation.
91         '';
92         type = lib.types.enum [
93           "90" "180" "270" "flipped" "flipped-90" "flipped-180" "flipped-270" null
94         ];
95         default = null;
96       };
97     };
98   };
100   optionalKV = k: v: lib.optionalString (v != null) "${k} = ${builtins.toString v}";
102   renderPhocOutput = name: output: let
103     modelines = if builtins.isList output.modeline
104       then output.modeline
105       else [ output.modeline ];
106     renderModeline = l: "modeline = ${l}";
107   in ''
108     [output:${name}]
109     ${lib.concatStringsSep "\n" (map renderModeline modelines)}
110     ${optionalKV "mode" output.mode}
111     ${optionalKV "scale" output.scale}
112     ${optionalKV "rotate" output.rotate}
113   '';
115   renderPhocConfig = phoc: let
116     outputs = lib.mapAttrsToList renderPhocOutput phoc.outputs;
117   in ''
118     [core]
119     xwayland = ${phoc.xwayland}
120     ${lib.concatStringsSep "\n" outputs}
121     [cursor]
122     theme = ${phoc.cursorTheme}
123   '';
127   options = {
128     services.xserver.desktopManager.phosh = {
129       enable = lib.mkOption {
130         type = lib.types.bool;
131         default = false;
132         description = "Enable the Phone Shell.";
133       };
135       package = lib.mkPackageOption pkgs "phosh" { };
137       user = lib.mkOption {
138         description = "The user to run the Phosh service.";
139         type = lib.types.str;
140         example = "alice";
141       };
143       group = lib.mkOption {
144         description = "The group to run the Phosh service.";
145         type = lib.types.str;
146         example = "users";
147       };
149       phocConfig = lib.mkOption {
150         description = ''
151           Configurations for the Phoc compositor.
152         '';
153         type = lib.types.oneOf [ lib.types.lines lib.types.path phocConfigType ];
154         default = {};
155       };
156     };
157   };
159   config = lib.mkIf cfg.enable {
160     systemd.defaultUnit = "graphical.target";
161     # Inspired by https://gitlab.gnome.org/World/Phosh/phosh/-/blob/main/data/phosh.service
162     systemd.services.phosh = {
163       wantedBy = [ "graphical.target" ];
164       serviceConfig = {
165         ExecStart = "${cfg.package}/bin/phosh-session";
166         User = cfg.user;
167         Group = cfg.group;
168         PAMName = "login";
169         WorkingDirectory = "~";
170         Restart = "always";
172         TTYPath = "/dev/tty7";
173         TTYReset = "yes";
174         TTYVHangup = "yes";
175         TTYVTDisallocate = "yes";
177         # Fail to start if not controlling the tty.
178         StandardInput = "tty-fail";
179         StandardOutput = "journal";
180         StandardError = "journal";
182         # Log this user with utmp, letting it show up with commands 'w' and 'who'.
183         UtmpIdentifier = "tty7";
184         UtmpMode = "user";
185       };
186       environment = {
187         # We are running without a display manager, so need to provide
188         # a value for XDG_CURRENT_DESKTOP.
189         #
190         # Among other things, this variable influences:
191         #  - visibility of desktop entries with "OnlyShowIn=Phosh;"
192         #    https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.5.html#key-onlyshowin
193         #  - the chosen xdg-desktop-portal configuration.
194         #    https://flatpak.github.io/xdg-desktop-portal/docs/portals.conf.html
195         XDG_CURRENT_DESKTOP = "Phosh:GNOME";
196         # pam_systemd uses these to identify the session in logind.
197         # https://www.freedesktop.org/software/systemd/man/latest/pam_systemd.html#desktop=
198         XDG_SESSION_DESKTOP = "phosh";
199         XDG_SESSION_TYPE = "wayland";
200       };
201     };
203     environment.systemPackages = [
204       pkgs.phoc
205       cfg.package
206       pkgs.squeekboard
207       oskItem
208     ];
210     systemd.packages = [ cfg.package ];
212     programs.feedbackd.enable = true;
214     security.pam.services.phosh = {};
216     hardware.graphics.enable = lib.mkDefault true;
218     services.gnome.core-shell.enable = true;
219     services.gnome.core-os-services.enable = true;
220     services.displayManager.sessionPackages = [ cfg.package ];
222     environment.etc."phosh/phoc.ini".source =
223       if builtins.isPath cfg.phocConfig then cfg.phocConfig
224       else if builtins.isString cfg.phocConfig then pkgs.writeText "phoc.ini" cfg.phocConfig
225       else pkgs.writeText "phoc.ini" (renderPhocConfig cfg.phocConfig);
226   };