Skip to content

0dpe/Linux-Journey

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 

Repository files navigation

Linux Journey

Started on Jun 20, 2024. Still updating.

Choosing

Linux Distribution

Distro Package Manager Minimal Comments
PeppermintOS Debian based Yes Small community; only xfce supported?
antiX Linux Debian based Extremely Looks old
Tiny Core Linux Extremely Advanced
Void Linux XBPS (independent) No
Arch Linux Pacman (AUR - independent) Maybe Great documentation
✔️ NixOS Nix (independent) No Has the most packages; clean config

Display Server & Window Manager

X11 will be replaced by Wayland.

Compositing
Window Manager
Minimal
Eyecandy
Comments
Qtile Neither Written in Python
Sway Neither
dwl Minimal dwm fork
SwayFX Eyecandy Inferior to Hyprland?
river Minimal Lacking wiki
✔️ Hyprland Eyecandy Great wiki

A command shell converts human commands to something the kernel understands. Use $ echo $SHELL to see the current command shell. NixOS by default uses Bourne-Again shell (bash). Popular modern command shells:

  • Friendly Interactive Shell (fish)
  • ✔️ Z Shell (zsh)

Terminal Emulator

Terminals provide access to the command shell. The TTY (TeleTYpe informally, virtual terminal formally, framebuffer terminal functionally; switch between TTYs using ctrl+alt+F#) does not support copy and paste, panels, scrolling, multitasking with multiple windows, etc... Therefore, a non-framebuffer terminal emulator is useful for using commands without having to switch out of the graphical environment.

  • Alacritty: A cross-platform, OpenGL terminal emulator.
  • Foot: A fast, lightweight and minimalistic Wayland terminal emulator.
  • Terminator: Multiple GNOME terminals in one window!
  • WezTerm: A GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust.
  • ✔️ kitty: The fast, feature-rich, GPU based terminal emulator.

Not included in Home Manager option documentation:

  • Konsole: A powerful and customizable terminal emulator made by KDE.
  • MLTERM: Multi Lingual TERMinal emulator.
  • st: A simple terminal implementation for X.
  • Warp: The terminal reimagined with AI and collaborative tools for better productivity.
  • Contour: A modern & actually fast Terminal Emulator.
  • Darktile: A GPU rendered terminal emulator designed for tiling window managers.

Not in nixpkgs:

  • Extraterm: The swiss army chainsaw of terminal emulators.
  • Tabby: A terminal for the modern age.
  • Blink: Chromium
  • QtWebEngine (Chromium): Qutebrowser
  • ✔️ Gecko: Firefox

Other web engines have outdated browsers.
Text based browsers are interesting but impractical.
Forks of Firefox or Chromium are generally inferior. Edge, Brave, and Opera are modern but bloated.

Cursor Theme

Numix, Gruppled, Graphite, Afterglow (recolored), McMojave, Breeze Hacked, Comix, Bibata, Google inspired, Phinger, Quintom, Whitesur, Vimix, Catppuccin, Vanilla DMZ, OpenZone, Hackneyed, Borealis, Nordzy, Simp1e, Volantes, ✔️ Capitaine, ...

Wallpaper Manager

Wayland wallpaper managers (all in nixpkgs):

Manager Animated Transitions Home
Manager
swaybg ? ? No
mpvpaper MPV ? No
Wallutils No Yes No
wbg No No No
hyprpaper No No Yes
wpaperd No Cross Fade Yes
✔️ swww GIF Yes No (CLI)

Status bar

Wayland status bars (all in nixpkgs):

  • eww: ElKowars wacky widgets (extreme, complicated customizability with its own language)
  • ags: Scaffolding CLI for Astal+TypeScript (similar to eww but with TypeScript)
  • Yambar: Modular status panel for X11 and Wayland (similar to Waybar but not as popular and not as many modules; does not support Hyprland by default)
  • xmobar: A minimalist status bar (originally designed to work with xmonad; Wayland support was an afterthought)
  • ✔️ Waybar: Highly customizable Wayland bar for Sway and Wlroots based compositors.

Not included in Home Manager option documentation:

  • HyprPanel: A Bar/Panel for Hyprland with extensive customizability. (only avaible through flake)
  • Luastatus: universal status bar content generator
  • Fabric: The next-generation framework for building desktop widgets using Python (only available through flake)
  • Ironbar: Customisable Wayland gtk bar written in Rust.
  • gBar: Blazingly fast status bar written with GTK
  • Root Bar: Root Bar is a bar for wlroots based wayland compositors such as sway and was designed to address the lack of good bars for wayland.
  • sfwbar: S* Floating Window Bar
  • nwg-panel: GTK3-based panel for sway and Hyprland Wayland compositors
  • ashell: A ready to go Wayland status bar for Hyprland (only available through flake)
  • Quickshell: Flexible toolkit for making desktop shells with QtQuick, targeting Wayland and X11 (only available through flake)

Installing NixOS

Installing NixOS minimal (no GNOME or KDE) on an HP ZHAN 66 Pro 14 G2:

  1. Download NixOS minimal ISO image.

  2. Use balenaEtcher to flash the .iso to a USB.

  3. Plug the USB into the laptop and turn on the laptop.
    Spam esc while booting to open the HP startup menu.
    Disable secure boot.
    Select boot from USB in Boot Menu (F9).

  4. Follow manual: Networking (use following commands).
    # systemctl start wpa_supplicant
    # wpa_cli
    > add_network (0 should appear)
    > set_network 0 ssid "CU_FS6D" (network called CU_FS6D)
    > set_network 0 psk "redacted" (using password redacted for CU_FS6D)
    > set_network 0 key_mgmt WPA-PSK
    > enable_network 0
    If something like <3>CTRL-EVENT-CONNECTED - Connection to 32:85:ab:ef:24:5c completed [id=0 id_str=] appears, then the laptop has connection to the internet. Leave wpa_cli using > quit.
    Note: Also use # ping www.baidu.com to test the internet connection. Use ctrl+c to quit ping.

  5. Follow manual: Partitioning.
    Turns out my laptop only has a single SSD drive, which means I have to install Linux and keep Windows on the same drive; I have to dual boot Windows 10 and Linux if I want to keep Windows. I could also install Linux on a USB, but that seems iffy.
    I ditch this installation and boot into Windows to create a partition for Linux on the same drive as Windows.
    Note: To launch the Windows 10 partitioning software, search in Windows "创建并格式换硬盘分区".
    I set up partitions. Windows displays that my drive has 476.92GB total (Why is it not 512GB?).

    • Before:
      1. 260MB: EFI系统分区
      2. Windows (C:)
      3. 930MB: 恢复分区
    • After:
      1. 260MB: EFI系统分区
      2. 243.82GB: Windows (C:)
      3. 231.93GB: 未分配
      4. 930MB: 恢复分区
  6. Boot into Linux and follow manual to partitioning again.
    /dev/sda is my USB; /dev/nvme0n1 is my SSD drive. Using # parted /dev/nvme0n1 print only shows non-free partitions, which are all numbered and named. Using # parted /dev/nvme0n1 print free shows free or 未分配 partitions:

    Model: INTEL SSDPEKNW512G8H (nvme)
    Disk /dev/nvme0n1: 512GB
    Sector size (logical/physical): 512GB/512GB
    Partition Table: gpt
    Disk Flags:
    
    Number  Start   End     Size    File system  Name                          Flags
            17.4kB  1049kB  1031kB  Free Space
     1      1049kB  274MB   273MB   fat32        EFI system partition          boot, esp, no_automount
     2      274MB   290MB   16.8MB               Microsoft reserved partition  msftres, no_automount
     3      290MB   262GB   262GB   ntfs         Basic data partition          msftdata
            262GB   511GB   249GB   Free Space
     4      511GB   512GB   975MB   ntfs         Basic data partition          hidden, diag, no_automount
            512GB   512GB   5578kB  Free Space
    

    This shows that the 未分配 partition I created is the Free Space after number 3 with apparently 249GB. From here on, I have to slightly deviate from the NixOS manual, which assumes a clean SATA (/dev/sda) drive; I have an NVMe drive with Windows 10 and some partitions already configured. The manual recommends:

    1. GPT partition table: # parted /dev/sda -- mklabel gpt
    2. A boot partition (EFI system partition) of 512MiB at the start of the drive: # parted /dev/sda -- mkpart ESP fat32 1MB 512MB and # parted /dev/sda -- set 3 esp on
    3. A swap (extended RAM) partition of 8GB at the end of the drive: # parted /dev/sda -- mkpart swap linux-swap -8GB 100%
    4. A root partition as large as possible in between: # parted /dev/sda -- mkpart root ext4 512MB -8GB

    Since /dev/nvme0n1 already has partition table: gpt, I can skip step 1.
    Since I know I'm using UEFI, and UEFI has only 1 EFI system partition, and Windows already created that partition with an esp flag, I can completely skip step 2.
    I use # parted /dev/nvme0n1 -- mkpart root ext4 262GB -10GB for creating the root partition.
    I use # parted /dev/nvme0n1 -- mkpart swap linux-swap -10GB 510GB for creating the swap partition.
    Note: Partition 4 created by Windows (Basic data partition) is likely the Windows recovery partition, so I left 1GB of margin for error to make sure it doesn't get messed up; I use 510GB instead of 511GB for the swap partition.
    After partitioning, # parted /dev/nvme0n1 print free shows (header incomplete):

    Number  Start   End     Size    File system  Name                          Flags
            17.4kB  1049kB  1031kB  Free Space
     1      1049kB  274MB   273MB   fat32        EFI system partition          boot, esp, no_automount
     2      274MB   290MB   16.8MB               Microsoft reserved partition  msftres, no_automount
     3      290MB   262GB   262GB   ntfs         Basic data partition          msftdata
     5      262GB   502GB   240GB                root
     6      502GB   510GB   7889MB               swap                          swap
            510GB   511GB   1129MB  Free Space
     4      511GB   512GB   975MB   ntfs         Basic data partition          hidden, diag, no_automount
            512GB   512GB   5578kB  Free Space
    
  7. Follow manual: Formatting.
    I use # mkfs.ext4 -L nixos /dev/nvme0n1p5 to my format my root partition (number 5).
    I use # mkswap -L swap /dev/nvme0n1p6 to format my swap partition (number 6).
    Note: I still use -L to label the partitions even though they already have names because filesystem labels are different from partition names. Filesystem labels are used to mount partitions in the next step.
    Note: Boot partition (EFI system partition; number 1) is already formatted.
    After formatting, # parted /dev/nvme0n1 print free shows (incomplete):

    Number  Start  End    Size    File system     Name  Flags
     5      262GB  502GB  240GB   ext4            root
     6      502GB  510GB  7889MB  linux-swap(v1)  swap  swap
    
  8. Follow manual: Mounting.
    Note: Saying "mount A to B" means "make the contents of A accessible at location B".
    I use # mount /dev/disk/by-label/nixos /mnt to mount my root partition to /mnt, which exists by default in the root filesystem of my live environment, which is a complete but temporary OS running in RAM using read-only storage in the USB. Use # ls / (/ is the root filesystem) to see mnt: bin dev etc home iso mnt nix proc root run srv sys tmp usr var. Here, the mount command creates a "bridge" between the partition on the disk and the live environment. After installation, the live environment "dies", meaning the temporary OS along with its files won't be accessible anymore. Also, the command uses the filesystem label; for me, it is equivalent to # mount /dev/nvme0n1p5 /mnt.
    I use # mkdir -p /mnt/boot then # mount -o umask=077 /dev/nvme0n1p1 /mnt/boot to mount Windows' EFI system partition to /mnt/boot. -o umask=077 specifies an option that sets the permission for the newly created directory; 077 means that the owner has full permissions (read, write, execute), and no permissions for everyone else (this only affects Linux, not Windows). I could maybe use # mount -o umask=077 /dev/disk/by-label/boot /mnt/boot, but I do not know for sure if Windows' boot partition has the filesystem label boot.
    I use # swapon /dev/disk/by-label/swap to enable swap. Equivalent to # swapon /dev/nvme0n1p6 for me.

  9. Follow manual: Configure NixOS and install.
    Generate the NixOS configuration file using # nixos-generate-config --root /mnt.
    Note: Using # ls /mnt/etc/nixos now shows configuration.nix hardware-configuration.nix.
    Edit the NixOS configuration using # nano /mnt/etc/nixos/configuration.nix.
    I'm using UEFI, and boot.loader.systemd-boot.enable = true; and boot.loader.efi.canTouchEfiVariables = true; have already been automatically uncommented, so I don't have to change that.
    Uncomment or edit:

    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        systemd-boot.enable = true;
        efi.canTouchEfiVariables = true;
      };
    
      networking = {
        hostName = "ZHAN";
        networkmanager.enable = true;
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        libinput.enable = true;
      };
    
      users = {
        users.tim = {
          isNormalUser = true;
          extraGroups = [ "wheel" "networkmanager" ];
        };
      };
    
      system.stateVersion = "24.05";
    }

    Press ctrl+o, enter, and ctrl+x to save configuration.nix and exit nano.
    Install NixOS using # nixos-install. Wait for a few minutes.
    Set the root password as prompted.
    Reboot using # reboot.
    Reboot automatically booted into Windows 10. Shutdown and turn on laptop to access HP startup menu again.
    Select "UEFI - Linux Boot Manager" from Boot Menu (F9).

  10. Login to NixOS by typing root for ZHAN login: and my root password for Password:.
    Use # passwd tim to set the password for tim as prompted.

