gaming: arctis-sound-manager + quickshell ANC switcher
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1df7ebb302
commit
027858ff1e
4 changed files with 143 additions and 1 deletions
23
flake.lock
generated
23
flake.lock
generated
|
|
@ -1,5 +1,27 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"arctis-sound-manager": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"dir": "nix",
|
||||||
|
"lastModified": 1785229953,
|
||||||
|
"narHash": "sha256-5oezrSj8OxNeMUN0IxjdKh4t2D9jn1BOainmBRNeXgE=",
|
||||||
|
"owner": "loteran",
|
||||||
|
"repo": "Arctis-Sound-Manager",
|
||||||
|
"rev": "aaa05fd62b7ac037d59802ce9c46b69c5bc84a33",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "nix",
|
||||||
|
"owner": "loteran",
|
||||||
|
"repo": "Arctis-Sound-Manager",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"base16": {
|
"base16": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fromYaml": "fromYaml"
|
"fromYaml": "fromYaml"
|
||||||
|
|
@ -372,6 +394,7 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"arctis-sound-manager": "arctis-sound-manager",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"nix-cachyos-kernel": "nix-cachyos-kernel",
|
"nix-cachyos-kernel": "nix-cachyos-kernel",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@
|
||||||
|
|
||||||
proton-cachyos-nix.url = "github:powerofthe69/proton-cachyos-nix";
|
proton-cachyos-nix.url = "github:powerofthe69/proton-cachyos-nix";
|
||||||
|
|
||||||
|
arctis-sound-manager = {
|
||||||
|
url = "github:loteran/Arctis-Sound-Manager?dir=nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
stylix.url = "github:nix-community/stylix/release-26.05";
|
stylix.url = "github:nix-community/stylix/release-26.05";
|
||||||
};
|
};
|
||||||
outputs =
|
outputs =
|
||||||
|
|
@ -49,7 +54,7 @@
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
nixosConfigurations = {
|
nixosConfigurations = {
|
||||||
FredOS-Gaming = mkHost "FredOS-Gaming" [];
|
FredOS-Gaming = mkHost "FredOS-Gaming" [ inputs.arctis-sound-manager.nixosModules.default ];
|
||||||
FredOS-Mediaserver = mkHost "FredOS-Mediaserver" [];
|
FredOS-Mediaserver = mkHost "FredOS-Mediaserver" [];
|
||||||
FredOS-Macbook = mkHost "FredOS-Macbook" [];
|
FredOS-Macbook = mkHost "FredOS-Macbook" [];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@
|
||||||
|
|
||||||
programs.geary.enable = true;
|
programs.geary.enable = true;
|
||||||
|
|
||||||
|
# SteelSeries Nova Pro control (ANC switching via quickshell talks to its
|
||||||
|
# asm-daemon over D-Bus)
|
||||||
|
services.arctis-sound-manager.enable = true;
|
||||||
|
|
||||||
# Force heavy builds off the gaming PC onto the media server so nix updates
|
# Force heavy builds off the gaming PC onto the media server so nix updates
|
||||||
# never compete with games for CPU/RAM. Pairs with the distributed build
|
# never compete with games for CPU/RAM. Pairs with the distributed build
|
||||||
# config in common.nix. builders-use-substitutes lets the server pull from
|
# config in common.nix. builders-use-substitutes lets the server pull from
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,7 @@ in
|
||||||
readonly property string systemctl: "${pkgs.systemd}/bin/systemctl"
|
readonly property string systemctl: "${pkgs.systemd}/bin/systemctl"
|
||||||
readonly property string weatherFetch: "${weatherFetchScript}"
|
readonly property string weatherFetch: "${weatherFetchScript}"
|
||||||
readonly property string ssh: "${pkgs.openssh}/bin/ssh"
|
readonly property string ssh: "${pkgs.openssh}/bin/ssh"
|
||||||
|
readonly property string gdbus: "${pkgs.glib}/bin/gdbus"
|
||||||
// Unprivileged ping works via ICMP dgram sockets — NixOS sets
|
// Unprivileged ping works via ICMP dgram sockets — NixOS sets
|
||||||
// net.ipv4.ping_group_range for everyone (no wrapper exists).
|
// net.ipv4.ping_group_range for everyone (no wrapper exists).
|
||||||
readonly property string ping: "${pkgs.iputils}/bin/ping"
|
readonly property string ping: "${pkgs.iputils}/bin/ping"
|
||||||
|
|
@ -2932,6 +2933,115 @@ in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${lib.optionalString (!isMacbook) ''
|
||||||
|
// Nova Pro noise cancelling — talks to asm-daemon
|
||||||
|
// (Arctis Sound Manager) over the session bus.
|
||||||
|
// SetSetting("noise_cancelling", "0|1|2"), status read
|
||||||
|
// back as off/transparent/on. Card hides when the
|
||||||
|
// daemon or headset is gone.
|
||||||
|
Card {
|
||||||
|
id: ancCard
|
||||||
|
visible: ancMode !== ""
|
||||||
|
width: parent.width
|
||||||
|
property string ancMode: ""
|
||||||
|
|
||||||
|
function setMode(key, value) {
|
||||||
|
if (key === ancMode) return;
|
||||||
|
ancMode = key; // optimistic; next poll corrects if the device disagrees
|
||||||
|
ancSet.mode = value;
|
||||||
|
ancSet.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: ancSet
|
||||||
|
property string mode: "0"
|
||||||
|
command: [Commands.gdbus, "call", "--session",
|
||||||
|
"--dest", "name.giacomofurlan.ArctisManager.Next",
|
||||||
|
"--object-path", "/name/giacomofurlan/ArctisManager/Next/Settings",
|
||||||
|
"--method", "name.giacomofurlan.ArctisManager.Next.Settings.SetSetting",
|
||||||
|
"noise_cancelling", mode]
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: ancStatus
|
||||||
|
command: [Commands.gdbus, "call", "--session",
|
||||||
|
"--dest", "name.giacomofurlan.ArctisManager.Next",
|
||||||
|
"--object-path", "/name/giacomofurlan/ArctisManager/Next/Status",
|
||||||
|
"--method", "name.giacomofurlan.ArctisManager.Next.Status.GetStatus"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
// gdbus prints a GVariant tuple: ('{"headset": ...}',)
|
||||||
|
const a = text.indexOf("('");
|
||||||
|
const b = text.lastIndexOf("',");
|
||||||
|
if (a < 0 || b <= a) { ancCard.ancMode = ""; return; }
|
||||||
|
try {
|
||||||
|
const st = JSON.parse(text.slice(a + 2, b));
|
||||||
|
const nc = st.headset ? st.headset.noise_cancelling : undefined;
|
||||||
|
ancCard.ancMode = (nc && nc.value) ? nc.value : "";
|
||||||
|
} catch (e) { ancCard.ancMode = ""; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll only while the dropdown is up; keeps ticking
|
||||||
|
// with the card hidden so a headset appearing
|
||||||
|
// mid-open reveals it.
|
||||||
|
Timer {
|
||||||
|
interval: 3000
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
running: volDropdown.visible
|
||||||
|
onTriggered: ancStatus.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: 6
|
||||||
|
SIcon { anchors.verticalCenter: parent.verticalCenter; text: "headphones" }
|
||||||
|
SText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: "Noise Cancelling"
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
{ key: "off", label: "Off", val: "0" },
|
||||||
|
{ key: "transparent", label: "Transparent", val: "1" },
|
||||||
|
{ key: "on", label: "ANC", val: "2" }
|
||||||
|
]
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
property bool active: ancCard.ancMode === modelData.key
|
||||||
|
width: (parent.width - 12) / 3
|
||||||
|
height: 26
|
||||||
|
radius: 6
|
||||||
|
color: active ? Theme.base0D : "transparent"
|
||||||
|
border.color: active ? Theme.base0D : Theme.base03
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
SText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: modelData.label
|
||||||
|
font.pixelSize: 11
|
||||||
|
color: active ? Theme.base00 : Theme.base05
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: ancCard.setMode(modelData.key, modelData.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''}
|
||||||
// Applications card
|
// Applications card
|
||||||
Card {
|
Card {
|
||||||
visible: appStreamsCol.childrenRect.height > 0
|
visible: appStreamsCol.childrenRect.height > 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue