nixos/modules/home-manager/polling-rate.nix
2026-01-21 23:58:24 +01:00

116 lines
3.3 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.polling-rate-switcher;
switcherScript = pkgs.writeShellScriptBin "polling-rate-switcher" ''
export PATH="${
lib.makeBinPath [
pkgs.coreutils
pkgs.gnugrep
pkgs.jq
pkgs.systemd
pkgs.niri
pkgs.gawk
]
}:$PATH"
# Find the Razer device object path via D-Bus
# We look for an object path that looks like /org/razer/device/...
# AND supports setPollRate
echo "Searching for OpenRazer device on D-Bus..."
RAZER_OBJ=""
# Simple retry loop in case the service starts before the device is ready
for i in {1..10}; do
# Find matches, then check introspection for setPollRate method
CANDIDATES=$(busctl --user tree --list org.razer | grep '^/org/razer/device/') || true
for obj in $CANDIDATES; do
if busctl --user introspect org.razer "$obj" 2>/dev/null | grep -q "setPollRate"; then
RAZER_OBJ="$obj"
break
fi
done
if [ -n "$RAZER_OBJ" ]; then
break
fi
sleep 2
done
if [ -z "$RAZER_OBJ" ]; then
echo "No Razer device found on D-Bus org.razer that supports setPollRate after retries."
exit 1
fi
echo "Found Razer device at $RAZER_OBJ"
# Function to set polling rate via D-Bus
set_rate() {
local rate="$1"
# Call OpenRazer method: setPollRate(int)
busctl --user call org.razer "$RAZER_OBJ" razer.device.misc setPollRate i "$rate"
}
current_rate=""
# Listen to Niri event stream
# We filter for WindowFocusChanged events, then query the state.
# This avoids parsing the complex event stream directly for state.
niri msg --json event-stream | jq --unbuffered -c 'select(.WindowFocusChanged) | .WindowFocusChanged.id' | while read -r _; do
# Query current focused window
# The focused-window output is wrapped in Ok.FocusedWindow
fw=$(niri msg --json focused-window 2>/dev/null || true)
# If no window is focused or command fails, these will be empty
app_id=$(echo "$fw" | jq -r '.Ok.FocusedWindow.app_id // ""' | tr '[:upper:]' '[:lower:]')
title=$(echo "$fw" | jq -r '.Ok.FocusedWindow.title // ""' | tr '[:upper:]' '[:lower:]')
# Default rate
target_rate=1000
# High polling rate for specific games
if [[ "$app_id" == *"overwatch"* || "$title" == *"overwatch"* ]]; then
target_rate=8000
fi
if [ "$target_rate" != "$current_rate" ]; then
echo "Switching rate to $target_rate Hz (App: $app_id, Title: $title)"
set_rate "$target_rate"
current_rate="$target_rate"
fi
done
'';
in
{
options.services.polling-rate-switcher = {
enable = lib.mkEnableOption "Polling Rate Switcher Service";
};
config = lib.mkIf cfg.enable {
systemd.user.services.polling-rate-switcher = {
Unit = {
Description = "Auto-switch Razer Polling Rate for Overwatch";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${switcherScript}/bin/polling-rate-switcher";
Restart = "always";
RestartSec = 5;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}