WIP: nixos on laptop

This commit is contained in:
Faris 2026-06-03 20:34:44 +01:00
parent 07ae12fc30
commit 6a9bdab235
20 changed files with 1312 additions and 365 deletions

View File

@ -52,6 +52,8 @@
optimise.automatic = true; optimise.automatic = true;
}; };
security.apparmor.enable = true;
# unfree # unfree
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
@ -75,6 +77,7 @@
usbutils # lsusb usbutils # lsusb
wget wget
zsh zsh
bc
# media # media
yt-dlp yt-dlp
@ -114,6 +117,7 @@
".." = "cd .."; ".." = "cd ..";
"lsa" = "ls -al"; "lsa" = "ls -al";
"yt-dlp" = "yt-dlp --paths ~/Downloads";
}; };
environment.pathsToLink = [ environment.pathsToLink = [

View File

@ -16,20 +16,16 @@
hardware.bluetooth.enable = true; hardware.bluetooth.enable = true;
hardware.bluetooth.powerOnBoot = true; hardware.bluetooth.powerOnBoot = true;
services.libinput.enable = true; # hyprland and trackpad
# Enable CUPS to print documents. # Enable CUPS to print documents.
services.printing.enable = true; services.printing.enable = true; # x86 only
services.avahi = { services.avahi = {
enable = true; enable = true;
nssmdns4 = true; nssmdns4 = true;
openFirewall = true; openFirewall = true;
}; };
services.tailscale.enable = true; services.tailscale.enable = true;
services.printing.drivers = [
pkgs.brlaser
pkgs.brgenml1lpr
pkgs.brgenml1cupswrapper
];
# Enable automatic login for the user. # Enable automatic login for the user.
# services.displayManager.autoLogin.enable = true; # services.displayManager.autoLogin.enable = true;
@ -45,16 +41,17 @@
# "--config=/mnt/services/emulators/retroarch/retroarch.cfg" # "--config=/mnt/services/emulators/retroarch/retroarch.cfg"
#]; #];
services.ollama = { #x86 only
enable = true; #services.ollama = {
acceleration = "cuda"; # enable = true;
openFirewall = true; # acceleration = "cuda";
host = "0.0.0.0"; # openFirewall = true;
# Optional: preload models, see https://ollama.com/library # host = "0.0.0.0";
loadModels = [ "gemma4" ]; # # Optional: preload models, see https://ollama.com/library
package = pkgs.unstable.ollama; # loadModels = [ "gemma4" ];
# package = pkgs.unstable.ollama;
}; #};
programs.chromium = { programs.chromium = {
enable = true; enable = true;
@ -67,6 +64,8 @@
hardware.keyboard.qmk.enable = true; hardware.keyboard.qmk.enable = true;
services.logind.settings."Login"."HandlePowerKey" = "suspend";
boot = { boot = {
# Graphical boot # Graphical boot
plymouth.enable = true; plymouth.enable = true;
@ -100,64 +99,67 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
brave brave
firefox firefox
#blender blender
#audacity audacity
#orca-slicer orca-slicer
#gimp gimp
#inkscape inkscape
#unstable.protonmail-desktop #unstable.protonmail-desktop
#bitwarden-desktop bitwarden-desktop
#libreoffice libreoffice
#freecad freecad
#kiwix kiwix
#obs-studio obs-studio
#obsidian obsidian
unstable.proton-authenticator #unstable.proton-authenticator #x86 only
#prismlauncher prismlauncher
#unstable.signal-desktop unstable.signal-desktop
tor-browser #tor-browser #x86 only
#wireshark wireshark
#monero-gui monero-gui
#moonlight-qt moonlight-qt
nvtopPackages.nvidia #nvtopPackages.nvidia
dconf-editor dconf-editor
#kdePackages.kdenlive kdePackages.kdenlive
#element-desktop element-desktop
#chirp chirp
scrcpy scrcpy
veracrypt veracrypt
#lmms lmms
restic-browser restic-browser
# games # games
#cataclysm-dda gnome-2048
#endless-sky gnome-chess
#mindustry cataclysm-dda
#openrct2 endless-sky
#openttd #mindustry #x86 only
#osu-lazer-bin openrct2
#runelite openttd
#unciv #osu-lazer-bin #x86 only
#webcord #runelite #x86 only
#wesnoth unciv
#xonotic webcord
#zeroad #0ad wesnoth
xonotic
zeroad # 0ad
# game utils # game utils
mangohud mangohud
umu-launcher #umu-launcher #x86 only
# emu # emu
#ryubing ryubing
#xemu xemu
xenia-canary #xenia-canary #86 only
#retroarch-full #retroarch-full
#rpcs3 rpcs3
#dolphin-emu dolphin-emu
#cemu #cemu #x86 only
# backup # backup
#keepassxc keepassxc
]; ];
fonts.fontconfig = { fonts.fontconfig = {
@ -167,47 +169,45 @@
hinting.autohint = true; hinting.autohint = true;
}; };
services.sunshine = { #services.sunshine = {
enable = true; # enable = true;
autoStart = true; # autoStart = true;
capSysAdmin = true; # capSysAdmin = true;
openFirewall = true; # openFirewall = true;
package = pkgs.sunshine.override { # package = pkgs.sunshine.override {
cudaSupport = true; # cudaSupport = true;
}; # };
}; #};
services.hardware.openrgb.enable = true; #services.hardware.openrgb.enable = true;
services.hardware.openrgb.package = pkgs.openrgb-with-all-plugins; #services.hardware.openrgb.package = pkgs.openrgb-with-all-plugins;
programs = { programs = {
adb.enable = true; adb.enable = true;
localsend = { localsend = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
}; };
coolercontrol = { #coolercontrol = {
enable = true; # enable = true;
}; #};
gamescope = { #gamescope = {
enable = true; # enable = true;
capSysNice = true; # capSysNice = true;
}; #};
steam = { #steam = { #x86 only
enable = true; # enable = true;
gamescopeSession.enable = true; # gamescopeSession.enable = true;
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play # remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server # dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers # localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers
}; #};
gamemode.enable = true; #gamemode.enable = true;
}; };
fileSystems."/mnt/media" = { fileSystems."/mnt/media" = {
@ -260,53 +260,52 @@
enable = true; enable = true;
onCalendar = "weekly"; # Default value onCalendar = "weekly"; # Default value
}; };
packages = [ # packages = [
"com.bitwarden.desktop" # "com.bitwarden.desktop"
"com.orcaslicer.OrcaSlicer" # "com.orcaslicer.OrcaSlicer"
"me.proton.Mail" # "me.proton.Mail"
"org.audacityteam.Audacity" # "org.audacityteam.Audacity"
"org.blender.Blender" # "org.blender.Blender"
"org.freecad.FreeCAD" # "org.freecad.FreeCAD"
"org.gimp.GIMP" # "org.gimp.GIMP"
"org.inkscape.Inkscape" # "org.inkscape.Inkscape"
"org.kiwix.desktop" # "org.kiwix.desktop"
"org.libreoffice.LibreOffice" # "org.libreoffice.LibreOffice"
"md.obsidian.Obsidian" # "md.obsidian.Obsidian"
"com.obsproject.Studio" # "com.obsproject.Studio"
"org.prismlauncher.PrismLauncher" # "org.prismlauncher.PrismLauncher"
"org.signal.Signal" # "org.wireshark.Wireshark"
"org.wireshark.Wireshark" # "org.getmonero.Monero"
"org.getmonero.Monero" # "com.moonlight_stream.Moonlight"
"com.moonlight_stream.Moonlight" # "org.kde.kdenlive"
"org.kde.kdenlive" # "im.riot.Riot"
"im.riot.Riot" # "com.chirpmyradio.chirp"
"com.chirpmyradio.chirp" # "io.lmms.LMMS"
"io.lmms.LMMS"
# games # # games
"com.github.Anuken.Mindustry" # "com.github.Anuken.Mindustry"
"com.play0ad.zeroad" # "com.play0ad.zeroad"
"io.github.endless_sky.endless_sky" # "io.github.endless_sky.endless_sky"
"io.github.spacingbat3.webcord" # "io.github.spacingbat3.webcord"
"io.github.yairm210.unciv" # "io.github.yairm210.unciv"
"io.openrct2.OpenRCT2" # "io.openrct2.OpenRCT2"
"net.runelite.RuneLite" # "net.runelite.RuneLite"
"org.cataclysmdda.CataclysmDDA" # "org.cataclysmdda.CataclysmDDA"
"org.openttd.OpenTTD" # "org.openttd.OpenTTD"
"org.wesnoth.Wesnoth" # "org.wesnoth.Wesnoth"
"org.xonotic.Xonotic" # "org.xonotic.Xonotic"
"sh.ppy.osu" # "sh.ppy.osu"
#
# # emu
# "io.github.ryubing.Ryujinx"
# "app.xemu.xemu"
# "net.rpcs3.RPCS3"
# "org.DolphinEmu.dolphin-emu"
# "info.cemu.Cemu"
# emu # # backup
"io.github.ryubing.Ryujinx" # "org.keepassxc.KeePassXC"
"app.xemu.xemu" #
"net.rpcs3.RPCS3" # ];
"org.DolphinEmu.dolphin-emu"
"info.cemu.Cemu"
# backup
"org.keepassxc.KeePassXC"
];
}; };
} }

