Skip to content

Commit 5771c4e

Browse files
AKArientrigg
authored andcommitted
panel: add wireplumber mixer widget (#307)
* Added wireplumber mixer widget * Added scroll control in full mixer * Added middle click to mute * add configuration options for wireplumber widget * uncrustify and style compliance * uncrustify ? * remove section of the code pending on pr 306 (extra options) * uncrustify ; though i cannot agree oops lol * Revert "remove section of the code pending on pr 306 (extra options)" This reverts commit 81bfaf3. * remove logic pertaining to pr 306 (vertical panel layouts) * fixed include in animated-scale.cpp * add popup on change option * added support for mute gestures to individual control * added defaults, fallbacks and corrected compliance with config options * fix scroll gesture, malformed panel.xml * cleaned up a bit middle click to mute * fixed improper naming of on -> handle_config_reload * removed obsolete functions * fixed left click actions on widget ; comments * added deselection guard for default + explanation * adjust logic of updates in on_mixer_changed ; removed unused enum ; comments * cleaup * fix popover jank and incorrect settings reloading * uncrustify * fixed gestures connection both in control and widget ; cleanup * lil forgotten * uncrustify * fixed doubling in existing widgets when catching up new one * de-duplicated icon_name_from_state * removed non breaking space and french quotes from comments * use std::string_view for string comparison * fixed configuration to use .value() missed it oops * unique instead of raw pointers * separators and labels not by new * add autos, make construction less barbaric * cleanup * exported volumelevel and icons to be common between volume and wireplumber * fixed broken setting of default node * added explanation to volume-level.hpp * fix indentation in util meson * uncrustify * fix headers * removed stuff from old pulseaudio volume code * added and cleaned up comments, declarations and small moves * separate wireplumber styles from volume and add size option * uncrustify * now uses a singleton, split into multiple files * fix face choice for default sink/source, static for config * remove warningns for volume and wireplumber + ipc header * minimum value of 0 for wireplumber scroll sensitifivy * improved icons handling and made it in common between volume and wp * added volume-level to build and build messages for volume/wp being unavailable * uncrustify * use c++ glib instead of C glib functions * fixed on_mixer_changed logic * fixed popups and ignore control that just did the change * add uniform styles * cancel hiding of the popover after user interaction * fixed scrolling fixedsegfaults on face = default sink/source try harder to not be faceless actually show face initially properly always refresh face/controls icons * uncrustify * put more of setting default to wpcommon formatting fixed node media classes added proper guard to getting mute and volume * oh come on what was i thinking * uncrustify sigh * fixed setting of face for face = default device * drop overloaded WfWpControlDevice copy to get a normal control for face * maybe i should think of running uncrustify myself * how long have the labels been blank oops lol * goofed up * fixed initialisation with default sink/source as face * fixed crashes without face / now shows icon when faceless * uncrustify * uncrustify * added slider length option * don’t repeat getting volume and mute * still don’t like it but if now if it fails it’s probaly less bad * don’t take cube root there, since it’s already done * forgot negation * restored mute oops * switch the priority of names * fix <long> formatting * fixed typo * face -> quick target everywhere * add wireplumber to example * switch naming from WayfireWireplumber to WpMixer * changed name for user-facing documents and added a quick explanation of pipewire and pulseaudio to example ini + moved the quick target selection to the top of config sections * word change + uncrustify * updated meson paths and includes for rename * made the names not drive up the size of the grid name change in metadata added spacing to boxes * add tooltips to main widget for current quick target * change icon for oor volume to emblem-unreadable * added option to put icosn on left * uncrustify + update some comments * make WfOptions read in member functions static * added margins and use helper lambda * fix wall of critical warnings * fix label attaching and clean up update_gestures + uncrustify * clean up gestures initialisation * add wireplumber-dev package to the build ci * prevent external changes from replacing the full mixer view * prevent new control appearance from replacing full mixer view * uncrustify * fixed memory leak on reload * change default target to default_sink and slight ini changes * adjust naming to popup * make spacing a proper config option instead of hacking it and make it properly reload * also update the ini ex
1 parent a10104e commit 5771c4e

20 files changed

Lines changed: 1680 additions & 124 deletions

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
steps:
1010
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/v3.22/community' > /etc/apk/repositories
1111
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/v3.22/main' >> /etc/apk/repositories
12-
- run: apk --no-cache add git g++ binutils pkgconf meson ninja musl-dev gtkmm4-dev vala gobject-introspection gobject-introspection-dev pulseaudio-dev libdbusmenu-glib-dev alsa-lib-dev yyjson-dev
12+
- run: apk --no-cache add git g++ binutils pkgconf meson ninja musl-dev gtkmm4-dev vala gobject-introspection gobject-introspection-dev pulseaudio-dev wireplumber-dev libdbusmenu-glib-dev alsa-lib-dev yyjson-dev
1313
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories
1414
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/edge/main' >> /etc/apk/repositories
1515
- run: apk --no-cache add wayland-protocols wayfire-dev gtk4-layer-shell-dev gtk4-layer-shell

meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ wfconfig = dependency('wf-config', version: '>=0.7.0') #TODO fallback submodule
2121
epoxy = dependency('epoxy')
2222
gtklayershell = dependency('gtk4-layer-shell-0', fallback: ['gtk4-layer-shell'])
2323
libpulse = dependency('libpulse', required: get_option('pulse'))
24+
wireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber'))
2425
dbusmenu_gtk = dependency('dbusmenu-glib-0.4')
2526
libgvc = subproject('gvc', default_options: ['static=true'], required: get_option('pulse'))
2627
xkbregistry = dependency('xkbregistry')
@@ -35,6 +36,10 @@ if libpulse.found()
3536
add_project_arguments('-DHAVE_PULSE=1', language: 'cpp')
3637
endif
3738

39+
if wireplumber.found()
40+
add_project_arguments('-DHAVE_WIREPLUMBER=1', language: 'cpp')
41+
endif
42+
3843
needs_libinotify = ['freebsd', 'dragonfly'].contains(host_machine.system())
3944
libinotify = dependency('libinotify', required: needs_libinotify)
4045

meson_options.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ option(
44
value: 'auto',
55
description: 'Build pulseaudio volume widget',
66
)
7+
option(
8+
'wireplumber',
9+
type: 'feature',
10+
value: 'auto',
11+
description: 'Build wireplumber and mixer widget',
12+
)
713
option(
814
'wayland-logout',
915
type: 'boolean',
1016
value: true,
1117
description: 'Install wayland-logout',
12-
)
18+
)

