diff --git a/bumblebee_status/modules/contrib/pactl.py b/bumblebee_status/modules/contrib/pactl.py new file mode 100644 index 0000000..cd62c9a --- /dev/null +++ b/bumblebee_status/modules/contrib/pactl.py @@ -0,0 +1,141 @@ +# 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: + 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