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