metadata/panel.xml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,110 @@
249249
</option>
250250
</group>
251251
<group>
252+
<_short>Audio mixer (wireplumber)</_short>
253+
<option name="wp_quick_target_choice" type="string">
254+
<_short>Quick action target</_short>
255+
<_long>Which audio control will be the target of the quick actions (scrolling and muting on the widget)</_long>
256+
<default>default_sink</default>
257+
<desc>
258+
<value>default_sink</value>
259+
<_name>Default sink</_name>
260+
</desc>
261+
<desc>
262+
<value>default_source</value>
263+
<_name>Default source</_name>
264+
</desc>
265+
<desc>
266+
<value>last_change</value>
267+
<_name>Last changed volume</_name>
268+
</desc>
269+
</option>
270+
<option name="wp_popup_on_change" type="bool">
271+
<_short>Pop up on change</_short>
272+
<_long>Whether to show a popup with the new values upon a change to the quick action target</_long>
273+
<default>true</default>
274+
</option>
275+
<option name="wp_popup_timeout" type="double">
276+
<_short>Popup timeout</_short>
277+
<default>2.5</default>
278+
<min>0</min>
279+
</option>
280+
<option name="wp_slider_length" type="int">
281+
<_short>Slider length</_short>
282+
<default>300</default>
283+
<min>1</min>
284+
</option>
285+
<option name="wp_scroll_sensitivity" type="double">
286+
<_short>Scroll sensitivity</_short>
287+
<default>1</default>
288+
<min>0</min>
289+
</option>
290+
<option name="wp_invert_scroll" type="bool">
291+
<_short>Invert scroll</_short>
292+
<_long>Inverts which scroll direction raises and lowers volume</_long>
293+
<default>false</default>
294+
</option>
295+
<option name="wp_icons_on_left" type="bool">
296+
<_short>Icons on left</_short>
297+
<_long>Displays the mute and set default icon on the left</_long>
298+
<default>false</default>
299+
</option>
300+
<option name="wp_spacing" type="int">
301+
<_short>Spacing between controls</_short>
302+
<default>8</default>
303+
<min>0</min>
304+
</option>
305+
<option name="wp_left_click_action" type="string">
306+
<_short>Left click action</_short>
307+
<default>show_mixer</default>
308+
<desc>
309+
<value>show_mixer</value>
310+
<_name>Show full mixer</_name>
311+
</desc>
312+
<desc>
313+
<value>show_quick_target</value>
314+
<_name>Show quick action target</_name>
315+
</desc>
316+
</option>
317+
<option name="wp_middle_click_action" type="string">
318+
<_short>Middle click action</_short>
319+
<default>mute_quick_target</default>
320+
<desc>
321+
<value>show_mixer</value>
322+
<_name>Show full mixer</_name>
323+
</desc>
324+
<desc>
325+
<value>show_quick_target</value>
326+
<_name>Show quick action target</_name>
327+
</desc>
328+
<desc>
329+
<value>mute_quick_target</value>
330+
<_name>Mute quick action target</_name>
331+
</desc>
332+
</option>
333+
<option name="wp_right_click_action" type="string">
334+
<_short>Right click action</_short>
335+
<default>show_quick_target</default>
336+
<desc>
337+
<value>show_mixer</value>
338+
<_name>Show full mixer</_name>
339+
</desc>
340+
<desc>
341+
<value>show_quick_target</value>
342+
<_name>Show quick action target</_name>
343+
</desc>
344+
<desc>
345+
<value>mute_quick_target</value>
346+
<_name>Mute quick action target</_name>
347+
</desc>
348+
</option>
349+
<option name="wp_icon_size" type="int">
350+
<_short>Icon size</_short>
351+
<default>32</default>
352+
<min>1</min>
353+
</option>
354+
</group>
355+
<group>
252356
<_short>Notifications</_short>
253357
<option name="notifications_autohide_timeout" type="double">
254358
<_short>Notifications Display Timeout</_short>

