Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
3054bc0
Added wireplumber mixer widget
AKArien Jan 21, 2026
6f771b0
Added scroll control in full mixer
AKArien Nov 2, 2025
7a0632d
Added middle click to mute
AKArien Nov 2, 2025
ba6e294
add configuration options for wireplumber widget
AKArien Nov 2, 2025
91a805d
uncrustify and style compliance
AKArien Dec 15, 2025
94c451a
uncrustify ?
AKArien Nov 3, 2025
4ed8808
remove section of the code pending on pr 306 (extra options)
AKArien Nov 3, 2025
45d419f
uncrustify ; though i cannot agree oops lol
AKArien Nov 3, 2025
df98546
Revert "remove section of the code pending on pr 306 (extra options)"
AKArien Nov 3, 2025
542f261
remove logic pertaining to pr 306 (vertical panel layouts)
AKArien Nov 3, 2025
0ea7f54
fixed include in animated-scale.cpp
AKArien Nov 3, 2025
e3485a1
add popup on change option
AKArien Nov 3, 2025
7ee1bcf
added support for mute gestures to individual control
AKArien Nov 3, 2025
2eb434c
added defaults, fallbacks and corrected compliance with config options
AKArien Nov 3, 2025
28856c7
fix scroll gesture, malformed panel.xml
AKArien Nov 3, 2025
59c0b7f
cleaned up a bit middle click to mute
AKArien Nov 3, 2025
2e95eba
fixed improper naming of on -> handle_config_reload
AKArien Nov 3, 2025
c4c28ec
removed obsolete functions
AKArien Nov 3, 2025
4254377
fixed left click actions on widget ; comments
AKArien Nov 3, 2025
d86eef3
added deselection guard for default + explanation
AKArien Nov 4, 2025
83a981f
adjust logic of updates in on_mixer_changed ; removed unused enum ; c…
AKArien Nov 4, 2025
00c74a3
cleaup
AKArien Nov 6, 2025
d7feb09
fix popover jank and incorrect settings reloading
AKArien Nov 6, 2025
0c30933
uncrustify
AKArien Nov 6, 2025
a9148fe
fixed gestures connection both in control and widget ; cleanup
AKArien Nov 6, 2025
4f64fd8
lil forgotten
AKArien Nov 6, 2025
e61e817
uncrustify
AKArien Nov 6, 2025
843b4f3
fixed doubling in existing widgets when catching up new one
AKArien Nov 16, 2025
caf71b9
de-duplicated icon_name_from_state
AKArien Nov 16, 2025
4bca93f
removed non breaking space and french quotes from comments
AKArien Nov 16, 2025
347d721
use std::string_view for string comparison
AKArien Nov 16, 2025
53262d8
fixed configuration to use .value() missed it oops
AKArien Nov 16, 2025
99c82c6
unique instead of raw pointers
AKArien Nov 16, 2025
be3a793
separators and labels not by new
AKArien Nov 16, 2025
38dead5
add autos, make construction less barbaric
AKArien Nov 16, 2025
78d9de5
cleanup
AKArien Nov 16, 2025
d446ea2
exported volumelevel and icons to be common between volume and wirepl…
AKArien Nov 16, 2025
c5a2688
fixed broken setting of default node
AKArien Dec 15, 2025
4816e6a
added explanation to volume-level.hpp
AKArien Dec 15, 2025
ab84eff
fix indentation in util meson
AKArien Dec 15, 2025
94ff628
uncrustify
AKArien Dec 15, 2025
325d37b
fix headers
AKArien Dec 15, 2025
03ffe93
removed stuff from old pulseaudio volume code
AKArien Dec 21, 2025
d0f0902
added and cleaned up comments, declarations and small moves
AKArien Jan 4, 2026
a55e348
separate wireplumber styles from volume and add size option
AKArien Jan 4, 2026
38f61f5
uncrustify
AKArien Jan 4, 2026
bbc110c
now uses a singleton, split into multiple files
AKArien Jan 5, 2026
694f548
fix face choice for default sink/source, static for config
AKArien Jan 5, 2026
8c315b5
remove warningns for volume and wireplumber + ipc header
AKArien Jan 6, 2026
4be57b9
minimum value of 0 for wireplumber scroll sensitifivy
AKArien Jan 6, 2026
17f27b9
improved icons handling and made it in common between volume and wp
AKArien Jan 6, 2026
a3f9290
added volume-level to build and build messages for volume/wp being un…
AKArien Jan 6, 2026
1b419ae
uncrustify
AKArien Jan 6, 2026
bcd88ad
use c++ glib instead of C glib functions
AKArien Jan 6, 2026
05a1754
fixed on_mixer_changed logic
AKArien Jan 6, 2026
327b857
fixed popups and ignore control that just did the change
AKArien Jan 6, 2026
0c16c5e
add uniform styles
AKArien Jan 6, 2026
b2d639e
cancel hiding of the popover after user interaction
AKArien Jan 6, 2026
9a0647f
fixed scrolling
AKArien Jan 7, 2026
7dc5b19
uncrustify
AKArien Jan 7, 2026
3e1d3c2
put more of setting default to wpcommon
AKArien Jan 7, 2026
486d8c2
oh come on what was i thinking
AKArien Jan 7, 2026
f9cce65
uncrustify sigh
AKArien Jan 7, 2026
0c77ecc
fixed setting of face for face = default device
AKArien Jan 7, 2026
1effebe
drop overloaded WfWpControlDevice copy to get a normal control for face
AKArien Jan 7, 2026
2cc67a8
maybe i should think of running uncrustify myself
AKArien Jan 7, 2026
4c65c66
how long have the labels been blank oops lol
AKArien Jan 7, 2026
e5a42ef
goofed up
AKArien Jan 7, 2026
ce30441
fixed initialisation with default sink/source as face
AKArien Jan 8, 2026
5fdc1e0
fixed crashes without face / now shows icon when faceless
AKArien Jan 8, 2026
47d1411
uncrustify
AKArien Jan 8, 2026
7f24845
uncrustify
AKArien Jan 8, 2026
3df09bf
added slider length option
AKArien Jan 8, 2026
8641e12
don’t repeat getting volume and mute
AKArien Jan 8, 2026
cdf4784
still don’t like it but if now if it fails it’s probaly less bad
AKArien Jan 8, 2026
a7f8e85
don’t take cube root there, since it’s already done
AKArien Jan 8, 2026
74c6f18
forgot negation
AKArien Jan 8, 2026
f303b15
restored mute oops
AKArien Jan 8, 2026
7a7456f
switch the priority of names
AKArien Jan 12, 2026
b6d3c1e
fix <long> formatting
AKArien Jan 13, 2026
bd5d07e
fixed typo
AKArien Jan 16, 2026
d5faee2
face -> quick target everywhere
AKArien Jan 16, 2026
97aff7b
add wireplumber to example
AKArien Jan 17, 2026
93e0ead
switch naming from WayfireWireplumber to WpMixer
AKArien Jan 17, 2026
e1777f4
changed name for user-facing documents and added a quick explanation of
AKArien Jan 17, 2026
51e5f2d
word change + uncrustify
AKArien Jan 17, 2026
a4a8a60
updated meson paths and includes for rename
AKArien Jan 17, 2026
e1b07d9
made the names not drive up the size of the grid
AKArien Jan 17, 2026
9ca37e8
add tooltips to main widget for current quick target
AKArien Jan 17, 2026
4df51c6
change icon for oor volume to emblem-unreadable
AKArien Jan 17, 2026
309f447
added option to put icosn on left
AKArien Jan 17, 2026
69c63bc
uncrustify + update some comments
AKArien Jan 17, 2026
3bda692
make WfOptions read in member functions static
AKArien Jan 17, 2026
7578cdc
added margins and use helper lambda
AKArien Jan 17, 2026
fce7dee
fix wall of critical warnings
AKArien Jan 18, 2026
8c9a9c5
fix label attaching and clean up update_gestures + uncrustify
AKArien Jan 18, 2026
86ec9cc
clean up gestures initialisation
AKArien Jan 18, 2026
2dd752d
add wireplumber-dev package to the build ci
AKArien Jan 18, 2026
8559059
prevent external changes from replacing the full mixer view
AKArien Jan 18, 2026
0670b5d
prevent new control appearance from replacing full mixer view
AKArien Jan 18, 2026
2f505f9
uncrustify
AKArien Jan 18, 2026
27b203f
fixed memory leak on reload
AKArien Jan 19, 2026
ee84b1a
change default target to default_sink and slight ini changes
AKArien Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
steps:
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/v3.22/community' > /etc/apk/repositories
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/v3.22/main' >> /etc/apk/repositories
- 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
- 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
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories
- run: echo 'http://dl-cdn.alpinelinux.org/alpine/edge/main' >> /etc/apk/repositories
- run: apk --no-cache add wayland-protocols wayfire-dev gtk4-layer-shell-dev gtk4-layer-shell
Expand Down
5 changes: 5 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ wfconfig = dependency('wf-config', version: '>=0.7.0') #TODO fallback submodule
epoxy = dependency('epoxy')
gtklayershell = dependency('gtk4-layer-shell-0', fallback: ['gtk4-layer-shell'])
libpulse = dependency('libpulse', required: get_option('pulse'))
wireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber'))
dbusmenu_gtk = dependency('dbusmenu-glib-0.4')
libgvc = subproject('gvc', default_options: ['static=true'], required: get_option('pulse'))
xkbregistry = dependency('xkbregistry')
Expand All @@ -35,6 +36,10 @@ if libpulse.found()
add_project_arguments('-DHAVE_PULSE=1', language: 'cpp')
endif

