1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.programs.uwsm;
9
10 # Helper function to create desktop entry files for UWSM-managed compositors
11 mk_uwsm_desktop_entry =
12 opts:
13 (pkgs.writeTextFile {
14 name = "${opts.name}-uwsm";
15 text = ''
16 [Desktop Entry]
17 Name=${opts.prettyName} (UWSM)
18 Comment=${opts.comment}
19 Exec=${lib.getExe cfg.package} start -S -F ${opts.binPath}
20 Type=Application
21 '';
22 destination = "/share/wayland-sessions/${opts.name}-uwsm.desktop";
23 derivationArgs = {
24 passthru.providedSessions = [ "${opts.name}-uwsm" ];
25 };
26 });
27in
28{
29 options.programs.uwsm = {
30 enable = lib.mkEnableOption ''
31 uwsm, which wraps standalone Wayland compositors with a set
32 of Systemd units on the fly. This essentially
33 binds the wayland compositor into `graphical-session-pre.target`,
34 `graphical-session.target`, `xdg-desktop-autostart.target`.
35
36 This is useful for Wayland compositors like Hyprland, Sway, Wayfire,
37 etc. that do not start these targets and services on their own.
38
39 ::: {.note}
40 You must configure `waylandCompositors` suboptions as well
41 so that UWSM knows which compositors to manage.
42
43 Additionally, this by default uses `dbus-broker` as the dbus
44 implementation for better compatibility. If you dislike this behavior
45 you can set `services.dbus.implementation = lib.mkForce "dbus"`
46 in your configuration.
47 :::
48
49 If you are having trouble starting a service that depends on
50 `graphical-session.target`, while using a WM, enabling this option
51 might help
52 '';
53 package = lib.mkPackageOption pkgs "uwsm" { };
54
55 waylandCompositors = lib.mkOption {
56 description = ''
57 Configuration for UWSM-managed Wayland Compositors. This
58 creates a desktop entry file which will be used by Display
59 Managers like GDM, to allow starting the UWSM managed session.
60 '';
61 type = lib.types.attrsOf (
62 lib.types.submodule (
63 { ... }:
64 {
65 options = {
66 prettyName = lib.mkOption {
67 type = lib.types.str;
68 description = "The full name of the desktop entry file.";
69 example = "ExampleWaylandCompositor";
70 };
71 comment = lib.mkOption {
72 type = lib.types.str;
73 description = "The comment field of the desktop entry file.";
74 default = "An intelligent Wayland compositor managed by UWSM.";
75 };
76 binPath = lib.mkOption {
77 type = lib.types.path;
78 description = ''
79 The wayland-compositor binary path that will be called by UWSM.
80
81 It is recommended to use the `/run/current-system/sw/bin/` path
82 instead of `lib.getExe pkgs.<compositor>` to avoid version mismatch
83 of the compositor used by UWSM and the one installed in the system.
84 '';
85 example = "/run/current-system/sw/bin/ExampleCompositor";
86 };
87 };
88 }
89 )
90 );
91 example = lib.literalExpression ''
92 hyprland = {
93 prettyName = "Hyprland";
94 comment = "Hyprland compositor managed by UWSM";
95 binPath = "/run/current-system/sw/bin/Hyprland";
96 };
97 sway = {
98 prettyName = "Sway";
99 comment = "Sway compositor managed by UWSM";
100 binPath = "/run/current-system/sw/bin/sway";
101 };
102 '';
103 };
104 };
105
106 config = lib.mkIf cfg.enable {
107 environment.systemPackages = [ cfg.package ];
108 systemd.packages = [ cfg.package ];
109 environment.pathsToLink = [ "/share/uwsm" ];
110
111 # UWSM recommends dbus broker for better compatibility
112 services.dbus.implementation = "broker";
113
114 services.displayManager = {
115 enable = true;
116 sessionPackages = lib.mapAttrsToList (
117 name: value:
118 mk_uwsm_desktop_entry {
119 inherit name;
120 inherit (value) prettyName comment binPath;
121 }
122 ) cfg.waylandCompositors;
123 };
124 };
125
126 meta.maintainers = with lib.maintainers; [
127 johnrtitor
128 kai-tub
129 ];
130}