Done! Unplug the USB.
Revert bootable USB to storage USB.

Using NixOS

Connecting to the Internet

Using # systemctl start wpa_supplicant and then # wpa_cli doesn't work anymore. Could not connect to wpa_supplicant: (nil) - re-trying. This is probably because in configuration.nix:

# Pick only one of the below networking options.
# networking.wireless.enable = true;
networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.

Anyways, since networking.networkmanager.enable = true; was uncommented, I can use that.

  • Use $ nmcli device wifi list to list available wifi.
  • Use $ nmcli device wifi connect "CU_FS6D" password "redacted" to connect.
    Seems like it automatically connects at each boot.

Using Flakes (and the new CLI)

Nixpkgs is the git repository containing many packages. A channel is a branch of a repository such as nixpkgs; the current nixpkgs stable channel is 24.05. Use # nix-channel --list to see nixos https://nixos.org/channels/nixos-24.05; my NixOS is currently following the nixpkgs stable channel. Before flakes existed, packages in NixOS had to be imperatively (opposite of declaratively) updated through using commands like # nix-channel --update, and using commands was required for adding new channels to use and update packages outside the nixpkgs repository (such as Home Manager with commands like nix-channel --add https://github.com/branch-of-home-manager).

Great resources for understanding flakes:

Nix is the programming language used primarily by NixOS. Flakes are simply Nix programs (flake.nix files) that have specific agreed upon structures making them do specific things. They all have input and output parts. This flake template lists common inputs and outputs and their use cases. Briefly speaking:

  • Inputs can be git repositories, local paths, or other flakes.
  • Outputs can be pretty much anything; some are recognized and used by NixOS.

flake.lock files are automatically created with flake.nix files. flake.lock files store information about the exact versions of the input(s) at the time the lock file was created (or updated). Therefore, the outputs of flake.nix with flake.lock are deterministic and reproducible since the inputs are locked.
An output for flakes used by NixOS is nixosConfigurations. So, I can declaratively set a flake's input as the nixpkgs unstable channel and its output as OS configurations. The flake.lock file generated with this flake specifies the version of the channel. I can use # nix flake update to update the flake.lock file; I no longer use # nix-channel --update.

Enable flakes for NixOS:

  1. Flakes is still an experimental feature; enable it by adding in configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
      
    + nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      users = {
        # ...
      };
    
    + programs = {
    +   git.enable = true;
    + };
    
      system.stateVersion = "24.05";
    }
  2. Connect to internet. Use # nixos-rebuild switch.

Switch system configuration to a flake:

  1. Use $ cd /etc/nixos then # nix flake init to create or initialize a flake file in /etc/nixos.
    Note: Use $ ls to see configuration.nix flake.nix hardware-configuration.nix.
  2. Edit flake.nix:
    {
      description = "NixOS configuration flake";
    
      inputs = {
        nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
      };
    
      outputs = { self, nixpkgs, ... }: {
        nixosConfigurations.ZHAN = nixpkgs.lib.nixosSystem {
          system = "x86_64-linux";
          modules = [
            ./configuration.nix
          ];
        };
      };
    }
    The input is the nixpkgs unstable channel, the git repository. The output is nixosConfigurations of ZHAN. ZHAN is my hostname; with "flakes" experimental feature enabled, # nixos-rebuild switch by default looks for the file /etc/nixos/flake.nix and builds the nixosConfigurations item matching the current hostname of the OS. To specify a different flake directory and hostname, use # nixos-rebuild switch --flake /path/to/flake#hostname. configuration.nix is included; . is a navigation shortcut for the current directory (on a side note, .. is the parent directory).
  3. Connect to internet. Use # nixos-rebuild switch. Wait a few minutes.
    A flake.lock file is generated in /etc/nixos; use $ ls to see configuration.nix flake.lock flake.nix hardware-configuration.nix.

Installing Home Manager

Files or directories that have names beginning with . like the directory /home/tim/.config/ are called dotfiles. Dotfiles are hidden by default; use $ ls -a to see them. Dotfiles typically store configuration settings.
Home Manager translates .nix files to dotfiles and creates symlinks (similar to shortcuts in Windows) for the dotfiles for programs like Firefox and Hyprland to use. This allows me to declare configurations for user specific programs with .nix files so I don't have to edit dotfiles, just like how I declare settings in configuration.nix for the OS. Home Manager also supports rollbacks.
Note: Home Manager can be used to declare or install packages for specific users, but that's not useful for me.
Home Manager can be installed 2 different ways in NixOS:

  • Standalone: Manage dotfiles independent from the OS. No root permission required. Works on other Linux distributions.
  • NixOS Module: Manage dotfiles through the OS for all users. Root permission required.

