commit
04864392f9
@ -1,34 +1,53 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from i3pystatus.core.color import ColorRangeModule
|
||||||
|
from i3pystatus.core.util import make_vertical_bar, make_bar
|
||||||
from .pulse import *
|
from .pulse import *
|
||||||
|
|
||||||
from i3pystatus import Module
|
from i3pystatus import Module
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class PulseAudio(Module, ColorRangeModule):
|
||||||
class PulseAudio(Module):
|
|
||||||
"""
|
"""
|
||||||
Shows volume of default PulseAudio sink (output).
|
Shows volume of default PulseAudio sink (output).
|
||||||
|
|
||||||
.. rubric:: Available formatters
|
Requires amixer for toggling mute and incrementing/decrementing volume on scroll.
|
||||||
|
|
||||||
|
Available formatters:
|
||||||
|
|
||||||
* `{volume}` — volume in percent (0...100)
|
* `{volume}` — volume in percent (0...100)
|
||||||
* `{db}` — volume in decibels relative to 100 %, i.e. 100 % = 0 dB, 50 % = -18 dB, 0 % = -infinity dB
|
* `{db}` — volume in decibels relative to 100 %, i.e. 100 % = 0 dB, 50 % = -18 dB, 0 % = -infinity dB
|
||||||
(the literal value for -infinity is `-∞`)
|
(the literal value for -infinity is `-∞`)
|
||||||
* `{muted}` — the value of one of the `muted` or `unmuted` settings
|
* `{muted}` — the value of one of the `muted` or `unmuted` settings
|
||||||
|
* `{volume_bar}` — unicode bar showing volume
|
||||||
"""
|
"""
|
||||||
|
|
||||||
settings = (
|
settings = (
|
||||||
"format",
|
"format",
|
||||||
("format_muted", "optional format string to use when muted"),
|
("format_muted", "optional format string to use when muted"),
|
||||||
"muted", "unmuted",
|
"muted", "unmuted",
|
||||||
"color_muted", "color_unmuted"
|
"color_muted", "color_unmuted",
|
||||||
|
("step", "percentage to increment volume on scroll"),
|
||||||
|
("bar_type", "type of volume bar. Allowed values are 'vertical' or 'horizontal'"),
|
||||||
|
("multi_colors", "whether or not to change the color from "
|
||||||
|
"'color_muted' to 'color_unmuted' based on volume percentage"),
|
||||||
|
("vertical_bar_width", "how many characters wide the vertical volume_bar should be")
|
||||||
)
|
)
|
||||||
|
|
||||||
muted = "M"
|
muted = "M"
|
||||||
unmuted = ""
|
unmuted = ""
|
||||||
format = "♪: {volume}"
|
format = "♪: {volume}"
|
||||||
format_muted = None
|
format_muted = None
|
||||||
|
currently_muted = False
|
||||||
|
has_amixer = False
|
||||||
color_muted = "#FF0000"
|
color_muted = "#FF0000"
|
||||||
color_unmuted = "#FFFFFF"
|
color_unmuted = "#FFFFFF"
|
||||||
|
|
||||||
|
step = 5
|
||||||
|
multi_colors = False
|
||||||
|
bar_type = 'vertical'
|
||||||
|
vertical_bar_width = 2
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
"""Creates context, when context is ready context_notify_cb is called"""
|
"""Creates context, when context is ready context_notify_cb is called"""
|
||||||
# Wrap callback methods in appropriate ctypefunc instances so
|
# Wrap callback methods in appropriate ctypefunc instances so
|
||||||
@ -51,6 +70,11 @@ class PulseAudio(Module):
|
|||||||
pa_context_connect(context, None, 0, None)
|
pa_context_connect(context, None, 0, None)
|
||||||
pa_threaded_mainloop_start(_mainloop)
|
pa_threaded_mainloop_start(_mainloop)
|
||||||
|
|
||||||
|
self.colors = self.get_hex_color_range(self.color_muted, self.color_unmuted, 100)
|
||||||
|
|
||||||
|
# Check that we have amixer for toggling mute/unmute and incrementing/decrementing volume
|
||||||
|
self.has_amixer = shutil.which('alsamixer') is not None
|
||||||
|
|
||||||
def request_update(self, context):
|
def request_update(self, context):
|
||||||
"""Requests a sink info update (sink_info_cb is called)"""
|
"""Requests a sink info update (sink_info_cb is called)"""
|
||||||
pa_operation_unref(pa_context_get_sink_info_by_name(
|
pa_operation_unref(pa_context_get_sink_info_by_name(
|
||||||
@ -94,6 +118,7 @@ class PulseAudio(Module):
|
|||||||
sink_info = sink_info_p.contents
|
sink_info = sink_info_p.contents
|
||||||
volume_percent = int(100 * sink_info.volume.values[0] / 0x10000)
|
volume_percent = int(100 * sink_info.volume.values[0] / 0x10000)
|
||||||
volume_db = pa_sw_volume_to_dB(sink_info.volume.values[0])
|
volume_db = pa_sw_volume_to_dB(sink_info.volume.values[0])
|
||||||
|
self.currently_muted = sink_info.mute
|
||||||
|
|
||||||
if volume_db == float('-Infinity'):
|
if volume_db == float('-Infinity'):
|
||||||
volume_db = "-∞"
|
volume_db = "-∞"
|
||||||
@ -101,22 +126,53 @@ class PulseAudio(Module):
|
|||||||
volume_db = int(volume_db)
|
volume_db = int(volume_db)
|
||||||
|
|
||||||
muted = self.muted if sink_info.mute else self.unmuted
|
muted = self.muted if sink_info.mute else self.unmuted
|
||||||
color = self.color_muted if sink_info.mute else self.color_unmuted
|
|
||||||
|
if self.multi_colors and not sink_info.mute:
|
||||||
|
color = self.get_gradient(volume_percent, self.colors)
|
||||||
|
else:
|
||||||
|
color = self.color_muted if sink_info.mute else self.color_unmuted
|
||||||
|
|
||||||
if muted and self.format_muted is not None:
|
if muted and self.format_muted is not None:
|
||||||
output_format = self.format_muted
|
output_format = self.format_muted
|
||||||
else:
|
else:
|
||||||
output_format = self.format
|
output_format = self.format
|
||||||
|
|
||||||
|
if self.bar_type == 'vertical':
|
||||||
|
volume_bar = make_vertical_bar(volume_percent, self.vertical_bar_width)
|
||||||
|
elif self.bar_type == 'horizontal':
|
||||||
|
volume_bar = make_bar(volume_percent)
|
||||||
|
else:
|
||||||
|
raise Exception("bar_type must be 'vertical' or 'horizontal'")
|
||||||
|
|
||||||
self.output = {
|
self.output = {
|
||||||
"color": color,
|
"color": color,
|
||||||
"full_text": output_format.format(
|
"full_text": output_format.format(
|
||||||
muted=muted,
|
muted=muted,
|
||||||
volume=volume_percent,
|
volume=volume_percent,
|
||||||
db=volume_db),
|
db=volume_db,
|
||||||
|
volume_bar=volume_bar),
|
||||||
}
|
}
|
||||||
|
|
||||||
def on_leftclick(self):
|
def on_leftclick(self):
|
||||||
import subprocess
|
|
||||||
|
|
||||||
subprocess.Popen(["pavucontrol"])
|
subprocess.Popen(["pavucontrol"])
|
||||||
|
|
||||||
|
def on_rightclick(self):
|
||||||
|
if self.has_amixer:
|
||||||
|
command = "amixer -q -D pulse sset Master "
|
||||||
|
if self.currently_muted:
|
||||||
|
command += 'unmute'
|
||||||
|
else:
|
||||||
|
command += 'mute'
|
||||||
|
subprocess.Popen(command.split())
|
||||||
|
|
||||||
|
def on_upscroll(self):
|
||||||
|
if self.has_amixer:
|
||||||
|
command = "amixer -q -D pulse sset Master %s%%+" % self.step
|
||||||
|
subprocess.Popen(command.split())
|
||||||
|
|
||||||
|
def on_downscroll(self):
|
||||||
|
if self.has_amixer:
|
||||||
|
command = "amixer -q -D pulse sset Master %s%%-" % self.step
|
||||||
|
subprocess.Popen(command.split())
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user