nixos/preload: init
[NixPkgs.git] / nixos / modules / services / x11 / desktop-managers / phosh.nix
blobe4cd9fd99e40c96b6b88da24d4739c82ddb674ab
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.xserver.desktopManager.phosh;
8   # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop
9   oskItem = pkgs.makeDesktopItem {
10     name = "sm.puri.OSK0";
11     desktopName = "On-screen keyboard";
12     exec = "${pkgs.squeekboard}/bin/squeekboard";
13     categories = [ "GNOME" "Core" ];
14     onlyShowIn = [ "GNOME" ];
15     noDisplay = true;
16     extraConfig = {
17       X-GNOME-Autostart-Phase = "Panel";
18       X-GNOME-Provides = "inputmethod";
19       X-GNOME-Autostart-Notify = "true";
20       X-GNOME-AutoRestart = "true";
21     };
22   };
24   phocConfigType = types.submodule {
25     options = {
26       xwayland = mkOption {
27         description = lib.mdDoc ''
28           Whether to enable XWayland support.
30           To start XWayland immediately, use `immediate`.
31         '';
32         type = types.enum [ "true" "false" "immediate" ];
33         default = "false";
34       };
35       cursorTheme = mkOption {
36         description = lib.mdDoc ''
37           Cursor theme to use in Phosh.
38         '';
39         type = types.str;
40         default = "default";
41       };
42       outputs = mkOption {
43         description = lib.mdDoc ''
44           Output configurations.
45         '';
46         type = types.attrsOf phocOutputType;
47         default = {
48           DSI-1 = {
49             scale = 2;
50           };
51         };
52       };
53     };
54   };
56   phocOutputType = types.submodule {
57     options = {
58       modeline = mkOption {
59         description = lib.mdDoc ''
60           One or more modelines.
61         '';
62         type = types.either types.str (types.listOf types.str);
63         default = [];
64         example = [
65           "87.25 720 776 848  976 1440 1443 1453 1493 -hsync +vsync"
66           "65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync"
67         ];
68       };
69       mode = mkOption {
70         description = lib.mdDoc ''
71           Default video mode.
72         '';
73         type = types.nullOr types.str;
74         default = null;
75         example = "768x1024";
76       };
77       scale = mkOption {
78         description = lib.mdDoc ''
79           Display scaling factor.
80         '';
81         type = types.nullOr (
82           types.addCheck
83           (types.either types.int types.float)
84           (x : x > 0)
85         ) // {
86           description = "null or positive integer or float";
87         };
88         default = null;
89         example = 2;
90       };
91       rotate = mkOption {
92         description = lib.mdDoc ''
93           Screen transformation.
94         '';
95         type = types.enum [
96           "90" "180" "270" "flipped" "flipped-90" "flipped-180" "flipped-270" null
97         ];
98         default = null;
99       };
100     };
101   };
103   optionalKV = k: v: optionalString (v != null) "${k} = ${builtins.toString v}";
105   renderPhocOutput = name: output: let
106     modelines = if builtins.isList output.modeline
107       then output.modeline
108       else [ output.modeline ];
109     renderModeline = l: "modeline = ${l}";
110   in ''
111     [output:${name}]
112     ${concatStringsSep "\n" (map renderModeline modelines)}
113     ${optionalKV "mode" output.mode}
114     ${optionalKV "scale" output.scale}
115     ${optionalKV "rotate" output.rotate}
116   '';
118   renderPhocConfig = phoc: let
119     outputs = mapAttrsToList renderPhocOutput phoc.outputs;
120   in ''
121     [core]
122     xwayland = ${phoc.xwayland}
123     ${concatStringsSep "\n" outputs}
124     [cursor]
125     theme = ${phoc.cursorTheme}
126   '';
130   options = {
131     services.xserver.desktopManager.phosh = {
132       enable = mkOption {
133         type = types.bool;
134         default = false;
135         description = lib.mdDoc "Enable the Phone Shell.";
136       };
138       package = mkOption {
139         type = types.package;
140         default = pkgs.phosh;
141         defaultText = literalExpression "pkgs.phosh";
142         example = literalExpression "pkgs.phosh";
143         description = lib.mdDoc ''
144           Package that should be used for Phosh.
145         '';
146       };
148       user = mkOption {
149         description = lib.mdDoc "The user to run the Phosh service.";
150         type = types.str;
151         example = "alice";
152       };
154       group = mkOption {
155         description = lib.mdDoc "The group to run the Phosh service.";
156         type = types.str;
157         example = "users";
158       };
160       phocConfig = mkOption {
161         description = lib.mdDoc ''
162           Configurations for the Phoc compositor.
163         '';
164         type = types.oneOf [ types.lines types.path phocConfigType ];
165         default = {};
166       };
167     };
168   };
170   config = mkIf cfg.enable {
171     systemd.defaultUnit = "graphical.target";
172     # Inspired by https://gitlab.gnome.org/World/Phosh/phosh/-/blob/main/data/phosh.service
173     systemd.services.phosh = {
174       wantedBy = [ "graphical.target" ];
175       serviceConfig = {
176         ExecStart = "${cfg.package}/bin/phosh-session";
177         User = cfg.user;
178         Group = cfg.group;
179         PAMName = "login";
180         WorkingDirectory = "~";
181         Restart = "always";
183         TTYPath = "/dev/tty7";
184         TTYReset = "yes";
185         TTYVHangup = "yes";
186         TTYVTDisallocate = "yes";
188         # Fail to start if not controlling the tty.
189         StandardInput = "tty-fail";
190         StandardOutput = "journal";
191         StandardError = "journal";
193         # Log this user with utmp, letting it show up with commands 'w' and 'who'.
194         UtmpIdentifier = "tty7";
195         UtmpMode = "user";
196       };
197     };
199     environment.systemPackages = [
200       pkgs.phoc
201       cfg.package
202       pkgs.squeekboard
203       oskItem
204     ];
206     systemd.packages = [ cfg.package ];
208     programs.feedbackd.enable = true;
210     security.pam.services.phosh = {};
212     hardware.opengl.enable = mkDefault true;
214     services.gnome.core-shell.enable = true;
215     services.gnome.core-os-services.enable = true;
216     services.xserver.displayManager.sessionPackages = [ cfg.package ];
218     environment.etc."phosh/phoc.ini".source =
219       if builtins.isPath cfg.phocConfig then cfg.phocConfig
220       else if builtins.isString cfg.phocConfig then pkgs.writeText "phoc.ini" cfg.phocConfig
221       else pkgs.writeText "phoc.ini" (renderPhocConfig cfg.phocConfig);
222   };