Install Home Manager as a NixOS module:

  1. Edit flake.nix:
    {
      description = "NixOS configuration flake";
    
      inputs = {
        nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
        
    +   home-manager.url = "github:nix-community/home-manager";
    +   home-manager.inputs.nixpkgs.follows = "nixpkgs";
      };
    
    - outputs = { self, nixpkgs, ... }: {
    + outputs = { self, nixpkgs, home-manager, ... }: {
        nixosConfigurations.ZHAN = nixpkgs.lib.nixosSystem {
          system = "x86_64-linux";
          modules = [
            ./configuration.nix
    +       home-manager.nixosModules.home-manager
    +       {
    +         home-manager.useGlobalPkgs = true;
    +         home-manager.useUserPackages = true;
    +         home-manager.users.tim = import ./home.nix;
    +       }
          ];
        };
      };
    }
    Since Home Manager isn't part of the nixpkgs repository, it is installed through adding its channel in the inputs of the flake. home-manager.inputs.nixpkgs.follows = "nixpkgs"; reduces the number of inputs the flake depends on; packages installed through Home Manager will still be installed from the same nixos-unstable branch used for configuration.nix. useGlobalPkgs and useUserPackages explained here.
  2. Create and edit /etc/nixos/home.nix (unlike non-flake setups, home.nix must be present before the flake is built):
    { config, lib, pkgs, ... }:
    
    {
      home = {
        username = "tim";
        homeDirectory = "/home/tim";
        stateVersion = "24.05";
      };
    
      programs.home-manager.enable = true;
    }
  3. Connect to internet. Use # nixos-rebuild switch.
    flake.lock is automatically updated and now contains version information about both nixpkgs and home-manager.
    See available options to edit home.nix.

iwlwifi Boot Bug (Jul 8, 2024)

Problem:

  • During boot, error messages appear (filtered to only iwlwifi):

    Messages
    [    5.962536] iwlwifi 0000:00:14.3: enabling device (0000 -> 0002)
    [    5.963629] iwlwifi 0000:00:14.3: Detected crf-id 0x2816, cnv-id 0x1000100 wfpm id 0x80000000
    [    5.963635] iwlwifi 0000:00:14.3: PCI dev 9df0/0034, rev=0x312, rfid=0x105110
    [    5.980414] iwlwifi 0000:00:14.3: WRT: Overriding region id 0
    [    5.980417] iwlwifi 0000:00:14.3: WRT: Overriding region id 1
    [    5.980419] iwlwifi 0000:00:14.3: WRT: Overriding region id 2
    [    5.980420] iwlwifi 0000:00:14.3: WRT: Overriding region id 3
    [    5.980421] iwlwifi 0000:00:14.3: WRT: Overriding region id 4
    [    5.980422] iwlwifi 0000:00:14.3: WRT: Overriding region id 6
    [    5.980423] iwlwifi 0000:00:14.3: WRT: Overriding region id 8
    [    5.980424] iwlwifi 0000:00:14.3: WRT: Overriding region id 9
    [    5.980425] iwlwifi 0000:00:14.3: WRT: Overriding region id 10
    [    5.980426] iwlwifi 0000:00:14.3: WRT: Overriding region id 11
    [    5.980427] iwlwifi 0000:00:14.3: WRT: Overriding region id 15
    [    5.980428] iwlwifi 0000:00:14.3: WRT: Overriding region id 16
    [    5.980429] iwlwifi 0000:00:14.3: WRT: Overriding region id 18
    [    5.980430] iwlwifi 0000:00:14.3: WRT: Overriding region id 19
    [    5.980431] iwlwifi 0000:00:14.3: WRT: Overriding region id 20
    [    5.980432] iwlwifi 0000:00:14.3: WRT: Overriding region id 21
    [    5.980433] iwlwifi 0000:00:14.3: WRT: Overriding region id 28
    [    5.980875] iwlwifi 0000:00:14.3: loaded firmware version 46.7e3e4b69.0 9000-pu-b0-jf-b0-46.ucode op_mode iwlmvm
    [    6.495945] iwlwifi 0000:00:14.3: Detected Intel(R) Wireless-AC 9560 160MHz, REV=0x312
    [   11.906805] iwlwifi 0000:00:14.3: Failed to load firmware chunk!
    [   11.906930] iwlwifi 0000:00:14.3: iwlwifi transaction failed, dumping registers
    [   11.906975] iwlwifi 0000:00:14.3: iwlwifi device config registers:
    [   11.907150] iwlwifi 0000:00:14.3: 00000000: 9df08086 00100406 02800011 00800000 f4210004 00000000 00000000 00000000
    [   11.907205] iwlwifi 0000:00:14.3: 00000020: 00000000 00000000 00000000 00348086 00000000 000000c8 00000000 000001ff
    [   11.907259] iwlwifi 0000:00:14.3: 00000040: 00928010 10000ec0 00100c10 00000000 00000000 00000000 00000000 00000000
    [   11.907336] iwlwifi 0000:00:14.3: 00000060: 00000000 00080812 00000405 00000000 00000000 00000000 00000000 00000000
    [   11.907389] iwlwifi 0000:00:14.3: 00000080: 000f0011 00002000 00003000 00000000 00000000 00000000 00000000 00000000
    [   11.907441] iwlwifi 0000:00:14.3: 000000a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    [   11.907494] iwlwifi 0000:00:14.3: 000000c0: 00000000 00000000 c823d001 0d000008 00814005 fee00478 00000000 00000000
    [   11.907546] iwlwifi 0000:00:14.3: 000000e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    [   11.907599] iwlwifi 0000:00:14.3: 00000100: 14c00000 ff000000 000000ff 00462031 00000000 00002000 00000000 00000000
    [   11.907652] iwlwifi 0000:00:14.3: 00000120: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    [   11.907704] iwlwifi 0000:00:14.3: 00000140: 00000000 00000000 00000000 16410018 00000000 0001001e 00481e1f 00000000
    [   11.907755] iwlwifi 0000:00:14.3: iwlwifi device memory mapped registers:
    [   11.907814] iwlwifi 0000:00:14.3: 00000000: 18c89002 00000040 00000000 08000000 00000000 00000000 00000000 00000000
    [   11.907868] iwlwifi 0000:00:14.3: 00000020: 00000010 0c000005 00000312 d55555d5 d55555d5 d55555d5 80008040 001f0042
    [   11.907946] iwlwifi 0000:00:14.3: Could not load the [0] uCode section
    [   11.907991] iwlwifi 0000:00:14.3: Failed to start INIT ucode: -110
    [   11.908029] iwlwifi 0000:00:14.3: WRT: Collecting data: ini trigger 13 fired (delay=0ms).
    [   12.158282] iwlwifi 0000:00:14.3: Not valid error log pointer 0x00000000 for Init uCode
    [   12.158301] iwlwifi 0000:00:14.3: IML/ROM dump:
    [   12.158308] iwlwifi 0000:00:14.3: 0x00000003 | IML/ROM error/state
    [   12.158339] iwlwifi 0000:00:14.3: 0x00000001 | IML/ROM data1
    [   12.158350] iwlwifi 0000:00:14.3: Fseq Registers:
    [   12.158374] iwlwifi 0000:00:14.3: 0x14163D33 | FSEQ_ERROR_CODE
    [   12.158383] iwlwifi 0000:00:14.3: 0x00000000 | FSEQ_TOP_INIT_VERSION
    [   12.158393] iwlwifi 0000:00:14.3: 0xA538284A | FSEQ_CNVIO_INIT_VERSION
    [   12.158402] iwlwifi 0000:00:14.3: 0x0000A384 | FSEQ_OTP_VERSION
    [   12.158412] iwlwifi 0000:00:14.3: 0x371909C7 | FSEQ_TOP_CONTENT_VERSION
    [   12.158421] iwlwifi 0000:00:14.3: 0xB6FEED1F | FSEQ_ALIVE_TOKEN
    [   12.158430] iwlwifi 0000:00:14.3: 0x6565DF70 | FSEQ_CNVI_ID
    [   12.158439] iwlwifi 0000:00:14.3: 0xA4EAB9C6 | FSEQ_CNVR_ID
    [   12.158448] iwlwifi 0000:00:14.3: 0x01000100 | CNVI_AUX_MISC_CHIP
    [   12.158460] iwlwifi 0000:00:14.3: 0x01300202 | CNVR_AUX_MISC_CHIP
    [   12.158838] iwlwifi 0000:00:14.3: 0x0000485B | CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM
    [   12.159245] iwlwifi 0000:00:14.3: 0xA5A5A5A2 | CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR
    [   12.159658] iwlwifi 0000:00:14.3: 0x3BF53F5D | FSEQ_PREV_CNVIO_INIT_VERSION
    [   12.160056] iwlwifi 0000:00:14.3: 0xE6DA90F4 | FSEQ_WIFI_FSEQ_VERSION
    [   12.160471] iwlwifi 0000:00:14.3: 0x50154F5B | FSEQ_BT_FSEQ_VERSION
    [   12.160847] iwlwifi 0000:00:14.3: 0x6543892F | FSEQ_CLASS_TP_VERSION
    [   12.653870] iwlwifi 0000:00:14.3: Master Disable Timed Out, 100 usec
    [   12.665455] iwlwifi 0000:00:14.3: Failed to run INIT ucode: -110
    [   12.678376] iwlwifi 0000:00:14.3: retry init count 0
    [   12.679991] iwlwifi 0000:00:14.3: Detected Intel(R) Wireless-AC 9560 160MHz, REV=0x312
    [   18.050795] iwlwifi 0000:00:14.3: Failed to load firmware chunk!
    [   18.052671] iwlwifi 0000:00:14.3: Could not load the [0] uCode section
    [   18.054516] iwlwifi 0000:00:14.3: Failed to start INIT ucode: -110
    [   18.056094] iwlwifi 0000:00:14.3: WRT: Collecting data: ini trigger 13 fired (delay=0ms).
    [   18.305373] iwlwifi 0000:00:14.3: Not valid error log pointer 0x00000000 for Init uCode
    [   18.305769] iwlwifi 0000:00:14.3: IML/ROM dump:
    [   18.306161] iwlwifi 0000:00:14.3: 0x00000003 | IML/ROM error/state
    [   18.306594] iwlwifi 0000:00:14.3: 0x00000001 | IML/ROM data1
    [   18.307015] iwlwifi 0000:00:14.3: Fseq Registers:
    [   18.307461] iwlwifi 0000:00:14.3: 0x14163D33 | FSEQ_ERROR_CODE
    [   18.307884] iwlwifi 0000:00:14.3: 0x00000000 | FSEQ_TOP_INIT_VERSION
    [   18.308271] iwlwifi 0000:00:14.3: 0xA538284A | FSEQ_CNVIO_INIT_VERSION
    [   18.308696] iwlwifi 0000:00:14.3: 0x0000A384 | FSEQ_OTP_VERSION
    [   18.309121] iwlwifi 0000:00:14.3: 0x371909C7 | FSEQ_TOP_CONTENT_VERSION
    [   18.309558] iwlwifi 0000:00:14.3: 0xB6FEED1F | FSEQ_ALIVE_TOKEN
    [   18.309980] iwlwifi 0000:00:14.3: 0x6565DF70 | FSEQ_CNVI_ID
    [   18.310400] iwlwifi 0000:00:14.3: 0xA4EAB9C6 | FSEQ_CNVR_ID
    [   18.310821] iwlwifi 0000:00:14.3: 0x01000100 | CNVI_AUX_MISC_CHIP
    [   18.311207] iwlwifi 0000:00:14.3: 0x01300202 | CNVR_AUX_MISC_CHIP
    [   18.311646] iwlwifi 0000:00:14.3: 0x0000485B | CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM
    [   18.312098] iwlwifi 0000:00:14.3: 0xA5A5A5A2 | CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR
    [   18.312526] iwlwifi 0000:00:14.3: 0x3BF53F5D | FSEQ_PREV_CNVIO_INIT_VERSION
    [   18.312939] iwlwifi 0000:00:14.3: 0xE6DA90F4 | FSEQ_WIFI_FSEQ_VERSION
    [   18.313313] iwlwifi 0000:00:14.3: 0x50154F5B | FSEQ_BT_FSEQ_VERSION
    [   18.313741] iwlwifi 0000:00:14.3: 0x6543892F | FSEQ_CLASS_TP_VERSION
    [   18.510910] iwlwifi 0000:00:14.3: Master Disable Timed Out, 100 usec
    [   18.522493] iwlwifi 0000:00:14.3: Failed to run INIT ucode: -110
    [   18.535348] iwlwifi 0000:00:14.3: retry init count 1
    [   18.537039] iwlwifi 0000:00:14.3: Detected Intel(R) Wireless-AC 9560 160MHz, REV=0x312
    [   23.682797] iwlwifi 0000:00:14.3: Failed to load firmware chunk!
    [   23.684352] iwlwifi 0000:00:14.3: Could not load the [0] uCode section
    [   23.685890] iwlwifi 0000:00:14.3: Failed to start INIT ucode: -110
    [   23.688076] iwlwifi 0000:00:14.3: WRT: Collecting data: ini trigger 13 fired (delay=0ms).
    [   23.937339] iwlwifi 0000:00:14.3: Not valid error log pointer 0x00000000 for Init uCode
    [   23.937743] iwlwifi 0000:00:14.3: IML/ROM dump:
    [   23.938118] iwlwifi 0000:00:14.3: 0x00000003 | IML/ROM error/state
    [   23.938538] iwlwifi 0000:00:14.3: 0x00000001 | IML/ROM data1
    [   23.939130] iwlwifi 0000:00:14.3: Fseq Registers:
    [   23.939589] iwlwifi 0000:00:14.3: 0x14163D33 | FSEQ_ERROR_CODE
    [   23.939991] iwlwifi 0000:00:14.3: 0x00000000 | FSEQ_TOP_INIT_VERSION
    [   23.940395] iwlwifi 0000:00:14.3: 0xA538284A | FSEQ_CNVIO_INIT_VERSION
    [   23.940932] iwlwifi 0000:00:14.3: 0x0000A384 | FSEQ_OTP_VERSION
    [   23.941293] iwlwifi 0000:00:14.3: 0x371909C7 | FSEQ_TOP_CONTENT_VERSION
    [   23.941783] iwlwifi 0000:00:14.3: 0xB6FEED1F | FSEQ_ALIVE_TOKEN
    [   23.942136] iwlwifi 0000:00:14.3: 0x6565DF70 | FSEQ_CNVI_ID
    [   23.942526] iwlwifi 0000:00:14.3: 0xA4EAB9C6 | FSEQ_CNVR_ID
    [   23.943049] iwlwifi 0000:00:14.3: 0x01000100 | CNVI_AUX_MISC_CHIP
    [   23.943441] iwlwifi 0000:00:14.3: 0x01300202 | CNVR_AUX_MISC_CHIP
    [   23.943921] iwlwifi 0000:00:14.3: 0x0000485B | CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM
    [   23.944328] iwlwifi 0000:00:14.3: 0xA5A5A5A2 | CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR
    [   23.944795] iwlwifi 0000:00:14.3: 0x3BF53F5D | FSEQ_PREV_CNVIO_INIT_VERSION
    [   23.945148] iwlwifi 0000:00:14.3: 0xE6DA90F4 | FSEQ_WIFI_FSEQ_VERSION
    [   23.945541] iwlwifi 0000:00:14.3: 0x50154F5B | FSEQ_BT_FSEQ_VERSION
    [   23.946069] iwlwifi 0000:00:14.3: 0x6543892F | FSEQ_CLASS_TP_VERSION
    [   24.143873] iwlwifi 0000:00:14.3: Master Disable Timed Out, 100 usec
    [   24.155544] iwlwifi 0000:00:14.3: Failed to run INIT ucode: -110
    [   24.169271] iwlwifi 0000:00:14.3: retry init count 2
    
  • After boot, using $ nmcli device wifi list shows nothing.

Fix:

  1. Enter HP startup menu.
  2. Select BIOS Setup (F10).
    Go to Advanced tab.
    Select Built-In Device Options.
    Uncheck Wireless Network Device (WLAN).
  3. Boot into NixOS. No iwlwifi logs appear.
  4. Reboot to enter HP startup menu again.
  5. Check Wireless Network Device (WLAN).
  6. Boot into NixOS again.
    Wifi works again; using $ nmcli device wifi list shows that I am connected.

Possible cause:
I did not completely shut down Windows 10 before booting into NixOS. Upon shutting down NixOS and booting into Windows 10, I see that Astrill VPN is still connected. So, Astrill VPN may have been connected while I first booted into NixOS; I disconnected Astrill VPN and shut down Windows 10, but upon booting into NixOS afterwards, the problem persisted.

Using Zsh

In configuration.nix, add:

{ config, lib, pkgs, ... }:

{
  imports =
    [
      ./hardware-configuration.nix
    ];

  boot.loader = {
    # ...
  };

  nix.settings.experimental-features = [ "nix-command" "flakes" ];

  networking = {
    # ...
  };

  time.timeZone = "America/New_York";

  services = {
    # ...
  };

  users = {
    users.tim = {
      # ...
    };
+   defaultUserShell = pkgs.zsh;
  };

  programs = {
    git.enable = true;

+   zsh = {
+     enable = true;
+     autosuggestions.enable = true;
+   };
  };

  system.stateVersion = "24.05";
}

Connect to internet. Use # nixos-rebuild switch and reboot. Use $ echo $SHELL to see /run/current-system/sw/bin/zsh.
Navigate in the zsh menu to generate /home/tim/.zshrc:

# Lines configured by zsh-newuser-install
# End of lines configured by zsh-newuser-install
# The following lines were added by compinstall
zstyle :compinstall filename '/home/tim/.zshrc'

autoload -Uz compinit
compinit
# End of lines added by compinstall

Since I don't need to further configure zsh (yet), I do not use Home Manager for zsh.

Using Hyprland

Hyprland can be installed 2 ways:

  • From the branch of nixpkgs defined in the flake input.
  • From the Hyprland git repository by defining a separate flake input.

Since I am following the nixos-unstable branch of nixpkgs, the Hyprland package from there will likely be up to date with Hyprland's own git repository; I do not have to define a separate flake input.
Note: There is a distinction between installing a package, and enabling or configuring it. By default, declarations like programs.hyprland.enable = true; in configuration.nix and wayland.windowManager.hyprland.enable = true; in home.nix installs and enables Hyprland. However, if Hyprland is already installed, either through a nixpkgs branch or through Hyprland's own git repository, I can tell NixOS (programs.hyprland.package = ...) and Home Manager (wayland.windowManager.hyprland.package = ...) to use it. Since I am installing Hyprland from nixpkgs, I do not have to explicitly specify the package.
Install Hyprland (and kitty) from nixpkgs for all users:

  1. In configuration.nix, add:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      users = {
        # ...
      };
    
    + environment = {
    +   systemPackages = with pkgs; [
    +     kitty
    +   ];
    +   sessionVariables.NIXOS_OZONE_WL = "1";
    + };
    
      programs = {
        git.enable = true;
    
        zsh = {
          # ...
        };
    
    +   hyprland.enable = true;
      };
    
      system.stateVersion = "24.05";
    }
    kitty is the default terminal emulator. Following wiki, only 1 environment variable is defined. XWayland is enabled by default.
  2. Connect to internet. Use # nixos-rebuild switch.
  3. Reboot. Use $ Hyprland to see that Hyprland works. No crashes.

Fixing kitty and xdg-desktop-portal:

  1. In Hyprland, use $ kitty to see [0.139] [glfw error 65544]: process_desktop_settings: failed with error: [org.freedesktop.DBus.Error.UnknownMethod] No such interface "org.freedesktop.portal.Settings" on object at path /org/freedesktop/portal/desktop. A new terminal appears, but as soon as I use ctrl+c in the original terminal, the new terminal disappears.
  2. Using $ systemctl --user status dbus shows no abnormalities, but using $ systemctl --user status xdg-desktop-portal.service shows (incomplete):
    systemd[1111]: Starting Portal service...
    .xdg-desktop-po[1240]: Failed to load RealtimeKit property: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.RealtimeKit1 was not provided by any .service files
    .xdg-desktop-po[1240]: Failed to load RealtimeKit property: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.RealtimeKit1 was not provided by any .service files
    .xdg-desktop-po[1240]: Failed to load RealtimeKit property: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.RealtimeKit1 was not provided by any .service files
    .xdg-desktop-po[1187]: No skeleton to export
    systemd[1111]: Started Portal service.
    
  3. Kitty's error can be fixed by adding xdg-desktop-portal-gtk to the system. This can seemingly be done 2 ways: either with environment.systemPackages or with xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];. Both ways install xdg-desktop-portal-gtk, and I choose the second option.
    Note: Using $ systemctl --user status xdg-desktop-portal-hyprland.service shows that the Hyprland version of xdg-desktop-portal is already running without any issues; I guess kitty either is not using this Hyprland version or does not work with it. Although, I do not mind installing the GTK version of xdg-desktop-portal, since it is recommended on the Hyprland wiki.
  4. xdg-desktop-portal's RealtimeKit error can be fixed by adding security.rtkit.enable = true; to configuration.nix. No skeleton to export can also be fixed by adding xdg-desktop-portal-gtk.
    After changing these two options, a new error appears when using $ systemctl --user status xdg-desktop-portal.service (incomplete):
    systemd[1143]: Starting Portal service...
    .xdg-desktop-po[1220]: Failed connect to PipeWire: Couldn't connect to PipeWire
    systemd[1143]: Started Portal service.
    
    This pipewire error can be fixed by adding services.pipewire.enable = true; to configuration.nix. After doing so, no xdg-desktop-portal errors occur.
  5. All additions to configuration.nix as a result of the errors:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        libinput.enable = true;
    +   pipewire.enable = true;
      };
    
    + security.rtkit.enable = true;
    
    + xdg.portal = {
    +   enable = true;
    +   extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
    + };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
  6. Connect to internet. Use # nixos-rebuild switch.