if wireplumber.found()
add_project_arguments('-DHAVE_WIREPLUMBER=1', language: 'cpp')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use spaces here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, apologies if i am somehow getting this wrong, but it seems other indentation in this file uses tabs ? such as the above libpulse check

endif

needs_libinotify = ['freebsd', 'dragonfly'].contains(host_machine.system())
libinotify = dependency('libinotify', required: needs_libinotify)

Expand Down
8 changes: 7 additions & 1 deletion meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ option(
value: 'auto',
description: 'Build pulseaudio volume widget',
)
option(
'wireplumber',
type: 'feature',
value: 'auto',
description: 'Build wireplumber and mixer widget',
)
option(
'wayland-logout',
type: 'boolean',
value: true,
description: 'Install wayland-logout',
)
)
99 changes: 99 additions & 0 deletions metadata/panel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,105 @@
</option>
</group>
<group>
<_short>Audio mixer (wireplumber)</_short>
<option name="wp_quick_target_choice" type="string">
<_short>Quick action target</_short>
<_long>Which audio control will be the target of the quick actions (scrolling and muting on the widget)</_long>
<default>default_sink</default>
<desc>
<value>default_sink</value>
<_name>Default sink</_name>
</desc>
<desc>
<value>default_source</value>
<_name>Default source</_name>
</desc>
<desc>
<value>last_change</value>
<_name>Last changed volume</_name>
</desc>
</option>
<option name="wp_popup_on_change" type="bool">
<_short>Pop up on change</_short>
<_long>Whether to show a popup with the new values upon a change to the quick action target</_long>
<default>true</default>
</option>
<option name="wp_display_timeout" type="double">
<_short>Display timeout</_short>
<default>2.5</default>
<min>0</min>
</option>
<option name="wp_slider_length" type="int">
<_short>Slider length</_short>
<default>300</default>
<min>1</min>
</option>
<option name="wp_scroll_sensitivity" type="double">
<_short>Scroll sensitivity</_short>
<default>1</default>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could set a value for this (0?) Otherwise, we could allow negative values but then we can remove the invert option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m sorry, i’m not quite sure what you mean ?
As for invert, we could indeed remove it, but i included it because i can’t help but feel it is way nicer as a user to have a toggle rather than making the value negative, at least when using wcm. Should it go ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m sorry, i’m not quite sure what you mean ?

