feat: install hyprland from liminalOS

This commit is contained in:
Kaito T Lex 2025-04-27 02:36:35 -07:00
parent 02701608a5
commit f1bf204d3c
24 changed files with 1486 additions and 538 deletions

View file

@ -0,0 +1,95 @@
{
lib,
pkgs,
config,
...
}:
let
cfg = config.liminalOS.desktop.hyprland;
hyprnome = "${pkgs.hyprnome}/bin/hyprnome";
in
{
wayland.windowManager.hyprland.settings = lib.mkIf cfg.enable {
bind = [
"$mod+Shift, $Left, scroller:movewindow, l"
"$mod+Shift, $Right, scroller:movewindow, r"
"$mod+Shift, $Up, scroller:movewindow, u"
"$mod+Shift, $Down, scroller:movewindow, d"
# Move around
"$mod, $Left, scroller:movefocus, l"
"$mod, $Right, scroller:movefocus, r"
"$mod, $Up, scroller:movefocus, u"
"$mod, $Down, scroller:movefocus, d"
"$mod, comma, scroller:admitwindow"
"$mod, period, scroller:expelwindow"
"$mod, F, scroller:fitsize, active"
"$mod, Y, scroller:fitsize, all"
"$mod, semicolon, scroller:cyclesize, next"
"$mod, apostrophe, scroller:cyclesize, previous"
"$mod+Shift, U, exec, ${hyprnome} --move"
"$mod+Shift, I, exec, ${hyprnome} --previous --move"
"$mod, U, exec, ${hyprnome}"
"$mod, I, exec, ${hyprnome} --previous"
"$mod, C, scroller:setmode, c"
"$mod, V, scroller:setmode, r"
"$mod, G, scroller:jump"
"$mod+Ctrl, G, scroller:toggleoverview"
# Window actions
"$mod, Q, killactive"
"$mod, W, togglefloating"
"$mod, Return, fullscreen"
# Utilities
"$mod, Space, exec, pkill -x rofi || rofi -show drun" # Run rofi application launcher
"$mod, X, exec, pkill -x rofi || rofi -show window" # Run rofi window switcher
"$mod, Backspace, exec, pkill -x wlogout || wlogout" # show logout menu
"$mod, Z, exec, loginctl lock-session"
# Media controls
",XF86AudioMute, exec, ${pkgs.pamixer}/bin/pamixer -t"
",XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl --player=%any,firefox play-pause"
",XF86AudioNext, exec, ${pkgs.playerctl}/bin/playerctl --player=%any,firefox next"
",XF86AudioRewind, exec, ${pkgs.playerctl}/bin/playerctl --player=%any,firefox previous"
"$mod, S, togglespecialworkspace"
"$mod+Alt, S, movetoworkspacesilent, special"
"$mod, Tab, workspace, previous"
''$mod+Shift, P, exec, ${pkgs.grim}/bin/grim - | ${pkgs.swappy}/bin/swappy -f -'' # Screenshot full screen
''$mod, P, exec, ${pkgs.grim}/bin/grim -g "$(${pkgs.slurp}/bin/slurp)" - | ${pkgs.swappy}/bin/swappy -f -'' # Screenshot
"$mod, B, exec, zen"
"$mod, N, exec, sleep 0.1 && ${pkgs.swaynotificationcenter}/bin/swaync-client -t -sw"
# Application Keybinds
"$mod, R, exec, ${pkgs.pavucontrol}/bin/pavucontrol -t 3" # open pavucontrol on 'outputs' tab
"$mod, T, exec, ${pkgs.kitty}/bin/kitty"
"$mod, E, exec, ${pkgs.xfce.thunar}/bin/thunar"
"$mod, M, exec, ${pkgs.thunderbird}/bin/thunderbird"
];
bindm = [
"$mod, mouse:272, movewindow"
"$mod, mouse:273, resizewindow"
];
bindel = [
",XF86MonBrightnessDown, exec, ${pkgs.brightnessctl}/bin/brightnessctl set 5%-"
",XF86MonBrightnessUp, exec, ${pkgs.brightnessctl}/bin/brightnessctl set 5%+"
",XF86AudioRaiseVolume, exec, ${pkgs.pamixer}/bin/pamixer -i 5"
",XF86AudioLowerVolume, exec, ${pkgs.pamixer}/bin/pamixer -d 5"
];
binde = [
# Resize windows
"$mod+Alt, $Right, resizeactive, 30 0"
"$mod+Alt, $Left, resizeactive, -30 0"
"$mod+Alt, $Up, resizeactive, 0 -30"
"$mod+Alt, $Down, resizeactive, 0 30"
];
};
}