Using Home Manager to configure Hyprland:

  1. Create and edit /etc/nixos/hslaToRgba.nix:
    { lib, builtins }:
    
    let
      mod = x: y: x - y * (builtins.floor (x / y));
      abs = x: if x < 0 then -x else x;
    
      hslaToRgba = hsla:
        let
          h = mod hsla.h 360 / 60.0;
          s = hsla.s;
          l = hsla.l;
          a = hsla.a;
    
          c = (1 - abs(2 * l - 1)) * s;
          x = c * (1 - abs (mod h 2 - 1));
          m = l - c / 2;
    
          rgb' =
            if h < 1 then { r = c; g = x; b = 0; }
            else if h < 2 then { r = x; g = c; b = 0; }
            else if h < 3 then { r = 0; g = c; b = x; }
            else if h < 4 then { r = 0; g = x; b = c; }
            else if h < 5 then { r = x; g = 0; b = c; }
            else { r = c; g = 0; b = x; };
    
          round = x: builtins.floor (x * 255 + 0.5);
          r = round (rgb'.r + m);
          g = round (rgb'.g + m);
          b = round (rgb'.b + m);
          alpha = round a;
    
          toHex = x: let 
            hex = lib.toHexString (lib.min 255 (lib.max 0 x));
          in if builtins.stringLength hex == 1 then "0${hex}" else hex;
        in
        "${toHex r}${toHex g}${toHex b}${toHex alpha}";
    in
    hslaToRgba
    Hyprland uses hexadecimal RGBA format to configure colors, which is not human readable. hslaToRgba.nix converts HSLA to hexadecimal RGBA.
  2. Edit home.nix:
    { config, lib, pkgs, ... }:
    
    +let
    + hslaToRgba = import ./hslaToRgba.nix { inherit lib builtins; };
    + formatRgba = color: "rgba(${hslaToRgba color})";
    
    + borderGradientDegree = 45;
    + borderActive1 = { h = 0; s = 0.0; l = 1.0; a = 1; };
    + borderActive2 = { h = 0; s = 0.0; l = 1.0; a = 0.65; };
    + borderInactive = { h = 210; s = 0.16; l = 0.5; a = 0.5; };
    + shadowActive = { h = 185; s = 0.5; l = 0.6; a = 0.1; };
    + shadowInactive = { h = 0; s = 0.0; l = 0.2; a = 0.1; };
    
    +in
    {
      home = {
        # ...
      };
    
    + wayland.windowManager.hyprland = {
    +   enable = true;
    +   settings = {
    +     bind = [
    +       "ALT, TAB, cyclenext"
    +       "ALT, TAB, alterzorder, top"
    +       "SUPER, T, exec, kitty"
    +       "SUPER, W, killactive"
    +       "SUPER, Q, togglefloating"
    +       "SUPER, F, fullscreenstate, 2 0"
    +       "SUPER, K, exit"
    +       "SUPER, tab, workspace, e+1"
    +       "SUPER_SHIFT, tab, movetoworkspace, e+1"
    +       "SUPER, E, movetoworkspace, emptyn"
    +     ];
    +     bindm = [
    +       "SHIFT, mouse:272, movewindow"
    +       "CTRL, mouse:272, resizewindow"
    +     ];
    +     bezier = [
    +       "linear, 0.5, 0.5, 0.5, 0.5"
    +       "snappyOut1, 0, 0.85, 0, 1"
    +       "snappyOut2, 0, 0.6, 0.15, 1"
    +       "expoOut, 0.16, 1, 0.3, 1"
    +     ];
    +     animation = [
    +       "windowsIn, 1, 5, snappyOut1, popin 20%"
    +       "windowsOut, 1, 8, snappyOut2, popin 80%"
    +       "windowsMove, 1, 5, snappyOut1"
    +       "fadeOut, 1, 4, expoOut"
    +       "borderangle, 1, 40, linear, loop"
    +       "workspaces, 1, 3, snappyOut1, slide"
    +     ];
    +     general = {
    +       border_size = 2;
    +       gaps_in = 4;
    +       gaps_out = 10;
    +       "col.active_border" = "${formatRgba borderActive1} ${formatRgba borderActive2} ${toString borderGradientDegree}deg";
    +       "col.inactive_border" = formatRgba borderInactive;
    +       resize_on_border = true;
    +     };
    +     decoration = {
    +       rounding = 8;
    +       shadow = {
    +         range = 6;
    +         render_power = 1;
    +         color = formatRgba shadowActive;
    +         color_inactive = formatRgba shadowInactive;
    +       };
    +       blur = {
    +         size = 6;
    +         passes = 3;
    +       };
    +     };
    +     input = {
    +       repeat_delay = 500;
    +       sensitivity = "0.3";
    +       accel_profile = "flat";
    +       touchpad = {
    +         disable_while_typing = false;
    +         natural_scroll = true;
    +         tap-and-drag = false;
    +       };
    +     };
    +     gestures = {
    +       workspace_swipe = true;
    +     };
    +     misc = {
    +       disable_hyprland_logo = true;
    +       disable_splash_rendering = true;
    +       animate_manual_resizes = true;
    +       animate_mouse_windowdragging = true;
    +       background_color = 000000;
    +       middle_click_paste = false;
    +     };
    +   };
    + };
    
      programs.home-manager.enable = true;
    }
    Since I am using Hyprland from hyprland.pkgs, I do not need to specify wayland.windowManager.hyprland.package for Home Manager.
    Options that have syntax similar to Nix need to be wrapped in "".
    Bind and flag(s) = modifier key(s), other key(s), action, parameter(s).
    Although trackpad (libinput) configuration can be done through configuration.nix, that doesn't work for me (/etc/X11/xorg.conf isn't generated), so I use Hyprland's options instead.
    On the Hyprland wiki, touch-and-drag's default is marked as false, but in reality it's default is actually true, so I explicitly set it.
  3. Connect to internet. Use # nixos-rebuild switch.
    An error occurs since a configuration file for Hyprland was automatically generated when installing Hyprland. Home Manager will not overwrite this existing configuration file.
  4. Use $ rm ~/.config/hypr/hyprland.conf, then # nixos-rebuild switch again.
    Now, the configurations from home.nix appear.
    Note: Home Manager creates a symlink at ~/.config/hypr/hyprland.conf. I can remove this symlink, and when I use # nixos-rebuild switch, Home Manager will generate the symlink according to home.nix again.

Using Home Manager to configure kitty:

  1. Edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        # ...
      };
    
    + programs.kitty = {
    +   enable = true;
    +   settings = {
    +     font_size = "9.5";
    +     cursor_shape = "beam";
    +     cursor_shape_unfocused = "beam";
    +     cursor_stop_blinking_after = 0;
    +     scrollback_fill_enlarged_window = true;
    +     mouse_hide_wait = -1;
    +     show_hyperlink_targets = true;
    +     clear_all_mouse_actions = true;
    +     enable_audio_bell = false;
    +     confirm_os_window_close = 0;
    +     background_opacity = "0.3";
    +     selection_background = "#ffffff";
    +     clear_all_shortcuts = true;
    +   };  
    +   extraConfig = ''
    +     mouse_map left press ungrabbed mouse_selection normal
    +     mouse_map left doublepress ungrabbed mouse_selection word
    +     mouse_map left triplepress ungrabbed mouse_selection line
    +     map ctrl+c copy_or_interrupt
    +     map ctrl+x copy_and_clear_or_interrupt
    +     map ctrl+v paste_from_clipboard
    +     map ctrl+equal change_font_size all +1.0
    +     map ctrl+minus change_font_size all -1.0
    +   '';
    + };
    
      programs.home-manager.enable = true;
    }
    The programs.kitty.shellIntegration.enableZshIntegration Home Manager option is not set because it does not actually achieve shell integration. Although kitty uses "yes" and "no", Home Manager converts true and false to the strings. '' defines multi-line strings in nix.
  2. Connect to internet. Use # nixos-rebuild switch.

Using Firefox (WIP)