You can set minimum and maximum values for integer options. <min>0</min> for example. I am fine with either retaining both options and setting the minimum to 0, or merging them together (in which case minimum makes no sense). Pick whichever one you like more.

<min>0</min>
</option>
<option name="wp_invert_scroll" type="bool">
<_short>Invert scroll</_short>
<_long>Inverts which scroll direction raises and lowers volume</_long>
<default>false</default>
</option>
<option name="wp_icons_on_left" type="bool">
<_short>Icons on left</_short>
<_long>Displays the mute and set default icon on the left</_long>
<default>false</default>
</option>
<option name="wp_left_click_action" type="string">
<_short>Left click action</_short>
<default>show_mixer</default>
<desc>
<value>show_mixer</value>
<_name>Show full mixer</_name>
</desc>
<desc>
<value>show_quick_target</value>
<_name>Show quick action target</_name>
</desc>
</option>
<option name="wp_middle_click_action" type="string">
<_short>Middle click action</_short>
<default>mute_quick_target</default>
<desc>
<value>show_mixer</value>
<_name>Show full mixer</_name>
</desc>
<desc>
<value>show_quick_target</value>
<_name>Show quick action target</_name>
</desc>
<desc>
<value>mute_quick_target</value>
<_name>Mute quick action target</_name>
</desc>
</option>
<option name="wp_right_click_action" type="string">
<_short>Right click action</_short>
<default>show_quick_target</default>
<desc>
<value>show_mixer</value>
<_name>Show full mixer</_name>
</desc>
<desc>
<value>show_quick_target</value>
<_name>Show quick action target</_name>
</desc>
<desc>
<value>mute_quick_target</value>
<_name>Mute quick action target</_name>
</desc>
</option>
<option name="wp_icon_size" type="int">
<_short>Icon size</_short>
<default>32</default>
<min>1</min>
</option>
</group>
<group>
<_short>Notifications</_short>
<option name="notifications_autohide_timeout" type="double">
<_short>Notifications Display Timeout</_short>
Expand Down
19 changes: 18 additions & 1 deletion src/panel/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,25 @@ deps = [
]