View file

@ -0,0 +1,242 @@
{
pkgs,
config,
lib,
osConfig,
...
}:
let
cfg = config.liminalOS.desktop.hyprland;
in
{
imports = [
./binds.nix
./utilities.nix
./windowrules.nix
];
options.liminalOS.formFactor = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"laptop"
"desktop"
]
);
default = osConfig.liminalOS.formFactor;
description = ''
Form factor of the machine. Adjusts some UI settings.
'';
};
options.liminalOS.powersave = lib.mkOption {
type = lib.types.bool;
default = osConfig.liminalOS.powersave;
description = ''
Whether to set some options to reduce power consumption (mostly Hyprland).
'';
};
options.liminalOS.desktop.hyprland = {
enable = lib.mkOption {
type = lib.types.bool;
default = config.liminalOS.desktop.enable;
description = ''
Whether to enable and rice Hyprland as well as some basic desktop utilities.
'';
};
gtkUseOpenGL = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to set GSK_RENDERER environment variable to stop GTK apps from crashing.
'';
};
idleDaemon.enable = lib.mkOption {
type = lib.types.bool;
default = cfg.enable;
description = ''
Whether to setup and enable Hypridle with some defaults to automatically lock the screen and suspend after idling.
'';
};
screenlocker.enable = lib.mkOption {
type = lib.types.bool;
default = cfg.enable && cfg.idleDaemon.enable;
description = ''
Whether to set up Hyprlock for screen locking.
'';
};
screenlocker.useCrashFix = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to use a workaround for Hyprlock background blur not working on some machines. Before locking, a screenshot will be taken and placed at `/tmp/__hyprlock-monitor-screenshot.png`.
'';
};
screenlocker.monitor = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Monitor to use for screen locker. Use `hyprctl monitors` to determine.
'';
};
bluelight.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to enable `hyprsunset` as a daemon.
'';
};
};
config = lib.mkIf cfg.enable {
home.packages = with pkgs; [
wl-clipboard
libsForQt5.qtstyleplugin-kvantum
libsForQt5.qt5ct
papirus-icon-theme
libsForQt5.qt5ct
hyprland-qtutils
];
wayland.windowManager.hyprland = {
enable = true;
plugins = [
(pkgs.hyprlandPlugins.hyprscroller.overrideAttrs {
version = "0-unstable-2025-03-28";
src = pkgs.fetchFromGitHub {
owner = "dawsers";
repo = "hyprscroller";
rev = "3f86916f3e9a583154b1be0af4e8a1ef1f7435b2";
hash = "sha256-mgYq3vc4JtIzVuAKTWdALOynImYyNZEXh7tiVPvMZg4=";
};
})
];
settings = {
exec-once = [
"hyprctl dispatch workspace 100000"
];
"$mod" = "SUPER";
"$Left" = "H";
"$Right" = "L";
"$Up" = "K";
"$Down" = "J";
env = (
lib.optionals cfg.gtkUseOpenGL [
"GSK_RENDERER,ngl"
]
);
layerrule = [
"blur,rofi"
"ignorezero,rofi"
"animation slide bottom 0.2 0.2 wind,rofi"
"blur,notifications"
"ignorezero,notifications"
"blur,swaync-notification-window"
"animation slide right 0.5 0.5,swaync-control-center"
"animation slide right 0.5 0.5,notifications"
"animation slide right 0.5 0.5,swaync-notification-window"
"ignorezero,swaync-notification-window"
"blur,swaync-control-center"
"ignorezero,swaync-control-center"
"blur,logout_dialog"
];
dwindle = {
pseudotile = "yes";
preserve_split = "yes";
};
animations = {
enabled = "yes";
bezier = [
"wind, 0.05, 0.9, 0.1, 1.05"
"winIn, 0.1, 1.1, 0.1, 1.1"
"winOut, 0.3, -0.3, 0, 1"
"liner, 1, 1, 1, 1"
"windup, 0.05, 0.9, 0.1, 1.05"
];
animation =
[
"windows, 1, 6, wind, slide"
"windowsIn, 1, 6, winIn, slide"
"windowsOut, 1, 5, winOut, slide"
"windowsMove, 1, 5, wind, slide"
"fade, 1, 10, default"
# "layers, 1, 8, default, slide"
"workspaces, 1, 5, wind, slidefadevert"
]
++ (lib.optionals (!osConfig.liminalOS.powersave) [
"border, 1, 1, liner"
"borderangle, 1, 30, liner, loop"
]);
};
general =
let
inherit (config.lib.stylix) colors;
in
{
gaps_in = "3";
gaps_out = "8";
border_size = "2";
# "col.active_border" = pkgs.lib.mkForce "rgba(ca9ee6ff) rgba(f2d5cfff) 45deg";
# "col.inactive_border" = pkgs.lib.mkForce "rgba(b4befecc) rgba(6c7086cc) 45deg";
"col.active_border" = "rgba(${colors.base0A}ff) rgba(${colors.base09}ff) 45deg";
"col.inactive_border" = "rgba(${colors.base01}cc) rgba(${colors.base02}cc) 45deg";
layout = "scroller";
resize_on_border = "true";
};
misc = {
disable_hyprland_logo = true;
disable_splash_rendering = true;
};
cursor = {
hide_on_key_press = true;
};
decoration = {
rounding = "10";
dim_special = "0.3";
blur = {
enabled = "yes";
size = "6";
passes = "3";
new_optimizations = "on";
ignore_opacity = "on";
xray = "false";
special = true;
};
shadow = {
enabled = false;
};
};
input = {
sensitivity = if config.liminalOS.formFactor == "laptop" then "0.0" else "-0.65";
};
plugin.scroller = {
column_widths = "onethird onehalf twothirds one";
column_heights = "onethird onehalf twothirds one";
};
experimental.xx_color_management_v4 = true;
};
};
wayland.windowManager.hyprland.settings.input.touchpad =
lib.mkIf (config.liminalOS.formFactor == "laptop")
{
natural_scroll = true;
disable_while_typing = true;
clickfinger_behavior = true;
tap-to-click = false;
scroll_factor = 0.15;
};
assertions = [
{
assertion =
!cfg.screenlocker.useCrashFix || (cfg.screenlocker.useCrashFix && cfg.screenlocker.monitor != null);
message = "To use the Nvidia crash fix, you must set screenlocker.monitor to the monitor you want to use as the lock screen that blurs! Use `hyprctl monitors` to determine the monitor codes (should be something like DP-1, HDMI-A-1, etc).";
}
];
};
}