In nixpkgs, *-bin means precompiled binary; *-unwrapped means not wrapped by NixOS. Installing firefox-bin instead of the normal non-precompiled version does not seem to make a difference during configuration with Home Manager or configuration.nix.

  1. To install firefox-bin for all users, edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
      programs = {
        git.enable = true;
    
        zsh = {
          # ...
        };
    
        hyprland.enable = true;
    
    +   firefox = {
    +     enable = true;
    +     package = pkgs.firefox-bin;
    +     policies = {
    +       DisablePocket = true;
    +       ExtensionSettings = ;
    +       FirefoxHome = {
    +         Search = false;
    +         TopSites = false;
    +         SponsoredTopSites = false;
    +         Highlights = false;
    +         Pocket = false;
    +         SponsoredPocket = false;
    +       };
    +       Homepage = {
    +         URL = "https://redacted.myschoolapp.com/app/student";
    +         StartPage = "previous-session";
    +       };
    +       OverrideFirstRidePage = "";
    +       Permissions = ;
    +       Preferences = {
    +         "browser.tabs.tabmanager.enabled" = {
    +           Value = false;
    +           Status = "user";
    +         };
    +         "browser.tabs.inTitlebar" = {
    +           Value = 0;
    +           Status = "user";
    +         };
    +         "browser.uidensity" = {
    +           Value = 1;
    +           Status = "user";
    +         };
    +         "browser.urlbar.shortcuts.tabs" = {
    +           Value = false;
    +           Status = "user";
    +         };
    +         "browser.urlbar.suggest.engines" = {
    +           Value = false;
    +           Status = "user";
    +         };
    +         "browser.urlbar.suggest.topsites" = {
    +           Value = false;
    +           Status = "user";
    +         };
    +         "browser.urlbar.suggest.calculator" = {
    +           Value = true;
    +           Status = "user";
    +         };
    +         "browser.urlbar.unitConversion.enabled" = {
    +           Value = true;
    +           Status = "user";
    +         };
    +         "browser.vpn_promo.enabled" = {
    +           Value = false;
    +           Status = "user";
    +         };
    +       };
    +     };
    +   };
      };
    
      system.stateVersion = "24.05";
    }
    Policies (view in Firefox internal page with URL about:policies; stored in file(s) in /nix/store/) are usually declared to allow administrators to set and lock browser settings. programs.firefox.policies is declared in configuration.nix because the Home Manager option programs.firefox.policies does not work. Other profile specific Home Manager options in programs.firefox.profiles work.
    Note: The Firefox policy NoDefaultBookmarks seems good, but actually breaks Home Manager's programs.firefox.profiles.<name>.bookmarks option. When any option under programs.firefox.profiles (like programs.firefox.profiles.<name>.bookmarks) fails, all other options under programs.firefox.profiles fail; no error messages are displayed, but the configurations have no effect. When programs.firefox.profiles.<name>.bookmarks works, it silently overrides the default Firefox bookmarks.
    programs.firefox.preferences declare configuration options mostly viewable in Firefox internal page about:config; in testing, this option seems to be equivalent to programs.firefox.policies = { Preferences = {} }, since the Preferences policy can be seen in about:policies. Home Manager's option programs.firefox.profiles.<name>.settings also modify options viewable in about:config, but the modifications are user specific; the modifications are stored in ~/.mozilla/firefox/<name>/user.js, which defines overrides to ~/.mozilla/firefox/<name>/prefs.js (Firefox modifies prefs.js when options are changed directly through about:config). So, to modify about:config for all users, using a policy is the only option, and to modify about:config for a specific user, using Home Manager's option programs.firefox.profiles.<name>.settings is the only option (there is no option for modifying prefs.js directly, but modifying user.js achieves the same result). Also, Home Manager's option programs.firefox.profiles.<name>.extraConfig modifies user.js as well, as seen in Home Manager's mkFirefoxModule.nix.
    Note: Nearly all Firefox configuration options hidden or shown in about:config can be found in firefox.js, all.js, StaticPrefList.yaml, and MozillaZine.
    Note: Firefox View (about:firefoxview) cannot be disabled with any configuration options, and can only be hidden. When opening about:firefoxview, the tab manager button appears, even though the browser.tabs.tabmanager.enabled value is already set to false.
  2. Connect to internet. Use # nixos-rebuild switch.
    Use $ firefox to see that Firefox works.
    In the terminal, minor error messages appear:
    [Parent 4325, Main Thread] WARNING: ../gdk/wayland/gdkcursor-wayland.c:243 cursor image size (9x16) not an integermultiple of scale (2): 'glib warning', file /builds/worker/checkouts/gecko/toolkit/xre/nsSigHandlers.cpp:187
    
    (firefox:4325): Gdk-WARNING **: 05:23:33.059: ../gdk/wayland/gdkcursor-wayland.c:243 cursor image size (9x16) not an integermultiple of scale (2)
    Gdk-Message: 05:23:36.908: Unable to load pointer from the cursor theme
    Gdk-Message: 05:27:02.544: Unable to load col-resize from the cursor theme
    
    Although Hyprland has Hyprcursor which is superior to XCursor, some applications like Firefox don't support Hyprcursor and will fall back to XCursor. I don't have any Hyprcursor themes installed, so Hyprland is also falling back to XCursor. I don't have any custom XCursor themes installed. To customize the cursor in XCursor, edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        systemPackages = with pkgs; [
          kitty
    +     capitaine-cursors
        ];
        sessionVariables.NIXOS_OZONE_WL = "1";
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
    And edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        username = "tim";
        homeDirectory = "/home/tim";
        stateVersion = "24.05";
    +   pointerCursor = {
    +     package = pkgs.capitaine-cursors;
    +     name = "capitaine-cursors";
    +     size = 22;
    +     gtk.enable = true;
    +     x11.enable = true;
    +     x11.defaultCursor = "default";
    +   };
      };
    
    + gtk = {
    +   enable = true;
    + };
    
      wayland.windowManager.hyprland = {
        # ...
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.home-manager.enable = true;
    }
    home.pointerCursor sets up many cursor configurations; it sets up XCURSOR environment variables which configure the cursor on Hyprland without Hyprcursor. Configuring environment variables need a reboot to apply.
    home.pointerCursor.package must be declared because it doesn't have a default.
    For programs like Firefox that use GDK (backbone of GTK, the GUI toolkit), cursor configurations must be configured through GTK. GTK configuration can be done through graphical apps like nwg-look, but there are Home Manager options for GTK. GTK cursor configurations (home.pointerCursor.gtk.enable = true;) only apply when gtk.enable = true;.
    Although pkgs.capitaine-cursors includes light and dark versions, the dark version seems to be the default, so I don't configure that manually.
    After connecting to internet and using # nixos-rebuild switch, running Firefox shows no error messages.
  3. To use Home Manager to configure Firefox, edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        # ...
      };
    
      programs.kitty = {
        # ...
      };
    
    + programs.firefox = {
    +   enable = true;
    +   package = pkgs.firefox-bin;
    +   profiles.tim1 = {
    +     name = "School";
    +     id = 0;
    +     bookmarks = [
    +       {
    +         toolbar = true;
    +         bookmarks = [
    +           {
    +             name = "Gmail";
    +             url = "https://mail.google.com";
    +             keyword = "m";
    +           }
    +           {
    +             name = "Drive";
    +             url = "https://drive.google.com/drive/u/0/my-drive";
    +             keyword = "d";
    +           }
    +           {
    +             name = "Claude";
    +             url = "https://claude.ai/chats";
    +             keyword = "c";
    +           }
    +         ];
    +       }
    +     ];
    +     extensions = ;
    +     settings = {
    +       "middlemouse.paste" = false;
    +       "findbar.highlightAll" = true;
    +     };
    +     extraConfig = ''
    +     '';
    +     search.engines = {
    +       "Bing CN" = {
    +         urls = [{ template = "https://cn.bing.com/search?q={searchTerms}&ensearch=1"; }];
    +         definedAliases = [ "@b" ];
    +         iconURL = "https://img.icons8.com/fluency/48/bing--v4.png";
    +       };
    +       "Bing".metaData.hidden = true;
    +       "DuckDuckGo".metaData.hidden = true;
    +       "Wikipedia (en)".metaData.hidden = true;
    +     };
    +     userChrome = ''
    +     '';
    +     userContent = ''
    +     '';
    +   };
    +   profiles.test = {
    +     id = 1;
    +   };
    + };
    
      programs.home-manager.enable = true;
    }
    The default for programs.firefox.package is pkgs.firefox, not pkgs.firefox-bin, so if the option is not set, Home Manager will try to install pkgs.firefox instead of using pkgs.firefox-bin that's already installed through configuration in configuration.nix.
    Note: When I first used # nixos-rebuild switch, a download of a Firefox package began, even though I specified programs.firefox.package = pkgs.firefox-bin, matching the package in configuration.nix. I updated /etc/nixos/flake.lock, used # nixos-rebuild switch without any Firefox configurations in home.nix, and then used # nixos-rebuild switch with Firefox configurations including programs.firefox.package = pkgs.firefox-bin in home.nix; during this rebuild no Firefox package was downloaded. Using nix-store -q --references /run/current-system/sw | grep firefox shows only one Firefox. A possible explanation could be that Home Manager queries a different version of the unstable branch of nixpkgs than the OS; Home Manager follows the same branch as the OS, as defined in flake.nix home-manager.inputs.nixpkgs.follows = "nixpkgs";, but a different version of the branch. So, during rebuild, Home Manager sees that in its version of the nixos-unstable branch of nixpkgs there is pkgs.firefox-bin of a different version than the pkgs.firefox-bin already installed in the system, so Home Manager tries to install the newer pkgs.firefox-bin from the version of the nixos-unstable branch of nixpkgs that it follows. So, when I update the version of the nixos-unstable branch of nixpkgs that the OS follows by updating /etc/nixos/flake.lock to possibly match the version that Home Manager is following, Home Manager does not install another Firefox anymore. One reason to suspect this is that before /etc/nixos/flake.lock was updated, Firefox version 127.x.x was installed, and after updating, it changed to version 128.0.3.
    Firefox generates profiles on first launch, not instillation. This means that if I delete the profile directories and use # nixos-rebuild switch without modifying home.nix, reopening Firefox will generate the default profile instead of profiles defined in programs.firefox.profiles.
    programs.firefox.profiles.<name>.id must be declared when multiple profiles are declared.
    The syntax inside programs.firefox.profiles.<name>.bookmarks does not allow ;'s after {}'s. The toolbar option defines the sub-bookmarks within the bookmark (functioning as directory) as toolbar bookmarks. This is the pseudo-structure: profileBookmarks = { menuBookmark1, menuBookmark2, toolbarBookmarks = { toolbarBookmark1, toolbarBookmark2, ... }, menuBookmark3, ... }. The keyword option redirects typing the string in the address bar to the bookmark.
    Configurations inside programs.firefox.profiles.<name>.settings are all configurations not available through the Preferences policy in configuration.nix.
    programs.firefox.profiles.<name>.search.force does not function as described in the documentation; the default search engines (Google, Bing, DuckDuckGo, Wikipedia (en)) still appear with the option set to true. On seemingly every launch, Firefox regenerates the file ~/.mozilla/firefox/<name>/search.json.mozlz4, replacing Home Manager's symlink, adding the default search engines back into the file; the issue is not resolved. The SearchEngines policy exists for Firefox ESR, and for security reasons it will not be supported for rapid release. Default search engines are hidden with metadata attributes in home.nix; default engines cannot be removed completely, although uninstalling them through extension related policies could work. programs.firefox.profiles.<name>.search.privateDefault does not work, but the default is Google.
    Remove the directories ~/.mozilla and ~/.cache/mozilla. Connect to internet. Use # nixos-rebuild switch.

Controlling Screen Backlight

Packages like light and brightnessctl work on Wayland and make changing screen brightness easy, but there is a simpler way to change screen brightness. The file /sys/class/backlight/intel_backlight/brightness contains an integer that defines the screen brightness. Use $ cat /sys/class/backlight/intel_backlight/max_brightness to see the max brightness is 24000 (a number <188 makes the backlight flicker or turn off). When the file brightness is modified, the screen brightness changes accordingly. However, the file brightness has permissions -rw-r--r-- (file type, permissions(owner, group, others)), so only the root user can modify it. Use $ sudo tee /sys/class/backlight/intel_backlight/brightness <<< MY_NUMBER to modify the file manually.
Notes: Using $ sudo echo MY_NUMBER > /sys/class/backlight/intel_backlight/max_brightness does not work because sudo elevates the command that follows it (echo in this case), not the redirector operator > which is handled by the command shell (zsh for me). sudo here does not affect the command shell's operations; > is supposed to write the output of echo MY_NUMBER to the file brightness but it's operated by the command shell which doesn't have permission. Using # echo MY_NUMBER > /sys/class/backlight/intel_backlight/max_brightness works because while using commands as root, all operations, including the command shell's operations, are performed with root privileges. Also, to use the command without root, use $ sudo sh -c 'echo MY_NUMBER > /sys/class/backlight/intel_backlight/max_brightness'. <<< (here string) is also a command shell operation, but in $ sudo tee /sys/class/backlight/intel_backlight/brightness <<< MY_NUMBER tee actually does the writing, and tee has elevated privileges. <<< only passes the string to tee for tee to modify the file brightness.

  1. To allow modification of the file brightness without sudo for all users, edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        libinput.enable = true;
        pipewire.enable = true;
    +   udev.extraRules = ''
    +     ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="intel_backlight", MODE="0666", RUN+="${pkgs.coreutils}/bin/chmod a+w /sys/class/backlight/%k/brightness"
    +   '';
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
    udev is enabled by default.
    After connecting to internet and using # nixos-rebuild switch, and then rebooting, /sys/class/backlight/intel_backlight/brightness has permissions -rw-rw-rw-.
  2. To bind dynamic commands to keybinds to change screen brightness in Hyprland, edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        enable = true;
        settings = {
          bind = [
            # ...
          ];
    +     binde = [
    +       "SUPER, MINUS, exec, val=$(< /sys/class/backlight/intel_backlight/brightness); tee /sys/class/backlight/intel_backlight/brightness <<< $((val <= 4188 ? 188 : val - 4000))"
    +       "SUPER, EQUAL, exec, val=$(< /sys/class/backlight/intel_backlight/brightness); tee /sys/class/backlight/intel_backlight/brightness <<< $((val >= 20000 ? 24000 : val + 4000))"
    +     ];
          bindm = [
            # ...
          ];
          # ...
        };
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.firefox = {
        # ...
      };
    
      programs.home-manager.enable = true;
    }
  3. Connect to internet. Use # nixos-rebuild switch.

