{user}: { lib, pkgs, config, ... }: with lib; let cfg = config.displays; renderDisplaysForHyprland = displays: (map displayHyprlandSetting (builtins.filter (d: d.enable) displays )); displayHyprlandSetting = display: specificDisplay display + ", " + resToString display.resolution + ", " + positionToHyprlandString display.position + ", " + toString display.scaling + ", " + "transform," + toString display.rotation; swaybgJob = displays: { Unit = { Description = "SwayBG"; }; Service = { ExecStart = "${pkgs.swaybg}/bin/swaybg " + concatStringsSep " " (map swaybgCmd displays); }; Install = { WantedBy = ["graphical-session.target"]; }; }; swaybgCmd = display: if (display.wallpaper != "") then "-o ${display.name} -i ${display.wallpaper} -m fill" else ""; specificDisplay = display: if display.description == "" then display.name else "desc:" + display.description; positionToHyprlandString = { x, y, }: if (x == -1 || y == -1) then "auto" else toString x + "x" + toString y; renderDisplaysForSway = displays: listToAttrs (map displaySwaySetting displays); displaySwaySetting = display: { name = display.name; value = let res = display.resolution; in { mode = mkIf (!resUnset res) "${toString res.x}x${toString res.y}@${toString res.freq}Hz"; bg = display.wallpaper + " fill"; scale = toString display.scaling; }; }; resolutionType = types.submodule { options = { x = mkOption { description = "x"; type = types.int; default = 0; }; y = mkOption { description = "y"; type = types.int; default = 0; }; freq = mkOption { description = "frequency"; type = types.int; default = 0; }; }; }; displayType = types.submodule { options = { enable = mkEnableOption "enable this display"; name = mkOption { description = "name of the display"; default = ""; }; description = mkOption { description = "description of display from hyprctl monitors"; default = ""; }; scaling = mkOption { type = types.float; default = 1.0; }; rotation = mkOption { type = types.int; default = 0; }; resolution = mkOption { description = "res"; type = resolutionType; default = {}; }; position.x = mkOption { default = -1; type = types.int; }; position.y = mkOption { default = -1; type = types.int; }; wallpaper = mkOption { description = "path to wallpaper"; default = ""; }; workspaces = mkOption { default = {}; type = types.submodule { options = { start = mkOption { type = types.int; default = 1; }; end = mkOption { type = types.int; default = 1; }; }; }; }; }; }; resUnset = res: (res.x == 0 || res.y == 0 || res.freq == 0); resToString = res: if resUnset res then "preferred" else "${toString res.x}x${toString res.y}@${toString res.freq}"; waybarWorkspaceConf = monitors: (map (display: { ${display.name} = display.workspaces.start; }) monitors); renderWorkspacesForHyprland = displays: (map hyprWorkspaceSetting displays); hyprWorkspaceSetting = display: specificDisplay display + ", " + toString display.workspaces.start; in { options.displays = { enable = mkEnableOption "manage displays"; displays = mkOption { type = types.attrsOf displayType; default = {}; }; }; config = mkIf cfg.enable { home-manager.users.${user}.imports = [ { systemd.user.services.swaybg = swaybgJob (attrValues cfg.displays); programs.waybar.settings.mainBar."hyprland/workspaces".persistent_workspaces = waybarWorkspaceConf (attrValues cfg.displays); wayland.windowManager = mkIf (cfg.displays != {}) { hyprland.settings = { monitor = renderDisplaysForHyprland (attrValues cfg.displays); workspace = renderWorkspacesForHyprland (attrValues cfg.displays); }; sway.config.output = renderDisplaysForSway (attrValues cfg.displays); }; } ]; }; }