diff --git a/foreign/dotfiles b/foreign/dotfiles deleted file mode 160000 index b76809f..0000000 --- a/foreign/dotfiles +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b76809f06d5460e8cd9cd4be770760e4e54a142f diff --git a/foreign/dotfiles/LICENSE b/foreign/dotfiles/LICENSE new file mode 100644 index 0000000..54ef845 --- /dev/null +++ b/foreign/dotfiles/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2021 Mihai Fufezan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/foreign/dotfiles/README.md b/foreign/dotfiles/README.md new file mode 100644 index 0000000..87e00fc --- /dev/null +++ b/foreign/dotfiles/README.md @@ -0,0 +1,74 @@ +

fufexan/dotfiles

+ +# 🗒 About + +In-house baked configs for Home-Manager and NixOS. Borrowed bits sprinkled on +top. Using [flakes](https://nixos.wiki/wiki/Flakes) and +[flake-parts](https://github.com/hercules-ci/flake-parts). + +See an overview of the flake outputs by running +`nix flake show github:fufexan/dotfiles`. + +## 🗃️ Contents + +- [modules](modules): NixOS common configs +- [hosts](hosts): host-specific configuration +- [home](home): my [Home Manager](https://github.com/nix-community/home-manager) config +- [lib](lib): helper functions +- [pkgs](pkgs): package definitions + +# 📦 Exported packages + +Run packages directly with: + +```console +nix run github:fufexan/dotfiles#packageName +``` + +Or install from the `packages` output. For example: + +```nix +# flake.nix +{ + inputs.fufexan-dotfiles.url = "github:fufexan/dotfiles"; + # Override my nixpkgs, binary cache will have less hits + inputs.fufexan-dotfiles.inputs.nixpkgs.follows = "nixpkgs"; +} + +# configuration.nix +{pkgs, inputs, ...}: { + environment.systemPackages = [ + inputs.fufexan-dotfiles.packages."x86_64-linux".packageName + ]; +} +``` + +## 💻 Desktop preview + + + Desktop Preview + +*Hint: click to go to a video showcase* + +# 💾 Resources + +Other configurations from where I learned and copied: + +- [colemickens/nixcfg](https://github.com/colemickens/nixcfg) +- [flake-utils-plus](https://github.com/gytis-ivaskevicius/flake-utils-plus) +- [gytis-ivaskevicius/nixfiles](https://github.com/gytis-ivaskevicius/nixfiles) +- [Mic92/dotfiles](https://github.com/Mic92/dotfiles) +- [NobbZ/nixos-config](https://github.com/NobbZ/nixos-config) +- [privatevoid-net/privatevoid-infrastructure](https://github.com/privatevoid-net/privatevoid-infrastructure) +- [RicArch97/nixos-config](https://github.com/RicArch97/nixos-config) +- [viperML/dotfiles](https://github.com/viperML/dotfiles) + +# 👥 People + +These are the people whom I've taken inspiration from while writing these +configs. There surely are more but I tend to forget. Regardless, I am thankful +to all of them. + +DieracDelta - gytis-ivaskevicius - hlissner - keksbg - Kranzes - +matthewcroughan - max-privatevoid - Misterio77 - NobbZ - OPNA2608 - +pnotequalnp - RicArch97 - tadeokondrak - viperML - Xe - yusdacra diff --git a/foreign/dotfiles/flake.lock b/foreign/dotfiles/flake.lock new file mode 100644 index 0000000..1901bbe --- /dev/null +++ b/foreign/dotfiles/flake.lock @@ -0,0 +1,788 @@ +{ + "nodes": { + "agenix": { + "inputs": { + "darwin": "darwin", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1677969766, + "narHash": "sha256-AIp/ZYZMNLDZR/H7iiAlaGpu4lcXsVt9JQpBlf43HRY=", + "owner": "ryantm", + "repo": "agenix", + "rev": "03b51fe8e459a946c4b88dcfb6446e45efb2c24e", + "type": "github" + }, + "original": { + "owner": "ryantm", + "repo": "agenix", + "type": "github" + } + }, + "crane": { + "flake": false, + "locked": { + "lastModified": 1670900067, + "narHash": "sha256-VXVa+KBfukhmWizaiGiHRVX/fuk66P8dgSFfkVN4/MY=", + "owner": "ipetkov", + "repo": "crane", + "rev": "59b31b41a589c0a65e4a1f86b0e5eac68081468b", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "darwin": { + "inputs": { + "nixpkgs": [ + "agenix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1673295039, + "narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=", + "owner": "lnl7", + "repo": "nix-darwin", + "rev": "87b9d090ad39b25b2400029c64825fc2a8868943", + "type": "github" + }, + "original": { + "owner": "lnl7", + "ref": "master", + "repo": "nix-darwin", + "type": "github" + } + }, + "devshell": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1678957337, + "narHash": "sha256-Gw4nVbuKRdTwPngeOZQOzH/IFowmz4LryMPDiJN/ah4=", + "owner": "numtide", + "repo": "devshell", + "rev": "3e0e60ab37cd0bf7ab59888f5c32499d851edb47", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "dream2nix": { + "inputs": { + "alejandra": [ + "helix", + "nci" + ], + "all-cabal-json": [ + "helix", + "nci" + ], + "crane": "crane", + "devshell": [ + "helix", + "nci" + ], + "flake-parts": [ + "helix", + "nci", + "parts" + ], + "flake-utils-pre-commit": [ + "helix", + "nci" + ], + "ghc-utils": [ + "helix", + "nci" + ], + "gomod2nix": [ + "helix", + "nci" + ], + "mach-nix": [ + "helix", + "nci" + ], + "nix-pypi-fetcher": [ + "helix", + "nci" + ], + "nixpkgs": [ + "helix", + "nci", + "nixpkgs" + ], + "poetry2nix": [ + "helix", + "nci" + ], + "pre-commit-hooks": [ + "helix", + "nci" + ], + "pruned-racket-catalog": [ + "helix", + "nci" + ] + }, + "locked": { + "lastModified": 1677289985, + "narHash": "sha256-lUp06cTTlWubeBGMZqPl9jODM99LpWMcwxRiscFAUJg=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "28b973a8d4c30cc1cbb3377ea2023a76bc3fb889", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, + "eww": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": [ + "rust-overlay" + ] + }, + "locked": { + "lastModified": 1678303550, + "narHash": "sha256-JlpoMXL+QIO0DUIyAcGRJte2G/jF/rSeO/zze5W7S/s=", + "owner": "elkowar", + "repo": "eww", + "rev": "45154bbf5962cad9c4e6c76f75d57dd8d740d307", + "type": "github" + }, + "original": { + "owner": "elkowar", + "repo": "eww", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1678379998, + "narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c13d60b89adea3dc20704c045ec4d50dd964d447", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1642700792, + "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "fu": { + "locked": { + "lastModified": 1678901627, + "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "helix": { + "inputs": { + "nci": "nci", + "nixpkgs": "nixpkgs", + "parts": [ + "flake-parts" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1678544833, + "narHash": "sha256-aLs6qjiViaxryP2XMjKPzrJGYfIQsGTbNWfbySfivmY=", + "owner": "SoraTenshi", + "repo": "helix", + "rev": "80782f4f3cd557617d09b0d8f42dbce780552630", + "type": "github" + }, + "original": { + "owner": "SoraTenshi", + "ref": "daily-driver", + "repo": "helix", + "type": "github" + } + }, + "hm": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "utils": "utils" + }, + "locked": { + "lastModified": 1679067095, + "narHash": "sha256-G2dJQURL/CCi+8RP6jNJG8VqgtzEMCA+6mNodd3VR6E=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "3239e0b40f242f47bf6c0c37b2fd35ab3e76e370", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "hyprland": { + "inputs": { + "hyprland-protocols": "hyprland-protocols", + "nixpkgs": "nixpkgs_2", + "wlroots": "wlroots", + "xdph": "xdph" + }, + "locked": { + "lastModified": 1679166084, + "narHash": "sha256-yr+alTr1eGjEKpMiD06FTTMP6vaoNwYEZT6mW6dQ5rM=", + "owner": "hyprwm", + "repo": "Hyprland", + "rev": "06244555915339967864292dd0b83cd9732516d8", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "Hyprland", + "type": "github" + } + }, + "hyprland-contrib": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1679036674, + "narHash": "sha256-2s3Hfq56jL8ePyc3+calPT34FNMK2zksqwPhIxAq20o=", + "owner": "hyprwm", + "repo": "contrib", + "rev": "1af47a008e850c595aeddc83bb3f04fd81935caa", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "contrib", + "type": "github" + } + }, + "hyprland-protocols": { + "inputs": { + "nixpkgs": [ + "hyprland", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1671839510, + "narHash": "sha256-+PY1qqJfmZzzROgcIY4I7AkCwpnC+qBIYk2eFoA9RWc=", + "owner": "hyprwm", + "repo": "hyprland-protocols", + "rev": "b8f55e02a328c47ed373133c52483bbfa20a1b75", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-protocols", + "type": "github" + } + }, + "kmonad": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "dir": "nix", + "lastModified": 1673185501, + "narHash": "sha256-uEtWPpl9nH7QqochHo1z+giPga1zXR1Ko3dOXHIapFY=", + "owner": "kmonad", + "repo": "kmonad", + "rev": "3413f1be996142c8ef4f36e246776a6df7175979", + "type": "github" + }, + "original": { + "dir": "nix", + "owner": "kmonad", + "repo": "kmonad", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "mk-naked-shell": { + "flake": false, + "locked": { + "lastModified": 1676572903, + "narHash": "sha256-oQoDHHUTxNVSURfkFcYLuAK+btjs30T4rbEUtCUyKy8=", + "owner": "yusdacra", + "repo": "mk-naked-shell", + "rev": "aeca9f8aa592f5e8f71f407d081cb26fd30c5a57", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "mk-naked-shell", + "type": "github" + } + }, + "nci": { + "inputs": { + "dream2nix": "dream2nix", + "mk-naked-shell": "mk-naked-shell", + "nixpkgs": [ + "helix", + "nixpkgs" + ], + "parts": "parts", + "rust-overlay": [ + "helix", + "rust-overlay" + ] + }, + "locked": { + "lastModified": 1677297103, + "narHash": "sha256-ArlJIbp9NGV9yvhZdV0SOUFfRlI/kHeKoCk30NbSiLc=", + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "rev": "a79272a2cb0942392bb3a5bf9a3ec6bc568795b2", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "type": "github" + } + }, + "nix-gaming": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1678324469, + "narHash": "sha256-FWI0+K1zdfzOq8BbgGhRvy+qp2J4KauGL1geAc19PRQ=", + "owner": "fufexan", + "repo": "nix-gaming", + "rev": "18fac9dd032d0bb1d4c660b3257fa00df7f0145d", + "type": "github" + }, + "original": { + "owner": "fufexan", + "repo": "nix-gaming", + "type": "github" + } + }, + "nix-super": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": "nixpkgs_4", + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1677536397, + "narHash": "sha256-pKp+dmOJc3/9R3dBP30u2zXOyCuF5dVzgFlS1upSwZk=", + "owner": "privatevoid-net", + "repo": "nix-super", + "rev": "8eb40776e51819038fbb8a087d9885842451a333", + "type": "github" + }, + "original": { + "owner": "privatevoid-net", + "repo": "nix-super", + "type": "github" + } + }, + "nix-xilinx": { + "inputs": { + "flake-compat": "flake-compat_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1678957299, + "narHash": "sha256-myDk5QC9Q5FESSJl5N3Nf67w+dwq2KpvcguTwodKGqo=", + "owner": "doronbehar", + "repo": "nix-xilinx", + "rev": "e1533146984dd11bdefd6042c282cd123aca8d71", + "type": "gitlab" + }, + "original": { + "owner": "doronbehar", + "repo": "nix-xilinx", + "type": "gitlab" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1677063315, + "narHash": "sha256-qiB4ajTeAOVnVSAwCNEEkoybrAlA+cpeiBxLobHndE8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "988cc958c57ce4350ec248d2d53087777f9e1949", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1677676435, + "narHash": "sha256-6FxdcmQr5JeZqsQvfinIMr0XcTyTuR7EXX0H3ANShpQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a08d6979dd7c82c4cef0dcc6ac45ab16051c1169", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1677995890, + "narHash": "sha256-eOnCn0o3I6LP48fAi8xWFcn49V2rL7oX5jCtJTeN1LI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a1240f6b4a0bcc84fc48008b396a140d9f3638f6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1670461440, + "narHash": "sha256-jy1LB8HOMKGJEGXgzFRLDU1CBGL0/LlkolgnqIsF0D8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "04a75b2eecc0acf6239acf9dd04485ff8d14f425", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_5": { + "locked": { + "lastModified": 1678898370, + "narHash": "sha256-xTICr1j+uat5hk9FyuPOFGxpWHdJRibwZC+ATi0RbtE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ac718d02867a84b42522a0ece52d841188208f2c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "parts": { + "inputs": { + "nixpkgs-lib": [ + "helix", + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1675933616, + "narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "47478a4a003e745402acf63be7f9a092d51b83d7", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "root": { + "inputs": { + "agenix": "agenix", + "devshell": "devshell", + "eww": "eww", + "flake-parts": "flake-parts", + "fu": "fu", + "helix": "helix", + "hm": "hm", + "hyprland": "hyprland", + "hyprland-contrib": "hyprland-contrib", + "kmonad": "kmonad", + "nix-gaming": "nix-gaming", + "nix-super": "nix-super", + "nix-xilinx": "nix-xilinx", + "nixpkgs": "nixpkgs_5", + "rust-overlay": "rust-overlay_2", + "spicetify-nix": "spicetify-nix" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "helix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1677292251, + "narHash": "sha256-D+6q5Z2MQn3UFJtqsM5/AvVHi3NXKZTIMZt1JGq/spA=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "34cdbf6ad480ce13a6a526f57d8b9e609f3d65dc", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "rust-overlay_2": { + "inputs": { + "flake-utils": [ + "fu" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1679106165, + "narHash": "sha256-03Opt2yu4E/AIFjvlgib0/nhMn6B4B/t/nvwS2bzOGw=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "7313c06ac334d6262ddfe30a38b3abc3da6bd565", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "spicetify-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1675736383, + "narHash": "sha256-fjQmN3pYGstBNHCFdmPzDf1/Dt04v3b7+/w4cQ4gkKo=", + "owner": "the-argus", + "repo": "spicetify-nix", + "rev": "ec85c2a2f5d1035142b8e383cc77b68bb0b9ebc8", + "type": "github" + }, + "original": { + "owner": "the-argus", + "repo": "spicetify-nix", + "type": "github" + } + }, + "utils": { + "locked": { + "lastModified": 1676283394, + "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "wlroots": { + "flake": false, + "locked": { + "host": "gitlab.freedesktop.org", + "lastModified": 1677789111, + "narHash": "sha256-dWrk+Q3bLdtFe5rkyaAKWCQJCeE/KFNllcu1DvBC38c=", + "owner": "wlroots", + "repo": "wlroots", + "rev": "5ae17de23f5fd9bb252a698f3771c840280e2c05", + "type": "gitlab" + }, + "original": { + "host": "gitlab.freedesktop.org", + "owner": "wlroots", + "repo": "wlroots", + "type": "gitlab" + } + }, + "xdph": { + "inputs": { + "hyprland-protocols": [ + "hyprland", + "hyprland-protocols" + ], + "nixpkgs": [ + "hyprland", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1673116118, + "narHash": "sha256-eR0yDSkR2XYMesfdRWJs25kAdXET2mbNNHu5t+KUcKA=", + "owner": "hyprwm", + "repo": "xdg-desktop-portal-hyprland", + "rev": "d479c846531fd0e1d2357c9588b8310a2b859ef2", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "xdg-desktop-portal-hyprland", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/foreign/dotfiles/flake.nix b/foreign/dotfiles/flake.nix new file mode 100644 index 0000000..18a5944 --- /dev/null +++ b/foreign/dotfiles/flake.nix @@ -0,0 +1,111 @@ +{ + description = "fufexan's NixOS and Home-Manager flake"; + + outputs = inputs: + inputs.flake-parts.lib.mkFlake {inherit inputs;} { + systems = ["x86_64-linux"]; + + imports = [ + ./home/profiles + ./hosts + ./modules + ./pkgs + ./lib + {config._module.args._inputs = inputs // {inherit (inputs) self;};} + ]; + + perSystem = { + config, + inputs', + pkgs, + system, + ... + }: { + imports = [ + { + _module.args.pkgs = inputs.self.legacyPackages.${system}; + } + ]; + + devShells.default = inputs'.devshell.legacyPackages.mkShell { + packages = [ + pkgs.alejandra + pkgs.git + config.packages.repl + ]; + name = "dots"; + }; + + formatter = pkgs.alejandra; + }; + }; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + + agenix = { + url = "github:ryantm/agenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + devshell = { + url = "github:numtide/devshell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + eww = { + url = "github:elkowar/eww"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-overlay.follows = "rust-overlay"; + }; + + fu.url = "github:numtide/flake-utils"; + + helix = { + url = "github:SoraTenshi/helix/daily-driver"; + inputs.parts.follows = "flake-parts"; + }; + + hm = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + hyprland.url = "github:hyprwm/Hyprland"; + + hyprland-contrib = { + url = "github:hyprwm/contrib"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + kmonad = { + url = "github:kmonad/kmonad?dir=nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + nix-gaming.url = "github:fufexan/nix-gaming"; + + nix-super.url = "github:privatevoid-net/nix-super"; + + nix-xilinx = { + url = "gitlab:doronbehar/nix-xilinx"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "fu"; + }; + + spicetify-nix = { + url = "github:the-argus/spicetify-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; +} diff --git a/foreign/dotfiles/home/README.md b/foreign/dotfiles/home/README.md new file mode 100644 index 0000000..84d13f5 --- /dev/null +++ b/foreign/dotfiles/home/README.md @@ -0,0 +1,15 @@ +# Home config + +Home-Manager configurations for different hosts. + +Name | Description +--------------- | ----------- +`default.nix` | Home-Manager specific configuration +`editors` | Helix & Neovim +`programs` | Programs +`shell` | Zsh, Nix options, etc. +`terminals` | Terminal configs +`wayland` | Wayland-specific options, including Sway and Waybar configs + +`profiles` is a special dir where `homeConfigurations` are set up. They're +basically the entrypoints of the configs. diff --git a/foreign/dotfiles/home/default.nix b/foreign/dotfiles/home/default.nix new file mode 100644 index 0000000..d3a469b --- /dev/null +++ b/foreign/dotfiles/home/default.nix @@ -0,0 +1,18 @@ +{ + home = { + username = "mihai"; + homeDirectory = "/home/mihai"; + stateVersion = "20.09"; + extraOutputsToInstall = ["doc" "devdoc"]; + }; + + # disable manuals as nmd fails to build often + manual = { + html.enable = false; + json.enable = false; + manpages.enable = false; + }; + + # let HM manage itself when in standalone mode + programs.home-manager.enable = true; +} diff --git a/foreign/dotfiles/home/editors/helix/default.nix b/foreign/dotfiles/home/editors/helix/default.nix new file mode 100644 index 0000000..7489b14 --- /dev/null +++ b/foreign/dotfiles/home/editors/helix/default.nix @@ -0,0 +1,44 @@ +{ + inputs, + pkgs, + ... +} @ args: { + home.packages = [pkgs.shellcheck]; + + programs.helix = { + enable = true; + package = inputs.helix.packages.${pkgs.hostPlatform.system}.default; + + languages = import ./languages.nix args; + + settings = { + theme = "catppuccin_mocha"; + editor = { + true-color = true; + color-modes = true; + cursorline = true; + cursor-shape = { + insert = "bar"; + normal = "block"; + select = "underline"; + }; + indent-guides = { + render = true; + rainbow-option = "dim"; + }; + rainbow-brackets = true; + statusline.center = ["position-percentage"]; + whitespace.characters = { + newline = "↴"; + tab = "⇥"; + }; + }; + + keys.normal.space.u = { + f = ":format"; # format using LSP formatter + w = ":set whitespace.render all"; + W = ":set whitespace.render none"; + }; + }; + }; +} diff --git a/foreign/dotfiles/home/editors/helix/languages.nix b/foreign/dotfiles/home/editors/helix/languages.nix new file mode 100644 index 0000000..6f40bee --- /dev/null +++ b/foreign/dotfiles/home/editors/helix/languages.nix @@ -0,0 +1,47 @@ +{pkgs, ...}: +with pkgs; [ + { + name = "bash"; + language-server = { + command = "${nodePackages.bash-language-server}/bin/bash-language-server"; + args = ["start"]; + }; + auto-format = true; + formatter = { + command = "${shfmt}/bin/shfmt"; + args = ["-i" "2" "-"]; + }; + } + { + name = "cpp"; + language-server = { + command = "${clang-tools}/bin/clangd"; + clangd.fallbackFlags = ["-std=c++2b"]; + }; + } + { + name = "nix"; + language-server = {command = lib.getExe nil;}; + config.nil.formatting.command = ["alejandra" "-q"]; + } + { + name = "clojure"; + scope = "source.clojure"; + injection-regex = "(clojure|clj|edn|boot|yuck)"; + file-types = ["clj" "cljs" "cljc" "clje" "cljr" "cljx" "edn" "boot" "yuck"]; + roots = ["project.clj" "build.boot" "deps.edn" "shadow-cljs.edn"]; + comment-token = ";"; + language-server = {command = "clojure-lsp";}; + indent = { + tab-width = 2; + unit = " "; + }; + } + { + name = "css"; + language-server = { + command = "${nodePackages.vscode-css-languageserver-bin}/bin/css-languageserver"; + args = ["--stdio"]; + }; + } +] diff --git a/foreign/dotfiles/home/editors/neovim/config/init.lua b/foreign/dotfiles/home/editors/neovim/config/init.lua new file mode 100644 index 0000000..cc30859 --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/init.lua @@ -0,0 +1,14 @@ +vim.opt.expandtab = true +vim.opt.hidden = true +vim.opt.incsearch = true +vim.opt.mouse = "a" +vim.opt.number = true +vim.opt.shiftwidth = 2 +vim.opt.splitbelow = true +vim.opt.splitright = true +vim.opt.signcolumn = "yes:3" +vim.opt.tabstop = 2 +vim.opt.timeoutlen = 0 +vim.wo.wrap = false +vim.opt.exrc = true +vim.cmd("syntax on") \ No newline at end of file diff --git a/foreign/dotfiles/home/editors/neovim/config/lspconfig.lua b/foreign/dotfiles/home/editors/neovim/config/lspconfig.lua new file mode 100644 index 0000000..428282b --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/lspconfig.lua @@ -0,0 +1,28 @@ +local lspc = require'lspconfig' + +lspc.nil_ls.setup{} +lspc.clangd.setup{} + +local buf_map = function(bufnr, mode, lhs, rhs, opts) + vim.api.nvim_buf_set_keymap(bufnr, mode, lhs, rhs, opts or { + silent = true, + }) +end + +lspc.tsserver.setup({ + on_attach = function(client, bufnr) + client.resolved_capabilities.document_formatting = false + client.resolved_capabilities.document_range_formatting = false + + local ts_utils = require("nvim-lsp-ts-utils") + ts_utils.setup({}) + ts_utils.setup_client(client) + + buf_map(bufnr, "n", "gs", ":TSLspOrganize") + buf_map(bufnr, "n", "gi", ":TSLspRenameFile") + buf_map(bufnr, "n", "go", ":TSLspImportAll") + + on_attach(client, bufnr) + end, +}) + diff --git a/foreign/dotfiles/home/editors/neovim/config/nvim-cmp.lua b/foreign/dotfiles/home/editors/neovim/config/nvim-cmp.lua new file mode 100644 index 0000000..13bff54 --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/nvim-cmp.lua @@ -0,0 +1,73 @@ +local has_words_before = function() + local line, col = unpack(vim.api.nvim_win_get_cursor(0)) + return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil +end + +local feedkey = function(key, mode) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) +end + +local cmp = require("cmp") +local lspkind = require("lspkind") + +cmp.setup({ + sources = { + { name = "nvim_lsp" }, + { name = "cmp_tabnine" }, + { name = "treesitter" }, + { name = "buffer" }, + { name = "path" }, + { name = "vsnip" }, + -- { name = "copilot" }, + }, + + snippet = { + expand = function(args) + vim.fn["vsnip#anonymous"](args.body) + end, + }, + + formatting = { + format = lspkind.cmp_format({ + with_text = true, + menu = { + buffer = "[Buf]", + nvim_lsp = "[LSP]", + nvim_lua = "[Lua]", + latex_symbols = "[Latex]", + treesitter = "[TS]", + cmp_tabnine = "[TN]", + vsnip = "[Snip]", + }, + }), + }, + + mapping = { + [""] = cmp.mapping.confirm({ select = true }), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif vim.fn["vsnip#available"](1) == 1 then + feedkey("(vsnip-expand-or-jump)", "") + elseif has_words_before() then + cmp.complete() + else + fallback() + end + end, { + "i", + "s", + }), + + [""] = cmp.mapping(function() + if cmp.visible() then + cmp.select_prev_item() + elseif vim.fn["vsnip#jumpable"](-1) == 1 then + feedkey("(vsnip-jump-prev)", "") + end + end, { + "i", + "s", + }), + }, +}) diff --git a/foreign/dotfiles/home/editors/neovim/config/theming.lua b/foreign/dotfiles/home/editors/neovim/config/theming.lua new file mode 100644 index 0000000..091687e --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/theming.lua @@ -0,0 +1,20 @@ +-- set colorscheme +vim.cmd 'set termguicolors' + +vim.g.catppuccin_flavour = "mocha" + +require("catppuccin").setup() + +vim.cmd [[colorscheme catppuccin]] + +-- enable colorizer +require'colorizer'.setup() + +-- set sign +vim.cmd 'sign define DiagnosticSignError text= linehl= texthl=DiagnosticSignError numhl=' +vim.cmd 'sign define DiagnosticSignHint text= linehl= texthl=DiagnosticSignHint numhl=' +vim.cmd 'sign define DiagnosticSignInfo text= linehl= texthl=DiagnosticSignInfo numhl=' +vim.cmd 'sign define DiagnosticSignWarn text= linehl= texthl=DiagnosticSignWarn numhl=' + +-- set lightline theme to horizon +vim.g.lightline = { colorscheme = "catppuccin" } diff --git a/foreign/dotfiles/home/editors/neovim/config/treesitter-textobjects.lua b/foreign/dotfiles/home/editors/neovim/config/treesitter-textobjects.lua new file mode 100644 index 0000000..8f2c6d6 --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/treesitter-textobjects.lua @@ -0,0 +1,14 @@ +require'nvim-treesitter.configs'.setup { + textobjects = { + select = { + enable = true, + keymaps = { + -- You can use the capture groups defined in textobjects.scm + ["af"] = "@function.outer", + ["if"] = "@function.inner", + ["ac"] = "@class.outer", + ["ic"] = "@class.inner", + }, + }, + }, +} diff --git a/foreign/dotfiles/home/editors/neovim/config/treesitter.lua b/foreign/dotfiles/home/editors/neovim/config/treesitter.lua new file mode 100644 index 0000000..1f33ef0 --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/treesitter.lua @@ -0,0 +1,30 @@ +require("nvim-treesitter.configs").setup({ + highlight = { + enable = true, + disable = {}, + }, + rainbow = { + enable = true, + extended_mode = true, + }, + autotag = { + enable = true, + }, + context_commentstring = { + enable = true, + }, + incremental_selection = { + enable = true, + keymaps = { + init_selection = "gnn", + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + }, +}) + +-- breaks highlight +-- vim.cmd([[set foldmethod=expr]]) +-- vim.cmd([[set foldlevel=10]]) +-- vim.cmd([[set foldexpr=nvim_treesitter#foldexpr()]]) \ No newline at end of file diff --git a/foreign/dotfiles/home/editors/neovim/config/utils.lua b/foreign/dotfiles/home/editors/neovim/config/utils.lua new file mode 100644 index 0000000..e1a264c --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/utils.lua @@ -0,0 +1,29 @@ +-- telescope +require('telescope').load_extension('fzy_native') + +-- null-ls +local nb = require('null-ls').builtins + +require('null-ls').setup({ + sources = { + nb.formatting.alejandra, + nb.code_actions.statix, + nb.diagnostics.cppcheck, + nb.diagnostics.deadnix, + nb.diagnostics.statix, + nb.diagnostics.eslint, + nb.completion.spell, + }, +}) + +require("gitsigns").setup() + +-- autopairs +require('nvim-autopairs').setup{} + +-- copy to system clipboard +vim.api.nvim_set_keymap( 'v', 'y', '"+y', {noremap = true}) +vim.api.nvim_set_keymap( 'n', 'y', ':%+y', {noremap = true}) + +-- paste from system clipboard +vim.api.nvim_set_keymap( 'n', 'p', '"+p', {noremap = true}) diff --git a/foreign/dotfiles/home/editors/neovim/config/which-key.lua b/foreign/dotfiles/home/editors/neovim/config/which-key.lua new file mode 100644 index 0000000..ba3bc32 --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/config/which-key.lua @@ -0,0 +1,39 @@ +vim.g.mapleader = " " + +local wk = require("which-key") + +wk.setup({}) + +wk.register({ + [""] = { + b = { "Telescope buffers", "Buffers" }, + ["/"] = { "Telescope live_grep", "Live Grep" }, + f = { "Telescope find_files", "Find File" }, + g = { + name = "Git / VCS", + i = { "lua require('telescope').extensions.gh.issues()", "Github Issues" }, + p = { "lua require('telescope').extensions.gh.pull_request()", "Github PRs" }, + b = { "ToggleBlameLine", "Toggle BlameLine" }, + c = { "Neogit commit", "Commit" }, + s = { "Neogit kind=split", "Staging" }, + }, + a = { "lua require('telescope.builtin').lsp_code_actions()", "Code Actions" }, + d = { "lua require('telescope.builtin').lsp_document_diagnostics()", "LSP Diagnostics" }, + k = { "lua vim.lsp.buf.signature_help()", "Signature Help" }, + l = { + name = "LSP", + f = { "lua vim.lsp.buf.formatting_sync()", "Format file"}, + q = { "lua vim.lsp.diagnostic.set_loclist()", "Set Loclist" }, + e = { "lua vim.lsp.diagnostic.show_line_diagnostics()", "Show Line Diagnostics" }, + }, + p = { "\"+p", "Paste from clipboard" }, + P = { "\"+P", "Paste from clipboard before cursor" }, + y = { "\"+y", "Yank to clipboard" }, + }, + g = { + l = { "$", "Line end" }, + h = { "0", "Line start" }, + s = { "^", "First non-blank in line" }, + e = { "G", "Bottom" }, + }, +}) diff --git a/foreign/dotfiles/home/editors/neovim/default.nix b/foreign/dotfiles/home/editors/neovim/default.nix new file mode 100644 index 0000000..ba0a8f5 --- /dev/null +++ b/foreign/dotfiles/home/editors/neovim/default.nix @@ -0,0 +1,62 @@ +{pkgs, ...}: { + programs.neovim = { + enable = true; + + vimAlias = true; + viAlias = true; + vimdiffAlias = true; + + plugins = with pkgs.vimPlugins; [ + catppuccin-nvim + cmp-buffer + cmp-nvim-lsp + cmp-path + cmp-spell + cmp-treesitter + cmp-vsnip + friendly-snippets + gitsigns-nvim + lightline-vim + lspkind-nvim + neogit + null-ls-nvim + nvim-autopairs + nvim-cmp + nvim-colorizer-lua + nvim-lspconfig + nvim-tree-lua + nvim-ts-rainbow + (nvim-treesitter.withPlugins (_: pkgs.tree-sitter.allGrammars)) + plenary-nvim + telescope-fzy-native-nvim + telescope-nvim + vim-floaterm + vim-sneak + vim-vsnip + which-key-nvim + ]; + + extraPackages = with pkgs; [gcc ripgrep fd]; + + extraConfig = let + luaRequire = module: + builtins.readFile (builtins.toString + ./config + + "/${module}.lua"); + luaConfig = builtins.concatStringsSep "\n" (map luaRequire [ + "init" + "lspconfig" + "nvim-cmp" + "theming" + "treesitter" + "treesitter-textobjects" + "utils" + "which-key" + ]); + in '' + lua <<  + ${luaConfig} +  + ''; + }; +} diff --git a/foreign/dotfiles/home/profiles/default.nix b/foreign/dotfiles/home/profiles/default.nix new file mode 100644 index 0000000..cfbcfe9 --- /dev/null +++ b/foreign/dotfiles/home/profiles/default.nix @@ -0,0 +1,44 @@ +{ + inputs, + withSystem, + module_args, + ... +}: let + sharedModules = [ + ../. + ../shell + module_args + ]; + + homeImports = { + "mihai@io" = + [ + ./io + inputs.spicetify-nix.homeManagerModule + inputs.hyprland.homeManagerModules.default + ] + ++ sharedModules; + server = sharedModules ++ [./server]; + }; + + inherit (inputs.hm.lib) homeManagerConfiguration; +in { + imports = [ + {_module.args = {inherit homeImports;};} + ]; + + flake = { + homeConfigurations = withSystem "x86_64-linux" ({pkgs, ...}: { + "mihai@io" = homeManagerConfiguration { + modules = homeImports."mihai@io" ++ module_args; + inherit pkgs; + }; + server = homeManagerConfiguration { + modules = homeImports.server ++ module_args; + inherit pkgs; + }; + }); + + homeManagerModules.eww-hyprland = import ./programs/eww; + }; +} diff --git a/foreign/dotfiles/home/profiles/io/default.nix b/foreign/dotfiles/home/profiles/io/default.nix new file mode 100644 index 0000000..b798f79 --- /dev/null +++ b/foreign/dotfiles/home/profiles/io/default.nix @@ -0,0 +1,73 @@ +{ + imports = [ + ../../editors/helix + ../../editors/neovim + ../../programs + ../../programs/games.nix + ../../programs/dunst.nix + ../../wayland + ../../terminals/alacritty.nix + ../../terminals/wezterm.nix + ]; + + services = { + kanshi = { + # use 1.6 scaling: 2560 : 1.6 = 1600, exact division. good for offsets + # restart eww every time because it won't expand/contract automatically + enable = true; + profiles = { + undocked = { + outputs = [ + { + criteria = "eDP-1"; + position = "0,0"; + } + ]; + }; + docked-all = { + outputs = [ + { + criteria = "eDP-1"; + position = "1366,0"; + } + { + criteria = "DP-1"; + position = "0,0"; + } + { + criteria = "DP-2"; + position = "1600,0"; + } + ]; + }; + + docked1 = { + outputs = [ + { + criteria = "eDP-1"; + position = "1366,0"; + } + { + criteria = "DP-1"; + position = "0,0"; + } + ]; + }; + + docked2 = { + outputs = [ + { + criteria = "eDP-1"; + position = "1366,0"; + } + { + criteria = "DP-2"; + position = "0,0"; + } + ]; + }; + }; + systemdTarget = "graphical-session.target"; + }; + }; +} diff --git a/foreign/dotfiles/home/profiles/server/default.nix b/foreign/dotfiles/home/profiles/server/default.nix new file mode 100644 index 0000000..786b2b2 --- /dev/null +++ b/foreign/dotfiles/home/profiles/server/default.nix @@ -0,0 +1,3 @@ +{ + programs.helix.enable = true; +} diff --git a/foreign/dotfiles/home/programs/cinny.nix b/foreign/dotfiles/home/programs/cinny.nix new file mode 100644 index 0000000..79f7801 --- /dev/null +++ b/foreign/dotfiles/home/programs/cinny.nix @@ -0,0 +1,13 @@ +{pkgs, ...}: { + # use Cinny Matrix client + # create systemd service that serves it on localhost:9999 + systemd.user.services.cinny = { + Unit.Description = "Cinny Service"; + Service = { + Type = "simple"; + ExecStart = "${pkgs.darkhttpd}/bin/darkhttpd ${pkgs.cinny} --addr 127.0.0.1 --port 9999"; + TimeoutStopSec = 5; + }; + Install.WantedBy = ["default.target"]; + }; +} diff --git a/foreign/dotfiles/home/programs/default.nix b/foreign/dotfiles/home/programs/default.nix new file mode 100644 index 0000000..b040fcc --- /dev/null +++ b/foreign/dotfiles/home/programs/default.nix @@ -0,0 +1,57 @@ +{ + pkgs, + config, + ... +}: { + imports = [ + ../shell/nix.nix + ./cinny.nix + ./files + ./media.nix + ./git.nix + ./gtk.nix + ./packages.nix + ./qt.nix + ./spicetify.nix + ./xdg.nix + ./zathura.nix + ]; + + programs = { + chromium = { + enable = true; + commandLineArgs = ["--enable-features=TouchpadOverscrollHistoryNavigation"]; + extensions = [ + {id = "cjpalhdlnbpafiamejdnhcphjbkeiagm";} + {id = "bkkmolkhemgaeaeggcmfbghljjjoofoh";} + ]; + }; + + firefox = { + enable = true; + profiles.mihai = {}; + }; + + gpg = { + enable = true; + homedir = "${config.xdg.dataHome}/gnupg"; + }; + + password-store = { + enable = true; + package = pkgs.pass.withExtensions (exts: [exts.pass-otp]); + settings.PASSWORD_STORE_DIR = "${config.xdg.dataHome}/password-store"; + }; + }; + + services = { + gpg-agent = { + enable = true; + enableSshSupport = true; + pinentryFlavor = "gnome3"; + sshKeys = ["73D1C4271E8C508E1E55259660C94BE828B07738"]; + }; + + syncthing.enable = true; + }; +} diff --git a/foreign/dotfiles/home/programs/dunst.nix b/foreign/dotfiles/home/programs/dunst.nix new file mode 100644 index 0000000..49c2f5e --- /dev/null +++ b/foreign/dotfiles/home/programs/dunst.nix @@ -0,0 +1,58 @@ +{ + pkgs, + default, + ... +}: { + # notification daemon + services.dunst = { + enable = true; + iconTheme = { + name = "Papirus-Dark"; + package = pkgs.papirus-icon-theme; + }; + settings = { + global = { + alignment = "center"; + corner_radius = 16; + follow = "mouse"; + font = "Roboto 10"; + format = "%s\\n%b"; + frame_width = 1; + offset = "5x5"; + horizontal_padding = 8; + icon_position = "left"; + indicate_hidden = "yes"; + markup = "yes"; + max_icon_size = 64; + mouse_left_click = "do_action"; + mouse_middle_click = "close_all"; + mouse_right_click = "close_current"; + padding = 8; + plain_text = "no"; + separator_color = "auto"; + separator_height = 1; + show_indicators = false; + shrink = "no"; + word_wrap = "yes"; + }; + + fullscreen_delay_everything = {fullscreen = "delay";}; + + urgency_critical = { + background = default.xcolors.bg; + foreground = default.xcolors.fg; + frame_color = default.xcolors.red; + }; + urgency_low = { + background = default.xcolors.bg; + foreground = default.xcolors.fg; + frame_color = default.xcolors.blue; + }; + urgency_normal = { + background = default.xcolors.bg; + foreground = default.xcolors.fg; + frame_color = default.xcolors.green; + }; + }; + }; +} diff --git a/foreign/dotfiles/home/programs/eww/README.md b/foreign/dotfiles/home/programs/eww/README.md new file mode 100644 index 0000000..fb27f74 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/README.md @@ -0,0 +1,68 @@ +# Eww configuration + +This configuration aims to provide a fully working shell replacement for +compositors/window managers. Features constantly get added and existing ones +get improved. + +## 🗃️ Components + +The same daemon runs multiple windows which interact with each other: + +### bar + +![bar](https://user-images.githubusercontent.com/36706276/216402839-0f8ec9b0-dc4b-4cb8-9834-db59b61db97f.png) + +### music window + +![music](https://user-images.githubusercontent.com/36706276/192146077-f8da4691-9a0c-487f-9805-3fd4d55551e9.gif) + +### calendar + +![calendar](https://user-images.githubusercontent.com/36706276/204923748-f5c7db3a-5000-40cf-ba41-cd2d5f14146a.png) + +### system info + +![system](https://user-images.githubusercontent.com/36706276/216403137-a3231c60-976a-4e5d-85c0-899679ab0a92.png) + +## ❔ Usage + +### Home Manager + +If you use Home Manager, installing is as simple as adding my flake to your +inputs, passing `inputs` to `extraSpecialArgs` and importing the relevant +module: +```nix +{inputs, ...}: { + imports = [inputs.fufexan.homeManagerModules.eww-hyprland]; + + programs.eww-hyprland = { + enable = true; + + # default package + package = pkgs.eww-wayland; + + # if you want to change colors + colors = builtins.readFile ./macchiato.scss; + + # set to true to reload on change + autoReload = false; + }; +} +``` + +Make sure to also add the fonts listed below. + +### Other distros + +To quickly install this config, grab all the files in this directory and put +them in `~/.config/eww`. Then run `eww daemon` and `eww open bar`. Enjoy! + +Dependencies: +- Icon fonts: `material-symbols-outline` (any variation can be used as long as you change the `font-family` property of `.icon`) +- Text font: [Jost](https://fonts.google.com/specimen/Jost) +- Script deps: everything in `default.nix`'s `dependencies` list. + +## 🎨 Theme + +The theme colors can be changed in `css/_colors.scss`. Currently the theme used +is [Catppuccin Mocha](https://github.com/catppuccin/catppuccin). diff --git a/foreign/dotfiles/home/programs/eww/css/_calendar.scss b/foreign/dotfiles/home/programs/eww/css/_calendar.scss new file mode 100644 index 0000000..6e5a1e1 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_calendar.scss @@ -0,0 +1,31 @@ +.calendar-win { + @include window; + background-color: $bg; + color: $fg; + padding: .2em; +} + +calendar { + padding: 5px; + + :selected { + color: $magenta; + } + + .header { + color: $black; + } + + .highlight { + color: $red; + font-weight: bold; + } + + .button { + color: $blue; + } + + :indeterminate { + color: $green; + } +} diff --git a/foreign/dotfiles/home/programs/eww/css/_colors.scss b/foreign/dotfiles/home/programs/eww/css/_colors.scss new file mode 100644 index 0000000..f899781 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_colors.scss @@ -0,0 +1,35 @@ +$rosewater: #f5e0dc; +$flamingo: #f2cdcd; +$pink: #f5c2e7; +$mauve: #cba6f7; +$red: #f38ba8; +$maroon: #eba0ac; +$peach: #fab387; +$yellow: #f9e2af; +$green: #a6e3a1; +$teal: #94e2d5; +$sky: #89dceb; +$sapphire: #74c7ec; +$blue: #89b4fa; +$lavender: #b4befe; + +$text: #cdd6f4; +$subtext1: #bac2de; +$subtext0: #a6adc8; +$overlay2: #9399b2; +$overlay1: #7f849c; +$overlay0: #6c7086; + +$surface2: #585b70; +$surface1: #45475a; +$surface0: #313244; + +$base: #1e1e2e; +$mantle: #181825; +$crust: #11111b; + +$fg: $text; +$bg: rgba(30, 30, 46, 0.6); +$bg1: rgba(49, 50, 68, 0.6); +$border: #28283d; +$shadow: $crust; diff --git a/foreign/dotfiles/home/programs/eww/css/_music.scss b/foreign/dotfiles/home/programs/eww/css/_music.scss new file mode 100644 index 0000000..b3d0ee4 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_music.scss @@ -0,0 +1,66 @@ +.song-cover-art { + @include rounding; + background-position: center; + background-size: cover; + margin: 4px 5px 4px 0; + min-height: 24px; + min-width: 24px; +} + +.music-window { + @include window; + background-color: $bg; + border: 1px solid $border; + color: $fg; +} + +.music-cover-art { + background-position: center; + background-size: cover; + border-radius: 8px; + margin: 1em; + min-height: 170px; + min-width: 170px; +} + +.music-box { + margin: 1rem 1rem 1rem 0; +} + +.music-title { + font-size: 1.1rem; + font-weight: bold; +} + +.music-artist { + color: $white_a; +} + +.music-button label { + color: $white_a; + font-size: 2rem; +} + +.music-time { + color: $white_a; + margin: 0 1rem; +} + +.music-bar scale { + highlight { + background-image: linear-gradient(to right, $cyan, $blue_a); + border-radius: 24px; + } + + trough { + background-color: $bg1; + border-radius: 24px; + margin-top: 0; + min-height: 10px; + min-width: 170px; + } +} + +.playctl { + color: $yellow; +} diff --git a/foreign/dotfiles/home/programs/eww/css/_osd.scss b/foreign/dotfiles/home/programs/eww/css/_osd.scss new file mode 100644 index 0000000..ab605a2 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_osd.scss @@ -0,0 +1,36 @@ +.osd-part { + @include window; + background: $bg; + margin: 0 .5rem .5rem; + + label { + color: $text; + font-size: 2rem; + margin: 0 .1rem 0 .2rem; + } + + scale { + margin: -.2rem 0; + } + + trough { + @include rounding; + background-color: $bg1; + margin: 1rem 0 .5rem; + min-height: 10rem; + min-width: .7rem; + + highlight { + @include rounding; + min-width: 0; + } + } +} + +.osd-volume highlight { + background-image: linear-gradient(to top, $cyan, $blue_a); +} + +.osd-brightness highlight { + background-image: linear-gradient(to top, $yellow, $yellow_a); +} diff --git a/foreign/dotfiles/home/programs/eww/css/_sidebar.scss b/foreign/dotfiles/home/programs/eww/css/_sidebar.scss new file mode 100644 index 0000000..58af83f --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_sidebar.scss @@ -0,0 +1,127 @@ +.system-menu-box { + @include window; + background-color: $bg; + color: $text; +} + +.separator { + font-size: 1rem; +} + +.top-row { + margin: 1rem 1.5rem 0; + + .time { font-size: 2rem; } + + .date-box { + margin: 0 1rem; + + label { font-size: 1.1rem; } + + .date { + background: unset; + margin: 0 .5rem 0 0; + padding: 0; + } + } +} + +.system-row { + margin: .5rem .7rem; + + .airplane-box button { + padding: 1rem 3rem; + } + + label { + font-size: 1rem; + margin: 0 .1rem; + } +} + +.element { + @include rounding; + background-color: $black; + margin: .3rem; + + button { + @include rounding; + padding: 1rem; + + label { + font-size: 1.5rem; + } + + &:hover { + background-color: $cyan_a; + } + } +} + +.sliders { + @include rounding; + background-color: $black; + margin: .5rem 1rem; + padding: .6rem 1rem; + + scale { + margin-right: -1rem; + min-width: 21.5rem; + + trough { margin-right: 0; } + } + + box { margin: .2rem 0; } + label { font-size: 1.2rem; } +} + +.volume-slider-box, +.brightness-slider-box { + trough { background-color: $base; } +} + +.volume-bar highlight { + @include rounding; + background-image: linear-gradient(to right, $cyan, $blue_a); +} + +.brightness-slider-box scale highlight { + @include rounding; + background-image: linear-gradient(to right, $yellow, $yellow_a); +} + +.bottom-row { + margin: .5rem 1rem; + + .battery-icon { font-size: 2rem; } + .battery-wattage { color: $magenta; } + + .battery-status { + color: $green; + margin: 0 .5rem; + } + + button { + background-color: $black; + border-radius: 50%; + margin-bottom: .1rem; + padding: 0 .5rem; + + label { font-size: 1.5rem; } + &:hover { background-color: $black_a; } + } +} + +.bt-connected { + background-color: $blue; + color: $red; + + button:hover { background-color: rgba(0, 0, 0, .1); } +} + +.wifi-connected { + background-color: $magenta; + color: $red; + + button:hover { background-color: rgba(0, 0, 0, .1); } +} diff --git a/foreign/dotfiles/home/programs/eww/css/_system.scss b/foreign/dotfiles/home/programs/eww/css/_system.scss new file mode 100644 index 0000000..b59736a --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_system.scss @@ -0,0 +1,80 @@ +* { + transition: 1s; +} + +.membar { + color: $yellow; +} + +.cpubar { + color: $blue; +} + +.batbar { + color: $green; +} + +.membar, +.cpubar, +.batbar { + background-color: $bg1; +} + +.iconmem { + color: $yellow; +} + +.iconcpu { + color: $blue; +} + +.icon-text { + font-size: 3rem; + padding: .7rem; +} + +.sys-text-sub { + color: $text; +} + +.sys-text-mem, +.sys-text-cpu { + font-size: 1rem; + font-weight: bold; +} + +.sys-icon-mem, +.sys-icon-cpu { + font-size: 1.5rem; + margin: 1.5rem; +} + +.system-info-box { + @include rounding; + background-color: $black; + margin: .5rem 1rem; + padding: .5rem; +} + +.sys-mem, +.sys-cpu { + background-color: $bg; +} + +.sys-icon-mem, +.sys-text-mem, +.sys-mem { + color: $yellow; +} + +.sys-icon-cpu, +.sys-text-cpu, +.sys-cpu { + color: $blue; +} + +.sys-box { + margin: .3em; + + box { margin-left: 1rem; } +} diff --git a/foreign/dotfiles/home/programs/eww/css/_volume.scss b/foreign/dotfiles/home/programs/eww/css/_volume.scss new file mode 100644 index 0000000..1f340f6 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/css/_volume.scss @@ -0,0 +1,5 @@ +.vol-icon { color: $green; } +.volbar highlight { + background-image: linear-gradient(to right, $cyan, $blue_a); + border-radius: 10px; +} diff --git a/foreign/dotfiles/home/programs/eww/default.nix b/foreign/dotfiles/home/programs/eww/default.nix new file mode 100644 index 0000000..5e2eb52 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/default.nix @@ -0,0 +1,128 @@ +{ + config, + pkgs, + lib, + ... +}: let + dependencies = with pkgs; [ + config.wayland.windowManager.hyprland.package + cfg.package + bash + bc + blueberry + bluez + coreutils + dbus + dunst + findutils + gawk + gnome.gnome-control-center + gnused + gojq + imagemagick + jaq + light + networkmanager + pavucontrol + playerctl + procps + pulseaudio + ripgrep + socat + udev + upower + util-linux + wget + wireplumber + wlogout + socat + fuzzel + swaynotificationcenter + ]; + + reload_script = pkgs.writeShellScript "reload_eww" '' + windows=$(eww windows | rg '\*' | tr -d '*') + + systemctl --user restart eww.service + + echo $windows | while read -r w; do + eww open $w + done + ''; + + cfg = config.programs.eww-hyprland; +in { + options.programs.eww-hyprland = { + enable = lib.mkEnableOption "eww Hyprland config"; + + package = lib.mkOption { + type = with lib.types; nullOr package; + default = pkgs.eww-wayland; + defaultText = lib.literalExpression "pkgs.eww-wayland"; + description = "Eww package to use."; + }; + + autoReload = lib.mkOption { + type = lib.types.bool; + default = false; + defaultText = lib.literalExpression "false"; + description = "Whether to restart the eww daemon and windows on change."; + }; + + colors = lib.mkOption { + type = with lib.types; nullOr lines; + default = null; + defaultText = lib.literalExpression "null"; + description = '' + SCSS file with colors defined in the same way as Catppuccin colors are, + to be used by eww. + + Defaults to Catppuccin Mocha. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = [cfg.package]; + + # remove nix files + xdg.configFile."eww" = { + source = lib.cleanSourceWith { + filter = name: _type: let + baseName = baseNameOf (toString name); + in + !(lib.hasSuffix ".nix" baseName) && !(baseName == "_colors.scss"); + src = lib.cleanSource ./.; + }; + + # links each file individually, which lets us insert the colors file separately + recursive = true; + + onChange = + if cfg.autoReload + then reload_script.outPath + else ""; + }; + + # colors file + xdg.configFile."eww/css/_colors.scss".text = + if cfg.colors != null + then cfg.colors + else (builtins.readFile ./css/_colors.scss); + + systemd.user.services.eww = { + Unit = { + Description = "Eww Daemon"; + # not yet implemented + # PartOf = ["tray.target"]; + PartOf = ["graphical-session.target"]; + }; + Service = { + Environment = "PATH=/run/wrappers/bin:${lib.makeBinPath dependencies}"; + ExecStart = "${cfg.package}/bin/eww daemon --no-daemonize"; + Restart = "on-failure"; + }; + Install.WantedBy = ["graphical-session.target"]; + }; + }; +} diff --git a/foreign/dotfiles/home/programs/eww/eww.scss b/foreign/dotfiles/home/programs/eww/eww.scss new file mode 100644 index 0000000..e809818 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/eww.scss @@ -0,0 +1,93 @@ +@import 'css/colors'; + +@mixin rounding { + border-radius: 16px; +} + +@mixin window { + border: 1px solid $border; + box-shadow: 0 2px 3px $shadow; + margin: 5px 5px 0px; + @include rounding; +} + +* { + all: unset; + font-family: "Montreux Classic", "Jost *", Roboto, sans-serif; + transition: 200ms ease; +} + +@import 'css/calendar'; +@import 'css/music'; +@import 'css/osd'; +@import 'css/sidebar'; +@import 'css/system'; +@import 'css/volume'; + +.bar { + color: $fg; + label { + font-size: 1.4rem; + } +} + +.vis { + background-color: $bg; + border-radius: 24px; + padding: 4px 24px; + margin-top: 12px; + margin-bottom: 0px; + border: 3px solid $outline; +} + +.invis { + background-color: rgba(0, 0, 0, 0); +} + +tooltip { + background: $bg; + border: 1px solid $border; + border-radius: 8px; + + label { + font-size: 1rem; + } +} + +.icon, +.icon label { font-family: Material Symbols Outlined; } + +.module { margin: 0 5px; } + +.hour { + padding-left: 5px; +} + +.minute { + padding-right: .7rem; +} + +.date { + background: $bg; + color: $yellow; + + label { + font-size: 1.2rem; + } +} + +.bright-icon { color: $yellow_a; } +.module-bt { font-size: 1.2rem; } + +scale trough { + background-color: $bg; + border-radius: 24px; + margin: 0 1rem; + min-height: 10px; + min-width: 70px; +} + +.workspaces { + margin-left: 10px; + button { transition: color 2s ease; } +} diff --git a/foreign/dotfiles/home/programs/eww/eww.yuck b/foreign/dotfiles/home/programs/eww/eww.yuck new file mode 100644 index 0000000..d1b2654 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/eww.yuck @@ -0,0 +1,56 @@ +(include "./modules/clock.yuck") +(include "./modules/music.yuck") +(include "./modules/net.yuck") +(include "./modules/sys.yuck") +(include "./modules/variables.yuck") +(include "./modules/volume.yuck") +(include "./modules/workspaces.yuck") +(include "./modules/current_win.yuck") + +(include "./windows/calendar.yuck") +(include "./windows/music.yuck") +(include "./windows/osd.yuck") +(include "./windows/system-menu.yuck") + +(defwidget left [] + (box + :space-evenly false + :halign "start" + :class "vis" + (workspaces))) + +(defwidget right [] + (box + :space-evenly false + :halign "end" + :class "vis bar" + (music-module) + (volume-module) + (net) + (sys) + (clock_module))) + +(defwidget center [] + (box + :space-evenly false + :halign "center" + :class "vis bar" + (current-win-module))) + +(defwidget bar-box [] + (centerbox + :class "invis bar" + (left) + (center) + (right))) + +(defwindow bar + :monitor 0 + :geometry (geometry :x "0%" + :y "0%" + :width "98%" + :height "24px" + :anchor "top center") + :stacking "fg" + :exclusive true + (bar-box)) diff --git a/foreign/dotfiles/home/programs/eww/modules/bluetooth.yuck b/foreign/dotfiles/home/programs/eww/modules/bluetooth.yuck new file mode 100644 index 0000000..5b023b8 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/bluetooth.yuck @@ -0,0 +1,8 @@ +(defwidget bluetooth [] + (button + :class "module-bt module icon" + :onclick "blueberry" + :onrightclick "scripts/bluetooth toggle" + :tooltip "${bluetooth.text} ${bluetooth.battery}" + :style "color: ${bluetooth.color};" + {bluetooth.icon})) diff --git a/foreign/dotfiles/home/programs/eww/modules/bright.yuck b/foreign/dotfiles/home/programs/eww/modules/bright.yuck new file mode 100644 index 0000000..27b1b5f --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/bright.yuck @@ -0,0 +1,9 @@ +(defwidget bright [] + (box + :class "module" + (eventbox + :onscroll "echo {} | sed -e 's/up/-U 1/g' -e 's/down/-A 1/g' | xargs light" + (label + :text {brightness.icon} + :class "bright-icon icon" + :tooltip "brightness ${round(brightness.percent, 0)}%")))) diff --git a/foreign/dotfiles/home/programs/eww/modules/clock.yuck b/foreign/dotfiles/home/programs/eww/modules/clock.yuck new file mode 100644 index 0000000..4b13d9e --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/clock.yuck @@ -0,0 +1,24 @@ +(defvar date_rev false) + +(defwidget clock_module [] + (eventbox + :onhover "${EWW_CMD} update date_rev=true" + :onhoverlost "${EWW_CMD} update date_rev=false" + (overlay + :class "module" + (box + :space-evenly false + (label + :text {time.hour} + :class "hour") + (label + :text ":") + (label + :text {time.minute} + :class "minute")) + (revealer + :reveal date_rev + (button + :class "date" + :onclick "${EWW_CMD} open --toggle calendar" + {time.date}))))) diff --git a/foreign/dotfiles/home/programs/eww/modules/current_win.yuck b/foreign/dotfiles/home/programs/eww/modules/current_win.yuck new file mode 100644 index 0000000..a4142d7 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/current_win.yuck @@ -0,0 +1,8 @@ +(defwidget current-win-module [] + (box + (button + :class "module" + {current_win.title} + ) + ) +) diff --git a/foreign/dotfiles/home/programs/eww/modules/music.yuck b/foreign/dotfiles/home/programs/eww/modules/music.yuck new file mode 100644 index 0000000..0b7ae88 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/music.yuck @@ -0,0 +1,28 @@ +(defwidget music-module [] + (eventbox + :onhover "${EWW_CMD} update music_reveal=true" + :onhoverlost "${EWW_CMD} update music_reveal=false" + (box + :class "module" + :space-evenly false + (box + :class "song-cover-art" + :style "background-image: url(\"${music.cover}\");") + (button + :class "module" + :onclick "${EWW_CMD} open --toggle music" + {music.status == "Stopped" || music.status == "" ? "" : + "${music.artist} - ${music.title}"}) + (box + :class "icon" + {music.static == "Stopped" || music.status == "" ? "" : music.status == "Playing" ? "" : "" }) + (revealer + :class "playctl" + :transition "slideright" + :reveal music_reveal + :duration "350ms" + (box + :class "icon" + (button :class "song-button" :onclick "playerctl previous" "") + (button :class "song-button" :onclick "playerctl play-pause" {music.status == "Playing" ? "" : ""}) + (button :class "song-button" :onclick "playerctl next" "")))))) diff --git a/foreign/dotfiles/home/programs/eww/modules/net.yuck b/foreign/dotfiles/home/programs/eww/modules/net.yuck new file mode 100644 index 0000000..3bfb4f8 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/net.yuck @@ -0,0 +1,7 @@ +(defwidget net [] + (button + :class "module icon" + :onclick "gnome-control-center &" + :tooltip {net.essid} + :style "color: ${net.color};" + {net.icon})) diff --git a/foreign/dotfiles/home/programs/eww/modules/sys.yuck b/foreign/dotfiles/home/programs/eww/modules/sys.yuck new file mode 100644 index 0000000..d5403a5 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/sys.yuck @@ -0,0 +1,32 @@ +(defwidget sys [] + (box + :class "module" + :space-evenly false + :spacing 5 + (circular-progress + :value "${EWW_CPU.avg}" + :class "cpubar" + :thickness 3 + (button + :tooltip "using ${round(EWW_CPU.avg,0)}% cpu" + :onclick "${EWW_CMD} open --toggle system-menu" + (label :class "icon-text" :text ""))) + + (circular-progress + :value {memory.percent} + :class "membar" + :thickness 3 + (button + :tooltip "using ${round(memory.percent,0)}% ram" + :onclick "${EWW_CMD} open --toggle system-menu" + (label :class "icon-text" :text ""))) + + (circular-progress + :value "${EWW_BATTERY["BAT0"].capacity}" + :class "batbar" + :style "color: ${battery.color};" + :thickness 3 + (button + :tooltip "battery on ${EWW_BATTERY["BAT0"].capacity}%" + :onclick "${EWW_CMD} open --toggle system-menu" + (label :class "icon-text" :text ""))))) diff --git a/foreign/dotfiles/home/programs/eww/modules/variables.yuck b/foreign/dotfiles/home/programs/eww/modules/variables.yuck new file mode 100644 index 0000000..6af2136 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/variables.yuck @@ -0,0 +1,21 @@ +(defvar bright_reveal false) +(defvar bt_rev false) +(defvar music_reveal false) +(defvar net_rev false) +(defvar time_rev false) +(defvar vol_reveal false) +(defvar osd-brightness false) +(defvar osd-volume false) + +(defpoll time :interval "5s" `date +'{"date": "%d/%m", "hour": "%H", "minute": "%M", "day": "%A"}'`) + +(deflisten airplane "scripts/airplane") +(deflisten battery "scripts/battery") +(deflisten bluetooth "scripts/bluetooth") +(deflisten brightness "scripts/brightness") +(deflisten memory "scripts/memory") +(deflisten music "scripts/music") +(deflisten net "scripts/net") +(deflisten volume "scripts/volume") +(deflisten workspace "scripts/workspaces") +(deflisten current_win "scripts/current_win") diff --git a/foreign/dotfiles/home/programs/eww/modules/volume.yuck b/foreign/dotfiles/home/programs/eww/modules/volume.yuck new file mode 100644 index 0000000..c4dceee --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/volume.yuck @@ -0,0 +1,11 @@ +(defwidget volume-module [] + (box + :class "module icon" + (eventbox + :onscroll "echo {} | sed -e 's/up/-/g' -e 's/down/+/g' | xargs -I% wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.005%" + :onclick "pavucontrol &" + :onrightclick "scripts/volume mute SINK" + (label + :class "vol-icon" + :tooltip "volume ${volume.percent}%" + :text {volume.icon})))) diff --git a/foreign/dotfiles/home/programs/eww/modules/workspaces.yuck b/foreign/dotfiles/home/programs/eww/modules/workspaces.yuck new file mode 100644 index 0000000..f8d0ac1 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/modules/workspaces.yuck @@ -0,0 +1,13 @@ +(defwidget workspaces [] + (eventbox + :onscroll "echo {} | sed -e \"s/up/-1/g\" -e \"s/down/+1/g\" | xargs hyprctl dispatch workspace" + (box + :class "module workspaces" + :spacing 5 + (for ws in workspace + (button + :onclick "hyprctl dispatch workspace ${ws.number}" + :class "ws icon" + :style "color: ${ws.color};" + ; :tooltip {ws.tooltip} + "●"))))) diff --git a/foreign/dotfiles/home/programs/eww/scripts/airplane b/foreign/dotfiles/home/programs/eww/scripts/airplane new file mode 100755 index 0000000..61db360 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/airplane @@ -0,0 +1,29 @@ +#!/bin/sh + +icon() { + if [ "$STATUS" = "no" ]; then + echo "" + else + echo "" + fi +} + +toggle() { + if [ "$STATUS" = "no" ]; then + rfkill block all + notify-send --urgency=normal -i airplane-mode-symbolic "Airplane Mode" "Airplane mode has been turned on!" + else + rfkill unblock all + notify-send --urgency=normal -i airplane-mode-disabled-symbolic "Airplane Mode" "Airplane mode has been turned off!" + fi +} + +if [ "$1" = "toggle" ]; then + toggle +else + while true; do + STATUS="$(rfkill list | sed -n 2p | awk '{print $3}')" + icon + sleep 3; + done +fi diff --git a/foreign/dotfiles/home/programs/eww/scripts/battery b/foreign/dotfiles/home/programs/eww/scripts/battery new file mode 100755 index 0000000..ad383d2 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/battery @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +icons=("" "" "" "" "" "" "" "") +num_icons=$(bc <<< "100 / ${#icons[@]}") + + +geticon() { + level=$(awk -v n="$CAPACITY" -v c="$num_icons" 'BEGIN{print int(n/c-1)}') + if [[ "$level" -lt 0 ]]; then + level=0 + fi + echo "${icons[$level]}" +} + +status() { + if [ "$STATE" = "Charging" ]; then + echo -n "charging" + + if [ "$RATE" -gt 0 ]; then + echo ", $(gettime) left" + else + echo "" + fi + elif [ "$STATE" = "Discharging" ]; then + echo "$(gettime)h left" + else + echo "fully charged" + fi +} + +color() { + if [ "$CAPACITY" -le 20 ]; then + echo '#f38ba8' + else + echo '#a6e3a1' + fi +} + +wattage() { + microwatts=1000000 + echo "$(bc -l <<< "scale=1; $RATE / $microwatts") W" +} + +gettime() { + FULL=$(cat /sys/class/power_supply/BAT0/energy_full) + NOW=$(cat /sys/class/power_supply/BAT0/energy_now) + + if [ "$RATE" -gt 0 ]; then + if [ "$STATE" = "Discharging" ]; then + EX="$NOW / $RATE" + else + EX="($FULL - $NOW) / $RATE" + fi + date -u -d@"$(bc -l <<< "$EX * 3600")" +%H:%M + fi +} + +while true; do + RATE=$(cat /sys/class/power_supply/BAT0/power_now) + CAPACITY=$(cat /sys/class/power_supply/BAT0/capacity) + STATE=$(cat /sys/class/power_supply/BAT0/status) + + echo '{ "icon": "'"$(geticon)"'", "percent": '"$CAPACITY"', "wattage": "'"$(wattage)"'", "status": "'"$(status)"'", "color": "'"$(color)"'" }' + sleep 3 +done \ No newline at end of file diff --git a/foreign/dotfiles/home/programs/eww/scripts/bluetooth b/foreign/dotfiles/home/programs/eww/scripts/bluetooth new file mode 100755 index 0000000..63ce547 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/bluetooth @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +toggle() { + status=$(rfkill -J | jaq -r '.rfkilldevices[] | select(.type == "bluetooth") | .soft' | head -1) + + if [ "$status" = "unblocked" ]; then + rfkill block bluetooth + else + rfkill unblock bluetooth + fi +} + +if [ "$1" = "toggle" ]; then + toggle +else + while true; do + powered=$(bluetoothctl show | rg Powered | cut -f 2- -d ' ') + status=$(bluetoothctl info) + name=$(echo "$status" | rg Name | cut -f 2- -d ' ') + mac=$(echo "$status" | head -1 | awk '{print $2}' | tr ':' '_') + + if [[ "$(echo "$status" | rg Percentage)" != "" ]]; then + battery="$(upower -i /org/freedesktop/UPower/devices/headset_dev_"$mac" | rg percentage | awk '{print $2}' | cut -f 1 -d '%')%" + else + battery="" + fi + + if [ "$powered" = "yes" ]; then + if [ "$status" != "Missing device address argument" ]; then + text="$name" + icon="" + color="#89b4fa" + class="bt-connected" + else + icon="" + text="Disconnected" + color="#45475a" + class="" + fi + else + icon="" + text="Bluetooth off" + color="#45475a" + class="" + fi + + echo '{ "icon": "'"$icon"'", "battery": "'"$battery"'", "text": "'"$text"'", "color": "'"$color"'", "class": "'"$class"'" }' + + sleep 3 + done +fi diff --git a/foreign/dotfiles/home/programs/eww/scripts/brightness b/foreign/dotfiles/home/programs/eww/scripts/brightness new file mode 100755 index 0000000..5ca2dd1 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/brightness @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +icons=("" "" "") +if [ ! "$XDG_CACHE_HOME" ]; then + XDG_CACHE_HOME="/home/mihai/.local/cache" +fi +date="$XDG_CACHE_HOME/eww/osd_brightness.date" + +osd() { + if [ ! -f "$date" ]; then + mkdir -p "$XDG_CACHE_HOME/eww" + fi + date +%s > "$date" +} + +osd_handler() { + lock=0 + rundate=0 + if [ ! -f "$date" ]; then + mkdir -p "$XDG_CACHE_HOME/eww" + echo 0 > "$date" + fi + + while true; do + # get dates + rundate=$(cat "$date") + currentdate=$(date +%s) + + # handle showing + if [ "$rundate" = "$currentdate" ] && [ "$lock" -eq 0 ]; then + eww open osd + eww update osd-brightness=true + lock=1 + elif [ "$((currentdate - rundate))" = "2" ] && [ "$lock" -eq 1 ]; then + eww update osd-brightness=false + lock=0 + if [ "$(eww get osd-brightness)" = "false" ] && [ "$(eww get osd-volume)" = "false" ]; then + eww close osd + fi + fi + + sleep 0.1 + done + + eww close osd +} + +if [ "$1" = "osd" ]; then + osd +else + osd_handler & + # initial + icon=${icons[$(awk -v n="$(light)" 'BEGIN{print int(n/34)}')]} + echo '{ "percent": '"$(light)"', "icon": "'"$icon"'" }' + + udevadm monitor | rg --line-buffered "backlight" | while read -r _; do + icon="${icons[$(awk -v n="$(light)" 'BEGIN{print int(n/34)}')]}" + + echo '{ "percent": '"$(light)"', "icon": "'"$icon"'" }' + done +fi diff --git a/foreign/dotfiles/home/programs/eww/scripts/current_win b/foreign/dotfiles/home/programs/eww/scripts/current_win new file mode 100755 index 0000000..10ee3d2 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/current_win @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +socat -u UNIX-CLIENT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock STDOUT | +while read line; do + if [[ "$line" == activewindow\>\>* ]]; then + windowtitle="$(echo $line | cut -d',' -f 2)" + echo '{"title": "'$windowtitle'"}' + fi +done + diff --git a/foreign/dotfiles/home/programs/eww/scripts/memory b/foreign/dotfiles/home/programs/eww/scripts/memory new file mode 100755 index 0000000..0ae499b --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/memory @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +while true; do + # human-readable + freeH=$(free -h --si | rg "Mem:") + # non-human-readable + freeN=$(free --mega | rg "Mem:") + + total="$(echo "$freeH" | awk '{ print $2 }')" + used="$(echo "$freeH" | awk '{ print $3 }')" + t="$(echo "$freeN" | awk '{ print $2 }')" + u="$(echo "$freeN" | awk '{ print $3 }')" + + free=$(printf '%.1fG' "$(bc -l <<< "($t - $u) / 1000")") + perc=$(printf '%.1f' "$(free -m | rg Mem | awk '{print ($3/$2)*100}')") + + echo '{ "total": "'"$total"'", "used": "'"$used"'", "free": "'"$free"'", "percent": '"$perc"' }' + + sleep 3 +done diff --git a/foreign/dotfiles/home/programs/eww/scripts/music b/foreign/dotfiles/home/programs/eww/scripts/music new file mode 100755 index 0000000..3128109 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/music @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +get_status() { + s=$1 + if [ "$s" = "Playing" ]; then + echo "" + else + echo "" + fi +} + +get_length_sec() { + len=$1 + if [ -z "$len" ]; then + echo 0 + else + bc <<<"$len / 1000000" + fi +} + +get_length_time() { + len=$1 + if [ -n "$len" ]; then + len=$(bc <<<"$len / 1000000 + 1") + date -d@"$len" +%M:%S + else + echo "" + fi +} + +get_position() { + pos=$1 + len=$2 + if [ -n "$pos" ]; then + bc -l <<<"$pos / $len * 100" + else + echo 0 + fi +} + +get_position_time() { + pos=$1 + len=$2 + if [ -n "$pos" ]; then + date -d@"$(bc <<<"$pos / 1000000")" +%M:%S + else + echo "" + fi +} + +get_cover() { + mkdir -p "$XDG_CACHE_HOME/eww_covers" + cd "$XDG_CACHE_HOME/eww_covers" || exit + + IMGPATH="$XDG_CACHE_HOME/eww_covers/cover_art.png" + + COVER_URL="$1" + + if [[ "$COVER_URL" = https* ]]; then + if [ ! -e "$XDG_CACHE_HOME/eww_covers/$(basename "$COVER_URL")" ]; then + wget -N "$COVER_URL" -o /dev/null + fi + + rm "$IMGPATH" + ln -s "$(basename "$COVER_URL")" "$IMGPATH" + + IMG="${IMGPATH}" + elif [ "$COVER_URL" = "" ]; then + IMG="" + else + IMG="$COVER_URL" + fi + + echo "$IMG" +} + +sanitize() { + echo "$1" | sed 's/"/\"/g' +} + +prevCover='' + +playerctl -F metadata -f '{{title}}\{{artist}}\{{status}}\{{position}}\{{mpris:length}}\{{mpris:artUrl}}' 2>/dev/null | while IFS="$(printf '\')" read -r title artist status position len cover; do + if [[ "$cover" != "$prevCover" ]]; then + COVER=$(get_cover "$cover") + + if [ "$COVER" != "" ]; then + cols=$(convert "$COVER" -colors 2 -format "%c" histogram:info: | awk '{print $3}') + color1=$(echo "$cols" | head -1) + color1=$(printf "rgba(%d, %d, %d, 0.6)" ${color1:1:2} ${color1:3:2} ${color1:5:2}) + color2=$(echo "$cols" | tail -1) + else + color1="#1e1e2e" + color2="#28283d" + fi + fi + + jaq --null-input -r -c \ + --arg artist "$(sanitize "$artist")" \ + --arg title "$(sanitize "$title")" \ + --arg status "$status" \ + --arg pos "$(get_position "$position" "$len")" \ + --arg pos_time "$(get_position_time "$position" "$len")" \ + --arg length "$(get_length_time "$len")" \ + --arg cover "$COVER" \ + --arg color1 "$color1" \ + --arg color2 "$color2" \ + '{"artist": $artist, "title": $title, "status": $status, "position": $pos, "position_time": $pos_time, "length": $length, "cover": $cover, "color1": $color1, "color2": $color2}' + + prevCover=$cover +done diff --git a/foreign/dotfiles/home/programs/eww/scripts/net b/foreign/dotfiles/home/programs/eww/scripts/net new file mode 100755 index 0000000..a84b9b9 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/net @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +toggle() { + status=$(rfkill -J | jaq -r '.rfkilldevices[] | select(.type == "wlan") | .soft' | head -1) + + if [ "$status" = "unblocked" ]; then + rfkill block wlan + else + rfkill unblock wlan + fi +} + +if [ "$1" = "toggle" ]; then + toggle +else + while true; do + status=$(nmcli -f state g| tail -1) + wifistatus=$(nmcli -t -f in-use,ssid,signal dev wifi | rg '\*' | sed 's/\"/\\"/g') + signal=$(echo "$wifistatus" | awk -F: '{print $3}') + essid=$(echo "$wifistatus" | awk -F: '{print $2}') + + icons=("" "" "" "" "") + + if [ "$status" = "disconnected" ] ; then + icon="" + color="#988ba2" + class="" + else + level=$(awk -v n="$signal" 'BEGIN{print int((n-1)/20)}') + if [ "$level" -gt 4 ]; then + level=4 + fi + + icon=${icons[$level]} + color="#cba6f7" + class="wifi-connected" + fi + + echo '{ "essid": "'"$essid"'", "icon": "'"$icon"'", "color": "'"$color"'", "class": "'"$class"'" }' + + sleep 3 + done +fi diff --git a/foreign/dotfiles/home/programs/eww/scripts/volume b/foreign/dotfiles/home/programs/eww/scripts/volume new file mode 100755 index 0000000..7e9b11d --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/volume @@ -0,0 +1,102 @@ +#!/usr/bin/env bash + +volicons=("" "" "") +if [ ! "$XDG_CACHE_HOME" ]; then + XDG_CACHE_HOME="/home/mihai/.local/cache" +fi +date="$XDG_CACHE_HOME/eww/osd_vol.date" + +vol() { + wpctl get-volume @DEFAULT_AUDIO_"$1"@ | awk '{print int($2*100)}' +} +ismuted() { + wpctl get-volume @DEFAULT_AUDIO_"$1"@ | rg -i muted + echo $? +} +setvol() { + wpctl set-volume @DEFAULT_AUDIO_"$1"@ "$(awk -v n="$2" 'BEGIN{print (n / 100)}')" +} +setmute() { + wpctl set-mute @DEFAULT_AUDIO_"$1"@ toggle +} + +osd() { + if [ ! -f "$date" ]; then + mkdir -p "$XDG_CACHE_HOME/eww" + fi + date +%s > "$date" +} + +osd_handler() { + lock=0 + rundate=0 + if [ ! -f "$date" ]; then + mkdir -p "$XDG_CACHE_HOME/eww" + echo 0 > "$date" + fi + + while true; do + # get dates + rundate=$(cat "$date") + currentdate=$(date +%s) + + # handle showing + if [ "$rundate" = "$currentdate" ] && [ "$lock" -eq 0 ]; then + eww open osd + eww update osd-volume=true + lock=1 + elif [ "$((currentdate - rundate))" = "2" ] && [ "$lock" -eq 1 ]; then + eww update osd-volume=false + lock=0 + if [ "$(eww get osd-volume)" = "false" ] && [ "$(eww get osd-brightness)" = "false" ]; then + eww close osd + fi + fi + + sleep 0.1 + done + + eww close osd +} + +if [ "$1" = "mute" ]; then + if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then + echo "Can only mute SINK or SOURCE"; exit 1 + fi + setmute "$2" +elif [ "$1" = "setvol" ]; then + if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then + echo "Can only set volume for SINK or SOURCE"; exit 1 + elif [ "$3" -lt 1 ] || [ "$3" -gt 100 ]; then + echo "Volume must be between 1 and 100"; exit 1 + fi + setvol "$2" "$3" +elif [ "$1" = "osd" ]; then + osd +else + # initial values + lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}') + ismuted=$(ismuted "SINK") + + if [ "$ismuted" = 1 ]; then + icon="${volicons[$lvl]}" + else + icon="" + fi + echo '{ "icon": "'"$icon"'", "percent": "'"$(vol "SINK")"'", "microphone": "'"$(vol "SOURCE")"'" }' + + osd_handler & + + # event loop + pactl subscribe | rg --line-buffered "change" | while read -r _; do + lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}') + ismuted=$(ismuted "SINK") + + if [ "$ismuted" = 1 ]; then + icon="${volicons[$lvl]}" + else + icon="" + fi + echo '{ "icon": "'"$icon"'", "percent": "'"$(vol "SINK")"'", "microphone": "'"$(vol "SOURCE")"'" }' + done +fi diff --git a/foreign/dotfiles/home/programs/eww/scripts/workspaces b/foreign/dotfiles/home/programs/eww/scripts/workspaces new file mode 100755 index 0000000..c7caf95 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/scripts/workspaces @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +# define colors +# red peach green blue +colors=("#f38ba8" "#fab387" "#a6e3a1" "#89b4fa") +# pink yellow teal lavender +dimmed=("#f5c2e7" "#f9e2af" "#94e2d5" "#b4befe") +empty="#313244" + +# get initial focused workspace +focusedws=$(hyprctl -j monitors | jaq -r '.[] | select(.focused == true) | .activeWorkspace.id') + +declare -A o=([1]=0 [2]=0 [3]=0 [4]=0) +declare -A monitormap +declare -A workspaces + +# set color for each workspace +status() { + if [ "${o[$1]}" -eq 1 ]; then + mon=${monitormap[${workspaces[$1]}]} + + if [ "$focusedws" -eq "$1" ]; then + echo -n "#27ae60" + # echo -n "${colors[$mon]}" + else + # echo -n "${dimmed[$mon]}" + echo -n "#fdbc4b" + fi + else + # echo -n "$empty" + echo -n "#2f343f" + fi +} + +# handle workspace create/destroy +workspace_event() { + o[$1]=$2 + while read -r k v; do workspaces[$k]="$v"; done < <(hyprctl -j workspaces | gojq -r '.[]|"\(.id) \(.monitor)"') +} +# handle monitor (dis)connects +monitor_event() { + while read -r k v; do monitormap["$k"]=$v; done < <(hyprctl -j monitors | gojq -r '.[]|"\(.name) \(.id) "') +} + +# get all apps titles in a workspace +applist() { + ws="$1" + + apps=$(hyprctl -j clients | jaq -jr '.[] | select(.workspace.id == '"$ws"') | .title + "\\n"') + echo -En "${apps%"\n"}" +} + +# generate the json for eww +generate() { + echo -n '[' + + for i in {1..4}; do + echo -n ''"$([ "$i" -eq 1 ] || echo ,)" '{ "number": "'"$i"'", "color": "'"$(status "$i")"'" }' #, "tooltip": "'$(applist "$i")'" }' + done + + echo ']' +} + +# setup + +# add monitors +monitor_event + +# add workspaces +while read -r k v; do workspaces[$k]="$v"; done < <(hyprctl -j workspaces | gojq -r '.[]|"\(.id) \(.monitor)"') + +# check occupied workspaces +for num in "${!workspaces[@]}"; do + o[$num]=1 +done +# generate initial widget +generate + +# main loop +socat -u UNIX-CONNECT:/tmp/hypr/"$HYPRLAND_INSTANCE_SIGNATURE"/.socket2.sock - | rg --line-buffered "workspace|mon(itor)?" | while read -r line; do + case ${line%>>*} in + "workspace") + focusedws=${line#*>>} + ;; + "focusedmon") + focusedws=${line#*,} + ;; + "createworkspace") + workspace_event "${line#*>>}" 1 + ;; + "destroyworkspace") + workspace_event "${line#*>>}" 0 + ;; + "monitor"*) + monitor_event + ;; + esac + generate +done diff --git a/foreign/dotfiles/home/programs/eww/windows/calendar.yuck b/foreign/dotfiles/home/programs/eww/windows/calendar.yuck new file mode 100644 index 0000000..403d732 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/windows/calendar.yuck @@ -0,0 +1,14 @@ +(defwidget calendar-win [] + (box + :class "calendar-win" + (calendar))) + +(defwindow calendar + :monitor 0 + :geometry (geometry + :x "0%" + :y "0%" + :anchor "top right" + :width "0px" + :height "0px") + (calendar-win)) diff --git a/foreign/dotfiles/home/programs/eww/windows/music.yuck b/foreign/dotfiles/home/programs/eww/windows/music.yuck new file mode 100644 index 0000000..4e7d5e7 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/windows/music.yuck @@ -0,0 +1,55 @@ +(defwidget music [] + (box + :class "music-window" + :space-evenly false + :style "background-color: ${music.color1}; border: 1px solid ${music.color2};" + (box + :class "music-cover-art" + :style "background-image: url(\"${music.cover}\");") + (box + :orientation "v" + :class "music-box" + (label + :class "music-title" + :wrap true + :text {music.title}) + (label + :class "music-artist" + :wrap true + :text {music.artist}) + (centerbox + :halign "center" + :class "music-button-box icon" + (button :class "music-button" :onclick "playerctl previous" "") + (button :class "music-button" :onclick "playerctl play-pause" {music.status}) + (button :class "music-button" :onclick "playerctl next" "")) + (box + :orientation "v" + (box + (label + :xalign 0 + :class "music-time" + :text {music.position_time}) + (label + :xalign 1 + :class "music-time" + :text {music.length})) + (box + :class "music-bar" + (scale + ; doesn't work, looking for other ways + ; :style "background: linear-gradient(to right, ${music.color1}, ${music.color2});" + :onchange "playerctl position `bc <<< \"{} * $(playerctl metadata mpris:length) / 1000000 / 100\"`" + :value {music.position})))))) + +(defwindow music + :stacking "fg" + :focusable false + :monitor 0 + :geometry (geometry + :x "0%" + :y "0%" + :width "0%" + :height "0%" + :anchor "top center") + (music)) diff --git a/foreign/dotfiles/home/programs/eww/windows/osd.yuck b/foreign/dotfiles/home/programs/eww/windows/osd.yuck new file mode 100644 index 0000000..138bc06 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/windows/osd.yuck @@ -0,0 +1,43 @@ +(defwidget osd-part [icon value class] + (box + :class "osd-part osd-${class}" + :orientation "v" + :space-evenly false + (scale + :flipped true + :orientation "v" + :value value) + (label + :text icon))) + +(defwidget osd [] + (box + :class "osd-container" + :space-evenly false + (revealer + :reveal {osd-brightness} + :transition "slideright" + (osd-part + :class "brightness" + :icon {brightness.icon} + :value {brightness.percent})) + (revealer + :reveal {osd-volume} + :transition "slideright" + (osd-part + :class "volume" + :icon {volume.icon} + :value {volume.percent}))) +) + +(defwindow osd + :stacking "fg" + :focusable false + :monitor 0 + :geometry (geometry + :x "1%" + :y "0%" + :width "0%" + :height "0%" + :anchor "center left") + (osd)) diff --git a/foreign/dotfiles/home/programs/eww/windows/system-menu.yuck b/foreign/dotfiles/home/programs/eww/windows/system-menu.yuck new file mode 100644 index 0000000..888b178 --- /dev/null +++ b/foreign/dotfiles/home/programs/eww/windows/system-menu.yuck @@ -0,0 +1,204 @@ +(defwidget system-menu [] + (box + :class "system-menu-box" + :space-evenly false + :orientation "v" + (box + :class "top-row" + :space-evenly false + (label + :class "time" + :text "${time.hour}:${time.minute}") + (box + :class "date-box" + :space-evenly false + (label + :class "date" + :text {time.date}) + (label + :class "day" + :text {time.day}))) + + (centerbox + :class "system-row" + (box + :class "wifi-box" + :space-evenly false + :orientation "v" + (box + :class "element icon ${net.class}" + :space-evenly false + (button + :class "wifi-button" + :onclick "scripts/net toggle" + {net.icon}) + (label + :class "separator" + :text "│") + (button + :class "wifi-arrow-btn" + :onclick "eww close system-menu && gnome-control-center &" + "")) + (label + :text {net.essid} + :xalign 0.5 + :limit-width 15)) + + (box + :class "bluetooth-box" + :space-evenly false + :orientation "v" + (box + :class "element icon ${bluetooth.class}" + :space-evenly false + (button + :class "bluetooth-button" + :onclick "scripts/bluetooth toggle" + {bluetooth.icon}) + (label + :class "separator" + :text "│") + (button + :class "bluetooth-arrow-btn" + :onclick "eww close system-menu && blueberry" + "")) + (label + :text {bluetooth.text} + :xalign 0.5 + :tooltip "${bluetooth.text} ${bluetooth.battery}" + :limit-width 15)) + + (box + :class "airplane-box" + :space-evenly false + :orientation "v" + (box + :class "element" + (button + :class "airplane-button" + :onclick "scripts/airplane toggle" + airplane)) + (label + :text "Airplane Mode" + :xalign 0.5 + :limit-width 16))) + + (box + :class "sliders" + :orientation "v" + (box + :class "volume-slider-box" + :space-evenly false + (button + :class "volume-icon icon" + :onclick "scripts/volume mute SINK" + {volume.icon}) + (scale + :class "volume-bar" + :value {volume.percent} + :tooltip "volume on ${volume.percent}%" + :onchange "scripts/volume setvol SINK {}")) + (box + :class "brightness-slider-box" + :space-evenly false + (button + :class "brightness-slider-icon icon" + {brightness.icon}) + (scale + :class "brightness-slider" + :value {brightness.percent} + :marks true + :onchange "light -S {}"))) + + (box + :class "system-info-box" + + ; cpu + (box + :class "sys-box" + :space-evenly false + :halign "start" + (circular-progress + :value "${EWW_CPU.avg}" + :class "sys-cpu" + :thickness 3 + (label + :text "" + :class "sys-icon-cpu icon")) + (box + :orientation "v" + :vexpand false + (label + :text "cpu" + :halign "start" + :class "sys-text-cpu") + (label + :text "${round(EWW_CPU.avg,2)}%" + :halign "start" + :class "sys-text-sub") + (label + :text "${EWW_CPU.cores[0].freq} MHz" + :halign "start" + :class "sys-text-sub"))) + + ; memory + (box + :class "sys-box" + :space-evenly false + :halign "end" + (circular-progress + :value {memory.percent} + :class "sys-mem" + :thickness 3 + (label + :text "" + :class "sys-icon-mem icon")) + (box + :orientation "v" + (label + :text "memory" + :halign "start" + :class "sys-text-mem") + (label + :text "${memory.used} | ${memory.total}" + :halign "start" + :class "sys-text-sub")))) + + (centerbox + :class "bottom-row" + (box + :class "battery-box" + :space-evenly false + :halign "start" + (label + :class "battery-icon icon" + :style "color: ${battery.color}" + :text {battery.icon}) + (label + :text {EWW_BATTERY["BAT0"].capacity}) + (label + :class "battery-status" + :text {battery.status}) + (label + :class "battery-wattage" + :text {battery.wattage})) + (label) + (box + :space-evenly false + :halign "end" + (button + :halign "end" + :class "power-button icon" + :onclick "wlogout -p layer-shell &" + ""))))) + +(defwindow system-menu + :stacking "fg" + :monitor 0 + :geometry (geometry + :x "0" + :y "0" + :width "0%" + :height "0%" + :anchor "right top") + (system-menu)) diff --git a/foreign/dotfiles/home/programs/files/config/wofi/config b/foreign/dotfiles/home/programs/files/config/wofi/config new file mode 100644 index 0000000..b37c110 --- /dev/null +++ b/foreign/dotfiles/home/programs/files/config/wofi/config @@ -0,0 +1,7 @@ +width=40% +height=30% +show=drun +prompt=Search +allow_images=true +allow_markup=true +insensitive=true \ No newline at end of file diff --git a/foreign/dotfiles/home/programs/files/config/youtube-dl/config b/foreign/dotfiles/home/programs/files/config/youtube-dl/config new file mode 100644 index 0000000..759e1c7 --- /dev/null +++ b/foreign/dotfiles/home/programs/files/config/youtube-dl/config @@ -0,0 +1,25 @@ +# youtube-dl config file + +# use .netrc for logins +-n + +# ignore errors (unavailable videos, etc) +-i + +# don't overwrite +-w + +# make an archive of downloaded videos and only download unlisted items, then list them +# useful for music playlists you update often +--download-archive ~/Music/untagged/ignore.these + +# convert all downloads to 192kbps mp3 +-x +--audio-format mp3 #opus +--audio-quality 192K + +# embed thumbnail into audio +#--embed-thumbnail + +# save all music in the following folder and format +-o '~/Music/untagged/%(artist)s - %(title)s.%(ext)s' diff --git a/foreign/dotfiles/home/programs/files/default.nix b/foreign/dotfiles/home/programs/files/default.nix new file mode 100644 index 0000000..2c42964 --- /dev/null +++ b/foreign/dotfiles/home/programs/files/default.nix @@ -0,0 +1,34 @@ +{ + pkgs, + lib, + config, + ... +}: +# manage files in ~ +{ + imports = [ + ./nix-index-update-db.nix + ./wlogout.nix + ./wofi-style.nix + ]; + + home.file.".config" = { + source = ./config; + recursive = true; + }; + + xdg.configFile = { + "btop/themes/catppuccin_mocha.theme".source = pkgs.fetchurl { + url = "https://raw.githubusercontent.com/catppuccin/btop/main/catppuccin_mocha.theme"; + hash = "sha256-MGK5ECB5sXiHdi2A3Y4s/Sx7nSRQ+KLyZjEKElRPKf0="; + }; + + "xilinx/nix.sh" = { + executable = true; + text = '' + INSTALL_DIR=$HOME/Documents/code/xilinx/tools/Xilinx + VERSION=2022.2 + ''; + }; + }; +} diff --git a/foreign/dotfiles/home/programs/files/nix-index-update-db.nix b/foreign/dotfiles/home/programs/files/nix-index-update-db.nix new file mode 100644 index 0000000..93b76a9 --- /dev/null +++ b/foreign/dotfiles/home/programs/files/nix-index-update-db.nix @@ -0,0 +1,35 @@ +{ + pkgs, + lib, + ... +}: { + # set up nix-index + systemd.user.timers.nix-index-db-update = { + Timer = { + OnCalendar = "weekly"; + Persistent = true; + RandomizedDelaySec = 0; + }; + }; + + systemd.user.services.nix-index-db-update = { + Unit = { + Description = "nix-index database update"; + PartOf = ["multi-user.target"]; + }; + Service = let + script = pkgs.writeShellScript "nix-index-update-db" '' + export filename="index-x86_64-$(uname | tr A-Z a-z)" + mkdir -p ~/.cache/nix-index + cd ~/.cache/nix-index + # -N will only download a new version if there is an update. + wget -N https://github.com/Mic92/nix-index-database/releases/latest/download/$filename + ln -f $filename files + ''; + in { + Environment = "PATH=/run/wrappers/bin:${lib.makeBinPath [pkgs.wget pkgs.coreutils]}"; + ExecStart = "${script}"; + }; + Install.WantedBy = ["multi-user.target"]; + }; +} diff --git a/foreign/dotfiles/home/programs/files/wlogout.nix b/foreign/dotfiles/home/programs/files/wlogout.nix new file mode 100644 index 0000000..0633039 --- /dev/null +++ b/foreign/dotfiles/home/programs/files/wlogout.nix @@ -0,0 +1,57 @@ +{ + default, + pkgs, + ... +}: let + w = pkgs.wlogout; +in { + xdg.configFile."wlogout/style.css".text = '' + * { + background-image: none; + font-family: "Jost *", Roboto, sans-serif; + } + window { + background-color: rgba(12, 12, 12, 0.9); + } + button { + background: unset; + border-radius: 16px; + border: 1px solid #28283d; + color: ${default.xcolors.text}; + margin: 1rem; + background-repeat: no-repeat; + background-position: center; + background-size: 25%; + } + + button:focus, button:active, button:hover { + background-color: ${default.xcolors.blue}; + color: ${default.xcolors.base}; + outline-style: none; + } + + #lock { + background-image: image(url("${w}/share/wlogout/icons/lock.png"), url("${w}/local/share/wlogout/icons/lock.png")); + } + + #logout { + background-image: image(url("${w}/share/wlogout/icons/logout.png"), url("${w}/local/share/wlogout/icons/logout.png")); + } + + #suspend { + background-image: image(url("${w}/share/wlogout/icons/suspend.png"), url("${w}/local/share/wlogout/icons/suspend.png")); + } + + #hibernate { + background-image: image(url("${w}/share/wlogout/icons/hibernate.png"), url("${w}/local/share/wlogout/icons/hibernate.png")); + } + + #shutdown { + background-image: image(url("${w}/share/wlogout/icons/shutdown.png"), url("${w}/local/share/wlogout/icons/shutdown.png")); + } + + #reboot { + background-image: image(url("${w}/share/wlogout/icons/reboot.png"), url("${w}/local/share/wlogout/icons/reboot.png")); + } + ''; +} diff --git a/foreign/dotfiles/home/programs/files/wofi-style.nix b/foreign/dotfiles/home/programs/files/wofi-style.nix new file mode 100644 index 0000000..614a79c --- /dev/null +++ b/foreign/dotfiles/home/programs/files/wofi-style.nix @@ -0,0 +1,46 @@ +{default, ...}: { + xdg.configFile."wofi/style.css".text = '' + window { background: unset; } + flowboxchild { outline-width: 0; } + + #outer-box { + background: ${default.xcolors.base}; + border: 1px solid ${default.xcolors.border}; + border-radius: 24px; + box-shadow: 0 2px 3px ${default.xcolors.crust}; + margin: 5px 5px 10px; + padding: 5px 5px 10px; + } + + #input { + background-color: ${default.xcolors.crust}; + border: none; + border-radius: 16px; + color: ${default.xcolors.text}; + margin: 5px; + } + + #inner-box { + background-color: ${default.xcolors.base}; + border: none; + border-radius: 16px; + margin: 5px; + } + + #scroll { + border: none; + margin: 0px; + } + + #text { + color: ${default.xcolors.text}; + margin: 5px; + } + + #entry { border-radius: 16px; } + + #entry:selected { + background-color: ${default.xcolors.surface0}; + } + ''; +} diff --git a/foreign/dotfiles/home/programs/games.nix b/foreign/dotfiles/home/programs/games.nix new file mode 100644 index 0000000..9973495 --- /dev/null +++ b/foreign/dotfiles/home/programs/games.nix @@ -0,0 +1,12 @@ +{ + pkgs, + inputs, + ... +}: +# games +{ + home.packages = with pkgs; [ + inputs.nix-gaming.packages.${pkgs.hostPlatform.system}.osu-lazer-bin + gamescope + ]; +} diff --git a/foreign/dotfiles/home/programs/git.nix b/foreign/dotfiles/home/programs/git.nix new file mode 100644 index 0000000..b2648dc --- /dev/null +++ b/foreign/dotfiles/home/programs/git.nix @@ -0,0 +1,52 @@ +{ + pkgs, + default, + ... +}: { + home.packages = [pkgs.gh]; + + programs.git = { + enable = true; + + delta = { + enable = true; + options.map-styles = "bold purple => syntax ${default.xcolors.mauve}, bold cyan => syntax ${default.xcolors.blue}"; + }; + + extraConfig = { + diff.colorMoved = "default"; + merge.conflictstyle = "diff3"; + }; + + aliases = { + a = "add"; + b = "branch"; + c = "commit"; + ca = "commit --amend"; + cm = "commit -m"; + co = "checkout"; + d = "diff"; + ds = "diff --staged"; + p = "push"; + pf = "push --force-with-lease"; + pl = "pull"; + l = "log"; + r = "rebase"; + s = "status --short"; + ss = "status"; + forgor = "commit --amend --no-edit"; + graph = "log --all --decorate --graph --oneline"; + oops = "checkout --"; + }; + + ignores = ["*~" "*.swp" "*result*" ".direnv" "node_modules"]; + + signing = { + key = "5899325F2F120900"; + signByDefault = true; + }; + + userEmail = "fufexan@protonmail.com"; + userName = "Mihai Fufezan"; + }; +} diff --git a/foreign/dotfiles/home/programs/gtk.nix b/foreign/dotfiles/home/programs/gtk.nix new file mode 100644 index 0000000..4e324ac --- /dev/null +++ b/foreign/dotfiles/home/programs/gtk.nix @@ -0,0 +1,38 @@ +{ + pkgs, + config, + ... +}: { + home.pointerCursor = { + package = pkgs.bibata-cursors; + name = "Bibata-Modern-Classic"; + size = 24; + gtk.enable = true; + x11.enable = true; + }; + + gtk = { + enable = true; + + font = { + name = "Roboto"; + package = pkgs.roboto; + }; + + gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc"; + + iconTheme = { + name = "Papirus-Dark"; + package = pkgs.papirus-icon-theme; + }; + + theme = { + name = "Catppuccin-Mocha-Compact-Mauve-Dark"; + package = pkgs.catppuccin-gtk.override { + accents = ["mauve"]; + size = "compact"; + variant = "mocha"; + }; + }; + }; +} diff --git a/foreign/dotfiles/home/programs/media.nix b/foreign/dotfiles/home/programs/media.nix new file mode 100644 index 0000000..6d4b279 --- /dev/null +++ b/foreign/dotfiles/home/programs/media.nix @@ -0,0 +1,55 @@ +{ + pkgs, + config, + ... +}: +# media - control and enjoy audio/video +{ + imports = [ + ./spicetify.nix + ]; + + home.packages = with pkgs; [ + # audio control + pavucontrol + playerctl + pulsemixer + # images + imv + + spotify-tui + ]; + + programs = { + mpv = { + enable = true; + defaultProfiles = ["gpu-hq"]; + scripts = [pkgs.mpvScripts.mpris]; + }; + + obs-studio.enable = true; + }; + + services = { + playerctld.enable = true; + + spotifyd = { + enable = true; + package = pkgs.spotifyd.override {withMpris = true;}; + settings.global = { + autoplay = true; + backend = "pulseaudio"; + bitrate = 320; + cache_path = "${config.xdg.cacheHome}/spotifyd"; + device_type = "computer"; + initial_volume = "100"; + password_cmd = "tail -1 /run/agenix/spotify"; + use_mpris = true; + username_cmd = "head -1 /run/agenix/spotify"; + volume_normalisation = false; + }; + }; + + udiskie.enable = true; + }; +} diff --git a/foreign/dotfiles/home/programs/packages.nix b/foreign/dotfiles/home/programs/packages.nix new file mode 100644 index 0000000..e666596 --- /dev/null +++ b/foreign/dotfiles/home/programs/packages.nix @@ -0,0 +1,33 @@ +{ + pkgs, + inputs, + ... +}: { + home.packages = with pkgs; [ + # archives + zip + unzip + unrar + + # office + libreoffice + + # messaging + tdesktop + + # school stuff + inputs.nix-xilinx.packages.${pkgs.hostPlatform.system}.vivado + jetbrains.idea-community + + # torrents + transmission-remote-gtk + + # misc + libnotify + xdg-utils + + # productivity + obsidian + xournalpp + ]; +} diff --git a/foreign/dotfiles/home/programs/qt.nix b/foreign/dotfiles/home/programs/qt.nix new file mode 100644 index 0000000..f592842 --- /dev/null +++ b/foreign/dotfiles/home/programs/qt.nix @@ -0,0 +1,18 @@ +{pkgs, ...}: +# Qt theming with Kvantum +{ + home.packages = with pkgs; [ + libsForQt5.qtstyleplugin-kvantum + (catppuccin-kvantum.override { + accent = "Mauve"; + variant = "Mocha"; + }) + ]; + home.sessionVariables = { + QT_STYLE_OVERRIDE = "kvantum"; + }; + + xdg.configFile."Kvantum/kvantum.kvconfig".source = (pkgs.formats.ini {}).generate "kvantum.kvconfig" { + General.Theme = "Catppuccin-Mocha-Mauve"; + }; +} diff --git a/foreign/dotfiles/home/programs/spicetify.nix b/foreign/dotfiles/home/programs/spicetify.nix new file mode 100644 index 0000000..3871515 --- /dev/null +++ b/foreign/dotfiles/home/programs/spicetify.nix @@ -0,0 +1,24 @@ +{ + pkgs, + inputs, + ... +}: { + # themable spotify + programs.spicetify = let + spicePkgs = inputs.spicetify-nix.packages.${pkgs.hostPlatform.system}.default; + in { + enable = true; + + spotifyPackage = inputs.self.packages.${pkgs.hostPlatform.system}.spotify; + + theme = spicePkgs.themes.catppuccin-mocha; + + colorScheme = "flamingo"; + + enabledExtensions = with spicePkgs.extensions; [ + fullAppDisplay + hidePodcasts + shuffle + ]; + }; +} diff --git a/foreign/dotfiles/home/programs/xdg.nix b/foreign/dotfiles/home/programs/xdg.nix new file mode 100644 index 0000000..1b22a8a --- /dev/null +++ b/foreign/dotfiles/home/programs/xdg.nix @@ -0,0 +1,47 @@ +{config, ...}: let + browser = ["firefox.desktop"]; + + # XDG MIME types + associations = { + "application/x-extension-htm" = browser; + "application/x-extension-html" = browser; + "application/x-extension-shtml" = browser; + "application/x-extension-xht" = browser; + "application/x-extension-xhtml" = browser; + "application/xhtml+xml" = browser; + "text/html" = browser; + "x-scheme-handler/about" = browser; + "x-scheme-handler/chrome" = ["chromium-browser.desktop"]; + "x-scheme-handler/ftp" = browser; + "x-scheme-handler/http" = browser; + "x-scheme-handler/https" = browser; + "x-scheme-handler/unknown" = browser; + + "audio/*" = ["mpv.desktop"]; + "video/*" = ["mpv.dekstop"]; + "image/*" = ["imv.desktop"]; + "application/json" = browser; + "application/pdf" = ["org.pwmt.zathura.desktop.desktop"]; + "x-scheme-handler/discord" = ["discordcanary.desktop"]; + "x-scheme-handler/spotify" = ["spotify.desktop"]; + "x-scheme-handler/tg" = ["telegramdesktop.desktop"]; + }; +in { + xdg = { + enable = true; + cacheHome = config.home.homeDirectory + "/.local/cache"; + + mimeApps = { + enable = true; + defaultApplications = associations; + }; + + userDirs = { + enable = true; + createDirectories = true; + extraConfig = { + XDG_SCREENSHOTS_DIR = "${config.xdg.userDirs.pictures}/Screenshots"; + }; + }; + }; +} diff --git a/foreign/dotfiles/home/programs/zathura.nix b/foreign/dotfiles/home/programs/zathura.nix new file mode 100644 index 0000000..98542d8 --- /dev/null +++ b/foreign/dotfiles/home/programs/zathura.nix @@ -0,0 +1,27 @@ +{pkgs, ...}: { + programs.zathura = { + enable = true; + options = { + recolor-lightcolor = "rgba(0,0,0,0)"; + default-bg = "rgba(0,0,0,0.7)"; + + font = "Lexend 12"; + selection-notification = true; + + selection-clipboard = "clipboard"; + adjust-open = "best-fit"; + pages-per-row = "1"; + scroll-page-aware = "true"; + scroll-full-overlap = "0.01"; + scroll-step = "100"; + zoom-min = "10"; + }; + + extraConfig = "include catppuccin-mocha"; + }; + + xdg.configFile."zathura/catppuccin-mocha".source = pkgs.fetchurl { + url = "https://raw.githubusercontent.com/catppuccin/zathura/main/src/catppuccin-mocha"; + hash = "sha256-/HXecio3My2eXTpY7JoYiN9mnXsps4PAThDPs4OCsAk="; + }; +} diff --git a/foreign/dotfiles/home/shell/cli.nix b/foreign/dotfiles/home/shell/cli.nix new file mode 100644 index 0000000..ef2dfde --- /dev/null +++ b/foreign/dotfiles/home/shell/cli.nix @@ -0,0 +1,49 @@ +{pkgs, ...}: { + home.packages = with pkgs; [ + # archives + zip + unzip + unrar + + # utils + file + du-dust + duf + fd + ripgrep + + # file managers + joshuto + ranger + ]; + + programs = { + bat = { + enable = true; + config = { + pager = "less -FR"; + theme = "Catppuccin-mocha"; + }; + themes = { + Catppuccin-mocha = builtins.readFile (pkgs.fetchurl { + url = "https://raw.githubusercontent.com/catppuccin/bat/main/Catppuccin-mocha.tmTheme"; + hash = "sha256-qMQNJGZImmjrqzy7IiEkY5IhvPAMZpq0W6skLLsng/w="; + }); + }; + }; + + btop.enable = true; + exa.enable = true; + ssh.enable = true; + + skim = { + enable = true; + enableZshIntegration = true; + defaultCommand = "rg --files --hidden"; + changeDirWidgetOptions = [ + "--preview 'exa --icons --git --color always -T -L 3 {} | head -200'" + "--exact" + ]; + }; + }; +} diff --git a/foreign/dotfiles/home/shell/default.nix b/foreign/dotfiles/home/shell/default.nix new file mode 100644 index 0000000..a2fe57a --- /dev/null +++ b/foreign/dotfiles/home/shell/default.nix @@ -0,0 +1,28 @@ +{config, ...}: let + d = config.xdg.dataHome; + c = config.xdg.configHome; + cache = config.xdg.cacheHome; +in { + imports = [ + ./cli.nix + ./nushell + ./starship.nix + ./transient-services.nix + ./zsh.nix + ]; + + # add environment variables + home.sessionVariables = { + # clean up ~ + LESSHISTFILE = cache + "/less/history"; + LESSKEY = c + "/less/lesskey"; + WINEPREFIX = d + "/wine"; + XAUTHORITY = "$XDG_RUNTIME_DIR/Xauthority"; + + # enable scrolling in git diff + DELTA_PAGER = "less -R"; + + EDITOR = "hx"; + MANPAGER = "sh -c 'col -bx | bat -l man -p'"; + }; +} diff --git a/foreign/dotfiles/home/shell/nix.nix b/foreign/dotfiles/home/shell/nix.nix new file mode 100644 index 0000000..b1ec809 --- /dev/null +++ b/foreign/dotfiles/home/shell/nix.nix @@ -0,0 +1,21 @@ +{ + pkgs, + inputs, + ... +}: +# nix tooling +{ + home.packages = with pkgs; [ + alejandra + deadnix + nix-index + statix + inputs.self.packages.${pkgs.hostPlatform.system}.repl + ]; + + programs.direnv = { + enable = true; + nix-direnv.enable = true; + enableZshIntegration = true; + }; +} diff --git a/foreign/dotfiles/home/shell/nushell/config.nu b/foreign/dotfiles/home/shell/nushell/config.nu new file mode 100644 index 0000000..cfd0195 --- /dev/null +++ b/foreign/dotfiles/home/shell/nushell/config.nu @@ -0,0 +1,514 @@ +# Nushell Config File + +module completions { + # Custom completions for external commands (those outside of Nushell) + # Each completions has two parts: the form of the external command, including its flags and parameters + # and a helper command that knows how to complete values for those flags and parameters + # + # This is a simplified version of completions for git branches and git remotes + def "nu-complete git branches" [] { + ^git branch | lines | each { |line| $line | str replace '[\*\+] ' '' | str trim } + } + + def "nu-complete git remotes" [] { + ^git remote | lines | each { |line| $line | str trim } + } + + # Download objects and refs from another repository + export extern "git fetch" [ + repository?: string@"nu-complete git remotes" # name of the repository to fetch + branch?: string@"nu-complete git branches" # name of the branch to fetch + --all # Fetch all remotes + --append(-a) # Append ref names and object names to .git/FETCH_HEAD + --atomic # Use an atomic transaction to update local refs. + --depth: int # Limit fetching to n commits from the tip + --deepen: int # Limit fetching to n commits from the current shallow boundary + --shallow-since: string # Deepen or shorten the history by date + --shallow-exclude: string # Deepen or shorten the history by branch/tag + --unshallow # Fetch all available history + --update-shallow # Update .git/shallow to accept new refs + --negotiation-tip: string # Specify which commit/glob to report while fetching + --negotiate-only # Do not fetch, only print common ancestors + --dry-run # Show what would be done + --write-fetch-head # Write fetched refs in FETCH_HEAD (default) + --no-write-fetch-head # Do not write FETCH_HEAD + --force(-f) # Always update the local branch + --keep(-k) # Keep dowloaded pack + --multiple # Allow several arguments to be specified + --auto-maintenance # Run 'git maintenance run --auto' at the end (default) + --no-auto-maintenance # Don't run 'git maintenance' at the end + --auto-gc # Run 'git maintenance run --auto' at the end (default) + --no-auto-gc # Don't run 'git maintenance' at the end + --write-commit-graph # Write a commit-graph after fetching + --no-write-commit-graph # Don't write a commit-graph after fetching + --prefetch # Place all refs into the refs/prefetch/ namespace + --prune(-p) # Remove obsolete remote-tracking references + --prune-tags(-P) # Remove any local tags that do not exist on the remote + --no-tags(-n) # Disable automatic tag following + --refmap: string # Use this refspec to map the refs to remote-tracking branches + --tags(-t) # Fetch all tags + --recurse-submodules: string # Fetch new commits of populated submodules (yes/on-demand/no) + --jobs(-j): int # Number of parallel children + --no-recurse-submodules # Disable recursive fetching of submodules + --set-upstream # Add upstream (tracking) reference + --submodule-prefix: string # Prepend to paths printed in informative messages + --upload-pack: string # Non-default path for remote command + --quiet(-q) # Silence internally used git commands + --verbose(-v) # Be verbose + --progress # Report progress on stderr + --server-option(-o): string # Pass options for the server to handle + --show-forced-updates # Check if a branch is force-updated + --no-show-forced-updates # Don't check if a branch is force-updated + -4 # Use IPv4 addresses, ignore IPv6 addresses + -6 # Use IPv6 addresses, ignore IPv4 addresses + --help # Display this help message + ] + + # Check out git branches and files + export extern "git checkout" [ + ...targets: string@"nu-complete git branches" # name of the branch or files to checkout + --conflict: string # conflict style (merge or diff3) + --detach(-d) # detach HEAD at named commit + --force(-f) # force checkout (throw away local modifications) + --guess # second guess 'git checkout ' (default) + --ignore-other-worktrees # do not check if another worktree is holding the given ref + --ignore-skip-worktree-bits # do not limit pathspecs to sparse entries only + --merge(-m) # perform a 3-way merge with the new branch + --orphan: string # new unparented branch + --ours(-2) # checkout our version for unmerged files + --overlay # use overlay mode (default) + --overwrite-ignore # update ignored files (default) + --patch(-p) # select hunks interactively + --pathspec-from-file: string # read pathspec from file + --progress # force progress reporting + --quiet(-q) # suppress progress reporting + --recurse-submodules: string # control recursive updating of submodules + --theirs(-3) # checkout their version for unmerged files + --track(-t) # set upstream info for new branch + -b: string # create and checkout a new branch + -B: string # create/reset and checkout a branch + -l # create reflog for new branch + --help # Display this help message + ] + + # Push changes + export extern "git push" [ + remote?: string@"nu-complete git remotes", # the name of the remote + ...refs: string@"nu-complete git branches" # the branch / refspec + --all # push all refs + --atomic # request atomic transaction on remote side + --delete(-d) # delete refs + --dry-run(-n) # dry run + --exec: string # receive pack program + --follow-tags # push missing but relevant tags + --force-with-lease # require old value of ref to be at this value + --force(-f) # force updates + --ipv4(-4) # use IPv4 addresses only + --ipv6(-6) # use IPv6 addresses only + --mirror # mirror all refs + --no-verify # bypass pre-push hook + --porcelain # machine-readable output + --progress # force progress reporting + --prune # prune locally removed refs + --push-option(-o): string # option to transmit + --quiet(-q) # be more quiet + --receive-pack: string # receive pack program + --recurse-submodules: string # control recursive pushing of submodules + --repo: string # repository + --set-upstream(-u) # set upstream for git pull/status + --signed: string # GPG sign the push + --tags # push tags (can't be used with --all or --mirror) + --thin # use thin pack + --verbose(-v) # be more verbose + --help # Display this help message + ] +} + +# Get just the extern definitions without the custom completion commands +use completions * + +# for more information on themes see +# https://www.nushell.sh/book/coloring_and_theming.html +let dark_theme = { + # color for nushell primitives + separator: white + leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off + header: green_bold + empty: blue + bool: white + int: white + filesize: white + duration: white + date: white + range: white + float: white + string: white + nothing: white + binary: white + cellpath: white + row_index: green_bold + record: white + list: white + block: white + hints: dark_gray + + # shapes are used to change the cli syntax highlighting + shape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b} + shape_binary: purple_bold + shape_bool: light_cyan + shape_int: purple_bold + shape_float: purple_bold + shape_range: yellow_bold + shape_internalcall: cyan_bold + shape_external: cyan + shape_externalarg: green_bold + shape_literal: blue + shape_operator: yellow + shape_signature: green_bold + shape_string: green + shape_string_interpolation: cyan_bold + shape_datetime: cyan_bold + shape_list: cyan_bold + shape_table: blue_bold + shape_record: cyan_bold + shape_block: blue_bold + shape_filepath: cyan + shape_globpattern: cyan_bold + shape_variable: purple + shape_flag: blue_bold + shape_custom: green + shape_nothing: light_cyan +} + +let light_theme = { + # color for nushell primitives + separator: dark_gray + leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off + header: green_bold + empty: blue + bool: dark_gray + int: dark_gray + filesize: dark_gray + duration: dark_gray + date: dark_gray + range: dark_gray + float: dark_gray + string: dark_gray + nothing: dark_gray + binary: dark_gray + cellpath: dark_gray + row_index: green_bold + record: white + list: white + block: white + hints: dark_gray + + # shapes are used to change the cli syntax highlighting + shape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b} + shape_binary: purple_bold + shape_bool: light_cyan + shape_int: purple_bold + shape_float: purple_bold + shape_range: yellow_bold + shape_internalcall: cyan_bold + shape_external: cyan + shape_externalarg: green_bold + shape_literal: blue + shape_operator: yellow + shape_signature: green_bold + shape_string: green + shape_string_interpolation: cyan_bold + shape_datetime: cyan_bold + shape_list: cyan_bold + shape_table: blue_bold + shape_record: cyan_bold + shape_block: blue_bold + shape_filepath: cyan + shape_globpattern: cyan_bold + shape_variable: purple + shape_flag: blue_bold + shape_custom: green + shape_nothing: light_cyan +} + +# External completer example +# let carapace_completer = {|spans| +# carapace $spans.0 nushell $spans | from json +# } + + +# The default config record. This is where much of your global configuration is setup. +let-env config = { + external_completer: $nothing # check 'carapace_completer' above to as example + filesize_metric: false # true => (KB, MB, GB), false => (KiB, MiB, GiB) + table_mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other + use_ls_colors: true + rm_always_trash: false + color_config: $dark_theme # if you want a light theme, replace `$dark_theme` to `$light_theme` + use_grid_icons: true + footer_mode: "25" # always, never, number_of_rows, auto + quick_completions: true # set this to false to prevent auto-selecting completions when only one remains + partial_completions: true # set this to false to prevent partial filling of the prompt + completion_algorithm: "prefix" # prefix, fuzzy + float_precision: 2 + # buffer_editor: "emacs" # command that will be used to edit the current line buffer with ctrl+o, if unset fallback to $env.EDITOR and $env.VISUAL + use_ansi_coloring: true + filesize_format: "auto" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, zb, zib, auto + edit_mode: emacs # emacs, vi + max_history_size: 10000 # Session has to be reloaded for this to take effect + sync_history_on_enter: true # Enable to share the history between multiple sessions, else you have to close the session to persist history to file + history_file_format: "plaintext" # "sqlite" or "plaintext" + shell_integration: true # enables terminal markers and a workaround to arrow keys stop working issue + table_index_mode: always # "always" show indexes, "never" show indexes, "auto" = show indexes when a table has "index" column + cd_with_abbreviations: false # set to true to allow you to do things like cd s/o/f and nushell expand it to cd some/other/folder + case_sensitive_completions: false # set to true to enable case-sensitive completions + enable_external_completion: true # set to false to prevent nushell looking into $env.PATH to find more suggestions, `false` recommended for WSL users as this look up my be very slow + max_external_completion_results: 100 # setting it lower can improve completion performance at the cost of omitting some options + # A strategy of managing table view in case of limited space. + table_trim: { + methodology: wrapping, # truncating + # A strategy which will be used by 'wrapping' methodology + wrapping_try_keep_words: true, + # A suffix which will be used with 'truncating' methodology + # truncating_suffix: "..." + } + show_banner: false # true or false to enable or disable the banner + show_clickable_links_in_ls: true # true or false to enable or disable clickable links in the ls listing. your terminal has to support links. + + hooks: { + pre_prompt: [{ + $nothing # replace with source code to run before the prompt is shown + }] + pre_execution: [{ + $nothing # replace with source code to run before the repl input is run + }] + env_change: { + PWD: [{|before, after| + $nothing # replace with source code to run if the PWD environment is different since the last repl input + }] + } + } + menus: [ + # Configuration for default nushell menus + # Note the lack of souce parameter + { + name: completion_menu + only_buffer_difference: false + marker: "| " + type: { + layout: columnar + columns: 4 + col_width: 20 # Optional value. If missing all the screen width is used to calculate column width + col_padding: 2 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + } + { + name: history_menu + only_buffer_difference: true + marker: "? " + type: { + layout: list + page_size: 10 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + } + { + name: help_menu + only_buffer_difference: true + marker: "? " + type: { + layout: description + columns: 4 + col_width: 20 # Optional value. If missing all the screen width is used to calculate column width + col_padding: 2 + selection_rows: 4 + description_rows: 10 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + } + # Example of extra menus created using a nushell source + # Use the source field to create a list of records that populates + # the menu + { + name: commands_menu + only_buffer_difference: false + marker: "# " + type: { + layout: columnar + columns: 4 + col_width: 20 + col_padding: 2 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + source: { |buffer, position| + $nu.scope.commands + | where command =~ $buffer + | each { |it| {value: $it.command description: $it.usage} } + } + } + { + name: vars_menu + only_buffer_difference: true + marker: "# " + type: { + layout: list + page_size: 10 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + source: { |buffer, position| + $nu.scope.vars + | where name =~ $buffer + | sort-by name + | each { |it| {value: $it.name description: $it.type} } + } + } + { + name: commands_with_description + only_buffer_difference: true + marker: "# " + type: { + layout: description + columns: 4 + col_width: 20 + col_padding: 2 + selection_rows: 4 + description_rows: 10 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + source: { |buffer, position| + $nu.scope.commands + | where command =~ $buffer + | each { |it| {value: $it.command description: $it.usage} } + } + } + ] + keybindings: [ + { + name: completion_menu + modifier: none + keycode: tab + mode: emacs # Options: emacs vi_normal vi_insert + event: { + until: [ + { send: menu name: completion_menu } + { send: menunext } + ] + } + } + { + name: completion_previous + modifier: shift + keycode: backtab + mode: [emacs, vi_normal, vi_insert] # Note: You can add the same keybinding to all modes by using a list + event: { send: menuprevious } + } + { + name: history_menu + modifier: control + keycode: char_r + mode: emacs + event: { send: menu name: history_menu } + } + { + name: next_page + modifier: control + keycode: char_x + mode: emacs + event: { send: menupagenext } + } + { + name: undo_or_previous_page + modifier: control + keycode: char_z + mode: emacs + event: { + until: [ + { send: menupageprevious } + { edit: undo } + ] + } + } + { + name: yank + modifier: control + keycode: char_y + mode: emacs + event: { + until: [ + {edit: pastecutbufferafter} + ] + } + } + { + name: unix-line-discard + modifier: control + keycode: char_u + mode: [emacs, vi_normal, vi_insert] + event: { + until: [ + {edit: cutfromlinestart} + ] + } + } + { + name: kill-line + modifier: control + keycode: char_k + mode: [emacs, vi_normal, vi_insert] + event: { + until: [ + {edit: cuttolineend} + ] + } + } + # Keybindings used to trigger the user defined menus + { + name: commands_menu + modifier: control + keycode: char_t + mode: [emacs, vi_normal, vi_insert] + event: { send: menu name: commands_menu } + } + { + name: vars_menu + modifier: alt + keycode: char_o + mode: [emacs, vi_normal, vi_insert] + event: { send: menu name: vars_menu } + } + { + name: commands_with_description + modifier: control + keycode: char_s + mode: [emacs, vi_normal, vi_insert] + event: { send: menu name: commands_with_description } + } + ] +} + +source ~/.cache/starship/init.nu diff --git a/foreign/dotfiles/home/shell/nushell/default.nix b/foreign/dotfiles/home/shell/nushell/default.nix new file mode 100644 index 0000000..9165426 --- /dev/null +++ b/foreign/dotfiles/home/shell/nushell/default.nix @@ -0,0 +1,7 @@ +{ + programs.nushell = { + enable = true; + configFile.source = ./config.nu; + envFile.source = ./env.nu; + }; +} diff --git a/foreign/dotfiles/home/shell/nushell/env.nu b/foreign/dotfiles/home/shell/nushell/env.nu new file mode 100644 index 0000000..f2e0c7f --- /dev/null +++ b/foreign/dotfiles/home/shell/nushell/env.nu @@ -0,0 +1,36 @@ +# Nushell Environment Config File + +# Specifies how environment variables are: +# - converted from a string to a value on Nushell startup (from_string) +# - converted from a value back to a string when running external commands (to_string) +# Note: The conversions happen *after* config.nu is loaded +let-env ENV_CONVERSIONS = { + "PATH": { + from_string: { |s| $s | split row (char esep) | path expand -n } + to_string: { |v| $v | path expand -n | str join (char esep) } + } + "Path": { + from_string: { |s| $s | split row (char esep) | path expand -n } + to_string: { |v| $v | path expand -n | str join (char esep) } + } +} + +# Directories to search for scripts when calling source or use +# +# By default, /scripts is added +let-env NU_LIB_DIRS = [ + ($nu.config-path | path dirname | path join 'scripts') +] + +# Directories to search for plugin binaries when calling register +# +# By default, /plugins is added +let-env NU_PLUGIN_DIRS = [ + ($nu.config-path | path dirname | path join 'plugins') +] + +# To add entries to PATH (on Windows you might use Path), you can use the following pattern: +# let-env PATH = ($env.PATH | split row (char esep) | prepend '/some/path') + +mkdir ~/.cache/starship +starship init nu | sed "s/size -c/size/" | save ~/.cache/starship/init.nu diff --git a/foreign/dotfiles/home/shell/starship.nix b/foreign/dotfiles/home/shell/starship.nix new file mode 100644 index 0000000..df99bfc --- /dev/null +++ b/foreign/dotfiles/home/shell/starship.nix @@ -0,0 +1,13 @@ +{config, ...}: { + home.sessionVariables.STARSHIP_CACHE = "${config.xdg.cacheHome}/starship"; + + programs.starship = { + enable = true; + settings = { + character = { + success_symbol = "[›](bold green)"; + error_symbol = "[›](bold red)"; + }; + }; + }; +} diff --git a/foreign/dotfiles/home/shell/transient-services.nix b/foreign/dotfiles/home/shell/transient-services.nix new file mode 100644 index 0000000..da5c1ec --- /dev/null +++ b/foreign/dotfiles/home/shell/transient-services.nix @@ -0,0 +1,30 @@ +{ + pkgs, + config, + lib, + ... +}: let + apply-hm-env = pkgs.writeShellScript "apply-hm-env" '' + ${lib.optionalString (config.home.sessionPath != []) '' + export PATH=${builtins.concatStringsSep ":" config.home.sessionPath}:$PATH + ''} + ${builtins.concatStringsSep "\n" (lib.mapAttrsToList (k: v: '' + export ${k}=${toString v} + '') + config.home.sessionVariables)} + ${config.home.sessionVariablesExtra} + exec "$@" + ''; + + # runs processes as systemd transient services + run-as-service = pkgs.writeShellScriptBin "run-as-service" '' + exec ${pkgs.systemd}/bin/systemd-run \ + --slice=app-manual.slice \ + --property=ExitType=cgroup \ + --user \ + --wait \ + bash -lc "exec ${apply-hm-env} $@" + ''; +in { + home.packages = [run-as-service]; +} diff --git a/foreign/dotfiles/home/shell/zsh.nix b/foreign/dotfiles/home/shell/zsh.nix new file mode 100644 index 0000000..55ff509 --- /dev/null +++ b/foreign/dotfiles/home/shell/zsh.nix @@ -0,0 +1,76 @@ +{ + config, + pkgs, + lib, + ... +}: { + programs.zsh = { + enable = true; + enableAutosuggestions = true; + autocd = true; + dirHashes = { + dl = "$HOME/Downloads"; + docs = "$HOME/Documents"; + code = "$HOME/Documents/code"; + dots = "$HOME/Documents/code/dotfiles"; + pics = "$HOME/Pictures"; + vids = "$HOME/Videos"; + nixpkgs = "$HOME/Documents/code/git/nixpkgs"; + }; + dotDir = ".config/zsh"; + history = { + expireDuplicatesFirst = true; + path = "${config.xdg.dataHome}/zsh_history"; + }; + + initExtra = '' + # search history based on what's typed in the prompt + autoload -U history-search-end + zle -N history-beginning-search-backward-end history-search-end + zle -N history-beginning-search-forward-end history-search-end + bindkey "^[OA" history-beginning-search-backward-end + bindkey "^[OB" history-beginning-search-forward-end + + # case insensitive tab completion + zstyle ':completion:*' completer _complete _ignored _approximate + zstyle ':completion:*' list-colors '\' + zstyle ':completion:*' list-prompt %SAt %p: Hit TAB for more, or the character to insert%s + zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' + zstyle ':completion:*' menu select + zstyle ':completion:*' select-prompt %SScrolling active: current selection at %p%s + zstyle ':completion:*' verbose true + _comp_options+=(globdots) + + ${lib.optionalString config.services.gpg-agent.enable '' + gnupg_path=$(ls $XDG_RUNTIME_DIR/gnupg) + export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/gnupg/$gnupg_path/S.gpg-agent.ssh" + ''} + + ${lib.optionalString config.programs.kitty.enable '' + if test -n "$KITTY_INSTALLATION_DIR"; then + export KITTY_SHELL_INTEGRATION="enabled" + autoload -Uz -- "$KITTY_INSTALLATION_DIR"/shell-integration/zsh/kitty-integration + kitty-integration + unfunction kitty-integration + fi + ''} + + # run programs that are not in PATH with comma + command_not_found_handler() { + ${pkgs.comma}/bin/comma "$@" + } + ''; + + shellAliases = { + grep = "grep --color"; + ip = "ip --color"; + l = "exa -l"; + la = "exa -la"; + md = "mkdir -p"; + + us = "systemctl --user"; + rs = "sudo systemctl"; + }; + shellGlobalAliases = {exa = "exa --icons --git";}; + }; +} diff --git a/foreign/dotfiles/home/terminals/alacritty.nix b/foreign/dotfiles/home/terminals/alacritty.nix new file mode 100644 index 0000000..3fbb717 --- /dev/null +++ b/foreign/dotfiles/home/terminals/alacritty.nix @@ -0,0 +1,52 @@ +{default, ...}: +# terminals +let + inherit (default.terminal) font size opacity; + inherit (default) xcolors; +in { + programs.alacritty = { + enable = true; + settings = { + window = { + decorations = "none"; + dynamic_padding = true; + padding = { + x = 5; + y = 5; + }; + startup_mode = "Maximized"; + }; + + scrolling.history = 10000; + + font = { + normal.family = font; + bold.family = font; + italic.family = font; + inherit size; + }; + + draw_bold_text_with_bright_colors = true; + colors = rec { + primary = { + background = xcolors.crust; + foreground = xcolors.fg; + }; + normal = { + inherit (xcolors) red green yellow blue; + black = xcolors.mantle; + magenta = xcolors.mauve; + cyan = xcolors.sky; + white = xcolors.text; + }; + bright = + normal + // { + black = xcolors.base; + white = xcolors.rosewater; + }; + }; + window.opacity = opacity; + }; + }; +} diff --git a/foreign/dotfiles/home/terminals/kitty.nix b/foreign/dotfiles/home/terminals/kitty.nix new file mode 100644 index 0000000..f72bf95 --- /dev/null +++ b/foreign/dotfiles/home/terminals/kitty.nix @@ -0,0 +1,54 @@ +{default, ...}: let + inherit (default) xcolors; +in { + programs.kitty = { + enable = true; + font = { + inherit (default.terminal) size; + name = default.terminal.font; + }; + settings = { + scrollback_lines = 10000; + placement_strategy = "center"; + + allow_remote_control = "yes"; + enable_audio_bell = "no"; + visual_bell_duration = "0.1"; + visual_bell_color = xcolors.rosewater; + + copy_on_select = "clipboard"; + + selection_foreground = "none"; + selection_background = "none"; + + # colors + background_opacity = toString default.terminal.opacity; + foreground = xcolors.fg; + background = xcolors.crust; + # black + color0 = xcolors.mantle; + color8 = xcolors.base; + # red + color1 = xcolors.red; + color9 = xcolors.red; + # green + color2 = xcolors.green; + color10 = xcolors.green; + # yellow + color3 = xcolors.yellow; + color11 = xcolors.yellow; + # blue + color4 = xcolors.blue; + color12 = xcolors.blue; + # magenta + color5 = xcolors.pink; + color13 = xcolors.pink; + # cyan + color6 = xcolors.sky; + color14 = xcolors.sky; + # white + color7 = xcolors.text; + color15 = xcolors.rosewater; + }; + }; +} diff --git a/foreign/dotfiles/home/terminals/wezterm.nix b/foreign/dotfiles/home/terminals/wezterm.nix new file mode 100644 index 0000000..d914f7a --- /dev/null +++ b/foreign/dotfiles/home/terminals/wezterm.nix @@ -0,0 +1,28 @@ +{default, ...}: { + programs.wezterm = { + enable = true; + extraConfig = '' + local wezterm = require "wezterm" + + return { + font = wezterm.font_with_fallback({ "${default.terminal.font}", }, { + weight = "Regular", + }), + font_size = ${toString default.terminal.size}, + color_scheme = "Catppuccin Mocha", + window_background_opacity = ${toString default.terminal.opacity}, + enable_scroll_bar = false, + enable_tab_bar = false, + scrollback_lines = 10000, + window_padding = { + left = 10, + right = 10, + top = 10, + bottom = 10, + }, + check_for_updates = false, + default_cursor_style = "SteadyBar", + } + ''; + }; +} diff --git a/foreign/dotfiles/home/wayland/default.nix b/foreign/dotfiles/home/wayland/default.nix new file mode 100644 index 0000000..5578f8c --- /dev/null +++ b/foreign/dotfiles/home/wayland/default.nix @@ -0,0 +1,68 @@ +{ + pkgs, + lib, + inputs, + ... +}: +# Wayland config +let + # use OCR and copy to clipboard + ocrScript = let + inherit (pkgs) grim libnotify slurp tesseract5 wl-clipboard; + _ = lib.getExe; + in + pkgs.writeShellScriptBin "wl-ocr" '' + ${_ grim} -g "$(${_ slurp})" -t ppm - | ${_ tesseract5} - - | ${wl-clipboard}/bin/wl-copy + ${_ libnotify} "$(${wl-clipboard}/bin/wl-paste)" + ''; +in { + imports = [ + ../programs/eww + ./hyprland + ./sway.nix + ./swaybg.nix + ./swayidle.nix + ./swaylock.nix + ]; + + programs.eww-hyprland = { + enable = true; + package = inputs.eww.packages.${pkgs.hostPlatform.system}.eww-wayland; + }; + + home.packages = with pkgs; [ + # screenshot + grim + slurp + + # idle/lock + swaybg + swaylock-effects + + # utils + ocrScript + wf-recorder + wl-clipboard + wlogout + wlr-randr + wofi + ]; + + # make stuff work on wayland + home.sessionVariables = { + QT_QPA_PLATFORM = "wayland"; + SDL_VIDEODRIVER = "wayland"; + XDG_SESSION_TYPE = "wayland"; + }; + + programs.obs-studio.plugins = with pkgs.obs-studio-plugins; [wlrobs]; + + # fake a tray to let apps start + # https://github.com/nix-community/home-manager/issues/2064 + systemd.user.targets.tray = { + Unit = { + Description = "Home Manager System Tray"; + Requires = ["graphical-session-pre.target"]; + }; + }; +} diff --git a/foreign/dotfiles/home/wayland/hyprland/config.nix b/foreign/dotfiles/home/wayland/hyprland/config.nix new file mode 100644 index 0000000..f743460 --- /dev/null +++ b/foreign/dotfiles/home/wayland/hyprland/config.nix @@ -0,0 +1,248 @@ +{ + config, + pkgs, + default, + ... +}: let + inherit (default) colors; + + pointer = config.home.pointerCursor; + homeDir = config.home.homeDirectory; + + emoji = "${pkgs.wofi-emoji}/bin/wofi-emoji"; + launcher = "wofi"; +in { + wayland.windowManager.hyprland.extraConfig = '' + $mod = SUPER + + env = _JAVA_AWT_WM_NONREPARENTING,1 + env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1 + + # scale apps + env = GDK_SCALE,2 + exec-once = xprop -root -f _XWAYLAND_GLOBAL_OUTPUT_SCALE 32c -set _XWAYLAND_GLOBAL_OUTPUT_SCALE 2 + + # set cursor for HL itself + exec-once = hyprctl setcursor ${pointer.name} ${toString pointer.size} + + exec-once = systemctl --user start clight + exec-once = eww open bar + + misc { + # disable auto polling for config file changes + disable_autoreload = true + focus_on_activate = true + # disable dragging animation + animate_mouse_windowdragging = false + } + + # touchpad gestures + gestures { + workspace_swipe = true + workspace_swipe_forever = true + } + + input { + kb_layout = ro + + # focus change on cursor move + follow_mouse = 1 + accel_profile = flat + touchpad { + scroll_factor = 0.3 + } + } + + device:MSFT0001:00 04F3:31EB Touchpad { + accel_profile = adaptive + natural_scroll = true + sensitivity = 0.1 + } + + general { + gaps_in = 5 + gaps_out = 5 + border_size = 2 + col.active_border = rgb(${colors.blue}) rgb(${colors.mauve}) 270deg + col.inactive_border = rgb(${colors.crust}) rgb(${colors.lavender}) 270deg + + # group borders + col.group_border_active = rgb(${colors.pink}) + col.group_border = rgb(${colors.surface0}) + } + + decoration { + rounding = 16 + blur = true + blur_size = 3 + blur_passes = 3 + blur_new_optimizations = true + + drop_shadow = true + shadow_ignore_window = true + shadow_offset = 0 5 + shadow_range = 50 + shadow_render_power = 3 + col.shadow = rgba(00000099) + } + + animations { + enabled = true + animation = border, 1, 2, default + animation = fade, 1, 4, default + animation = windows, 1, 3, default, popin 80% + animation = workspaces, 1, 2, default, slide + } + + dwindle { + # keep floating dimentions while tiling + pseudotile = true + preserve_split = true + } + + # only allow shadows for floating windows + windowrulev2 = noshadow, floating:0 + + # telegram media viewer + windowrulev2 = float, title:^(Media viewer)$ + + # make Firefox PiP window floating and sticky + windowrulev2 = float, title:^(Picture-in-Picture)$ + windowrulev2 = pin, title:^(Picture-in-Picture)$ + + # throw sharing indicators away + windowrulev2 = workspace special silent, title:^(Firefox — Sharing Indicator)$ + windowrulev2 = workspace special silent, title:^(.*is sharing (your screen|a window)\.)$ + + # start spotify tiled in ws9 + windowrulev2 = tile, class:^(Spotify)$ + windowrulev2 = workspace 9 silent, class:^(Spotify)$ + + # start Discord/WebCord in ws2 + windowrulev2 = workspace 2, title:^(.*(Disc|WebC)ord.*)$ + + # idle inhibit while watching videos + windowrulev2 = idleinhibit focus, class:^(mpv|.+exe)$ + windowrulev2 = idleinhibit focus, class:^(firefox)$, title:^(.*YouTube.*)$ + windowrulev2 = idleinhibit fullscreen, class:^(firefox)$ + + windowrulev2 = dimaround, class:^(gcr-prompter)$ + + # fix xwayland apps + windowrulev2 = rounding 0, xwayland:1, floating:1 + windowrulev2 = center, class:^(.*jetbrains.*)$, title:^(Confirm Exit|Open Project|win424|win201|splash)$ + windowrulev2 = size 640 400, class:^(.*jetbrains.*)$, title:^(splash)$ + + layerrule = blur, ^(gtk-layer-shell)$ + layerrule = ignorezero, ^(gtk-layer-shell)$ + + # mouse movements + bindm = $mod, mouse:272, movewindow + bindm = $mod, mouse:273, resizewindow + bindm = $mod ALT, mouse:272, resizewindow + + # compositor commands + bind = $mod SHIFT, E, exec, pkill Hyprland + bind = $mod, Q, killactive, + bind = $mod, F, fullscreen, + bind = $mod, G, togglegroup, + bind = $mod SHIFT, N, changegroupactive, f + bind = $mod SHIFT, P, changegroupactive, b + bind = $mod, R, togglesplit, + bind = $mod, T, togglefloating, + bind = $mod, P, pseudo, + bind = $mod ALT, ,resizeactive, + # toggle "monocle" (no_gaps_when_only) + $kw = dwindle:no_gaps_when_only + bind = $mod, M, exec, hyprctl keyword $kw $(($(hyprctl getoption $kw -j | jaq -r '.int') ^ 1)) + + # utility + # launcher + bindr = $mod, SUPER_L, exec, pkill .${launcher}-wrapped || run-as-service ${launcher} + # terminal + bind = $mod, Return, exec, run-as-service ${default.terminal.name} + # logout menu + bind = $mod, Escape, exec, wlogout -p layer-shell + # lock screen + bind = $mod, L, exec, loginctl lock-session + # emoji picker + bind = $mod, E, exec, ${emoji} + # select area to perform OCR on + bind = $mod, O, exec, run-as-service wl-ocr + + # move focus + bind = $mod, left, movefocus, l + bind = $mod, right, movefocus, r + bind = $mod, up, movefocus, u + bind = $mod, down, movefocus, d + + # window resize + bind = $mod, S, submap, resize + + submap = resize + binde = , right, resizeactive, 10 0 + binde = , left, resizeactive, -10 0 + binde = , up, resizeactive, 0 -10 + binde = , down, resizeactive, 0 10 + bind = , escape, submap, reset + submap = reset + + # media controls + bindl = , XF86AudioPlay, exec, playerctl play-pause + bindl = , XF86AudioPrev, exec, playerctl previous + bindl = , XF86AudioNext, exec, playerctl next + + # volume + bindle = , XF86AudioRaiseVolume, exec, wpctl set-volume -l "1.0" @DEFAULT_AUDIO_SINK@ 6%+ + binde = , XF86AudioRaiseVolume, exec, ${homeDir}/.config/eww/scripts/volume osd + bindle = , XF86AudioLowerVolume, exec, wpctl set-volume -l "1.0" @DEFAULT_AUDIO_SINK@ 6%- + binde = , XF86AudioLowerVolume, exec, ${homeDir}/.config/eww/scripts/volume osd + bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle + bind = , XF86AudioMute, exec, ${homeDir}/.config/eww/scripts/volume osd + bindl = , XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle + + # backlight + bindle = , XF86MonBrightnessUp, exec, light -A 5 + binde = , XF86MonBrightnessUp, exec, ${homeDir}/.config/eww/scripts/brightness osd + bindle = , XF86MonBrightnessDown, exec, light -U 5 + binde = , XF86MonBrightnessDown, exec, ${homeDir}/.config/eww/scripts/brightness osd + + # screenshot + # stop animations while screenshotting; makes black border go away + $screenshotarea = hyprctl keyword animation "fadeOut,0,0,default"; grimblast --notify copysave area; hyprctl keyword animation "fadeOut,1,4,default" + bind = , Print, exec, $screenshotarea + bind = $mod SHIFT, R, exec, $screenshotarea + + bind = CTRL, Print, exec, grimblast --notify --cursor copysave output + bind = $mod SHIFT CTRL, R, exec, grimblast --notify --cursor copysave output + + bind = ALT, Print, exec, grimblast --notify --cursor copysave screen + bind = $mod SHIFT ALT, R, exec, grimblast --notify --cursor copysave screen + + # workspaces + # binds mod + [shift +] {1..10} to [move to] ws {1..10} + ${builtins.concatStringsSep "\n" (builtins.genList ( + x: let + ws = let + c = (x + 1) / 10; + in + builtins.toString (x + 1 - (c * 10)); + in '' + bind = $mod, ${ws}, workspace, ${toString (x + 1)} + bind = $mod SHIFT, ${ws}, movetoworkspace, ${toString (x + 1)} + '' + ) + 10)} + + # special workspace + bind = $mod SHIFT, grave, movetoworkspace, special + bind = $mod, grave, togglespecialworkspace, eDP-1 + + # cycle workspaces + bind = $mod, bracketleft, workspace, m-1 + bind = $mod, bracketright, workspace, m+1 + # cycle monitors + bind = $mod SHIFT, braceleft, focusmonitor, l + bind = $mod SHIFT, braceright, focusmonitor, r + ''; +} diff --git a/foreign/dotfiles/home/wayland/hyprland/default.nix b/foreign/dotfiles/home/wayland/hyprland/default.nix new file mode 100644 index 0000000..72745de --- /dev/null +++ b/foreign/dotfiles/home/wayland/hyprland/default.nix @@ -0,0 +1,20 @@ +{ + inputs, + lib, + pkgs, + ... +}: { + imports = [./config.nix]; + + home.packages = with pkgs; [ + jaq + xorg.xprop + inputs.hyprland-contrib.packages.${pkgs.hostPlatform.system}.grimblast + ]; + + # start swayidle as part of hyprland, not sway + systemd.user.services.swayidle.Install.WantedBy = lib.mkForce ["hyprland-session.target"]; + + # enable hyprland + wayland.windowManager.hyprland.enable = true; +} diff --git a/foreign/dotfiles/home/wayland/sway.nix b/foreign/dotfiles/home/wayland/sway.nix new file mode 100644 index 0000000..3a51634 --- /dev/null +++ b/foreign/dotfiles/home/wayland/sway.nix @@ -0,0 +1,74 @@ +{ + config, + pkgs, + lib, + inputs, + default, + ... +}: { + wayland.windowManager.sway = { + enable = true; + package = inputs.self.packages.${pkgs.hostPlatform.system}.sway-hidpi; + config = { + keybindings = let + m = config.wayland.windowManager.sway.config.modifier; + in + lib.mkOptionDefault { + "${m}+Return" = "exec ${default.terminal.name}"; + "${m}+q" = "kill"; + "${m}+space" = "exec wofi"; + "${m}+t" = "floating toggle"; + + # screenshots + "Print" = "grim -g \"$(slurp)\" - | wl-copy -t image/png"; + "${m}+Shift+r" = "grim -g \"$(slurp)\" - | wl-copy -t image/png"; + "Alt+Print" = "grim - | wl-copy -t image/png"; + "${m}+Alt+Shift+r" = "grim - | wl-copy -t image/png"; + }; + + keycodebindings = { + "--locked --no-repeat 121" = "exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; # mute + "--locked 122" = "exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 6%-"; # vol- + "--locked 123" = "exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 6%+"; # vol+ + "--locked 171" = "exec playerctl next"; # next song + "--locked --no-repeat 172" = "exec playerctl play-pause"; # play/pause + "--locked 173" = "exec playerctl previous"; # prev song + "--locked --no-repeat 198" = "exec wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; # mic mute + "--locked 232" = "exec light -U 5"; # brightness- + "--locked 233" = "exec light -A 5"; # brightness+ + }; + + menu = "wofi"; + terminal = default.terminal.name; + modifier = "Mod4"; + bars = []; + + gaps = { + smartBorders = "on"; + outer = 5; + inner = 5; + }; + + startup = [{command = "dbus-update-activation-environment --systemd WAYLAND_DISPLAY DISPLAY";}]; + + input = { + "type:pointer" = { + accel_profile = "flat"; + pointer_accel = "0"; + }; + "type:touchpad" = { + middle_emulation = "enabled"; + natural_scroll = "enabled"; + tap = "enabled"; + }; + }; + output."*".bg = "~/.config/wallpaper.png fill"; + }; + + extraConfig = '' + exec ${pkgs.xorg.xprop}/bin/xprop -root -f _XWAYLAND_GLOBAL_OUTPUT_SCALE 32c -set _XWAYLAND_GLOBAL_OUTPUT_SCALE 2 + ''; + + wrapperFeatures.gtk = true; + }; +} diff --git a/foreign/dotfiles/home/wayland/swaybg.nix b/foreign/dotfiles/home/wayland/swaybg.nix new file mode 100644 index 0000000..d6e6b27 --- /dev/null +++ b/foreign/dotfiles/home/wayland/swaybg.nix @@ -0,0 +1,18 @@ +{ + pkgs, + lib, + default, + ... +}: { + systemd.user.services.swaybg = { + Unit = { + Description = "Wayland wallpaper daemon"; + PartOf = ["graphical-session.target"]; + }; + Service = { + ExecStart = "${lib.getExe pkgs.swaybg} -i ${default.wallpaper} -m fill"; + Restart = "on-failure"; + }; + Install.WantedBy = ["graphical-session.target"]; + }; +} diff --git a/foreign/dotfiles/home/wayland/swayidle.nix b/foreign/dotfiles/home/wayland/swayidle.nix new file mode 100644 index 0000000..bf98993 --- /dev/null +++ b/foreign/dotfiles/home/wayland/swayidle.nix @@ -0,0 +1,34 @@ +{pkgs, ...}: let + suspendScript = pkgs.writeShellScript "suspend-script" '' + ${pkgs.pipewire}/bin/pw-cli i all | ${pkgs.ripgrep}/bin/rg running + # only suspend if audio isn't running + if [ $? == 1 ]; then + ${pkgs.systemd}/bin/systemctl suspend + fi + ''; +in { + # screen idle + services.swayidle = { + enable = true; + events = [ + { + event = "before-sleep"; + command = "${pkgs.systemd}/bin/loginctl lock-session"; + } + { + event = "lock"; + command = "${pkgs.swaylock-effects}/bin/swaylock -fF"; + } + ]; + timeouts = [ + { + timeout = 310; + command = "${pkgs.systemd}/bin/loginctl lock-session"; + } + { + timeout = 310; + command = suspendScript.outPath; + } + ]; + }; +} diff --git a/foreign/dotfiles/home/wayland/swaylock.nix b/foreign/dotfiles/home/wayland/swaylock.nix new file mode 100644 index 0000000..f8e161f --- /dev/null +++ b/foreign/dotfiles/home/wayland/swaylock.nix @@ -0,0 +1,31 @@ +{default, ...}: { + programs.swaylock.settings = let + inherit (default) xcolors; + in { + clock = true; + font = "Jost *"; + image = default.wallpaper; + indicator = true; + + bs-hl-color = xcolors.red; + key-hl-color = xcolors.text; + separator-color = xcolors.base; + text-color = xcolors.base; + + inside-color = xcolors.mauve; + line-color = xcolors.mauve; + ring-color = xcolors.base; + + inside-clear-color = xcolors.yellow; + line-clear-color = xcolors.yellow; + ring-clear-color = xcolors.base; + + inside-ver-color = xcolors.lavender; + line-ver-color = xcolors.lavender; + ring-ver-color = xcolors.base; + + inside-wrong-color = xcolors.red; + line-wrong-color = xcolors.red; + ring-wrong-color = xcolors.base; + }; +} diff --git a/foreign/dotfiles/home/wayland/wayfire.nix b/foreign/dotfiles/home/wayland/wayfire.nix new file mode 100644 index 0000000..5652f69 --- /dev/null +++ b/foreign/dotfiles/home/wayland/wayfire.nix @@ -0,0 +1,340 @@ +{ + pkgs, + default, + ... +}: let + inherit (default) xrgbaColors; +in { + home.packages = [pkgs.wayfire]; + + xdg.configFile."wayfire.ini".text = '' + [alpha] + min_value = 0.100000 + modifier = + + [animate] + close_animation = fade + open_animation = zoom + startup_duration = 300 + duration = 300 + enabled_for = type equals "toplevel" | (type equals "x-or" & focusable equals true)) + + fade_duration = 400 + fade_enabled_for = type equals "overlay" + + [autostart] + 0_environment = dbus-update-activation-environment --systemd WAYLAND_DISPLAY DISPLAY XAUTHORITY + 1_hm = systemctl --user start graphical-session.target + 2_eww = eww daemon + + autostart_wf_shell = false + background = swaybg -i ~/.config/wallpaper.png + idle = swayidle -w \ + timeout 360 'swaylock' \ + before-sleep 'swaylock' + panel = eww open bar + + [blur] + blur_by_default = type is "toplevel" + + bokeh_degrade = 1 + bokeh_iterations = 5 + bokeh_offset = 5.000000 + + box_degrade = 1 + box_iterations = 2 + box_offset = 1.000000 + + gaussian_degrade = 1 + gaussian_iterations = 2 + gaussian_offset = 1.000000 + + kawase_degrade = 3 + kawase_iterations = 3 + kawase_offset = 1.000000 + + method = kawase + saturation = 1.000000 + #toggle = KEY_B + + [command] + binding_launcher = KEY_SPACE + binding_lock = KEY_L + binding_logout = KEY_ESC + binding_mute = KEY_MUTE + binding_mic_mute = KEY_F20 + binding_next = KEY_NEXTSONG + binding_pause = KEY_PLAYPAUSE + binding_prev = KEY_PREVIOUSSONG + binding_screenshot = KEY_PRINT | KEY_R + binding_screenshot_interactive = KEY_PRINT | KEY_R + binding_terminal = KEY_ENTER + + command_launcher = wofi --show=drun -I + command_light_down = light -U 5 + command_light_up = light -A 5 + command_lock = swaylock + command_logout = wlogout -p layer-shell + command_mute = wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle + command_mic_mute = wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle + command_next = playerctl next + command_pause = playerctl play-pause + command_prev = playerctl previous + command_screenshot = screenshot area + command_screenshot_interactive = screenshot monitor + command_terminal = ${default.terminal.name} + command_volume_down = pulsemixer --change-volume -6 + command_volume_up = pulsemixer --change-volume +6 + + repeatable_binding_light_down = KEY_BRIGHTNESSDOWN + repeatable_binding_light_up = KEY_BRIGHTNESSUP + repeatable_binding_volume_down = KEY_VOLUMEDOWN + repeatable_binding_volume_up = KEY_VOLUMEUP + + [core] + background_color = #${xrgbaColors.base00} + close_top_view = KEY_Q | KEY_F4 + focus_button_with_modifiers = false + focus_buttons = BTN_LEFT | BTN_MIDDLE | BTN_RIGHT + focus_buttons_passthrough = true + plugins = autostart \ + blur \ + command \ + decoration \ + expo \ + fast-switcher \ + idle \ + matcher \ + move \ + oswitch \ + place \ + resize \ + simple-tile \ + vswipe \ + window-rules \ + wrot \ + zoom + vheight = 3 + vwidth = 3 + xwayland = true + + [cube] + activate = BTN_LEFT + background = #${xrgbaColors.base00} + background_mode = simple + cubemap_image = + deform = 0 + initial_animation = 350 + light = true + rotate_left = KEY_LEFT + rotate_right = KEY_RIGHT + skydome_mirror = true + skydome_texture = + speed_spin_horiz = 0.020000 + speed_spin_vert = 0.020000 + speed_zoom = 0.070000 + zoom = 0.100000 + + [decoration] + active_color = #${xrgbaColors.base00} + border_size = 0 + button_order = minimize maximize close + font = Roboto + ignore_views = none + inactive_color = #${xrgbaColors.base04} + title_height = 20 + + [expo] + background = #${xrgbaColors.base00} + duration = 50 + offset = 10 + select_workspace_1 = KEY_1 + select_workspace_2 = KEY_2 + select_workspace_3 = KEY_3 + select_workspace_4 = KEY_4 + select_workspace_5 = KEY_5 + select_workspace_6 = KEY_6 + select_workspace_7 = KEY_7 + select_workspace_8 = KEY_8 + select_workspace_9 = KEY_9 + toggle = + + [extra-gestures] + close_fingers = 5 + move_delay = 500 + move_fingers = 3 + + [fast-switcher] + activate = KEY_ESC + activate_backward = KEY_ESC + + [fisheye] + radius = 450.000000 + toggle = KEY_F + zoom = 7.000000 + + [grid] + duration = 300 + restore = KEY_DOWN | KEY_KP0 + slot_b = KEY_KP2 + slot_bl = KEY_KP1 + slot_br = KEY_KP3 + slot_c = KEY_UP | KEY_KP5 + slot_l = KEY_LEFT | KEY_KP4 + slot_r = KEY_RIGHT | KEY_KP6 + slot_t = KEY_KP8 + slot_tl = KEY_KP7 + slot_tr = KEY_KP9 + type = crossfade + + [idle] + cube_max_zoom = 1.500000 + cube_rotate_speed = 1.000000 + cube_zoom_speed = 1000 + disable_on_fullscreen = true + dpms_timeout = 300 + screensaver_timeout = -1 + toggle = none + + [input] + click_method = default + cursor_size = 24 + cursor_theme = "Bibata-Modern-Classic" + disable_touchpad_while_mouse = false + disable_touchpad_while_typing = false + gesture_sensitivity = 1.000000 + middle_emulation = false + modifier_binding_timeout = 400 + mouse_accel_profile = flat + natural_scroll = true + touchpad_scroll_speed = 0.3 + xkb_layout = ro + xkb_rules = evdev + + [invert] + preserve_hue = false + toggle = KEY_I + + [move] + activate = BTN_LEFT + enable_snap = true + enable_snap_off = true + join_views = false + quarter_snap_threshold = 50 + snap_off_threshold = 10 + snap_threshold = 10 + workspace_switch_after = -1 + + [oswitch] + next_output = KEY_O + next_output_with_win = KEY_O + + [output] + mode = auto + position = auto + scale = 1.000000 + transform = normal + + [place] + mode = center + + [preserve-output] + last_output_focus_timeout = 10000 + + [resize] + activate = BTN_RIGHT + + [scale] + allow_zoom = false + bg_color = #${xrgbaColors.base00} + duration = 750 + inactive_alpha = 0.750000 + interact = false + middle_click_close = false + spacing = 50 + text_color = #${xrgbaColors.base00} + title_font_size = 14 + title_overlay = all + title_position = center + toggle = KEY_P + toggle_all = + + [switcher] + next_view = KEY_TAB + prev_view = KEY_TAB + speed = 500 + view_thumbnail_scale = 1.000000 + + [vswipe] + background = #${xrgbaColors.base00} + duration = 180 + enable_smooth_transition = true + enable_vertical = true + fingers = 3 + gap = 32.000000 + speed_cap = 0.500000 + speed_factor = 500.000000 + #threshold = 0.250000 + + [vswitch] + background = #${xrgbaColors.base00} + binding_down = KEY_DOWN + binding_left = KEY_LEFT + binding_right = KEY_RIGHT + binding_up = KEY_UP + binding_win_down = KEY_DOWN + binding_win_left = KEY_LEFT + binding_win_right = KEY_RIGHT + binding_win_up = KEY_UP + duration = 300 + gap = 20 + wraparound = false + + [window-rules] + + [wm-actions] + minimize = none + toggle_always_on_top = none + toggle_fullscreen = KEY_F + toggle_maximize = none + toggle_showdesktop = none + toggle_sticky = none + + [wobbly] + friction = 3.000000 + grid_resolution = 6 + spring_k = 8.000000 + + [workarounds] + all_dialogs_modal = true + app_id_mode = stock + dynamic_repaint_delay = false + + [wrot] + activate = BTN_RIGHT + activate-3d = BTN_RIGHT + invert = false + reset = KEY_R + reset-one = KEY_R + reset_radius = 25.000000 + sensitivity = 24 + + [zoom] + modifier = + smoothing_duration = 300 + speed = 0.010000 + + [simple-tile] + button_move = BTN_LEFT + button_resize = BTN_RIGHT + inner_gap_size = 2 + keep_fullscreen_on_adjacent = true + key_focus_above = KEY_K + key_focus_below = KEY_J + key_focus_left = KEY_H + key_focus_right = KEY_L + key_toggle = KEY_T + tile_by_default = type is "toplevel" + #wm-actions.toggle_always_on_top + ''; +} diff --git a/foreign/dotfiles/hosts/README.md b/foreign/dotfiles/hosts/README.md new file mode 100644 index 0000000..0a82810 --- /dev/null +++ b/foreign/dotfiles/hosts/README.md @@ -0,0 +1,9 @@ +# Hosts config + +Name | Description +------------ | ----------- +`io` | Lenovo laptop, main machine +`kiiro` | Previous main machine, retired and rarely used server now + +All the hosts have a shared config in `modules/minimal.nix`. +Host specific configs are stored inside the specific host dir. diff --git a/foreign/dotfiles/hosts/default.nix b/foreign/dotfiles/hosts/default.nix new file mode 100644 index 0000000..b5317cb --- /dev/null +++ b/foreign/dotfiles/hosts/default.nix @@ -0,0 +1,38 @@ +{ + inputs, + withSystem, + sharedModules, + desktopModules, + homeImports, + ... +}: { + flake.nixosConfigurations = withSystem "x86_64-linux" ({system, ...}: { + io = inputs.nixpkgs.lib.nixosSystem { + inherit system; + + modules = + [ + ./io + ../modules/greetd.nix + ../modules/desktop.nix + ../modules/gamemode.nix + ../modules/howdy + ../modules/linux-enable-ir-emitter.nix + {home-manager.users.mihai.imports = homeImports."mihai@io";} + ] + ++ sharedModules + ++ desktopModules; + }; + + kiiro = inputs.nixpkgs.lib.nixosSystem { + inherit system; + + modules = + [ + ./kiiro + {home-manager.users.mihai.imports = homeImports.server;} + ] + ++ sharedModules; + }; + }); +} diff --git a/foreign/dotfiles/hosts/io/default.nix b/foreign/dotfiles/hosts/io/default.nix new file mode 100644 index 0000000..d7ab090 --- /dev/null +++ b/foreign/dotfiles/hosts/io/default.nix @@ -0,0 +1,162 @@ +{ + config, + pkgs, + inputs, + ... +} @ args: { + imports = [./hardware-configuration.nix]; + + age.secrets.spotify = { + file = "${inputs.self}/secrets/spotify.age"; + owner = "mihai"; + group = "users"; + }; + + boot = { + initrd = { + systemd.enable = true; + supportedFilesystems = ["ext4"]; + }; + + # load modules on boot + kernelModules = ["acpi_call" "amdgpu" "amd_pstate"]; + + # use latest kernel + kernelPackages = pkgs.linuxPackages_xanmod_latest; + # Panel Self Refresh + kernelParams = ["amdgpu.dcfeaturemask=0x8" "initcall_blacklist=acpi_cpufreq_init" "amd_pstate=passive" "amd_pstate.shared_mem=1"]; + + loader = { + # systemd-boot on UEFI + efi.canTouchEfiVariables = true; + systemd-boot.enable = true; + }; + + plymouth = { + enable = true; + themePackages = [inputs.self.packages.${pkgs.hostPlatform.system}.catppuccin-plymouth]; + # theme = "catppuccin-mocha"; + # font = "${pkgs.noto-fonts}/share/fonts/truetype/noto/NotoSans-Light.ttf"; + }; + }; + + environment.systemPackages = [config.boot.kernelPackages.cpupower]; + + hardware = { + bluetooth = { + enable = true; + # battery info support + package = pkgs.bluez5-experimental; + settings = { + # make Xbox Series X controller work + General = { + Class = "0x000100"; + ControllerMode = "bredr"; + FastConnectable = true; + JustWorksRepairing = "always"; + Privacy = "device"; + Experimental = true; + }; + }; + }; + + cpu.amd.updateMicrocode = true; + + enableRedistributableFirmware = true; + + opentabletdriver.enable = true; + + video.hidpi.enable = true; + + xpadneo.enable = true; + }; + + networking = { + hostName = "io"; + firewall = { + allowedTCPPorts = [42355]; + allowedUDPPorts = [5353]; + }; + }; + + programs = { + # enable hyprland and required options + hyprland.enable = true; + + # backlight control + light.enable = true; + + steam.enable = true; + }; + + security.tpm2 = { + enable = true; + abrmd.enable = true; + }; + + services = { + # for SSD/NVME + fstrim.enable = true; + + howdy = { + enable = true; + package = inputs.self.packages.${pkgs.system}.howdy; + settings = { + core.no_confirmation = true; + video.device_path = "/dev/video2"; + video.dark_threshold = 90; + }; + }; + + linux-enable-ir-emitter.enable = true; + + kmonad.keyboards = { + io = { + name = "io"; + device = "/dev/input/by-path/platform-i8042-serio-0-event-kbd"; + defcfg = { + enable = true; + fallthrough = true; + allowCommands = false; + }; + config = builtins.readFile "${inputs.self}/modules/main.kbd"; + }; + }; + + # see https://github.com/fufexan/nix-gaming/#pipewire-low-latency + pipewire.lowLatency.enable = true; + + printing.enable = true; + + # configure mice + ratbagd.enable = true; + + # power saving + tlp = { + enable = true; + settings = { + PCIE_ASPM_ON_BAT = "powersupersave"; + CPU_SCALING_GOVERNOR_ON_AC = "performance"; + CPU_SCALING_GOVERNOR_ON_BAT = "conservative"; + NMI_WATCHDOG = 0; + }; + }; + + udev.extraRules = let + inherit (import ./plugged.nix args) plugged unplugged; + in '' + # add my android device to adbusers + SUBSYSTEM=="usb", ATTR{idVendor}=="22d9", MODE="0666", GROUP="adbusers" + + # start/stop services on power (un)plug + SUBSYSTEM=="power_supply", ATTR{online}=="1", RUN+="${plugged}" + SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="${unplugged}" + ''; + + # add hyprland to display manager sessions + xserver.displayManager.sessionPackages = [inputs.hyprland.packages.${pkgs.hostPlatform.system}.default]; + }; + + # https://github.com/NixOS/nixpkgs/issues/114222 + systemd.user.services.telephony_client.enable = false; +} diff --git a/foreign/dotfiles/hosts/io/hardware-configuration.nix b/foreign/dotfiles/hosts/io/hardware-configuration.nix new file mode 100644 index 0000000..52c35a3 --- /dev/null +++ b/foreign/dotfiles/hosts/io/hardware-configuration.nix @@ -0,0 +1,35 @@ +# 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, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "usb_storage" "sd_mod"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-amd"]; + boot.extraModulePackages = []; + + fileSystems."/" = { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-label/boot"; + fsType = "vfat"; + }; + + swapDevices = []; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + # high-resolution display + hardware.video.hidpi.enable = lib.mkDefault true; +} diff --git a/foreign/dotfiles/hosts/io/plugged.nix b/foreign/dotfiles/hosts/io/plugged.nix new file mode 100644 index 0000000..008a020 --- /dev/null +++ b/foreign/dotfiles/hosts/io/plugged.nix @@ -0,0 +1,24 @@ +{ + pkgs, + lib, + inputs, + ... +}: let + programs = lib.makeBinPath [inputs.hyprland.packages.${pkgs.hostPlatform.system}.default]; +in { + unplugged = pkgs.writeShellScript "unplugged" '' + export PATH=$PATH:${programs} + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -w1 /tmp/hypr | tail -1) + + systemctl --user --machine=1000@ stop easyeffects syncthing + hyprctl --batch 'keyword decoration:drop_shadow 0 ; keyword animations:enabled 0' + ''; + + plugged = pkgs.writeShellScript "plugged" '' + export PATH=$PATH:${programs} + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -w1 /tmp/hypr | tail -1) + + systemctl --user --machine=1000@ start easyeffects syncthing + hyprctl --batch 'keyword decoration:drop_shadow 1 ; keyword animations:enabled 1' + ''; +} diff --git a/foreign/dotfiles/hosts/kiiro/default.nix b/foreign/dotfiles/hosts/kiiro/default.nix new file mode 100644 index 0000000..20580f0 --- /dev/null +++ b/foreign/dotfiles/hosts/kiiro/default.nix @@ -0,0 +1,31 @@ +# biggest homeserver +{lib, ...}: { + imports = [ + ./hardware-configuration.nix + ./services.nix + ]; + + # used by tailscale for exit node + boot.kernel.sysctl = { + "net.ipv4.ip_forward" = 1; + "net.ipv6.conf.all.forwarding" = 1; + }; + + # bootloader + boot.loader = { + efi.canTouchEfiVariables = true; + systemd-boot.enable = true; + }; + + hardware = { + cpu.intel.updateMicrocode = true; + + enableRedistributableFirmware = true; + }; + + networking.hostName = "kiiro"; + + services.btrfs.autoScrub.enable = true; + + system.stateVersion = lib.mkForce "21.11"; +} diff --git a/foreign/dotfiles/hosts/kiiro/hardware-configuration.nix b/foreign/dotfiles/hosts/kiiro/hardware-configuration.nix new file mode 100644 index 0000000..96d30d1 --- /dev/null +++ b/foreign/dotfiles/hosts/kiiro/hardware-configuration.nix @@ -0,0 +1,30 @@ +# 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. +{modulesPath, ...}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-intel"]; + boot.extraModulePackages = []; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/a2fc0125-f3e6-4984-9eff-9010c154cb7b"; + fsType = "btrfs"; + options = ["subvol=root"]; + }; + + fileSystems."/persist" = { + device = "/dev/disk/by-uuid/a2fc0125-f3e6-4984-9eff-9010c154cb7b"; + fsType = "btrfs"; + options = ["subvol=persist"]; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/7B5A-471E"; + fsType = "vfat"; + }; +} diff --git a/foreign/dotfiles/hosts/kiiro/services.nix b/foreign/dotfiles/hosts/kiiro/services.nix new file mode 100644 index 0000000..d13cf0a --- /dev/null +++ b/foreign/dotfiles/hosts/kiiro/services.nix @@ -0,0 +1,67 @@ +# server services +{ + security.acme.defaults = { + email = "fufexan@protonmail.com"; + server = "https://acme-staging-v02.api.letsencrypt.org/directory"; + }; + + services.minecraft-server = { + enable = false; + eula = true; + jvmOpts = '' + -Xmx6G -Xms1G -XX:+UseG1GC + -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 + ''; + openFirewall = true; + }; + + services.nginx = { + enable = false; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL"; + + virtualHosts = { + "jellyfin.fufexan.net" = { + forceSSL = true; + enableACME = true; + + locations."= /".return = "302 https://$host/web"; + locations."/" = { + proxyPass = "http://127.0.0.1:8096"; + extraConfig = "proxy_buffering off"; + }; + locations."= /web/".proxyPass = "http://127.0.0.1:8096/web/index.html"; + locations."/socket" = { + proxyPass = "http://127.0.0.1:8096"; + proxyWebsockets = true; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + ''; + }; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [80 139 443 445 5357 8384 8443]; + networking.firewall.allowedUDPPorts = [137 138 3702]; + + services.syncthing = { + enable = true; + group = "users"; + guiAddress = ":8384"; + openDefaultPorts = true; + declarative = {}; + }; + + services.transmission = { + openFirewall = true; + settings.rpc-bind-address = "0.0.0.0"; + settings.rpc-whitelist-enables = false; + }; +} diff --git a/foreign/dotfiles/lib/README.md b/foreign/dotfiles/lib/README.md new file mode 100644 index 0000000..716632e --- /dev/null +++ b/foreign/dotfiles/lib/README.md @@ -0,0 +1,9 @@ +# Lib + +Various functions I use throughout the config: + +Name | Description +------------- | ----------- +`colors.nix` | Functions for dealing with colors. Used for `default`. +`default.nix` | Module for flake-parts +`repl.nix` | Cool Nix REPL wrapper diff --git a/foreign/dotfiles/lib/colors.nix b/foreign/dotfiles/lib/colors.nix new file mode 100644 index 0000000..10bf428 --- /dev/null +++ b/foreign/dotfiles/lib/colors.nix @@ -0,0 +1,67 @@ +lib: +with lib; rec { + # color-related functions + + # convert rrggbb hex to #rrggbb + x = c: "#${c}"; + + # convert rrggbb hex to rgba(r, g, b, a) css + rgba = c: let + r = toString (hexToDec (__substring 0 2 c)); + g = toString (hexToDec (__substring 2 2 c)); + b = toString (hexToDec (__substring 4 2 c)); + res = "rgba(${r}, ${g}, ${b}, .5)"; + in + res; + + # general stuff + + # functions copied from https://gist.github.com/corpix/f761c82c9d6fdbc1b3846b37e1020e11 + # convert a hex value to an integer + hexToDec = v: let + hexToInt = { + "0" = 0; + "1" = 1; + "2" = 2; + "3" = 3; + "4" = 4; + "5" = 5; + "6" = 6; + "7" = 7; + "8" = 8; + "9" = 9; + "a" = 10; + "b" = 11; + "c" = 12; + "d" = 13; + "e" = 14; + "f" = 15; + "A" = 10; + "B" = 11; + "C" = 12; + "D" = 13; + "E" = 14; + "F" = 15; + }; + chars = stringToCharacters v; + charsLen = length chars; + in + foldl + (a: v: a + v) + 0 + (imap0 + (k: v: hexToInt."${v}" * (pow 16 (charsLen - k - 1))) + chars); + + pow = let + pow' = base: exponent: value: + # FIXME: It will silently overflow on values > 2**62 :( + # The value will become negative or zero in this case + if exponent == 0 + then 1 + else if exponent <= 1 + then value + else (pow' base (exponent - 1) (value * base)); + in + base: exponent: pow' base exponent base; +} diff --git a/foreign/dotfiles/lib/default.nix b/foreign/dotfiles/lib/default.nix new file mode 100644 index 0000000..bdfb630 --- /dev/null +++ b/foreign/dotfiles/lib/default.nix @@ -0,0 +1,41 @@ +{inputs, ...}: +# personal lib +let + inherit (inputs.nixpkgs) lib; + + colorlib = import ./colors.nix lib; + default = import ./theme {inherit colorlib lib;}; +in { + imports = [ + {_module.args = {inherit default;};} + ]; + + perSystem = {system, ...}: { + legacyPackages = import inputs.nixpkgs { + inherit system; + config.allowUnfree = true; + config.overlays = [ + ( + _: prev: { + steam = prev.steam.override { + extraPkgs = pkgs: + with pkgs; [ + keyutils + libkrb5 + libpng + libpulseaudio + libvorbis + stdenv.cc.cc.lib + xorg.libXcursor + xorg.libXi + xorg.libXinerama + xorg.libXScrnSaver + ]; + extraProfile = "export GDK_SCALE=2"; + }; + } + ) + ]; + }; + }; +} diff --git a/foreign/dotfiles/lib/repl.nix b/foreign/dotfiles/lib/repl.nix new file mode 100644 index 0000000..7f14583 --- /dev/null +++ b/foreign/dotfiles/lib/repl.nix @@ -0,0 +1,49 @@ +{ + flakePath ? null, + hostnamePath ? "/etc/hostname", + registryPath ? /etc/nix/registry.json, +}: let + inherit (builtins) getFlake head match currentSystem readFile pathExists filter fromJSON; + + selfFlake = + if pathExists registryPath + then filter (it: it.from.id == "self") (fromJSON (readFile registryPath)).flakes + else []; + + flakePath' = + toString + ( + if flakePath != null + then flakePath + else if selfFlake != [] + then (head selfFlake).to.path + else "/etc/nixos" + ); + + flake = + if pathExists flakePath' + then getFlake flakePath' + else {}; + hostname = + if pathExists hostnamePath + then head (match "([a-zA-Z0-9\\-]+)\n" (readFile hostnamePath)) + else ""; + + nixpkgsFromInputsPath = flake.inputs.nixpkgs.outPath or ""; + nixpkgs = + flake.pkgs.${currentSystem}.nixpkgs + or ( + if nixpkgsFromInputsPath != "" + then import nixpkgsFromInputsPath {} + else {} + ); + + nixpkgsOutput = removeAttrs (nixpkgs // nixpkgs.lib or {}) ["options" "config"]; +in + {inherit flake;} + // flake + // builtins + // (flake.nixosConfigurations or {}) + // flake.nixosConfigurations.${hostname} or {} + // nixpkgsOutput + // {getFlake = path: getFlake (toString path);} diff --git a/foreign/dotfiles/lib/theme/colors.nix b/foreign/dotfiles/lib/theme/colors.nix new file mode 100644 index 0000000..f468bf1 --- /dev/null +++ b/foreign/dotfiles/lib/theme/colors.nix @@ -0,0 +1,37 @@ +rec { + rosewater = "f5e0dc"; + flamingo = "f2cdcd"; + pink = "f5c2e7"; + mauve = "cba6f7"; + red = "f38ba8"; + maroon = "eba0ac"; + peach = "fab387"; + yellow = "f9e2af"; + green = "a6e3a1"; + teal = "94e2d5"; + sky = "89dceb"; + sapphire = "74c7ec"; + blue = "89b4fa"; + lavender = "b4befe"; + + text = "cdd6f4"; + subtext1 = "bac2de"; + subtext0 = "a6adc8"; + overlay2 = "9399b2"; + overlay1 = "7f849c"; + overlay0 = "6c7086"; + + surface2 = "585b70"; + surface1 = "45475a"; + surface0 = "313244"; + + base = "1e1e2e"; + mantle = "181825"; + crust = "11111b"; + + fg = text; + bg = base; + bg1 = surface0; + border = "28283d"; + shadow = crust; +} diff --git a/foreign/dotfiles/lib/theme/default.nix b/foreign/dotfiles/lib/theme/default.nix new file mode 100644 index 0000000..3d997f5 --- /dev/null +++ b/foreign/dotfiles/lib/theme/default.nix @@ -0,0 +1,25 @@ +{ + colorlib, + lib, +}: rec { + colors = import ./colors.nix; + # #RRGGBB + xcolors = lib.mapAttrs (_: colorlib.x) colors; + # rgba(,,,) colors (css) + rgbaColors = lib.mapAttrs (_: colorlib.rgba) colors; + + browser = "firefox"; + + terminal = { + font = "JetBrainsMono Nerd Font"; + name = "wezterm"; + opacity = 0.9; + size = 11; + }; + + wallpaper = builtins.fetchurl rec { + name = "wallpaper-${sha256}.png"; + url = "https://images.unsplash.com/photo-1567095716798-1d95d8f4c479?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8"; + sha256 = "1x9y9rzqb9mpxc5lmgvc7jxqdyn3j7ryv16vn5lx6qrhpwp24kym"; + }; +} diff --git a/foreign/dotfiles/modules/README.md b/foreign/dotfiles/modules/README.md new file mode 100644 index 0000000..ea50472 --- /dev/null +++ b/foreign/dotfiles/modules/README.md @@ -0,0 +1,15 @@ +# Modules + +As of now, there are multiple modules included: + +Name | Description +-------------- | ----------- +`default.nix` | Flake-parts module +Desktop | Config aimed at desktop usage +Gamemode | Gamemode settings +Gnome | GNOME config +Greetd | Greetd + GTKGreet config +Minimal | Shared configuration +Nix | Nix-related options +Security | Tweaks for a more secure system, borrowed from [hlissner](https://github.com/hlissner/dotfiles/blob/master/modules/security.nix) +Xserver | Xorg config diff --git a/foreign/dotfiles/modules/default.nix b/foreign/dotfiles/modules/default.nix new file mode 100644 index 0000000..16aa0a4 --- /dev/null +++ b/foreign/dotfiles/modules/default.nix @@ -0,0 +1,47 @@ +{ + _inputs, + inputs, + default, + ... +}: let + module_args = { + _module.args = { + inputs = _inputs; + inherit default; + }; + }; +in { + imports = [ + { + _module.args = { + inherit module_args; + + sharedModules = [ + {home-manager.useGlobalPkgs = true;} + {disabledModules = ["security/pam.nix"];} + inputs.agenix.nixosModules.default + inputs.hm.nixosModule + ./minimal.nix + ./pam.nix + module_args + ./nix.nix + ./security.nix + ]; + + desktopModules = with inputs; [ + hyprland.nixosModules.default + kmonad.nixosModules.default + nix-gaming.nixosModules.default + ]; + }; + } + ]; + + flake.nixosModules = { + desktop = import ./desktop.nix; + gamemode = import ./gamemode.nix; + greetd = import ./greetd.nix; + minimal = import ./minimal.nix; + nix = import ./nix.nix; + }; +} diff --git a/foreign/dotfiles/modules/desktop.nix b/foreign/dotfiles/modules/desktop.nix new file mode 100644 index 0000000..fc9aad6 --- /dev/null +++ b/foreign/dotfiles/modules/desktop.nix @@ -0,0 +1,153 @@ +{ + config, + pkgs, + inputs, + ... +}: { + fonts = { + fonts = with pkgs; [ + # icon fonts + material-symbols + + # normal fonts + jost + lexend + noto-fonts + noto-fonts-cjk + noto-fonts-emoji + roboto + + # nerdfonts + (nerdfonts.override {fonts = ["FiraCode" "JetBrainsMono"];}) + ]; + + # use fonts specified by user rather than default ones + enableDefaultFonts = false; + + # user defined fonts + # the reason there's Noto Color Emoji everywhere is to override DejaVu's + # B&W emojis that would sometimes show instead of some Color emojis + fontconfig.defaultFonts = { + serif = ["Noto Serif" "Noto Color Emoji"]; + sansSerif = ["Noto Sans" "Noto Color Emoji"]; + monospace = ["JetBrainsMono Nerd Font" "Noto Color Emoji"]; + emoji = ["Noto Color Emoji"]; + }; + }; + + # use Wayland where possible (electron) + environment.variables.NIXOS_OZONE_WL = "1"; + + # enable location service + location.provider = "geoclue2"; + + networking = { + firewall = { + # for Rocket League + allowedTCPPortRanges = [ + { + from = 27015; + to = 27030; + } + { + from = 27036; + to = 27037; + } + ]; + allowedUDPPorts = [4380 27036 34197]; + allowedUDPPortRanges = [ + { + from = 7000; + to = 9000; + } + { + from = 27000; + to = 27031; + } + ]; + + # Spotify track sync with other devices + allowedTCPPorts = [57621]; + }; + }; + + nix = { + # package = inputs.nix-super.packages.${pkgs.hostPlatform.system}.nix; + settings = { + substituters = [ + "https://nix-gaming.cachix.org" + "https://hyprland.cachix.org" + "https://cache.privatevoid.net" + ]; + trusted-public-keys = [ + "nix-gaming.cachix.org-1:nbjlureqMbRAxR1gJ/f3hxemL9svXaZF/Ees8vCUUs4=" + "hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc=" + "cache.privatevoid.net:SErQ8bvNWANeAvtsOESUwVYr2VJynfuc9JRwlzTTkVg=" + ]; + }; + }; + + # make HM-managed GTK stuff work + programs.dconf.enable = true; + + services = { + # use Ambient Light Sensors for auto brightness adjustment + clight = { + enable = true; + settings = { + verbose = true; + dpms.timeouts = [900 300]; + dimmer.timeouts = [870 270]; + screen.disabled = true; + }; + }; + + # provide location + geoclue2.enable = true; + + # keyboard remapping + kmonad = { + enable = true; + package = inputs.kmonad.packages.${pkgs.hostPlatform.system}.default; + keyboards = { + one2mini = { + device = "/dev/input/by-id/usb-Ducky_Ducky_One2_Mini_RGB_DK-V1.17-190813-event-kbd"; + defcfg = { + enable = true; + fallthrough = true; + allowCommands = false; + }; + config = builtins.readFile "${inputs.self}/modules/main.kbd"; + }; + }; + }; + + pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + jack.enable = true; + pulse.enable = true; + }; + + # battery info & stuff + upower.enable = true; + + # needed for GNOME services outside of GNOME Desktop + dbus.packages = [pkgs.gcr]; + udev.packages = with pkgs; [gnome.gnome-settings-daemon]; + }; + + security = { + # allow wayland lockers to unlock the screen + pam.services.swaylock.text = "auth include login"; + + # userland niceness + rtkit.enable = true; + }; + + xdg.portal = { + enable = true; + extraPortals = [pkgs.xdg-desktop-portal-gtk]; + }; +} diff --git a/foreign/dotfiles/modules/gamemode.nix b/foreign/dotfiles/modules/gamemode.nix new file mode 100644 index 0000000..1e601c0 --- /dev/null +++ b/foreign/dotfiles/modules/gamemode.nix @@ -0,0 +1,34 @@ +{ + config, + pkgs, + lib, + ... +}: let + programs = lib.makeBinPath [config.programs.hyprland.package]; + + startscript = pkgs.writeShellScript "gamemode-start" '' + export PATH=$PATH:${programs} + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -1 /tmp/hypr | tail -1) + hyprctl --batch 'keyword decoration:blur 0 ; keyword animations:enabled 0 ; keyword misc:no_vfr 1' + ''; + + endscript = pkgs.writeShellScript "gamemode-end" '' + export PATH=$PATH:${programs} + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -1 /tmp/hypr | tail -1) + hyprctl --batch 'keyword decoration:blur 1 ; keyword animations:enabled 1 ; keyword misc:no_vfr 0' + ''; +in { + programs.gamemode = { + enable = true; + settings = { + general = { + softrealtime = "auto"; + renice = 15; + }; + custom = { + start = startscript.outPath; + end = endscript.outPath; + }; + }; + }; +} diff --git a/foreign/dotfiles/modules/gnome.nix b/foreign/dotfiles/modules/gnome.nix new file mode 100644 index 0000000..6a09b00 --- /dev/null +++ b/foreign/dotfiles/modules/gnome.nix @@ -0,0 +1,44 @@ +{ + lib, + pkgs, + ... +}: +# GNOME 41 config +{ + environment.systemPackages = with pkgs.gnomeExtensions; [ + appindicator + gsconnect + ideapad-mode + vitals + pkgs.gnome.gnome-tweaks + ]; + + # we're using pipewire instead + hardware.pulseaudio.enable = lib.mkForce false; + + networking = { + # for GSConnect + firewall = { + allowedTCPPortRanges = [ + { + from = 1714; + to = 1764; + } + ]; + allowedUDPPortRanges = [ + { + from = 1714; + to = 1764; + } + ]; + }; + }; + + services.gnome.games.enable = true; + services.power-profiles-daemon.enable = lib.mkForce false; + services.xserver = { + desktopManager.gnome = { + enable = true; + }; + }; +} diff --git a/foreign/dotfiles/modules/greetd.nix b/foreign/dotfiles/modules/greetd.nix new file mode 100644 index 0000000..c229ccb --- /dev/null +++ b/foreign/dotfiles/modules/greetd.nix @@ -0,0 +1,63 @@ +{ + lib, + pkgs, + config, + inputs, + default, + ... +}: +# greetd display manager +let + greetdSwayConfig = pkgs.writeText "greetd-sway-config" '' + exec "dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP" + input "type:touchpad" { + tap enabled + } + seat seat0 xcursor_theme Bibata-Modern-Classic 24 + + xwayland disable + + bindsym XF86MonBrightnessUp exec light -A 5 + bindsym XF86MonBrightnessDown exec light -U 5 + bindsym Print exec ${lib.getExe pkgs.grim} /tmp/regreet.png + bindsym Mod4+shift+e exec swaynag \ + -t warning \ + -m 'What do you want to do?' \ + -b 'Poweroff' 'systemctl poweroff' \ + -b 'Reboot' 'systemctl reboot' + + exec "${lib.getExe config.programs.regreet.package} -l debug; swaymsg exit" + ''; +in { + imports = [./regreet.nix]; + environment.systemPackages = with pkgs; [ + # theme packages + (catppuccin-gtk.override { + accents = ["mauve"]; + size = "compact"; + variant = "mocha"; + }) + bibata-cursors + papirus-icon-theme + ]; + + programs.regreet = { + enable = true; + package = inputs.self.packages.${pkgs.hostPlatform.system}.regreet; + settings = { + background = default.wallpaper; + background_fit = "Cover"; + GTK = { + cursor_theme_name = "Bibata-Modern-Classic"; + font_name = "Jost * 12"; + icon_theme_name = "Papirus-Dark"; + theme_name = "Catppuccin-Mocha-Compact-Mauve-Dark"; + }; + }; + }; + + services.greetd.settings.default_session.command = "${inputs.self.packages.${pkgs.hostPlatform.system}.sway-hidpi}/bin/sway --config ${greetdSwayConfig}"; + + # unlock GPG keyring on login + security.pam.services.greetd.gnupg.enable = true; +} diff --git a/foreign/dotfiles/modules/howdy/config.nix b/foreign/dotfiles/modules/howdy/config.nix new file mode 100644 index 0000000..c585871 --- /dev/null +++ b/foreign/dotfiles/modules/howdy/config.nix @@ -0,0 +1,45 @@ +{ + core = { + detection_notice = false; + timeout_notice = true; + no_confirmation = false; + suppress_unknown = false; + abort_if_ssh = true; + abort_if_lid_closed = true; + disabled = false; + use_cnn = false; + workaround = "off"; + }; + + video = { + certainty = 3.5; + timeout = 4; + device_path = "/dev/video2"; + warn_no_device = true; + max_height = 320; + frame_width = -1; + frame_height = -1; + dark_threshold = 60; + recording_plugin = "opencv"; + device_format = "v4l2"; + force_mjpeg = false; + exposure = -1; + rotate = 0; + }; + + snapshots = { + save_failed = false; + save_successful = false; + }; + + rubberstamps = { + enabled = false; + stamp_rules = "nod 5s failsafe min_distance=12"; + }; + + debug = { + end_report = false; + verbose_stamps = false; + gtk_stdout = false; + }; +} diff --git a/foreign/dotfiles/modules/howdy/default.nix b/foreign/dotfiles/modules/howdy/default.nix new file mode 100644 index 0000000..95506b2 --- /dev/null +++ b/foreign/dotfiles/modules/howdy/default.nix @@ -0,0 +1,45 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.services.howdy; + settingsType = pkgs.formats.ini {}; +in { + options = { + services.howdy = { + enable = + mkEnableOption (mdDoc "") + // { + description = mdDoc '' + Howdy and PAM module for face recognition. See + `services.linux-enable-ir-emitter` for enabling the IR emitter support. + ''; + }; + + package = mkPackageOptionMD pkgs "howdy" {}; + + settings = mkOption { + inherit (settingsType) type; + default = import ./config.nix; + description = mdDoc '' + Howdy configuration file. Refer to + + for options. + ''; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + environment.etc."howdy/config.ini".source = settingsType.generate "howdy-config.ini" cfg.settings; + }) + { + services.howdy.settings = mapAttrsRecursive (name: mkDefault) (import ./config.nix); + } + ]; +} diff --git a/foreign/dotfiles/modules/linux-enable-ir-emitter.nix b/foreign/dotfiles/modules/linux-enable-ir-emitter.nix new file mode 100644 index 0000000..1d8c48b --- /dev/null +++ b/foreign/dotfiles/modules/linux-enable-ir-emitter.nix @@ -0,0 +1,64 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.services.linux-enable-ir-emitter; +in { + options = { + services.linux-enable-ir-emitter = { + enable = mkEnableOption { + description = '' + Linux Enable IR Emitter. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.linux-enable-ir-emitter or inputs.self.packages.${pkgs.system}.linux-enable-ir-emitter; + defaultText = "pkgs.linux-enable-ir-emitter"; + description = '' + Package to use for the Linux Enable IR Emitter service. + ''; + }; + + device = mkOption { + type = types.lines; + default = "video2"; + defaultText = "video2"; + description = '' + Emitter device to depend on. Find this with the command + {command}`realpath /dev/v4l/by-path/`. + ''; + }; + }; + }; + config = mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + systemd.services.linux-enable-ir-emitter = let + targets = [ + "multi-user.target" + "suspend.target" + "hybrid-sleep.target" + "hibernate.target" + "suspend-then-hibernate.target" + ]; + in { + description = "Enable the infrared emitter."; + + script = "${lib.getExe cfg.package} run"; + + wantedBy = targets; + after = targets ++ ["dev-${cfg.device}.device"]; + }; + + systemd.tmpfiles.rules = [ + "d /var/lib/linux-enable-ir-emitter 0755 root root - -" + ]; + environment.etc."linux-enable-ir-emitter".source = "/var/lib/linux-enable-ir-emitter"; + }; +} diff --git a/foreign/dotfiles/modules/main.kbd b/foreign/dotfiles/modules/main.kbd new file mode 100644 index 0000000..7c80c79 --- /dev/null +++ b/foreign/dotfiles/modules/main.kbd @@ -0,0 +1,49 @@ +(defsrc + grv 1 2 3 4 5 6 7 8 9 0 - = bspc + tab q w e r t y u i o p [ ] \ + caps a s d f g h j k l ; ' ret + lsft z x c v b n m , . / rsft + lctl lmet lalt spc ralt rmet cmp rctl +) + +(deflayer colemak + grv 1 2 3 4 5 6 7 8 9 0 - = bspc + tab q w f p b j l u y ; [ ] \ + @esc a r s t g m n e i o ' ret + lsft x c d v z k h , . / rsft + @lay lmet lalt @spc ralt rmet cmp rctl +) + +(deflayer qwerty + grv 1 2 3 4 5 6 7 8 9 0 - = bspc + tab q w e r t y u i o p [ ] \ + @cps a s d f g h j k l ; ' ret + lsft z x c v b n m , . / rsft + lctl lmet lalt spc ralt rmet cmp rctl +) + +(deflayer layouts + _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ + caps _ _ _ @cmk _ _ @qwe _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ +) + +(deflayer symbols + _ f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 del + _ _ _ _ _ _ ins _ up _ pgup home prnt _ + _ _ _ _ _ _ _ left down rght pgdn end _ + _ _ _ _ _ _ _ mute vold volu _ _ + pp _ prev _ next _ _ _ +) + +(defalias + lay (layer-toggle layouts) + cmk (layer-switch colemak) + qwe (layer-switch qwerty) + sym (layer-toggle symbols) + esc (tap-next-release esc lctl) + spc (tap-next-release spc @sym) + cps (tap-next-release caps @lay) +) diff --git a/foreign/dotfiles/modules/minimal.nix b/foreign/dotfiles/modules/minimal.nix new file mode 100644 index 0000000..6d3704b --- /dev/null +++ b/foreign/dotfiles/modules/minimal.nix @@ -0,0 +1,97 @@ +{ + pkgs, + config, + lib, + inputs, + ... +}: +# configuration shared by all hosts +{ + # enable zsh autocompletion for system packages (systemd, etc) + environment.pathsToLink = ["/share/zsh"]; + + i18n = { + defaultLocale = "en_US.UTF-8"; + # saves space + supportedLocales = ["en_US.UTF-8/UTF-8" "ja_JP.UTF-8/UTF-8" "ro_RO.UTF-8/UTF-8"]; + }; + + # graphics drivers / HW accel + hardware.opengl.enable = true; + + networking = { + # required to connect to Tailscale exit nodes + firewall.checkReversePath = "loose"; + + networkmanager = { + enable = true; + dns = "systemd-resolved"; + wifi.powersave = true; + }; + }; + + # pickup pkgs from flake export + nixpkgs.pkgs = inputs.self.legacyPackages.${config.nixpkgs.system}; + + # enable programs + programs = { + less.enable = true; + + zsh = { + enable = true; + autosuggestions.enable = true; + syntaxHighlighting = { + enable = true; + patterns = {"rm -rf *" = "fg=white,bg=red";}; + styles = {"alias" = "fg=magenta";}; + highlighters = ["main" "brackets" "pattern"]; + }; + }; + }; + + # don't ask for password for wheel group + security.sudo.wheelNeedsPassword = false; + + services = { + # network discovery, mDNS + avahi = { + enable = true; + nssmdns = true; + publish.enable = true; + publish.domain = true; + publish.userServices = true; + }; + + openssh = { + enable = true; + settings.UseDns = true; + }; + + # DNS resolver + resolved.enable = true; + + # inter-machine VPN + tailscale.enable = true; + }; + + # don't touch this + system.stateVersion = lib.mkDefault "20.09"; + + # Don't wait for network startup + # https://old.reddit.com/r/NixOS/comments/vdz86j/how_to_remove_boot_dependency_on_network_for_a + systemd = { + targets.network-online.wantedBy = pkgs.lib.mkForce []; # Normally ["multi-user.target"] + services.NetworkManager-wait-online.wantedBy = pkgs.lib.mkForce []; # Normally ["network-online.target"] + }; + + time.timeZone = lib.mkDefault "Europe/Bucharest"; + + users.users.mihai = { + isNormalUser = true; + shell = pkgs.zsh; + extraGroups = ["adbusers" "input" "libvirtd" "networkmanager" "plugdev" "transmission" "video" "wheel"]; + }; + + # compresses half the ram for use as swap + zramSwap.enable = true; +} diff --git a/foreign/dotfiles/modules/nix.nix b/foreign/dotfiles/modules/nix.nix new file mode 100644 index 0000000..4c1a78f --- /dev/null +++ b/foreign/dotfiles/modules/nix.nix @@ -0,0 +1,71 @@ +{ + config, + pkgs, + inputs, + lib, + ... +}: { + # we need git for flakes + environment.systemPackages = [pkgs.git]; + + nix = { + # extra builders to offload work onto + # don't set a machine as a builder to itself (throws warnings) + buildMachines = lib.filter (x: x.hostName != config.networking.hostName) [ + { + system = "aarch64-linux"; + sshUser = "root"; + sshKey = "/etc/ssh/ssh_host_ed25519_key"; + maxJobs = 4; + hostName = "arm-server"; + supportedFeatures = ["nixos-test" "benchmark" "kvm" "big-parallel"]; + } + { + system = "x86_64-linux"; + sshUser = "root"; + sshKey = "/root/.ssh/id_ed25519"; + maxJobs = 8; + hostName = "io"; + supportedFeatures = ["nixos-test" "benchmark" "kvm" "big-parallel"]; + } + ]; + distributedBuilds = true; + + # auto garbage collect + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 7d"; + }; + + # pin the registry to avoid downloading and evaling a new nixpkgs version every time + registry = lib.mapAttrs (_: v: {flake = v;}) inputs; + + # set the path for channels compat + nixPath = lib.mapAttrsToList (key: _: "${key}=flake:${key}") config.nix.registry; + + settings = { + auto-optimise-store = true; + builders-use-substitutes = true; + experimental-features = ["nix-command" "flakes"]; + flake-registry = "/etc/nix/registry.json"; + + # for direnv GC roots + keep-derivations = true; + keep-outputs = true; + + substituters = [ + "https://nix-community.cachix.org" + "https://helix.cachix.org" + "https://fufexan.cachix.org" + ]; + trusted-public-keys = [ + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + "helix.cachix.org-1:ejp9KQpR1FBI2onstMQ34yogDm4OgU2ru6lIwPvuCVs=" + "fufexan.cachix.org-1:LwCDjCJNJQf5XD2BV+yamQIMZfcKWR9ISIFy5curUsY=" + ]; + + trusted-users = ["root" "@wheel"]; + }; + }; +} diff --git a/foreign/dotfiles/modules/pam.nix b/foreign/dotfiles/modules/pam.nix new file mode 100644 index 0000000..591e7ca --- /dev/null +++ b/foreign/dotfiles/modules/pam.nix @@ -0,0 +1,1445 @@ +# This module provides configuration for the PAM (Pluggable +# Authentication Modules) system. +{ + config, + lib, + pkgs, + ... +}: +with lib; let + parentConfig = config; + + pamOpts = { + config, + name, + ... + }: let + cfg = config; + in let + config = parentConfig; + in { + options = { + name = mkOption { + example = "sshd"; + type = types.str; + description = lib.mdDoc "Name of the PAM service."; + }; + + unixAuth = mkOption { + default = true; + type = types.bool; + description = lib.mdDoc '' + Whether users can log in with passwords defined in + {file}`/etc/shadow`. + ''; + }; + + rootOK = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, root doesn't need to authenticate (e.g. for the + {command}`useradd` service). + ''; + }; + + p11Auth = mkOption { + default = config.security.pam.p11.enable; + defaultText = literalExpression "config.security.pam.p11.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, keys listed in + {file}`~/.ssh/authorized_keys` and + {file}`~/.eid/authorized_certificates` + can be used to log in with the associated PKCS#11 tokens. + ''; + }; + + u2fAuth = mkOption { + default = config.security.pam.u2f.enable; + defaultText = literalExpression "config.security.pam.u2f.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, users listed in + {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or + {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is + not set) are able to log in with the associated U2F key. Path can be + changed using {option}`security.pam.u2f.authFile` option. + ''; + }; + + usshAuth = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, users with an SSH certificate containing an authorized principal + in their SSH agent are able to log in. Specific options are controlled + using the {option}`security.pam.ussh` options. + + Note that the {option}`security.pam.ussh.enable` must also be + set for this option to take effect. + ''; + }; + + yubicoAuth = mkOption { + default = config.security.pam.yubico.enable; + defaultText = literalExpression "config.security.pam.yubico.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, users listed in + {file}`~/.yubico/authorized_yubikeys` + are able to log in with the associated Yubikey tokens. + ''; + }; + + googleAuthenticator = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, users with enabled Google Authenticator (created + {file}`~/.google_authenticator`) will be required + to provide Google Authenticator token to log in. + ''; + }; + }; + + usbAuth = mkOption { + default = config.security.pam.usb.enable; + defaultText = literalExpression "config.security.pam.usb.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, users listed in + {file}`/etc/pamusb.conf` are able to log in + with the associated USB key. + ''; + }; + + otpwAuth = mkOption { + default = config.security.pam.enableOTPW; + defaultText = literalExpression "config.security.pam.enableOTPW"; + type = types.bool; + description = lib.mdDoc '' + If set, the OTPW system will be used (if + {file}`~/.otpw` exists). + ''; + }; + + googleOsLoginAccountVerification = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, will use the Google OS Login PAM modules + (`pam_oslogin_login`, + `pam_oslogin_admin`) to verify possible OS Login + users and set sudoers configuration accordingly. + This only makes sense to enable for the `sshd` PAM + service. + ''; + }; + + googleOsLoginAuthentication = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, will use the `pam_oslogin_login`'s user + authentication methods to authenticate users using 2FA. + This only makes sense to enable for the `sshd` PAM + service. + ''; + }; + + mysqlAuth = mkOption { + default = config.users.mysql.enable; + defaultText = literalExpression "config.users.mysql.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, the `pam_mysql` module will be used to + authenticate users against a MySQL/MariaDB database. + ''; + }; + + fprintAuth = mkOption { + default = config.services.fprintd.enable; + defaultText = literalExpression "config.services.fprintd.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, fingerprint reader will be used (if exists and + your fingerprints are enrolled). + ''; + }; + + howdyAuth = mkOption { + default = config.services.howdy.enable; + defaultText = literalExpression "config.services.howdy.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, IR camera will be used (if exists and your + facial models are enrolled). + ''; + }; + + oathAuth = mkOption { + default = config.security.pam.oath.enable; + defaultText = literalExpression "config.security.pam.oath.enable"; + type = types.bool; + description = lib.mdDoc '' + If set, the OATH Toolkit will be used. + ''; + }; + + sshAgentAuth = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, the calling user's SSH agent is used to authenticate + against the keys in the calling user's + {file}`~/.ssh/authorized_keys`. This is useful + for {command}`sudo` on password-less remote systems. + ''; + }; + + duoSecurity = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, use the Duo Security pam module + `pam_duo` for authentication. Requires + configuration of {option}`security.duosec` options. + ''; + }; + }; + + startSession = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If set, the service will register a new session with + systemd's login manager. For local sessions, this will give + the user access to audio devices, CD-ROM drives. In the + default PolicyKit configuration, it also allows the user to + reboot the system. + ''; + }; + + setEnvironment = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether the service should set the environment variables + listed in {option}`environment.sessionVariables` + using `pam_env.so`. + ''; + }; + + setLoginUid = mkOption { + type = types.bool; + description = lib.mdDoc '' + Set the login uid of the process + ({file}`/proc/self/loginuid`) for auditing + purposes. The login uid is only set by ‘entry points’ like + {command}`login` and {command}`sshd`, not by + commands like {command}`sudo`. + ''; + }; + + ttyAudit = { + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable or disable TTY auditing for specified users + ''; + }; + + enablePattern = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + For each user matching one of comma-separated + glob patterns, enable TTY auditing + ''; + }; + + disablePattern = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + For each user matching one of comma-separated + glob patterns, disable TTY auditing + ''; + }; + + openOnly = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Set the TTY audit flag when opening the session, + but do not restore it when closing the session. + Using this option is necessary for some services + that don't fork() to run the authenticated session, + such as sudo. + ''; + }; + }; + + forwardXAuth = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether X authentication keys should be passed from the + calling user to the target user (e.g. for + {command}`su`) + ''; + }; + + pamMount = mkOption { + default = config.security.pam.mount.enable; + defaultText = literalExpression "config.security.pam.mount.enable"; + type = types.bool; + description = lib.mdDoc '' + Enable PAM mount (pam_mount) system to mount filesystems on user login. + ''; + }; + + allowNullPassword = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether to allow logging into accounts that have no password + set (i.e., have an empty password field in + {file}`/etc/passwd` or + {file}`/etc/group`). This does not enable + logging into disabled accounts (i.e., that have the password + field set to `!`). Note that regardless of + what the pam_unix documentation says, accounts with hashed + empty passwords are always allowed to log in. + ''; + }; + + nodelay = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether the delay after typing a wrong password should be disabled. + ''; + }; + + requireWheel = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether to permit root access only to members of group wheel. + ''; + }; + + limits = mkOption { + default = []; + type = limitsType; + description = lib.mdDoc '' + Attribute set describing resource limits. Defaults to the + value of {option}`security.pam.loginLimits`. + The meaning of the values is explained in {manpage}`limits.conf(5)`. + ''; + }; + + showMotd = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc "Whether to show the message of the day."; + }; + + makeHomeDir = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether to try to create home directories for users + with `$HOME`s pointing to nonexistent + locations on session login. + ''; + }; + + updateWtmp = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc "Whether to update {file}`/var/log/wtmp`."; + }; + + logFailures = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc "Whether to log authentication failures in {file}`/var/log/faillog`."; + }; + + enableAppArmor = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Enable support for attaching AppArmor profiles at the + user/group level, e.g., as part of a role based access + control scheme. + ''; + }; + + enableKwallet = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If enabled, pam_wallet will attempt to automatically unlock the + user's default KDE wallet upon login. If the user has no wallet named + "kdewallet", or the login password does not match their wallet + password, KDE will prompt separately after login. + ''; + }; + sssdStrictAccess = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc "enforce sssd access control"; + }; + + enableGnomeKeyring = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If enabled, pam_gnome_keyring will attempt to automatically unlock the + user's default Gnome keyring upon login. If the user login password does + not match their keyring password, Gnome Keyring will prompt separately + after login. + ''; + }; + + failDelay = { + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + If enabled, this will replace the `FAIL_DELAY` setting from `login.defs`. + Change the delay on failure per-application. + ''; + }; + + delay = mkOption { + default = 3000000; + type = types.int; + example = 1000000; + description = lib.mdDoc "The delay time (in microseconds) on failure."; + }; + }; + + gnupg = { + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + If enabled, pam_gnupg will attempt to automatically unlock the + user's GPG keys with the login password via + {command}`gpg-agent`. The keygrips of all keys to be + unlocked should be written to {file}`~/.pam-gnupg`, + and can be queried with {command}`gpg -K --with-keygrip`. + Presetting passphrases must be enabled by adding + `allow-preset-passphrase` in + {file}`~/.gnupg/gpg-agent.conf`. + ''; + }; + + noAutostart = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Don't start {command}`gpg-agent` if it is not running. + Useful in conjunction with starting {command}`gpg-agent` as + a systemd user service. + ''; + }; + + storeOnly = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Don't send the password immediately after login, but store for PAM + `session`. + ''; + }; + }; + + text = mkOption { + type = types.nullOr types.lines; + description = lib.mdDoc "Contents of the PAM service file."; + }; + }; + + # The resulting /etc/pam.d/* file contents are verified in + # nixos/tests/pam/pam-file-contents.nix. Please update tests there when + # changing the derivation. + config = { + name = mkDefault name; + setLoginUid = mkDefault cfg.startSession; + limits = mkDefault config.security.pam.loginLimits; + + # !!! TODO: move the LDAP stuff to the LDAP module, and the + # Samba stuff to the Samba module. This requires that the PAM + # module provides the right hooks. + text = + mkDefault + ( + '' + # Account management. + '' + + optionalString use_ldap '' + account sufficient ${pam_ldap}/lib/security/pam_ldap.so + '' + + optionalString cfg.mysqlAuth '' + account sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + '' + + optionalString (config.services.sssd.enable && cfg.sssdStrictAccess == false) '' + account sufficient ${pkgs.sssd}/lib/security/pam_sss.so + '' + + optionalString (config.services.sssd.enable && cfg.sssdStrictAccess) '' + account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so + '' + + optionalString config.security.pam.krb5.enable '' + account sufficient ${pam_krb5}/lib/security/pam_krb5.so + '' + + optionalString cfg.googleOsLoginAccountVerification '' + account [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + account [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so + '' + + optionalString config.services.homed.enable '' + account sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + '' + + + # The required pam_unix.so module has to come after all the sufficient modules + # because otherwise, the account lookup will fail if the user does not exist + # locally, for example with MySQL- or LDAP-auth. + '' + account required pam_unix.so + + # Authentication management. + '' + + optionalString cfg.googleOsLoginAuthentication '' + auth [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + '' + + optionalString cfg.rootOK '' + auth sufficient pam_rootok.so + '' + + optionalString cfg.requireWheel '' + auth required pam_wheel.so use_uid + '' + + optionalString cfg.logFailures '' + auth required pam_faillock.so + '' + + optionalString cfg.mysqlAuth '' + auth sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + '' + + optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) '' + auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} + '' + + (let + p11 = config.security.pam.p11; + in + optionalString cfg.p11Auth '' + auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so + '') + + (let + u2f = config.security.pam.u2f; + in + optionalString cfg.u2fAuth ('' + auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} '' + + '' ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} + '')) + + optionalString cfg.usbAuth '' + auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so + '' + + (let + ussh = config.security.pam.ussh; + in + optionalString (config.security.pam.ussh.enable && cfg.usshAuth) '' + auth ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} + '') + + (let + oath = config.security.pam.oath; + in + optionalString cfg.oathAuth '' + auth requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} + '') + + (let + yubi = config.security.pam.yubico; + in + optionalString cfg.yubicoAuth '' + auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} + '') + + optionalString cfg.fprintAuth '' + auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so + '' + + optionalString cfg.howdyAuth '' + auth sufficient ${config.services.howdy.package}/lib/security/pam_howdy.so + '' + + + # Modules in this block require having the password set in PAM_AUTHTOK. + # pam_unix is marked as 'sufficient' on NixOS which means nothing will run + # after it succeeds. Certain modules need to run after pam_unix + # prompts the user for password so we run it once with 'optional' at an + # earlier point and it will run again with 'sufficient' further down. + # We use try_first_pass the second time to avoid prompting password twice. + # + # The same principle applies to systemd-homed + (optionalString ((cfg.unixAuth || config.services.homed.enable) + && (config.security.pam.enableEcryptfs + || config.security.pam.enableFscrypt + || cfg.pamMount + || cfg.enableKwallet + || cfg.enableGnomeKeyring + || cfg.googleAuthenticator.enable + || cfg.gnupg.enable + || cfg.failDelay.enable + || cfg.duoSecurity.enable)) + ( + optionalString config.services.homed.enable '' + auth optional ${config.systemd.package}/lib/security/pam_systemd_home.so + '' + + optionalString cfg.unixAuth '' + auth optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth + '' + + optionalString config.security.pam.enableEcryptfs '' + auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap + '' + + optionalString config.security.pam.enableFscrypt '' + auth optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + '' + + optionalString cfg.pamMount '' + auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + '' + + optionalString cfg.enableKwallet '' + auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + '' + + optionalString cfg.enableGnomeKeyring '' + auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so + '' + + optionalString cfg.gnupg.enable '' + auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly " store-only"} + '' + + optionalString cfg.failDelay.enable '' + auth optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} + '' + + optionalString cfg.googleAuthenticator.enable '' + auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp + '' + + optionalString cfg.duoSecurity.enable '' + auth required ${pkgs.duo-unix}/lib/security/pam_duo.so + '' + )) + + optionalString config.services.homed.enable '' + auth sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + '' + + optionalString cfg.unixAuth '' + auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass + '' + + optionalString cfg.otpwAuth '' + auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so + '' + + optionalString use_ldap '' + auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass + '' + + optionalString config.services.sssd.enable '' + auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass + '' + + optionalString config.security.pam.krb5.enable '' + auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass + auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass + '' + + '' + auth required pam_deny.so + + # Password management. + '' + + optionalString config.services.homed.enable '' + password sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + '' + + '' + password sufficient pam_unix.so nullok sha512 + '' + + optionalString config.security.pam.enableEcryptfs '' + password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + '' + + optionalString config.security.pam.enableFscrypt '' + password optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + '' + + optionalString cfg.pamMount '' + password optional ${pkgs.pam_mount}/lib/security/pam_mount.so + '' + + optionalString use_ldap '' + password sufficient ${pam_ldap}/lib/security/pam_ldap.so + '' + + optionalString cfg.mysqlAuth '' + password sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + '' + + optionalString config.services.sssd.enable '' + password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok + '' + + optionalString config.security.pam.krb5.enable '' + password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + '' + + optionalString cfg.enableGnomeKeyring '' + password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok + '' + + '' + + # Session management. + '' + + optionalString cfg.setEnvironment '' + session required pam_env.so conffile=/etc/pam/environment readenv=0 + '' + + '' + session required pam_unix.so + '' + + optionalString cfg.setLoginUid '' + session ${ + if config.boot.isContainer + then "optional" + else "required" + } pam_loginuid.so + '' + + optionalString cfg.ttyAudit.enable (concatStringsSep " \\\n " ( + [ + "session required ${pkgs.pam}/lib/security/pam_tty_audit.so" + ] + ++ optional cfg.ttyAudit.openOnly "open_only" + ++ optional (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}" + ++ optional (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}" + )) + + optionalString config.services.homed.enable '' + session required ${config.systemd.package}/lib/security/pam_systemd_home.so + '' + + optionalString cfg.makeHomeDir '' + session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0077 + '' + + optionalString cfg.updateWtmp '' + session required ${pkgs.pam}/lib/security/pam_lastlog.so silent + '' + + optionalString config.security.pam.enableEcryptfs '' + session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + '' + + optionalString config.security.pam.enableFscrypt '' + # Work around https://github.com/systemd/systemd/issues/8598 + # Skips the pam_fscrypt module for systemd-user sessions which do not have a password + # anyways. + # See also https://github.com/google/fscrypt/issues/95 + session [success=1 default=ignore] pam_succeed_if.so service = systemd-user + session optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + '' + + optionalString cfg.pamMount '' + session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + '' + + optionalString use_ldap '' + session optional ${pam_ldap}/lib/security/pam_ldap.so + '' + + optionalString cfg.mysqlAuth '' + session optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + '' + + optionalString config.services.sssd.enable '' + session optional ${pkgs.sssd}/lib/security/pam_sss.so + '' + + optionalString config.security.pam.krb5.enable '' + session optional ${pam_krb5}/lib/security/pam_krb5.so + '' + + optionalString cfg.otpwAuth '' + session optional ${pkgs.otpw}/lib/security/pam_otpw.so + '' + + optionalString cfg.startSession '' + session optional ${config.systemd.package}/lib/security/pam_systemd.so + '' + + optionalString cfg.forwardXAuth '' + session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 + '' + + optionalString (cfg.limits != []) '' + session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} + '' + + optionalString (cfg.showMotd && (config.users.motd != null || config.users.motdFile != null)) '' + session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} + '' + + optionalString (cfg.enableAppArmor && config.security.apparmor.enable) '' + session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug + '' + + optionalString (cfg.enableKwallet) '' + session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + '' + + optionalString (cfg.enableGnomeKeyring) '' + session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start + '' + + optionalString cfg.gnupg.enable '' + session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} + '' + + optionalString (config.virtualisation.lxc.lxcfs.enable) '' + session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all + '' + ); + }; + }; + + inherit (pkgs) pam_krb5 pam_ccreds; + + use_ldap = config.users.ldap.enable && config.users.ldap.loginPam; + pam_ldap = + if config.users.ldap.daemon.enable + then pkgs.nss_pam_ldapd + else pkgs.pam_ldap; + + # Create a limits.conf(5) file. + makeLimitsConf = limits: + pkgs.writeText "limits.conf" + (concatMapStrings ({ + domain, + type, + item, + value, + }: "${domain} ${type} ${item} ${toString value}\n") + limits); + + limitsType = with lib.types; + listOf (submodule ({...}: { + options = { + domain = mkOption { + description = lib.mdDoc "Username, groupname, or wildcard this limit applies to"; + example = "@wheel"; + type = str; + }; + + type = mkOption { + description = lib.mdDoc "Type of this limit"; + type = enum ["-" "hard" "soft"]; + default = "-"; + }; + + item = mkOption { + description = lib.mdDoc "Item this limit applies to"; + type = enum [ + "core" + "data" + "fsize" + "memlock" + "nofile" + "rss" + "stack" + "cpu" + "nproc" + "as" + "maxlogins" + "maxsyslogins" + "priority" + "locks" + "sigpending" + "msgqueue" + "nice" + "rtprio" + ]; + }; + + value = mkOption { + description = lib.mdDoc "Value of this limit"; + type = oneOf [str int]; + }; + }; + })); + + motd = + if isNull config.users.motdFile + then pkgs.writeText "motd" config.users.motd + else config.users.motdFile; + + makePAMService = name: service: { + name = "pam.d/${name}"; + value.source = pkgs.writeText "${name}.pam" service.text; + }; +in { + imports = [ + (mkRenamedOptionModule ["security" "pam" "enableU2F"] ["security" "pam" "u2f" "enable"]) + ]; + + ###### interface + + options = { + security.pam.loginLimits = mkOption { + default = []; + type = limitsType; + example = [ + { + domain = "ftp"; + type = "hard"; + item = "nproc"; + value = "0"; + } + { + domain = "@student"; + type = "-"; + item = "maxlogins"; + value = "4"; + } + ]; + + description = lib.mdDoc '' + Define resource limits that should apply to users or groups. + Each item in the list should be an attribute set with a + {var}`domain`, {var}`type`, + {var}`item`, and {var}`value` + attribute. The syntax and semantics of these attributes + must be that described in {manpage}`limits.conf(5)`. + + Note that these limits do not apply to systemd services, + whose limits can be changed via {option}`systemd.extraConfig` + instead. + ''; + }; + + security.pam.services = mkOption { + default = {}; + type = with types; attrsOf (submodule pamOpts); + description = lib.mdDoc '' + This option defines the PAM services. A service typically + corresponds to a program that uses PAM, + e.g. {command}`login` or {command}`passwd`. + Each attribute of this set defines a PAM service, with the attribute name + defining the name of the service. + ''; + }; + + security.pam.makeHomeDir.skelDirectory = mkOption { + type = types.str; + default = "/var/empty"; + example = "/etc/skel"; + description = lib.mdDoc '' + Path to skeleton directory whose contents are copied to home + directories newly created by `pam_mkhomedir`. + ''; + }; + + security.pam.enableSSHAgentAuth = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable sudo logins if the user's SSH agent provides a key + present in {file}`~/.ssh/authorized_keys`. + This allows machines to exclusively use SSH keys instead of + passwords. + ''; + }; + + security.pam.enableOTPW = mkEnableOption (lib.mdDoc "the OTPW (one-time password) PAM module"); + + security.pam.krb5 = { + enable = mkOption { + default = config.krb5.enable; + defaultText = literalExpression "config.krb5.enable"; + type = types.bool; + description = lib.mdDoc '' + Enables Kerberos PAM modules (`pam-krb5`, + `pam-ccreds`). + + If set, users can authenticate with their Kerberos password. + This requires a valid Kerberos configuration + (`config.krb5.enable` should be set to + `true`). + + Note that the Kerberos PAM modules are not necessary when using SSS + to handle Kerberos authentication. + ''; + }; + }; + + security.pam.p11 = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Enables P11 PAM (`pam_p11`) module. + + If set, users can log in with SSH keys and PKCS#11 tokens. + + More information can be found [here](https://github.com/OpenSC/pam_p11). + ''; + }; + + control = mkOption { + default = "sufficient"; + type = types.enum ["required" "requisite" "sufficient" "optional"]; + description = lib.mdDoc '' + This option sets pam "control". + If you want to have multi factor authentication, use "required". + If you want to use the PKCS#11 device instead of the regular password, + use "sufficient". + + Read + {manpage}`pam.conf(5)` + for better understanding of this option. + ''; + }; + }; + + security.pam.u2f = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Enables U2F PAM (`pam-u2f`) module. + + If set, users listed in + {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or + {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is + not set) are able to log in with the associated U2F key. The path can + be changed using {option}`security.pam.u2f.authFile` option. + + File format is: + `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key` + This file can be generated using {command}`pamu2fcfg` command. + + More information can be found [here](https://developers.yubico.com/pam-u2f/). + ''; + }; + + authFile = mkOption { + default = null; + type = with types; nullOr path; + description = lib.mdDoc '' + By default `pam-u2f` module reads the keys from + {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or + {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is + not set). + + If you want to change auth file locations or centralize database (for + example use {file}`/etc/u2f-mappings`) you can set this + option. + + File format is: + `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key` + This file can be generated using {command}`pamu2fcfg` command. + + More information can be found [here](https://developers.yubico.com/pam-u2f/). + ''; + }; + + appId = mkOption { + default = null; + type = with types; nullOr str; + description = lib.mdDoc '' + By default `pam-u2f` module sets the application + ID to `pam://$HOSTNAME`. + + When using {command}`pamu2fcfg`, you can specify your + application ID with the `-i` flag. + + More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html) + ''; + }; + + origin = mkOption { + default = null; + type = with types; nullOr str; + description = lib.mdDoc '' + By default `pam-u2f` module sets the origin + to `pam://$HOSTNAME`. + Setting origin to an host independent value will allow you to + reuse credentials across machines + + When using {command}`pamu2fcfg`, you can specify your + application ID with the `-o` flag. + + More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html) + ''; + }; + + control = mkOption { + default = "sufficient"; + type = types.enum ["required" "requisite" "sufficient" "optional"]; + description = lib.mdDoc '' + This option sets pam "control". + If you want to have multi factor authentication, use "required". + If you want to use U2F device instead of regular password, use "sufficient". + + Read + {manpage}`pam.conf(5)` + for better understanding of this option. + ''; + }; + + debug = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Debug output to stderr. + ''; + }; + + interactive = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Set to prompt a message and wait before testing the presence of a U2F device. + Recommended if your device doesn’t have a tactile trigger. + ''; + }; + + cue = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + By default `pam-u2f` module does not inform user + that he needs to use the u2f device, it just waits without a prompt. + + If you set this option to `true`, + `cue` option is added to `pam-u2f` + module and reminder message will be displayed. + ''; + }; + }; + + security.pam.ussh = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Enables Uber's USSH PAM (`pam-ussh`) module. + + This is similar to `pam-ssh-agent`, except that + the presence of a CA-signed SSH key with a valid principal is checked + instead. + + Note that this module must both be enabled using this option and on a + per-PAM-service level as well (using `usshAuth`). + + More information can be found [here](https://github.com/uber/pam-ussh). + ''; + }; + + caFile = mkOption { + default = null; + type = with types; nullOr path; + description = lib.mdDoc '' + By default `pam-ussh` reads the trusted user CA keys + from {file}`/etc/ssh/trusted_user_ca`. + + This should be set the same as your `TrustedUserCAKeys` + option for sshd. + ''; + }; + + authorizedPrincipals = mkOption { + default = null; + type = with types; nullOr commas; + description = lib.mdDoc '' + Comma-separated list of authorized principals to permit; if the user + presents a certificate with one of these principals, then they will be + authorized. + + Note that `pam-ussh` also requires that the certificate + contain a principal matching the user's username. The principals from + this list are in addition to those principals. + + Mutually exclusive with `authorizedPrincipalsFile`. + ''; + }; + + authorizedPrincipalsFile = mkOption { + default = null; + type = with types; nullOr path; + description = lib.mdDoc '' + Path to a list of principals; if the user presents a certificate with + one of these principals, then they will be authorized. + + Note that `pam-ussh` also requires that the certificate + contain a principal matching the user's username. The principals from + this file are in addition to those principals. + + Mutually exclusive with `authorizedPrincipals`. + ''; + }; + + group = mkOption { + default = null; + type = with types; nullOr str; + description = lib.mdDoc '' + If set, then the authenticating user must be a member of this group + to use this module. + ''; + }; + + control = mkOption { + default = "sufficient"; + type = types.enum ["required" "requisite" "sufficient" "optional"]; + description = lib.mdDoc '' + This option sets pam "control". + If you want to have multi factor authentication, use "required". + If you want to use the SSH certificate instead of the regular password, + use "sufficient". + + Read + {manpage}`pam.conf(5)` + for better understanding of this option. + ''; + }; + }; + + security.pam.yubico = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Enables Yubico PAM (`yubico-pam`) module. + + If set, users listed in + {file}`~/.yubico/authorized_yubikeys` + are able to log in with the associated Yubikey tokens. + + The file must have only one line: + `username:yubikey_token_id1:yubikey_token_id2` + More information can be found [here](https://developers.yubico.com/yubico-pam/). + ''; + }; + control = mkOption { + default = "sufficient"; + type = types.enum ["required" "requisite" "sufficient" "optional"]; + description = lib.mdDoc '' + This option sets pam "control". + If you want to have multi factor authentication, use "required". + If you want to use Yubikey instead of regular password, use "sufficient". + + Read + {manpage}`pam.conf(5)` + for better understanding of this option. + ''; + }; + id = mkOption { + example = "42"; + type = types.str; + description = lib.mdDoc "client id"; + }; + + debug = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Debug output to stderr. + ''; + }; + mode = mkOption { + default = "client"; + type = types.enum ["client" "challenge-response"]; + description = lib.mdDoc '' + Mode of operation. + + Use "client" for online validation with a YubiKey validation service such as + the YubiCloud. + + Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1 + Challenge-Response configurations. See the man-page ykpamcfg(1) for further + details on how to configure offline Challenge-Response validation. + + More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html). + ''; + }; + challengeResponsePath = mkOption { + default = null; + type = types.nullOr types.path; + description = lib.mdDoc '' + If not null, set the path used by yubico pam module where the challenge expected response is stored. + + More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html). + ''; + }; + }; + + security.pam.enableEcryptfs = mkEnableOption (lib.mdDoc "eCryptfs PAM module (mounting ecryptfs home directory on login)"); + security.pam.enableFscrypt = mkEnableOption (lib.mdDoc '' + Enables fscrypt to automatically unlock directories with the user's login password. + + This also enables a service at security.pam.services.fscrypt which is used by + fscrypt to verify the user's password when setting up a new protector. If you + use something other than pam_unix to verify user passwords, please remember to + adjust this PAM service. + ''); + + users.motd = mkOption { + default = null; + example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178."; + type = types.nullOr types.lines; + description = lib.mdDoc "Message of the day shown to users when they log in."; + }; + + users.motdFile = mkOption { + default = null; + example = "/etc/motd"; + type = types.nullOr types.path; + description = lib.mdDoc "A file containing the message of the day shown to users when they log in."; + }; + }; + + ###### implementation + + config = { + assertions = [ + { + assertion = isNull config.users.motd || isNull config.users.motdFile; + message = '' + Only one of users.motd and users.motdFile can be set. + ''; + } + ]; + + environment.systemPackages = + # Include the PAM modules in the system path mostly for the manpages. + [pkgs.pam] + ++ optional config.users.ldap.enable pam_ldap + ++ optional config.services.sssd.enable pkgs.sssd + ++ optionals config.security.pam.krb5.enable [pam_krb5 pam_ccreds] + ++ optionals config.security.pam.enableOTPW [pkgs.otpw] + ++ optionals config.security.pam.oath.enable [pkgs.oath-toolkit] + ++ optionals config.security.pam.p11.enable [pkgs.pam_p11] + ++ optionals config.security.pam.enableFscrypt [pkgs.fscrypt-experimental] + ++ optionals config.security.pam.u2f.enable [pkgs.pam_u2f]; + + boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs ["ecryptfs"]; + + security.wrappers = { + unix_chkpwd = { + setuid = true; + owner = "root"; + group = "root"; + source = "${pkgs.pam}/bin/unix_chkpwd"; + }; + }; + + environment.etc = mapAttrs' makePAMService config.security.pam.services; + + security.pam.services = + { + other.text = '' + auth required pam_warn.so + auth required pam_deny.so + account required pam_warn.so + account required pam_deny.so + password required pam_warn.so + password required pam_deny.so + session required pam_warn.so + session required pam_deny.so + ''; + + # Most of these should be moved to specific modules. + i3lock = {}; + i3lock-color = {}; + vlock = {}; + xlock = {}; + xscreensaver = {}; + + runuser = { + rootOK = true; + unixAuth = false; + setEnvironment = false; + }; + + /* + FIXME: should runuser -l start a systemd session? Currently + it complains "Cannot create session: Already running in a + session". + */ + runuser-l = { + rootOK = true; + unixAuth = false; + }; + } + // optionalAttrs (config.security.pam.enableFscrypt) { + # Allow fscrypt to verify login passphrase + fscrypt = {}; + }; + + security.apparmor.includes."abstractions/pam" = let + isEnabled = test: fold or false (map test (attrValues config.security.pam.services)); + in + lib.concatMapStrings + (name: "r ${config.environment.etc."pam.d/${name}".source},\n") + (attrNames config.security.pam.services) + + '' + mr ${getLib pkgs.pam}/lib/security/pam_filter/*, + mr ${getLib pkgs.pam}/lib/security/pam_*.so, + r ${getLib pkgs.pam}/lib/security/, + '' + + optionalString use_ldap '' + mr ${pam_ldap}/lib/security/pam_ldap.so, + '' + + optionalString config.services.sssd.enable '' + mr ${pkgs.sssd}/lib/security/pam_sss.so, + '' + + optionalString config.security.pam.krb5.enable '' + mr ${pam_krb5}/lib/security/pam_krb5.so, + mr ${pam_ccreds}/lib/security/pam_ccreds.so, + '' + + optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) '' + mr ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so, + mr ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so, + '' + + optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication)) '' + mr ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so, + '' + + optionalString (config.security.pam.enableSSHAgentAuth + && isEnabled (cfg: cfg.sshAgentAuth)) '' + mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so, + '' + + optionalString (isEnabled (cfg: cfg.fprintAuth)) '' + mr ${pkgs.fprintd}/lib/security/pam_fprintd.so, + '' + + optionalString (isEnabled (cfg: cfg.howdyAuth)) '' + mr ${config.services.howdy.package}/lib/security/pam_howdy.so, + '' + + optionalString (isEnabled (cfg: cfg.u2fAuth)) '' + mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so, + '' + + optionalString (isEnabled (cfg: cfg.usbAuth)) '' + mr ${pkgs.pam_usb}/lib/security/pam_usb.so, + '' + + optionalString (isEnabled (cfg: cfg.usshAuth)) '' + mr ${pkgs.pam_ussh}/lib/security/pam_ussh.so, + '' + + optionalString (isEnabled (cfg: cfg.oathAuth)) '' + "mr ${pkgs.oath-toolkit}/lib/security/pam_oath.so, + '' + + optionalString (isEnabled (cfg: cfg.mysqlAuth)) '' + mr ${pkgs.pam_mysql}/lib/security/pam_mysql.so, + '' + + optionalString (isEnabled (cfg: cfg.yubicoAuth)) '' + mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so, + '' + + optionalString (isEnabled (cfg: cfg.duoSecurity.enable)) '' + mr ${pkgs.duo-unix}/lib/security/pam_duo.so, + '' + + optionalString (isEnabled (cfg: cfg.otpwAuth)) '' + mr ${pkgs.otpw}/lib/security/pam_otpw.so, + '' + + optionalString config.security.pam.enableEcryptfs '' + mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so, + '' + + optionalString config.security.pam.enableFscrypt '' + mr ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so, + '' + + optionalString (isEnabled (cfg: cfg.pamMount)) '' + mr ${pkgs.pam_mount}/lib/security/pam_mount.so, + '' + + optionalString (isEnabled (cfg: cfg.enableGnomeKeyring)) '' + mr ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so, + '' + + optionalString (isEnabled (cfg: cfg.startSession)) '' + mr ${config.systemd.package}/lib/security/pam_systemd.so, + '' + + optionalString (isEnabled (cfg: cfg.enableAppArmor) + && config.security.apparmor.enable) '' + mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so, + '' + + optionalString (isEnabled (cfg: cfg.enableKwallet)) '' + mr ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so, + '' + + optionalString config.virtualisation.lxc.lxcfs.enable '' + mr ${pkgs.lxc}/lib/security/pam_cgfs.so + '' + + optionalString config.services.homed.enable '' + mr ${config.systemd.package}/lib/security/pam_systemd_home.so + ''; + }; +} diff --git a/foreign/dotfiles/modules/regreet.nix b/foreign/dotfiles/modules/regreet.nix new file mode 100644 index 0000000..494f0e6 --- /dev/null +++ b/foreign/dotfiles/modules/regreet.nix @@ -0,0 +1,73 @@ +{ + lib, + pkgs, + config, + ... +}: let + cfg = config.programs.regreet; + settingsFormat = pkgs.formats.toml {}; +in { + options.programs.regreet = { + enable = + lib.mkEnableOption null + // { + description = lib.mdDoc '' + Enable ReGreet, a clean and customizable greeter for greetd. + + To use ReGreet, {option}`services.greetd` has to be enabled and + {option}`services.greetd.settings.default_session` should contain the + appropriate configuration to launch + {option}`config.programs.regreet.package`. For examples, see the + [ReGreet Readme](https://github.com/rharish101/ReGreet#set-as-default-session). + ''; + }; + + package = lib.mkPackageOptionMD pkgs ["greetd" "regreet"] {}; + + settings = lib.mkOption { + type = lib.types.either lib.types.path settingsFormat.type; + default = {}; + description = lib.mdDoc '' + ReGreet configuration file. Refer + + for options. + ''; + }; + + extraCss = lib.mkOption { + type = lib.types.either lib.types.path lib.types.lines; + default = ""; + description = lib.mdDoc '' + Extra CSS rules to apply on top of the GTK theme. Refer to + [GTK CSS Properties](https://docs.gtk.org/gtk4/css-properties.html) for + modifiable properties. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + services.greetd = { + enable = lib.mkDefault true; + settings.default_session.command = lib.mkDefault "${lib.getExe pkgs.cage} -s -- ${lib.getExe cfg.package}"; + }; + + environment.etc = { + "greetd/regreet.css" = + if lib.isPath cfg.extraCss + then {source = cfg.extraCss;} + else {text = cfg.extraCss;}; + + "greetd/regreet.toml".source = + if lib.isPath cfg.settings + then cfg.settings + else settingsFormat.generate "regreet.toml" cfg.settings; + }; + + systemd.tmpfiles.rules = let + user = config.services.greetd.settings.default_session.user; + in [ + "d /var/log/regreet 0755 greeter ${user} - -" + "d /var/cache/regreet 0755 greeter ${user} - -" + ]; + }; +} diff --git a/foreign/dotfiles/modules/security.nix b/foreign/dotfiles/modules/security.nix new file mode 100644 index 0000000..3bc2d1c --- /dev/null +++ b/foreign/dotfiles/modules/security.nix @@ -0,0 +1,50 @@ +# security tweaks borrowed from @hlissner +{ + boot.kernel.sysctl = { + # The Magic SysRq key is a key combo that allows users connected to the + # system console of a Linux kernel to perform some low-level commands. + # Disable it, since we don't need it, and is a potential security concern. + "kernel.sysrq" = 0; + + ## TCP hardening + # Prevent bogus ICMP errors from filling up logs. + "net.ipv4.icmp_ignore_bogus_error_responses" = 1; + # Reverse path filtering causes the kernel to do source validation of + # packets received from all interfaces. This can mitigate IP spoofing. + "net.ipv4.conf.default.rp_filter" = 1; + "net.ipv4.conf.all.rp_filter" = 1; + # Do not accept IP source route packets (we're not a router) + "net.ipv4.conf.all.accept_source_route" = 0; + "net.ipv6.conf.all.accept_source_route" = 0; + # Don't send ICMP redirects (again, we're on a router) + "net.ipv4.conf.all.send_redirects" = 0; + "net.ipv4.conf.default.send_redirects" = 0; + # Refuse ICMP redirects (MITM mitigations) + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv4.conf.default.accept_redirects" = 0; + "net.ipv4.conf.all.secure_redirects" = 0; + "net.ipv4.conf.default.secure_redirects" = 0; + "net.ipv6.conf.all.accept_redirects" = 0; + "net.ipv6.conf.default.accept_redirects" = 0; + # Protects against SYN flood attacks + "net.ipv4.tcp_syncookies" = 1; + # Incomplete protection again TIME-WAIT assassination + "net.ipv4.tcp_rfc1337" = 1; + + ## TCP optimization + # TCP Fast Open is a TCP extension that reduces network latency by packing + # data in the sender’s initial TCP SYN. Setting 3 = enable TCP Fast Open for + # both incoming and outgoing connections: + "net.ipv4.tcp_fastopen" = 3; + # Bufferbloat mitigations + slight improvement in throughput & latency + "net.ipv4.tcp_congestion_control" = "bbr"; + "net.core.default_qdisc" = "cake"; + }; + boot.kernelModules = ["tcp_bbr"]; + + # So we don't have to do this later... + security.acme = { + acceptTerms = true; + defaults.email = "fufexan@proton.me"; + }; +} diff --git a/foreign/dotfiles/modules/xserver.nix b/foreign/dotfiles/modules/xserver.nix new file mode 100644 index 0000000..3c95f05 --- /dev/null +++ b/foreign/dotfiles/modules/xserver.nix @@ -0,0 +1,16 @@ +{ + services.xserver = { + enable = true; + displayManager.gdm.enable = true; + + libinput = { + enable = true; + # disable mouse acceleration + mouse.accelProfile = "flat"; + mouse.accelSpeed = "0"; + mouse.middleEmulation = false; + # touchpad settings + touchpad.naturalScrolling = true; + }; + }; +} diff --git a/foreign/dotfiles/pkgs/README.md b/foreign/dotfiles/pkgs/README.md new file mode 100644 index 0000000..8de390f --- /dev/null +++ b/foreign/dotfiles/pkgs/README.md @@ -0,0 +1,17 @@ +# Packages & Overlays + +Here are all of the packages I couldn't find anywhere and packaged by myself, +or overrides that I use throughout the configuration. + +Name | Description +---- | ----------- +DiscordCanary | DiscordCanary + OpenASAR +[GDB-Frontend](https://github.com/rohanrhu/gdb-frontend) | Easy, flexible and extensible GUI debugger +iso | My system config built as an ISO +`patches` | Various patches used in my system +Repl | Cool Nix Repl that auto-loads the system flake or the current dir flake +[SpotifyWM](https://github.com/dasJ/spotifywm) | Spotify preloader that sets WM_NAME on start +Spotify Wrapped WM | Spotify wrapped with SpotifyWM +[Sway-hidpi](https://github.com/swaywm/sway) | Sway with XWayland HiDPI patches +[Technic](https://www.technicpack.net) | Technic Launcher derivation +[Waveform](https://www.tracktion.com/welcome/waveform-free) | DAW from Tracktion that works on Linux diff --git a/foreign/dotfiles/pkgs/catppuccin-plymouth/default.nix b/foreign/dotfiles/pkgs/catppuccin-plymouth/default.nix new file mode 100644 index 0000000..22517dd --- /dev/null +++ b/foreign/dotfiles/pkgs/catppuccin-plymouth/default.nix @@ -0,0 +1,36 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, +}: +stdenvNoCC.mkDerivation { + pname = "catppuccin-plymouth"; + version = "unstable-2022-12-10"; + + src = fetchFromGitHub { + owner = "catppuccin"; + repo = "plymouth"; + rev = "d4105cf336599653783c34c4a2d6ca8c93f9281c"; + hash = "sha256-quBSH8hx3gD7y1JNWAKQdTk3CmO4t1kVo4cOGbeWlNE="; + }; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out/share/plymouth + cp -r themes $out/share/plymouth/ + + runHook postInstall + ''; + + meta = { + description = "Soothing pastel theme for Plymouth"; + homepage = "https://github.com/catppuccin/plymouth"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [fufexan]; + platforms = lib.platforms.linux; + }; +} diff --git a/foreign/dotfiles/pkgs/default.nix b/foreign/dotfiles/pkgs/default.nix new file mode 100644 index 0000000..c3904b9 --- /dev/null +++ b/foreign/dotfiles/pkgs/default.nix @@ -0,0 +1,13 @@ +{ + _inputs, + self, + ... +}: { + systems = ["x86_64-linux"]; + + flake.overlays.default = import ./overlays.nix _inputs; + + perSystem = {pkgs, ...}: { + packages = self.overlays.default null pkgs; + }; +} diff --git a/foreign/dotfiles/pkgs/discord.nix b/foreign/dotfiles/pkgs/discord.nix new file mode 100644 index 0000000..68d3e56 --- /dev/null +++ b/foreign/dotfiles/pkgs/discord.nix @@ -0,0 +1,59 @@ +{ + lib, + pkgs, + inputs, + ... +}: +with pkgs; let + binaryName = "DiscordCanary"; + + disableBreakingUpdates = + runCommand "disable-breaking-updates.py" + { + pythonInterpreter = "${python3.interpreter}"; + configDirName = lib.toLower binaryName; + } '' + mkdir -p $out/bin + cp "${inputs.nixpkgs}/pkgs/applications/networking/instant-messengers/discord/disable-breaking-updates.py" $out/bin/disable-breaking-updates.py + substituteAllInPlace $out/bin/disable-breaking-updates.py + chmod +x $out/bin/disable-breaking-updates.py + ''; +in + (discord-canary.override { + nss = pkgs.nss_latest; + withOpenASAR = true; + }) + .overrideAttrs (old: rec { + libPath = old.libPath + ":${libglvnd}/lib"; + + installPhase = '' + runHook preInstall + + mkdir -p $out/{bin,opt/${binaryName},share/pixmaps,share/icons/hicolor/256x256/apps} + mv * $out/opt/${binaryName} + + chmod +x $out/opt/${binaryName}/${binaryName} + patchelf --set-interpreter ${stdenv.cc.bintools.dynamicLinker} \ + $out/opt/${binaryName}/${binaryName} + + wrapProgramShell $out/opt/${binaryName}/${binaryName} \ + "''${gappsWrapperArgs[@]}" \ + --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform=wayland --enable-features=WaylandWindowDecorations}}" \ + --prefix XDG_DATA_DIRS : "${gtk3}/share/gsettings-schemas/${gtk3.name}/" \ + --prefix LD_LIBRARY_PATH : ${libPath}:$out/opt/${binaryName} \ + --run "${lib.getExe disableBreakingUpdates}" + + ln -s $out/opt/${binaryName}/${binaryName} $out/bin/ + # Without || true the install would fail on case-insensitive filesystems + ln -s $out/opt/${binaryName}/${binaryName} $out/bin/${ + lib.strings.toLower binaryName + } || true + + ln -s $out/opt/${binaryName}/discord.png $out/share/pixmaps/${old.pname}.png + ln -s $out/opt/${binaryName}/discord.png $out/share/icons/hicolor/256x256/apps/${old.pname}.png + + ln -s "${old.desktopItem}/share/applications" $out/share/ + + runHook postInstall + ''; + }) diff --git a/foreign/dotfiles/pkgs/gdb-frontend/default.nix b/foreign/dotfiles/pkgs/gdb-frontend/default.nix new file mode 100644 index 0000000..49a4bb0 --- /dev/null +++ b/foreign/dotfiles/pkgs/gdb-frontend/default.nix @@ -0,0 +1,54 @@ +{ + lib, + bash, + stdenv, + fetchFromGitHub, + python3, + gdb, + tmux, +}: +stdenv.mkDerivation rec { + pname = "gdb-frontend"; + version = "0.10.3-beta"; + + src = fetchFromGitHub { + repo = pname; + owner = "rohanrhu"; + rev = "v${version}"; + sha256 = "sha256-+l1SguRKnLDqT4rgXcod9xhiCOJZEKdZlOZfTNzP7fk="; + }; + + buildInputs = [gdb python3 tmux]; + propagatedUserEnvPkgs = [gdb python3 tmux]; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + mkdir -p $out/bin + cp -r . $out + echo "${python3}/bin/python $out/run.py \"\$@\"" > $out/bin/gdbfrontend + chmod +x $out/bin/gdbfrontend + ''; + + postPatch = '' + substituteInPlace "run.py" \ + --replace "/bin/bash" "${bash}/bin/bash" + substituteInPlace "gdbfrontend-window" \ + --replace "/bin/bash" "${bash}/bin/bash" + substituteInPlace "build_gdb.sh" \ + --replace "/bin/bash\n" "${bash}/bin/bash\n" + substituteInPlace "url_modules/api/shell.py" \ + --replace "/bin/bash" "${bash}/bin/bash" + substituteInPlace "commands/gdbfrontend" \ + --replace "/bin/bash" "${bash}/bin/bash" + ''; + + meta = with lib; { + description = "GDBFrontend is an easy, flexible and extensionable gui debugger"; + homepage = "https://github.com/rohanrhu/gdb-frontend"; + mainProgram = "gdbfrontend"; + platforms = platforms.linux; + maintainers = with maintainers; [fufexan]; + }; +} diff --git a/foreign/dotfiles/pkgs/howdy/default.nix b/foreign/dotfiles/pkgs/howdy/default.nix new file mode 100644 index 0000000..62ab8a0 --- /dev/null +++ b/foreign/dotfiles/pkgs/howdy/default.nix @@ -0,0 +1,86 @@ +{ + stdenv, + lib, + bzip2, + fetchFromGitHub, + fetchurl, + fmt, + gettext, + inih, + installShellFiles, + libevdev, + meson, + ninja, + pam, + pkg-config, + python3, +} @ args: let + data = import ./sources.nix args; +in + stdenv.mkDerivation { + pname = "howdy"; + version = "unstable-2023-02-28"; + inherit (data) src; + + # fix paths + patches = [./howdy.patch]; + + postPatch = let + howdypath = "${placeholder "out"}/lib/security/howdy"; + in '' + substituteInPlace howdy/src/cli/add.py --replace "@PATH@" "${howdypath}" + substituteInPlace howdy/src/cli/config.py --replace '/bin/nano' 'nano' + substituteInPlace howdy/src/cli/test.py --replace "@PATH@" "${howdypath}" + + substituteInPlace howdy/src/pam/main.cc \ + --replace "python3" "${data.py}/bin/python" \ + --replace "/lib/security/howdy/compare.py" "${howdypath}/compare.py" + + substituteInPlace howdy/src/compare.py \ + --replace "/lib/security/howdy" "${howdypath}" \ + --replace "@PATH@" "${howdypath}" + ''; + + nativeBuildInputs = [bzip2 installShellFiles meson ninja pkg-config]; + buildInputs = [data.py fmt gettext inih libevdev pam]; + + # build howdy_pam + preConfigure = '' + cd howdy/src/pam + export DESTDIR=$out + ''; + + postInstall = let + libDir = "$out/lib/security/howdy"; + inherit (lib) mapAttrsToList concatStrings; + in '' + # done with howdy_pam, go back to source root + cd ../../../.. + + mkdir -p $out/share/licenses/howdy + install -Dm644 LICENSE $out/share/licenses/howdy/LICENSE + rm -rf howdy/src/pam + mkdir -p ${libDir} + cp -r howdy/src/* ${libDir} + + rm -rf ${libDir}/pam-config ${libDir}/dlib-data/* + ${concatStrings (mapAttrsToList (n: v: '' + bzip2 -dc ${v} > ${libDir}/dlib-data/${n} + '') + data.data)} + + mkdir -p $out/bin + ln -s ${libDir}/cli.py $out/bin/howdy + + mkdir -p "$out/share/bash-completion/completions" + installShellCompletion --bash howdy/src/autocomplete/howdy + ''; + + meta = { + description = "Windows Hello™ style facial authentication for Linux"; + homepage = "https://github.com/boltgolt/howdy"; + license = lib.licenses.mit; + platforms = lib.platforms.linux; + maintainers = with lib.maintainers; [fufexan]; + }; + } diff --git a/foreign/dotfiles/pkgs/howdy/howdy.patch b/foreign/dotfiles/pkgs/howdy/howdy.patch new file mode 100644 index 0000000..34a481e --- /dev/null +++ b/foreign/dotfiles/pkgs/howdy/howdy.patch @@ -0,0 +1,155 @@ +diff --git a/howdy/src/cli/add.py b/howdy/src/cli/add.py +index 8951e31..4f793d7 100644 +--- a/howdy/src/cli/add.py ++++ b/howdy/src/cli/add.py +@@ -30,9 +30,9 @@ import cv2 + config_path = "/etc/howdy" + + # Test if at lest 1 of the data files is there and abort if it's not +-if not os.path.isfile(config_path + "/dlib-data/shape_predictor_5_face_landmarks.dat"): ++if not os.path.isfile("@PATH@/dlib-data/shape_predictor_5_face_landmarks.dat"): + print(_("Data files have not been downloaded, please run the following commands:")) +- print("\n\tcd " + config_path + "/dlib-data") ++ print("\n\tcd " + "@PATH@/dlib-data") + print("\tsudo ./install.sh\n") + sys.exit(1) + +@@ -42,23 +42,23 @@ config.read(config_path + "/config.ini") + + use_cnn = config.getboolean("core", "use_cnn", fallback=False) + if use_cnn: +- face_detector = dlib.cnn_face_detection_model_v1(config_path + "/dlib-data/mmod_human_face_detector.dat") ++ face_detector = dlib.cnn_face_detection_model_v1("@PATH@/dlib-data/mmod_human_face_detector.dat") + else: + face_detector = dlib.get_frontal_face_detector() + +-pose_predictor = dlib.shape_predictor(config_path + "/dlib-data/shape_predictor_5_face_landmarks.dat") +-face_encoder = dlib.face_recognition_model_v1(config_path + "/dlib-data/dlib_face_recognition_resnet_model_v1.dat") ++pose_predictor = dlib.shape_predictor("@PATH@/dlib-data/shape_predictor_5_face_landmarks.dat") ++face_encoder = dlib.face_recognition_model_v1("@PATH@/dlib-data/dlib_face_recognition_resnet_model_v1.dat") + + user = builtins.howdy_user + # The permanent file to store the encoded model in +-enc_file = config_path + "/models/" + user + ".dat" ++enc_file = "/var/lib/howdy/models/" + user + ".dat" + # Known encodings + encodings = [] + + # Make the ./models folder if it doesn't already exist +-if not os.path.exists(config_path + "/models"): ++if not os.path.exists("/var/lib/howdy/models"): + print(_("No face model folder found, creating one")) +- os.makedirs(config_path + "/models") ++ os.makedirs("/var/lib/howdy/models") + + # To try read a premade encodings file if it exists + try: +diff --git a/howdy/src/cli/clear.py b/howdy/src/cli/clear.py +index 6fa5f3e..fc7676c 100644 +--- a/howdy/src/cli/clear.py ++++ b/howdy/src/cli/clear.py +@@ -8,7 +8,7 @@ import builtins + from i18n import _ + + # Get the full path to this file +-path = "/etc/howdy/models" ++path = "/var/lib/howdy/models" + # Get the passed user + user = builtins.howdy_user + +diff --git a/howdy/src/cli/list.py b/howdy/src/cli/list.py +index 3532e9f..b9e2a31 100644 +--- a/howdy/src/cli/list.py ++++ b/howdy/src/cli/list.py +@@ -10,7 +10,7 @@ import builtins + from i18n import _ + + # Get the absolute path and the username +-path = "/etc/howdy" ++path = "/var/lib/howdy" + user = builtins.howdy_user + + # Check if the models file has been created yet +diff --git a/howdy/src/cli/remove.py b/howdy/src/cli/remove.py +index 6321e0b..7c13d79 100644 +--- a/howdy/src/cli/remove.py ++++ b/howdy/src/cli/remove.py +@@ -9,7 +9,7 @@ import builtins + from i18n import _ + + # Get the absolute path and the username +-path = "/etc/howdy" ++path = "/var/lib/howdy" + user = builtins.howdy_user + + # Check if enough arguments have been passed +diff --git a/howdy/src/cli/test.py b/howdy/src/cli/test.py +index d54929a..fa45500 100644 +--- a/howdy/src/cli/test.py ++++ b/howdy/src/cli/test.py +@@ -59,20 +59,20 @@ use_cnn = config.getboolean('core', 'use_cnn', fallback=False) + + if use_cnn: + face_detector = dlib.cnn_face_detection_model_v1( +- path + "/dlib-data/mmod_human_face_detector.dat" ++ "@PATH@/dlib-data/mmod_human_face_detector.dat" + ) + else: + face_detector = dlib.get_frontal_face_detector() + +-pose_predictor = dlib.shape_predictor(path + "/dlib-data/shape_predictor_5_face_landmarks.dat") +-face_encoder = dlib.face_recognition_model_v1(path + "/dlib-data/dlib_face_recognition_resnet_model_v1.dat") ++pose_predictor = dlib.shape_predictor("@PATH@/dlib-data/shape_predictor_5_face_landmarks.dat") ++face_encoder = dlib.face_recognition_model_v1("@PATH@/dlib-data/dlib_face_recognition_resnet_model_v1.dat") + + encodings = [] + models = None + + try: + user = builtins.howdy_user +- models = json.load(open(path + "/models/" + user + ".dat")) ++ models = json.load(open("/var/lib/howdy/models/" + user + ".dat")) + + for model in models: + encodings += model["data"] +diff --git a/howdy/src/compare.py b/howdy/src/compare.py +index be19464..86a8d8f 100644 +--- a/howdy/src/compare.py ++++ b/howdy/src/compare.py +@@ -48,22 +48,22 @@ def init_detector(lock): + global face_detector, pose_predictor, face_encoder + + # Test if at lest 1 of the data files is there and abort if it's not +- if not os.path.isfile(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat"): ++ if not os.path.isfile("@PATH@/dlib-data/shape_predictor_5_face_landmarks.dat"): + print(_("Data files have not been downloaded, please run the following commands:")) +- print("\n\tcd " + PATH + "/dlib-data") ++ print("\n\tcd " + "@PATH@/dlib-data") + print("\tsudo ./install.sh\n") + lock.release() + exit(1) + + # Use the CNN detector if enabled + if use_cnn: +- face_detector = dlib.cnn_face_detection_model_v1(PATH + "/dlib-data/mmod_human_face_detector.dat") ++ face_detector = dlib.cnn_face_detection_model_v1("@PATH@/dlib-data/mmod_human_face_detector.dat") + else: + face_detector = dlib.get_frontal_face_detector() + + # Start the others regardless +- pose_predictor = dlib.shape_predictor(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat") +- face_encoder = dlib.face_recognition_model_v1(PATH + "/dlib-data/dlib_face_recognition_resnet_model_v1.dat") ++ pose_predictor = dlib.shape_predictor("@PATH@/dlib-data/shape_predictor_5_face_landmarks.dat") ++ face_encoder = dlib.face_recognition_model_v1("@PATH@/dlib-data/dlib_face_recognition_resnet_model_v1.dat") + + # Note the time it took to initialize detectors + timings["ll"] = time.time() - timings["ll"] +@@ -129,7 +129,7 @@ face_encoder = None + + # Try to load the face model from the models folder + try: +- models = json.load(open(PATH + "/models/" + user + ".dat")) ++ models = json.load(open("/var/lib/howdy/models/" + user + ".dat")) + + for model in models: + encodings += model["data"] diff --git a/foreign/dotfiles/pkgs/howdy/sources.nix b/foreign/dotfiles/pkgs/howdy/sources.nix new file mode 100644 index 0000000..6d3d19a --- /dev/null +++ b/foreign/dotfiles/pkgs/howdy/sources.nix @@ -0,0 +1,33 @@ +{ + fetchurl, + fetchFromGitHub, + python3, + ... +}: { + data = { + "dlib_face_recognition_resnet_model_v1.dat" = fetchurl { + url = "https://github.com/davisking/dlib-models/raw/master/dlib_face_recognition_resnet_model_v1.dat.bz2"; + sha256 = "0fjm265l1fz5zdzx5n5yphl0v0vfajyw50ffamc4cd74848gdcdb"; + }; + "mmod_human_face_detector.dat" = fetchurl { + url = "https://github.com/davisking/dlib-models/raw/master/mmod_human_face_detector.dat.bz2"; + sha256 = "117wv582nsn585am2n9mg5q830qnn8skjr1yxgaiihcjy109x7nv"; + }; + "shape_predictor_5_face_landmarks.dat" = fetchurl { + url = "https://github.com/davisking/dlib-models/raw/master/shape_predictor_5_face_landmarks.dat.bz2"; + sha256 = "0wm4bbwnja7ik7r28pv00qrl3i1h6811zkgnjfvzv7jwpyz7ny3f"; + }; + }; + + src = fetchFromGitHub { + owner = "fufexan"; + repo = "howdy"; + rev = "dceebf0f194e55429baaf9696ed5ae38ab9ddf92"; + hash = "sha256-6C8AqB83pJ5LMq+0JdsrMfkGNxxn1uv8jfoCe8BAGeY="; + }; + + py = python3.withPackages (p: [ + p.face_recognition + (p.opencv4.override {enableGtk3 = true;}) + ]); +} diff --git a/foreign/dotfiles/pkgs/linux-enable-ir-emitter/default.nix b/foreign/dotfiles/pkgs/linux-enable-ir-emitter/default.nix new file mode 100644 index 0000000..ef5d52c --- /dev/null +++ b/foreign/dotfiles/pkgs/linux-enable-ir-emitter/default.nix @@ -0,0 +1,33 @@ +{ + stdenv, + lib, + makeWrapper, + fetchFromGitHub, + fetchurl, + meson, + ninja, + pkg-config, + python3, + opencv, + usbutils, +}: +stdenv.mkDerivation rec { + pname = "linux-enable-ir-emitter"; + version = "4.5.0"; + + src = fetchFromGitHub { + owner = "EmixamPP"; + repo = pname; + rev = version; + hash = "sha256-Dv1ukn2TkXfBk1vc+6Uq7tw8WwCAfIcKl13BoOifz+Q="; + }; + + nativeBuildInputs = [meson ninja pkg-config makeWrapper]; + buildInputs = [python3 opencv]; + + patches = [./remove-boot-set.patch]; + + postInstall = '' + wrapProgram $out/bin/${pname} --prefix PATH : ${lib.makeBinPath [usbutils]} + ''; +} diff --git a/foreign/dotfiles/pkgs/linux-enable-ir-emitter/remove-boot-set.patch b/foreign/dotfiles/pkgs/linux-enable-ir-emitter/remove-boot-set.patch new file mode 100644 index 0000000..9cd26f4 --- /dev/null +++ b/foreign/dotfiles/pkgs/linux-enable-ir-emitter/remove-boot-set.patch @@ -0,0 +1,11 @@ +diff --git a/sources/command/configure.py b/sources/command/configure.py +index 2cd20d2..84ffb73 100644 +--- a/sources/command/configure.py ++++ b/sources/command/configure.py +@@ -25,6 +25,5 @@ def configure(device: str, emitters: int, neg_answer_limit: int) -> NoReturn: + logging.info("Do not hesitate to visit the GitHub ! https://github.com/EmixamPP/linux-enable-ir-emitter/wiki") + else: + logging.info("The driver has been successfully generated.") +- boot("enable") + + exit(exit_code) diff --git a/foreign/dotfiles/pkgs/overlays.nix b/foreign/dotfiles/pkgs/overlays.nix new file mode 100644 index 0000000..51beb84 --- /dev/null +++ b/foreign/dotfiles/pkgs/overlays.nix @@ -0,0 +1,26 @@ +inputs: _: prev: { + # instant repl with automatic flake loading + repl = prev.callPackage ./repl {}; + + catppuccin-plymouth = prev.callPackage ./catppuccin-plymouth {}; + + discord-canary = prev.callPackage ./discord.nix { + pkgs = prev; + inherit inputs; + inherit (prev) lib; + }; + + gdb-frontend = prev.callPackage ./gdb-frontend {}; + + regreet = prev.callPackage ./regreet {}; + + howdy = prev.callPackage ./howdy {}; + + linux-enable-ir-emitter = prev.callPackage ./linux-enable-ir-emitter {}; + + waveform = prev.callPackage ./waveform {}; + + spotify = prev.callPackage ./spotify {}; + + sway-hidpi = import ./sway-hidpi.nix prev; +} diff --git a/foreign/dotfiles/pkgs/patches/xwayland-hidpi.patch b/foreign/dotfiles/pkgs/patches/xwayland-hidpi.patch new file mode 100644 index 0000000..85f56e0 --- /dev/null +++ b/foreign/dotfiles/pkgs/patches/xwayland-hidpi.patch @@ -0,0 +1,498 @@ +diff --git a/hw/xwayland/xwayland-cursor.c b/hw/xwayland/xwayland-cursor.c +index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e7501b02245 100644 +--- a/hw/xwayland/xwayland-cursor.c ++++ b/hw/xwayland/xwayland-cursor.c +@@ -171,6 +171,8 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat, + } + + wl_surface_attach(xwl_cursor->surface, buffer, 0, 0); ++ wl_surface_set_buffer_scale(xwl_cursor->surface, ++ xwl_seat->xwl_screen->global_output_scale); + xwl_surface_damage(xwl_seat->xwl_screen, xwl_cursor->surface, 0, 0, + xwl_seat->x_cursor->bits->width, + xwl_seat->x_cursor->bits->height); +@@ -190,6 +192,7 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat, + void + xwl_seat_set_cursor(struct xwl_seat *xwl_seat) + { ++ struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; + struct xwl_cursor *xwl_cursor = &xwl_seat->cursor; + PixmapPtr pixmap; + CursorPtr cursor; +@@ -220,8 +223,8 @@ xwl_seat_set_cursor(struct xwl_seat *xwl_seat) + wl_pointer_set_cursor(xwl_seat->wl_pointer, + xwl_seat->pointer_enter_serial, + xwl_cursor->surface, +- xwl_seat->x_cursor->bits->xhot, +- xwl_seat->x_cursor->bits->yhot); ++ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot), ++ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot)); + + xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap); + } +@@ -230,6 +233,7 @@ void + xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool) + { + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; ++ struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; + struct xwl_cursor *xwl_cursor = &xwl_tablet_tool->cursor; + PixmapPtr pixmap; + CursorPtr cursor; +@@ -258,9 +262,9 @@ xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool) + zwp_tablet_tool_v2_set_cursor(xwl_tablet_tool->tool, + xwl_tablet_tool->proximity_in_serial, + xwl_cursor->surface, +- xwl_seat->x_cursor->bits->xhot, +- xwl_seat->x_cursor->bits->yhot); +- ++ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot), ++ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot)); ++ wl_surface_set_buffer_scale(xwl_cursor->surface, xwl_screen->global_output_scale); + xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap); + } + +diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c +index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf129b5e17a 100644 +--- a/hw/xwayland/xwayland-input.c ++++ b/hw/xwayland/xwayland-input.c +@@ -412,8 +412,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, + DeviceIntPtr dev = get_pointer_device(xwl_seat); + DeviceIntPtr master; + int i; +- int sx = wl_fixed_to_int(sx_w); +- int sy = wl_fixed_to_int(sy_w); ++ int sx = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale; ++ int sy = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale; + int dx, dy; + ScreenPtr pScreen = xwl_seat->xwl_screen->screen; + ValuatorMask mask; +@@ -592,13 +592,14 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) + { + struct xwl_seat *xwl_seat = data; ++ int32_t scale = xwl_seat->xwl_screen->global_output_scale; + + if (!xwl_seat->focus_window) + return; + + xwl_seat->pending_pointer_event.has_absolute = TRUE; +- xwl_seat->pending_pointer_event.x = sx_w; +- xwl_seat->pending_pointer_event.y = sy_w; ++ xwl_seat->pending_pointer_event.x = sx_w * scale; ++ xwl_seat->pending_pointer_event.y = sy_w * scale; + + if (wl_proxy_get_version((struct wl_proxy *) xwl_seat->wl_pointer) < 5) + dispatch_pointer_motion_event(xwl_seat); +@@ -672,7 +673,8 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer, + xorg_list_del(&pending->l); + free(pending); + } else { +- valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor); ++ double scaled_value = wl_fixed_to_double(value); ++ valuator_mask_set_double(&mask, index, scaled_value / divisor); + } + + QueuePointerEvents(get_pointer_device(xwl_seat), +@@ -740,12 +742,13 @@ relative_pointer_handle_relative_motion(void *data, + wl_fixed_t dy_unaccelf) + { + struct xwl_seat *xwl_seat = data; ++ int32_t scale = xwl_seat->xwl_screen->global_output_scale; + + xwl_seat->pending_pointer_event.has_relative = TRUE; +- xwl_seat->pending_pointer_event.dx = wl_fixed_to_double(dxf); +- xwl_seat->pending_pointer_event.dy = wl_fixed_to_double(dyf); +- xwl_seat->pending_pointer_event.dx_unaccel = wl_fixed_to_double(dx_unaccelf); +- xwl_seat->pending_pointer_event.dy_unaccel = wl_fixed_to_double(dy_unaccelf); ++ xwl_seat->pending_pointer_event.dx = wl_fixed_to_double(dxf) * scale; ++ xwl_seat->pending_pointer_event.dy = wl_fixed_to_double(dyf) * scale; ++ xwl_seat->pending_pointer_event.dx_unaccel = wl_fixed_to_double(dx_unaccelf) * scale; ++ xwl_seat->pending_pointer_event.dy_unaccel = wl_fixed_to_double(dy_unaccelf) * scale; + + if (!xwl_seat->focus_window) + return; +@@ -1057,8 +1060,8 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, + + xwl_touch->window = wl_surface_get_user_data(surface); + xwl_touch->id = id; +- xwl_touch->x = wl_fixed_to_int(sx_w); +- xwl_touch->y = wl_fixed_to_int(sy_w); ++ xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale; ++ xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale; + xorg_list_add(&xwl_touch->link_touch, &xwl_seat->touches); + + xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchBegin); +@@ -1094,8 +1097,8 @@ touch_handle_motion(void *data, struct wl_touch *wl_touch, + if (!xwl_touch) + return; + +- xwl_touch->x = wl_fixed_to_int(sx_w); +- xwl_touch->y = wl_fixed_to_int(sy_w); ++ xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale;; ++ xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale;; + xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchUpdate); + } + +@@ -1726,8 +1729,8 @@ tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + int32_t dx, dy; +- double sx = wl_fixed_to_double(x); +- double sy = wl_fixed_to_double(y); ++ double sx = wl_fixed_to_double(x) * xwl_seat->xwl_screen->global_output_scale; ++ double sy = wl_fixed_to_double(y) * xwl_seat->xwl_screen->global_output_scale; + + if (!xwl_seat->tablet_focus_window) + return; +@@ -2714,6 +2717,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em + int x, + int y) + { ++ struct xwl_screen *xwl_screen; + struct zwp_locked_pointer_v1 *locked_pointer = + warp_emulator->locked_pointer; + WindowPtr window; +@@ -2725,6 +2729,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em + if (!warp_emulator->xwl_seat->focus_window) + return; + ++ xwl_screen = warp_emulator->xwl_seat->xwl_screen; + window = warp_emulator->xwl_seat->focus_window->window; + if (x >= window->drawable.x || + y >= window->drawable.y || +@@ -2733,8 +2738,8 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em + sx = x - window->drawable.x; + sy = y - window->drawable.y; + zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer, +- wl_fixed_from_int(sx), +- wl_fixed_from_int(sy)); ++ wl_fixed_from_int(xwl_scale_to(xwl_screen, sx)), ++ wl_fixed_from_int(xwl_scale_to(xwl_screen, sy))); + wl_surface_commit(warp_emulator->xwl_seat->focus_window->surface); + } + } +diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c +index ef705bc01bf8c2d2f170cda9ba21ed8293f50559..b8f6cd51bd240ed5e16271eb4749db18868bea7b 100644 +--- a/hw/xwayland/xwayland-output.c ++++ b/hw/xwayland/xwayland-output.c +@@ -191,6 +191,9 @@ update_screen_size(struct xwl_output *xwl_output, int width, int height) + { + struct xwl_screen *xwl_screen = xwl_output->xwl_screen; + ++ width *= xwl_screen->global_output_scale; ++ height *= xwl_screen->global_output_scale; ++ + if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL) + SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE); + +@@ -497,14 +500,15 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client, + xwl_output_set_randr_emu_props(xwl_output->xwl_screen, client); + } + +-static void +-apply_output_change(struct xwl_output *xwl_output) ++void ++xwl_output_apply_changes(struct xwl_output *xwl_output) + { + struct xwl_screen *xwl_screen = xwl_output->xwl_screen; + struct xwl_output *it; + int mode_width, mode_height, count; + int width = 0, height = 0, has_this_output = 0; + RRModePtr *randr_modes; ++ int32_t scale = xwl_screen->global_output_scale; + + /* Clear out the "done" received flags */ + xwl_output->wl_output_done = FALSE; +@@ -523,10 +527,10 @@ apply_output_change(struct xwl_output *xwl_output) + } + + /* Build a fresh modes array using the current refresh rate */ +- randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count); ++ randr_modes = output_get_rr_modes(xwl_output, mode_width * scale, mode_height * scale, &count); + RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1); + RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0], +- xwl_output->x, xwl_output->y, ++ xwl_output->x * scale, xwl_output->y * scale, + xwl_output->rotation, NULL, 1, &xwl_output->randr_output); + /* RROutputSetModes takes ownership of the passed in modes, so we only + * have to free the pointer array. +@@ -567,7 +571,7 @@ output_handle_done(void *data, struct wl_output *wl_output) + */ + if (xwl_output->xdg_output_done || !xwl_output->xdg_output || + zxdg_output_v1_get_version(xwl_output->xdg_output) >= 3) +- apply_output_change(xwl_output); ++ xwl_output_apply_changes(xwl_output); + } + + static void +@@ -610,7 +614,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) + xwl_output->xdg_output_done = TRUE; + if (xwl_output->wl_output_done && + zxdg_output_v1_get_version(xdg_output) < 3) +- apply_output_change(xwl_output); ++ xwl_output_apply_changes(xwl_output); + } + + static void +@@ -678,6 +682,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id) + RROutputSetConnection(xwl_output->randr_output, RR_Connected); + RRTellChanged(xwl_screen->screen); + ++ xwl_output->scale = 1; ++ + /* We want the output to be in the list as soon as created so we can + * use it when binding to the xdg-output protocol... + */ +diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h +index 02b9831083e82a33d85d4404e39d00f06f6c56fd..ec089757f44178dcd7f9c48907f790ce1b2a2729 100644 +--- a/hw/xwayland/xwayland-output.h ++++ b/hw/xwayland/xwayland-output.h +@@ -53,7 +53,7 @@ struct xwl_output { + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; + uint32_t server_output_id; +- int32_t x, y, width, height, refresh; ++ int32_t x, y, width, height, refresh, scale; + Rotation rotation; + Bool wl_output_done; + Bool xdg_output_done; +@@ -100,6 +100,8 @@ void xwl_output_set_emulated_mode(struct xwl_output *xwl_output, + void xwl_output_set_window_randr_emu_props(struct xwl_screen *xwl_screen, + WindowPtr window); + ++void xwl_output_apply_changes(struct xwl_output *xwl_output); ++ + void xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen); + + #endif /* XWAYLAND_OUTPUT_H */ +diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c +index c9cf8c2f569a319034e0789e7587414e50237065..5be0c208ca46b1a53a136885fdc8ab44251fe7ff 100644 +--- a/hw/xwayland/xwayland-present.c ++++ b/hw/xwayland/xwayland-present.c +@@ -680,6 +680,8 @@ xwl_present_flip(WindowPtr present_window, + + /* We can flip directly to the main surface (full screen window without clips) */ + wl_surface_attach(xwl_window->surface, buffer, 0, 0); ++ wl_surface_set_buffer_scale(xwl_window->surface, ++ xwl_window->xwl_screen->global_output_scale); + + if (!xwl_window->frame_callback) + xwl_window_create_frame_callback(xwl_window); +diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c +index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f417925f0f8 100644 +--- a/hw/xwayland/xwayland-screen.c ++++ b/hw/xwayland/xwayland-screen.c +@@ -51,6 +51,7 @@ + #include "xwayland-pixmap.h" + #include "xwayland-present.h" + #include "xwayland-shm.h" ++#include "xwayland-window-buffers.h" + + #ifdef MITSHM + #include "shmint.h" +@@ -110,6 +111,12 @@ xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen) + return xwl_screen->rootless && xwl_screen_has_viewport_support(xwl_screen); + } + ++int ++xwl_scale_to(struct xwl_screen *xwl_screen, int value) ++{ ++ return value / (double)xwl_screen->global_output_scale + 0.5; ++} ++ + /* Return the output @ 0x0, falling back to the first output in the list */ + struct xwl_output * + xwl_screen_get_first_output(struct xwl_screen *xwl_screen) +@@ -127,6 +134,37 @@ xwl_screen_get_first_output(struct xwl_screen *xwl_screen) + return xorg_list_first_entry(&xwl_screen->output_list, struct xwl_output, link); + } + ++static void ++xwl_screen_set_global_scale_from_property(struct xwl_screen *screen, ++ PropertyPtr prop) ++{ ++ CARD32 *propdata; ++ ++ if (prop->type != XA_CARDINAL || prop->format != 32 || prop->size != 1) { ++ // TODO: handle warnings more cleanly. ++ LogMessageVerb(X_WARNING, 0, "Bad value for property %s.\n", ++ NameForAtom(prop->propertyName)); ++ return; ++ } ++ ++ propdata = prop->data; ++ xwl_screen_set_global_scale(screen, propdata[0]); ++} ++ ++static void ++xwl_screen_update_property(struct xwl_screen *screen, ++ PropertyStateRec *propstate) ++{ ++ switch (propstate->state) { ++ case PropertyNewValue: ++ xwl_screen_set_global_scale_from_property(screen, propstate->prop); ++ break; ++ case PropertyDelete: ++ xwl_screen_set_global_scale(screen, 1); ++ break; ++ } ++} ++ + static void + xwl_property_callback(CallbackListPtr *pcbl, void *closure, + void *calldata) +@@ -134,19 +172,24 @@ xwl_property_callback(CallbackListPtr *pcbl, void *closure, + ScreenPtr screen = closure; + PropertyStateRec *rec = calldata; + struct xwl_screen *xwl_screen; +- struct xwl_window *xwl_window; + + if (rec->win->drawable.pScreen != screen) + return; + +- xwl_window = xwl_window_get(rec->win); +- if (!xwl_window) +- return; +- + xwl_screen = xwl_screen_get(screen); + +- if (rec->prop->propertyName == xwl_screen->allow_commits_prop) ++ if (rec->prop->propertyName == xwl_screen->allow_commits_prop) { ++ struct xwl_window *xwl_window; ++ ++ xwl_window = xwl_window_get(rec->win); ++ if (!xwl_window) ++ return; ++ + xwl_window_update_property(xwl_window, rec); ++ } ++ else if (rec->prop->propertyName == xwl_screen->global_output_scale_prop) { ++ xwl_screen_update_property(xwl_screen, rec); ++ } + } + + Bool +@@ -521,8 +564,14 @@ void xwl_surface_damage(struct xwl_screen *xwl_screen, + { + if (wl_surface_get_version(surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) + wl_surface_damage_buffer(surface, x, y, width, height); +- else ++ else { ++ x = xwl_scale_to(xwl_screen, x); ++ y = xwl_scale_to(xwl_screen, y); ++ width = xwl_scale_to(xwl_screen, width); ++ height = xwl_scale_to(xwl_screen, height); ++ + wl_surface_damage(surface, x, y, width, height); ++ } + } + + void +@@ -538,10 +587,34 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen) + xwl_give_up("could not connect to wayland server\n"); + } + ++void ++xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale) ++{ ++ struct xwl_output *it; ++ struct xwl_window *xwl_window; ++ ++ xwl_screen->global_output_scale = scale; ++ ++ /* change randr resolutions and positions */ ++ xorg_list_for_each_entry(it, &xwl_screen->output_list, link) { ++ xwl_output_apply_changes(it); ++ } ++ ++ if (!xwl_screen->rootless && xwl_screen->screen->root) { ++ /* Clear all the buffers, so that they'll be remade with the new sizes ++ * (this doesn't occur automatically because as far as Xorg is ++ * concerned, the window's size is the same) */ ++ xorg_list_for_each_entry(xwl_window, &xwl_screen->window_list, link_window) { ++ xwl_window_buffers_recycle(xwl_window); ++ } ++ } ++} ++ + Bool + xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) + { + static const char allow_commits[] = "_XWAYLAND_ALLOW_COMMITS"; ++ static const char global_output_scale[] = "_XWAYLAND_GLOBAL_OUTPUT_SCALE"; + struct xwl_screen *xwl_screen; + Pixel red_mask, blue_mask, green_mask; + int ret, bpc, green_bpc, i; +@@ -573,6 +646,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) + #ifdef XWL_HAS_GLAMOR + xwl_screen->glamor = 1; + #endif ++ xwl_screen->global_output_scale = 1; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-rootless") == 0) { +@@ -743,6 +817,12 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) + if (xwl_screen->allow_commits_prop == BAD_RESOURCE) + return FALSE; + ++ xwl_screen->global_output_scale_prop = MakeAtom(global_output_scale, ++ strlen(global_output_scale), ++ TRUE); ++ if (xwl_screen->global_output_scale_prop == BAD_RESOURCE) ++ return FALSE; ++ + AddCallback(&PropertyStateCallback, xwl_property_callback, pScreen); + + xwl_screen_roundtrip(xwl_screen); +diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h +index b965dddd7f964b1d100bbb9d10da1c35ab39810e..7446829d098fbe235e605084a016daff1a8eaea2 100644 +--- a/hw/xwayland/xwayland-screen.h ++++ b/hw/xwayland/xwayland-screen.h +@@ -72,6 +72,8 @@ struct xwl_screen { + struct xorg_list damage_window_list; + struct xorg_list window_list; + ++ int32_t global_output_scale; ++ + int wayland_fd; + struct wl_display *display; + struct wl_registry *registry; +@@ -107,6 +109,7 @@ struct xwl_screen { + struct glamor_context *glamor_ctx; + + Atom allow_commits_prop; ++ Atom global_output_scale_prop; + + /* The preferred GLVND vendor. If NULL, "mesa" is assumed. */ + const char *glvnd_vendor; +@@ -134,5 +137,7 @@ void xwl_screen_roundtrip (struct xwl_screen *xwl_screen); + void xwl_surface_damage(struct xwl_screen *xwl_screen, + struct wl_surface *surface, + int32_t x, int32_t y, int32_t width, int32_t height); ++int xwl_scale_to(struct xwl_screen *xwl_screen, int value); ++void xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale); + + #endif /* XWAYLAND_SCREEN_H */ +diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c +index 00f161eda084e335ac07471a2198176d75d9fcf0..ed3903853f0dab1dad390cd8429639541546157d 100644 +--- a/hw/xwayland/xwayland-window.c ++++ b/hw/xwayland/xwayland-window.c +@@ -470,7 +470,8 @@ ensure_surface_for_window(WindowPtr window) + } + + wl_region_add(region, 0, 0, +- window->drawable.width, window->drawable.height); ++ xwl_scale_to(xwl_screen, window->drawable.width), ++ xwl_scale_to(xwl_screen, window->drawable.height)); + wl_surface_set_opaque_region(xwl_window->surface, region); + wl_region_destroy(region); + } +@@ -820,6 +821,7 @@ xwl_window_post_damage(struct xwl_window *xwl_window) + #endif + + wl_surface_attach(xwl_window->surface, buffer, 0, 0); ++ wl_surface_set_buffer_scale(xwl_window->surface, xwl_screen->global_output_scale); + + /* Arbitrary limit to try to avoid flooding the Wayland + * connection. If we flood it too much anyway, this could + diff --git a/foreign/dotfiles/pkgs/patches/xwayland-vsync.patch b/foreign/dotfiles/pkgs/patches/xwayland-vsync.patch new file mode 100644 index 0000000..375db88 --- /dev/null +++ b/foreign/dotfiles/pkgs/patches/xwayland-vsync.patch @@ -0,0 +1,12 @@ +--- a/hw/xwayland/xwayland-present.c ++++ b/hw/xwayland/xwayland-present.c +@@ -824,7 +824,8 @@ + dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id); + vblank->pixmap = NULL; + +- if (xwl_present_queue_vblank(screen, window, vblank->crtc, ++ if (vblank->target_msc > crtc_msc && ++ xwl_present_queue_vblank(screen, window, vblank->crtc, + vblank->event_id, crtc_msc + 1) + == Success) + return; diff --git a/foreign/dotfiles/pkgs/regreet/default.nix b/foreign/dotfiles/pkgs/regreet/default.nix new file mode 100644 index 0000000..e64de02 --- /dev/null +++ b/foreign/dotfiles/pkgs/regreet/default.nix @@ -0,0 +1,35 @@ +{ + rustPlatform, + lib, + fetchFromGitHub, + pkg-config, + glib, + gtk4, + pango, +}: +rustPlatform.buildRustPackage { + pname = "regreet"; + version = "unstable-2023-03-12"; + + src = fetchFromGitHub { + owner = "rharish101"; + repo = "ReGreet"; + rev = "afd124b369bd36be0985f246a59df75f27b1f171"; + sha256 = "sha256-Mlyw+OI6f7YeykReEDDAKpdGHaIpl3HzUTuZjgiFzAk="; + }; + + cargoHash = "sha256-koz6bSmBvoW0R3sRgJU2Hizr2CVsGFDyqoPov/F0VL8="; + + buildFeatures = ["gtk4_8"]; + + nativeBuildInputs = [pkg-config]; + buildInputs = [glib gtk4 pango]; + + meta = with lib; { + description = "Clean and customizable greeter for greetd"; + homepage = "https://github.com/rharish101/ReGreet"; + license = licenses.gpl3Plus; + maintainers = with maintainers; [fufexan]; + platforms = platforms.linux; + }; +} diff --git a/foreign/dotfiles/pkgs/repl/default.nix b/foreign/dotfiles/pkgs/repl/default.nix new file mode 100644 index 0000000..dd4d6ee --- /dev/null +++ b/foreign/dotfiles/pkgs/repl/default.nix @@ -0,0 +1,25 @@ +# modified from https://github.com/gytis-ivaskevicius/flake-utils/plus +{ + coreutils, + gnused, + writeShellScriptBin, +}: let + repl = ../../lib/repl.nix; + example = command: desc: ''\n\u001b[33m ${command}\u001b[0m - ${desc}''; +in + writeShellScriptBin "repl" '' + case "$1" in + "-h"|"--help"|"help") + printf "%b\n\e[4mUsage\e[0m: \ + ${example "repl" "Loads system flake if available."} \ + ${example "repl /path/to/flake.nix" "Loads specified flake."}\n" + ;; + *) + if [ -z "$1" ]; then + nix repl ${repl} + else + nix repl --arg flakePath $(${coreutils}/bin/readlink -f $1 | ${gnused}/bin/sed 's|/flake.nix||') ${repl} + fi + ;; + esac + '' diff --git a/foreign/dotfiles/pkgs/spotify/default.nix b/foreign/dotfiles/pkgs/spotify/default.nix new file mode 100644 index 0000000..b975e02 --- /dev/null +++ b/foreign/dotfiles/pkgs/spotify/default.nix @@ -0,0 +1,209 @@ +{ + fetchurl, + lib, + stdenv, + squashfsTools, + xorg, + alsa-lib, + makeWrapper, + wrapGAppsHook, + openssl, + freetype, + glib, + pango, + cairo, + atk, + gdk-pixbuf, + gtk3, + cups, + nspr, + nss, + libpng, + libnotify, + libgcrypt, + systemd, + fontconfig, + dbus, + expat, + ffmpeg, + curlWithGnuTls, + zlib, + gnome, + at-spi2-atk, + at-spi2-core, + libpulseaudio, + libdrm, + mesa, + libxkbcommon, + # High-DPI support: Spotify's --force-device-scale-factor argument + # not added if `null`, otherwise, should be a number. + deviceScaleFactor ? null, +}: let + # TO UPDATE: just execute the ./update.sh script (won't do anything if there is no update) + # "rev" decides what is actually being downloaded + # If an update breaks things, one of those might have valuable info: + # https://aur.archlinux.org/packages/spotify/ + # https://community.spotify.com/t5/Desktop-Linux + version = "1.1.99.878.g1e4ccc6e"; + # To get the latest stable revision: + # curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/spotify?channel=stable' | jq '.download_url,.version,.last_updated' + # To get general information: + # curl -H 'Snap-Device-Series: 16' 'https://api.snapcraft.io/v2/snaps/info/spotify' | jq '.' + # More examples of api usage: + # https://github.com/canonical-websites/snapcraft.io/blob/master/webapp/publisher/snaps/views.py + rev = "62"; + + deps = [ + alsa-lib + at-spi2-atk + at-spi2-core + atk + cairo + cups + curlWithGnuTls + dbus + expat + ffmpeg + fontconfig + freetype + gdk-pixbuf + glib + gtk3 + libdrm + libgcrypt + libnotify + libpng + libpulseaudio + libxkbcommon + mesa + nss + pango + stdenv.cc.cc + systemd + xorg.libICE + xorg.libSM + xorg.libX11 + xorg.libxcb + xorg.libXcomposite + xorg.libXcursor + xorg.libXdamage + xorg.libXext + xorg.libXfixes + xorg.libXi + xorg.libXrandr + xorg.libXrender + xorg.libXScrnSaver + xorg.libxshmfence + xorg.libXtst + zlib + ]; +in + stdenv.mkDerivation { + pname = "spotify"; + inherit version; + + # fetch from snapcraft instead of the debian repository most repos fetch from. + # That is a bit more cumbersome. But the debian repository only keeps the last + # two versions, while snapcraft should provide versions indefinately: + # https://forum.snapcraft.io/t/how-can-a-developer-remove-her-his-app-from-snap-store/512 + + # This is the next-best thing, since we're not allowed to re-distribute + # spotify ourselves: + # https://community.spotify.com/t5/Desktop-Linux/Redistribute-Spotify-on-Linux-Distributions/td-p/1695334 + src = fetchurl { + url = "https://api.snapcraft.io/api/v1/snaps/download/pOBIoZ2LrCB3rDohMxoYGnbN14EHOgD7_${rev}.snap"; + sha512 = "9b8f63c067632f15f5abf6449d09d28fd56839decfc9801d4526cf5b97b9cb5933ede179bcd0b0f2d8b6d4c7e72f55c7fc13dfb0ae4293ef447ead1d018b9cc6"; + }; + + nativeBuildInputs = [makeWrapper wrapGAppsHook squashfsTools]; + + dontStrip = true; + dontPatchELF = true; + + unpackPhase = '' + runHook preUnpack + unsquashfs "$src" '/usr/share/spotify' '/usr/bin/spotify' '/meta/snap.yaml' + cd squashfs-root + if ! grep -q 'grade: stable' meta/snap.yaml; then + # Unfortunately this check is not reliable: At the moment (2018-07-26) the + # latest version in the "edge" channel is also marked as stable. + echo "The snap package is marked as unstable:" + grep 'grade: ' meta/snap.yaml + echo "You probably chose the wrong revision." + exit 1 + fi + if ! grep -q '${version}' meta/snap.yaml; then + echo "Package version differs from version found in snap metadata:" + grep 'version: ' meta/snap.yaml + echo "While the nix package specifies: ${version}." + echo "You probably chose the wrong revision or forgot to update the nix version." + exit 1 + fi + runHook postUnpack + ''; + + # Prevent double wrapping + dontWrapGApps = true; + + installPhase = '' + runHook preInstall + + libdir=$out/lib/spotify + mkdir -p $libdir + mv ./usr/* $out/ + + cp meta/snap.yaml $out + + # Work around Spotify referring to a specific minor version of + # OpenSSL. + + ln -s ${lib.getLib openssl}/lib/libssl.so $libdir/libssl.so.1.0.0 + ln -s ${lib.getLib openssl}/lib/libcrypto.so $libdir/libcrypto.so.1.0.0 + ln -s ${nspr.out}/lib/libnspr4.so $libdir/libnspr4.so + ln -s ${nspr.out}/lib/libplc4.so $libdir/libplc4.so + + ln -s ${ffmpeg.lib}/lib/libavcodec.so* $libdir + ln -s ${ffmpeg.lib}/lib/libavformat.so* $libdir + + rpath="$out/share/spotify:$libdir" + + patchelf \ + --interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \ + --set-rpath $rpath $out/share/spotify/spotify + + librarypath="${lib.makeLibraryPath deps}:$libdir" + wrapProgram $out/share/spotify/spotify \ + ''${gappsWrapperArgs[@]} \ + ${lib.optionalString (deviceScaleFactor != null) '' + --add-flags "--force-device-scale-factor=${toString deviceScaleFactor}" \ + ''} \ + --prefix LD_LIBRARY_PATH : "$librarypath" \ + --prefix PATH : "${gnome.zenity}/bin" + + # fix Icon line in the desktop file (#48062) + sed -i "s:^Icon=.*:Icon=spotify-client:" "$out/share/spotify/spotify.desktop" + + # Desktop file + mkdir -p "$out/share/applications/" + cp "$out/share/spotify/spotify.desktop" "$out/share/applications/" + + # Icons + for i in 16 22 24 32 48 64 128 256 512; do + ixi="$i"x"$i" + mkdir -p "$out/share/icons/hicolor/$ixi/apps" + ln -s "$out/share/spotify/icons/spotify-linux-$i.png" \ + "$out/share/icons/hicolor/$ixi/apps/spotify-client.png" + done + + runHook postInstall + ''; + + meta = with lib; { + homepage = "https://www.spotify.com/"; + description = "Play music from the Spotify music service"; + sourceProvenance = with sourceTypes; [binaryNativeCode]; + license = licenses.unfree; + maintainers = with maintainers; [eelco ftrvxmtrx sheenobu mudri timokau ma27]; + platforms = ["x86_64-linux"]; + }; + } diff --git a/foreign/dotfiles/pkgs/sway-hidpi.nix b/foreign/dotfiles/pkgs/sway-hidpi.nix new file mode 100644 index 0000000..85022d2 --- /dev/null +++ b/foreign/dotfiles/pkgs/sway-hidpi.nix @@ -0,0 +1,51 @@ +prev: let + sway-hidpi = prev.sway.override { + inherit sway-unwrapped; + withGtkWrapper = true; + }; + + sway-unwrapped = + (prev.sway-unwrapped.overrideAttrs (oa: { + src = prev.fetchFromGitHub { + owner = "swaywm"; + repo = "sway"; + rev = "8d8a21c9c321fa41b033caf9b5b62cdd584483c1"; + sha256 = "sha256-WRvJsSvDv2+qirqkpaV2c7fFOgJAT3sXxPtKLure580="; + }; + + buildInputs = oa.buildInputs ++ [prev.pcre2 prev.xorg.xcbutilwm]; + })) + .override {wlroots_0_16 = wlroots-sway;}; + + wlroots-sway = + (prev.wlroots.overrideAttrs (_: { + src = prev.fetchFromGitLab { + domain = "gitlab.freedesktop.org"; + owner = "wlroots"; + repo = "wlroots"; + rev = "add44b3e2e4ff7ef98b16813fb3c9e1d8b398008"; + sha256 = "sha256-/fJGHeDuZ9aLjCSxILqNSm2aMrvlLZMZpx/WzX5A/XU="; + }; + + patches = [ + (prev.fetchpatch { + url = "https://gitlab.freedesktop.org/lilydjwg/wlroots/-/commit/6c5ffcd1fee9e44780a6a8792f74ecfbe24a1ca7.diff"; + sha256 = "sha256-Eo1pTa/PIiJsRZwIUnHGTIFFIedzODVf0ZeuXb0a3TQ="; + }) + (prev.fetchpatch { + url = "https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/18595000f3a21502fd60bf213122859cc348f9af.diff"; + sha256 = "sha256-jvfkAMh3gzkfuoRhB4E9T5X1Hu62wgUjj4tZkJm0mrI="; + revert = true; + }) + ]; + })) + .override {inherit xwayland;}; + + xwayland = prev.xwayland.overrideAttrs (_: { + patches = [ + ./patches/xwayland-vsync.patch + ./patches/xwayland-hidpi.patch + ]; + }); +in + sway-hidpi diff --git a/foreign/dotfiles/pkgs/waveform/default.nix b/foreign/dotfiles/pkgs/waveform/default.nix new file mode 100644 index 0000000..f45c747 --- /dev/null +++ b/foreign/dotfiles/pkgs/waveform/default.nix @@ -0,0 +1,126 @@ +{ + alsa-lib, + atk, + cairo, + cups, + curl, + dbus, + dpkg, + expat, + fetchurl, + fontconfig, + freetype, + gdk-pixbuf, + glib, + gnome2, + gtk3, + gtk4, + lib, + libX11, + libxcb, + libXScrnSaver, + libXcomposite, + libXcursor, + libXdamage, + libXext, + libXfixes, + libXi, + libXrandr, + libXrender, + libXtst, + libdrm, + libnotify, + libpulseaudio, + libuuid, + libxshmfence, + mesa, + nspr, + nss, + pango, + stdenv, + at-spi2-atk, + at-spi2-core, + autoPatchelfHook, + wrapGAppsHook, +}: let + mirror = "https://cdn.tracktion.com/file/tracktiondownload/waveform"; +in + stdenv.mkDerivation rec { + pname = "waveform"; + version = "11.5.18"; + pversion = lib.concatStrings (lib.remove "." (lib.stringToCharacters version)); + + src = fetchurl { + url = "${mirror}/${pversion}/${pname}_64bit_v${version}.deb"; + sha256 = "sha256-kRqgiIfgpmkPOwAuSuxhi/H18ezgicRo+wbTWeFaweU="; + }; + + unpackCmd = "${dpkg}/bin/dpkg-deb -x $curSrc ."; + + nativeBuildInputs = [ + autoPatchelfHook + wrapGAppsHook + ]; + + # not sure how much of this all is needed + buildInputs = [ + alsa-lib + at-spi2-atk + at-spi2-core + atk + cairo + cups + curl + dbus + expat + fontconfig.lib + freetype + gdk-pixbuf + glib + gnome2.GConf + gtk3 + libX11 + libXScrnSaver + libXcomposite + libXcursor + libXdamage + libXext + libXfixes + libXi + libXrandr + libXrender + libXtst + libdrm + libnotify + libuuid + libxcb + libxshmfence + mesa + nspr + nss + pango + stdenv.cc.cc.lib + ]; + + runtimeDependencies = [ + # without this, it complains about not finding libcurl + curl.out + + # not sure if needed + libpulseaudio.out + gtk3 + gtk4 + ]; + + installPhase = '' + mkdir -p $out + cp -r . $out/ + ''; + + meta = with lib; { + homepage = "https://www.tracktion.com/welcome/waveform-free"; + description = "Waveform DAW"; + platforms = ["x86_64-linux"]; + license = licenses.unfree; + }; + } diff --git a/foreign/dotfiles/secrets/secrets.nix b/foreign/dotfiles/secrets/secrets.nix new file mode 100644 index 0000000..63d0b8a --- /dev/null +++ b/foreign/dotfiles/secrets/secrets.nix @@ -0,0 +1,7 @@ +let + mihai = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH81M2NZOzd5tGGRsDv//wkSrVNJJpaiaLghPZBH8VTd"; + + io = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMRDsoSresP7/VnrQOYsWWO/5V+EdPEx5PwI0DxW9H00 root@io"; +in { + "spotify.age".publicKeys = [mihai io]; +} diff --git a/foreign/dotfiles/secrets/spotify.age b/foreign/dotfiles/secrets/spotify.age new file mode 100644 index 0000000..d1809ac --- /dev/null +++ b/foreign/dotfiles/secrets/spotify.age @@ -0,0 +1,10 @@ +age-encryption.org/v1 +-> ssh-ed25519 gC7Y0g gNDFkWou+lP6bb/DeykjpLnU58Q02WSNf9MuIbKgtg8 +EFqUCkPKPV169Nq2Fxw77v/hLMMfsBZtiyfHibGCrso +-> ssh-ed25519 4DWTmg Cb32NOKXPGY2vmdJjjpvDccA+NFqhLU+VN1h86jRlFM +9s2hXLh51mxkq4vLdZIT9F9og2bPPGvQrC4EGmo0ekw +-> 3%I0CmT-grease p = g)aHR,b0 +MvuI9zJMrw9+vKpsRGf6x4ltLg +--- 4kybI/sbTweYmJf5sG5vu6eF3HTZlkCM+gqRgdsk86A + $Y̌4&)Xmd>Sf]V$ +> \ No newline at end of file