1 { config, lib, pkgs, ... }:
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 {
8 desktopName = "On-screen keyboard";
9 exec = "${pkgs.squeekboard}/bin/squeekboard";
10 categories = [ "GNOME" "Core" ];
11 onlyShowIn = [ "GNOME" ];
14 X-GNOME-Autostart-Phase = "Panel";
15 X-GNOME-Provides = "inputmethod";
16 X-GNOME-Autostart-Notify = "true";
17 X-GNOME-AutoRestart = "true";
21 phocConfigType = lib.types.submodule {
23 xwayland = lib.mkOption {
25 Whether to enable XWayland support.
27 To start XWayland immediately, use `immediate`.
29 type = lib.types.enum [ "true" "false" "immediate" ];
32 cursorTheme = lib.mkOption {
34 Cursor theme to use in Phosh.
39 outputs = lib.mkOption {
41 Output configurations.
43 type = lib.types.attrsOf phocOutputType;
53 phocOutputType = lib.types.submodule {
55 modeline = lib.mkOption {
57 One or more modelines.
59 type = lib.types.either lib.types.str (lib.types.listOf lib.types.str);
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"
70 type = lib.types.nullOr lib.types.str;
74 scale = lib.mkOption {
76 Display scaling factor.
78 type = lib.types.nullOr (
80 (lib.types.either lib.types.int lib.types.float)
83 description = "null or positive integer or float";
88 rotate = lib.mkOption {
90 Screen transformation.
92 type = lib.types.enum [
93 "90" "180" "270" "flipped" "flipped-90" "flipped-180" "flipped-270" null
100 optionalKV = k: v: lib.optionalString (v != null) "${k} = ${builtins.toString v}";
102 renderPhocOutput = name: output: let
103 modelines = if builtins.isList output.modeline
105 else [ output.modeline ];
106 renderModeline = l: "modeline = ${l}";
109 ${lib.concatStringsSep "\n" (map renderModeline modelines)}
110 ${optionalKV "mode" output.mode}
111 ${optionalKV "scale" output.scale}
112 ${optionalKV "rotate" output.rotate}
115 renderPhocConfig = phoc: let
116 outputs = lib.mapAttrsToList renderPhocOutput phoc.outputs;
119 xwayland = ${phoc.xwayland}
120 ${lib.concatStringsSep "\n" outputs}
122 theme = ${phoc.cursorTheme}
128 services.xserver.desktopManager.phosh = {
129 enable = lib.mkOption {
130 type = lib.types.bool;
132 description = "Enable the Phone Shell.";
135 package = lib.mkPackageOption pkgs "phosh" { };
137 user = lib.mkOption {
138 description = "The user to run the Phosh service.";
139 type = lib.types.str;
143 group = lib.mkOption {
144 description = "The group to run the Phosh service.";
145 type = lib.types.str;
149 phocConfig = lib.mkOption {
151 Configurations for the Phoc compositor.
153 type = lib.types.oneOf [ lib.types.lines lib.types.path phocConfigType ];
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" ];
165 ExecStart = "${cfg.package}/bin/phosh-session";
169 WorkingDirectory = "~";
172 TTYPath = "/dev/tty7";
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";
187 # We are running without a display manager, so need to provide
188 # a value for XDG_CURRENT_DESKTOP.
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";
203 environment.systemPackages = [
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);