mirror of
https://github.com/gentoo-mirror/guru.git
synced 2026-07-08 22:52:59 -04:00
1173 lines
46 KiB
Diff
1173 lines
46 KiB
Diff
diff --git a/man/swaync.5.scd b/man/swaync.5.scd.2
|
|
index 70fdd6e..7b4f638 100644
|
|
--- a/man/swaync.5.scd
|
|
+++ b/man/swaync.5.scd.2
|
|
@@ -405,54 +405,6 @@ 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 ++
|
|
- per-app-volume ++
|
|
- properties: ++
|
|
- label: ++
|
|
- type: string ++
|
|
- optional: true ++
|
|
- default: "Volume" ++
|
|
- description: Text displayed in front of the volume slider ++
|
|
- show-per-app: ++
|
|
- type: bool ++
|
|
- optional: true ++
|
|
- default: false ++
|
|
- description: Show per app volume control ++
|
|
- empty-list-label: ++
|
|
- type: string ++
|
|
- optional: true ++
|
|
- default: "No active sink input" ++
|
|
- description: Text displayed when there are not active sink inputs ++
|
|
- expand-button-label: ++
|
|
- type: string ++
|
|
- optional: true ++
|
|
- default: "⇧" ++
|
|
- description: Label displayed on button to show per app volume control ++
|
|
- collapse-button-label: ++
|
|
- type: string ++
|
|
- optional: true ++
|
|
- default: "⇩" ++
|
|
- description: Label displayed on button to hide per app volume control ++
|
|
- icon-size: ++
|
|
- type: integer ++
|
|
- optional: true ++
|
|
- default: 24 ++
|
|
- description: Size of the application icon in per app volume control ++
|
|
- animation-type: ++
|
|
- type: string ++
|
|
- optional: true ++
|
|
- default: "slide_down" ++
|
|
- description: Animation type for the per app volume control ++
|
|
- enum: ["slide_down", "slide_up", "none"] ++
|
|
- animation-duration: ++
|
|
- type: integer ++
|
|
- optional: true ++
|
|
- default: 250 ++
|
|
- description: Duration of animation in milliseconds ++
|
|
- description: Slider to control pulse volume ++
|
|
*backlight*++
|
|
type: object ++
|
|
css class: widget-backlight ++
|
|
diff --git a/src/configSchema.json b/src/configSchema.json2
|
|
index b54e247..2390951 100644
|
|
--- a/src/configSchema.json
|
|
+++ b/src/configSchema.json2
|
|
@@ -475,54 +475,6 @@
|
|
}
|
|
}
|
|
},
|
|
- "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"
|
|
- },
|
|
- "show-per-app": {
|
|
- "type": "boolean",
|
|
- "default": false,
|
|
- "description": "Show per app volume control"
|
|
- },
|
|
- "empty-list-label": {
|
|
- "type": "string",
|
|
- "default": "No active sink input",
|
|
- "description": "Text displayed when there are not active sink inputs"
|
|
- },
|
|
- "expand-button-label": {
|
|
- "type": "string",
|
|
- "default": "⇧",
|
|
- "description": "Label displayed on button to show per app volume control"
|
|
- },
|
|
- "collapse-button-label": {
|
|
- "type": "string",
|
|
- "default": "⇩",
|
|
- "description": "Label displayed on button to hide per app volume control"
|
|
- },
|
|
- "icon-size": {
|
|
- "type": "integer",
|
|
- "default": 24,
|
|
- "description": "Size of the application icon in per app volume control"
|
|
- },
|
|
- "animation-type": {
|
|
- "type": "string",
|
|
- "default": "slide_down",
|
|
- "description": "Animation type for menu",
|
|
- "enum": ["slide_down", "slide_up", "none"]
|
|
- },
|
|
- "animation-duration":{
|
|
- "type": "integer",
|
|
- "default": 250,
|
|
- "description": "Duration of animation in milliseconds"
|
|
- }
|
|
- }
|
|
- },
|
|
"backlight": {
|
|
"type": "object",
|
|
"description": "Slider to control monitor brightness",
|
|
diff --git a/src/controlCenter/widgets/factory.vala b/src/controlCenter/widgets/factory.vala
|
|
index aea178d..f2ed445 100644
|
|
--- a/src/controlCenter/widgets/factory.vala
|
|
+++ b/src/controlCenter/widgets/factory.vala
|
|
@@ -26,9 +26,6 @@ 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;
|
|
case "backlight":
|
|
widget = new Backlight (suffix, swaync_daemon, noti_daemon);
|
|
break;
|
|
diff --git a/src/controlCenter/widgets/volume/pulseDaemon.vala b/src/controlCenter/widgets/volume/pulseDaemon.vala.2
|
|
index 7742e25..e69de29 100644
|
|
--- a/src/controlCenter/widgets/volume/pulseDaemon.vala
|
|
+++ b/src/controlCenter/widgets/volume/pulseDaemon.vala.2
|
|
@@ -1,647 +0,0 @@
|
|
-// From SwaySettings PulseAudio page: https://github.com/ErikReider/SwaySettings/blob/407c9e99dd3e50a0f09c64d94a9e6ff741488378/src/Pages/Pulse/PulseDaemon.vala
|
|
-using PulseAudio;
|
|
-using Gee;
|
|
-
|
|
-namespace SwayNotificationCenter.Widgets {
|
|
- /**
|
|
- * Loosely based off of Elementary OS switchboard-plug-sound
|
|
- * https://github.com/elementary/switchboard-plug-sound
|
|
- */
|
|
- public class PulseDaemon : Object {
|
|
- private Context ? context;
|
|
- private GLibMainLoop mainloop;
|
|
- private bool quitting = false;
|
|
-
|
|
- public bool running { get; private set; }
|
|
-
|
|
- private string default_sink_name { get; private set; }
|
|
- private string default_source_name { get; private set; }
|
|
-
|
|
- private PulseDevice ? default_sink = null;
|
|
-
|
|
- public HashMap<string, PulseDevice> sinks { get; private set; }
|
|
-
|
|
- public HashMap<uint32, PulseSinkInput> active_sinks { get; private set; }
|
|
-
|
|
- construct {
|
|
- mainloop = new GLibMainLoop ();
|
|
-
|
|
- sinks = new HashMap<string, PulseDevice> ();
|
|
-
|
|
- active_sinks = new HashMap<uint32, PulseSinkInput> ();
|
|
- }
|
|
-
|
|
- public void start () {
|
|
- get_context ();
|
|
- }
|
|
-
|
|
- public void close () {
|
|
- quitting = true;
|
|
- if (this.context != null) {
|
|
- context.disconnect ();
|
|
- context = null;
|
|
- }
|
|
- }
|
|
-
|
|
- ~PulseDaemon () {
|
|
- this.close ();
|
|
- }
|
|
-
|
|
- public signal void change_default_device (PulseDevice device);
|
|
-
|
|
- public signal void new_active_sink (PulseSinkInput device);
|
|
- public signal void change_active_sink (PulseSinkInput device);
|
|
- public signal void remove_active_sink (PulseSinkInput device);
|
|
-
|
|
- public signal void new_device (PulseDevice device);
|
|
- public signal void change_device (PulseDevice device);
|
|
- public signal void remove_device (PulseDevice device);
|
|
-
|
|
- private void get_context () {
|
|
- var ctx = new Context (mainloop.get_api (), null);
|
|
- ctx.set_state_callback ((ctx) => {
|
|
- debug ("Pulse Status: %s\n", ctx.get_state ().to_string ());
|
|
- switch (ctx.get_state ()) {
|
|
- case Context.State.CONNECTING:
|
|
- case Context.State.AUTHORIZING:
|
|
- case Context.State.SETTING_NAME:
|
|
- break;
|
|
- case Context.State.READY:
|
|
- ctx.set_subscribe_callback (subscription);
|
|
- ctx.subscribe (Context.SubscriptionMask.SINK_INPUT |
|
|
- Context.SubscriptionMask.SINK |
|
|
- Context.SubscriptionMask.CARD |
|
|
- Context.SubscriptionMask.SERVER);
|
|
- // Init data
|
|
- ctx.get_server_info (this.get_server_info);
|
|
- running = true;
|
|
- break;
|
|
- case Context.State.TERMINATED:
|
|
- case Context.State.FAILED:
|
|
- running = false;
|
|
- if (quitting) {
|
|
- quitting = false;
|
|
- break;
|
|
- }
|
|
- stderr.printf (
|
|
- "PulseAudio connection lost. Will retry connection.\n");
|
|
- get_context ();
|
|
- break;
|
|
- default:
|
|
- running = false;
|
|
- stderr.printf ("Connection failure: %s\n",
|
|
- PulseAudio.strerror (ctx.errno ()));
|
|
- break;
|
|
- }
|
|
- });
|
|
- if (ctx.connect (
|
|
- null, Context.Flags.NOFAIL, null) < 0) {
|
|
- stdout.printf ("pa_context_connect() failed: %s\n",
|
|
- PulseAudio.strerror (ctx.errno ()));
|
|
- }
|
|
- this.context = ctx;
|
|
- }
|
|
-
|
|
- private void subscription (Context ctx,
|
|
- Context.SubscriptionEventType t,
|
|
- uint32 index) {
|
|
- var type = t & Context.SubscriptionEventType.FACILITY_MASK;
|
|
- var event = t & Context.SubscriptionEventType.TYPE_MASK;
|
|
- switch (type) {
|
|
- case Context.SubscriptionEventType.SINK_INPUT:
|
|
- switch (event) {
|
|
- default: break;
|
|
- case Context.SubscriptionEventType.NEW:
|
|
- case Context.SubscriptionEventType.CHANGE:
|
|
- ctx.get_sink_input_info_list (this.get_sink_input_info);
|
|
- break;
|
|
- case Context.SubscriptionEventType.REMOVE:
|
|
- // A safe way of removing the sink_input
|
|
- var iter = active_sinks.map_iterator ();
|
|
- while (iter.next ()) {
|
|
- var sink_input = iter.get_value ();
|
|
- if (sink_input.index != index) continue;
|
|
- this.remove_active_sink (sink_input);
|
|
- iter.unset ();
|
|
- break;
|
|
- }
|
|
- break;
|
|
- }
|
|
- break;
|
|
- case Context.SubscriptionEventType.SINK:
|
|
- switch (event) {
|
|
- default: break;
|
|
- case Context.SubscriptionEventType.NEW:
|
|
- case Context.SubscriptionEventType.CHANGE:
|
|
- ctx.get_sink_info_by_index (index, this.get_sink_info);
|
|
- break;
|
|
- case Context.SubscriptionEventType.REMOVE:
|
|
- foreach (var sink in sinks.values) {
|
|
- if (sink.device_index != index) continue;
|
|
- sink.removed = true;
|
|
- sink.is_default = false;
|
|
- this.remove_device (sink);
|
|
- break;
|
|
- }
|
|
- break;
|
|
- }
|
|
- break;
|
|
- case Context.SubscriptionEventType.CARD:
|
|
- switch (event) {
|
|
- default: break;
|
|
- case Context.SubscriptionEventType.NEW:
|
|
- case Context.SubscriptionEventType.CHANGE:
|
|
- ctx.get_card_info_by_index (index, this.get_card_info);
|
|
- break;
|
|
- case Context.SubscriptionEventType.REMOVE:
|
|
- // A safe way of removing the sink_input
|
|
- var iter = sinks.map_iterator ();
|
|
- while (iter.next ()) {
|
|
- var device = iter.get_value ();
|
|
- if (device.card_index != index) continue;
|
|
- device.removed = true;
|
|
- device.is_default = false;
|
|
- iter.unset ();
|
|
- this.remove_device (device);
|
|
- break;
|
|
- }
|
|
- break;
|
|
- }
|
|
- break;
|
|
- case Context.SubscriptionEventType.SERVER:
|
|
- ctx.get_server_info (this.get_server_info);
|
|
- break;
|
|
- default: break;
|
|
- }
|
|
- }
|
|
-
|
|
- /*
|
|
- * Getters
|
|
- */
|
|
-
|
|
- /**
|
|
- * Gets called when any server value changes like default devices
|
|
- * Calls `get_card_info_list` and `get_sink_info_list`
|
|
- */
|
|
- private void get_server_info (Context ctx, ServerInfo ? info) {
|
|
- if (this.default_sink_name == null) {
|
|
- this.default_sink_name = info.default_sink_name;
|
|
- }
|
|
- if (this.default_sink_name != info.default_sink_name) {
|
|
- this.default_sink_name = info.default_sink_name;
|
|
- }
|
|
-
|
|
- ctx.get_card_info_list (this.get_card_info);
|
|
- ctx.get_sink_info_list (this.get_sink_info);
|
|
-
|
|
- foreach (var sink_input in active_sinks) {
|
|
- sink_input.value.active = false;
|
|
- }
|
|
- ctx.get_sink_input_info_list (this.get_sink_input_info);
|
|
- var iter = active_sinks.map_iterator ();
|
|
- while (iter.next ()) {
|
|
- var sink_input = iter.get_value ();
|
|
- if (!sink_input.active) {
|
|
- this.remove_active_sink (sink_input);
|
|
- iter.unset ();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private void get_sink_input_info (Context ctx, SinkInputInfo ? info, int eol) {
|
|
- if (info == null || eol != 0) return;
|
|
-
|
|
- uint32 id = PulseSinkInput.get_hash_map_key (info.index);
|
|
- PulseSinkInput sink_input = null;
|
|
- bool has_sink_input = active_sinks.has_key (id);
|
|
- if (has_sink_input) {
|
|
- sink_input = active_sinks.get (id);
|
|
- } else {
|
|
- sink_input = new PulseSinkInput ();
|
|
- }
|
|
-
|
|
- sink_input.index = info.index;
|
|
- sink_input.sink_index = info.sink;
|
|
- sink_input.client_index = info.client;
|
|
-
|
|
- sink_input.name = info.proplist.gets ("application.name");
|
|
- sink_input.application_binary = info.proplist
|
|
- .gets ("application.process.binary");
|
|
- sink_input.application_icon_name = info.proplist
|
|
- .gets ("application.icon_name");
|
|
- sink_input.media_name = info.proplist.gets ("media.name");
|
|
-
|
|
- sink_input.is_muted = info.mute == 1;
|
|
-
|
|
- sink_input.cvolume = info.volume;
|
|
- sink_input.channel_map = info.channel_map;
|
|
- sink_input.balance = sink_input.cvolume
|
|
- .get_balance (sink_input.channel_map);
|
|
- sink_input.volume_operations.foreach ((op) => {
|
|
- if (op.get_state () != Operation.State.RUNNING) {
|
|
- sink_input.volume_operations.remove (op);
|
|
- }
|
|
- return Source.CONTINUE;
|
|
- });
|
|
- if (sink_input.volume_operations.is_empty) {
|
|
- sink_input.volume = volume_to_double (
|
|
- sink_input.cvolume.max ());
|
|
- }
|
|
- sink_input.active = true;
|
|
-
|
|
- if (!has_sink_input) {
|
|
- active_sinks.set (id, sink_input);
|
|
- this.new_active_sink (sink_input);
|
|
- } else {
|
|
- this.change_active_sink (sink_input);
|
|
- }
|
|
- }
|
|
-
|
|
- private void get_card_info (Context ctx, CardInfo ? info, int eol) {
|
|
- if (info == null || eol != 0) return;
|
|
-
|
|
- unowned string ? description = info.proplist
|
|
- .gets ("device.description");
|
|
- unowned string ? props_icon = info.proplist
|
|
- .gets ("device.icon_name");
|
|
-
|
|
- PulseDevice[] ports = {};
|
|
- foreach (var port in info.ports) {
|
|
- if (port->available == PortAvailable.NO) continue;
|
|
-
|
|
- bool is_input = port->direction == Direction.INPUT;
|
|
- HashMap<string, PulseDevice> devices = this.sinks;
|
|
- string id = PulseDevice.get_hash_map_key (
|
|
- description, port.name);
|
|
-
|
|
- bool has_device = devices.has_key (id);
|
|
- PulseDevice device = has_device
|
|
- ? devices.get (id) : new PulseDevice ();
|
|
- bool device_is_removed = device.removed;
|
|
- device.removed = false;
|
|
-
|
|
- device.is_bluetooth = info.proplist.gets ("device.api") == "bluez5";
|
|
-
|
|
- device.card_index = info.index;
|
|
- device.direction = port.direction;
|
|
-
|
|
- device.card_name = info.name;
|
|
- device.card_description = description;
|
|
- device.card_active_profile = info.active_profile2->name;
|
|
-
|
|
- device.port_name = port.name;
|
|
- device.port_description = port.description;
|
|
- device.port_id = port->proplist.gets ("card.profile.port");
|
|
-
|
|
- // Get port profiles2 (profiles is "Superseded by profiles2")
|
|
- // and sort largest priority first
|
|
- var profiles = new ArrayList<unowned CardProfileInfo2 *>
|
|
- .wrap (port->profiles2);
|
|
-
|
|
- profiles.sort ((a, b) => {
|
|
- if (a->priority == b->priority) return 0;
|
|
- return a.priority > b.priority ? -1 : 1;
|
|
- });
|
|
- string[] new_profiles = {};
|
|
- Array<PulseCardProfile> pulse_profiles = new Array<PulseCardProfile> ();
|
|
- foreach (var profile in profiles) {
|
|
- new_profiles += profile->name;
|
|
-
|
|
- var card_profile = new PulseCardProfile (profile);
|
|
- pulse_profiles.append_val (card_profile);
|
|
- if (profile->name == device.card_active_profile) {
|
|
- device.active_profile = card_profile;
|
|
- }
|
|
- }
|
|
- device.port_profiles = new_profiles;
|
|
- device.profiles = pulse_profiles;
|
|
-
|
|
- device.icon_name = port->proplist.gets ("device.icon_name")
|
|
- ?? props_icon;
|
|
- if (device.icon_name == null) {
|
|
- device.icon_name = is_input
|
|
- ? "microphone-sensitivity-high"
|
|
- : "audio-speakers";
|
|
- }
|
|
- devices.set (id, device);
|
|
- ports += device;
|
|
- if (!has_device || device_is_removed) {
|
|
- this.new_device (device);
|
|
- }
|
|
- }
|
|
-
|
|
- /** Removes ports that are no longer available */
|
|
- var iter = sinks.map_iterator ();
|
|
- while (iter.next ()) {
|
|
- var device = iter.get_value ();
|
|
- if (device.card_index != info.index) continue;
|
|
- bool found = false;
|
|
- foreach (var p in ports) {
|
|
- if (device.get_current_hash_key ()
|
|
- == p.get_current_hash_key ()) {
|
|
- found = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
- if (!found) {
|
|
- iter.unset ();
|
|
- remove_device (device);
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private void get_sink_info (Context ctx, SinkInfo ? info, int eol) {
|
|
- if (info == null || eol != 0) return;
|
|
-
|
|
- bool found = false;
|
|
- foreach (PulseDevice device in sinks.values) {
|
|
- if (device.card_index == info.card) {
|
|
- // Sets the name and index to profiles that aren't active
|
|
- // Ex: The HDMI audio port that's not active
|
|
- device.device_name = info.name;
|
|
- device.device_description = info.description;
|
|
- device.device_index = info.index;
|
|
- // If the current selected sink profile is this
|
|
- if (info.active_port != null
|
|
- && info.active_port->name == device.port_name) {
|
|
- found = true;
|
|
-
|
|
- device.card_sink_port_name = info.active_port->name;
|
|
- bool is_default =
|
|
- device.device_name == this.default_sink_name;
|
|
- device.is_default = is_default;
|
|
-
|
|
- device.is_muted = info.mute == 1;
|
|
-
|
|
- device.is_virtual = info.proplist.gets ("node.virtual") == "true";
|
|
-
|
|
- device.cvolume = info.volume;
|
|
- device.channel_map = info.channel_map;
|
|
- device.balance = device.cvolume
|
|
- .get_balance (device.channel_map);
|
|
- device.volume_operations.foreach ((op) => {
|
|
- if (op.get_state () != Operation.State.RUNNING) {
|
|
- device.volume_operations.remove (op);
|
|
- }
|
|
- return Source.CONTINUE;
|
|
- });
|
|
- if (device.volume_operations.is_empty) {
|
|
- device.volume = volume_to_double (
|
|
- device.cvolume.max ());
|
|
- }
|
|
-
|
|
- if (is_default) {
|
|
- this.default_sink = device;
|
|
- this.change_default_device (device);
|
|
- }
|
|
- }
|
|
- this.change_device (device);
|
|
- }
|
|
- }
|
|
- // If not found, it's a cardless device
|
|
- if (found) return;
|
|
-
|
|
- HashMap<string, PulseDevice> devices = this.sinks;
|
|
- string id = PulseDevice.get_hash_map_key (
|
|
- info.index.to_string (), info.description);
|
|
- bool has_device = devices.has_key (id);
|
|
- PulseDevice device = has_device ? devices.get (id) : new PulseDevice ();
|
|
-
|
|
- bool device_is_removed = device.removed;
|
|
- device.removed = false;
|
|
-
|
|
- device.has_card = false;
|
|
-
|
|
- device.direction = PulseAudio.Direction.OUTPUT;
|
|
-
|
|
- device.device_name = info.name;
|
|
- device.device_description = info.description;
|
|
- device.device_index = info.index;
|
|
-
|
|
- bool is_default = device.device_name == this.default_source_name;
|
|
- device.is_default = is_default;
|
|
-
|
|
- device.is_muted = info.mute == 1;
|
|
-
|
|
- device.is_virtual = info.proplist.gets ("node.virtual") == "true";
|
|
-
|
|
- device.icon_name = "application-x-executable-symbolic";
|
|
-
|
|
- device.cvolume = info.volume;
|
|
- device.channel_map = info.channel_map;
|
|
- device.balance = device.cvolume
|
|
- .get_balance (device.channel_map);
|
|
- device.volume_operations.foreach ((op) => {
|
|
- if (op.get_state () != Operation.State.RUNNING) {
|
|
- device.volume_operations.remove (op);
|
|
- }
|
|
- return Source.CONTINUE;
|
|
- });
|
|
- if (device.volume_operations.is_empty) {
|
|
- device.volume = volume_to_double (
|
|
- device.cvolume.max ());
|
|
- }
|
|
-
|
|
- devices.set (id, device);
|
|
-
|
|
- if (is_default) {
|
|
- this.default_sink = device;
|
|
- this.change_default_device (device);
|
|
- }
|
|
- if (!has_device || device_is_removed) {
|
|
- this.new_device (device);
|
|
- }
|
|
- this.change_device (device);
|
|
- }
|
|
-
|
|
- /*
|
|
- * Setters
|
|
- */
|
|
- public void set_sink_input_volume (PulseSinkInput sink_input, double volume) {
|
|
- sink_input.volume_operations.foreach ((operation) => {
|
|
- if (operation.get_state () == Operation.State.RUNNING) {
|
|
- operation.cancel ();
|
|
- }
|
|
-
|
|
- sink_input.volume_operations.remove (operation);
|
|
- return GLib.Source.CONTINUE;
|
|
- });
|
|
-
|
|
- var cvol = sink_input.cvolume;
|
|
- cvol.scale (double_to_volume (volume));
|
|
- Operation ? operation = null;
|
|
- operation = context.set_sink_input_volume (
|
|
- sink_input.index, cvol);
|
|
- if (operation != null) {
|
|
- sink_input.volume_operations.add (operation);
|
|
- }
|
|
- }
|
|
-
|
|
- public void set_device_volume (PulseDevice device, double volume) {
|
|
- device.volume_operations.foreach ((operation) => {
|
|
- if (operation.get_state () == Operation.State.RUNNING) {
|
|
- operation.cancel ();
|
|
- }
|
|
-
|
|
- device.volume_operations.remove (operation);
|
|
- return GLib.Source.CONTINUE;
|
|
- });
|
|
-
|
|
- var cvol = device.cvolume;
|
|
- cvol.scale (double_to_volume (volume));
|
|
- Operation ? operation = null;
|
|
- if (device.direction == Direction.OUTPUT) {
|
|
- operation = context.set_sink_volume_by_name (
|
|
- device.device_name, cvol);
|
|
- }
|
|
-
|
|
- if (operation != null) {
|
|
- device.volume_operations.add (operation);
|
|
- }
|
|
- }
|
|
-
|
|
- public async void set_default_device (PulseDevice device) {
|
|
- if (device == null) return;
|
|
- bool is_input = device.direction == Direction.INPUT;
|
|
-
|
|
- // Only set port and card profile if the device is attached to a card
|
|
- if (device.has_card) {
|
|
- // Gets the profile that includes support for your other device
|
|
- string profile_name = device.port_profiles[0];
|
|
- PulseDevice alt_device = default_sink;
|
|
- if (alt_device != null) {
|
|
- foreach (var profile in device.port_profiles) {
|
|
- if (profile in alt_device.port_profiles) {
|
|
- profile_name = profile;
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (profile_name != device.card_active_profile) {
|
|
- yield set_card_profile_by_index (profile_name, device);
|
|
- yield wait_for_update<string> (device, "device-name");
|
|
- }
|
|
-
|
|
- if (!is_input) {
|
|
- if (device.port_name != device.card_sink_port_name) {
|
|
- debug ("Setting port to: %s", device.port_name);
|
|
- yield set_sink_port_by_name (device);
|
|
- }
|
|
- }
|
|
-
|
|
- if (device.device_name == null) {
|
|
- yield wait_for_update<string> (device, "device-name");
|
|
- }
|
|
- }
|
|
-
|
|
- if (!is_input) {
|
|
- if (device.device_name != default_sink_name) {
|
|
- debug ("Setting default sink to: %s", device.device_name);
|
|
- yield set_default_sink (device);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private async void wait_for_update<T> (PulseDevice device,
|
|
- string prop_name) {
|
|
- SourceFunc callback = wait_for_update.callback;
|
|
- ulong handler_id = 0;
|
|
- handler_id = device.notify[prop_name].connect ((s, p) => {
|
|
- T prop_value;
|
|
- device.get (prop_name, out prop_value);
|
|
- if (prop_value != null) {
|
|
- device.disconnect (handler_id);
|
|
- Idle.add ((owned) callback);
|
|
- }
|
|
- });
|
|
- yield;
|
|
- }
|
|
-
|
|
- public async void set_bluetooth_card_profile (PulseCardProfile profile,
|
|
- PulseDevice device) {
|
|
- context.set_card_profile_by_index (device.card_index,
|
|
- profile.name,
|
|
- (c, success) => {
|
|
- if (success == 1) {
|
|
- set_bluetooth_card_profile.callback ();
|
|
- } else {
|
|
- stderr.printf ("setting the card %s profile to %s failed\n",
|
|
- device.card_name, profile.name);
|
|
- }
|
|
- });
|
|
- yield;
|
|
- // Wait until the device has been updated
|
|
- yield wait_for_update<string> (device, "device-name");
|
|
- }
|
|
-
|
|
- private async void set_card_profile_by_index (string profile_name,
|
|
- PulseDevice device) {
|
|
- context.set_card_profile_by_index (device.card_index,
|
|
- profile_name,
|
|
- (c, success) => {
|
|
- if (success == 1) {
|
|
- set_card_profile_by_index.callback ();
|
|
- } else {
|
|
- stderr.printf ("setting the card %s profile to %s failed\n",
|
|
- device.card_name, profile_name);
|
|
- }
|
|
- });
|
|
- yield;
|
|
- }
|
|
-
|
|
- private async void set_sink_port_by_name (PulseDevice device) {
|
|
- context.set_sink_port_by_name (device.device_name,
|
|
- device.port_name,
|
|
- (c, success) => {
|
|
- if (success == 1) {
|
|
- set_sink_port_by_name.callback ();
|
|
- } else {
|
|
- stderr.printf ("setting sink port to %s failed\n",
|
|
- device.port_name);
|
|
- }
|
|
- });
|
|
- yield;
|
|
- }
|
|
-
|
|
- private async void set_default_sink (PulseDevice device) {
|
|
- context.set_default_sink (device.device_name, (c, success) => {
|
|
- if (success == 1) {
|
|
- set_default_sink.callback ();
|
|
- } else {
|
|
- stderr.printf ("setting default sink to %s failed\n",
|
|
- device.device_name);
|
|
- }
|
|
- });
|
|
- yield;
|
|
- }
|
|
-
|
|
- public void set_device_mute (bool state, PulseDevice device) {
|
|
- if (device.is_muted == state) return;
|
|
- if (device.direction == Direction.OUTPUT) {
|
|
- context.set_sink_mute_by_index (
|
|
- device.device_index, state);
|
|
- }
|
|
- }
|
|
-
|
|
- public void set_sink_input_mute (bool state, PulseSinkInput sink_input) {
|
|
- if (sink_input.is_muted == state) return;
|
|
- context.set_sink_input_mute (sink_input.index, state);
|
|
- }
|
|
-
|
|
- /*
|
|
- * Volume utils
|
|
- */
|
|
-
|
|
- private static double volume_to_double (PulseAudio.Volume vol) {
|
|
- double tmp = (double) (vol - PulseAudio.Volume.MUTED);
|
|
- return 100 * tmp / (double) (PulseAudio.Volume.NORM - PulseAudio.Volume.MUTED);
|
|
- }
|
|
-
|
|
- private static PulseAudio.Volume double_to_volume (double vol) {
|
|
- double tmp = (double) (PulseAudio.Volume.NORM - PulseAudio.Volume.MUTED) * vol / 100;
|
|
- return (PulseAudio.Volume) tmp + PulseAudio.Volume.MUTED;
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/controlCenter/widgets/volume/pulseDevice.vala b/src/controlCenter/widgets/volume/pulseDevice.vala
|
|
deleted file mode 100644
|
|
index accb8dc..0000000
|
|
--- a/src/controlCenter/widgets/volume/pulseDevice.vala
|
|
+++ /dev/null
|
|
@@ -1,135 +0,0 @@
|
|
-// 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> ();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/controlCenter/widgets/volume/volume.vala b/src/controlCenter/widgets/volume/volume.vala.2
|
|
index 5830602..e69de29 100644
|
|
--- a/src/controlCenter/widgets/volume/volume.vala
|
|
+++ b/src/controlCenter/widgets/volume/volume.vala.2
|
|
@@ -1,185 +0,0 @@
|
|
-namespace SwayNotificationCenter.Widgets {
|
|
- public class Volume : BaseWidget {
|
|
- public override string widget_name {
|
|
- get {
|
|
- return "volume";
|
|
- }
|
|
- }
|
|
-
|
|
- Gtk.Box main_volume_slider_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
|
|
- Gtk.Label label_widget = new Gtk.Label (null);
|
|
- Gtk.Scale slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);
|
|
-
|
|
- // Per app volume controll
|
|
- Gtk.ListBox levels_listbox;
|
|
- Gtk.Button reveal_button;
|
|
- Gtk.Revealer revealer;
|
|
- Gtk.Label no_sink_inputs_label;
|
|
- string empty_label = "No active sink input";
|
|
-
|
|
- string expand_label = "⇧";
|
|
- string collapse_label = "⇩";
|
|
- int icon_size = 24;
|
|
-
|
|
- Gtk.RevealerTransitionType revealer_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
|
|
- int revealer_duration = 250;
|
|
-
|
|
- private PulseDevice ? default_sink = null;
|
|
- private PulseDaemon client = new PulseDaemon ();
|
|
-
|
|
- private bool show_per_app;
|
|
-
|
|
- 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");
|
|
-
|
|
- show_per_app = get_prop<bool> (config, "show-per-app") ? true : false;
|
|
-
|
|
- string ? el = get_prop<string> (config, "empty-list-label");
|
|
- if (el != null) empty_label = el;
|
|
-
|
|
- string ? l1 = get_prop<string> (config, "expand-button-label");
|
|
- if (l1 != null) expand_label = l1;
|
|
- string ? l2 = get_prop<string> (config, "collapse-button-label");
|
|
- if (l2 != null) collapse_label = l2;
|
|
-
|
|
- int i = int.max (get_prop<int> (config, "icon-size"), 0);
|
|
- if (i != 0) icon_size = i;
|
|
-
|
|
- revealer_duration = int.max (0, get_prop<int> (config, "animation-duration"));
|
|
- if (revealer_duration == 0) revealer_duration = 250;
|
|
-
|
|
- string ? animation_type = get_prop<string> (config, "animation-type");
|
|
- if (animation_type != null) {
|
|
- switch (animation_type) {
|
|
- default:
|
|
- case "none":
|
|
- revealer_type = Gtk.RevealerTransitionType.NONE;
|
|
- break;
|
|
- case "slide_up":
|
|
- revealer_type = Gtk.RevealerTransitionType.SLIDE_UP;
|
|
- break;
|
|
- case "slide_down":
|
|
- revealer_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- this.orientation = Gtk.Orientation.VERTICAL;
|
|
-
|
|
- slider.draw_value = false;
|
|
-
|
|
- main_volume_slider_container.add (label_widget);
|
|
- main_volume_slider_container.pack_start (slider, true, true, 0);
|
|
- add (main_volume_slider_container);
|
|
-
|
|
- if (show_per_app) {
|
|
- reveal_button = new Gtk.Button.with_label (expand_label);
|
|
- revealer = new Gtk.Revealer ();
|
|
- revealer.transition_type = revealer_type;
|
|
- revealer.transition_duration = revealer_duration;
|
|
- levels_listbox = new Gtk.ListBox ();
|
|
- levels_listbox.get_style_context ().add_class ("per-app-volume");
|
|
- revealer.add (levels_listbox);
|
|
-
|
|
- if (this.client.active_sinks.size == 0) {
|
|
- no_sink_inputs_label = new Gtk.Label (empty_label);
|
|
- levels_listbox.add (no_sink_inputs_label);
|
|
- }
|
|
-
|
|
- foreach (var item in this.client.active_sinks.values) {
|
|
- levels_listbox.add (new SinkInputRow (item, client, icon_size));
|
|
- }
|
|
-
|
|
- this.client.change_active_sink.connect (active_sink_change);
|
|
- this.client.new_active_sink.connect (active_sink_added);
|
|
- this.client.remove_active_sink.connect (active_sink_removed);
|
|
-
|
|
- reveal_button.clicked.connect (() => {
|
|
- bool show = revealer.reveal_child;
|
|
- revealer.set_reveal_child (!show);
|
|
- if (show) {
|
|
- reveal_button.label = expand_label;
|
|
- } else {
|
|
- reveal_button.label = collapse_label;
|
|
- }
|
|
- });
|
|
-
|
|
- main_volume_slider_container.pack_end (reveal_button, false, false, 0);
|
|
- add (revealer);
|
|
- }
|
|
-
|
|
- show_all ();
|
|
- }
|
|
-
|
|
- public override void on_cc_visibility_change (bool val) {
|
|
- if (val) {
|
|
- this.client.start ();
|
|
- } else {
|
|
- this.client.close ();
|
|
- if (show_per_app) revealer.set_reveal_child (false);
|
|
- }
|
|
- }
|
|
-
|
|
- private void default_device_changed (PulseDevice device) {
|
|
- if (device != null && device.direction == PulseAudio.Direction.OUTPUT) {
|
|
- this.default_sink = device;
|
|
- slider.set_value (device.volume);
|
|
- }
|
|
- }
|
|
-
|
|
- private void active_sink_change (PulseSinkInput sink) {
|
|
- foreach (var row in levels_listbox.get_children ()) {
|
|
- if (row == null) continue;
|
|
- var s = (SinkInputRow) row;
|
|
- if (s.sink_input.cmp (sink)) {
|
|
- s.update (sink);
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private void active_sink_added (PulseSinkInput sink) {
|
|
- // one element added -> remove the empty label
|
|
- if (this.client.active_sinks.size == 1) {
|
|
- var label = levels_listbox.get_children ().first ().data;
|
|
- levels_listbox.remove ((Gtk.Widget) label);
|
|
- }
|
|
- levels_listbox.add (new SinkInputRow (sink, client, icon_size));
|
|
- show_all ();
|
|
- }
|
|
-
|
|
- private void active_sink_removed (PulseSinkInput sink) {
|
|
- foreach (var row in levels_listbox.get_children ()) {
|
|
- if (row == null) continue;
|
|
- var s = (SinkInputRow) row;
|
|
- if (s.sink_input.cmp (sink)) {
|
|
- levels_listbox.remove (row);
|
|
- break;
|
|
- }
|
|
- }
|
|
- if (levels_listbox.get_children ().length () == 0) {
|
|
- levels_listbox.add (no_sink_inputs_label);
|
|
- show_all ();
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/meson.build b/src/meson.build2
|
|
index 78ce9f1..1ca63a3 100644
|
|
--- a/src/meson.build
|
|
+++ b/src/meson.build2
|
|
@@ -40,12 +40,6 @@ 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',
|
|
- 'controlCenter/widgets/volume/pulseSinkInput.vala',
|
|
- 'controlCenter/widgets/volume/sinkInputRow.vala',
|
|
# Widget: Backlight Slider
|
|
'controlCenter/widgets/backlight/backlight.vala',
|
|
'controlCenter/widgets/backlight/backlightUtil.vala',
|
|
@@ -79,8 +73,6 @@ app_deps = [
|
|
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
|
|
diff --git a/src/style.css b/src/style2.css
|
|
index c9e93b3..e8fc5d6 100644
|
|
--- a/src/style.css
|
|
+++ b/src/style2.css
|
|
@@ -313,27 +313,6 @@
|
|
background: transparent;
|
|
}
|
|
|
|
-/* Volume widget */
|
|
-
|
|
-.widget-volume {
|
|
- background-color: @noti-bg;
|
|
- padding: 8px;
|
|
- margin: 8px;
|
|
- border-radius: 12px;
|
|
-}
|
|
-
|
|
-.widget-volume>box>button {
|
|
- background: transparent;
|
|
- border: none;
|
|
-}
|
|
-
|
|
-.per-app-volume {
|
|
- background-color: @noti-bg-alt;
|
|
- padding: 4px 8px 8px 8px;
|
|
- margin: 0px 8px 8px 8px;
|
|
- border-radius: 12px
|
|
-}
|
|
-
|
|
/* Backlight widget */
|
|
.widget-backlight {
|
|
background-color: @noti-bg;
|