diff --git a/config/common.nix b/config/common.nix index 3e2297f..ca4cc5c 100644 --- a/config/common.nix +++ b/config/common.nix @@ -52,6 +52,8 @@ optimise.automatic = true; }; + security.apparmor.enable = true; + # unfree nixpkgs.config.allowUnfree = true; @@ -75,6 +77,7 @@ usbutils # lsusb wget zsh + bc # media yt-dlp @@ -114,6 +117,7 @@ ".." = "cd .."; "lsa" = "ls -al"; + "yt-dlp" = "yt-dlp --paths ~/Downloads"; }; environment.pathsToLink = [ diff --git a/config/desktop.nix b/config/desktop.nix index 72fc66a..a217b2b 100644 --- a/config/desktop.nix +++ b/config/desktop.nix @@ -16,20 +16,16 @@ hardware.bluetooth.enable = true; hardware.bluetooth.powerOnBoot = true; + services.libinput.enable = true; # hyprland and trackpad # Enable CUPS to print documents. - services.printing.enable = true; + services.printing.enable = true; # x86 only services.avahi = { enable = true; nssmdns4 = true; openFirewall = true; }; services.tailscale.enable = true; - services.printing.drivers = [ - pkgs.brlaser - pkgs.brgenml1lpr - pkgs.brgenml1cupswrapper - ]; # Enable automatic login for the user. # services.displayManager.autoLogin.enable = true; @@ -45,16 +41,17 @@ # "--config=/mnt/services/emulators/retroarch/retroarch.cfg" #]; - services.ollama = { - enable = true; - acceleration = "cuda"; - openFirewall = true; - host = "0.0.0.0"; - # Optional: preload models, see https://ollama.com/library - loadModels = [ "gemma4" ]; - package = pkgs.unstable.ollama; + #x86 only + #services.ollama = { + # enable = true; + # acceleration = "cuda"; + # openFirewall = true; + # host = "0.0.0.0"; + # # Optional: preload models, see https://ollama.com/library + # loadModels = [ "gemma4" ]; + # package = pkgs.unstable.ollama; - }; + #}; programs.chromium = { enable = true; @@ -67,6 +64,8 @@ hardware.keyboard.qmk.enable = true; + services.logind.settings."Login"."HandlePowerKey" = "suspend"; + boot = { # Graphical boot plymouth.enable = true; @@ -100,64 +99,67 @@ environment.systemPackages = with pkgs; [ brave firefox - #blender - #audacity - #orca-slicer - #gimp - #inkscape + blender + audacity + orca-slicer + gimp + inkscape #unstable.protonmail-desktop - #bitwarden-desktop - #libreoffice - #freecad - #kiwix - #obs-studio - #obsidian - unstable.proton-authenticator - #prismlauncher - #unstable.signal-desktop - tor-browser - #wireshark - #monero-gui - #moonlight-qt - nvtopPackages.nvidia + bitwarden-desktop + libreoffice + freecad + kiwix + obs-studio + obsidian + #unstable.proton-authenticator #x86 only + prismlauncher + unstable.signal-desktop + #tor-browser #x86 only + wireshark + monero-gui + moonlight-qt + #nvtopPackages.nvidia dconf-editor - #kdePackages.kdenlive - #element-desktop - #chirp + kdePackages.kdenlive + element-desktop + chirp scrcpy veracrypt - #lmms + lmms restic-browser + # games - #cataclysm-dda - #endless-sky - #mindustry - #openrct2 - #openttd - #osu-lazer-bin - #runelite - #unciv - #webcord - #wesnoth - #xonotic - #zeroad #0ad + gnome-2048 + gnome-chess + cataclysm-dda + endless-sky + #mindustry #x86 only + openrct2 + openttd + #osu-lazer-bin #x86 only + #runelite #x86 only + unciv + webcord + wesnoth + xonotic + zeroad # 0ad # game utils mangohud - umu-launcher + #umu-launcher #x86 only # emu - #ryubing - #xemu - xenia-canary + ryubing + xemu + #xenia-canary #86 only #retroarch-full - #rpcs3 - #dolphin-emu - #cemu + rpcs3 + dolphin-emu + #cemu #x86 only # backup - #keepassxc + keepassxc ]; fonts.fontconfig = { @@ -167,47 +169,45 @@ hinting.autohint = true; }; - services.sunshine = { - enable = true; - autoStart = true; - capSysAdmin = true; - openFirewall = true; - package = pkgs.sunshine.override { - cudaSupport = true; - }; - }; + #services.sunshine = { + # enable = true; + # autoStart = true; + # capSysAdmin = true; + # openFirewall = true; + # package = pkgs.sunshine.override { + # cudaSupport = true; + # }; + #}; - services.hardware.openrgb.enable = true; - services.hardware.openrgb.package = pkgs.openrgb-with-all-plugins; + #services.hardware.openrgb.enable = true; + #services.hardware.openrgb.package = pkgs.openrgb-with-all-plugins; programs = { adb.enable = true; - - localsend = { enable = true; openFirewall = true; }; - coolercontrol = { - enable = true; - }; + #coolercontrol = { + # enable = true; + #}; - gamescope = { - enable = true; - capSysNice = true; - }; + #gamescope = { + # enable = true; + # capSysNice = true; + #}; - steam = { - enable = true; - gamescopeSession.enable = true; - remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play - 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 - }; + #steam = { #x86 only + # enable = true; + # gamescopeSession.enable = true; + # remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play + # 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 + #}; - gamemode.enable = true; + #gamemode.enable = true; }; fileSystems."/mnt/media" = { @@ -260,53 +260,52 @@ enable = true; onCalendar = "weekly"; # Default value }; - packages = [ - "com.bitwarden.desktop" - "com.orcaslicer.OrcaSlicer" - "me.proton.Mail" - "org.audacityteam.Audacity" - "org.blender.Blender" - "org.freecad.FreeCAD" - "org.gimp.GIMP" - "org.inkscape.Inkscape" - "org.kiwix.desktop" - "org.libreoffice.LibreOffice" - "md.obsidian.Obsidian" - "com.obsproject.Studio" - "org.prismlauncher.PrismLauncher" - "org.signal.Signal" - "org.wireshark.Wireshark" - "org.getmonero.Monero" - "com.moonlight_stream.Moonlight" - "org.kde.kdenlive" - "im.riot.Riot" - "com.chirpmyradio.chirp" - "io.lmms.LMMS" + # packages = [ + # "com.bitwarden.desktop" + # "com.orcaslicer.OrcaSlicer" + # "me.proton.Mail" + # "org.audacityteam.Audacity" + # "org.blender.Blender" + # "org.freecad.FreeCAD" + # "org.gimp.GIMP" + # "org.inkscape.Inkscape" + # "org.kiwix.desktop" + # "org.libreoffice.LibreOffice" + # "md.obsidian.Obsidian" + # "com.obsproject.Studio" + # "org.prismlauncher.PrismLauncher" + # "org.wireshark.Wireshark" + # "org.getmonero.Monero" + # "com.moonlight_stream.Moonlight" + # "org.kde.kdenlive" + # "im.riot.Riot" + # "com.chirpmyradio.chirp" + # "io.lmms.LMMS" - # games - "com.github.Anuken.Mindustry" - "com.play0ad.zeroad" - "io.github.endless_sky.endless_sky" - "io.github.spacingbat3.webcord" - "io.github.yairm210.unciv" - "io.openrct2.OpenRCT2" - "net.runelite.RuneLite" - "org.cataclysmdda.CataclysmDDA" - "org.openttd.OpenTTD" - "org.wesnoth.Wesnoth" - "org.xonotic.Xonotic" - "sh.ppy.osu" - - # emu - "io.github.ryubing.Ryujinx" - "app.xemu.xemu" - "net.rpcs3.RPCS3" - "org.DolphinEmu.dolphin-emu" - "info.cemu.Cemu" + # # games + # "com.github.Anuken.Mindustry" + # "com.play0ad.zeroad" + # "io.github.endless_sky.endless_sky" + # "io.github.spacingbat3.webcord" + # "io.github.yairm210.unciv" + # "io.openrct2.OpenRCT2" + # "net.runelite.RuneLite" + # "org.cataclysmdda.CataclysmDDA" + # "org.openttd.OpenTTD" + # "org.wesnoth.Wesnoth" + # "org.xonotic.Xonotic" + # "sh.ppy.osu" + # + # # emu + # "io.github.ryubing.Ryujinx" + # "app.xemu.xemu" + # "net.rpcs3.RPCS3" + # "org.DolphinEmu.dolphin-emu" + # "info.cemu.Cemu" - # backup - "org.keepassxc.KeePassXC" - - ]; + # # backup + # "org.keepassxc.KeePassXC" + # + # ]; }; } diff --git a/config/hyprland.nix b/config/hyprland.nix index 1eabb53..a4bab75 100644 --- a/config/hyprland.nix +++ b/config/hyprland.nix @@ -25,14 +25,25 @@ tesseract # ocr hyprpicker satty # image annotation + brightnessctl + rofimoji + + # dev + jq # for scripts # gnome stuff + eog # eye of gnome gnome-calculator - nautilus - gnome-font-viewer 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 } diff --git a/firmware/all_firmware.tar.gz b/firmware/all_firmware.tar.gz new file mode 100755 index 0000000..0f27569 Binary files /dev/null and b/firmware/all_firmware.tar.gz differ diff --git a/firmware/kernelcache.release.mac14g b/firmware/kernelcache.release.mac14g new file mode 100755 index 0000000..72c731f Binary files /dev/null and b/firmware/kernelcache.release.mac14g differ diff --git a/flake.lock b/flake.lock index 65b210f..e032cab 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,41 @@ { "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": { "inputs": { "systems": "systems" @@ -181,6 +217,7 @@ }, "root": { "inputs": { + "apple-silicon-support": "apple-silicon-support", "home-manager": "home-manager", "home-manager-unstable": "home-manager-unstable", "lazyvim": "lazyvim", diff --git a/flake.nix b/flake.nix index 63a8ed1..c988143 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,10 @@ nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; lazyvim.url = "github:pfassina/lazyvim-nix"; 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 = @@ -24,6 +28,7 @@ nix-darwin, lazyvim, nix-flatpak, + apple-silicon-support, ... }@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 + ]; + }; + } + ]; + }; }; } diff --git a/home/common.nix b/home/common.nix index 8a984ce..99a70f0 100644 --- a/home/common.nix +++ b/home/common.nix @@ -38,12 +38,12 @@ lazyvim = { enable = true; plugins = { - colorscheme = '' - return { - "kepano/flexoki-neovim" - } - ''; - }; + colorscheme = '' + return { + "kepano/flexoki-neovim" + } + ''; + }; }; # neovim = { # enable = true; @@ -131,7 +131,7 @@ bindkey -M menuselect 'l' vi-forward-char bindkey -M menuselect 'j' vi-down-line-or-history bindkey -v '^?' backward-delete-char - + # Change cursor shape for different vi modes. function zle-keymap-select () { case $KEYMAP in diff --git a/home/hyprland.nix b/home/hyprland.nix index d384c9a..300f0f5 100644 --- a/home/hyprland.nix +++ b/home/hyprland.nix @@ -1,4 +1,9 @@ -{ pkgs, hostname, ... }: +{ + config, + pkgs, + hostname, + ... +}: let nix_background = "rgb(16, 15, 15)"; nix_text = "rgb(206, 205, 195)"; @@ -9,7 +14,7 @@ let nix_font = "JetBrainsMono Nerd Font"; nix_border_size = 3; nix_corner = 10; - nix_bar_height = 30; + nix_bar_height = if (hostname == "macbook-air") then 38 else 26; in { home.pointerCursor = { @@ -23,6 +28,16 @@ in # gtk = { # enable = true; # }; + # + # + xdg.portal = { + enable = true; + extraPortals = [ + pkgs.xdg-desktop-portal-gtk + pkgs.xdg-desktop-portal-hyprland + ]; + + }; services.udiskie.enable = true; # automount USB @@ -97,8 +112,7 @@ in "~/Downloads/wall.jpg" ]; wallpaper = [ - "DP-2,~/Downloads/wall.jpg" - "HDMI-A-1,~/Downloads/wall.jpg" + ",~/Downloads/wall.jpg" ]; }; }; @@ -118,11 +132,13 @@ in gaps = true; gap_size = 15; corner_radius = nix_corner; + layer = "top"; }; urgency_critical = { frame_color = nix_alert; timeout = 0; + layer = "overlay"; # show above fullscreen apps }; }; @@ -181,7 +197,7 @@ in border-radius: 0; min-height: 0; font-family: '${nix_font}'; - font-size: 14px; /* up from 12px */ + font-size: 12px; /* up from 12px */ } .modules-left { @@ -211,7 +227,6 @@ in #cpu, #battery, #pulseaudio, - #custom-power, #idle_inhibitor, #bluetooth, #privacy, @@ -265,11 +280,11 @@ in opacity: 0; } - #privacy privacy-item *{ + #privacy privacy-item * { min-width: 12px; margin-left: 5px; margin-right: 0; - font-size: 10px; + font-size: 9px; padding-bottom: 1px; color: #ffffff; } @@ -291,23 +306,78 @@ in "hyprland/workspaces" "hyprland/submap" ]; - modules-center = [ - "clock" - "idle_inhibitor" - ]; + modules-center = + if hostname == "macbook-air" then + [ ] + else + [ + "clock" + "idle_inhibitor" + ]; modules-right = [ "group/minimized" "hyprland/language" "bluetooth" "network" "pulseaudio" - "custom/power" + "battery" "privacy" - ]; + ] + ++ ( + if hostname == "macbook-air" then + [ + "clock" + "idle_inhibitor" + ] + else + [ ] + ); privacy = { 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" = { on-click = "activate"; @@ -352,14 +422,9 @@ in on-click = "hyprctl switchxkblayout monsgeek-monsgeek-keyboard next"; }; - "custom/power" = { - format = "⏻"; - on-click = "hyprlock & systemctl suspend"; - }; - "custom/menu" = { format = ""; - on-click = "walker"; + on-click = "rofi -show drun"; }; "custom/expand" = { @@ -408,6 +473,7 @@ in clock = { format = "{:%a %b %d %H:%M}"; 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 = { @@ -422,8 +488,8 @@ in format = "{icon}"; format-muted = ""; format-icons = { - headphone = ""; - headset = ""; + #headphone = ""; + #headset = ""; 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 = { enable = true; font = "${nix_font} 14"; 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 = { enable = true; @@ -640,12 +633,12 @@ in listener = [ { - timeout = 300; + timeout = if (hostname == "macbook-air") then 120 else 300; on-timeout = "loginctl lock-session & hyprctl dispatch dpms off"; on-resume = "hyprctl dispatch dpms on"; } { - timeout = 900; + timeout = if (hostname == "macbook-air") then 130 else 900; on-timeout = "systemctl suspend"; on-resume = "hyprctl dispatch dpms on"; } @@ -653,27 +646,242 @@ in }; }; - xdg.configFile."hypr/scripts/media.sh" = { + 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" = { executable = true; text = '' # Define a unique ID to reuse for replacing notifications NOTIF_ID=9999 PLAYER=$(playerctl -l 2>/dev/null | grep -i 'brave\|chromium\|chrome' | head -n 1) - + if [ -z "$PLAYER" ]; then exit 1 fi sleep 0.4s - + # Get play/pause status STATUS=$(playerctl -p "$PLAYER" status 2>/dev/null) - + # Fetch metadata TITLE=$(playerctl -p "$PLAYER" metadata title 2>/dev/null) ARTIST=$(playerctl -p "$PLAYER" metadata artist 2>/dev/null) - + # Format the message based on artist presence if [ -z "$ARTIST" ]; then MSG="$TITLE" @@ -687,25 +895,13 @@ in else notify-send -r $NOTIF_ID "Now Playing" "$MSG" -i media-playback-start -t 5000 fi - ''; - }; + ''; + }; xdg.configFile."hypr/scripts/screenshot.sh" = { executable = true; text = '' MODE=$1 - 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 @@ -741,6 +937,7 @@ in enable = true; plugins = [ pkgs.hyprlandPlugins.hyprbars + pkgs.hyprlandPlugins.hyprspace ]; settings = { # This is an example Hyprland config file for Nix. @@ -765,17 +962,19 @@ in plugin = { hyprbars = { - bar_height = nix_bar_height; + bar_height = if (hostname == "macbook-air") then 30 else nix_bar_height; hyprbars-button = [ - "rgb(ff605C), 16, 󰖭, hyprctl dispatch killactive" - "rgb(00CA4E), 16, 󰊔, hyprctl dispatch fullscreen 1" - "rgb(FFBD44), 16, , hyprctl dispatch togglefloating" + "rgb(ff605C), 15, 󰖭, hyprctl dispatch killactive" + "rgb(00CA4E), 15, 󰊔, hyprctl dispatch fullscreen 1" + "rgb(FFBD44), 15, , hyprctl dispatch togglefloating" ]; on_double_click = "hyprctl dispatch fullscreen 1"; icon_on_hover = true; bar_color = nix_background; "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" "HDMI-A-1, 1920x1080@75,2560x0,1,vrr,1" ] + else if (hostname == "macbook-air") then + [ + "eDP-1, 2560x1660@60, 0x0, 1.6" + ] else [ ",preferred,auto,auto" ]; ################### - ### MY PROGRAMS ### + ### MY PROGRAMS ###0 ################### # See https://wiki.hypr.land/Configuring/Keywords/ @@ -805,9 +1008,9 @@ in # Set programs that you use "$terminal" = "alacritty"; - "$fileManager" = "dolphin"; - "$menu" = "walker"; - "$browser" = "brave"; + "$fileManager" = "nautilus"; + "$menu" = "rofi -show drun"; + "$browser" = "brave --ozone-platform=wayland --enable-features=TouchpadOverscrollHistoryNavigation"; ################# ### AUTOSTART ### @@ -820,6 +1023,7 @@ in "waybar" "steam -silent" "webcord --start-minimized" + "signal-desktop --start-in-tray" #"[workspace 1 silent]$browser" #"[workspace 3 silent]proton-mail" @@ -864,8 +1068,8 @@ in # https://wiki.hypr.land/Configuring/Variables/#general general = { - gaps_in = 10; - gaps_out = 20; + gaps_in = if (hostname == "macbook-air") then 5 else 10; + gaps_out = if (hostname == "macbook-air") then 10 else 20; border_size = nix_border_size; @@ -937,8 +1141,8 @@ in "blur, notifications" "ignorezero, notifications" - # turn off anim for walker - "noanim, walker" + # turn off anim for rofi + "noanim, rofi" ]; # add blur to notifications @@ -1020,21 +1224,27 @@ in accel_profile = "flat"; touchpad = { + scroll_factor = 1.0; 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 - #gestures = { - # workspace_swipe = false; - #}; + gesture = [ "3, horizontal, workspace" ]; # Example per-device config # See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more device = { - name = "epic-mouse-v1"; - sensitivity = -0.5; + name = "apple-mtp-multi-touch"; + accel_profile = ""; + sensitivity = -0.1; }; ################### @@ -1047,20 +1257,23 @@ in bind = [ # Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more "$mainMod, Return, exec, $terminal" - "$mainMod, Q, killactive," + "$mainMod, C, killactive," "alt, F4, killactive," "$mainMod SHIFT, Q, exit," "$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, F, fullscreen, 3" "$mainMod, M, fullscreen, 1" "$mainMod, W, exec, $browser" "$mainMod, space, exec, $menu" + #"$mainMod, P, pseudo," # 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, 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 SHIFT, S, exec, ~/.config/hypr/scripts/screenshot.sh region" ", PRINT, exec, ~/.config/hypr/scripts/screenshot.sh fullscreen" @@ -1075,6 +1288,11 @@ in "$mainMod, l, movefocus, r" "$mainMod, k, movefocus, u" "$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, bringactivetotop" @@ -1112,6 +1330,7 @@ in # Scroll through existing workspaces with mainMod + scroll "$mainMod, mouse_down, workspace, e+1" "$mainMod, mouse_up, workspace, e-1" + ]; # Move/resize windows with mainMod + LMB/RMB and dragging @@ -1122,20 +1341,22 @@ in # Laptop multimedia keys for volume and LCD brightness bindel = [ - ",XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+" - ",XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-" - ",XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" - ",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle" - ",XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+" - ",XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-" + ",XF86AudioRaiseVolume, exec, ~/.config/hypr/scripts/osd.sh volume_up" + ",XF86AudioLowerVolume, exec, ~/.config/hypr/scripts/osd.sh volume_down" + ",XF86AudioMute, exec, ~/.config/hypr/scripts/osd.sh volume_mute" + ",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" + ",XF86MonBrightnessUp, exec, ~/.config/hypr/scripts/osd.sh brightness_up" + ",XF86MonBrightnessDown, exec, ~/.config/hypr/scripts/osd.sh brightness_down" + #",Caps_Lock, exec, " ]; # Requires playerctl bindl = [ - ", XF86AudioNext, exec, playerctl next& ~/.config/hypr/scripts/media.sh" - ", XF86AudioPause, exec, playerctl play-pause& ~/.config/hypr/scripts/media.sh" - ", XF86AudioPlay, exec, playerctl play-pause& ~/.config/hypr/scripts/media.sh" - ", XF86AudioPrev, exec, playerctl previous& ~/.config/hypr/scripts/media.sh" + ", XF86AudioNext, exec, playerctl next& ~/.config/hypr/scripts/osd.sh next_track" + ", XF86AudioPause, exec, playerctl play-pause& ~/.config/hypr/scripts/osd.sh play_pause" + ", XF86AudioPlay, exec, playerctl play-pause& ~/.config/hypr/scripts/osd.sh play_pause" + ", 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" # 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, class:(org.gnome.NautilusPreviewer)" "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 "workspace name:games silent, class:$games" "noanim, class:$games" @@ -1201,9 +1427,6 @@ in "float, tag:floating-window" "center, tag:floating-window" "size 875 600, tag:floating-window" - - # hyprbar - "plugin:hyprbars:nobar, tag:^(?!floating-window$).*$" ]; "$games" = diff --git a/hosts/macbook-air/apple-silicon-support/default.nix b/hosts/macbook-air/apple-silicon-support/default.nix new file mode 100644 index 0000000..71a5dd0 --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + imports = [ + ./modules/default.nix + ]; +} diff --git a/hosts/macbook-air/apple-silicon-support/modules/boot-m1n1/default.nix b/hosts/macbook-air/apple-silicon-support/modules/boot-m1n1/default.nix new file mode 100644 index 0000000..38c99b8 --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/modules/boot-m1n1/default.nix @@ -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. + ''; + }; + }; +} diff --git a/hosts/macbook-air/apple-silicon-support/modules/default.nix b/hosts/macbook-air/apple-silicon-support/modules/default.nix new file mode 100644 index 0000000..91e5084 --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/modules/default.nix @@ -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. + ''; + }; + }; +} diff --git a/hosts/macbook-air/apple-silicon-support/modules/kernel/default.nix b/hosts/macbook-air/apple-silicon-support/modules/kernel/default.nix new file mode 100644 index 0000000..510043f --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/modules/kernel/default.nix @@ -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.") + ]; +} diff --git a/hosts/macbook-air/apple-silicon-support/modules/peripheral-firmware/default.nix b/hosts/macbook-air/apple-silicon-support/modules/peripheral-firmware/default.nix new file mode 100644 index 0000000..27f1f34 --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/modules/peripheral-firmware/default.nix @@ -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. + ''; + }; + }; +} diff --git a/hosts/macbook-air/apple-silicon-support/modules/sound/default.nix b/hosts/macbook-air/apple-silicon-support/modules/sound/default.nix new file mode 100644 index 0000000..91a20ee --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/modules/sound/default.nix @@ -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 ]; + } + ] + ); +} diff --git a/hosts/macbook-air/apple-silicon-support/packages/linux-asahi/default.nix b/hosts/macbook-air/apple-silicon-support/packages/linux-asahi/default.nix new file mode 100644 index 0000000..8c1393a --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/packages/linux-asahi/default.nix @@ -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) diff --git a/hosts/macbook-air/apple-silicon-support/packages/overlay.nix b/hosts/macbook-air/apple-silicon-support/packages/overlay.nix new file mode 100644 index 0000000..8d9adf3 --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/packages/overlay.nix @@ -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; +} diff --git a/hosts/macbook-air/apple-silicon-support/packages/uboot-asahi/default.nix b/hosts/macbook-air/apple-silicon-support/packages/uboot-asahi/default.nix new file mode 100644 index 0000000..894650c --- /dev/null +++ b/hosts/macbook-air/apple-silicon-support/packages/uboot-asahi/default.nix @@ -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 + ''; + }) diff --git a/hosts/macbook-air/hardware.nix b/hosts/macbook-air/hardware.nix new file mode 100644 index 0000000..3a792e3 --- /dev/null +++ b/hosts/macbook-air/hardware.nix @@ -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"; +} diff --git a/hosts/macbook-air/settings.nix b/hosts/macbook-air/settings.nix new file mode 100644 index 0000000..25ed357 --- /dev/null +++ b/hosts/macbook-air/settings.nix @@ -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? + +}