if libpulse.found()
widget_sources += 'widgets/volume.cpp'
widget_sources += [
'widgets/volume.cpp',
'widgets/volume-level.cpp',
]
deps += [libpulse, libgvc]
else
message('Pulse not found, volume widget will not be available.')
endif

if wireplumber.found()
widget_sources += [
'widgets/wp-mixer/wp-mixer.cpp',
'widgets/wp-mixer/wf-wp-control.cpp',
'widgets/wp-mixer/wp-common.cpp',
'widgets/volume-level.cpp',
]
deps += wireplumber
else
message('Wireplumber not found, mixer widget will not be available.')
endif

executable(
Expand Down
15 changes: 14 additions & 1 deletion src/panel/panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
#ifdef HAVE_PULSE
#include "widgets/volume.hpp"
#endif
#ifdef HAVE_WIREPLUMBER
#include "widgets/wp-mixer/wp-mixer.hpp"
#endif
#include "widgets/window-list/window-list.hpp"
#include "widgets/notifications/notification-center.hpp"
#include "widgets/tray/tray.hpp"
Expand Down Expand Up @@ -160,12 +163,21 @@ class WayfirePanel::impl
#ifdef HAVE_PULSE
return Widget(new WayfireVolume());
#else
#warning "Pulse not found, volume widget will not be available."
std::cerr << "Built without pulse support, volume widget "
" is not available." << std::endl;
#endif
}

if (name == "wp-mixer")
{
#ifdef HAVE_WIREPLUMBER
return Widget(new WayfireWpMixer());
#else
std::cerr << "Built without wireplumber support, mixer widget "
" is not available." << std::endl;
#endif
}