View file

@ -0,0 +1,183 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.liminalOS.desktop.hyprland;
in
{
config = lib.mkIf cfg.enable {
systemd.user.services = lib.mkIf cfg.bluelight.enable {
hyprsunset = {
Unit = {
Description = "Start the hyprsunset daemon";
PartOf = "hyprland-session.target";
After = "hyprland-session.target";
};
Service = {
Type = "simple";
ExecStart = "${pkgs.hyprsunset}/bin/hyprsunset";
Restart = "on-failure";
RestartSec = 3;
};
Install = {
WantedBy = [ "hyprland-session.target" ];
};
};
};
services.hyprpaper.enable = true;
programs.wlogout.enable = true;
programs.rofi = {
enable = true;
package = pkgs.rofi-wayland;
terminal = "${pkgs.kitty}/bin/kitty";
theme =
let
inherit (config.lib.formats.rasi) mkLiteral;
mkRgba =
opacity: color:
let
c = config.lib.stylix.colors;
r = c."${color}-rgb-r";
g = c."${color}-rgb-g";
b = c."${color}-rgb-b";
in
mkLiteral "rgba ( ${r}, ${g}, ${b}, ${opacity} % )";
mkRgb = mkRgba "100";
rofiOpacity = builtins.toString (builtins.ceil (config.stylix.opacity.popups * 100));
in
{
"*" = {
font = "${config.stylix.fonts.monospace.name} ${toString config.stylix.fonts.sizes.popups}";
text-color = mkRgb "base05";
background-color = mkRgba rofiOpacity "base00";
};
"window" = {
height = mkLiteral "20em";
width = mkLiteral "30em";
border-radius = mkLiteral "8px";
border-width = mkLiteral "2px";
padding = mkLiteral "1.5em";
};
"mainbox" = {
background-color = mkRgba rofiOpacity "base01";
};
"inputbar" = {
margin = mkLiteral "0 0 1em 0";
};
"prompt" = {
enabled = false;
};
"entry" = {
placeholder = "Search...";
padding = mkLiteral "1em 1em";
text-color = mkRgb "base05";
background-color = mkRgba rofiOpacity "base00";
border-radius = mkLiteral "8px";
};
"element-text" = {
padding = mkLiteral "0.5em 1em";
margin = mkLiteral "0 0.5em";
};
"element-icon" = {
size = mkLiteral "3ch";
};
"element-text selected" = {
background-color = mkRgba rofiOpacity "base0A";
text-color = mkRgb "base01";
border-radius = mkLiteral "8px";
};
};
};
services.swayosd.enable = true;
programs.hyprlock = lib.mkIf cfg.screenlocker.enable {
enable = true;
settings = {
general = {
hide_cursor = true;
grace = 0;
};
background = {
monitor = cfg.screenlocker.monitor;
path =
if cfg.screenlocker.useCrashFix then "/tmp/__hyprlock-monitor-screenshot.png" else "screenshot";
blur_passes = 3;
blur_size = 7;
noise = 0.0117;
contrast = 0.8916;
brightness = 0.8172;
vibrancy = 0.1696;
vibrancy_darkness = 0.0;
};
input-field = {
monitor = "";
size = "200, 50";
outline_thickness = 3;
dots_size = 0.33;
dots_spacing = 0.15;
dots_center = false;
dots_rounding = -1;
outer_color = "rgb(151515)";
inner_color = "rgb(200, 200, 200)";
font_color = "rgb(10, 10, 10)";
fade_on_empty = true;
fade_timeout = 1000;
placeholder_text = "<i>Input Password...</i>";
hide_input = false;
rounding = -1;
check_color = "rgb(204, 136, 34)";
fail_color = "rgb(204, 34, 34)";
fail_text = "<i>$FAIL <b>($ATTEMPTS)</b></i>";
fail_timeout = 2000;
fail_transition = 300;
capslock_color = -1;
numlock_color = -1;
bothlock_color = -1;
invert_numlock = false;
swap_font_color = false;
position = "0, -20";
halign = "center";
valign = "center";
};
};
};
services.hypridle = lib.mkIf cfg.idleDaemon.enable {
enable = true;
settings = {
general = {
lock_cmd =
if cfg.screenlocker.useCrashFix then
"pidof hyprlock || ${pkgs.grim}/bin/grim -o ${config.programs.hyprlock.settings.background.monitor} /tmp/__hyprlock-monitor-screenshot.png && ${pkgs.hyprlock}/bin/hyprlock"
else
"pidof hyprlock || hyprlock";
before_sleep_cmd = "loginctl lock-session"; # lock before suspend.
after_sleep_cmd = "hyprctl dispatch dpms on"; # to avoid having to press a key twice to turn on the display.
};
listener = [
{
timeout = 1500;
on-timeout = "loginctl lock-session";
}
{
timeout = 330; # 5.5min
on-timeout = "hyprctl dispatch dpms off"; # screen off when timeout has passed
on-resume = "hyprctl dispatch dpms on"; # screen on when activity is detected after timeout has fired.
}
{
timeout = 1800;
on-timeout = "systemctl suspend";
}
];
};
};
};
}