View File

@ -25,14 +25,25 @@
tesseract # ocr tesseract # ocr
hyprpicker hyprpicker
satty # image annotation satty # image annotation
brightnessctl
rofimoji
# dev
jq # for scripts
# gnome stuff # gnome stuff
eog # eye of gnome
gnome-calculator gnome-calculator
nautilus
gnome-font-viewer
gnome-calendar gnome-calendar
gnome-clocks
gnome-contacts
gnome-font-viewer
gnome-weather
nautilus
]; ];
services.upower.enable = true; # battery stuff
services.udisks2.enable = true; # udiskie automount USB services.udisks2.enable = true; # udiskie automount USB
} }

BIN
firmware/all_firmware.tar.gz Executable file

Binary file not shown.

Binary file not shown.

37
flake.lock generated
View File

@ -1,5 +1,41 @@
{ {
"nodes": { "nodes": {
"apple-silicon-support": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1779284798,
"narHash": "sha256-D0I5TMDo8n1EvVc0d1dshsA+hzFRNxgx9YyMmkWMy+w=",
"owner": "nix-community",
"repo": "nixos-apple-silicon",
"rev": "f2506ef24a154e2bcc8fb10f2c73579324f9ead0",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-apple-silicon",
"type": "github"
}
},
"flake-compat": {
"locked": {
"lastModified": 1761640442,
"narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=",
"owner": "nix-community",
"repo": "flake-compat",
"rev": "4a56054d8ffc173222d09dad23adf4ba946c8884",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@ -181,6 +217,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"apple-silicon-support": "apple-silicon-support",
"home-manager": "home-manager", "home-manager": "home-manager",
"home-manager-unstable": "home-manager-unstable", "home-manager-unstable": "home-manager-unstable",
"lazyvim": "lazyvim", "lazyvim": "lazyvim",

View File

@ -13,6 +13,10 @@
nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
lazyvim.url = "github:pfassina/lazyvim-nix"; lazyvim.url = "github:pfassina/lazyvim-nix";
nix-flatpak.url = "github:gmodena/nix-flatpak"; nix-flatpak.url = "github:gmodena/nix-flatpak";
apple-silicon-support = {
url = "github:nix-community/nixos-apple-silicon";
inputs.nixpkgs.follows = "nixpkgs"; # avoids a second nixpkgs copy
};
}; };
outputs = outputs =
@ -24,6 +28,7 @@
nix-darwin, nix-darwin,
lazyvim, lazyvim,
nix-flatpak, nix-flatpak,
apple-silicon-support,
... ...
}@inputs: }@inputs:
{ {
@ -103,6 +108,42 @@
} }
]; ];
}; };
nixosConfigurations.macbook-air = nixpkgs.lib.nixosSystem {
specialArgs = {
inherit self inputs;
inherit nix-flatpak;
hostname = "macbook-air";
};
#inherit system;
modules = [
./config/common.nix
./config/linux.nix
./config/desktop.nix
./config/hyprland.nix
./hosts/macbook-air/hardware.nix
./hosts/macbook-air/settings.nix
apple-silicon-support.nixosModules.apple-silicon-support
{ hardware.asahi.peripheralFirmwareDirectory = "${self}/firmware"; }
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = {
inherit lazyvim;
inherit inputs;
hostname = "macbook-air";
};
home-manager.users.admin = {
imports = [
./home/common.nix
./home/desktop.nix
./home/hyprland.nix
];
};
}
];
};
}; };
} }

View File