Using a Display (Login) Manager

Since I only have one user and one window manager or desktop environment, I do not need a graphical display manager, or any display manager at all.

  1. To automate typing in the username, edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        libinput.enable = true;
        pipewire.enable = true;
        udev.extraRules = ''
          # ...
        '';
    +   getty.extraArgs = [ "--skip-login" "--login-options" "tim" ];
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
    getty (get-tty) manages TTYs.
    services.getty.autologinUser is not used because it skips the password too.
  2. To start Hyprland automatically after login, edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        # ...
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.firefox = {
        # ...
      };
    
    + programs.zsh = {
    +   enable = true;
    +   profileExtra = ''
    +     if [ -z "''${DISPLAY}" ] && [ "''${XDG_VTNR}" -eq 1 ]; then
    +       exec Hyprland
    +     fi
    +   '';
    + };
    
      programs.home-manager.enable = true;
    }
    Note: Options similar to programs.zsh.profileExtra exist for configuration.nix, but they are system wide.
    Zsh executes a few files in a specific order on startup. the .zprofile file is executed on all login startups.
    -z checks for zero length; if Hyprland is already running, ${DISPLAY} will have length. In nix, $s inside multi-line strings can be escaped with ''. ${XDG_VTNR} indicates TTY#; -eq means "equal".
    Remove /home/tim/.zshrc, then connect to internet and use # nixos-rebuild switch.

Using swww

