50 lines
1.7 KiB
Python
50 lines
1.7 KiB
Python
"""Default-sink volume via pactl — same tool scripts/getvol already shells out
|
|
to. Simple synchronous subprocess calls on click/scroll, no continuous polling:
|
|
the bar only needs to reflect what IT changed, and hardware volume keys are rare
|
|
enough on a device that also runs Hyprland that a poll loop isn't worth the cost.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import subprocess
|
|
|
|
_STEP = 5 # percent per scroll tick, matches typical bar/WM conventions
|
|
|
|
_VOL_RE = re.compile(r"(\d+)%")
|
|
|
|
|
|
def get_percent() -> int:
|
|
try:
|
|
out = subprocess.run(["pactl", "get-sink-volume", "@DEFAULT_SINK@"],
|
|
capture_output=True, text=True, timeout=1.0, check=True).stdout
|
|
m = _VOL_RE.search(out)
|
|
return int(m.group(1)) if m else 0
|
|
except (subprocess.SubprocessError, OSError):
|
|
return 0
|
|
|
|
|
|
def is_muted() -> bool:
|
|
try:
|
|
out = subprocess.run(["pactl", "get-sink-mute", "@DEFAULT_SINK@"],
|
|
capture_output=True, text=True, timeout=1.0, check=True).stdout
|
|
return "yes" in out.lower()
|
|
except (subprocess.SubprocessError, OSError):
|
|
return False
|
|
|
|
|
|
def _set(percent: int) -> None:
|
|
percent = max(0, min(100, percent))
|
|
subprocess.Popen(["pactl", "set-sink-volume", "@DEFAULT_SINK@", f"{percent}%"],
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
|
|
def adjust(delta_ticks: int) -> None:
|
|
"""delta_ticks > 0 raises, < 0 lowers, in _STEP-sized increments."""
|
|
_set(get_percent() + delta_ticks * _STEP)
|
|
|
|
|
|
def toggle_mute() -> None:
|
|
subprocess.Popen(["pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle"],
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|