@ -1,4 +1,9 @@
{ pkgs, hostname, ... }: {
config,
pkgs,
hostname,
...
}:
let let
nix_background = "rgb(16, 15, 15)"; nix_background = "rgb(16, 15, 15)";
nix_text = "rgb(206, 205, 195)"; nix_text = "rgb(206, 205, 195)";
@ -9,7 +14,7 @@ let
nix_font = "JetBrainsMono Nerd Font"; nix_font = "JetBrainsMono Nerd Font";
nix_border_size = 3; nix_border_size = 3;
nix_corner = 10; nix_corner = 10;
nix_bar_height = 30; nix_bar_height = if (hostname == "macbook-air") then 38 else 26;
in in
{ {
home.pointerCursor = { home.pointerCursor = {
@ -23,6 +28,16 @@ in
# gtk = { # gtk = {
# enable = true; # enable = true;
# }; # };
#
#
xdg.portal = {
enable = true;
extraPortals = [
pkgs.xdg-desktop-portal-gtk
pkgs.xdg-desktop-portal-hyprland
];
};
services.udiskie.enable = true; # automount USB services.udiskie.enable = true; # automount USB
@ -97,8 +112,7 @@ in
"~/Downloads/wall.jpg" "~/Downloads/wall.jpg"
]; ];
wallpaper = [ wallpaper = [
"DP-2,~/Downloads/wall.jpg" ",~/Downloads/wall.jpg"
"HDMI-A-1,~/Downloads/wall.jpg"
]; ];
}; };
}; };
@ -118,11 +132,13 @@ in
gaps = true; gaps = true;
gap_size = 15; gap_size = 15;
corner_radius = nix_corner; corner_radius = nix_corner;
layer = "top";
}; };
urgency_critical = { urgency_critical = {
frame_color = nix_alert; frame_color = nix_alert;
timeout = 0; timeout = 0;
layer = "overlay"; # show above fullscreen apps
}; };
}; };
@ -181,7 +197,7 @@ in
border-radius: 0; border-radius: 0;
min-height: 0; min-height: 0;
font-family: '${nix_font}'; font-family: '${nix_font}';
font-size: 14px; /* up from 12px */ font-size: 12px; /* up from 12px */
} }
.modules-left { .modules-left {
@ -211,7 +227,6 @@ in
#cpu, #cpu,
#battery, #battery,
#pulseaudio, #pulseaudio,
#custom-power,
#idle_inhibitor, #idle_inhibitor,
#bluetooth, #bluetooth,
#privacy, #privacy,
@ -269,7 +284,7 @@ in
min-width: 12px; min-width: 12px;
margin-left: 5px; margin-left: 5px;
margin-right: 0; margin-right: 0;
font-size: 10px; font-size: 9px;
padding-bottom: 1px; padding-bottom: 1px;
color: #ffffff; color: #ffffff;
} }
@ -291,7 +306,11 @@ in
"hyprland/workspaces" "hyprland/workspaces"
"hyprland/submap" "hyprland/submap"
]; ];
modules-center = [ modules-center =
if hostname == "macbook-air" then
[ ]
else
[
"clock" "clock"
"idle_inhibitor" "idle_inhibitor"
]; ];
@ -301,13 +320,64 @@ in
"bluetooth" "bluetooth"
"network" "network"
"pulseaudio" "pulseaudio"
"custom/power" "battery"
"privacy" "privacy"
]; ]
++ (
if hostname == "macbook-air" then
[
"clock"
"idle_inhibitor"
]
else
[ ]
);
privacy = { privacy = {
icon-size = 14; icon-size = 14;
}; };
battery = {
format = "{capacity}% {icon}";
format-discharging = "{icon}";
format-charging = "{icon}";
format-plugged = "";
format-icons = {
charging = [
"󰢜"
"󰂆"
"󰂇"
"󰂈"
"󰢝"
"󰂉"
"󰢞"
"󰂊"
"󰂋"
"󰂅"
];
default = [
"󰁺"
"󰁻"
"󰁼"
"󰁽"
"󰁾"
"󰁿"
"󰂀"
"󰂁"
"󰂂"
"󰁹"
];
};
format-full = "󰂅";
tooltip-format-discharging = "{power:>1.0f}W {capacity}%";
tooltip-format-charging = "{power:>1.0f}W {capacity}%";
interval = 5;
#on-click = "omarchy-menu power";
#on-click-right = ''notify-send -u low "$(omarchy-battery-status)"'';
states = {
warning = 20;
critical = 10;
};
};
"hyprland/workspaces" = { "hyprland/workspaces" = {
on-click = "activate"; on-click = "activate";
@ -352,14 +422,9 @@ in
on-click = "hyprctl switchxkblayout monsgeek-monsgeek-keyboard next"; on-click = "hyprctl switchxkblayout monsgeek-monsgeek-keyboard next";
}; };
"custom/power" = {
format = "";
on-click = "hyprlock & systemctl suspend";
};
"custom/menu" = { "custom/menu" = {
format = ""; format = "";
on-click = "walker"; on-click = "rofi -show drun";
}; };
"custom/expand" = { "custom/expand" = {
@ -408,6 +473,7 @@ in
clock = { clock = {
format = "{:%a %b %d %H:%M}"; format = "{:%a %b %d %H:%M}";
tooltip-format = "{calendar}"; tooltip-format = "{calendar}";
on-click = "for i in {1..10}; do dunstctl history-pop; done; sleep 5 && dunstctl close-all"; # show last 10 notifications for 5 seconds
}; };
idle_inhibitor = { idle_inhibitor = {
@ -422,8 +488,8 @@ in
format = "{icon}"; format = "{icon}";
format-muted = ""; format-muted = "";
format-icons = { format-icons = {
headphone = ""; #headphone = "";
headset = ""; #headset = "";
default = [ default = [
"" ""
"" ""
@ -439,156 +505,83 @@ in
}; };
}; };
}; };
services.walker = {
enable = true;
systemd.enable = true;
settings = {
app_launch_prefix = "";
as_window = false;
close_when_open = false;
disable_click_to_close = false;
force_keyboard_focus = false;
hotreload_theme = false;
locale = "";
monitor = "";
terminal_title_flag = "";
timeout = 0;
};
theme.name = "my-theme";
theme.style = ''
@define-color background ${nix_background};
@define-color foreground ${nix_text};
@define-color selected-text ${nix_accent};
@define-color base ${nix_background};
@define-color border ${nix_accent};
@define-color text ${nix_text};
* {
all: unset;
}
* {
font-family: monospace;
font-size: 18px;
color: @text;
}
scrollbar {
opacity: 0;
}
.normal-icons {
-gtk-icon-size: 16px;
}
.large-icons {
-gtk-icon-size: 32px;
}
.box-wrapper {
background: alpha(@base, 0.95);
padding: 20px;
border: 2px solid @border;
}
.preview-box {
}
.box {
}
.search-container {
background: @base;
padding: 10px;
}
.input placeholder {
opacity: 0.5;
}
.input {
}
.input:focus,
.input:active {
box-shadow: none;
outline: none;
}
.content-container {
}
.placeholder {
}
.scroll {
}
.list {
}
child,
child > * {
}
child:hover .item-box {
}
child:selected .item-box {
}
child:selected .item-box * {
color: @selected-text;
}
child:selected {
background: alpha(@text, 0.07);
}
.item-box {
padding-left: 14px;
}
.item-text-box {
all: unset;
padding: 14px 0;
}
.item-text {
}
.item-subtext {
font-size: 0px;
min-height: 0px;
margin: 0px;
padding: 0px;
}
.item-image {
margin-right: 14px;
-gtk-icon-transform: scale(0.9);
}
.current {
font-style: italic;
}
.keybind-hints {
background: @background;
padding: 10px;
margin-top: 10px;
}
.preview {
}
'';
};
programs.rofi = { programs.rofi = {
enable = true; enable = true;
font = "${nix_font} 14"; font = "${nix_font} 14";
plugins = [ plugins = [
pkgs.rofimoji pkgs.rofi-calc
]; ];
#theme = let
# inherit (config.lib.formats.rasi) mkLiteral;
#in {
# "*" = {
# background-color = mkLiteral "#1e1e2e";
# foreground-color = mkLiteral "#cdd6f4";
# border-color = mkLiteral "#89b4fa";
# font = "JetBrains Mono Nerd Font 12";
# padding = mkLiteral "0px";
# spacing = 0;
# };
# "#window" = {
# background-color = mkLiteral "@background-color";
# border = mkLiteral "2px";
# border-color = mkLiteral "@border-color";
# border-radius = mkLiteral "8px";
# width = mkLiteral "40%";
# location = mkLiteral "center";
# };
# "#mainbox" = {
# background-color = mkLiteral "transparent";
# padding = mkLiteral "12px";
# spacing = mkLiteral "8px";
# };
# "#inputbar" = {
# background-color = mkLiteral "#313244";
# border-radius = mkLiteral "6px";
# padding = mkLiteral "8px 12px";
# spacing = mkLiteral "8px";
# children = map mkLiteral [ "prompt" "entry" ];
# };
# "#prompt" = {
# background-color = mkLiteral "transparent";
# foreground-color = mkLiteral "@border-color";
# };
# "#entry" = {
# background-color = mkLiteral "transparent";
# foreground-color = mkLiteral "@foreground-color";
# placeholder = "Search...";
# placeholder-color = mkLiteral "#6c7086";
# };
# "#listview" = {
# background-color = mkLiteral "transparent";
# lines = 8;
# columns = 1;
# spacing = mkLiteral "4px";
# scrollbar = false;
# };
# "#element" = {
# background-color = mkLiteral "transparent";
# foreground-color = mkLiteral "@foreground-color";
# border-radius = mkLiteral "6px";
# padding = mkLiteral "8px 12px";
# };
# "#element.selected" = {
# background-color = mkLiteral "#89b4fa";
# foreground-color = mkLiteral "#1e1e2e";
# };
# "#element.alternate" = {
# background-color = mkLiteral "transparent";
# };
#};
}; };
programs.hyprlock = { programs.hyprlock = {
enable = true; enable = true;
@ -640,12 +633,12 @@ in
listener = [ listener = [
{ {
timeout = 300; timeout = if (hostname == "macbook-air") then 120 else 300;
on-timeout = "loginctl lock-session & hyprctl dispatch dpms off"; on-timeout = "loginctl lock-session & hyprctl dispatch dpms off";
on-resume = "hyprctl dispatch dpms on"; on-resume = "hyprctl dispatch dpms on";
} }
{ {
timeout = 900; timeout = if (hostname == "macbook-air") then 130 else 900;
on-timeout = "systemctl suspend"; on-timeout = "systemctl suspend";
on-resume = "hyprctl dispatch dpms on"; on-resume = "hyprctl dispatch dpms on";
} }
@ -653,6 +646,221 @@ in
}; };
}; };
xdg.configFile."hypr/scripts/battery-monitor.sh" = {
executable = true;
text = ''
BATTERY_THRESHOLD=35
NOTIFICATION_FLAG="/run/user/$UID/battery_notified"
BATTERY_LEVEL=$(upower -i $(upower -e | grep -i 'BAT') | awk '/percentage/ { print int($2); exit }')
BATTERY_STATE=$(upower -i $(upower -e | grep -i 'BAT') | grep -E "state" | awk '{print $2}')
send_notification() {
notify-send -u critical "󱐋 Time to recharge!" "Battery is down to $\{1}%" -i battery-caution -t 30000
}
if [[ -n $BATTERY_LEVEL && $BATTERY_LEVEL =~ ^[0-9]+$ ]]; then
if [[ $BATTERY_STATE == "discharging" ]] && (( BATTERY_LEVEL <= BATTERY_THRESHOLD )); then
if [[ ! -f $NOTIFICATION_FLAG ]]; then
send_notification $BATTERY_LEVEL
touch $NOTIFICATION_FLAG
fi
else
rm -f $NOTIFICATION_FLAG
fi
fi
'';
};
systemd.user.services.battery-monitor = {
Unit = {
Description = "Battery level monitor";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${config.home.homeDirectory}/.config/hypr/scripts/battery-monitor.sh";
};
};
systemd.user.timers.battery-monitor = {
Unit.Description = "Run battery monitor periodically";
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "1min";
Unit = "battery-monitor.service";
};
Install.WantedBy = [ "timers.target" ];
};
xdg.configFile."hypr/scripts/osd.sh" = {
executable = true;
text = ''
# See README.md for usage instructions
volume_step=5
brightness_step="5%"
max_volume=100
notification_timeout=1000
download_album_art=true
show_album_art=true
show_music_in_volume_indicator=true
show_percentages=false
# wpctl outputs "Volume: 0.75", so we multiply by 100 to get an integer percentage
function get_volume {
wpctl get-volume @DEFAULT_SINK@ | awk '{printf "%d", $2 * 100}'
}
# wpctl appends "[MUTED]" to the output when muted
function get_mute {
wpctl get-volume @DEFAULT_SINK@ | grep -q '\[MUTED\]' && echo "yes" || echo "no"
}
# Uses regex to get brightness from brightnessctl
function get_brightness {
brightnessctl | grep -oP '\d+(?=%)' | head -n 1
}
# Returns a mute icon, a volume-low icon, or a volume-high icon, depending on the volume
function get_volume_icon {
volume=$(get_volume)
mute=$(get_mute)
if [ "$volume" -eq 0 ] || [ "$mute" == "yes" ]; then
volume_icon=" "
elif [ "$volume" -lt 50 ]; then
volume_icon=" "
else
volume_icon=" "
fi
}
# Always returns the same icon
function get_brightness_icon {
brightness_icon=" "
}
function get_album_art {
url=$(playerctl -f "{{mpris:artUrl}}" metadata)
if [[ $url == "file://"* ]]; then
album_art="$\{url/file:\/\//}"
elif [[ $url == "http://"* || $url == "https://"* ]] && [[ $download_album_art == "true" ]]; then
filename="$(echo $url | sed "s/.*\///")"
if [ ! -f "/tmp/$filename" ]; then
wget -O "/tmp/$filename" "$url"
fi
album_art="/tmp/$filename"
else
album_art=""
fi
}
# Displays a volume notification
function show_volume_notif {
volume=$(get_volume)
get_volume_icon
if [[ $show_percentages == "true" ]]; then
volume_label="$volume_icon $volume%"
else
volume_label="$volume_icon"
fi
if [[ $show_music_in_volume_indicator == "true" ]]; then
current_song=$(playerctl -f "{{title}} - {{artist}}" metadata)
if [[ $show_album_art == "true" ]]; then
get_album_art
fi
notify-send -t $notification_timeout -h string:x-dunst-stack-tag:volume_notif -h int:value:$volume -i "$album_art" "$volume_label" "$current_song"
else
notify-send -t $notification_timeout -h string:x-dunst-stack-tag:volume_notif -h int:value:$volume "$volume_label"
fi
}
# Displays a music notification
function show_music_notif {
song_title=$(playerctl -f "{{title}}" metadata)
song_artist=$(playerctl -f "{{artist}}" metadata)
#song_album=$(playerctl -f "{{album}}" metadata)
if [[ $show_album_art == "true" ]]; then
get_album_art
fi
#notify-send -t $notification_timeout -h string:x-dunst-stack-tag:music_notif -i "$album_art" "$song_title" "$song_artist - $song_album"
notify-send -t $notification_timeout -h string:x-dunst-stack-tag:music_notif -i "$album_art" "$song_title" "$song_artist"
}
# Displays a brightness notification
function show_brightness_notif {
brightness=$(get_brightness)
get_brightness_icon
if [[ $show_percentages == "true" ]]; then
brightness_label="$brightness_icon $brightness%"
else
brightness_label="$brightness_icon"
fi
notify-send -t $notification_timeout -h string:x-dunst-stack-tag:brightness_notif -h int:value:$brightness "$brightness_label"
}
# Main function - Takes user input, "volume_up", "volume_down", "brightness_up", or "brightness_down"
case $1 in
volume_up)
wpctl set-mute @DEFAULT_SINK@ 0
volume=$(get_volume)
if [ $(( "$volume" + "$volume_step" )) -gt $max_volume ]; then
wpctl set-volume @DEFAULT_SINK@ $(echo "$max_volume / 100" | bc -l)
else
wpctl set-volume @DEFAULT_SINK@ $(echo "$volume_step / 100" | bc -l)+
fi
show_volume_notif
;;
volume_down)
wpctl set-volume @DEFAULT_SINK@ $(echo "$volume_step / 100" | bc -l)-
show_volume_notif
;;
volume_mute)
wpctl set-mute @DEFAULT_SINK@ toggle
show_volume_notif
;;
brightness_up)
brightnessctl set +$brightness_step
show_brightness_notif
;;
brightness_down)
brightnessctl set $brightness_step-
show_brightness_notif
;;
next_track)
playerctl next
sleep 0.5 && show_music_notif
;;
prev_track)
playerctl previous
sleep 0.5 && show_music_notif
;;
play_pause)
playerctl play-pause
show_music_notif
;;
esac
'';
};
xdg.configFile."hypr/scripts/media.sh" = { xdg.configFile."hypr/scripts/media.sh" = {
executable = true; executable = true;
text = '' text = ''
@ -696,18 +904,6 @@ in
MODE=$1 MODE=$1
FILE=$(mktemp /tmp/screenshot-XXXXXX.png) FILE=$(mktemp /tmp/screenshot-XXXXXX.png)
grim -g "$(slurp)" "$FILE" || exit 1
wl-copy < "$FILE"
ACTION=$(dunstify "Screenshot taken" "Copied to clipboard" \
--icon="$FILE" \
--action="open,Open in Satty" \
--timeout=5000)
[ "$ACTION" = "2" ] && satty --filename "$FILE"
FILE=$(mktemp /tmp/screenshot-XXXXXX.png)
case "$MODE" in case "$MODE" in
region) region)
grim -g "$(slurp)" "$FILE" || exit 1 grim -g "$(slurp)" "$FILE" || exit 1
@ -741,6 +937,7 @@ in
enable = true; enable = true;
plugins = [ plugins = [
pkgs.hyprlandPlugins.hyprbars pkgs.hyprlandPlugins.hyprbars
pkgs.hyprlandPlugins.hyprspace
]; ];
settings = { settings = {
# This is an example Hyprland config file for Nix. # This is an example Hyprland config file for Nix.
@ -765,17 +962,19 @@ in
plugin = { plugin = {
hyprbars = { hyprbars = {
bar_height = nix_bar_height; bar_height = if (hostname == "macbook-air") then 30 else nix_bar_height;
hyprbars-button = [ hyprbars-button = [
"rgb(ff605C), 16, 󰖭, hyprctl dispatch killactive" "rgb(ff605C), 15, 󰖭, hyprctl dispatch killactive"
"rgb(00CA4E), 16, 󰊔, hyprctl dispatch fullscreen 1" "rgb(00CA4E), 15, 󰊔, hyprctl dispatch fullscreen 1"
"rgb(FFBD44), 16, , hyprctl dispatch togglefloating" "rgb(FFBD44), 15, , hyprctl dispatch togglefloating"
]; ];
on_double_click = "hyprctl dispatch fullscreen 1"; on_double_click = "hyprctl dispatch fullscreen 1";
icon_on_hover = true; icon_on_hover = true;
bar_color = nix_background; bar_color = nix_background;
"col.text" = nix_text; "col.text" = nix_text;
bar_buttons_alignment = "right"; bar_text_font = nix_font;
bar_text_size = 9;
bar_buttons_alignment = "left";
}; };
}; };
@ -791,13 +990,17 @@ in
"DP-2, 2560x1440@180,0x0,1,vrr,1,bitdepth,10" "DP-2, 2560x1440@180,0x0,1,vrr,1,bitdepth,10"
"HDMI-A-1, 1920x1080@75,2560x0,1,vrr,1" "HDMI-A-1, 1920x1080@75,2560x0,1,vrr,1"
] ]
else if (hostname == "macbook-air") then
[
"eDP-1, 2560x1660@60, 0x0, 1.6"
]
else else
[ [
",preferred,auto,auto" ",preferred,auto,auto"
]; ];
################### ###################
### MY PROGRAMS ### ### MY PROGRAMS ###0
################### ###################
# See https://wiki.hypr.land/Configuring/Keywords/ # See https://wiki.hypr.land/Configuring/Keywords/
@ -805,9 +1008,9 @@ in
# Set programs that you use # Set programs that you use
"$terminal" = "alacritty"; "$terminal" = "alacritty";
"$fileManager" = "dolphin"; "$fileManager" = "nautilus";
"$menu" = "walker"; "$menu" = "rofi -show drun";
"$browser" = "brave"; "$browser" = "brave --ozone-platform=wayland --enable-features=TouchpadOverscrollHistoryNavigation";
################# #################
### AUTOSTART ### ### AUTOSTART ###
@ -820,6 +1023,7 @@ in
"waybar" "waybar"
"steam -silent" "steam -silent"
"webcord --start-minimized" "webcord --start-minimized"
"signal-desktop --start-in-tray"
#"[workspace 1 silent]$browser" #"[workspace 1 silent]$browser"
#"[workspace 3 silent]proton-mail" #"[workspace 3 silent]proton-mail"
@ -864,8 +1068,8 @@ in
# https://wiki.hypr.land/Configuring/Variables/#general # https://wiki.hypr.land/Configuring/Variables/#general
general = { general = {
gaps_in = 10; gaps_in = if (hostname == "macbook-air") then 5 else 10;
gaps_out = 20; gaps_out = if (hostname == "macbook-air") then 10 else 20;
border_size = nix_border_size; border_size = nix_border_size;
@ -937,8 +1141,8 @@ in
"blur, notifications" "blur, notifications"
"ignorezero, notifications" "ignorezero, notifications"
# turn off anim for walker # turn off anim for rofi
"noanim, walker" "noanim, rofi"
]; # add blur to notifications ]; # add blur to notifications
@ -1020,21 +1224,27 @@ in
accel_profile = "flat"; accel_profile = "flat";
touchpad = { touchpad = {
scroll_factor = 1.0;
disable_while_typing = false; disable_while_typing = false;
natural_scroll = false; natural_scroll = true;
tap-to-click = false;
clickfinger_behavior = true;
}; };
}; };
gestures = {
workspace_swipe_distance = 150;
workspace_swipe_touch = true;
};
# https://wiki.hypr.land/Configuring/Variables/#gestures # https://wiki.hypr.land/Configuring/Variables/#gestures
#gestures = { gesture = [ "3, horizontal, workspace" ];
# workspace_swipe = false;
#};
# Example per-device config # Example per-device config
# See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more # See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more
device = { device = {
name = "epic-mouse-v1"; name = "apple-mtp-multi-touch";
sensitivity = -0.5; accel_profile = "";
sensitivity = -0.1;
}; };
################### ###################
@ -1047,20 +1257,23 @@ in
bind = [ bind = [
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more # Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
"$mainMod, Return, exec, $terminal" "$mainMod, Return, exec, $terminal"
"$mainMod, Q, killactive," "$mainMod, C, killactive,"
"alt, F4, killactive," "alt, F4, killactive,"
"$mainMod SHIFT, Q, exit," "$mainMod SHIFT, Q, exit,"
"$mainMod, E, exec, $fileManager" "$mainMod, E, exec, $fileManager"
"$mainMod, S, exec, hyprctl clients -j | jq -e '.[] | select(.class == \"signal\")' > /dev/null && hyprctl dispatch focuswindow class:signal || signal-desktop"
"$mainMod, D, exec, hyprctl clients -j | jq -e '.[] | select(.class == \"webcord\")' > /dev/null && hyprctl dispatch focuswindow class:webcord || webcord"
"$mainMod, V, togglefloating," "$mainMod, V, togglefloating,"
"$mainMod, F, fullscreen, 3" "$mainMod, F, fullscreen, 3"
"$mainMod, M, fullscreen, 1" "$mainMod, M, fullscreen, 1"
"$mainMod, W, exec, $browser" "$mainMod, W, exec, $browser"
"$mainMod, space, exec, $menu" "$mainMod, space, exec, $menu"
#"$mainMod, P, pseudo," # dwindle #"$mainMod, P, pseudo," # dwindle
"$mainMod, T, togglesplit," # dwindle "$mainMod, T, togglesplit," # dwindle
"$mainMod SHIFT, L, exec, hyprlock" #"$mainMod SHIFT, L, exec, hyprlock& systemctl suspend" #idk
"$mainMod SHIFT, F, workspaceopt, allfloat" "$mainMod SHIFT, F, workspaceopt, allfloat"
"$mainMod SHIFT, H, exec, for i in {1..10}; do dunstctl history-pop; done; sleep 5 && dunstctl close-all" # show last 10 notifications for 5 seconds #"$mainMod SHIFT, H, exec, for i in {1..10}; do dunstctl history-pop; done; sleep 5 && dunstctl close-all" # show last 10 notifications for 5 seconds
"$mainMod, G, togglegroup," "$mainMod, G, togglegroup,"
"$mainMod SHIFT, S, exec, ~/.config/hypr/scripts/screenshot.sh region" "$mainMod SHIFT, S, exec, ~/.config/hypr/scripts/screenshot.sh region"
", PRINT, exec, ~/.config/hypr/scripts/screenshot.sh fullscreen" ", PRINT, exec, ~/.config/hypr/scripts/screenshot.sh fullscreen"
@ -1075,6 +1288,11 @@ in
"$mainMod, l, movefocus, r" "$mainMod, l, movefocus, r"
"$mainMod, k, movefocus, u" "$mainMod, k, movefocus, u"
"$mainMod, j, movefocus, d" "$mainMod, j, movefocus, d"
"$mainMod SHIFT, H, movewindow, l"
"$mainMod SHIFT, L, movewindow, r"
"$mainMod SHIFT, K, movewindow, u"
"$mainMod SHIFT, J, movewindow, d"
"$mainMod, Tab, cyclenext" "$mainMod, Tab, cyclenext"
"$mainMod, Tab, bringactivetotop" "$mainMod, Tab, bringactivetotop"
@ -1112,6 +1330,7 @@ in
# Scroll through existing workspaces with mainMod + scroll # Scroll through existing workspaces with mainMod + scroll
"$mainMod, mouse_down, workspace, e+1" "$mainMod, mouse_down, workspace, e+1"
"$mainMod, mouse_up, workspace, e-1" "$mainMod, mouse_up, workspace, e-1"
]; ];
# Move/resize windows with mainMod + LMB/RMB and dragging # Move/resize windows with mainMod + LMB/RMB and dragging
@ -1122,20 +1341,22 @@ in
# Laptop multimedia keys for volume and LCD brightness # Laptop multimedia keys for volume and LCD brightness
bindel = [ bindel = [
",XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+" ",XF86AudioRaiseVolume, exec, ~/.config/hypr/scripts/osd.sh volume_up"
",XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-" ",XF86AudioLowerVolume, exec, ~/.config/hypr/scripts/osd.sh volume_down"
",XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" ",XF86AudioMute, exec, ~/.config/hypr/scripts/osd.sh volume_mute"
",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle" ",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
",XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+" ",XF86MonBrightnessUp, exec, ~/.config/hypr/scripts/osd.sh brightness_up"
",XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-" ",XF86MonBrightnessDown, exec, ~/.config/hypr/scripts/osd.sh brightness_down"
#",Caps_Lock, exec, "
]; ];
# Requires playerctl # Requires playerctl
bindl = [ bindl = [
", XF86AudioNext, exec, playerctl next& ~/.config/hypr/scripts/media.sh" ", XF86AudioNext, exec, playerctl next& ~/.config/hypr/scripts/osd.sh next_track"
", XF86AudioPause, exec, playerctl play-pause& ~/.config/hypr/scripts/media.sh" ", XF86AudioPause, exec, playerctl play-pause& ~/.config/hypr/scripts/osd.sh play_pause"
", XF86AudioPlay, exec, playerctl play-pause& ~/.config/hypr/scripts/media.sh" ", XF86AudioPlay, exec, playerctl play-pause& ~/.config/hypr/scripts/osd.sh play_pause"
", XF86AudioPrev, exec, playerctl previous& ~/.config/hypr/scripts/media.sh" ", XF86AudioPrev, exec, playerctl previous& ~/.config/hypr/scripts/osd.sh prev_track"
",switch:on:Lid Switch, exec, hyprlock & systemctl suspend"
]; ];
############################## ##############################
@ -1155,10 +1376,15 @@ in
"nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0" "nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0"
# make tuis accessed from waybar float in center # make tuis accessed from waybar float in center
"tag +floating-window, title:(bluetui|impala|wiremix)" "tag +floating-window, title:(bluetui|impala|wiremix|Signal.*|.*WebCord.*|Weather|Calendar)"
"tag +floating-window, title:^(Open.*Files?|Open [F|f]older.*|Save.*Files?|Save.*As|Save|All Files|.*wants to [open|save].*|[C|c]hoose.*)" "tag +floating-window, title:^(Open.*Files?|Open [F|f]older.*|Save.*Files?|Save.*As|Save|All Files|.*wants to [open|save].*|[C|c]hoose.*)"
"tag +floating-window, class:(org.gnome.NautilusPreviewer)"
"float, class:(org.gnome.Calculator)" "float, class:(org.gnome.Calculator)"
# fix scrolling speed in chromium based apps
"tag +chromium-based, class:(chrom.*|brave-browser|signal|WebCord|obsidian|electron)"
"scrolltouchpad 0.15, tag:chromium-based"
# send games to specific workspace # send games to specific workspace
"workspace name:games silent, class:$games" "workspace name:games silent, class:$games"
"noanim, class:$games" "noanim, class:$games"
@ -1201,9 +1427,6 @@ in
"float, tag:floating-window" "float, tag:floating-window"
"center, tag:floating-window" "center, tag:floating-window"
"size 875 600, tag:floating-window" "size 875 600, tag:floating-window"
# hyprbar
"plugin:hyprbars:nobar, tag:^(?!floating-window$).*$"
]; ];
"$games" = "$games" =

View File

@ -0,0 +1,7 @@
{ ... }:
{
imports = [
./modules/default.nix
];
}

View File

@ -0,0 +1,68 @@
{
config,
pkgs,
lib,
...
}:
let
pkgs' = config.hardware.asahi.pkgs;
bootM1n1 =
if (config.boot.m1n1CustomLogo != null) then
pkgs'.m1n1.override {
customLogo = config.boot.m1n1CustomLogo;
}
else
pkgs'.m1n1;
bootUBoot = pkgs'.uboot-asahi.override {
m1n1 = bootM1n1;
};
bootFiles = {
"m1n1/boot.bin" = pkgs.runCommand "boot.bin" { } ''
cat ${bootM1n1}/lib/m1n1/m1n1.bin \
${config.boot.kernelPackages.kernel}/dtbs/apple/*.dtb \
${bootUBoot}/u-boot-nodtb.bin.gz \
> $out
if [ -n "${config.boot.m1n1ExtraOptions}" ]; then
echo '${config.boot.m1n1ExtraOptions}' >> $out
fi
'';
};
in
{
config = lib.mkIf config.hardware.asahi.enable {
# install m1n1 with the boot loader
boot.loader.grub.extraFiles = bootFiles;
boot.loader.systemd-boot.extraFiles = bootFiles;
boot.loader.limine.additionalFiles = bootFiles;
# ensure the installer has m1n1 in the image
system.extraDependencies = lib.mkForce [
bootM1n1
bootUBoot
];
system.build.m1n1 = bootFiles."m1n1/boot.bin";
};
options.boot = {
m1n1ExtraOptions = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Append extra options to the m1n1 boot binary. Might be useful for fixing
display problems on Mac minis.
https://github.com/AsahiLinux/m1n1/issues/159
'';
};
m1n1CustomLogo = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Custom logo to build into m1n1. The path must point to a 256x256 PNG.
'';
};
};
}

View File

@ -0,0 +1,87 @@
{
config,
pkgs,
lib,
...
}:
{
imports = [
./kernel
./peripheral-firmware
./boot-m1n1
./sound
];
config =
let
cfg = config.hardware.asahi;
in
lib.mkIf cfg.enable {
nixpkgs.overlays = lib.mkBefore [ cfg.overlay ];
hardware.asahi.pkgs =
if cfg.pkgsSystem != "aarch64-linux" then
import (pkgs.path) {
crossSystem.system = "aarch64-linux";
localSystem.system = cfg.pkgsSystem;
overlays = [ cfg.overlay ];
}
else
pkgs;
# 900 is higher priority than mkDefault but lower than just setting
hardware.graphics.package = lib.mkOverride 900 (
lib.warnIf
(lib.versionAtLeast pkgs.mesa.version "25.3" && lib.versionOlder pkgs.mesa.version "25.3.2")
''
Mesa 25.3.0 and 25.3.1 are known to cause crashes in Firefox on Asahi
GPUs. Please update to Mesa >= 25.3.2 by updating Nixpkgs. See
https://github.com/nix-community/nixos-apple-silicon/issues/380 for
more info.''
pkgs.mesa
);
};
options.hardware.asahi = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable the basic Asahi Linux components, such as kernel and boot setup.
'';
};
pkgsSystem = lib.mkOption {
type = lib.types.str;
default = "aarch64-linux";
description = ''
System architecture that should be used to build the major Asahi
packages, if not the default aarch64-linux. This allows installing from
a cross-built ISO without rebuilding them during installation.
'';
};
pkgs = lib.mkOption {
type = lib.types.raw;
description = ''
Package set used to build the major Asahi packages. Defaults to the
ambient set if not cross-built, otherwise re-imports the ambient set
with the system defined by `hardware.asahi.pkgsSystem`.
'';
};
overlay = lib.mkOption {
type = lib.mkOptionType {
name = "nixpkgs-overlay";
description = "nixpkgs overlay";
check = lib.isFunction;
merge = lib.mergeOneOption;
};
default = import ../packages/overlay.nix;
defaultText = "overlay provided with the module";
description = ''
The nixpkgs overlay for asahi packages.
'';
};
};
}

View File

@ -0,0 +1,114 @@
# the Asahi Linux kernel and options that must go along with it
{
config,
lib,
...
}:
{
config = lib.mkIf config.hardware.asahi.enable {
boot.kernelPackages =
let
pkgs' = config.hardware.asahi.pkgs;
in
pkgs'.linux-asahi.override {
_kernelPatches = config.boot.kernelPatches;
};
# we definitely want to use CONFIG_ENERGY_MODEL, and
# schedutil is a prerequisite for using it
# source: https://www.kernel.org/doc/html/latest/scheduler/sched-energy.html
powerManagement.cpuFreqGovernor = lib.mkOverride 800 "schedutil";
boot.initrd.availableKernelModules = [
# list of initrd modules stolen from
# https://github.com/AsahiLinux/asahi-scripts/blob/f461f080a1d2575ae4b82879b5624360db3cff8c/initcpio/install/asahi
"apple-mailbox"
"apple_nvmem_spmi"
"nvme_apple"
"pinctrl-apple-gpio"
"macsmc"
"macsmc-power"
"macsmc-input"
"macsmc-hwmon"
"macsmc-reboot"
"i2c-pasemi-platform"
"tps6598x"
"apple-dart"
"dwc3"
"dwc3-of-simple"
"xhci-pci"
"pcie-apple"
"gpio_macsmc"
"phy-apple-atc"
"nvmem_apple_efuses"
"spi-apple"
"spi-hid-apple"
"spi-hid-apple-of"
"rtc-macsmc"
"spmi-apple-controller"
"apple-dockchannel"
"dockchannel-hid"
"apple-rtkit-helper"
# additional stuff necessary to boot off USB for the installer
# and if the initrd (i.e. stage 1) goes wrong
"usb-storage"
"xhci-plat-hcd"
"usbhid"
"hid_generic"
];
boot.kernelParams = [
"earlycon"
"console=tty0"
"boot.shell_on_fail"
# Apple's SSDs are slow (~dozens of ms) at processing flush requests which
# slows down programs that make a lot of fsync calls. This parameter sets
# a delay in ms before actually flushing so that such requests can be
# coalesced. Be warned that increasing this parameter above zero (default
# is 1000) has the potential, though admittedly unlikely, risk of
# UNBOUNDED data corruption in case of power loss!!!! Don't even think
# about it on desktops!!
"nvme_apple.flush_interval=0"
];
# U-Boot does not support EFI variables
boot.loader.efi.canTouchEfiVariables = lib.mkForce false;
# GRUB has to be installed as removable if the user chooses to use it
boot.loader.grub = lib.mkDefault {
efiSupport = true;
efiInstallAsRemovable = true;
device = "nodev";
};
# autosuspend was enabled as safe for the PCI SD card reader
# "Genesys Logic, Inc GL9755 SD Host Controller [17a0:9755] (rev 01)"
# by recent systemd versions, but this has a "negative interaction"
# with our kernel/SoC and causes random boot hangs. disable it!
services.udev.extraHwdb = ''
pci:v000017A0d00009755*
ID_AUTOSUSPEND=0
'';
};
imports = [
(lib.mkRemovedOptionModule [
"hardware"
"asahi"
"addEdgeKernelConfig"
] "All edge kernel config options are now the default.")
(lib.mkRemovedOptionModule [
"hardware"
"asahi"
"experimentalGPUInstallMode"
] "This option became unnecessary with asahi support landing in mainline mesa.")
(lib.mkRemovedOptionModule [
"hardware"
"asahi"
"useExperimentalGPUDriver"
] "This option became unnecessary with asahi support landing in mainline mesa.")
(lib.mkRemovedOptionModule [ "hardware" "asahi" "withRust" ] "Rust support is now the default.")
];
}

View File

@ -0,0 +1,82 @@
{
config,
pkgs,
lib,
...
}:
{
config = lib.mkIf config.hardware.asahi.enable {
assertions = lib.mkIf config.hardware.asahi.extractPeripheralFirmware [
{
assertion = config.hardware.asahi.peripheralFirmwareDirectory != null;
message = ''
Asahi peripheral firmware extraction is enabled but the firmware
location appears incorrect.
'';
}
];
hardware.firmware =
let
pkgs' = config.hardware.asahi.pkgs;
in
lib.mkIf
(
(config.hardware.asahi.peripheralFirmwareDirectory != null)
&& config.hardware.asahi.extractPeripheralFirmware
)
[
(pkgs.stdenv.mkDerivation {
name = "asahi-peripheral-firmware";
nativeBuildInputs = [
pkgs'.asahi-fwextract
pkgs.cpio
];
buildCommand = ''
mkdir extracted
asahi-fwextract ${config.hardware.asahi.peripheralFirmwareDirectory} extracted
mkdir -p $out/lib/firmware
cat extracted/firmware.cpio | cpio -id --quiet --no-absolute-filenames
mv vendorfw/* $out/lib/firmware
'';
})
];
};
options.hardware.asahi = {
extractPeripheralFirmware = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Automatically extract the non-free non-redistributable peripheral
firmware necessary for features like Wi-Fi.
'';
};
peripheralFirmwareDirectory = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = lib.findFirst (path: builtins.pathExists (path + "/all_firmware.tar.gz")) null [
# path when the system is operating normally
/boot/asahi
# path when the system is mounted in the installer
/mnt/boot/asahi
];
description = ''
Path to the directory containing the non-free non-redistributable
peripheral firmware necessary for features like Wi-Fi. Ordinarily, this
will automatically point to the appropriate location on the ESP. Flake
users and those interested in maximum purity will want to copy those
files elsewhere and specify this manually.
Currently, this consists of the files `all-firmware.tar.gz` and
`kernelcache*`. The official Asahi Linux installer places these files
in the `asahi` directory of the EFI system partition when creating it.
'';
};
};
}

View File

@ -0,0 +1,65 @@
{
config,
options,
pkgs,
lib,
...
}:
{
options.hardware.asahi = {
setupAsahiSound = lib.mkOption {
type = lib.types.bool;
default = config.hardware.asahi.enable;
description = ''
Set up the Asahi DSP components so that the speakers and headphone jack
work properly and safely.
'';
};
};
config =
let
cfg = config.hardware.asahi;
in
lib.mkIf (cfg.setupAsahiSound && cfg.enable) (
lib.mkMerge [
{
# can't be used by Asahi sound infrastructure
services.pulseaudio.enable = false;
# enable pipewire to run real-time and avoid audible glitches
security.rtkit.enable = true;
# set up pipewire with the supported capabilities (instead of pulseaudio)
# and asahi-audio configs and plugins
services.pipewire = {
enable = true;
alsa.enable = true;
pulse.enable = true;
configPackages = [ pkgs.asahi-audio ];
wireplumber = {
enable = true;
configPackages = [ pkgs.asahi-audio ];
};
};
# set up enivronment so that UCM configs are used as well
environment.variables.ALSA_CONFIG_UCM2 = "${pkgs.alsa-ucm-conf-asahi}/share/alsa/ucm2";
systemd.user.services.pipewire.environment.ALSA_CONFIG_UCM2 =
config.environment.variables.ALSA_CONFIG_UCM2;
systemd.user.services.wireplumber.environment.ALSA_CONFIG_UCM2 =
config.environment.variables.ALSA_CONFIG_UCM2;
systemd.services.pipewire.environment.ALSA_CONFIG_UCM2 =
config.environment.variables.ALSA_CONFIG_UCM2;
systemd.services.wireplumber.environment.ALSA_CONFIG_UCM2 =
config.environment.variables.ALSA_CONFIG_UCM2;
# enable speakersafetyd to protect speakers
systemd.packages = [ pkgs.speakersafetyd ];
services.udev.packages = [ pkgs.speakersafetyd ];
}
]
);
}

View File

@ -0,0 +1,64 @@
{
lib,
callPackage,
linuxPackagesFor,
_kernelPatches ? [ ],
}:
let
linux-asahi-pkg =
{
stdenv,
lib,
fetchFromGitHub,
fetchpatch,
buildLinux,
...
}:
buildLinux rec {
inherit stdenv lib;
pname = "linux-asahi";
version = "7.0.8";
modDirVersion = version;
extraMeta.branch = "7.0";
src = fetchFromGitHub {
owner = "AsahiLinux";
repo = "linux";
tag = "asahi-7.0.8-1";
hash = "sha256-ak1dckvTxiR09xMf4xFb6qOPiAQNgIR18v8WLtQBnWQ=";
};
kernelPatches = [
{
name = "Asahi config";
patch = null;
structuredExtraConfig = with lib.kernel; {
# Needed for GPU
ARM64_16K_PAGES = yes;
ARM64_MEMORY_MODEL_CONTROL = yes;
ARM64_ACTLR_STATE = yes;
# Might lead to the machine rebooting if not loaded soon enough
APPLE_WATCHDOG = yes;
# Can not be built as a module, defaults to no
APPLE_M1_CPU_PMU = yes;
# Defaults to 'y', but we want to allow the user to set options in modprobe.d
HID_APPLE = module;
APPLE_PMGR_MISC = yes;
APPLE_PMGR_PWRSTATE = yes;
};
features.rust = true;
}
]
++ _kernelPatches;
};
linux-asahi = callPackage linux-asahi-pkg { };
in
lib.recurseIntoAttrs (linuxPackagesFor linux-asahi)

View File

@ -0,0 +1,19 @@
final: prev: {
linux-asahi = final.callPackage ./linux-asahi { };
uboot-asahi = final.callPackage ./uboot-asahi { };
mesa =
if prev.mesa.version == "26.0.5" then
# Workaround for https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41040
prev.mesa.overrideAttrs (old: {
version = "26.0.4";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "mesa";
repo = "mesa";
rev = "mesa-26.0.4";
hash = "sha256-gsrqhFCxZRrTbA5MMWARrN6lFVp4Q3D5Jz7MDYbXznY=";
};
})
else
prev.mesa;
}

View File

@ -0,0 +1,45 @@
{
lib,
fetchFromGitHub,
buildUBoot,
m1n1,
}:
(buildUBoot rec {
src = fetchFromGitHub {
owner = "AsahiLinux";
repo = "u-boot";
rev = "asahi-v2026.04-1";
hash = "sha256-RdcxvKZW2ms6pezBRNPyj/HTYQubpNX6r+nXqkV5oeo=";
};
version = "2026.04-1-asahi";
defconfig = "apple_m1_defconfig";
extraMeta.platforms = [ "aarch64-linux" ];
filesToInstall = [
"u-boot-nodtb.bin.gz"
"m1n1-u-boot.bin"
];
extraConfig = ''
CONFIG_IDENT_STRING=" ${version}"
CONFIG_VIDEO_FONT_4X6=n
CONFIG_VIDEO_FONT_8X16=n
CONFIG_VIDEO_FONT_SUN12X22=n
CONFIG_VIDEO_FONT_16X32=y
CONFIG_CMD_BOOTMENU=y
'';
}).overrideAttrs
(o: {
# nixos's downstream patches are not applicable
patches = [
];
# DTC= flag somehow breaks DTC compilation so we remove it
makeFlags = builtins.filter (s: (!(lib.strings.hasPrefix "DTC=" s))) o.makeFlags;
preInstall = ''
# compress so that m1n1 knows U-Boot's size and can find things after it
gzip -n u-boot-nodtb.bin
cat ${m1n1}/lib/m1n1/m1n1.bin arch/arm/dts/t[68]*.dtb u-boot-nodtb.bin.gz > m1n1-u-boot.bin
'';
})

View File

@ -0,0 +1,39 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "usb_storage" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/mapper/vg-nixos";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/2ED7-130C";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}

View File

@ -0,0 +1,42 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{
config,
lib,
pkgs,
...
}:
{
boot.kernelParams = [
"hid_apple.swap_fn_leftctrl=1"
"hid_apple.swap_opt_cmd=1"
"appledrm.show_notch=1"
];
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
boot.initrd.luks.devices."encrypted".device =
"/dev/disk/by-uuid/ebf580bd-9d71-4a2a-b6ae-a0c7e9a11395";
networking.networkmanager.enable = true;
networking.networkmanager.wifi.backend = "iwd";
networking.wireless.iwd.enable = true;
# Specify path to peripheral firmware files.
#hardware.asahi.peripheralFirmwareDirectory = "./firmware";
# Or disable extraction and management of them completely.
hardware.asahi.extractPeripheralFirmware = true;
nix.settings = {
extra-substituters = [
"https://nixos-apple-silicon.cachix.org"
];
extra-trusted-public-keys = [
"nixos-apple-silicon.cachix.org-1:8psDu5SA5dAD7qA0zMy5UT292TxeEPzIz8VVEr2Js20="
];
};
system.stateVersion = "26.05"; # Did you read the comment?
}