diff --git a/flake.lock b/flake.lock
index ca2b67b..2d38dbe 100644
--- a/flake.lock
+++ b/flake.lock
@@ -74,6 +74,75 @@
"type": "github"
}
},
+ "hyprland": {
+ "inputs": {
+ "hyprland-protocols": "hyprland-protocols",
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": "systems",
+ "wlroots": "wlroots",
+ "xdph": "xdph"
+ },
+ "locked": {
+ "lastModified": 1695935601,
+ "narHash": "sha256-LLlL4EXxupanb3GwSMcogCCsx7WAfd7/u13QkAwyBgQ=",
+ "owner": "hyprwm",
+ "repo": "hyprland",
+ "rev": "3f09b14381e8b28dd2cc1d292763374f2d6c8484",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "hyprland",
+ "type": "github"
+ }
+ },
+ "hyprland-protocols": {
+ "inputs": {
+ "nixpkgs": [
+ "hyprland",
+ "nixpkgs"
+ ],
+ "systems": [
+ "hyprland",
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1691753796,
+ "narHash": "sha256-zOEwiWoXk3j3+EoF3ySUJmberFewWlagvewDRuWYAso=",
+ "owner": "hyprwm",
+ "repo": "hyprland-protocols",
+ "rev": "0c2ce70625cb30aef199cb388f99e19a61a6ce03",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "hyprland-protocols",
+ "type": "github"
+ }
+ },
+ "hyprwm-contrib": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1695455081,
+ "narHash": "sha256-AtAMze2J5Maol28OLQoCFgppRWEy06Mn9RhduXNmhiI=",
+ "owner": "hyprwm",
+ "repo": "contrib",
+ "rev": "33663f663e07b4ca52c9165f74e3d793f08b15e7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "contrib",
+ "type": "github"
+ }
+ },
"nixpkgs": {
"locked": {
"lastModified": 1695346609,
@@ -111,9 +180,74 @@
"firefox-addons": "firefox-addons",
"hardware": "hardware",
"home-manager": "home-manager",
+ "hyprland": "hyprland",
+ "hyprwm-contrib": "hyprwm-contrib",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable"
}
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "type": "github"
+ }
+ },
+ "wlroots": {
+ "flake": false,
+ "locked": {
+ "host": "gitlab.freedesktop.org",
+ "lastModified": 1695919988,
+ "narHash": "sha256-4RBgIZHaVqH0m1POnfzYRzwCWxifIKH4xQ0kCn2LGkA=",
+ "owner": "wlroots",
+ "repo": "wlroots",
+ "rev": "c2aa7fd965cb7ee8bed24f4122b720aca8f0fc1e",
+ "type": "gitlab"
+ },
+ "original": {
+ "host": "gitlab.freedesktop.org",
+ "owner": "wlroots",
+ "repo": "wlroots",
+ "rev": "c2aa7fd965cb7ee8bed24f4122b720aca8f0fc1e",
+ "type": "gitlab"
+ }
+ },
+ "xdph": {
+ "inputs": {
+ "hyprland-protocols": [
+ "hyprland",
+ "hyprland-protocols"
+ ],
+ "nixpkgs": [
+ "hyprland",
+ "nixpkgs"
+ ],
+ "systems": [
+ "hyprland",
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1694628480,
+ "narHash": "sha256-Qg9hstRw0pvjGu5hStkr2UX1D73RYcQ9Ns/KnZMIm9w=",
+ "owner": "hyprwm",
+ "repo": "xdg-desktop-portal-hyprland",
+ "rev": "8f45a6435069b9e24ebd3160eda736d7a391cbf2",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "xdg-desktop-portal-hyprland",
+ "type": "github"
+ }
}
},
"root": "root",
diff --git a/flake.nix b/flake.nix
index aa63f52..57322ef 100644
--- a/flake.nix
+++ b/flake.nix
@@ -5,6 +5,11 @@
# - https://github.com/Misterio77/nix-starter-configs/
# - https://github.com/Misterio77/nix-config/
+ nixConfig = {
+ extra-substituters = ["https://hyprland.cachix.org"];
+ extra-trusted-public-keys = ["hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="];
+ };
+
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
# You can access packages and modules from different nixpkgs revs
@@ -26,6 +31,16 @@
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
inputs.nixpkgs.follows = "nixpkgs";
};
+
+ # Hyprland
+ hyprland = {
+ url = "github:hyprwm/hyprland";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ hyprwm-contrib = {
+ url = "github:hyprwm/contrib";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
};
outputs = { self, nixpkgs, home-manager, ... } @inputs:
diff --git a/home/features/desktop/common/default.nix b/home/features/desktop/common/default.nix
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/home/features/desktop/common/default.nix
@@ -0,0 +1,2 @@
+{
+}
diff --git a/home/features/desktop/common/wayland-wm/default.nix b/home/features/desktop/common/wayland-wm/default.nix
new file mode 100644
index 0000000..220570e
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/default.nix
@@ -0,0 +1,36 @@
+{ pkgs, ... }: {
+ imports = [
+ ./gammastep.nix
+ ./kitty.nix
+ ./mako.nix
+ ./swayidle.nix
+ ./swaylock.nix
+ ./waybar.nix
+ ./wofi.nix
+ ./zathura.nix
+ ];
+
+ xdg.mimeApps.enable = true;
+ home.packages = with pkgs; [
+ grim
+ gtk3
+ imv
+ mimeo
+ primary-xwayland
+ pulseaudio
+ slurp
+ waypipe
+ wf-recorder
+ wl-clipboard
+ wl-mirror
+ wl-mirror-pick
+ xdg-utils-spawn-terminal
+ ydotool
+ ];
+
+ home.sessionVariables = {
+ MOZ_ENABLE_WAYLAND = 1;
+ QT_QPA_PLATFORM = "wayland";
+ LIBSEAT_BACKEND = "logind";
+ };
+}
\ No newline at end of file
diff --git a/home/features/desktop/common/wayland-wm/gammastep.nix b/home/features/desktop/common/wayland-wm/gammastep.nix
new file mode 100644
index 0000000..b63f992
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/gammastep.nix
@@ -0,0 +1,13 @@
+{
+ services.gammastep = {
+ enable = true;
+ provider = "geoclue2";
+ temperature = {
+ day = 6000;
+ night = 4600;
+ };
+ settings = {
+ general.adjustment-method = "wayland";
+ };
+ };
+}
\ No newline at end of file
diff --git a/home/features/desktop/common/wayland-wm/kitty.nix b/home/features/desktop/common/wayland-wm/kitty.nix
new file mode 100644
index 0000000..4d52767
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/kitty.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, ... }:
+
+let
+ kitty-xterm = pkgs.writeShellScriptBin "xterm" ''
+ ${config.programs.kitty.package}/bin/kitty -1 "$@"
+ '';
+in
+{
+ home = {
+ packages = [ kitty-xterm ];
+ sessionVariables = {
+ TERMINAL = "kitty -1";
+ };
+ };
+
+
+ programs.kitty = {
+ enable = true;
+ font = {
+ name = config.fontProfiles.monospace.family;
+ size = 12;
+ };
+ settings = {
+ shell_integration = "no-rc"; # I prefer to do it manually
+ scrollback_lines = 4000;
+ scrollback_pager_history_size = 2048;
+ window_padding_width = 15;
+ };
+ };
+}
diff --git a/home/features/desktop/common/wayland-wm/mako.nix b/home/features/desktop/common/wayland-wm/mako.nix
new file mode 100644
index 0000000..9dcadb4
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/mako.nix
@@ -0,0 +1,13 @@
+{ config, ... }: {
+ services.mako = {
+ enable = true;
+ font = "${config.fontProfiles.regular.family} 12";
+ padding = "10,20";
+ anchor = "top-center";
+ width = 400;
+ height = 150;
+ borderSize = 2;
+ defaultTimeout = 12000;
+ layer = "overlay";
+ };
+}
diff --git a/home/features/desktop/common/wayland-wm/swayidle.nix b/home/features/desktop/common/wayland-wm/swayidle.nix
new file mode 100644
index 0000000..c2b9892
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/swayidle.nix
@@ -0,0 +1,54 @@
+{ pkgs, lib, config, ... }:
+
+let
+ swaylock = "${config.programs.swaylock.package}/bin/swaylock";
+ pgrep = "${pkgs.procps}/bin/pgrep";
+ pactl = "${pkgs.pulseaudio}/bin/pactl";
+ hyprctl = "${config.wayland.windowManager.hyprland.package}/bin/hyprctl";
+ swaymsg = "${config.wayland.windowManager.sway.package}/bin/swaymsg";
+
+ isLocked = "${pgrep} -x ${swaylock}";
+ lockTime = 4 * 60; # TODO: configurable desktop (10 min)/laptop (4 min)
+
+ # Makes two timeouts: one for when the screen is not locked (lockTime+timeout) and one for when it is.
+ afterLockTimeout = { timeout, command, resumeCommand ? null }: [
+ { timeout = lockTime + timeout; inherit command resumeCommand; }
+ { command = "${isLocked} && ${command}"; inherit resumeCommand timeout; }
+ ];
+in
+{
+ services.swayidle = {
+ enable = true;
+ systemdTarget = "graphical-session.target";
+ timeouts =
+ # Lock screen
+ [{
+ timeout = lockTime;
+ command = "${swaylock} -i ${config.wallpaper} --daemonize";
+ }] ++
+ # Mute mic
+ (afterLockTimeout {
+ timeout = 10;
+ command = "${pactl} set-source-mute @DEFAULT_SOURCE@ yes";
+ resumeCommand = "${pactl} set-source-mute @DEFAULT_SOURCE@ no";
+ }) ++
+ # Turn off RGB
+ (lib.optionals config.services.rgbdaemon.enable (afterLockTimeout {
+ timeout = 20;
+ command = "systemctl --user stop rgbdaemon";
+ resumeCommand = "systemctl --user start rgbdaemon";
+ })) ++
+ # Turn off displays (hyprland)
+ (lib.optionals config.wayland.windowManager.hyprland.enable (afterLockTimeout {
+ timeout = 40;
+ command = "${hyprctl} dispatch dpms off";
+ resumeCommand = "${hyprctl} dispatch dpms on";
+ })) ++
+ # Turn off displays (sway)
+ (lib.optionals config.wayland.windowManager.sway.enable (afterLockTimeout {
+ timeout = 40;
+ command = "${swaymsg} 'output * dpms off'";
+ resumeCommand = "${swaymsg} 'output * dpms on'";
+ }));
+ };
+}
diff --git a/home/features/desktop/common/wayland-wm/swaylock.nix b/home/features/desktop/common/wayland-wm/swaylock.nix
new file mode 100644
index 0000000..e9b8c65
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/swaylock.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, ... }: {
+ programs.swaylock = {
+ enable = true;
+ package = pkgs.swaylock-effects;
+ settings = {
+ effect-blur = "20x3";
+ fade-in = 0.1;
+
+ font = config.fontProfiles.regular.family;
+ font-size = 15;
+
+ line-uses-inside = true;
+ disable-caps-lock-text = true;
+ indicator-caps-lock = true;
+ indicator-radius = 40;
+ indicator-idle-visible = true;
+ indicator-y-position = 1000;
+
+ };
+ };
+}
diff --git a/home/features/desktop/common/wayland-wm/waybar.nix b/home/features/desktop/common/wayland-wm/waybar.nix
new file mode 100644
index 0000000..f0fa0a4
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/waybar.nix
@@ -0,0 +1,392 @@
+{ outputs, config, lib, pkgs, ... }:
+
+let
+ # Dependencies
+ cat = "${pkgs.coreutils}/bin/cat";
+ cut = "${pkgs.coreutils}/bin/cut";
+ find = "${pkgs.findutils}/bin/find";
+ grep = "${pkgs.gnugrep}/bin/grep";
+ pgrep = "${pkgs.procps}/bin/pgrep";
+ tail = "${pkgs.coreutils}/bin/tail";
+ wc = "${pkgs.coreutils}/bin/wc";
+ xargs = "${pkgs.findutils}/bin/xargs";
+ timeout = "${pkgs.coreutils}/bin/timeout";
+ ping = "${pkgs.iputils}/bin/ping";
+
+ jq = "${pkgs.jq}/bin/jq";
+ gamemoded = "${pkgs.gamemode}/bin/gamemoded";
+ systemctl = "${pkgs.systemd}/bin/systemctl";
+ journalctl = "${pkgs.systemd}/bin/journalctl";
+ playerctl = "${pkgs.playerctl}/bin/playerctl";
+ playerctld = "${pkgs.playerctl}/bin/playerctld";
+ pavucontrol = "${pkgs.pavucontrol}/bin/pavucontrol";
+ wofi = "${pkgs.wofi}/bin/wofi";
+
+ # Function to simplify making waybar outputs
+ jsonOutput = name: { pre ? "", text ? "", tooltip ? "", alt ? "", class ? "", percentage ? "" }: "${pkgs.writeShellScriptBin "waybar-${name}" ''
+ set -euo pipefail
+ ${pre}
+ ${jq} -cn \
+ --arg text "${text}" \
+ --arg tooltip "${tooltip}" \
+ --arg alt "${alt}" \
+ --arg class "${class}" \
+ --arg percentage "${percentage}" \
+ '{text:$text,tooltip:$tooltip,alt:$alt,class:$class,percentage:$percentage}'
+ ''}/bin/waybar-${name}";
+in
+{
+ programs.waybar = {
+ enable = true;
+ package = pkgs.waybar.overrideAttrs (oa: {
+ mesonFlags = (oa.mesonFlags or [ ]) ++ [ "-Dexperimental=true" ];
+ });
+ systemd.enable = true;
+ settings = {
+ primary = {
+ mode = "dock";
+ layer = "top";
+ height = 40;
+ margin = "6";
+ position = "top";
+ modules-left = [
+ "custom/menu"
+ ] ++ (lib.optionals config.wayland.windowManager.sway.enable [
+ "sway/workspaces"
+ "sway/mode"
+ ]) ++ (lib.optionals config.wayland.windowManager.hyprland.enable [
+ "hyprland/workspaces"
+ "hyprland/submap"
+ ]) ++ [
+ "custom/currentplayer"
+ "custom/player"
+ ];
+
+ modules-center = [
+ "pulseaudio"
+ "battery"
+ "clock"
+ "custom/unread-mail"
+ "custom/gpg-agent"
+ ];
+
+ modules-right = [
+ "network"
+ "custom/tailscale-ping"
+ "custom/gamemode"
+ # TODO: currently broken for some reason
+ # "custom/gammastep"
+ "tray"
+ "custom/hostname"
+ ];
+
+ clock = {
+ interval = 1;
+ format = "{:%d/%m %H:%M:%S}";
+ format-alt = "{:%Y-%m-%d %H:%M:%S %z}";
+ on-click-left = "mode";
+ tooltip-format = ''
+ {:%Y %B}
+ {calendar}'';
+ };
+ pulseaudio = {
+ format = "{icon} {volume}%";
+ format-muted = " 0%";
+ format-icons = {
+ headphone = "";
+ headset = "";
+ portable = "";
+ default = [ "" "" "" ];
+ };
+ on-click = pavucontrol;
+ };
+ idle_inhibitor = {
+ format = "{icon}";
+ format-icons = {
+ activated = "";
+ deactivated = "";
+ };
+ };
+ battery = {
+ bat = "BAT0";
+ interval = 10;
+ format-icons = [ "" "" "" "" "" "" "" "" "" "" ];
+ format = "{icon} {capacity}%";
+ format-charging = " {capacity}%";
+ onclick = "";
+ };
+ "sway/window" = {
+ max-length = 20;
+ };
+ network = {
+ interval = 3;
+ format-wifi = " {essid}";
+ format-ethernet = " Connected";
+ format-disconnected = "";
+ tooltip-format = ''
+ {ifname}
+ {ipaddr}/{cidr}
+ Up: {bandwidthUpBits}
+ Down: {bandwidthDownBits}'';
+ on-click = "";
+ };
+ "custom/tailscale-ping" = {
+ interval = 10;
+ return-type = "json";
+ exec =
+ let
+ inherit (builtins) concatStringsSep attrNames;
+ hosts = attrNames outputs.nixosConfigurations;
+ homeMachine = "merope";
+ remoteMachine = "alcyone";
+ in
+ jsonOutput "tailscale-ping" {
+ # Build variables for each host
+ pre = ''
+ set -o pipefail
+ ${concatStringsSep "\n" (map (host: ''
+ ping_${host}="$(${timeout} 2 ${ping} -c 1 -q ${host} 2>/dev/null | ${tail} -1 | ${cut} -d '/' -f5 | ${cut} -d '.' -f1)ms" || ping_${host}="Disconnected"
+ '') hosts)}
+ '';
+ # Access a remote machine's and a home machine's ping
+ text = " $ping_${remoteMachine} / $ping_${homeMachine}";
+ # Show pings from all machines
+ tooltip = concatStringsSep "\n" (map (host: "${host}: $ping_${host}") hosts);
+ };
+ format = "{}";
+ on-click = "";
+ };
+ "custom/menu" = {
+ return-type = "json";
+ exec = jsonOutput "menu" {
+ text = "";
+ tooltip = ''$(${cat} /etc/os-release | ${grep} PRETTY_NAME | ${cut} -d '"' -f2)'';
+ };
+ on-click = "${wofi} -S drun -x 10 -y 10 -W 25% -H 60%";
+ };
+ "custom/hostname" = {
+ exec = "echo $USER@$HOSTNAME";
+ };
+ "custom/unread-mail" = {
+ interval = 5;
+ return-type = "json";
+ exec = jsonOutput "unread-mail" {
+ pre = ''
+ count=$(${find} ~/Mail/*/Inbox/new -type f | ${wc} -l)
+ if ${pgrep} mbsync &>/dev/null; then
+ status="syncing"
+ else if [ "$count" == "0" ]; then
+ status="read"
+ else
+ status="unread"
+ fi
+ fi
+ '';
+ text = "$count";
+ alt = "$status";
+ };
+ format = "{icon} ({})";
+ format-icons = {
+ "read" = "";
+ "unread" = "";
+ "syncing" = "";
+ };
+ };
+ "custom/gpg-agent" = {
+ interval = 2;
+ return-type = "json";
+ exec =
+ let gpgCmds = import ../../../cli/gpg-commands.nix { inherit pkgs; };
+ in
+ jsonOutput "gpg-agent" {
+ pre = ''status=$(${gpgCmds.isUnlocked} && echo "unlocked" || echo "locked")'';
+ alt = "$status";
+ tooltip = "GPG is $status";
+ };
+ format = "{icon}";
+ format-icons = {
+ "locked" = "";
+ "unlocked" = "";
+ };
+ on-click = "";
+ };
+ "custom/gamemode" = {
+ exec-if = "${gamemoded} --status | ${grep} 'is active' -q";
+ interval = 2;
+ return-type = "json";
+ exec = jsonOutput "gamemode" {
+ tooltip = "Gamemode is active";
+ };
+ format = " ";
+ };
+ "custom/gammastep" = {
+ interval = 5;
+ return-type = "json";
+ exec = jsonOutput "gammastep" {
+ pre = ''
+ if unit_status="$(${systemctl} --user is-active gammastep)"; then
+ status="$unit_status ($(${journalctl} --user -u gammastep.service -g 'Period: ' | ${tail} -1 | ${cut} -d ':' -f6 | ${xargs}))"
+ else
+ status="$unit_status"
+ fi
+ '';
+ alt = "\${status:-inactive}";
+ tooltip = "Gammastep is $status";
+ };
+ format = "{icon}";
+ format-icons = {
+ "activating" = " ";
+ "deactivating" = " ";
+ "inactive" = "? ";
+ "active (Night)" = " ";
+ "active (Nighttime)" = " ";
+ "active (Transition (Night)" = " ";
+ "active (Transition (Nighttime)" = " ";
+ "active (Day)" = " ";
+ "active (Daytime)" = " ";
+ "active (Transition (Day)" = " ";
+ "active (Transition (Daytime)" = " ";
+ };
+ on-click = "${systemctl} --user is-active gammastep && ${systemctl} --user stop gammastep || ${systemctl} --user start gammastep";
+ };
+ "custom/currentplayer" = {
+ interval = 2;
+ return-type = "json";
+ exec = jsonOutput "currentplayer" {
+ pre = ''
+ player="$(${playerctl} status -f "{{playerName}}" 2>/dev/null || echo "No player active" | ${cut} -d '.' -f1)"
+ count="$(${playerctl} -l | ${wc} -l)"
+ if ((count > 1)); then
+ more=" +$((count - 1))"
+ else
+ more=""
+ fi
+ '';
+ alt = "$player";
+ tooltip = "$player ($count available)";
+ text = "$more";
+ };
+ format = "{icon}{}";
+ format-icons = {
+ "No player active" = " ";
+ "Celluloid" = " ";
+ "spotify" = " ";
+ "ncspot" = " ";
+ "qutebrowser" = " ";
+ "firefox" = " ";
+ "discord" = " ";
+ "sublimemusic" = " ";
+ "kdeconnect" = " ";
+ "chromium" = " ";
+ };
+ on-click = "${playerctld} shift";
+ on-click-right = "${playerctld} unshift";
+ };
+ "custom/player" = {
+ exec-if = "${playerctl} status";
+ exec = ''${playerctl} metadata --format '{"text": "{{title}} - {{artist}}", "alt": "{{status}}", "tooltip": "{{title}} - {{artist}} ({{album}})"}' '';
+ return-type = "json";
+ interval = 2;
+ max-length = 30;
+ format = "{icon} {}";
+ format-icons = {
+ "Playing" = "";
+ "Paused" = " ";
+ "Stopped" = "";
+ };
+ on-click = "${playerctl} play-pause";
+ };
+ };
+
+ };
+ # Cheatsheet:
+ # x -> all sides
+ # x y -> vertical, horizontal
+ # x y z -> top, horizontal, bottom
+ # w x y z -> top, right, bottom, left
+ style = let inherit (config.colorscheme) colors; in /* css */ ''
+ * {
+ font-family: ${config.fontProfiles.regular.family}, ${config.fontProfiles.monospace.family};
+ font-size: 12pt;
+ padding: 0 8px;
+ }
+
+ .modules-right {
+ margin-right: -15px;
+ }
+
+ .modules-left {
+ margin-left: -15px;
+ }
+
+ window#waybar.top {
+ opacity: 0.95;
+ padding: 0;
+ background-color: #${colors.base00};
+ border: 2px solid #${colors.base0C};
+ border-radius: 10px;
+ }
+ window#waybar.bottom {
+ opacity: 0.90;
+ background-color: #${colors.base00};
+ border: 2px solid #${colors.base0C};
+ border-radius: 10px;
+ }
+
+ window#waybar {
+ color: #${colors.base05};
+ }
+
+ #workspaces button {
+ background-color: #${colors.base01};
+ color: #${colors.base05};
+ padding: 5px 1px;
+ margin: 3px 0;
+ }
+ #workspaces button.hidden {
+ background-color: #${colors.base00};
+ color: #${colors.base04};
+ }
+ #workspaces button.focused,
+ #workspaces button.active {
+ background-color: #${colors.base0A};
+ color: #${colors.base00};
+ }
+
+ #clock {
+ background-color: #${colors.base0C};
+ color: #${colors.base00};
+ padding-left: 15px;
+ padding-right: 15px;
+ margin-top: 0;
+ margin-bottom: 0;
+ border-radius: 10px;
+ }
+
+ #custom-menu {
+ background-color: #${colors.base0C};
+ color: #${colors.base00};
+ padding-left: 15px;
+ padding-right: 22px;
+ margin: 0;
+ border-radius: 10px;
+ }
+ #custom-hostname {
+ background-color: #${colors.base0C};
+ color: #${colors.base00};
+ padding-left: 15px;
+ padding-right: 18px;
+ margin-right: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ border-radius: 10px;
+ }
+ #custom-currentplayer {
+ padding-right: 0;
+ }
+ #tray {
+ color: #${colors.base05};
+ }
+ '';
+ };
+}
diff --git a/home/features/desktop/common/wayland-wm/wofi.nix b/home/features/desktop/common/wayland-wm/wofi.nix
new file mode 100644
index 0000000..4bd5836
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/wofi.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }: {
+ programs.wofi = {
+ enable = true;
+ package = pkgs.wofi.overrideAttrs (oa: {
+ patches = (oa.patches or [ ]) ++ [
+ ./wofi-run-shell.patch # Fix for https://todo.sr.ht/~scoopta/wofi/174
+ ];
+ });
+ settings = {
+ image_size = 48;
+ columns = 3;
+ allow_images = true;
+ insensitive = true;
+ run-always_parse_args = true;
+ run-cache_file = "/dev/null";
+ run-exec_search = true;
+ };
+ };
+
+ home.packages =
+ let
+ inherit (config.programs.password-store) package enable;
+ in
+ lib.optional enable (pkgs.pass-wofi.override { pass = package; });
+}
diff --git a/home/features/desktop/common/wayland-wm/zathura.nix b/home/features/desktop/common/wayland-wm/zathura.nix
new file mode 100644
index 0000000..97f8d24
--- /dev/null
+++ b/home/features/desktop/common/wayland-wm/zathura.nix
@@ -0,0 +1,9 @@
+{ config, ... }: {
+ programs.zathura = {
+ enable = true;
+ options = {
+ selection-clipboard = "clipboard";
+ font = "${config.fontProfiles.regular.family} 12";
+ };
+ };
+}
diff --git a/home/features/desktop/hyprland/basic-binds.nix b/home/features/desktop/hyprland/basic-binds.nix
new file mode 100644
index 0000000..2c8f84e
--- /dev/null
+++ b/home/features/desktop/hyprland/basic-binds.nix
@@ -0,0 +1,70 @@
+{ lib, ... }:
+let
+ workspaces =
+ (map toString (lib.range 0 9)) ++
+ (map (n: "F${toString n}") (lib.range 1 12));
+ # Map keys to hyprland directions
+ directions = rec {
+ left = "l"; right = "r"; up = "u"; down = "d";
+ h = left; l = right; k = up; j = down;
+ };
+in {
+ wayland.windowManager.hyprland.settings = {
+ bindm = [
+ "SUPER,mouse:272,movewindow"
+ "SUPER,mouse:273,resizewindow"
+ ];
+
+ bind = [
+ "SUPERSHIFT,q,killactive"
+ "SUPERSHIFT,e,exit"
+
+ "SUPER,s,togglesplit"
+ "SUPER,f,fullscreen,1"
+ "SUPERSHIFT,f,fullscreen,0"
+ "SUPERSHIFT,space,togglefloating"
+
+ "SUPER,minus,splitratio,-0.25"
+ "SUPERSHIFT,minus,splitratio,-0.3333333"
+
+ "SUPER,equal,splitratio,0.25"
+ "SUPERSHIFT,equal,splitratio,0.3333333"
+
+ "SUPER,g,togglegroup"
+ "SUPER,t,lockactivegroup,toggle"
+ "SUPER,apostrophe,changegroupactive,f"
+ "SUPERSHIFT,apostrophe,changegroupactive,b"
+
+ "SUPER,u,togglespecialworkspace"
+ "SUPERSHIFT,u,movetoworkspace,special"
+ ] ++
+ # Change workspace
+ (map (n:
+ "SUPER,${n},workspace,name:${n}"
+ ) workspaces) ++
+ # Move window to workspace
+ (map (n:
+ "SUPERSHIFT,${n},movetoworkspacesilent,name:${n}"
+ ) workspaces) ++
+ # Move focus
+ (lib.mapAttrsToList (key: direction:
+ "SUPER,${key},movefocus,${direction}"
+ ) directions) ++
+ # Swap windows
+ (lib.mapAttrsToList (key: direction:
+ "SUPERSHIFT,${key},swapwindow,${direction}"
+ ) directions) ++
+ # Move windows
+ (lib.mapAttrsToList (key: direction:
+ "SUPERCONTROL,${key},movewindoworgroup,${direction}"
+ ) directions) ++
+ # Move monitor focus
+ (lib.mapAttrsToList (key: direction:
+ "SUPERALT,${key},focusmonitor,${direction}"
+ ) directions) ++
+ # Move workspace to other monitor
+ (lib.mapAttrsToList (key: direction:
+ "SUPERALTSHIFT,${key},movecurrentworkspacetomonitor,${direction}"
+ ) directions);
+ };
+}
diff --git a/home/features/desktop/hyprland/default.nix b/home/features/desktop/hyprland/default.nix
new file mode 100644
index 0000000..98248bd
--- /dev/null
+++ b/home/features/desktop/hyprland/default.nix
@@ -0,0 +1,187 @@
+{ lib, config, pkgs, ... }: {
+ imports = [
+ ../common
+ ../common/wayland-wm
+
+ ./tty-init.nix
+ ./basic-binds.nix
+ ./systemd-fixes.nix
+ ];
+
+ home.packages = with pkgs; [
+ inputs.hyprwm-contrib.grimblast
+ hyprslurp
+ hyprpicker
+ ];
+
+ wayland.windowManager.hyprland = {
+ enable = true;
+ package = pkgs.inputs.hyprland.hyprland;
+
+ settings = {
+ general = {
+ gaps_in = 15;
+ gaps_out = 20;
+ border_size = 2.7;
+ cursor_inactive_timeout = 4;
+ "col.active_border" = "0xff${config.colorscheme.colors.base0C}";
+ "col.inactive_border" = "0xff${config.colorscheme.colors.base02}";
+ "col.group_border_active" = "0xff${config.colorscheme.colors.base0B}";
+ "col.group_border" = "0xff${config.colorscheme.colors.base04}";
+ };
+ input = {
+ kb_layout = "br,us";
+ touchpad.disable_while_typing = false;
+ };
+ dwindle.split_width_multiplier = 1.35;
+ misc.vfr = true;
+
+ decoration = {
+ active_opacity = 0.92;
+ inactive_opacity = 0.75;
+ fullscreen_opacity = 1.0;
+ rounding = 5;
+ blur = {
+ enabled = true;
+ size = 5;
+ passes = 3;
+ new_optimizations = true;
+ ignore_opacity = true;
+ };
+ drop_shadow = true;
+ shadow_range = 12;
+ shadow_offset = "3 3";
+ "col.shadow" = "0x44000000";
+ "col.shadow_inactive" = "0x66000000";
+ };
+ animations = {
+ enabled = true;
+ bezier = [
+ "easein,0.11, 0, 0.5, 0"
+ "easeout,0.5, 1, 0.89, 1"
+ "easeinback,0.36, 0, 0.66, -0.56"
+ "easeoutback,0.34, 1.56, 0.64, 1"
+ ];
+
+ animation = [
+ "windowsIn,1,3,easeoutback,slide"
+ "windowsOut,1,3,easeinback,slide"
+ "windowsMove,1,3,easeoutback"
+ "workspaces,1,2,easeoutback,slide"
+ "fadeIn,1,3,easeout"
+ "fadeOut,1,3,easein"
+ "fadeSwitch,1,3,easeout"
+ "fadeShadow,1,3,easeout"
+ "fadeDim,1,3,easeout"
+ "border,1,3,easeout"
+ ];
+ };
+
+ exec = [
+ "${pkgs.swaybg}/bin/swaybg -i ${config.wallpaper} --mode fill"
+ ];
+
+ bind = let
+ swaylock = "${config.programs.swaylock.package}/bin/swaylock";
+ playerctl = "${config.services.playerctld.package}/bin/playerctl";
+ playerctld = "${config.services.playerctld.package}/bin/playerctld";
+ makoctl = "${config.services.mako.package}/bin/makoctl";
+ wofi = "${config.programs.wofi.package}/bin/wofi";
+ pass-wofi = "${pkgs.pass-wofi.override {
+ pass = config.programs.password-store.package;
+ }}/bin/pass-wofi";
+
+ grimblast = "${pkgs.inputs.hyprwm-contrib.grimblast}/bin/grimblast";
+ pactl = "${pkgs.pulseaudio}/bin/pactl";
+ tly = "${pkgs.tly}/bin/tly";
+ gtk-play = "${pkgs.libcanberra-gtk3}/bin/canberra-gtk-play";
+ notify-send = "${pkgs.libnotify}/bin/notify-send";
+
+ gtk-launch = "${pkgs.gtk3}/bin/gtk-launch";
+ xdg-mime = "${pkgs.xdg-utils}/bin/xdg-mime";
+ defaultApp = type: "${gtk-launch} $(${xdg-mime} query default ${type})";
+
+ terminal = config.home.sessionVariables.TERMINAL;
+ browser = defaultApp "x-scheme-handler/https";
+ editor = defaultApp "text/plain";
+ in [
+ # Program bindings
+ "SUPER,Return,exec,${terminal}"
+ "SUPER,e,exec,${editor}"
+ "SUPER,v,exec,${editor}"
+ "SUPER,b,exec,${browser}"
+ # Brightness control (only works if the system has lightd)
+ ",XF86MonBrightnessUp,exec,light -A 10"
+ ",XF86MonBrightnessDown,exec,light -U 10"
+ # Volume
+ ",XF86AudioRaiseVolume,exec,${pactl} set-sink-volume @DEFAULT_SINK@ +5%"
+ ",XF86AudioLowerVolume,exec,${pactl} set-sink-volume @DEFAULT_SINK@ -5%"
+ ",XF86AudioMute,exec,${pactl} set-sink-mute @DEFAULT_SINK@ toggle"
+ "SHIFT,XF86AudioMute,exec,${pactl} set-source-mute @DEFAULT_SOURCE@ toggle"
+ ",XF86AudioMicMute,exec,${pactl} set-source-mute @DEFAULT_SOURCE@ toggle"
+ # Screenshotting
+ ",Print,exec,${grimblast} --notify --freeze copy output"
+ "SHIFT,Print,exec,${grimblast} --notify --freeze copy active"
+ "CONTROL,Print,exec,${grimblast} --notify --freeze copy screen"
+ "SUPER,Print,exec,${grimblast} --notify --freeze copy area"
+ "ALT,Print,exec,${grimblast} --notify --freeze copy area"
+ # Tally counter
+ "SUPER,z,exec,${notify-send} -t 1000 $(${tly} time) && ${tly} add && ${gtk-play} -i dialog-information" # Add new entry
+ "SUPERCONTROL,z,exec,${notify-send} -t 1000 $(${tly} time) && ${tly} undo && ${gtk-play} -i dialog-warning" # Undo last entry
+ "SUPERCONTROLSHIFT,z,exec,${tly} reset && ${gtk-play} -i complete" # Reset
+ "SUPERSHIFT,z,exec,${notify-send} -t 1000 $(${tly} time)" # Show current time
+ ] ++
+
+ (lib.optionals config.services.playerctld.enable [
+ # Media control
+ ",XF86AudioNext,exec,${playerctl} next"
+ ",XF86AudioPrev,exec,${playerctl} previous"
+ ",XF86AudioPlay,exec,${playerctl} play-pause"
+ ",XF86AudioStop,exec,${playerctl} stop"
+ "ALT,XF86AudioNext,exec,${playerctld} shift"
+ "ALT,XF86AudioPrev,exec,${playerctld} unshift"
+ "ALT,XF86AudioPlay,exec,systemctl --user restart playerctld"
+ ]) ++
+ # Screen lock
+ (lib.optionals config.programs.swaylock.enable [
+ ",XF86Launch5,exec,${swaylock} -i ${config.wallpaper}"
+ ",XF86Launch4,exec,${swaylock} -i ${config.wallpaper}"
+ "SUPER,backspace,exec,${swaylock} -i ${config.wallpaper}"
+ ]) ++
+ # Notification manager
+ (lib.optionals config.services.mako.enable [
+ "SUPER,w,exec,${makoctl} dismiss"
+ ]) ++
+
+ # Launcher
+ (lib.optionals config.programs.wofi.enable [
+ "SUPER,x,exec,${wofi} -S drun -x 10 -y 10 -W 25% -H 60%"
+ "SUPER,d,exec,${wofi} -S run"
+ ] ++ (lib.optionals config.programs.password-store.enable [
+ ",Scroll_Lock,exec,${pass-wofi}" # fn+k
+ ",XF86Calculator,exec,${pass-wofi}" # fn+f12
+ "SUPER,semicolon,exec,pass-wofi"
+ ]));
+
+ monitor = map (m: let
+ resolution = "${toString m.width}x${toString m.height}@${toString m.refreshRate}";
+ position = "${toString m.x}x${toString m.y}";
+ in
+ "${m.name},${if m.enabled then "${resolution},${position},1" else "disable"}"
+ ) (config.monitors);
+
+ workspace = map (m:
+ "${m.name},${m.workspace}"
+ ) (lib.filter (m: m.enabled && m.workspace != null) config.monitors);
+
+ };
+ # This is order sensitive, so it has to come here.
+ extraConfig = ''
+ # Passthrough mode (e.g. for VNC)
+ bind=SUPER,P,submap,passthrough
+ submap=passthrough
+ bind=SUPER,P,submap,reset
+ submap=reset
+ '';
+ };
+}
diff --git a/home/features/desktop/hyprland/systemd-fixes.nix b/home/features/desktop/hyprland/systemd-fixes.nix
new file mode 100644
index 0000000..f2b5e9b
--- /dev/null
+++ b/home/features/desktop/hyprland/systemd-fixes.nix
@@ -0,0 +1,30 @@
+{ lib, config, ... }:
+let
+ cfg = config.wayland.windowManager.hyprland;
+in
+{
+ config = lib.mkIf (cfg.enable && cfg.systemdIntegration) {
+ # Stolen from https://github.com/alebastr/sway-systemd/commit/0fdb2c4b10beb6079acd6073c5b3014bd58d3b74
+ systemd.user.targets.hyprland-session-shutdown = {
+ Unit = {
+ Description = "Shutdown running Hyprland session";
+ DefaultDependencies = "no";
+ StopWhenUnneeded = "true";
+
+ Conflicts = [
+ "graphical-session.target"
+ "graphical-session-pre.target"
+ "hyprland-session.target"
+ ];
+ After = [
+ "graphical-session.target"
+ "graphical-session-pre.target"
+ "hyprland-session.target"
+ ];
+ };
+ };
+ wayland.windowManager.hyprland.settings.bind = lib.mkAfter [
+ "SUPERSHIFT,e,exec,systemctl --user start hyprland-session-shutdown.target; hyprctl dispatch exit"
+ ];
+ };
+}
diff --git a/home/features/desktop/hyprland/tty-init.nix b/home/features/desktop/hyprland/tty-init.nix
new file mode 100644
index 0000000..16763a9
--- /dev/null
+++ b/home/features/desktop/hyprland/tty-init.nix
@@ -0,0 +1,19 @@
+{
+ programs = {
+ fish.loginShellInit = ''
+ if test (tty) = "/dev/tty1"
+ exec Hyprland &> /dev/null
+ end
+ '';
+ zsh.loginExtra = ''
+ if [ "$(tty)" = "/dev/tty1" ]; then
+ exec Hyprland &> /dev/null
+ fi
+ '';
+ zsh.profileExtra = ''
+ if [ "$(tty)" = "/dev/tty1" ]; then
+ exec Hyprland &> /dev/null
+ fi
+ '';
+ };
+}
diff --git a/home/pixnix.nix b/home/pixnix.nix
index 277e25f..2da1687 100644
--- a/home/pixnix.nix
+++ b/home/pixnix.nix
@@ -3,10 +3,19 @@
./global
./features/cli/extras
./features/desktop
+ ./features/desktop/hyprland
];
# packages
home.packages = with pkgs; [
vagrant
];
+
+ monitors = [{
+ name = "eDP-1";
+ width = 2400;
+ height = 1600;
+ workspace = "1";
+ primary = true;
+ }];
}
diff --git a/hosts/common/global/nix.nix b/hosts/common/global/nix.nix
index 1766ed3..6eec252 100644
--- a/hosts/common/global/nix.nix
+++ b/hosts/common/global/nix.nix
@@ -2,6 +2,8 @@
{
nix = {
settings = {
+ substituters = ["https://hyprland.cachix.org"];
+ trusted-public-keys = ["hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="];
trusted-users = [ "root" "@wheel" ];
auto-optimise-store = lib.mkDefault true;
experimental-features = [ "nix-command" "flakes" "repl-flake" ];
diff --git a/hosts/common/optional/gnome.nix b/hosts/common/optional/gnome.nix
index bce4bbf..daf4dd3 100644
--- a/hosts/common/optional/gnome.nix
+++ b/hosts/common/optional/gnome.nix
@@ -14,23 +14,6 @@
layout = "us";
xkbVariant = "";
};
- logind.lidSwitch = "ignore";
- };
- security.polkit.extraConfig = ''
- polkit.addRule(function(action, subject) {
- if (action.id == "org.freedesktop.login1.suspend" ||
- action.id == "org.freedesktop.login1.suspend-multiple-sessions" ||
- action.id == "org.freedesktop.login1.hibernate" ||
- action.id == "org.freedesktop.login1.hibernate-multiple-sessions")
- {
- return polkit.Result.NO;
- }
- });
- '';
- systemd.targets = {
- sleep.enable = false;
- suspend.enable = false;
- hibernate.enable = false;
- hybrid-sleep.enable = false;
+ geoclue2.enable = true;
};
}
\ No newline at end of file
diff --git a/hosts/common/optional/x11-no-suspend.nix b/hosts/common/optional/x11-no-suspend.nix
new file mode 100644
index 0000000..0005eed
--- /dev/null
+++ b/hosts/common/optional/x11-no-suspend.nix
@@ -0,0 +1,7 @@
+{
+ services.xserver.serverFlagsSection = ''
+ Option "StandbyTime" "0"
+ Option "SuspendTime" "0"
+ Option "OffTime" "0"
+ '';
+}
diff --git a/hosts/common/users/john/default.nix b/hosts/common/users/john/default.nix
index 433fee9..7ea7c3f 100644
--- a/hosts/common/users/john/default.nix
+++ b/hosts/common/users/john/default.nix
@@ -24,4 +24,6 @@ in
home-manager.users.john = import ../../../../home/${config.networking.hostName}.nix;
+ services.geoclue2.enable = true;
+ security.pam.services = { swaylock = {}; };
}
\ No newline at end of file
diff --git a/hosts/pixnix/default.nix b/hosts/pixnix/default.nix
index bfa20e3..8bb4f8c 100644
--- a/hosts/pixnix/default.nix
+++ b/hosts/pixnix/default.nix
@@ -9,7 +9,7 @@
../common/users/john
../common/optional/docker.nix
- ../common/optional/gnome.nix
+ #../common/optional/gnome.nix
../common/optional/libvirtd.nix
../common/optional/pipewire.nix
../common/optional/printing.nix
@@ -21,6 +21,22 @@
networkmanager.enable = true;
};
+ programs = {
+ light.enable = true;
+ adb.enable = true;
+ dconf.enable = true;
+ };
+
+ servies.logind = {
+ lidSwitch = "suspend";
+ lidSwitchExternalPower = "lock";
+ };
+
+ xdg.portal = {
+ enable = true;
+ wlr.enable = true;
+ };
+
system.stateVersion = "23.05";
}
diff --git a/modules/home-manager/default.nix b/modules/home-manager/default.nix
index e41f7d9..1d8f911 100644
--- a/modules/home-manager/default.nix
+++ b/modules/home-manager/default.nix
@@ -4,4 +4,5 @@
{
# List your module files here
# my-module = import ./my-module.nix;
+ monitors = import ./monitors.nix;
}
diff --git a/modules/home-manager/monitors.nix b/modules/home-manager/monitors.nix
new file mode 100644
index 0000000..0dbdefc
--- /dev/null
+++ b/modules/home-manager/monitors.nix
@@ -0,0 +1,58 @@
+{ lib, config, ... }:
+
+let
+ inherit (lib) mkOption types;
+ cfg = config.monitors;
+in
+{
+ options.monitors = mkOption {
+ type = types.listOf (types.submodule {
+ options = {
+ name = mkOption {
+ type = types.str;
+ example = "DP-1";
+ };
+ primary = mkOption {
+ type = types.bool;
+ default = false;
+ };
+ width = mkOption {
+ type = types.int;
+ example = 1920;
+ };
+ height = mkOption {
+ type = types.int;
+ example = 1080;
+ };
+ refreshRate = mkOption {
+ type = types.int;
+ default = 60;
+ };
+ x = mkOption {
+ type = types.int;
+ default = 0;
+ };
+ y = mkOption {
+ type = types.int;
+ default = 0;
+ };
+ enabled = mkOption {
+ type = types.bool;
+ default = true;
+ };
+ workspace = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ };
+ };
+ });
+ default = [ ];
+ };
+ config = {
+ assertions = [{
+ assertion = ((lib.length config.monitors) != 0) ->
+ ((lib.length (lib.filter (m: m.primary) config.monitors)) == 1);
+ message = "Exactly one monitor must be set to primary.";
+ }];
+ };
+}