src/panel/meson.build

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,25 @@ deps = [
3535
]
3636

3737
if libpulse.found()
38-
widget_sources += 'widgets/volume.cpp'
38+
widget_sources += [
39+
'widgets/volume.cpp',
40+
'widgets/volume-level.cpp',
41+
]
3942
deps += [libpulse, libgvc]
43+
else
44+
message('Pulse not found, volume widget will not be available.')
45+
endif
46+
47+
if wireplumber.found()
48+
widget_sources += [
49+
'widgets/wp-mixer/wp-mixer.cpp',
50+
'widgets/wp-mixer/wf-wp-control.cpp',
51+
'widgets/wp-mixer/wp-common.cpp',
52+
'widgets/volume-level.cpp',
53+
]
54+
deps += wireplumber
55+
else
56+
message('Wireplumber not found, mixer widget will not be available.')
4057
endif
4158

4259
executable(

src/panel/panel.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#ifdef HAVE_PULSE
2727
#include "widgets/volume.hpp"
2828
#endif
29+
#ifdef HAVE_WIREPLUMBER
30+
#include "widgets/wp-mixer/wp-mixer.hpp"
31+
#endif
2932
#include "widgets/window-list/window-list.hpp"
3033
#include "widgets/notifications/notification-center.hpp"
3134
#include "widgets/tray/tray.hpp"
@@ -160,12 +163,21 @@ class WayfirePanel::impl
160163
#ifdef HAVE_PULSE
161164
return Widget(new WayfireVolume());
162165
#else
163-
#warning "Pulse not found, volume widget will not be available."
164166
std::cerr << "Built without pulse support, volume widget "
165167
" is not available." << std::endl;
166168
#endif
167169
}
168170

171+
if (name == "wp-mixer")
172+
{
173+
#ifdef HAVE_WIREPLUMBER
174+
return Widget(new WayfireWpMixer());
175+
#else
176+
std::cerr << "Built without wireplumber support, mixer widget "
177+
" is not available." << std::endl;
178+
#endif
179+
}
180+
169181
if (name == "window-list")
170182
{
171183
return Widget(new WayfireWindowList(output));
@@ -363,6 +375,7 @@ void WayfirePanelApp::on_activate()
363375
new CssFromConfigInt("panel/battery_icon_size", ".battery image{-gtk-icon-size:", "px;}");
364376
new CssFromConfigInt("panel/network_icon_size", ".network{-gtk-icon-size:", "px;}");
365377
new CssFromConfigInt("panel/volume_icon_size", ".volume{-gtk-icon-size:", "px;}");
378+
new CssFromConfigInt("panel/wp_icon_size", ".wireplumber{-gtk-icon-size:", "px;}");
366379
new CssFromConfigInt("panel/notifications_icon_size", ".notification-center{-gtk-icon-size:", "px;}");
367380
new CssFromConfigInt("panel/tray_icon_size", ".tray-button{-gtk-icon-size:", "px;}");
368381
new CssFromConfigString("panel/background_color", ".wf-panel{background-color:", ";}");

src/panel/widgets/volume-level.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include <map>
2+
3+
#include "volume-level.hpp"
4+
5+
enum VolumeLevel
6+
{
7+
VOLUME_LEVEL_MUTE = 0,
8+
VOLUME_LEVEL_LOW,
9+
VOLUME_LEVEL_MED,
10+
VOLUME_LEVEL_HIGH,
11+
VOLUME_LEVEL_OOR, /* Out of range */
12+
};
13+
14+
const std::map<VolumeLevel, std::string> volume_icons = {
15+
{VOLUME_LEVEL_MUTE, "audio-volume-muted"},
16+
{VOLUME_LEVEL_LOW, "audio-volume-low"},
17+
{VOLUME_LEVEL_MED, "audio-volume-medium"},
18+
{VOLUME_LEVEL_HIGH, "audio-volume-high"},
19+
{VOLUME_LEVEL_OOR, "emblem-unreadable"},
20+
};
21+
22+
// volume is expected to be from 0 to 1
23+
std::string volume_icon_for(double volume)
24+
{
25+
double max = 1.0;
26+
auto third = max / 3;
27+
if (volume == 0)
28+
{
29+
return volume_icons.at(VOLUME_LEVEL_MUTE);
30+
} else if ((volume > 0) && (volume <= third))
31+
{
32+
return volume_icons.at(VOLUME_LEVEL_LOW);
33+
} else if ((volume > third) && (volume <= (third * 2)))
34+
{
35+
return volume_icons.at(VOLUME_LEVEL_MED);
36+
} else if ((volume > (third * 2)) && (volume <= max))
37+
{
38+
return volume_icons.at(VOLUME_LEVEL_HIGH);
39+
}
40+
41+
return volume_icons.at(VOLUME_LEVEL_OOR);
42+
}

src/panel/widgets/volume-level.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
// helper in common for volume and wireplumber widgets icons handling
6+
std::string volume_icon_for(double volume);

src/panel/widgets/volume.cpp

Lines changed: 4 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,98 +2,17 @@
22
#include <glibmm.h>
33
#include "volume.hpp"
44

5-
WayfireVolumeScale::WayfireVolumeScale()
6-
{
7-
value_changed = this->signal_value_changed().connect([=] ()
8-
{
9-
this->current_volume.animate(this->get_value(), this->get_value());
10-
if (this->user_changed_callback)
11-
{
12-
this->user_changed_callback();
13-
}
14-
});
15-
}
16-
17-
WayfireVolumeScale::~WayfireVolumeScale()
18-
{
19-
value_changed.disconnect();
20-
}
21-
22-
void WayfireVolumeScale::set_target_value(double value)
23-
{
24-
this->current_volume.animate(value);
25-
add_tick_callback(sigc::mem_fun(*this, &WayfireVolumeScale::update_animation));
26-
}
27-
28-
gboolean WayfireVolumeScale::update_animation(Glib::RefPtr<Gdk::FrameClock> frame_clock)
29-
{
30-
value_changed.block();
31-
this->set_value(this->current_volume);
32-
value_changed.unblock();
33-
// Once we've finished fading, stop this callback
34-
return this->current_volume.running() ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
35-
}
36-
37-
double WayfireVolumeScale::get_target_value() const
38-
{
39-
return this->current_volume.end;
40-
}
41-
42-
void WayfireVolumeScale::set_user_changed_callback(
43-
std::function<void()> callback)
44-
{
45-
this->user_changed_callback = callback;
46-
}
47-
48-
enum VolumeLevel
49-
{
50-
VOLUME_LEVEL_MUTE = 0,
51-
VOLUME_LEVEL_LOW,
52-
VOLUME_LEVEL_MED,
53-
VOLUME_LEVEL_HIGH,
54-
VOLUME_LEVEL_OOR, /* Out of range */
55-
};
56-
57-
static VolumeLevel get_volume_level(pa_volume_t volume, pa_volume_t max)
58-
{
59-
auto third = max / 3;
60-
if (volume == 0)
61-
{
62-
return VOLUME_LEVEL_MUTE;
63-
} else if ((volume > 0) && (volume <= third))
64-
{
65-
return VOLUME_LEVEL_LOW;
66-
} else if ((volume > third) && (volume <= (third * 2)))
67-
{
68-
return VOLUME_LEVEL_MED;
69-
} else if ((volume > (third * 2)) && (volume <= max))
70-
{
71-
return VOLUME_LEVEL_HIGH;
72-
}
73-
74-
return VOLUME_LEVEL_OOR;
75-
}
5+
#include "volume-level.hpp"
766

777
void WayfireVolume::update_icon()
788
{
79-
VolumeLevel current =
80-
get_volume_level(volume_scale.get_target_value(), max_norm);
81-
829
if (gvc_stream && gvc_mixer_stream_get_is_muted(gvc_stream))
8310
{
84-
main_image.set_from_icon_name("audio-volume-muted");
11+
main_image.set_from_icon_name(volume_icon_for(0)); // mute
8512
return;
8613
}
8714

88-
std::map<VolumeLevel, std::string> icon_name_from_state = {
89-
{VOLUME_LEVEL_MUTE, "audio-volume-muted"},
90-
{VOLUME_LEVEL_LOW, "audio-volume-low"},
91-
{VOLUME_LEVEL_MED, "audio-volume-medium"},
92-
{VOLUME_LEVEL_HIGH, "audio-volume-high"},
93-
{VOLUME_LEVEL_OOR, "audio-volume-muted"},
94-
};
95-
96-
main_image.set_from_icon_name(icon_name_from_state.at(current));
15+
main_image.set_from_icon_name(volume_icon_for(volume_scale.get_target_value() / (double)max_norm));
9716
}
9817

9918
bool WayfireVolume::on_popover_timeout(int timer)
@@ -197,8 +116,7 @@ void WayfireVolume::on_default_sink_changed()
197116
volume_scale.set_increments(max_norm * scroll_sensitivity,
198117
max_norm * scroll_sensitivity * 2);
199118

200-
/* Finally, update the displayed volume. However, do not show the
201-
* popup */
119+
/* Finally, update the displayed volume. However, do not show the popup */
202120
set_volume(gvc_mixer_stream_get_volume(gvc_stream), VOLUME_FLAG_NO_ACTION);
203121
}
204122

0 commit comments

Comments
 (0)