Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
63ce587
Added wireplumber mixer widget
AKArien Dec 15, 2025
3f0f0d1
Added scroll control in full mixer
AKArien Nov 2, 2025
f6b9237
Added middle click to mute
AKArien Nov 2, 2025
92440cf
add configuration options for wireplumber widget
AKArien Nov 2, 2025
e3d6f90
uncrustify and style compliance
AKArien Dec 15, 2025
57d5537
uncrustify ?
AKArien Nov 3, 2025
c4aac9b
remove section of the code pending on pr 306 (extra options)
AKArien Nov 3, 2025
a6bfee9
uncrustify ; though i cannot agree oops lol
AKArien Nov 3, 2025
8cd51f9
Revert "remove section of the code pending on pr 306 (extra options)"
AKArien Nov 3, 2025
089c88f
remove logic pertaining to pr 306 (vertical panel layouts)
AKArien Nov 3, 2025
7325642
fixed include in animated-scale.cpp
AKArien Nov 3, 2025
6885c29
add popup on change option
AKArien Nov 3, 2025
fbfc51b
added support for mute gestures to individual control
AKArien Nov 3, 2025
ea746c3
added defaults, fallbacks and corrected compliance with config options
AKArien Nov 3, 2025
a2a9eed
fix scroll gesture, malformed panel.xml
AKArien Nov 3, 2025
77a6c62
cleaned up a bit middle click to mute
AKArien Nov 3, 2025
1f0a3c8
fixed improper naming of on -> handle_config_reload
AKArien Nov 3, 2025
287536d
removed obsolete functions
AKArien Nov 3, 2025
21306b7
fixed left click actions on widget ; comments
AKArien Nov 3, 2025
6bf91e8
added deselection guard for default + explanation
AKArien Nov 4, 2025
13fdf9e
adjust logic of updates in on_mixer_changed ; removed unused enum ; c…
AKArien Nov 4, 2025
86ebf6e
cleaup
AKArien Nov 6, 2025
86cb8bb
fix popover jank and incorrect settings reloading
AKArien Nov 6, 2025
1d427f9
uncrustify
AKArien Nov 6, 2025
91f7074
fixed gestures connection both in control and widget ; cleanup
AKArien Nov 6, 2025
3da844f
lil forgotten
AKArien Nov 6, 2025
848fa40
uncrustify
AKArien Nov 6, 2025
62bb72d
fixed doubling in existing widgets when catching up new one
AKArien Nov 16, 2025
1cb5f06
de-duplicated icon_name_from_state
AKArien Nov 16, 2025
2dd2dee
removed non breaking space and french quotes from comments
AKArien Nov 16, 2025
709fa32
use std::string_view for string comparison
AKArien Nov 16, 2025
dbb30bc
fixed configuration to use .value() missed it oops
AKArien Nov 16, 2025
afbf13e
unique instead of raw pointers
AKArien Nov 16, 2025
bdb2a2e
separators and labels not by new
AKArien Nov 16, 2025
69af698
add autos, make construction less barbaric
AKArien Nov 16, 2025
a47ab40
cleanup
AKArien Nov 16, 2025
650e254
exported volumelevel and icons to be common between volume and wirepl…
AKArien Nov 16, 2025
384e94a
fixed broken setting of default node
AKArien Dec 15, 2025
71503d2
added explanation to volume-level.hpp
AKArien Dec 15, 2025
44f38e9
fix indentation in util meson
AKArien Dec 15, 2025
7f49733
uncrustify
AKArien Dec 15, 2025
6cd0410
fix headers
AKArien Dec 15, 2025
333881e
removed stuff from old pulseaudio volume code
AKArien Dec 21, 2025
b82e1e8
added and cleaned up comments, declarations and small moves
AKArien Jan 4, 2026
5b6ab53
separate wireplumber styles from volume and add size option
AKArien Jan 4, 2026
966d2d2
uncrustify
AKArien Jan 4, 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
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
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',
)
)
93 changes: 93 additions & 0 deletions metadata/panel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,99 @@
</option>
</group>
<group>
<_short>Wireplumber mixer</_short>
<option name="wp_popup_on_change" type="bool">
<_short>Pop up on change</_short>
<_long>
Wether 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>
</option>
<option name="wp_scroll_sensitivity" type="double">
<_short>Scroll sensitivity</_short>
<default>1</default>
</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_face_choice" type="string">
<_short>Quick action target</_short>
<_long>
Which audio control will be the target of the "Quick action" (choice for the click action)
</_long>
<default>last_change</default>
<desc>
<value>last_change</value>
<_name>Last changed volume</_name>
</desc>
<desc>
<value>default_sink</value>
<_name>Default sink</_name>
</desc>
<desc>
<value>default_source</value>
<_name>Default source</_name>
</desc>
</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_face</value>
<_name>Show quick action target</_name>
</desc>
</option>
<option name="wp_middle_click_action" type="string">
<_short>Middle click action</_short>
<default>mute_face</default>
<desc>
<value>show_mixer</value>
<_name>Show full mixer</_name>
</desc>
<desc>
<value>show_face</value>
<_name>Show quick action target</_name>
</desc>
<desc>
<value>mute_face</value>
<_name>Mute quick action target</_name>
</desc>
</option>
<option name="wp_right_click_action" type="string">
<_short>Right click action</_short>
<default>show_face</default>
<desc>
<value>show_mixer</value>
<_name>Show full mixer</_name>
</desc>
<desc>
<value>show_face</value>
<_name>Show quick action target</_name>
</desc>
<desc>
<value>mute_face</value>
<_name>Mute quick action target</_name>
</desc>
</option>
<option name="wp_icon_size" type="int">
<_short>Volume 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
5 changes: 5 additions & 0 deletions src/panel/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ if libpulse.found()
deps += [libpulse, libgvc]
endif

