Nix · 7759 bytes Raw Blame History
1 {
2 description = "HyprKVM - Hyprland-native software KVM switch";
3
4 inputs = {
5 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6 flake-utils.url = "github:numtide/flake-utils";
7 rust-overlay = {
8 url = "github:oxalica/rust-overlay";
9 inputs.nixpkgs.follows = "nixpkgs";
10 };
11 };
12
13 outputs = { self, nixpkgs, flake-utils, rust-overlay }:
14 let
15 # System-independent outputs
16 systemIndependent = {
17 # Home-manager module for easy service management
18 homeManagerModules.default = { config, lib, pkgs, ... }:
19 let
20 cfg = config.services.hyprkvm;
21 in {
22 options.services.hyprkvm = {
23 enable = lib.mkEnableOption "HyprKVM daemon";
24
25 package = lib.mkOption {
26 type = lib.types.package;
27 default = self.packages.${pkgs.system}.default;
28 defaultText = lib.literalExpression "pkgs.hyprkvm";
29 description = "The HyprKVM package to use.";
30 };
31
32 settings = lib.mkOption {
33 type = lib.types.attrs;
34 default = {};
35 description = ''
36 Configuration for HyprKVM. Will be written to
37 ~/.config/hyprkvm/hyprkvm.toml
38 '';
39 example = lib.literalExpression ''
40 {
41 machines = {
42 self_name = "my-machine";
43 neighbors = [
44 { name = "other-machine"; direction = "right"; address = "192.168.1.100:24850"; }
45 ];
46 };
47 network = {
48 listen_port = 24850;
49 };
50 }
51 '';
52 };
53
54 extraFlags = lib.mkOption {
55 type = lib.types.listOf lib.types.str;
56 default = [];
57 description = "Extra command-line flags to pass to the daemon.";
58 };
59 };
60
61 config = lib.mkIf cfg.enable {
62 # Install the package
63 home.packages = [ cfg.package ];
64
65 # Generate config file if settings provided
66 xdg.configFile."hyprkvm/hyprkvm.toml" = lib.mkIf (cfg.settings != {}) {
67 source = (pkgs.formats.toml {}).generate "hyprkvm.toml" cfg.settings;
68 };
69
70 # Systemd user service
71 systemd.user.services.hyprkvm = {
72 Unit = {
73 Description = "HyprKVM Daemon - Hyprland-native software KVM switch";
74 After = [ "graphical-session.target" ];
75 PartOf = [ "graphical-session.target" ];
76 };
77
78 Service = {
79 Type = "simple";
80 ExecStart = "${cfg.package}/bin/hyprkvm daemon ${lib.concatStringsSep " " cfg.extraFlags}";
81 Restart = "on-failure";
82 RestartSec = 1;
83 # Exit code 75 triggers restart for direction changes
84 RestartForceExitStatus = [ 75 ];
85 Environment = [ "WAYLAND_DISPLAY=wayland-1" ];
86 };
87
88 Install = {
89 WantedBy = [ "graphical-session.target" ];
90 };
91 };
92 };
93 };
94
95 # Alias for backwards compatibility
96 homeManagerModules.hyprkvm = self.homeManagerModules.default;
97 };
98
99 # System-specific outputs (packages, devShells)
100 systemSpecific = flake-utils.lib.eachDefaultSystem (system:
101 let
102 pkgs = import nixpkgs {
103 inherit system;
104 overlays = [ rust-overlay.overlays.default ];
105 };
106
107 # Build dependencies
108 buildDeps = with pkgs; [
109 pkg-config
110 ];
111
112 # Runtime/library dependencies
113 libDeps = with pkgs; [
114 # Wayland
115 wayland
116 wayland-protocols
117
118 # For smithay-client-toolkit
119 libxkbcommon
120
121 # TLS
122 openssl
123
124 # GUI (Iced) dependencies
125 vulkan-loader
126 libGL
127 xorg.libX11
128 xorg.libXcursor
129 xorg.libXrandr
130 xorg.libXi
131 ];
132
133 rustToolchain = pkgs.rust-bin.stable.latest.default.override {
134 extensions = [ "rust-src" "rust-analyzer" ];
135 };
136 in
137 {
138 # Development shell
139 devShells.default = pkgs.mkShell {
140 buildInputs = buildDeps ++ libDeps ++ [
141 rustToolchain
142 ];
143
144 # Set up pkg-config paths
145 PKG_CONFIG_PATH = pkgs.lib.makeSearchPath "lib/pkgconfig" libDeps;
146
147 # For wayland-scanner
148 WAYLAND_PROTOCOLS = "${pkgs.wayland-protocols}/share/wayland-protocols";
149
150 # Graphics library paths for GUI
151 LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath libDeps;
152
153 shellHook = ''
154 echo "HyprKVM development shell"
155 echo "Rust: $(rustc --version)"
156 echo ""
157 echo "Run 'cargo build' to compile"
158 '';
159 };
160
161 # Package (without GUI for smaller binary)
162 packages.default = pkgs.rustPlatform.buildRustPackage {
163 pname = "hyprkvm";
164 version = "0.6.1";
165 src = ./.;
166
167 cargoLock = {
168 lockFile = ./Cargo.lock;
169 };
170
171 nativeBuildInputs = buildDeps;
172 buildInputs = libDeps;
173
174 # Builds both hyprkvm (daemon) and hyprkvm-ctl (CLI)
175 # buildRustPackage automatically installs all workspace binaries
176
177 meta = with pkgs.lib; {
178 description = "Hyprland-native software KVM switch";
179 longDescription = ''
180 HyprKVM enables seamless keyboard/mouse control transfer between
181 Linux machines running Hyprland. Move past your last workspace
182 to switch to another machine.
183 '';
184 homepage = "https://github.com/tenseleyFlow/hyprKVM";
185 license = licenses.mit;
186 platforms = platforms.linux;
187 mainProgram = "hyprkvm";
188 };
189 };
190
191 # Package with GUI support
192 packages.gui = pkgs.rustPlatform.buildRustPackage {
193 pname = "hyprkvm";
194 version = "0.6.1";
195 src = ./.;
196
197 cargoLock = {
198 lockFile = ./Cargo.lock;
199 };
200
201 nativeBuildInputs = buildDeps ++ [ pkgs.makeWrapper ];
202 buildInputs = libDeps;
203
204 # Build with GUI feature
205 buildFeatures = [ "gui" ];
206
207 # Wrap binary to include graphics library paths
208 postInstall = ''
209 wrapProgram $out/bin/hyprkvm \
210 --prefix LD_LIBRARY_PATH : ${pkgs.lib.makeLibraryPath libDeps}
211 '';
212
213 meta = with pkgs.lib; {
214 description = "Hyprland-native software KVM switch (with GUI)";
215 longDescription = ''
216 HyprKVM enables seamless keyboard/mouse control transfer between
217 Linux machines running Hyprland. Move past your last workspace
218 to switch to another machine.
219
220 This package includes the GUI configuration tool.
221 '';
222 homepage = "https://github.com/tenseleyFlow/hyprKVM";
223 license = licenses.mit;
224 platforms = platforms.linux;
225 mainProgram = "hyprkvm";
226 };
227 };
228 }
229 );
230 in
231 # Merge system-independent and system-specific outputs
232 systemIndependent // systemSpecific;
233 }