The swww wallpaper manager does not have a configuration file; all configurations are done through commands.

  1. To install swww, edit configuration.nix:

    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        systemPackages = with pkgs; [
          kitty
          capitaine-cursors
    +     swww
        ];
        sessionVariables.NIXOS_OZONE_WL = "1";
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
  2. Use $ mkdir /home/tim/Wallpapers to create a directory for the wallpapers.

  3. To autostart swww and automatically randomize wallpapers, edit home.nix:

    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        username = "tim";
        homeDirectory = "/home/tim";
        stateVersion = "24.05";
        pointerCursor = {
          # ...
        };
    +   file = {
    +     ".swwwRandomizer" = {
    +       enable = true;
    +       executable = true;
    +       text = ''
    +         #!/usr/bin/env zsh
    +         used=()
    +         while :; do
    +           all=(''$1/*)
    +           [[ ''$#used -eq ''$#all ]] && used=(''$selected)
    +           unused=(''${all:|used})
    +           selected=''${unused[''$RANDOM % ''$#unused + 1]}
    +           random_pos=(''$(seq 0.1 .1 0.9 | shuf))
    +           swww img ''$selected \
    +             --resize crop \
    +             -t grow \
    +             --transition-pos ''$random_pos[1],''$random_pos[2] \
    +             --transition-duration 4 \
    +             --transition-fps 60 \
    +             -f Nearest
    +           used+=(''$selected)
    +           sleep ''$2
    +         done
    +       '';
    +     };
    +   };
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        enable = true;
        settings = {
          # ...
          misc = {
            # ...
          };
    +     exec-once = [
    +       "swww-daemon --no-cache"
    +       "swww clear 000000 && ~/.swwwRandomizer /home/tim/Wallpapers 3600"
    +     ];
        };
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.firefox = {
        # ...
      };
    
      programs.zsh = {
        # ...
      };
    
      programs.home-manager.enable = true;
    }

    Home Manager's home.file.<name>.target option defines the path and name of the generated symlink or file relative to ~. This option defaults to ~/<name> when undefined.
    Although the zsh script can be run as a one-line command with $ zsh -c "command", using Hyprland's exec-once to execute the command on Hyprland startup does not seem to work. Also, the NixOS option writeShellApplication might be worth exploring for system wide scripts.
    Pseudocode for the script:

    1. Shebang for NixOS.
    2. Define used as an empty array.
      Putting spaces before and after the equal sign = would result in a syntax error.
    3. While true, loop:
      : stands for true. Semicolons ; are mandatory for compacting the script into a single line; they are optional with newlines.
      1. Define all as an array of files inside $1/*. $ indicates a variable. The variable 1 is the first argument passed to the script when it is run, like so: $ ~/.swwwRandomizer first_argument. Using $ ~/.swwwRandomizer /home/tim/Wallpapers simplifies to (/home/tim/Wallpapers/*). The wildcard * lists all files in the directory.
      2. Length of used equals length of all and redefine used as an array with only selected.
        Double brackets [[ ]] are used for testing conditions. # tests for the length of the array variable. &&, the logical AND, only runs the second command if the first returns true. selected is defined later in the script; putting it here will never cause an error unless all is defined as an empty array. Putting selected ensures that images never get picked twice in a row after all images have been used.
        Note: $#var works in zsh, but not bash. In bash, curly braces {} are required: ${#var}.
      3. Define unused as an array of all without used.
        {} are required for zsh to group commands. :| removes anything inside the second array from the first array. :| needs to be inside the parameter expansion ${}. Otherwise, :| in unused=($all:|$used) would be interpreted as a pipe operator followed by a colon.
      4. Define selected as the path of the (($RANDOM modulo length of unused) + 1)th file of unused.
        $RANDOM is a pre-defined command shell variable that returns a random integer between 0 and 32767. The modulo operator % returns the remainder from dividing $RANDOM by the length of unused. Since zsh indexing start at one, not zero, one is added to the remainder to accurately calculate the index, which is denoted with brackets [].
      5. Define random_pos as the array of the sequence of 0.1 to 0.9 with step 0.1 shuffled.
        The set of inner parentheses $() capture command substitution. The pipe operator | feeds the output of the first command as input to the second command.
      6. Execute swww img with the image with path selected.
        Back slashes \ are used to separate one line commands to multiple lines. Use swww img --help to see all image display and transition options. --resize fit shrinks the image until the entire image is displayed; --resize crop enlarges the image until the screen is filled. -t grow transition animation type is a growing circle. -t any randomizes the position of the circle, but it also randomizes between grow and outer (shrinking circle) animations, so it is not used. --transition-pos values separated by commas are float percentage values; arbitrarily the first and second floats of the shuffled random_pos array are used. -f Nearest is the fastest filter; best for pixel art.
      7. Append selected to the used array.
      8. Pause for variable 2 seconds.
    4. End of loop.

    The --no-cache flag is declared when starting swww-daemon because swww does not yet remember options such as filter and resize for cached images.
    Using $ swww-daemon for the first time on instillation or using $ swww-daemon --no-cache (to clear cache, use $ swww clear-cache) and then using $ swww img /path/to/any/image displays a black wallpaper. Using $ swww query shows that swww has attempted to display an image. Using any swww command to attempt to display something again fixes the black wallpaper. So, swww clear 000000 is executed once in the start to arbitrarily display black. Putting swww clear 000000 right after the shebang inside the script yields inconsistent results, indicating that the command is run too quickly, maybe before swww daemon has completely started, so && is necessary in exec-once.
    Connect to internet. Use # nixos-rebuild switch.

Customizing Zsh Prompt

Oh My Posh is used for making shells look better.

  1. To install Oh My Posh, edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        systemPackages = with pkgs; [
          kitty
          capitaine-cursors
          swww
    +     oh-my-posh
        ];
        sessionVariables.NIXOS_OZONE_WL = "1";
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
  2. Connect to internet. Use # nixos-rebuild switch.
  3. To use Home Manager to configure Oh My Posh, edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        # ... 
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.firefox = {
        # ...
      };
    
      programs.zsh = {
        # ...
      };
    
    + programs.oh-my-posh = {
    +   enable = true;
    +   settings = {
    +     "$schema" = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json";
    +     final_space = true;
    +     blocks = [
    +       {
    +         type = "prompt";
    +         alignment = "left";
    +         segments = [
    +           {
    +             type = "session";
    +             style = "plain";
    +             foreground = "white";
    +             template = "{{ .UserName }}";
    +           }
    +           {
    +             type = "path";
    +             style = "plain";
    +             foreground = "white";
    +             properties = {
    +               style = "full";
    +             };
    +           }
    +           {
    +             type = "text";
    +             style = "plain";
    +             foreground = "white";
    +             template = "❱";
    +           }
    +         ];
    +       }
    +       {
    +         type = "rprompt";
    +         alignment = "right";
    +         overflow = "break";
    +         segments = [
    +           {
    +             type = "executiontime";
    +             style = "plain";
    +             foreground = "white";
    +           }
    +         ];
    +       }
    +     ];
    +   };
    + };
    
      programs.home-manager.enable = true;
    }
    Note: While Oh My Posh's documentation suggests to edit .zshrc, Home Manager automatically does that. is \u2771; use $ echo "\u2771" to see the symbol.
  4. Connect to internet. Use # nixos-rebuild switch.

Controlling Audio & Bluetooth

For Linux systems, PipeWire is used for managing audio. PipeWire is more modern compared to PulseAudio and JACK. While services.pipewire.enable = true; is already in my configuration.nix, it is recommended to add some extra options.
BlueZ is the official standard bluetooth package.

  1. Edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        libinput.enable = true;
    -   pipewire.enable = true;
    +   pipewire = {
    +     enable = true;
    +     wireplumber.enable = true;
    +     pulse.enable = true;
    +     alsa.enable = true;
    +     alsa.support32Bit = true;
    +   };
        udev.extraRules = ''
          # ...
        '';
        # ...
      };
    
    + hardware = {
    +   bluetooth = {
    +     enable = true;
    +     powerOnBoot = true;
    +   };
    + };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
    WirePlumber is PipeWire's session manager, a daemon that manages PipeWire.
    services.pipewire.pulse.enable = true; allows PulseAudio applications to work with PipeWire. Likewise, ALSA configurations allow ALSA compatibility.
    hardware.bluetooth configures BlueZ. To use bluetooth, use $ bluetoothctl; use [bluetooth]# scan on, [bluetooth]# pair F8:4E:17:D3:E7:4A, and [bluetooth]# connect F8:4E:17:D3:E7:4A to connect to F8:4E:17:D3:E7:4A. Use [bluetooth]# trust F8:4E:17:D3:E7:4A to automatically connect. Using [bluetooth]# quit will not terminate the connection. Use $ bluetoothctl devices [Paired/Bonded/Trusted/Connected] to see paired, bonded, trusted, or connected devices.
  2. Connect to internet. Use # nixos-rebuild switch.
  3. To bind the buttons XF86AudioMute, XF86AudioLowerVolume, and XF86AudioRaiseVolume to WirePlumber commands to manage volume through Hyprland, edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        enable = true;
        settings = {
          bind = [
            # ...
          ];
          binde = [
            "SUPER, MINUS, exec, val=$(< /sys/class/backlight/intel_backlight/brightness); tee /sys/class/backlight/intel_backlight/brightness <<< $((val <= 4188 ? 188 : val - 4000))"
            "SUPER, EQUAL, exec, val=$(< /sys/class/backlight/intel_backlight/brightness); tee /sys/class/backlight/intel_backlight/brightness <<< $((val >= 20000 ? 24000 : val + 4000))"
    +       ", XF86AudioMute, exec, wpctl set-mute @DEFAULT_SINK@ toggle"
    +       ", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_SINK@ 2%-"
    +       ", XF86AudioRaiseVolume, exec, wpctl get-volume @DEFAULT_SINK@ | awk '{if ($2 < 2.20) system(\"wpctl set-volume @DEFAULT_SINK 2%+\")}'"
          ];
          bindm = [
            # ...
          ];
          # ...
        };
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.firefox = {
        # ...
      };
      
      programs.zsh = {
        # ...
      };
    
      programs.oh-my-posh = {
        # ...
      };
    
      programs.home-manager.enable = true;
    }
    Note: wpctl get-volume @DEFAULT_SINK@ | awk '{if ($2 < 2.20) system(\"wpctl set-volume @DEFAULT_SINK 2%+\")}' or $ wpctl get-volume @DEFAULT_SINK@ | awk '{if ($2 < 2.20) system("wpctl set-volume @DEFAULT_SINK@ 2%+")}' raises the volume by 0.02 if the volume is lower than 2.20. Through testing, it seems that while values can go above 2.20 to infinity, values above 2.20 do not actually make the speakers louder.
  4. Connect to internet. Use # nixos-rebuild switch.

Using Fonts

  1. Edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
    + fonts = {
    +   enableDefaultPackages = false;
    +   packages = with pkgs; [
    +     nerd-fonts.go-mono
    +     nerd-fonts.roboto-mono
    +     nerd-fonts.lilex
    +     nerd-fonts.dejavu-sans-mono
    +     noto-fonts
    +     twemoji-color-font
    +   ];
    +   fontconfig.defaultFonts = {
    +     serif = [ "Noto Serif" ];
    +     sansSerif = [ "Noto Sans" ];
    +     monospace = [ "DejaVuSansM Nerd Font Mono" ];
    +     emoji = [ "Twitter Color Emoji" ];
    +   };
    + };
    
      programs = {
        # ...
      };
    
      system.stateVersion = "24.05";
    }
    fonts.enableDefaultPackages installs some fonts like DejaVu; DejaVu was the font used by kitty before this configuration was set to false (use $ kitty +list-fonts to see the fonts available to kitty). If this configuration was set to false and no other fonts are in fonts.packages, then kitty's font rendering breaks.
    Names in fonts.fontconfig.defaultFonts are found by finding the TrueType file and using $ fc-query font.ttf | grep '^\s\+family:' | cut -d'"' -f2. To find the location of the TrueType file, use # find / -iname "*font*".
    Note: Many symbols in Nerd Fonts have \Uxxxxx unicodes like \Uf00af. Using $ echo "\uf00af" does not work since there are more than 4 characters in f00af. To see it in the terminal, either use $ echo "\Uf00af" (with uppercase U) or convert f00af. This is a python script that converts Nerd Font unicodes:
    try:
        hex_input = input("Enter code from Nerd Fonts (like 'f00af'): ").strip().lower()
        try:
            hex_input = int(hex_input, 16)
            char = chr(hex_input)
        except ValueError as e:
            raise ValueError(f"Invalid code point: {hex_input}") from e
        utf8_bytes = char.encode('utf-8')
        print(''.join([f'\\x{byte:02x}' for byte in utf8_bytes]))
    except ValueError as e:
        print(f"Error: {e}")
    For f00af, the script should output \xf3\xb0\x82\xaf. Use $ echo "\xf3\xb0\x82\xaf" and the symbol should render.
  2. Connect to internet. Use # nixos-rebuild switch.

Using Waybar (WIP)

  1. To install Waybar, edit configuration.nix:
    { config, lib, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      boot.loader = {
        # ...
      };
    
      nix.settings.experimental-features = [ "nix-command" "flakes" ];
    
      networking = {
        # ...
      };
    
      time.timeZone = "America/New_York";
    
      services = {
        # ...
      };
    
      security.rtkit.enable = true;
    
      xdg.portal = {
        # ...
      };
    
      users = {
        # ...
      };
    
      environment = {
        # ...
      };
    
      fonts = {
        # ...
      };
    
      programs = {
        # ...
        firefox = {
          # ...
        };
    
    +   waybar.enable = true;
      };
    
      system.stateVersion = "24.05";
    }
  2. Connect to internet. Use # nixos-rebuild switch.
  3. To use Home Manager to configure Waybar, edit home.nix:
    { config, lib, pkgs, ... }:
    
    let
      # ...
    
    in
    {
      home = {
        # ...
      };
    
      gtk = {
        # ...
      };
    
      wayland.windowManager.hyprland = {
        # ...
      };
    
      programs.kitty = {
        # ...
      };
    
      programs.firefox = {
        # ...
      };
    
      programs.zsh = {
        # ...
      };
    
      programs.oh-my-posh = {
        # ...
      };
    
    + programs.waybar = {
    +   enable = true;
    +   settings.bar = {
    +     layer = "top";
    +     position = "top";
    +     modules-left = [ "" ];
    +     modules-right = [ "" ];
    +     spacing = 0;
    +   };
    +   style = ''
    +     
    +   '';
    + };
    
      programs.home-manager.enable = true;
    }
    Note: bar in programs.waybar.settings.bar is an arbitrary name for the bar; Waybar supports having multiple bars at the same time, but I only need one bar. Waybar automatically starts on startup since it is inside $ systemctl --user list-units, so there needs to be neither Hyprland nor zsh configuration for autostarting Waybar. Automatically starting through systemctl though means that it does not have proper $PATH, meaning that it must use the full path of bluetoothctl (/run/current-system/sw/bin/bluetoothctl) to find bluetoothctl.
    \Uf057e \Uf0e08 is the muted icon. \Uf091f, \Uf0922,5,8, \Uf029e no wifi \Uf00af is the bluetooth icon, \Uf00b2 disabled, \Uf007a-f, \Uf0080-2, \Uf0079 are battery icons
  4. Connect to internet. Use # nixos-rebuild switch.

WIP

Using Python Environment Python and Python packages can be installed system wide, but using nix shells is recommended. Nix shells can:

  • Create isolated environments. Packages installed through the shell cannot be accessed outside the shell and are deleted when # nix-collect-garbage -d is used. Nix shells only effect packages and environment variables.
  • Create reproducible environments. Shells can be shared to create identical environments on different NixOS machines.

Nix shells can be started with commands ($ nix shell in flake systems) or more declaratively with shell.nix files for more configuration options.
Note: Nix shells are different from command shells (zsh, bash, etc...); they are all called shells because they function as wrappers. Flakes can be used with shell.nix to define the input nixpkgs channel.

All things in NixOS are packages. The system is a package that depends on whatever packages you install (like Firefox). Packages are built with stdenv.mkDerivation. mkShell is just a wrapper of stdenv.mkDerivation.

# nix-collect-garbage -d deletes generations and store objects.
$ nix-collect-garbage -d deletes home manager generations? https://discourse.nixos.org/t/home-manager-and-garbage-collection/41715

Finish Firefox config: stuff in about:preferences(included in about:config?) about:addons about:logins about:policies(done?) about:config, every rebuild home manager fails and have to remove .mozilla; fixable with https://discourse.nixos.org/t/way-to-automatically-override-home-manager-collisions/33038/6
Firefox css: https://www.reddit.com/r/FirefoxCSS/top/?t=all https://firefoxcss-store.github.io/index.html https://support.mozilla.org/en-US/kb/customize-your-new-tab-page#firefox:linux:fx129 https://support.mozilla.org/en-US/kb/customize-firefox-controls-buttons-and-toolbars#firefox:linux:fx129

Minimizing windows in Hyprland or just using workspaces? Minimizing: hyprwm/Hyprland#995 https://github.com/DreamMaoMao/hych/tree/main hyprwm/Hyprland#8281 https://wiki.hyprland.org/Configuring/Uncommon-tips--tricks/#minimize-windows-using-special-workspaces
Finish hyprland animation customization (layers, etc...)\

finish waybar config: TOOLTIPS CAN BE STYLED \

Properly manage colors:
Pywal alternative
https://sw.kovidgoyal.net/kitty/conf/#the-color-table
https://github.com/Misterio77/flavours
https://github.com/warpwm/lule + https://github.com/Misterio77/nix-colors?
https://github.com/danth/stylix is a bit magical, it might interfere with transparency settings and it sets the wallpaper, font, and cursor too? \

Home manage git
Zsh frameworks? oh my zsh, prezto, zinit, antigen,
Zsh plugins (plugin managers as well?)
Packages for terminal: fonts, improved ls find grep cat man etc...,
git integration?
terminal file manager (any file manager)?
system monitoring?

Microphone Control:
Keyboard Language Control: English, Chinese, Spanish. Remember to add bar indicator https://github.com/Alexays/Waybar/wiki/Module:-Hyprland#language Chinese needs an IME, https://discourse.nixos.org/t/how-to-setup-an-input-method-for-cjk-users/9533/3 https://discourse.nixos.org/t/pinyin-input-method-in-hyprland-wayland-for-simplified-chinese/49186/2 https://nixos.wiki/wiki/Fcitx5
Camera Control:
Clipboard Manager:
Screenshot Utility:
Text / Code Editor: Micro, neovim, helix, vscodium
Calculator:
Media Player:
Image Viewer:
WeChat
Stremio
Mullvad

Screen Locker:
Notification Daemon:
Application Launcher:
Authentication Agent: Starting method: manual (exec-once) Authentication agents are the things that pop up a window asking you for a password whenever an app wants to elevate its privileges.
https://gitlab.com/christosangel/ascii-matrix
Find wallpapers
Customize Grub

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks