diff --git a/modules/desktop/fht-compositor/default.nix b/modules/desktop/fht-compositor/default.nix deleted file mode 100644 index d6812eb..0000000 --- a/modules/desktop/fht-compositor/default.nix +++ /dev/null @@ -1,398 +0,0 @@ -{ - inputs, - config, - pkgs, - lib, - ... -}: -let - filename = "fht-compositor.toml"; - - session-programs = pkgs.writeShellApplication { - name = "session-programs"; - runtimeInputs = with pkgs; [ - procps - util-linux - ]; - text = '' - restart() { - pattern=$1 - shift - pkill -f "$pattern" || true - for _ in $(seq 50); do - pgrep -f "$pattern" >/dev/null || break - sleep 0.1 - done - setsid "$@" >/dev/null 2>&1 $out - ''; - - # Only load the providers Walker uses - "xdg/elephant/elephant.toml".source = (pkgs.formats.toml { }).generate "elephant.toml" { - terminal_cmd = "ptyxis -x"; - ignored_providers = [ - "1password" - "aptpackages" - "archlinuxpkgs" - "bitwarden" - "bluetooth" - "bookmarks" - "clipboard" - "dnfpackages" - "files" - "menus" - "niriactions" - "nirisessions" - "playerctl" - "protonpass" - "providerlist" - "runner" - "snippets" - "symbols" - "todo" - "unicode" - "windows" - "wireplumber" - ]; - }; - - # Use the same search engines as LibreWolf, with its aliases as prefixes (e.g. "@no nix") - "xdg/elephant/websearch.toml".source = - let - inherit (config.programs.firefox.policies) SearchEngines; - in - (pkgs.formats.toml { }).generate "websearch.toml" { - entries = map ( - engine: - { - name = engine.Name; - url = lib.replaceStrings [ "{searchTerms}" ] [ "%TERM%" ] engine.URLTemplate; - default = engine.Name == SearchEngines.Default; - } - // lib.optionalAttrs (engine ? Alias) { prefix = "${engine.Alias} "; } - ) SearchEngines.Add; - }; - - # FHT config - ${filename}.source = (pkgs.formats.toml { }).generate "config.toml" { - autostart = [ "session-programs" ]; - - general = { - focus-follows-mouse = true; - inner-gaps = 10; - }; - - decorations = { - border = { - thickness = 4; - radius = 12; - }; - blur.disable = true; - }; - - keybinds = { - Alt-Tab = "focus-next-window"; - Super-q = "close-focused-window"; - Super-f = "fullscreen-focused-window"; - Super-Ctrl-q = "quit"; - Super-Ctrl-r = { - action = "run-command"; - arg = "fht-compositor ipc action reload-config; session-programs"; - }; - - # Runs when Super is tapped on its own (patched in, see lone-modifier.patch) - Super_L = { - action = "run-command"; - arg = "walker"; - }; - - Print = { - action = "run-command"; - arg = "${lib.getExe pkgs.grim} -g \"$(${lib.getExe pkgs.slurp})\" - | gradia"; - }; - - Super-t = { - action = "run-command"; - arg = "ptyxis --new-window"; - }; - Super-e = { - action = "run-command"; - arg = "nautilus"; - }; - Super-c = { - action = "run-command"; - arg = "codium"; - }; - Super-b = { - action = "run-command"; - arg = "librewolf"; - }; - - Super-Down = { - action = "change-window-proportion"; - arg = 0.5; - }; - Super-Up = { - action = "change-window-proportion"; - arg = -0.5; - }; - Super-Left = { - action = "change-mwfact"; - arg = -0.1; - }; - Super-Right = { - action = "change-mwfact"; - arg = 0.1; - }; - } - // builtins.listToAttrs ( - builtins.genList (i: { - name = "Super-${toString (i + 1)}"; - value = { - action = "focus-workspace"; - arg = i; - }; - }) 9 - ) - # Media keys, with a SwayOSD popup - // - lib.mapAttrs - (_: args: { - action = "run-command"; - arg = "${lib.getExe' pkgs.swayosd "swayosd-client"} ${args}"; - # Holding raise/lower keeps changing the volume, but holding mute shouldn't keep toggling - repeat = !lib.hasSuffix "mute-toggle" args; - }) - { - XF86AudioRaiseVolume = "--output-volume raise"; - XF86AudioLowerVolume = "--output-volume lower"; - XF86AudioMute = "--output-volume mute-toggle"; - XF86AudioMicMute = "--input-volume mute-toggle"; - }; - - mousebinds = { - Super-Left = "swap-tile"; - # Works on tiled windows too (patched in, see resize-tiled-windows.patch) - Super-Right = "resize-tile"; - Super-wheeldown = "focus-previous-workspace"; - Super-wheelup = "focus-next-workspace"; - }; - - input.touchpad = { - natural-scrolling = true; - click-method = "clickfinger"; - }; - - gesturebinds = { - focus-next-workspace = { - fingers = 3; - direction = "left"; - min-swipe-distance = 1; - }; - - focus-previous-workspace = { - fingers = 3; - direction = "right"; - min-swipe-distance = 1; - }; - }; - - rules = [ - { - match-app-id = [ ".*" ]; - floating = false; - } - ]; - - outputs = { - HDMI-A-1.position.x = 0; - DP-3.position.x = 1920; - }; - - cursor = { - name = "GoogleDot-Blue"; - size = 24; - }; - }; - }; - }; - - programs = { - hyprlock.enable = true; - - fht-compositor = { - enable = true; - package = - inputs.fht-compositor.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs - (old: { - patches = (old.patches or [ ]) ++ [ - ./resize-tiled-windows.patch - ./lone-modifier.patch - ]; - - nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ]; - - postInstall = (old.postInstall or "") + '' - wrapProgram $out/bin/fht-compositor \ - --add-flags "-c /etc/${filename}" - ''; - }); - }; - }; -} diff --git a/modules/desktop/wm/fht-compositor/default.nix b/modules/desktop/wm/fht-compositor/default.nix new file mode 100644 index 0000000..afd364a --- /dev/null +++ b/modules/desktop/wm/fht-compositor/default.nix @@ -0,0 +1,215 @@ +{ + inputs, + config, + pkgs, + lib, + ... +}: +let + cfg = config.programs.fht-compositor; + filename = "fht-compositor.toml"; + format = pkgs.formats.toml { }; + + # (Re)starts the programs that run alongside the compositor. Run at login, and on Super+Ctrl+R + # so they pick up rebuilt versions and config. Each runs as a systemd user unit named + # session-, which is stopped (waiting for it to exit, since single-instance apps would + # otherwise hand off to the old one) and started again. Logs: journalctl --user -u session- + session-programs = pkgs.writeShellApplication { + name = "session-programs"; + runtimeInputs = [ pkgs.dbus ]; + text = '' + # Give the units this session's environment (display, desktop name, etc.) + dbus-update-activation-environment --systemd --all + + ${lib.concatLines ( + lib.mapAttrsToList (name: command: '' + systemctl --user stop session-${name} 2>/dev/null || true + systemd-run --user --unit=session-${name} --collect --quiet -- sh -c ${ + lib.escapeShellArg "exec ${command}" + } + '') cfg.sessionPrograms + )} + ''; + }; +in +{ + imports = [ inputs.fht-compositor.nixosModules.default ]; + + options.programs.fht-compositor = { + settings = lib.mkOption { + inherit (format) type; + default = { }; + description = "fht-compositor's config, written to /etc/${filename}."; + }; + + sessionPrograms = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + description = "Commands started at login and restarted on Super+Ctrl+R, as systemd user units named session-."; + }; + }; + + config = { + # Prefer the GNOME portal (e.g. Nautilus as the file picker), falling back to GTK. This replaces + # the portal config fht-compositor ships. + xdg.portal.config.fht-compositor = { + default = [ + "gnome" + "gtk" + ]; + + # GNOME's sends them to GNOME Shell, GTK's to any notification daemon + "org.freedesktop.impl.portal.Notification" = [ "gtk" ]; + + # GNOME's needs GNOME Shell, and GTK doesn't have one + "org.freedesktop.impl.portal.ScreenCast" = [ "fht-compositor" ]; + }; + + environment = { + # Called by name (rather than by store path), so Super+Ctrl+R after a rebuild runs the new version + systemPackages = [ session-programs ]; + etc.${filename}.source = format.generate "config.toml" cfg.settings; + }; + + programs.fht-compositor = { + enable = true; + package = + inputs.fht-compositor.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs + (old: { + patches = (old.patches or [ ]) ++ [ + ./resize-tiled-windows.patch + ./lone-modifier.patch + ]; + + nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ]; + + postInstall = (old.postInstall or "") + '' + wrapProgram $out/bin/fht-compositor \ + --add-flags "-c /etc/${filename}" + ''; + }); + + sessionPrograms.wallpaper = "${lib.getExe pkgs.swaybg} -i ${../../../../background.jpg} -m fill"; + + settings = { + autostart = [ "session-programs" ]; + + general = { + focus-follows-mouse = true; + inner-gaps = 10; + }; + + decorations = { + border = { + thickness = 4; + radius = 12; + }; + blur.disable = true; + }; + + keybinds = { + Alt-Tab = "focus-next-window"; + Super-q = "close-focused-window"; + Super-f = "fullscreen-focused-window"; + Super-Ctrl-q = "quit"; + Super-Ctrl-r = { + action = "run-command"; + arg = "fht-compositor ipc action reload-config; session-programs"; + }; + + Print = { + action = "run-command"; + arg = "${lib.getExe pkgs.grim} -g \"$(${lib.getExe pkgs.slurp})\" - | gradia"; + }; + + Super-t = { + action = "run-command"; + arg = "ptyxis --new-window"; + }; + Super-e = { + action = "run-command"; + arg = "nautilus"; + }; + Super-c = { + action = "run-command"; + arg = "codium"; + }; + Super-b = { + action = "run-command"; + arg = "librewolf"; + }; + + Super-Down = { + action = "change-window-proportion"; + arg = 0.5; + }; + Super-Up = { + action = "change-window-proportion"; + arg = -0.5; + }; + Super-Left = { + action = "change-mwfact"; + arg = -0.1; + }; + Super-Right = { + action = "change-mwfact"; + arg = 0.1; + }; + } + // builtins.listToAttrs ( + builtins.genList (i: { + name = "Super-${toString (i + 1)}"; + value = { + action = "focus-workspace"; + arg = i; + }; + }) 9 + ); + + mousebinds = { + Super-Left = "swap-tile"; + # Works on tiled windows too (patched in, see resize-tiled-windows.patch) + Super-Right = "resize-tile"; + Super-wheeldown = "focus-previous-workspace"; + Super-wheelup = "focus-next-workspace"; + }; + + input.touchpad = { + natural-scrolling = true; + click-method = "clickfinger"; + }; + + gesturebinds = { + focus-next-workspace = { + fingers = 3; + direction = "left"; + min-swipe-distance = 1; + }; + + focus-previous-workspace = { + fingers = 3; + direction = "right"; + min-swipe-distance = 1; + }; + }; + + rules = [ + { + match-app-id = [ ".*" ]; + floating = false; + } + ]; + + outputs = { + HDMI-A-1.position.x = 0; + DP-3.position.x = 1920; + }; + + cursor = { + name = "GoogleDot-Blue"; + size = 24; + }; + }; + }; + }; +} diff --git a/modules/desktop/fht-compositor/lone-modifier.patch b/modules/desktop/wm/fht-compositor/lone-modifier.patch similarity index 100% rename from modules/desktop/fht-compositor/lone-modifier.patch rename to modules/desktop/wm/fht-compositor/lone-modifier.patch diff --git a/modules/desktop/fht-compositor/resize-tiled-windows.patch b/modules/desktop/wm/fht-compositor/resize-tiled-windows.patch similarity index 100% rename from modules/desktop/fht-compositor/resize-tiled-windows.patch rename to modules/desktop/wm/fht-compositor/resize-tiled-windows.patch diff --git a/modules/desktop/wm/launcher.nix b/modules/desktop/wm/launcher.nix new file mode 100644 index 0000000..bf5ea8e --- /dev/null +++ b/modules/desktop/wm/launcher.nix @@ -0,0 +1,74 @@ +{ + config, + pkgs, + lib, + ... +}: +{ + environment.etc = { + "xdg/walker/config.toml".source = (pkgs.formats.toml { }).generate "walker.toml" { + force_keyboard_focus = true; + theme = "system"; + }; + + "xdg/walker/themes/system/style.css".source = pkgs.runCommand "walker-system-style.css" { } '' + sed '/^@define-color/d' ${pkgs.walker.src}/resources/themes/default/style.css > $out + ''; + + "xdg/elephant/elephant.toml".source = (pkgs.formats.toml { }).generate "elephant.toml" { + terminal_cmd = "ptyxis -x"; + ignored_providers = [ + "1password" + "aptpackages" + "archlinuxpkgs" + "bitwarden" + "bluetooth" + "bookmarks" + "clipboard" + "dnfpackages" + "files" + "menus" + "niriactions" + "nirisessions" + "playerctl" + "protonpass" + "providerlist" + "runner" + "snippets" + "symbols" + "todo" + "unicode" + "windows" + "wireplumber" + ]; + }; + + "xdg/elephant/websearch.toml".source = + let + inherit (config.programs.firefox.policies) SearchEngines; + in + (pkgs.formats.toml { }).generate "websearch.toml" { + entries = map ( + engine: + { + name = engine.Name; + url = lib.replaceStrings [ "{searchTerms}" ] [ "%TERM%" ] engine.URLTemplate; + default = engine.Name == SearchEngines.Default; + } + // lib.optionalAttrs (engine ? Alias) { prefix = "${engine.Alias} "; } + ) SearchEngines.Add; + }; + }; + + programs.fht-compositor = { + sessionPrograms = { + elephant = lib.getExe pkgs.elephant; + walker = "walker --gapplication-service"; + }; + + settings.keybinds.Super_L = { + action = "run-command"; + arg = "walker"; + }; + }; +} diff --git a/modules/desktop/wm/lockscreen.nix b/modules/desktop/wm/lockscreen.nix new file mode 100644 index 0000000..6afec7a --- /dev/null +++ b/modules/desktop/wm/lockscreen.nix @@ -0,0 +1,52 @@ +{ + config, + pkgs, + lib, + ... +}: +{ + services.logind.settings.Login.HandlePowerKey = "suspend"; + + programs = { + hyprlock.enable = true; + + fht-compositor.sessionPrograms.hypridle = + "${lib.getExe pkgs.hypridle} --config ${pkgs.writeText "hypridle.conf" '' + general { + lock_cmd = pidof hyprlock || ${lib.getExe pkgs.hyprlock} --config ${ + let + inherit (config.lib.stylix) colors; + in + pkgs.writeText "hyprlock.conf" '' + background { + path = screenshot + blur_passes = 3 + } + + label { + text = $TIME + font_size = 96 + position = 0, 200 + } + + label { + text = cmd[update:60000] date +"%A, %B %-d" + font_size = 20 + position = 0, 110 + } + + input-field { + size = 300, 50 + outline_thickness = 0 + inner_color = rgb(${colors.base01}) + font_color = rgb(${colors.base05}) + check_color = rgb(${colors.base0D}) + fade_on_empty = false + } + '' + } + before_sleep_cmd = loginctl lock-session + } + ''}"; + }; +} diff --git a/modules/desktop/wm/notifications.nix b/modules/desktop/wm/notifications.nix new file mode 100644 index 0000000..dcec193 --- /dev/null +++ b/modules/desktop/wm/notifications.nix @@ -0,0 +1,76 @@ +{ pkgs, lib, ... }: +let + swaync = pkgs.swaynotificationcenter; +in +{ + programs.fht-compositor.sessionPrograms.swaync = + "${lib.getExe' swaync "swaync"} --style ${pkgs.writeText "swaync.css" '' + @import url("file://${swaync}/etc/xdg/swaync/style.css"); + + :root { + --noti-bg-focus: transparent; + --noti-border-color: transparent; + --notification-shadow: none; + --border-radius: 16px; + --notification-icon-size: 48px; + --font-size-summary: 14px; + --font-size-body: 14px; + } + + .control-center { + background: shade(@theme_bg_color, 1.1); + } + + .notification-row .notification-background .notification, + .notification-group.collapsed .notification-row .notification { + background: shade(@theme_bg_color, 1.25); + } + + .notification-row .notification-background .notification .notification-default-action:hover, + .control-center .control-center-list .notification .notification-default-action:hover, + .notification-group.collapsed:hover .notification-row:not(:only-child) .notification { + background: shade(@theme_bg_color, 1.4); + } + + .control-center .control-center-list-placeholder { + margin-bottom: 24px; + } + + .notification-row .notification-background .notification .notification-default-action .notification-content { + padding: 6px; + } + + .notification-row .notification-background .notification .notification-default-action .notification-content .text-box .time { + font-size: 12px; + font-weight: normal; + opacity: 0.6; + } + + .notification-row .notification-background .notification .notification-default-action .notification-content .text-box .body { + opacity: 0.8; + } + ''} --config ${ + (pkgs.formats.json { }).generate "swaync.json" ( + let + default = lib.importJSON "${swaync}/etc/xdg/swaync/config.json"; + in + default + // lib.genAttrs (map (side: "control-center-margin-${side}") [ + "top" + "bottom" + "right" + ]) (_: 8) + // { + notification-window-width = 420; + ignore-gtk-theme = false; + fit-to-screen = false; + control-center-height = -1; + widget-config = default.widget-config // { + notifications = default.widget-config.notifications or { } // { + vexpand = false; + }; + }; + } + ) + }"; +} diff --git a/modules/desktop/wm/osd.nix b/modules/desktop/wm/osd.nix new file mode 100644 index 0000000..2a35e03 --- /dev/null +++ b/modules/desktop/wm/osd.nix @@ -0,0 +1,26 @@ +{ pkgs, lib, ... }: +{ + programs.fht-compositor = { + sessionPrograms.swayosd = "${lib.getExe' pkgs.swayosd "swayosd-server"} --style ${pkgs.writeText "swayosd.css" '' + @import url("file://${pkgs.swayosd}/etc/xdg/swayosd/style.css"); + + window#osd { + background: shade(@theme_bg_color, 1.15); + } + ''}"; + + settings.keybinds = + lib.mapAttrs + (_: args: { + action = "run-command"; + arg = "${lib.getExe' pkgs.swayosd "swayosd-client"} ${args}"; + repeat = true; + }) + { + XF86AudioRaiseVolume = "--output-volume raise"; + XF86AudioLowerVolume = "--output-volume lower"; + XF86AudioMute = "--output-volume mute-toggle"; + XF86AudioMicMute = "--input-volume mute-toggle"; + }; + }; +} diff --git a/modules/desktop/wm/panel.nix b/modules/desktop/wm/panel.nix new file mode 100644 index 0000000..b609f80 --- /dev/null +++ b/modules/desktop/wm/panel.nix @@ -0,0 +1,4 @@ +{ + # gaypanel itself is configured in wrappers/desktop/gaypanel + programs.fht-compositor.sessionPrograms.gaypanel = "gaypanel"; +}