View file

@ -0,0 +1,95 @@
{ config, lib, ... }:
{
config.wayland.windowManager.hyprland.settings.windowrulev2 =
lib.mkIf config.liminalOS.desktop.hyprland.enable
[
"opacity 0.90 0.90,class:^(librewolf)$"
"opacity 0.90 0.90,class:^(floorp)$"
"opacity 0.90 0.90,class:^(zen-alpha)$"
"opacity 0.90 0.90,class:^(zen-beta)$"
"opacity 0.90 0.90,class:^(zen)$"
"opacity 0.90 0.90,class:^(Brave-browser)$"
"opacity 0.80 0.80,class:^(Steam)$"
"opacity 0.80 0.80,class:^(steam)$"
"opacity 0.80 0.80,class:^(steamwebhelper)$"
"opacity 0.80 0.80,class:^(Spotify)$"
"opacity 0.80 0.80,initialTitle:^(Spotify Premium)$"
"opacity 0.80 0.80,initialTitle:^(Spotify Free)$"
"opacity 0.80 0.80,class:^(code-oss)$"
"opacity 0.80 0.80,class:^(Code)$"
"opacity 0.80 0.80,class:^(code-url-handler)$"
"opacity 0.80 0.80,class:^(code-insiders-url-handler)$"
"opacity 0.80 0.80,class:^(kitty)$"
"opacity 0.80 0.80,class:^(neovide)$"
"opacity 0.80 0.80,class:^(org.kde.dolphin)$"
"opacity 0.80 0.80,class:^(thunar)$"
"opacity 0.80 0.80,class:^(org.kde.ark)$"
"opacity 0.80 0.80,class:^(nwg-look)$"
"opacity 0.80 0.80,class:^(qt5ct)$"
"opacity 0.80 0.80,class:^(qt6ct)$"
"opacity 0.80 0.80,class:^(kvantummanager)$"
"opacity 0.80 0.80,class:^(waypaper)$"
"opacity 0.80 0.80,class:^(org.pulseaudio.pavucontrol)$"
"opacity 0.80 0.80,class:^(com.github.wwmm.easyeffects)$"
"opacity 0.80 0.80,class:^(thunderbird)$"
"opacity 0.90 0.90,class:^(com.github.rafostar.Clapper)$ # Clapper-Gtk"
"opacity 0.80 0.80,class:^(com.github.tchx84.Flatseal)$ # Flatseal-Gtk"
"opacity 0.80 0.80,class:^(hu.kramo.Cartridges)$ # Cartridges-Gtk"
"opacity 0.80 0.80,class:^(com.obsproject.Studio)$ # Obs-Qt"
"opacity 0.80 0.80,class:^(gnome-boxes)$ # Boxes-Gtk"
"opacity 0.80 0.80,class:^(discord)$ # Discord-Electron"
"opacity 0.80 0.80,class:^(vesktop)$ # Vesktop-Electron"
"opacity 0.80 0.80,class:^(ArmCord)$ # ArmCord-Electron"
"opacity 0.80 0.80,class:^(app.drey.Warp)$ # Warp-Gtk"
"opacity 0.80 0.80,class:^(net.davidotek.pupgui2)$ # ProtonUp-Qt"
"opacity 0.80 0.80,class:^(yad)$ # Protontricks-Gtk"
"opacity 0.80 0.80,class:^(signal)$ # Signal-Gtk"
"opacity 0.80 0.80,class:^(io.github.alainm23.planify)$ # planify-Gtk"
"opacity 0.80 0.80,class:^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk"
"opacity 0.80 0.80,class:^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gtk"
"opacity 0.80 0.80,class:^(lutris)$ # Lutris game launcher"
"opacity 0.80 0.70,class:^(pavucontrol)$"
"opacity 0.80 0.70,class:^(blueman-manager)$"
"opacity 0.80 0.70,class:^(nm-applet)$"
"opacity 0.80 0.70,class:^(nm-connection-editor)$"
"opacity 0.80 0.70,class:^(org.kde.polkit-kde-authentication-agent-1)$"
"float,class:^(org.kde.dolphin)$,title:^(Progress Dialog Dolphin)$"
"float,class:^(org.kde.dolphin)$,title:^(Copying Dolphin)$"
"float,title:^(Picture-in-Picture)$"
"float,class:^(librewolf)$,title:^(Library)$"
"float,class:^(floorp)$,title:^(Library)$"
"float,class:^(zen-alpha)$,title:^(Library)$"
"float,class:^(zen-beta)$,title:^(Library)$"
"float,class:^(zen)$,title:^(Library)$"
''float,class:^(zen)$,title:^(.*Extension: \(Bitwarden Password Manager\).*)$''
"float,class:^(vlc)$"
"float,class:^(kvantummanager)$"
"float,class:^(qt5ct)$"
"float,class:^(qt6ct)$"
"float,class:^(nwg-look)$"
"float,class:^(org.kde.ark)$"
"float,class:^(org.pulseaudio.pavucontrol)$"
"float,class:^(com.github.rafostar.Clapper)$ # Clapper-Gtk"
"float,class:^(app.drey.Warp)$ # Warp-Gtk"
"float,class:^(net.davidotek.pupgui2)$ # ProtonUp-Qt"
"float,class:^(yad)$ # Protontricks-Gtk"
"float,class:^(eog)$ # Imageviewer-Gtk"
"float,class:^(io.github.alainm23.planify)$ # planify-Gtk"
"float,class:^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk"
"float,class:^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gkk"
"float,class:^(blueman-manager)$"
"float,class:^(nm-applet)$"
"float,class:^(nm-connection-editor)$"
"float,class:^(org.kde.polkit-kde-authentication-agent-1)$"
"opacity 0.80 0.80,class:^(org.freedesktop.impl.portal.desktop.gtk)$"
"opacity 0.80 0.80,class:^(org.freedesktop.impl.portal.desktop.hyprland)$"
''size 70% 70%,class:^(zen)$,title:^(.*Extension: \(Bitwarden Password Manager\).*)$''
"size 50% 50%,class:^(org.pulseaudio.pavucontrol)"
"stayfocused, class:^(pinentry-)" # fix pinentry losing focus
];
}