if wireplumber.found()
widget_sources += 'widgets/wireplumber.cpp'
deps += wireplumber
endif

executable(
'wf-panel',
['panel.cpp'] + widget_sources,
Expand Down
15 changes: 15 additions & 0 deletions src/panel/panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
#ifdef HAVE_PULSE
#include "widgets/volume.hpp"
#endif
#ifdef HAVE_WIREPLUMBER
#include "widgets/wireplumber.hpp"
#endif
#include "widgets/window-list/window-list.hpp"
#include "widgets/notifications/notification-center.hpp"
#include "widgets/tray/tray.hpp"
Expand Down Expand Up @@ -167,6 +170,17 @@ class WayfirePanel::impl
#endif
}

if (name == "wireplumber")
{
#ifdef HAVE_WIREPLUMBER
return Widget(new WayfireWireplumber());
#else
#warning "Wireplumber not found, mixer widget will not be available."
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 @@ -364,6 +378,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
23 changes: 23 additions & 0 deletions src/panel/widgets/volume-level.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <string>
#include <map>

// header for volume and wireplumber widgets, which both use the following enum and map

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> 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"},
};
61 changes: 2 additions & 59 deletions src/panel/widgets/volume.cpp
Original file line number Diff line number Diff line change
@@ -1,56 +1,8 @@
#include <gtkmm.h>
#include <iostream>
#include <glibmm.h>
#include "volume.hpp"
#include "launchers.hpp"
#include "gtk-utils.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();
}
});
}

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 */
};
#include "volume-level.hpp"

static VolumeLevel get_volume_level(pa_volume_t volume, pa_volume_t max)
{
Expand Down Expand Up @@ -83,14 +35,6 @@ void WayfireVolume::update_icon()
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));
}

Expand Down Expand Up @@ -195,8 +139,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
37 changes: 5 additions & 32 deletions src/panel/widgets/volume.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,15 @@
#define WIDGETS_VOLUME_HPP

#include "../widget.hpp"
#include "wf-popover.hpp"
#include <gtkmm/image.h>
#include <gtkmm/scale.h>
#include "../../util/animated-scale.hpp"
#include <pulse/pulseaudio.h>
#include "gvc-mixer-control.h"
#include <wayfire/util/duration.hpp>

/**
* A custom scale which animates transitions when its value is
* changed programatically.
*/
class WayfireVolumeScale : public Gtk::Scale
{
wf::animation::simple_animation_t current_volume{wf::create_option(200)};
sigc::connection value_changed;
std::function<void()> user_changed_callback;

public:
WayfireVolumeScale();

/* Gets the current target value */
double get_target_value() const;
/* Set a target value to animate towards */
void set_target_value(double value);
/** Set the callback when the user changes the scale value */
void set_user_changed_callback(std::function<void()> callback);
/** Callback to animate volume control */
gboolean update_animation(Glib::RefPtr<Gdk::FrameClock> clock);
};

class WayfireVolume : public WayfireWidget
{
Gtk::Image main_image;
WayfireVolumeScale volume_scale;
WayfireAnimatedScale volume_scale;
Gtk::Button button;
Gtk::Popover popover;

Expand Down Expand Up @@ -71,9 +46,8 @@ class WayfireVolume : public WayfireWidget
};

/**
* Set the current volume level to volume_level.
* This updates both the popover scale and the real pulseaudio volume,
* depending on the passed flags.
* Set the current volume level to volume_level. This updates both the popover scale and the real
* pulseaudio volume, depending on the passed flags.
*
* Precondition: volume_level should be between 0 and max_norm
*/
Expand All @@ -94,8 +68,7 @@ class WayfireVolume : public WayfireWidget
void on_default_sink_changed();

/**
* Check whether the popover should be auto-hidden, and if yes, start
* a timer to hide it
* Check whether the popover should be auto-hidden, and if yes, start a timer to hide it
*/
void check_set_popover_timeout();
};
Expand Down
Loading