Unverified Commit bfcf9b9a authored by Jannis's avatar Jannis Committed by GitHub

Audio slider (#207)

parent 60ef8f93
......@@ -18,7 +18,7 @@ jobs:
container: archlinux:latest
runs-on: ubuntu-latest
env:
PACKAGES: meson gtk3 gobject-introspection vala json-glib libhandy gtk-layer-shell scdoc
PACKAGES: meson gtk3 gobject-introspection vala json-glib libhandy gtk-layer-shell scdoc libpulse libgee
steps:
- name: Install packages
run: |
......
......@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
env:
DEBIAN_FRONTEND: noninteractive
PACKAGES: meson libwayland-dev libgtk-3-dev gobject-introspection libgirepository1.0-dev valac libjson-glib-dev libhandy-1-dev libgtk-layer-shell-dev scdoc
PACKAGES: meson libwayland-dev libgtk-3-dev gobject-introspection libgirepository1.0-dev valac libjson-glib-dev libhandy-1-dev libgtk-layer-shell-dev scdoc libgee-0.8-dev libpulse-dev
steps:
- name: Install packages
run: |
......
......@@ -51,6 +51,7 @@ These widgets can be customized, added, removed and even reordered
- Mpris (Media player controls for Spotify, Firefox, Chrome, etc...)
- Menubar with dropdown and buttons
- Button grid
- Volume slider using PulseAudio
## Planned Features
......
......@@ -11,7 +11,7 @@ arch=(
'armv7h' # ARM v7 hardfloat
)
license=(GPL3)
depends=("gtk3" "gtk-layer-shell" "dbus" "glib2" "gobject-introspection" "libgee" "json-glib" "libhandy")
depends=("gtk3" "gtk-layer-shell" "dbus" "glib2" "gobject-introspection" "libgee" "json-glib" "libhandy" "libpulse")
conflicts=("swaync" "swaync-client")
provides=("swaync" "swaync-client")
makedepends=(vala meson git scdoc)
......
......@@ -11,7 +11,7 @@ arch=(
'armv7h' # ARM v7 hardfloat
)
license=('GPL3')
depends=("gtk3" "gtk-layer-shell" "dbus" "glib2" "gobject-introspection" "libgee" "json-glib" "libhandy")
depends=("gtk3" "gtk-layer-shell" "dbus" "glib2" "gobject-introspection" "libgee" "json-glib" "libhandy" "libpulse" )
conflicts=("swaync" "swaync-client")
provides=("swaync" "swaync-client")
makedepends=(vala meson git scdoc)
......
......@@ -23,6 +23,7 @@ BuildRequires: libhandy-devel >= 1.4.0
BuildRequires: systemd-devel
BuildRequires: systemd
BuildRequires: scdoc
BuildRequires: pulseaudio-libs-devel
%{?systemd_requires}
%description
......
......@@ -366,7 +366,17 @@ config file to be able to detect config errors
description: "Command to be executed on click" ++
description: A list of actions containing a label and a command ++
description: A grid of buttons that execute shell commands ++
*volume*++
type: object ++
css class: widget-volume ++
properties: ++
label: ++
type: string ++
optional: true ++
default: "Volume" ++
description: Text displayed in front of the volume slider ++
description: Slider to control pulse volume ++
example:
```
{
......
......@@ -272,6 +272,9 @@
},
"^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/menubar"
},
"^volume(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/volume"
}
}
}
......@@ -415,6 +418,18 @@
}
}
}
},
"volume": {
"type": "object",
"description": "Slider to control pulse volume",
"additionalProperties": false,
"properties": {
"label": {
"type": "string",
"description": "Text displayed in front of the volume slider",
"default": "Volume"
}
}
}
}
}
......@@ -26,6 +26,9 @@ namespace SwayNotificationCenter.Widgets {
case "buttons-grid":
widget = new ButtonsGrid (suffix, swaync_daemon, noti_daemon);
break;
case "volume":
widget = new Volume (suffix, swaync_daemon, noti_daemon);
break;
default:
warning ("Could not find widget: \"%s\"!", key);
return null;
......
// From SwaySettings PulseAudio page: https://github.com/ErikReider/SwaySettings/blob/407c9e99dd3e50a0f09c64d94a9e6ff741488378/src/Pages/Pulse/PulseDevice.vala
using PulseAudio;
using Gee;
namespace SwayNotificationCenter.Widgets {
public class PulseCardProfile : Object {
public string name;
public string description;
public uint32 n_sinks;
public uint32 priority;
int available;
public PulseCardProfile (CardProfileInfo2 * profile) {
this.name = profile->name;
this.description = profile->description;
this.n_sinks = profile->n_sinks;
this.priority = profile->priority;
this.available = profile->available;
}
public bool cmp (PulseCardProfile profile) {
return profile.name == name
&& profile.description == description
&& profile.n_sinks == n_sinks
&& profile.priority == priority
&& profile.available == available;
}
}
public class PulseDevice : Object {
public bool removed { get; set; default = false; }
public bool has_card { get; set; default = true; }
/** The card index: ex. `Card #49` */
public uint32 card_index { get; set; }
/** Sink index: ex. `Sink #55` */
public uint32 device_index { get; set; }
/** Input or Output */
public Direction direction { get; set; }
/** Is default Sink */
public bool is_default { get; set; }
/** If the device is virtual */
public bool is_virtual { get; set; default = false; }
/** If the device is a bluetooth device */
public bool is_bluetooth { get; set; default = false; }
/** The icon name: `device.icon_name` */
public string icon_name { get; set; }
/** The card name: `Name` */
public string card_name { get; set; }
/** The card description: `device.description` */
public string card_description { get; set; }
/** The card active profile: `Active Profile` */
public string card_active_profile { get; set; }
/** The card sink port name: `Active Port` */
public string card_sink_port_name { get; set; }
/** The Sink name: `Name` */
public string ? device_name { get; set; }
/** The Sink description: `Description` */
public string device_description { get; set; }
/** If the Sink is muted: `Mute` */
public bool is_muted { get; set; }
public double volume { get; set; }
public float balance { get; set; default = 0; }
public CVolume cvolume;
public ChannelMap channel_map;
public LinkedList<Operation> volume_operations { get; set; }
/** Gets the name to be shown to the user:
* "port_description - card_description"
*/
public string ? get_display_name () {
if (card_name == null) {
return device_description;
}
string p_desc = port_description;
string c_desc = card_description;
return "%s - %s".printf (p_desc, c_desc);
}
/** Compares PulseDevices. Returns true if they're the same */
public bool cmp (PulseDevice device) {
return device.card_index == card_index
&& device.device_index == device_index
&& device.device_name == device_name
&& device.device_description == device_description
&& device.is_default == is_default
&& device.removed == removed
&& device.card_active_profile == card_active_profile
&& device.port_name == port_name;
}
/**
* Gets the name to be shown to the user:
* If has card: "card_description:port_name"
* If cardless: "device_index:device_description"
*/
public string get_current_hash_key () {
if (card_name == null) {
return get_hash_map_key (device_index.to_string (),
device_description);
}
return get_hash_map_key (card_description, port_name);
}
/** Gets the name to be shown to the user:
* "card_description:port_name"
*/
public static string get_hash_map_key (string c_desc, string p_name) {
return string.joinv (":", new string[] { c_desc, p_name });
}
/** The port name: `Name` */
public string port_name { get; set; }
/** The port name: `Description` */
public string port_description { get; set; }
/** The port name: `card.profile.port` */
public string port_id { get; set; }
/** All port profiles */
public string[] port_profiles { get; set; }
public Array<PulseCardProfile> profiles { get; set; }
public PulseCardProfile ? active_profile { get; set; }
construct {
volume_operations = new LinkedList<Operation> ();
}
}
}
namespace SwayNotificationCenter.Widgets {
public class Volume : BaseWidget {
public override string widget_name {
get {
return "volume";
}
}
Gtk.Label label_widget = new Gtk.Label (null);
Gtk.Scale slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);
private PulseDevice ? default_sink = null;
private PulseDaemon client = new PulseDaemon ();
construct {
this.client.change_default_device.connect (default_device_changed);
slider.value_changed.connect (() => {
if (default_sink != null) {
this.client.set_device_volume (
default_sink,
(float) slider.get_value ());
slider.tooltip_text = ((int) slider.get_value ()).to_string ();
}
});
}
public Volume (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
base (suffix, swaync_daemon, noti_daemon);
Json.Object ? config = get_config (this);
if (config != null) {
string ? label = get_prop<string> (config, "label");
label_widget.set_label (label ?? "Volume");
}
slider.draw_value = false;
add (label_widget);
pack_start (slider, true, true, 0);
show_all ();
}
public override void on_cc_visibility_change (bool val) {
if (val) {
this.client.start ();
} else {
this.client.close ();
}
}
private void default_device_changed (PulseDevice device) {
if (device != null && device.direction == PulseAudio.Direction.OUTPUT) {
this.default_sink = device;
slider.set_value (device.volume);
}
}
}
}
......@@ -40,6 +40,10 @@ widget_sources = [
'controlCenter/widgets/menubar/menubar.vala',
# Widget: Buttons Grid
'controlCenter/widgets/buttonsGrid/buttonsGrid.vala',
# Widget: Volume
'controlCenter/widgets/volume/volume.vala',
'controlCenter/widgets/volume/pulseDaemon.vala',
'controlCenter/widgets/volume/pulseDevice.vala',
]
app_sources = [
......@@ -67,6 +71,9 @@ app_deps = [
meson.get_compiler('c').find_library('gtk-layer-shell'),
meson.get_compiler('c').find_library('m', required : true),
meson.get_compiler('vala').find_library('posix'),
dependency('gee-0.8'),
dependency('libpulse'),
dependency('libpulse-mainloop-glib'),
]
# Checks if the user wants scripting enabled
......
......@@ -282,4 +282,13 @@
.topbar-buttons>button { /* Name defined in config after # */
border: none;
background: transparent;
}
/* Volume widget */
.widget-volume {
background-color: @noti-bg;
padding: 8px;
margin: 8px;
border-radius: 12px;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment