116 lines
3.3 KiB
Nix
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" ];
|
|
};
|
|
};
|
|
};
|
|
}
|