Linux - Hyprland

Table of Contents

Introduction

Hyprland is a dynamic tiling compositor based on wlroot. Compared to sway, hyprland provides better experience and easy configuration. What is exciting, its development is much active.

Hyprland's core components and necessary applications can be simply installed and configured, e.g.,

  • hyprland: core components
  • foot: a native terminal in wayland
  • fuzzel: an application launcher
  • light: a backlight controller
  • swaync: a notification utility
  • grim: a screenshot utility
  • slurp: a region selection utility in wayland
  • swaybg: a background setting utility
  • swaylock: a utility to lock screen
  • wdisplays: an arandr counterpart in wayland
  • waybar: a bar utility in wayland
  • ttf-font-awesome: a font to display icons in waybar
  • xdg-desktop-portal-hyprland: XDG desktop handler enabling D-Bus based communication for other applications
  • qt5-wayland/qt6-wayland: Wayland support for QT

Installation

pacman -S hyprland waybar foot fuzzel swaybg swaylock light grim slurp ttf-font-awesome qt5-wayland qt6-wayland blueman network-manager-applet pipewire wireplumber xdg-desktop-portal-hyprland-git

Configuration

By default, all the configuration files are stored in directory ~/.config/hypr.

  • ~/.config/hypr/hyprland.conf

    ########################################################################################
    AUTOGENERATED HYPR CONFIG.
    PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hypr.conf AND EDIT IT,
    OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS.
    ########################################################################################
    
    #
    # Please note not all available settings / options are set here.
    # For a full list, see the wiki
    #
    
    # autogenerated = 1 # remove this line to remove the warning
    
    # See https://wiki.hyprland.org/Configuring/Monitors/
    # monitor=name,resolution,position,scale
    monitor=eDP-1,disable
    monitor=DP-1,preferred,auto,2
    
    # See https://wiki.hyprland.org/Configuring/Keywords/ for more
    
    # Execute your favorite apps at launch
    exec-once = waybar --config ~/.config/hypr/waybar/config --style ~/.config/hypr/waybar/style.css
    exec-once = swaybg -i /usr/share/backgrounds/archlinux/small.png
    exec-once = swaync --config ~/.config/hypr/swaync.json
    exec-once = fcitx5 -D
    exec-once = nm-applet
    exec-once = blueman-applet
    exec-once = /usr/lib/polkit-kde-authentication-agent-1
    
    # Source a file (multi-file configs)
    source = ~/.config/hypr/bind.conf
    source = ~/.config/hypr/env.conf
    
    # Window rules
    windowrulev2 = opacity 0.3, floating:1, class:foot
    
    misc {
        disable_hyprland_logo = true
        disable_splash_rendering = true
        focus_on_activate = true
    }
    
    # For all categories, see https://wiki.hyprland.org/Configuring/Variables/
    input {
        kb_layout = us
        kb_variant =
        kb_model =
        kb_options = caps:ctrl_modifier
        kb_rules =
        repeat_rate = 60
        repeat_delay = 300
        follow_mouse = 1
        touchpad {
            natural_scroll = yes
            disable_while_typing = true
        }
        sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
    }
    
    general {
        # See https://wiki.hyprland.org/Configuring/Variables/ for more
        gaps_in = 1
        gaps_out = 1
        border_size = 3
        col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
        col.inactive_border = rgba(595959aa)
        layout = dwindle
    }
    
    decoration {
        # See https://wiki.hyprland.org/Configuring/Variables/ for more
        rounding = 9
        drop_shadow = yes
        shadow_range = 3
        shadow_render_power = 3
        col.shadow = rgba(1a1a1aee)
    }
    
    animations {
        enabled = yes
        # Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
        bezier = myBezier, 0.05, 0.9, 0.1, 1.05
        animation = windows, 1, 8, default
        animation = windowsOut, 1, 8, default, slide
        animation = border, 1, 10, default
        animation = borderangle, 1, 8, default
        animation = fade, 1, 8, default
        animation = workspaces, 1, 8, default, fade
    }
    
    dwindle {
        # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
        pseudotile = yes # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
        preserve_split = yes # you probably want this
    }
    
    master {
        # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
        new_is_master = true
    }
    
    gestures {
        # See https://wiki.hyprland.org/Configuring/Variables/ for more
        workspace_swipe = off
    }
    
    # Example per-device config
    # See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
    device:epic-mouse-v1 {
        sensitivity = -0.5
    }
    
  • ~/.config/hypr/bind.conf

    # See https://wiki.hyprland.org/Configuring/Keywords/ for more
    $mainMod = SUPER
    
    # Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
    bind = $mainMod, Q, killactive
    bind = $mainMod SHIFT, SPACE, togglefloating
    bind = $mainMod, E, togglesplit # dwindle
    bind = $mainMod, T, toggleopaque
    bind = $mainMod, F, fullscreen
    
    bind = $mainMod, Return, exec, foot --config ~/.config/hypr/foot.ini
    bind = $mainMod, D, exec, fuzzel --config ~/.config/hypr/fuzzel.ini
    bind = , Print, exec, grim $(date +'%Y%m%d-%H%M%S.png')
    bind = SHIFT, Print, exec, grim -g "$(slurp)" $(date +'%Y%m%d-%H%M%S.png')
    
    # Move focus
    bind = $mainMod, O, cyclenext
    bind = $mainMod, left, movefocus, l
    bind = $mainMod, right, movefocus, r
    bind = $mainMod, up, movefocus, u
    bind = $mainMod, down, movefocus, d
    
    # Switch workspaces
    bind = $mainMod, 1, workspace, 1
    bind = $mainMod, 2, workspace, 2
    bind = $mainMod, 3, workspace, 3
    bind = $mainMod, 4, workspace, 4
    bind = $mainMod, 5, workspace, 5
    bind = $mainMod, 6, workspace, 6
    bind = $mainMod, 7, workspace, 7
    bind = $mainMod, 8, workspace, 8
    bind = $mainMod, 9, workspace, 9
    
    # Move active window to a workspace
    bind = $mainMod SHIFT, 1, movetoworkspacesilent, 1
    bind = $mainMod SHIFT, 2, movetoworkspacesilent, 2
    bind = $mainMod SHIFT, 3, movetoworkspacesilent, 3
    bind = $mainMod SHIFT, 4, movetoworkspacesilent, 4
    bind = $mainMod SHIFT, 5, movetoworkspacesilent, 5
    bind = $mainMod SHIFT, 6, movetoworkspacesilent, 6
    bind = $mainMod SHIFT, 7, movetoworkspacesilent, 7
    bind = $mainMod SHIFT, 8, movetoworkspacesilent, 8
    bind = $mainMod SHIFT, 9, movetoworkspacesilent, 9
    
    # Move/resize windows
    bind = $mainMod SHIFT, R, submap, resize_window
    submap = resize_window
    binde = , right, resizeactive, 10 0
    binde = , left, resizeactive, -10 0
    binde = , up, resizeactive, 0 -10
    binde = , down, resizeactive, 0 10
    bind = , escape, submap, reset
    submap = reset
    bindm = $mainMod, mouse:272, movewindow
    bindm = $mainMod, mouse:273, resizewindow
    
    # Adjust volume
    bindle = , XF86AudioRaiseVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ +5%
    bindle = , XF86AudioLowerVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ -5%
    bindle = , XF86AudioMute, exec, pactl set-sink-mute @DEFAULT_SINK@ toggle
    bindle = , XF86AudioMicMute, exec, pactl set-source-mute @DEFAULT_SOURCE@ toggle
    
    # Adjust Brightness
    bindle = , XF86MonBrightnessUp, exec, sudo light -A 5
    bindle = , XF86MonBrightnessDown, exec, sudo light -U 5
    
    # System submap: (E)xit, (H)alt, (R)eboot.
    bind = $mainMod SHIFT, E, submap, system
    submap = system
    bind = , E, exit
    bind = , L, exec, swaylock -i /usr/share/backgrounds/archlinux/small.png
    bind = , H, exec, systemctl poweroff
    bind = , R, exec, systemctl reboot
    bind = , escape, submap, reset
    submap = reset
    
  • ~/.config/hypr/env.conf

    env = LANG,en_US.UTF-8
    env = XCURSOR_SIZE,32
    env = GTK_IM_MODULE,fcitx
    env = QT_IM_MODULE,fcitx
    env = XMODIFIERS,@im=fcitx
    env = QT_QPA_PLATFORM,wayland
    env = MOZ_ENABLE_WAYLAND,1
    env = XAPIAN_CJK_NGRAM,1
    env = EDITOR,/usr/bin/emacsclient -a "" -t
    env = LIBSEAT_BACKEND,logind
    
  • ~/.config/hypr/foot.ini

    [main]
    font=JetBrains Mono:size=18
    
  • ~/.config/hypr/fuzzel.ini

    font=JetBrains Mono
    dpi-aware=auto
    prompt=" "
    icon-theme=Arc
    icons-enabled=yes
    fuzzy=yes
    show-actions=no
    lines=9
    width=60
    vertical-pad=0
    line-height=32
    
    [colors]
    background=fbf1c7ff
    text=32302fff
    match=ae6962ff
    selection=d5c4a1ff
    selection-text=282828ff
    
    [border]
    width=0
    radius=12
    
  • ~/.config/hypr/swaync.json

    {
      "$schema": "/etc/xdg/swaync/configSchema.json",
      "positionX": "right",
      "positionY": "top",
      "layer": "overlay",
      "control-center-layer": "top",
      "layer-shell": true,
      "cssPriority": "application",
      "control-center-margin-top": 0,
      "control-center-margin-bottom": 0,
      "control-center-margin-right": 0,
      "control-center-margin-left": 0,
      "notification-2fa-action": true,
      "notification-inline-replies": false,
      "notification-icon-size": 64,
      "notification-body-image-height": 100,
      "notification-body-image-width": 200,
      "timeout": 10,
      "timeout-low": 5,
      "timeout-critical": 0,
      "fit-to-screen": true,
      "control-center-width": 500,
      "control-center-height": 600,
      "notification-window-width": 300,
      "keyboard-shortcuts": true,
      "image-visibility": "when-available",
      "transition-time": 200,
      "hide-on-clear": false,
      "hide-on-action": true,
      "script-fail-notify": true,
      "scripts": {
        "example-script": {
          "exec": "echo 'Do something...'",
          "urgency": "Normal"
        },
        "example-action-script": {
          "exec": "echo 'Do something actionable!'",
          "urgency": "Normal",
          "run-on": "action"
        }
      },
      "notification-visibility": {
        "example-name": {
          "state": "muted",
          "urgency": "Low",
          "app-name": "Spotify"
        }
      },
      "widgets": [
        "inhibitors",
        "title",
        "dnd",
        "notifications"
      ],
      "widget-config": {
        "inhibitors": {
          "text": "Inhibitors",
          "button-text": "Clear All",
          "clear-all-button": true
        },
        "title": {
          "text": "Notifications",
          "clear-all-button": true,
          "button-text": "Clear All"
        },
        "dnd": {
          "text": "Do Not Disturb"
        },
        "label": {
          "max-lines": 5,
          "text": "Label Text"
        },
        "mpris": {
          "image-size": 96,
          "image-radius": 12
        }
      }
    }
    
  • ~/.config/hypr/waybar/config

    {
        "layer": "top",
        "modules-left": ["hyprland/workspaces"],
        "modules-center": [],
        "modules-right": ["cpu", "memory", "temperature", "backlight", "battery", "pulseaudio", "clock", "tray", "notification"],
        // Modules configuration
        "hyprland/workspaces": {
            "format": "{name}{icon}",
            "on-click": "activate",
            "sort-by-number": true
        },
        "tray": {
            "spacing": 10,
            "icon-size": 30
        },
        "clock": {
            "format": "{: %Y-%m-%d %a %H:%M}",
            "tooltip": false
        },
        "cpu": {
            "format": "{usage}% ",
            "tooltip": false
        },
        "memory": {
            "format": "{}% ",
            "tooltip": true
        },
        "temperature": {
            "critical-threshold": 80,
            "format": "{temperatureC}°C {icon}",
            "format-icons": ["", "", ""]
        },
        "backlight": {
            "format": "{percent}% {icon} ",
            // "format-icons": ["", ""]
            "format-icons": ["", "", "", "", "", "", "", "", ""]
        },
        "battery": {
            "states": {
                // "good": 95,
                "warning": 30,
                "critical": 15
            },
            "format": "{capacity}% {icon} ",
            "format-charging": "{capacity}% ",
            "format-plugged": "{capacity}% ",
            "format-alt": "{time} {icon}",
            "tooltip": false,
            "format-icons": ["", "", "", "", ""]
        },
        "network": {
            "format-wifi": "{essid} ({signalStrength}%)  ",
            "format-ethernet": "{ifname}: {ipaddr}/{cidr}  ",
            "format-linked": "{ifname} (No IP)  ",
            "format-disconnected": "Disconnected ⚠ ",
            "format-alt": "{ifname}: {ipaddr}/{cidr}",
            "tooltip": false
        },
        "pulseaudio": {
            "scroll-step": 10, // %, can be a float
            "format": "{volume}% {icon} {format_source}",
            "format-bluetooth": "{volume}% {icon} {format_source}",
            "format-muted": "{volume}%  {format_source}",
            "format-source": "{volume}% ",
            "format-source-muted": "{volume}% ",
            "format-icons": {
                "headphones": "",
                "handsfree": "",
                "headset": "",
                "phone": "",
                "portable": "",
                "car": "",
                "default": ["", "", ""]
            },
            "on-click": "pavucontrol"
        },
        "notification": {
            "tooltip": false,
            "format": "{icon}",
            "format‐icons": {
                 "notification": "<U+F0A2><span foreground=’red’><sup><U+F444></sup></span>",
                 "none": "<U+F0A2>",
                 "dnd‐notification": "<U+F1F7><span foreground=’red’><sup><U+F444></sup></span>",
                 "dnd‐none": "<U+F1F7>",
                 "inhibited‐notification": "<U+F0A2><span foreground=’red’><sup><U+F444></sup></span>",
                 "inhibited‐none": "<U+F0A2>",
                 "dnd‐inhibited‐notification": "<U+F1F7><span foreground=’red’><sup><U+F444></sup></span>",
                 "dnd‐inhibited‐none": "<U+F1F7>"
            },
            "return‐type": "json",
            "exec‐if": "which swaync‐client",
            "exec": "swaync‐client ‐swb",
            "on‐click": "swaync‐client ‐t ‐sw",
            "on‐click‐right": "swaync‐client ‐d ‐sw",
            "escape": true
        }
    }
    
  • ~/.config/hypr/waybar/style.css

    * {
        border: none;
        border-radius: 0;
        font-family: JetBrains Mono;
        font-size: 16px;
        min-height: 0;
    }
    
    window#waybar {
        background-color: transparent;
    }
    
    window#waybar.hidden {
        opacity: 0.3;
    }
    
    /*
    window#waybar.empty {
        background-color: transparent;
    }
    window#waybar.solo {
        background-color: #FFFFFF;
    }
    */
    
    window#waybar.termite {
        background-color: #3F3F3F;
    }
    
    window#waybar.chromium {
        background-color: #000000;
        border: none;
    }
    
    #workspaces button {
        padding: 0 5px;
        background-color: transparent;
        color: #ffffff;
        border-bottom: 3px solid transparent;
    }
    
    /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
    #workspaces button:hover {
        background: rgba(0, 0, 0, 0.2);
        box-shadow: inherit;
        border-bottom: 3px solid #ffffff;
    }
    
    #workspaces button.active {
        background-color: #64727D;
        border-bottom: 3px solid #ffffff;
    }
    
    #workspaces button.urgent {
        background-color: #eb4d4b;
    }
    
    #mode {
        background-color: #64727D;
        border-bottom: 3px solid #ffffff;
    }
    
    #clock,
    #battery,
    #cpu,
    #memory,
    #temperature,
    #backlight,
    #network,
    #pulseaudio,
    #custom-media,
    #tray,
    #mode,
    #idle_inhibitor {
        padding: 0 10px;
        margin: 0 0px;
        color: #ffffff;
    }
    
    #clock {
        background-color: #64727D;
    }
    
    #battery {
        background-color: #ffffff;
        color: #000000;
    }
    
    #battery.charging {
        color: #ffffff;
        background-color: #26A65B;
    }
    
    @keyframes blink {
        to {
            background-color: #ffffff;
            color: #000000;
        }
    }
    
    #battery.critical:not(.charging) {
        background-color: #f53c3c;
        color: #ffffff;
        animation-name: blink;
        animation-duration: 0.5s;
        animation-timing-function: linear;
        animation-iteration-count: infinite;
        animation-direction: alternate;
    }
    
    label:focus {
        background-color: #000000;
    }
    
    #cpu {
        background-color: #2ecc71;
        color: #000000;
    }
    
    #memory {
        background-color: #9b59b6;
    }
    
    #backlight {
        background-color: #90b1b1;
    }
    
    #network {
        background-color: #2980b9;
    }
    
    #network.disconnected {
        background-color: #f53c3c;
    }
    
    #pulseaudio {
        background-color: #f1c40f;
        color: #000000;
    }
    
    #pulseaudio.muted {
        background-color: #90b1b1;
        color: #2a5c45;
    }
    
    #custom-media {
        background-color: #66cc99;
        color: #2a5c45;
        min-width: 100px;
    }
    
    #custom-media.custom-spotify {
        background-color: #66cc99;
    }
    
    #custom-media.custom-vlc {
        background-color: #ffa000;
    }
    
    #temperature {
        background-color: #f0932b;
    }
    
    #temperature.critical {
        background-color: #eb4d4b;
    }
    
    #tray {
        background-color: #2980b9;
    }
    
    #idle_inhibitor {
        background-color: #2d3436;
    }
    
    #idle_inhibitor.activated {
        background-color: #ecf0f1;
        color: #2d3436;
    }
    
    #mpd {
        background-color: #66cc99;
        color: #2a5c45;
    }
    
    #mpd.disconnected {
        background-color: #f53c3c;
    }
    
    #mpd.stopped {
        background-color: #90b1b1;
    }
    
    #mpd.paused {
        background-color: #51a37a;
    }
    

Startup

After the installation and configuration, hyprland can be started by running command Hyprland from a TTY after login.