added new module 'pactl'

* added new module 'pactl' which displays the current default sink and allows to
  select a different default sink from the popup menu.
This commit is contained in:
Bernhard B 2022-02-28 19:14:21 +01:00
parent 58bbcf2ac5
commit 950931e1b9

View file

@ -0,0 +1,142 @@
# pylint: disable=C0111,R0903
""" Displays the current default sink.
Left click opens a popup menu that lists all available sinks and allows to change the default sink.
Per default, this module uses the sink names returned by "pactl list sinks short"
sample output of "pactl list sinks short":
2 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
3 alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDE
As "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" is not a particularly nice name, its possible to map the name to more a
user friendly name. e.g to map "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" to the name "Headset", add the following
bumblebee-status config entry: pactl.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=Headset
The module also allows to specify individual (unicode) icons for all the sinks. e.g in order to use the icon 🎧 for the
"alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" sink, add the following bumblebee-status config entry:
pactl.icon.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=🎧
Requirements:
* pulseaudio
* pactl
"""
import logging
import functools
import core.module
import core.widget
import core.input
import util.cli
import util.popup
class Sink(object):
def __init__(self, id, internal_name, friendly_name, icon):
super().__init__()
self.__id = id
self.__internal_name = internal_name
self.__friendly_name = friendly_name
self.__icon = icon
@property
def id(self):
return self.__id
@property
def internal_name(self):
return self.__internal_name
@property
def friendly_name(self):
return self.__friendly_name
@property
def icon(self):
return self.__icon
@property
def display_name(self):
display_name = (
self.__icon + " " + self.__friendly_name
if self.__icon != ""
else self.__friendly_name
)
return display_name
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.default_sink))
self.__default_sink = None
res = util.cli.execute("pactl list sinks short")
lines = res.splitlines()
self.__sinks = []
for line in lines:
info = line.split("\t")
try:
friendly_name = self.parameter(info[1], info[1])
icon = self.parameter("icon." + info[1], "")
self.__sinks.append(Sink(info[0], info[1], friendly_name, icon))
except:
logging.exception("Couldn't parse sink")
pass
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
def __sink(self, internal_sink_name):
for sink in self.__sinks:
logging.info(sink.internal_name)
if internal_sink_name == sink.internal_name:
return sink
return None
def update(self):
try:
res = util.cli.execute("pactl info")
lines = res.splitlines()
self.__default_sink = None
for line in lines:
if not line.startswith("Default Sink:"):
continue
internal_sink_name = line.replace("Default Sink: ", "")
self.__default_sink = self.__sink(internal_sink_name)
break
except Exception as e:
logging.exception("Could not get pactl info")
self.__default_sink = None
def default_sink(self, widget):
if self.__default_sink is None:
return "unknown"
return self.__default_sink.display_name
def __on_sink_selected(self, sink):
try:
util.cli.execute("pactl set-default-sink {}".format(sink.id))
self.__default_sink = sink
except Exception as e:
logging.exception("Couldn't set default sink")
def popup(self, widget):
menu = util.popup.menu()
for sink in self.__sinks:
menu.add_menuitem(
sink.friendly_name,
callback=functools.partial(self.__on_sink_selected, sink),
)
menu.show(widget)
def state(self, widget):
return []
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4