if (name == "window-list")
{
return Widget(new WayfireWindowList(output));
Expand Down Expand Up @@ -363,6 +375,7 @@ void WayfirePanelApp::on_activate()
new CssFromConfigInt("panel/battery_icon_size", ".battery image{-gtk-icon-size:", "px;}");
new CssFromConfigInt("panel/network_icon_size", ".network{-gtk-icon-size:", "px;}");
new CssFromConfigInt("panel/volume_icon_size", ".volume{-gtk-icon-size:", "px;}");
new CssFromConfigInt("panel/wp_icon_size", ".wireplumber{-gtk-icon-size:", "px;}");
new CssFromConfigInt("panel/notifications_icon_size", ".notification-center{-gtk-icon-size:", "px;}");
new CssFromConfigInt("panel/tray_icon_size", ".tray-button{-gtk-icon-size:", "px;}");
new CssFromConfigString("panel/background_color", ".wf-panel{background-color:", ";}");
Expand Down
42 changes: 42 additions & 0 deletions src/panel/widgets/volume-level.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <map>

#include "volume-level.hpp"

enum VolumeLevel
{
VOLUME_LEVEL_MUTE = 0,
VOLUME_LEVEL_LOW,
VOLUME_LEVEL_MED,
VOLUME_LEVEL_HIGH,
VOLUME_LEVEL_OOR, /* Out of range */
};

const std::map<VolumeLevel, std::string> volume_icons = {
{VOLUME_LEVEL_MUTE, "audio-volume-muted"},
{VOLUME_LEVEL_LOW, "audio-volume-low"},
{VOLUME_LEVEL_MED, "audio-volume-medium"},
{VOLUME_LEVEL_HIGH, "audio-volume-high"},
{VOLUME_LEVEL_OOR, "emblem-unreadable"},
};

// volume is expected to be from 0 to 1
std::string volume_icon_for(double volume)
{
double max = 1.0;
auto third = max / 3;
if (volume == 0)
{
return volume_icons.at(VOLUME_LEVEL_MUTE);
} else if ((volume > 0) && (volume <= third))
{
return volume_icons.at(VOLUME_LEVEL_LOW);
} else if ((volume > third) && (volume <= (third * 2)))
{
return volume_icons.at(VOLUME_LEVEL_MED);
} else if ((volume > (third * 2)) && (volume <= max))
{
return volume_icons.at(VOLUME_LEVEL_HIGH);
}

return volume_icons.at(VOLUME_LEVEL_OOR);
}
6 changes: 6 additions & 0 deletions src/panel/widgets/volume-level.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#include <string>

// helper in common for volume and wireplumber widgets icons handling
std::string volume_icon_for(double volume);
90 changes: 4 additions & 86 deletions src/panel/widgets/volume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,17 @@
#include <glibmm.h>
#include "volume.hpp"

WayfireVolumeScale::WayfireVolumeScale()
{
value_changed = this->signal_value_changed().connect([=] ()
{
this->current_volume.animate(this->get_value(), this->get_value());
if (this->user_changed_callback)
{
this->user_changed_callback();
}
});
}

WayfireVolumeScale::~WayfireVolumeScale()
{
value_changed.disconnect();
}

void WayfireVolumeScale::set_target_value(double value)
{
this->current_volume.animate(value);
add_tick_callback(sigc::mem_fun(*this, &WayfireVolumeScale::update_animation));
}

gboolean WayfireVolumeScale::update_animation(Glib::RefPtr<Gdk::FrameClock> frame_clock)
{
value_changed.block();
this->set_value(this->current_volume);
value_changed.unblock();
// Once we've finished fading, stop this callback
return this->current_volume.running() ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
}

double WayfireVolumeScale::get_target_value() const
{
return this->current_volume.end;
}

void WayfireVolumeScale::set_user_changed_callback(
std::function<void()> callback)
{
this->user_changed_callback = callback;
}

enum VolumeLevel
{
VOLUME_LEVEL_MUTE = 0,
VOLUME_LEVEL_LOW,
VOLUME_LEVEL_MED,
VOLUME_LEVEL_HIGH,
VOLUME_LEVEL_OOR, /* Out of range */
};

static VolumeLevel get_volume_level(pa_volume_t volume, pa_volume_t max)
{
auto third = max / 3;
if (volume == 0)
{
return VOLUME_LEVEL_MUTE;
} else if ((volume > 0) && (volume <= third))
{
return VOLUME_LEVEL_LOW;
} else if ((volume > third) && (volume <= (third * 2)))
{
return VOLUME_LEVEL_MED;
} else if ((volume > (third * 2)) && (volume <= max))
{
return VOLUME_LEVEL_HIGH;
}

return VOLUME_LEVEL_OOR;
}
#include "volume-level.hpp"

void WayfireVolume::update_icon()
{
VolumeLevel current =
get_volume_level(volume_scale.get_target_value(), max_norm);

if (gvc_stream && gvc_mixer_stream_get_is_muted(gvc_stream))
{
main_image.set_from_icon_name("audio-volume-muted");
main_image.set_from_icon_name(volume_icon_for(0)); // mute
return;
}

std::map<VolumeLevel, std::string> icon_name_from_state = {
{VOLUME_LEVEL_MUTE, "audio-volume-muted"},
{VOLUME_LEVEL_LOW, "audio-volume-low"},
{VOLUME_LEVEL_MED, "audio-volume-medium"},
{VOLUME_LEVEL_HIGH, "audio-volume-high"},
{VOLUME_LEVEL_OOR, "audio-volume-muted"},
};

main_image.set_from_icon_name(icon_name_from_state.at(current));
main_image.set_from_icon_name(volume_icon_for(volume_scale.get_target_value() / (double)max_norm));
}

bool WayfireVolume::on_popover_timeout(int timer)
Expand Down Expand Up @@ -197,8 +116,7 @@ void WayfireVolume::on_default_sink_changed()
volume_scale.set_increments(max_norm * scroll_sensitivity,
max_norm * scroll_sensitivity * 2);

/* Finally, update the displayed volume. However, do not show the
* popup */
/* Finally, update the displayed volume. However, do not show the popup */
set_volume(gvc_mixer_stream_get_volume(gvc_stream), VOLUME_FLAG_NO_ACTION);
}

Expand Down
Loading
Loading