feat(compositors)!: Add niri

According to its github page, niri is "A scrollable-tiling Wayland
compositor". Myself and coded have been looking for alternatives to
hyprland for stability and community reasons, and niri looks reasonable
for us

It's possible we'll remove hyprland altogether. If we do, we plan to
still maintain a more traditional compositor (e.g. swayfx)

BREAKING-CHANGE: This patch renames some hyprland options
Change-Id: I170dde769b55ebb32a3212a54012443fb4d0faf8
Reviewed-on: https://git.clicks.codes/c/Chimera/NixFiles/+/713
Reviewed-by: Samuel Shuert <coded@clicks.codes>
Tested-by: Samuel Shuert <coded@clicks.codes>
diff --git a/flake.lock b/flake.lock
index 9a03691..5ec076e 100644
--- a/flake.lock
+++ b/flake.lock
@@ -21,6 +21,37 @@
         "type": "github"
       }
     },
+    "cachix": {
+      "inputs": {
+        "devenv": [
+          "niri-flake",
+          "crate2nix"
+        ],
+        "flake-compat": [
+          "niri-flake",
+          "crate2nix"
+        ],
+        "nixpkgs": "nixpkgs",
+        "pre-commit-hooks": [
+          "niri-flake",
+          "crate2nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1709700175,
+        "narHash": "sha256-A0/6ZjLmT9qdYzKHmevnEIC7G+GiZ4UCr8v0poRPzds=",
+        "owner": "cachix",
+        "repo": "cachix",
+        "rev": "be97b37989f11b724197b5f4c7ffd78f12c8c4bf",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "ref": "latest",
+        "repo": "cachix",
+        "type": "github"
+      }
+    },
     "collabora-gtimelog": {
       "flake": false,
       "locked": {
@@ -49,6 +80,73 @@
         "url": "https://www.collabora.com/favicon.ico"
       }
     },
+    "crate2nix": {
+      "inputs": {
+        "cachix": "cachix",
+        "crate2nix_stable": "crate2nix_stable",
+        "devshell": "devshell",
+        "flake-compat": "flake-compat",
+        "flake-parts": "flake-parts_2",
+        "nix-test-runner": "nix-test-runner",
+        "nixpkgs": "nixpkgs_2",
+        "pre-commit-hooks": "pre-commit-hooks"
+      },
+      "locked": {
+        "lastModified": 1712821856,
+        "narHash": "sha256-rObTWSqfqt0qlOCokE2abesI11Ic8bCCkPNxlyk57Q0=",
+        "owner": "nix-community",
+        "repo": "crate2nix",
+        "rev": "cf034861fdc4e091fc7c5f01d6c022dc46686cf1",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "crate2nix",
+        "type": "github"
+      }
+    },
+    "crate2nix_stable": {
+      "inputs": {
+        "flake-utils": "flake-utils_2"
+      },
+      "locked": {
+        "lastModified": 1702842982,
+        "narHash": "sha256-A9AowkHIjsy1a4LuiPiVP88FMxyCWK41flZEZOUuwQM=",
+        "owner": "nix-community",
+        "repo": "crate2nix",
+        "rev": "75ac2973affa6b9b4f661a7b592cba6e4f51d426",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "ref": "0.12.0",
+        "repo": "crate2nix",
+        "type": "github"
+      }
+    },
+    "devshell": {
+      "inputs": {
+        "flake-utils": "flake-utils_3",
+        "nixpkgs": [
+          "niri-flake",
+          "crate2nix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1711099426,
+        "narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=",
+        "owner": "numtide",
+        "repo": "devshell",
+        "rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "devshell",
+        "type": "github"
+      }
+    },
     "ewwsalmoomedits--eww-widgets": {
       "flake": false,
       "locked": {
@@ -82,6 +180,20 @@
       }
     },
     "flake-compat": {
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "revCount": 57,
+        "type": "tarball",
+        "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
+      },
+      "original": {
+        "type": "tarball",
+        "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
+      }
+    },
+    "flake-compat_2": {
       "flake": false,
       "locked": {
         "lastModified": 1650374568,
@@ -118,6 +230,46 @@
         "type": "github"
       }
     },
+    "flake-parts_2": {
+      "inputs": {
+        "nixpkgs-lib": [
+          "niri-flake",
+          "crate2nix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1712014858,
+        "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "type": "github"
+      }
+    },
+    "flake-parts_3": {
+      "inputs": {
+        "nixpkgs-lib": "nixpkgs-lib"
+      },
+      "locked": {
+        "lastModified": 1715865404,
+        "narHash": "sha256-/GJvTdTpuDjNn84j82cU6bXztE0MSkdnTWClUCRub78=",
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "rev": "8dc45382d5206bd292f9c2768b8058a8fd8311d9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "type": "github"
+      }
+    },
     "flake-utils": {
       "inputs": {
         "systems": "systems"
@@ -138,7 +290,7 @@
     },
     "flake-utils-plus": {
       "inputs": {
-        "flake-utils": "flake-utils_2"
+        "flake-utils": "flake-utils_5"
       },
       "locked": {
         "lastModified": 1715533576,
@@ -173,6 +325,60 @@
         "type": "github"
       }
     },
+    "flake-utils_3": {
+      "inputs": {
+        "systems": "systems_3"
+      },
+      "locked": {
+        "lastModified": 1701680307,
+        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_4": {
+      "inputs": {
+        "systems": "systems_4"
+      },
+      "locked": {
+        "lastModified": 1710146030,
+        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_5": {
+      "inputs": {
+        "systems": "systems_5"
+      },
+      "locked": {
+        "lastModified": 1694529238,
+        "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
     "flakey-profile": {
       "locked": {
         "lastModified": 1712898590,
@@ -188,6 +394,29 @@
         "type": "github"
       }
     },
+    "gitignore": {
+      "inputs": {
+        "nixpkgs": [
+          "niri-flake",
+          "crate2nix",
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1709087332,
+        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
     "home-manager": {
       "inputs": {
         "nixpkgs": [
@@ -250,6 +479,53 @@
         "url": "https://git.lix.systems/lix-project/nixos-module"
       }
     },
+    "niri-flake": {
+      "inputs": {
+        "crate2nix": "crate2nix",
+        "flake-parts": "flake-parts_3",
+        "niri-stable": "niri-stable",
+        "niri-unstable": [
+          "niri-flake",
+          "niri-stable"
+        ],
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1716640018,
+        "narHash": "sha256-vty6GoIKET+rW+2sTcOImgSyQVa63BDczoGB0cdqF5U=",
+        "owner": "sodiboo",
+        "repo": "niri-flake",
+        "rev": "c990e5121b157c6765be9b39e1045052d8abf29f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "sodiboo",
+        "repo": "niri-flake",
+        "type": "github"
+      }
+    },
+    "niri-stable": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1716030039,
+        "narHash": "sha256-MJh0CR2YHJE0GNnxaTcElNMuZUEI0pe9fvC0mfy4484=",
+        "owner": "YaLTeR",
+        "repo": "niri",
+        "rev": "d96a66ddff1a6b88dbe3e23b049f7075533b216f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "YaLTeR",
+        "ref": "v0.1.6",
+        "repo": "niri",
+        "type": "github"
+      }
+    },
     "nix-index-database": {
       "inputs": {
         "nixpkgs": [
@@ -270,6 +546,22 @@
         "type": "github"
       }
     },
+    "nix-test-runner": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1588761593,
+        "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
+        "owner": "stoeffel",
+        "repo": "nix-test-runner",
+        "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
+        "type": "github"
+      },
+      "original": {
+        "owner": "stoeffel",
+        "repo": "nix-test-runner",
+        "type": "github"
+      }
+    },
     "nixos-hardware": {
       "locked": {
         "lastModified": 1716173274,
@@ -288,20 +580,32 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1716509168,
-        "narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
-        "owner": "nixos",
+        "lastModified": 1700612854,
+        "narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=",
+        "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "bfb7a882678e518398ce9a31a881538679f6f092",
+        "rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614",
         "type": "github"
       },
       "original": {
-        "owner": "nixos",
+        "owner": "NixOS",
         "ref": "nixos-unstable",
         "repo": "nixpkgs",
         "type": "github"
       }
     },
+    "nixpkgs-lib": {
+      "locked": {
+        "lastModified": 1714640452,
+        "narHash": "sha256-QBx10+k6JWz6u7VsohfSw8g8hjdBZEf8CFzXH1/1Z94=",
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/50eb7ecf4cd0a5756d7275c8ba36790e5bd53e33.tar.gz"
+      },
+      "original": {
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/50eb7ecf4cd0a5756d7275c8ba36790e5bd53e33.tar.gz"
+      }
+    },
     "nixpkgs-stable": {
       "locked": {
         "lastModified": 1716061101,
@@ -320,6 +624,36 @@
     },
     "nixpkgs_2": {
       "locked": {
+        "lastModified": 1712026416,
+        "narHash": "sha256-N/3VR/9e1NlN49p7kCiATiEY6Tzdo+CbrAG8kqCQKcI=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "080a4a27f206d07724b88da096e27ef63401a504",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "nixpkgs_3": {
+      "locked": {
+        "lastModified": 1716509168,
+        "narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "bfb7a882678e518398ce9a31a881538679f6f092",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_4": {
+      "locked": {
         "lastModified": 1716062047,
         "narHash": "sha256-OhysviwHQz4p2HZL4g7XGMLoUbWMjkMr/ogaR3VUYNA=",
         "owner": "NixOS",
@@ -349,6 +683,40 @@
         "type": "github"
       }
     },
+    "pre-commit-hooks": {
+      "inputs": {
+        "flake-compat": [
+          "niri-flake",
+          "crate2nix",
+          "flake-compat"
+        ],
+        "flake-utils": "flake-utils_4",
+        "gitignore": "gitignore",
+        "nixpkgs": [
+          "niri-flake",
+          "crate2nix",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": [
+          "niri-flake",
+          "crate2nix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1712055707,
+        "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
     "root": {
       "inputs": {
         "anyrun": "anyrun",
@@ -359,9 +727,10 @@
         "home-manager": "home-manager",
         "lix": "lix",
         "lix-module": "lix-module",
+        "niri-flake": "niri-flake",
         "nix-index-database": "nix-index-database",
         "nixos-hardware": "nixos-hardware",
-        "nixpkgs": "nixpkgs",
+        "nixpkgs": "nixpkgs_3",
         "nur": "nur",
         "snowfall-lib": "snowfall-lib",
         "sops-nix": "sops-nix",
@@ -370,7 +739,7 @@
     },
     "snowfall-lib": {
       "inputs": {
-        "flake-compat": "flake-compat",
+        "flake-compat": "flake-compat_2",
         "flake-utils-plus": "flake-utils-plus",
         "nixpkgs": [
           "nixpkgs"
@@ -393,7 +762,7 @@
     },
     "sops-nix": {
       "inputs": {
-        "nixpkgs": "nixpkgs_2",
+        "nixpkgs": "nixpkgs_4",
         "nixpkgs-stable": "nixpkgs-stable"
       },
       "locked": {
@@ -440,6 +809,51 @@
         "type": "github"
       }
     },
+    "systems_3": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_4": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_5": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
     "templates": {
       "locked": {
         "lastModified": 1708553502,
diff --git a/flake.nix b/flake.nix
index 617732f..dc58a3b 100644
--- a/flake.nix
+++ b/flake.nix
@@ -41,6 +41,15 @@
       inputs.nixpkgs.follows = "nixpkgs";
     };
 
+    niri-flake = {
+      url = "github:sodiboo/niri-flake";
+      inputs = {
+        niri-unstable.follows = "niri-flake/niri-stable";
+        nixpkgs.follows = "nixpkgs";
+        nixpkgs-stable.follows = "nixpkgs";
+      };
+    };
+
     nix-index-database = {
       url = "github:nix-community/nix-index-database";
       inputs.nixpkgs.follows = "nixpkgs";
@@ -68,11 +77,16 @@
       inherit inputs;
       src = ./.;
 
+      overlays = [
+        inputs.niri-flake.overlays.niri
+      ];
+
       homes.modules = [
         inputs.anyrun.homeManagerModules.default
         inputs.nur.hmModules.nur
         inputs.nix-index-database.hmModules.nix-index
         inputs.sops-nix.homeManagerModules.sops
+        inputs.niri-flake.homeModules.niri
       ];
 
       systems.modules.nixos = [
diff --git a/homes/x86_64-linux/minion@greylag/default.nix b/homes/x86_64-linux/minion@greylag/default.nix
index 0c5eb0a..84ef1f4 100644
--- a/homes/x86_64-linux/minion@greylag/default.nix
+++ b/homes/x86_64-linux/minion@greylag/default.nix
@@ -29,15 +29,31 @@
     modules.laptop.enable = true;
   };
 
-  chimera.hyprland.enable = true;
-  chimera.hyprland.hyprpaper.splash.enable = true;
   chimera.touchpad.enable = true;
 
-  chimera.hyprland.monitors = [
-    "eDP-1,preferred,0x0,1"
-    "desc:Dell Inc. DELL P2715Q V7WP95AV914L,preferred,2256x-1956,1,transform,1"
-    "desc:AOC 2460G5 0x00023C3F,preferred,336x-1080,1"
-  ];
+  chimera.hyprland.enable = false;
+  chimera.niri.enable = true;
+  chimera.niri.monitors = {
+    "eDP-1" = {
+      position = {
+        x = 0;
+        y = 0;
+      };
+    };
+    "DP-5" = {
+      position = {
+        x = 2256;
+        y = -1956;
+      };
+      transform.rotation = 90;
+    };
+    "DP-7" = {
+      position = {
+        x = 336;
+        y = -1080;
+      };
+    };
+  };
 
   chimera.theme.wallpaper = ./wallpaper.png;
 
diff --git a/modules/home/gestures/default.nix b/modules/home/gestures/default.nix
index f80d690..c471fcb 100644
--- a/modules/home/gestures/default.nix
+++ b/modules/home/gestures/default.nix
@@ -2,7 +2,8 @@
 {
   options.chimera.touchpad.enable = lib.mkEnableOption "Enable touchpad gestures";
 
-  config = lib.mkIf config.chimera.touchpad.enable {
+
+  config = lib.mkIf (config.chimera.touchpad.enable && config.chimera.hyprland.enable) {
     services.fusuma.enable = true;
 
     systemd.user.startServices = "sd-switch";
diff --git a/modules/home/waybar/default.nix b/modules/home/waybar/default.nix
index 9f3194c..52da59a 100644
--- a/modules/home/waybar/default.nix
+++ b/modules/home/waybar/default.nix
@@ -84,7 +84,7 @@
           layer = "top";
           position = "top";
           margin = "9 13 -10 18";
-          modules-left = [ "hyprland/workspaces" "keyboard-state" ];
+          modules-left = (if config.chimera.hyprland.enable then ["hyprland/workspaces"] else []);
           modules-center = ["clock"];
           modules-right = ["pulseaudio" "custom/mem" "cpu"]
             ++ (if config.chimera.waybar.modules.temperature.enable then [ "temperature" ] else [])
@@ -92,7 +92,7 @@
             ++ (if config.chimera.waybar.modules.battery.enable then [ "battery" ] else [])
             ++ [ "tray" ];
 
-          "hyprland/workspaces" = {
+          "hyprland/workspaces" = lib.mkIf config.chimera.hyprland.enable {
             "disable-scroll" = true;
           };
           clock = {
diff --git a/modules/home/wayland/default.nix b/modules/home/wayland/default.nix
new file mode 100644
index 0000000..3ea8d65
--- /dev/null
+++ b/modules/home/wayland/default.nix
@@ -0,0 +1,43 @@
+{
+  lib,
+  config,
+  ...
+}: {
+  options.chimera = {
+    wayland.enable = lib.mkOption {
+      type = lib.types.bool;
+      description = "Enable generic options which are useful for all wayland compositors";
+      default = false;
+      internal = true;
+    };
+    input.mouse.scrolling.natural = lib.mkEnableOption "Enable natural scrolling";
+    input.touchpad.scrolling.natural = lib.mkOption {
+      type = lib.types.bool;
+      description = "Enable natural scrolling";
+      default = config.chimera.input.mouse.scrolling.natural;
+    };
+    input.keyboard = {
+      layout = lib.mkOption {
+        type = lib.types.str;
+        description = "Keyboard layouts, comma seperated";
+        example = "us,de";
+        default = "us";
+      };
+      variant = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        description = "Keyboard layout variants, comma seperated";
+        example = "dvorak";
+        default = null;
+      };
+    };
+
+    input.keybinds.alternativeSearch.enable = lib.mkEnableOption "Use alt + space or SUPER + D to open search";
+
+    nvidia.enable = lib.mkEnableOption "Enable NVIDIA support";
+  };
+
+  config = lib.mkIf config.chimera.wayland.enable {
+    chimera.waybar.enable = lib.mkDefault true;
+    chimera.notifications.mako.enable = true;
+  };
+}
diff --git a/modules/home/hyprland/default.nix b/modules/home/wayland/hyprland/default.nix
similarity index 73%
rename from modules/home/hyprland/default.nix
rename to modules/home/wayland/hyprland/default.nix
index 6f70088..fab449e 100644
--- a/modules/home/hyprland/default.nix
+++ b/modules/home/wayland/hyprland/default.nix
@@ -6,72 +6,8 @@
   lib,
   ...
 }:
-let
-  lock = "${pkgs.waylock}/bin/waylock";
-in
 {
   options.chimera = {
-    input.mouse.scrolling.natural = lib.mkEnableOption "Enable natural scrolling";
-    input.touchpad.scrolling.natural = lib.mkOption {
-      type = lib.types.bool;
-      description = "Enable natural scrolling";
-      default = config.chimera.input.mouse.scrolling.natural;
-    };
-    input.keyboard = {
-      layout = lib.mkOption {
-        type = lib.types.str;
-        description = "Keyboard layouts, comma seperated";
-        example = "us,de";
-        default = "us";
-      };
-      variant = lib.mkOption {
-        type = lib.types.nullOr lib.types.str;
-        description = "Keyboard layout variants, comma seperated";
-        example = "dvorak";
-        default = null;
-      };
-    };
-
-    input.keybinds = {
-      alternativeSearch.enable = lib.mkEnableOption "Use alt + space or SUPER + D to open search";
-      volumeStep = lib.mkOption {
-        type = lib.types.int;
-        description = "Amount to increase volume by when media keys are pressed in %";
-        example = "5";
-        default = 5;
-      };
-      extraBinds = let
-        binds = lib.types.submodule {
-          options = {
-            meta = lib.mkOption {
-              type = lib.types.nullOr lib.types.str;
-              description = "Additional modifier keys space seperated";
-              default = null;
-            };
-            key = lib.mkOption {
-              type = lib.types.str;
-              description = "Final key";
-            };
-            function = lib.mkOption {
-              type = lib.types.str;
-              description = "Hyprland bind function";
-            };
-          };
-        };
-      in lib.mkOption {
-        type = lib.types.listOf binds;
-        description = "Extra keybinds to add";
-        default = [ ];
-      };
-    };
-    startupApplications = lib.mkOption {
-      type = lib.types.listOf lib.types.str;
-      description = "List of commands to run on hyprland start";
-      default = [ ];
-    };
-
-    nvidia.enable = lib.mkEnableOption "Enable NVIDIA support";
-
     hyprland = {
       enable = lib.mkEnableOption "Use hyprland as your window manager";
       monitors = lib.mkOption {
@@ -91,11 +27,48 @@
           default = 8;
         };
       };
+      startupApplications = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
+        description = "List of commands to run on hyprland start";
+        default = [ ];
+      };
+      keybinds = {
+        volumeStep = lib.mkOption {
+          type = lib.types.int;
+          description = "Amount to increase volume by when media keys are pressed in %";
+          example = "5";
+          default = 5;
+        };
+        extraBinds = let
+          binds = lib.types.submodule {
+            options = {
+              meta = lib.mkOption {
+                type = lib.types.nullOr lib.types.str;
+                description = "Additional modifier keys space seperated";
+                default = null;
+              };
+              key = lib.mkOption {
+                type = lib.types.str;
+                description = "Final key";
+              };
+              function = lib.mkOption {
+                type = lib.types.str;
+                description = "Hyprland bind function";
+              };
+            };
+          };
+        in lib.mkOption {
+          type = lib.types.listOf binds;
+          description = "Extra keybinds to add";
+          default = [ ];
+        };
+      };
     };
   };
-
-  config = lib.mkIf config.chimera.hyprland.enable {
-    chimera.waybar.enable = lib.mkDefault true;
+  config = lib.mkIf config.chimera.hyprland.enable (let
+    lock = "${pkgs.waylock}/bin/waylock";
+  in {
+    chimera.wayland.enable = true;
 
     programs.bash.profileExtra = lib.mkIf config.chimera.shell.bash.enable (lib.mkBefore ''
       if [ -z $WAYLAND_DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then
@@ -149,7 +122,7 @@
             "${pkgs.hyprpaper}/bin/hyprpaper"
             "hyprctl setcursor ${config.chimera.theme.cursor.name} ${builtins.toString config.chimera.theme.cursor.size}"
             "${pkgs.waybar}/bin/waybar"
-          ] ++ config.chimera.startupApplications;
+          ] ++ config.chimera.hyprland.startupApplications;
 
           monitor = config.chimera.hyprland.monitors ++ [ ",preferred,auto,1" ];
 
@@ -228,11 +201,11 @@
                 )
                 10
             ))
-            ++ (builtins.map (item: "SUPER${if isNull item.meta then "" else "_${item.meta}"}, ${item.key}, ${item.function}") config.chimera.input.keybinds.extraBinds)
+            ++ (builtins.map (item: "SUPER_${item.meta}, ${item.key}, ${item.function}") config.chimera.hyprland.keybinds.extraBinds)
             ++ [
               # Volume controls
-              ", XF86AudioRaiseVolume, exec, ${pkgs.pamixer}/bin/pamixer -i ${toString config.chimera.input.keybinds.volumeStep}"
-              ", XF86AudioLowerVolume, exec, ${pkgs.pamixer}/bin/pamixer -d ${toString config.chimera.input.keybinds.volumeStep}"
+              ", XF86AudioRaiseVolume, exec, ${pkgs.pamixer}/bin/pamixer -i ${toString config.chimera.hyprland.keybinds.volumeStep}"
+              ", XF86AudioLowerVolume, exec, ${pkgs.pamixer}/bin/pamixer -d ${toString config.chimera.hyprland.keybinds.volumeStep}"
               ", XF86AudioMute, exec, ${pkgs.pamixer}/bin/pamixer -t"
               # Pause and play
               ", XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl play-pause"
@@ -255,5 +228,5 @@
           ];
         };
     };
-  };
+  });
 }
diff --git a/modules/home/wayland/niri/default.nix b/modules/home/wayland/niri/default.nix
new file mode 100644
index 0000000..0695273
--- /dev/null
+++ b/modules/home/wayland/niri/default.nix
@@ -0,0 +1,277 @@
+{
+  pkgs,
+  config,
+  inputs,
+  system,
+  lib,
+  ...
+}: {
+
+  options.chimera.niri = {
+    enable = lib.mkEnableOption "Use Niri as your window manager";
+    monitors = lib.mkOption {
+      type = lib.types.attrsOf (lib.types.submodule {
+        options = {
+          enable = lib.mkOption {
+            type = lib.types.bool;
+            default = true;
+            description = "Enable this monitor";
+          };
+          mode = lib.mkOption {
+            type = lib.types.nullOr (lib.types.submodule {
+              options = {
+                width = lib.mkOption {
+                  type = lib.types.int;
+                };
+                height = lib.mkOption {
+                  type = lib.types.int;
+                };
+                refresh = lib.mkOption {
+                  type = lib.types.nullOr lib.types.float;
+                  default = null;
+                };
+              };
+            });
+            default = null;
+          };
+          position = lib.mkOption {
+            type = lib.types.nullOr (lib.types.submodule {
+              options = {
+                x = lib.mkOption {
+                  type = lib.types.int;
+                };
+                y = lib.mkOption {
+                  type = lib.types.int;
+                };
+              };
+            });
+            default = null;
+          };
+          scale = lib.mkOption {
+            type = lib.types.float;
+            default = 1.;
+          };
+          transform = {
+            flipped = lib.mkOption {
+              type = lib.types.bool;
+              default = false;
+            };
+            rotation = lib.mkOption {
+              type = lib.types.enum [ 0 90 180 270 ];
+              default = 0;
+            };
+          };
+          variable-refresh-rate = lib.mkEnableOption "Enable Variable Refresh Rate (AMD FreeSync / Nvidia G-Sync)";
+        };
+      });
+      description = "Atribute set of monitors";
+      default = { };
+    };
+  };
+
+  config = lib.mkIf config.chimera.niri.enable {
+    chimera.wayland.enable = true;
+
+    programs.bash.profileExtra = lib.mkIf config.chimera.shell.bash.enable (lib.mkBefore ''
+      if [ -z $WAYLAND_DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then
+        exec ${pkgs.systemd}/bin/systemd-cat -t niri ${pkgs.dbus}/bin/dbus-run-session ${config.programs.niri.package}/bin/niri --session
+      fi
+    '');
+
+    programs.zsh.profileExtra = lib.mkIf config.chimera.shell.zsh.enable (lib.mkBefore ''
+      if [ -z $WAYLAND_DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then
+        exec ${pkgs.systemd}/bin/systemd-cat -t niri ${pkgs.dbus}/bin/dbus-run-session ${config.programs.niri.package}/bin/niri --session
+      fi
+    '');
+
+    home.sessionVariables.NIXOS_OZONE_WL = "1";
+
+    programs.niri = let
+      mod = "Super";
+      mod1 = "Alt";
+      terminal = "${pkgs.kitty}/bin/kitty";
+      menu = (if config.chimera.runner.anyrun.enable then "${inputs.anyrun.packages.${system}.anyrun}/bin/anyrun" else "");
+
+      lock = ''${pkgs.swaylock}/bin/swaylock -i ${config.chimera.theme.wallpaper} -s fill'';
+    in {
+      enable = true;
+      package = pkgs.niri-stable;
+      settings = {
+        input.keyboard = {
+          track-layout = "window";
+          repeat-delay = 200;
+          repeat-rate = 25;
+          xkb = {
+            layout = config.chimera.input.keyboard.layout;
+            variant = config.chimera.input.keyboard.variant;
+          };
+        };
+
+        input.mouse.natural-scroll = config.chimera.input.mouse.scrolling.natural;
+        input.touchpad.natural-scroll = config.chimera.input.touchpad.scrolling.natural;
+
+        input.warp-mouse-to-focus = true;
+        input.focus-follows-mouse = true;
+
+        input.power-key-handling.enable = false;
+
+        binds = let
+          inherit (config.lib.niri) actions;
+
+          generateWorkspaceBindings = workspaceNumber: {
+            "${mod}+${builtins.toString (lib.mod workspaceNumber 10)}".action = actions.focus-workspace workspaceNumber;
+            "${mod}+Shift+${builtins.toString (lib.mod workspaceNumber 10)}".action = actions.move-column-to-workspace workspaceNumber;
+          };
+          joinAttrsetList = listOfAttrsets: lib.fold (a: b: a // b) {} listOfAttrsets;
+        in { # General Keybinds
+          "${mod}+Q".action = actions.close-window;
+          "${mod}+Return".action = actions.spawn "${terminal}";
+          "${mod}+L".action = actions.spawn ["sh" "-c" "${config.programs.niri.package}/bin/niri msg action do-screen-transition && ${lock}"];
+          "${mod}+P".action = actions.power-off-monitors;
+
+          "${mod}+R".action = actions.screenshot;
+          "${mod}+Ctrl+R".action = actions.screenshot-screen;
+          "${mod}+Shift+R".action = actions.screenshot-window;
+          "Print".action = actions.screenshot;
+          "Ctrl+Print".action = actions.screenshot-screen;
+          "Shift+Print".action = actions.screenshot-window;
+
+          "${mod}+Space".action = actions."switch-layout" "next";
+          "${mod}+Shift+Space".action = actions."switch-layout" "prev";
+
+          "${mod}+D" = lib.mkIf config.chimera.runner.enable { action = actions.spawn "${menu}"; };
+          "Alt+Space" = lib.mkIf (
+              config.chimera.runner.enable
+              && config.chimera.input.keybinds.alternativeSearch.enable
+            ) { action = actions.spawn "${menu}"; };
+          "${mod}+Shift+Slash".action = actions.show-hotkey-overlay;
+        } // ( # Workspace Keybinds
+          lib.pipe (lib.range 1 10) [
+            (map generateWorkspaceBindings)
+            joinAttrsetList
+          ]
+        ) // ( # Window Manipulation Bindings
+        {
+          "${mod}+BracketLeft".action = actions."consume-or-expel-window-left";
+          "${mod}+BracketRight".action = actions."consume-or-expel-window-right";
+          "${mod}+Shift+BracketLeft".action = actions."consume-window-into-column";
+          "${mod}+Shift+BracketRight".action = actions."expel-window-from-column";
+          "${mod}+Slash".action = actions."switch-preset-column-width";
+          "${mod}+${mod1}+F".action = actions."fullscreen-window";
+
+          # Focus
+          "${mod}+Up".action = actions."focus-window-or-workspace-up";
+          "${mod}+Down".action = actions."focus-window-or-workspace-down";
+
+          # Non Jump Movement
+          "${mod}+Shift+Up".action = actions."move-window-up-or-to-workspace-up";
+          "${mod}+Shift+Down".action = actions."move-window-down-or-to-workspace-down";
+          "${mod}+Shift+Left".action = actions."consume-or-expel-window-left";
+          "${mod}+Shift+Right".action = actions."consume-or-expel-window-right";
+
+          # To Monitor
+          "${mod}+Shift+Ctrl+Up".action = actions."move-window-to-monitor-up";
+          "${mod}+Shift+Ctrl+Down".action = actions."move-window-to-monitor-down";
+          "${mod}+Shift+Ctrl+Left".action = actions."move-window-to-monitor-left";
+          "${mod}+Shift+Ctrl+Right".action = actions."move-window-to-monitor-right";
+
+          # To Workspace
+          "${mod}+Ctrl+Up".action = actions."move-window-to-workspace-up";
+          "${mod}+Ctrl+Down".action = actions."move-window-to-workspace-down";
+
+          # Sizing
+          "${mod}+Equal".action = actions."set-window-height" "+5%";
+          "${mod}+Minus".action = actions."set-window-height" "-5%";
+        }) // ( # Column Manipulation Bindings
+        {
+          # Focus
+          "${mod}+Left".action = actions."focus-column-left";
+          "${mod}+Right".action = actions."focus-column-right";
+          "${mod}+${mod1}+C".action = actions."center-column";
+          "${mod}+F".action = actions."maximize-column";
+
+          # Non Monitor Movement
+          "${mod}+${mod1}+Shift+Up".action = actions."move-column-to-workspace-up";
+          "${mod}+${mod1}+Shift+Down".action = actions."move-column-to-workspace-down";
+          "${mod}+${mod1}+Shift+Left".action = actions."move-column-left";
+          "${mod}+${mod1}+Shift+Right".action = actions."move-column-right";
+
+          # To Monitor
+          "${mod}+${mod1}+Shift+Ctrl+Up".action = actions."move-column-to-monitor-up";
+          "${mod}+${mod1}+Shift+Ctrl+Down".action = actions."move-column-to-monitor-down";
+          "${mod}+${mod1}+Shift+Ctrl+Left".action = actions."move-column-to-monitor-left";
+          "${mod}+${mod1}+Shift+Ctrl+Right".action = actions."move-column-to-monitor-right";
+
+          # Sizing
+          "${mod}+${mod1}+Equal".action = actions."set-column-width" "+5%";
+          "${mod}+${mod1}+Minus".action = actions."set-column-width" "-5%";
+        }) // ( # Workspace Manipulation Bindings
+        {
+          # Focus
+          "${mod}+Page_Up".action = actions."focus-workspace-up";
+          "${mod}+Page_Down".action = actions."focus-workspace-down";
+
+          # Within Itself
+          "${mod}+Shift+Page_Up".action = actions."move-workspace-up";
+          "${mod}+Shift+Page_Down".action = actions."move-workspace-down";
+
+          # To Monitor
+          "${mod}+Shift+Ctrl+Page_Up".action = actions."move-workspace-to-monitor-up";
+          "${mod}+Shift+Ctrl+Page_Down".action = actions."move-workspace-to-monitor-down";
+          "${mod}+Shift+Ctrl+Home".action = actions."move-workspace-to-monitor-left";
+          "${mod}+Shift+Ctrl+End".action = actions."move-workspace-to-monitor-right";
+        }) // { # Audio
+          "XF86AudioRaiseVolume" = {
+            allow-when-locked = true;
+            action = actions.spawn "${pkgs.wireplumber}/bin/wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05+";
+          };
+          "XF86AudioLowerVolume" = {
+            allow-when-locked = true;
+            action = actions.spawn "${pkgs.wireplumber}/bin/wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05-";
+          };
+          "XF86AudioMute" = {
+            allow-when-locked = true;
+            action = actions.spawn "${pkgs.wireplumber}/bin/wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle";
+          };
+          "XF86AudioMicMute" = {
+            allow-when-locked = true;
+            action = actions.spawn "${pkgs.wireplumber}/bin/wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle";
+          };
+        };
+
+        outputs = config.chimera.niri.monitors;
+
+        cursor = {
+          size = config.chimera.theme.cursor.size;
+          theme = config.chimera.theme.cursor.name;
+        };
+
+        layout = {
+          gaps = 16;
+
+          center-focused-column = "on-overflow";
+
+          preset-column-widths = [
+            { proportion = 1. / 4.; }
+            { proportion = 1. / 3.; }
+            { proportion = 1. / 2.; }
+            { proportion = 2. / 3.; }
+          ]; # TODO: clicks to PR a docs update for niri-flake
+        };
+
+        prefer-no-csd = true; # No "client-side-decorations" (i.e. client-side window open/close buttons)
+        hotkey-overlay.skip-at-startup = true;
+        screenshot-path = null;
+
+        spawn-at-startup = [
+          {
+            command = [ "${pkgs.waybar}/bin/waybar" ];
+          }
+          {
+            command = [ "${pkgs.swaybg}/bin/swaybg" "-i" "${config.chimera.theme.wallpaper}" "-m" "fill" ];
+          }
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/nixos/compositors/default.nix b/modules/nixos/compositors/default.nix
new file mode 100644
index 0000000..3d4f109
--- /dev/null
+++ b/modules/nixos/compositors/default.nix
@@ -0,0 +1,28 @@
+{ pkgs, lib, config, ... }:
+{
+  options.chimera.compositors = {
+    hyprland.enable = lib.mkEnableOption "Enable if at least 1 user on the system uses hyprland";
+    niri.enable = lib.mkEnableOption "Enable if at least 1 user on the system uses niri";
+  };
+
+  config = {
+    fonts.enableDefaultPackages = lib.mkDefault true;
+    hardware.opengl.enable = lib.mkDefault true;
+
+    programs.hyprland.enable = config.chimera.compositors.hyprland.enable;
+
+    xdg.portal.enable = lib.mkIf config.chimera.compositors.niri.enable true;
+    xdg.portal.config.common.default = "*"; # HACK: fixme @minion3665, try removing this and check the warning
+
+    xdg.portal.extraPortals = lib.mkIf (
+      config.chimera.compositors.hyprland.enable
+      || config.chimera.compositors.niri.enable
+    ) [ pkgs.xdg-desktop-portal-gtk ];
+
+    programs.dconf.enable = true; # FIXME: should be set to true if gtk is being used
+
+    security.polkit.enable = true;
+
+    chimera.xdg-open.enable = lib.mkDefault true;
+  };
+}
diff --git a/modules/nixos/hyprland/default.nix b/modules/nixos/hyprland/default.nix
deleted file mode 100644
index 80a7733..0000000
--- a/modules/nixos/hyprland/default.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-{ pkgs, lib, ... }:
-{
-  programs.hyprland.enable = true;
-  xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
-  chimera.xdg-open.enable = lib.mkDefault true;
-}
diff --git a/modules/nixos/users/default.nix b/modules/nixos/users/default.nix
index 4f8b70d..6556aeb 100644
--- a/modules/nixos/users/default.nix
+++ b/modules/nixos/users/default.nix
@@ -37,5 +37,8 @@
     ];
   };
 
+   # TODO: can we determine these from what home-manager says our users are using?
+  security.pam.services.swaylock = { };
   security.pam.services.waylock = { };
+  security.pam.services.gtklock = { };
 }
diff --git a/systems/x86_64-linux/greylag/compositor/default.nix b/systems/x86_64-linux/greylag/compositor/default.nix
new file mode 100644
index 0000000..d6a852e
--- /dev/null
+++ b/systems/x86_64-linux/greylag/compositor/default.nix
@@ -0,0 +1,3 @@
+{
+  chimera.compositors.niri.enable = true;
+}
diff --git a/systems/x86_64-linux/greylag/default.nix b/systems/x86_64-linux/greylag/default.nix
index 0aa5820..ee66083 100644
--- a/systems/x86_64-linux/greylag/default.nix
+++ b/systems/x86_64-linux/greylag/default.nix
@@ -4,6 +4,7 @@
     ./boot/filesystems
     ./boot/initrd
     ./boot/logs
+    ./compositor
     ./console
     ./cpu
     ./credentials/gnome-keyring
diff --git a/systems/x86_64-linux/saurosuchus/compositor/default.nix b/systems/x86_64-linux/saurosuchus/compositor/default.nix
new file mode 100644
index 0000000..6ade8c5
--- /dev/null
+++ b/systems/x86_64-linux/saurosuchus/compositor/default.nix
@@ -0,0 +1,3 @@
+{
+  chimera.compositors.hyprland.enable = true;
+}
diff --git a/systems/x86_64-linux/saurosuchus/default.nix b/systems/x86_64-linux/saurosuchus/default.nix
index a0106ab..6341e28 100644
--- a/systems/x86_64-linux/saurosuchus/default.nix
+++ b/systems/x86_64-linux/saurosuchus/default.nix
@@ -2,6 +2,7 @@
 {
   imports = [
     ./console
+    ./compositor
     ./games
     ./hardware/boot
     ./hardware/cpu
diff --git a/systems/x86_64-linux/shorthair/hardware/boot/default.nix b/systems/x86_64-linux/shorthair/boot/default.nix
similarity index 100%
rename from systems/x86_64-linux/shorthair/hardware/boot/default.nix
rename to systems/x86_64-linux/shorthair/boot/default.nix
diff --git a/systems/x86_64-linux/shorthair/compositor/default.nix b/systems/x86_64-linux/shorthair/compositor/default.nix
new file mode 100644
index 0000000..d6a852e
--- /dev/null
+++ b/systems/x86_64-linux/shorthair/compositor/default.nix
@@ -0,0 +1,3 @@
+{
+  chimera.compositors.niri.enable = true;
+}
diff --git a/systems/x86_64-linux/shorthair/default.nix b/systems/x86_64-linux/shorthair/default.nix
index ddf78e2..8767771 100644
--- a/systems/x86_64-linux/shorthair/default.nix
+++ b/systems/x86_64-linux/shorthair/default.nix
@@ -1,10 +1,11 @@
 { ... }:
 {
   imports = [
+    ./boot
     ./backlight
+    ./compositor
     ./console
     ./games
-    ./hardware/boot
     ./hardware/cpu
     ./hardware/filesystems
     ./networking