950931e1b9
* added new module 'pactl' which displays the current default sink and allows to select a different default sink from the popup menu.
142 lines
4.5 KiB
Python
142 lines
4.5 KiB
Python
# 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
|