From de11629969afe3176b05a66204145a7874e23e62 Mon Sep 17 00:00:00 2001 From: uttarayan21 Date: Sat, 16 Aug 2025 19:36:17 +0530 Subject: [PATCH] feat: Added home manager module --- HOME-MANAGER.md | 228 ++++++++++++++++++++++++++++++++ example-home-manager-config.nix | 79 +++++++++++ flake.nix | 7 +- home-manager-module.nix | 213 +++++++++++++++++++++++++++++ 4 files changed, 526 insertions(+), 1 deletion(-) create mode 100644 HOME-MANAGER.md create mode 100644 example-home-manager-config.nix create mode 100644 home-manager-module.nix diff --git a/HOME-MANAGER.md b/HOME-MANAGER.md new file mode 100644 index 0000000..3b0de09 --- /dev/null +++ b/HOME-MANAGER.md @@ -0,0 +1,228 @@ +# Home Manager Module for Hyprmonitors + +This directory contains a Home Manager module for installing and configuring hyprmonitors, a Rust web server that provides an HTTP API for controlling Hyprland desktop monitors. + +## What is Hyprmonitors? + +Hyprmonitors is a lightweight web server that exposes a RESTful API to control your Hyprland monitors using `hyprctl dispatch dpms` commands. It allows you to: + +- Turn all monitors on/off via HTTP requests +- Control individual monitors by name +- Get current monitor status +- Integrate monitor control into web applications or scripts + +## Installation + +### Method 1: Direct Import + +Add the module to your Home Manager configuration: + +```nix +{ + imports = [ + /path/to/hyprmonitors/home-manager-module.nix + ]; + + # Enable Hyprland (required) + wayland.windowManager.hyprland.enable = true; + + # Enable hyprmonitors service + services.hyprmonitors.enable = true; +} +``` + +### Method 2: As a Flake Input + +Add hyprmonitors as a flake input in your `flake.nix`: + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + home-manager.url = "github:nix-community/home-manager"; + hyprmonitors.url = "path:/path/to/hyprmonitors"; + }; + + outputs = { nixpkgs, home-manager, hyprmonitors, ... }: { + homeConfigurations.youruser = home-manager.lib.homeManagerConfiguration { + modules = [ + hyprmonitors.homeManagerModules.default + { + wayland.windowManager.hyprland.enable = true; + services.hyprmonitors.enable = true; + } + ]; + }; + }; +} +``` + +## Configuration Options + +The module provides several configuration options: + +```nix +services.hyprmonitors = { + enable = true; # Enable the service + + host = "127.0.0.1"; # Host to bind to (default: 127.0.0.1) + port = 3000; # Port to bind to (default: 3000) + + logLevel = "info"; # Log level: error, warn, info, debug, trace + + package = pkgs.hyprmonitors; # Override the package (optional) + + environmentVariables = { # Additional environment variables + HYPRLAND_INSTANCE_SIGNATURE = "your-signature"; + }; + + settings = { # Future configuration options + timeout_seconds = 30; + }; +}; +``` + +## Usage + +Once installed and configured, the module provides several convenient tools: + +### Shell Aliases + +The module automatically adds these shell aliases: + +- `hyprmonitors-start` - Start the service +- `hyprmonitors-stop` - Stop the service +- `hyprmonitors-restart` - Restart the service +- `hyprmonitors-status` - Check service status +- `hyprmonitors-logs` - View service logs +- `hyprmonitors-test` - Test if the API is responding + +### API Helper Script + +The `hyprmonitors-curl` script provides easy access to the API: + +```bash +# Check if the service is running +hyprmonitors-curl health + +# Get status of all monitors +hyprmonitors-curl status + +# Turn all monitors on/off +hyprmonitors-curl on +hyprmonitors-curl off + +# Control specific monitors +hyprmonitors-curl on DP-1 +hyprmonitors-curl off HDMI-A-1 +``` + +### Manual API Access + +You can also use curl directly: + +```bash +# Health check +curl http://localhost:3000/health + +# Monitor status +curl http://localhost:3000/monitors/status + +# Turn all monitors on/off +curl -X POST http://localhost:3000/monitors/on +curl -X POST http://localhost:3000/monitors/off + +# Control specific monitors +curl -X POST http://localhost:3000/monitors/DP-1/on +curl -X POST http://localhost:3000/monitors/HDMI-A-1/off +``` + +## Service Management + +The service is automatically managed by systemd: + +```bash +# Check service status +systemctl --user status hyprmonitors.service + +# View logs +journalctl --user -u hyprmonitors.service -f + +# Manual start/stop (if needed) +systemctl --user start hyprmonitors.service +systemctl --user stop hyprmonitors.service +``` + +## Integration with Hyprland + +You can integrate hyprmonitors with Hyprland keybinds: + +```nix +wayland.windowManager.hyprland.settings = { + bind = [ + # Turn all monitors off/on + "SUPER SHIFT, M, exec, hyprmonitors-curl off" + "SUPER CTRL, M, exec, hyprmonitors-curl on" + + # Control specific monitors + "SUPER SHIFT, 1, exec, hyprmonitors-curl off DP-1" + "SUPER SHIFT, 2, exec, hyprmonitors-curl off HDMI-A-1" + "SUPER CTRL, 1, exec, hyprmonitors-curl on DP-1" + "SUPER CTRL, 2, exec, hyprmonitors-curl on HDMI-A-1" + ]; +}; +``` + +## Desktop Entry + +The module creates a desktop entry "Hyprmonitors Control" that opens the health endpoint in your browser for quick access to verify the service is running. + +## Finding Monitor Names + +To find your monitor names for use with the API: + +```bash +hyprctl monitors +``` + +Common monitor names include: +- `DP-1`, `DP-2` (DisplayPort) +- `HDMI-A-1`, `HDMI-A-2` (HDMI) +- `eDP-1` (Laptop screen) + +## Troubleshooting + +### Service won't start + +1. Ensure Hyprland is running and properly configured +2. Check that the port isn't already in use +3. View logs: `journalctl --user -u hyprmonitors.service` + +### API requests fail + +1. Verify the service is running: `hyprmonitors-test` +2. Check monitor names: `hyprctl monitors` +3. Ensure you're running commands in a Hyprland session + +### Permission issues + +The service runs as your user and should automatically have access to Hyprland's IPC socket. If you encounter permission issues, ensure: + +1. You're running the service as the same user running Hyprland +2. The `XDG_RUNTIME_DIR` environment variable is properly set + +## Security Considerations + +- The service binds to localhost by default for security +- If you need remote access, change the host to `0.0.0.0` but ensure proper firewall configuration +- The service has restricted permissions and resource limits applied + +## Complete Example + +See `example-home-manager-config.nix` for a complete working example configuration. + +## Requirements + +- NixOS or Nix with Home Manager +- Hyprland window manager +- The module automatically handles all dependencies \ No newline at end of file diff --git a/example-home-manager-config.nix b/example-home-manager-config.nix new file mode 100644 index 0000000..188fb51 --- /dev/null +++ b/example-home-manager-config.nix @@ -0,0 +1,79 @@ +{ + # Example Home Manager configuration for hyprmonitors + # Add this to your home.nix or import it as a module + + imports = [ + # Import the hyprmonitors module + ./home-manager-module.nix + ]; + + # Enable Hyprland (required for hyprmonitors) + wayland.windowManager.hyprland = { + enable = true; + # Add your Hyprland configuration here + }; + + # Configure hyprmonitors service + services.hyprmonitors = { + enable = true; + + # Optional: customize host and port (defaults shown) + host = "127.0.0.1"; + port = 3000; + + # Optional: set log level (default: "info") + logLevel = "info"; + + # Optional: add environment variables + environmentVariables = { + # Example: if you need to set specific Hyprland instance + # HYPRLAND_INSTANCE_SIGNATURE = "your-signature-here"; + }; + + # Optional: override the package (if you want to use a custom build) + # package = pkgs.hyprmonitors; # Uses default from module + }; + + # The module automatically adds these shell aliases: + # - hyprmonitors-start: Start the service + # - hyprmonitors-stop: Stop the service + # - hyprmonitors-restart: Restart the service + # - hyprmonitors-status: Check service status + # - hyprmonitors-logs: View service logs + # - hyprmonitors-test: Test if the API is responding + + # The module also adds a hyprmonitors-curl helper script for API testing: + # - hyprmonitors-curl health + # - hyprmonitors-curl status + # - hyprmonitors-curl on [monitor-name] + # - hyprmonitors-curl off [monitor-name] + + # Example of additional Hyprland configuration that works well with hyprmonitors + wayland.windowManager.hyprland.settings = { + # Your Hyprland config here + monitor = [ + "DP-1,1920x1080@60,0x0,1" + "HDMI-A-1,1920x1080@60,1920x0,1" + ]; + + # Example keybinds for monitor control (optional) + bind = [ + # Turn all monitors off/on + "SUPER SHIFT, M, exec, hyprmonitors-curl off" + "SUPER CTRL, M, exec, hyprmonitors-curl on" + + # Turn specific monitors off/on + "SUPER SHIFT, 1, exec, hyprmonitors-curl off DP-1" + "SUPER SHIFT, 2, exec, hyprmonitors-curl off HDMI-A-1" + "SUPER CTRL, 1, exec, hyprmonitors-curl on DP-1" + "SUPER CTRL, 2, exec, hyprmonitors-curl on HDMI-A-1" + ]; + }; + + # Optional: Install additional packages that work well with hyprmonitors + home.packages = with pkgs; [ + curl + jq + # Add other packages you need + ]; +} diff --git a/flake.nix b/flake.nix index 65f7def..44fde30 100644 --- a/flake.nix +++ b/flake.nix @@ -69,5 +69,10 @@ maintainers = []; }; }; - }); + }) + // { + # Home Manager module + homeManagerModules.default = import ./home-manager-module.nix; + homeManagerModules.hyprmonitors = import ./home-manager-module.nix; + }; } diff --git a/home-manager-module.nix b/home-manager-module.nix new file mode 100644 index 0000000..8217a63 --- /dev/null +++ b/home-manager-module.nix @@ -0,0 +1,213 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.services.hyprmonitors; + + hyprmonitors = pkgs.rustPlatform.buildRustPackage { + pname = "hyprmonitors"; + version = "0.1.0"; + + src = ./.; + + cargoLock = { + lockFile = ./Cargo.lock; + }; + + nativeBuildInputs = with pkgs; [ + pkg-config + ]; + + buildInputs = with pkgs; [ + openssl + ]; + + meta = with lib; { + description = "Hyprland monitor control server"; + homepage = "https://github.com/your-username/hyprmonitors"; + license = licenses.mit; + maintainers = []; + platforms = platforms.linux; + }; + }; +in { + options.services.hyprmonitors = { + enable = mkEnableOption "Hyprland monitor control server"; + + package = mkOption { + type = types.package; + default = hyprmonitors; + defaultText = literalExpression "pkgs.hyprmonitors"; + description = "The hyprmonitors package to use."; + }; + + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Host address to bind the server to."; + }; + + port = mkOption { + type = types.port; + default = 3000; + description = "Port to bind the server to."; + }; + + logLevel = mkOption { + type = types.enum ["error" "warn" "info" "debug" "trace"]; + default = "info"; + description = "Log level for the server."; + }; + + environmentVariables = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { + HYPRLAND_INSTANCE_SIGNATURE = "your-signature"; + }; + description = "Additional environment variables to set for the service."; + }; + + settings = mkOption { + type = types.attrs; + default = {}; + example = { + cors_origins = ["http://localhost:8080"]; + timeout_seconds = 30; + }; + description = "Additional configuration settings (if supported by future versions)."; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = config.wayland.windowManager.hyprland.enable or false; + message = "hyprmonitors requires Hyprland to be enabled. Set wayland.windowManager.hyprland.enable = true;"; + } + ]; + + home.packages = [cfg.package]; + + systemd.user.services.hyprmonitors = { + Unit = { + Description = "Hyprland Monitor Control Server"; + Documentation = "https://github.com/your-username/hyprmonitors"; + After = ["graphical-session.target" "hyprland-session.target"]; + Wants = ["graphical-session.target"]; + PartOf = ["hyprland-session.target"]; + }; + + Service = { + Type = "simple"; + ExecStart = "${cfg.package}/bin/hyprmonitors"; + Restart = "always"; + RestartSec = 5; + + # Environment variables + Environment = + [ + "RUST_LOG=${cfg.logLevel}" + "HYPRMONITORS_HOST=${cfg.host}" + "HYPRMONITORS_PORT=${toString cfg.port}" + "HYPRMONITORS_LOG_LEVEL=${cfg.logLevel}" + ] + ++ (mapAttrsToList (name: value: "${name}=${value}") cfg.environmentVariables); + + # Security settings + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = "read-only"; + ProtectSystem = "strict"; + ReadWritePaths = ["/tmp"]; + + # Resource limits + LimitNOFILE = 1024; + MemoryMax = "128M"; + }; + + Install = { + WantedBy = ["hyprland-session.target"]; + }; + }; + + # Create a target for Hyprland session if it doesn't exist + systemd.user.targets.hyprland-session = mkIf (!config.systemd.user.targets ? hyprland-session) { + Unit = { + Description = "Hyprland session"; + BindsTo = ["graphical-session.target"]; + Wants = ["graphical-session.target"]; + After = ["graphical-session.target"]; + }; + }; + + # Optional: Add a desktop entry for manual control + xdg.desktopEntries.hyprmonitors-control = mkIf (cfg.enable && cfg.host == "127.0.0.1") { + name = "Hyprmonitors Control"; + comment = "Control Hyprland monitors via web interface"; + exec = "${pkgs.xdg-utils}/bin/xdg-open http://${cfg.host}:${toString cfg.port}/health"; + icon = "preferences-desktop-display"; + categories = ["Settings" "System"]; + terminal = false; + type = "Application"; + }; + + # Add some useful aliases for controlling the service + home.shellAliases = mkIf cfg.enable { + hyprmonitors-start = "systemctl --user start hyprmonitors.service"; + hyprmonitors-stop = "systemctl --user stop hyprmonitors.service"; + hyprmonitors-restart = "systemctl --user restart hyprmonitors.service"; + hyprmonitors-status = "systemctl --user status hyprmonitors.service"; + hyprmonitors-logs = "journalctl --user -u hyprmonitors.service -f"; + hyprmonitors-test = "curl http://${cfg.host}:${toString cfg.port}/health"; + }; + + # Add some helper scripts + home.packages = mkIf cfg.enable [ + (pkgs.writeShellScriptBin "hyprmonitors-curl" '' + #!/usr/bin/env bash + # Helper script for testing hyprmonitors API + + BASE_URL="http://${cfg.host}:${toString cfg.port}" + + case "$1" in + health) + curl -s "$BASE_URL/health" | ${pkgs.jq}/bin/jq + ;; + status) + curl -s "$BASE_URL/monitors/status" | ${pkgs.jq}/bin/jq + ;; + on) + if [ -n "$2" ]; then + curl -s -X POST "$BASE_URL/monitors/$2/on" | ${pkgs.jq}/bin/jq + else + curl -s -X POST "$BASE_URL/monitors/on" | ${pkgs.jq}/bin/jq + fi + ;; + off) + if [ -n "$2" ]; then + curl -s -X POST "$BASE_URL/monitors/$2/off" | ${pkgs.jq}/bin/jq + else + curl -s -X POST "$BASE_URL/monitors/off" | ${pkgs.jq}/bin/jq + fi + ;; + *) + echo "Usage: hyprmonitors-curl {health|status|on [monitor]|off [monitor]}" + echo "" + echo "Examples:" + echo " hyprmonitors-curl health" + echo " hyprmonitors-curl status" + echo " hyprmonitors-curl on" + echo " hyprmonitors-curl off" + echo " hyprmonitors-curl on DP-1" + echo " hyprmonitors-curl off HDMI-A-1" + exit 1 + ;; + esac + '') + ]; + }; +}