Skip to content

Add varlink settings daemon interfaces for handling audio with pipewire#135

Open
mmstick wants to merge 54 commits into
masterfrom
varlink
Open

Add varlink settings daemon interfaces for handling audio with pipewire#135
mmstick wants to merge 54 commits into
masterfrom
varlink

Conversation

@mmstick
Copy link
Copy Markdown
Member

@mmstick mmstick commented Mar 12, 2026

Closes pop-os/cosmic-epoch#2776

  • Requires to be installed alongside all these
  • Creates a varlink socket at ${XDG_RUNTIME_DIR}/com.system76.CosmicSettings
    • Contains the com.system76.CosmicSettings.Audio interface
      • Which may be introspected with varlinkctl introspect ${XDG_RUNTIME_DIR}/com.system76.CosmicSettings com.system76.CosmicSettings.Audio
      • And called with varlinkctl call ${XDG_RUNTIME_DIR}/com.system76.CosmicSettings com.system76.CosmicSettings.Audio.{{name-of-method}} {{json-formatted-params}}
  • Moves all audio-handling logic from cosmic-settings, cosmic-applet-audio, and cosmic-osd to the settings daemon
    • Thus dropping dependencies on libpipewire and libpulse as the settings daemon will be the only process that needs to communicate with libpipewire.
  • Improves headphone and headset profile detection
    • The settings daemon now detects if a bluetooth headset was connected so that the headset OSD can be displayed for choosing between headphone and headset profiles.
    • cosmic-osd has been modified to request a headphone or headset profile from the settings-daemon. The settings daemon is able to make better decisions about what profiles and routes to set since it is already tracking everything.
    • Fixes the device route and profile switching issues that cosmic-settings caused by its attempt to automatically select profiles and routes on startup.
  • The mono audio accessibility feature is now implemented using Pipewire 1.4's node.features.audio.mono
    • Because we are using pipewire-pulse, this will automatically call pactl send-message /core pipewire-pulse:force-mono-output true|false when that setting is configured to force mono audio on all sinks connecting through pipewire-pulse.
    • This drops the need for a virtual mono sink node to be loaded through pactl
  • Adds a varlink method for subscribing to audio events through an anonymous pipe that streams a RON-based codec
    • On connect, clients will receive a batch of events to describe the current state of audio devices and nodes. Subsequent changes to audio state will then be broadcasted to all connected clients
  • Existing DBus methods will continue to be supported so existing audio shortcuts using them will continue to work

  • I have disclosed use of any AI generated code in my commit messages.
    • If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR.
    • In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment.
  • I understand these changes in full and will be able to respond to review comments.
  • My change is accurately described in the commit message.
  • My contribution is tested and working as described.
  • I have read the Developer Certificate of Origin and certify my contribution under its conditions.

@mmstick mmstick changed the title feat: impl varlink service w/ audio APIs Add varlink settings daemon interfaces for handling audio with pipewire Mar 12, 2026
@mmstick mmstick force-pushed the varlink branch 4 times, most recently from 3e874a6 to de82cd9 Compare March 14, 2026 03:31
@mmstick mmstick marked this pull request as ready for review March 14, 2026 03:47
@mmstick mmstick force-pushed the varlink branch 3 times, most recently from 9487b0b to e353775 Compare March 17, 2026 15:26
@ids1024
Copy link
Copy Markdown
Member

ids1024 commented Mar 18, 2026

At first I was a little unsure about if we want to get varlink involved here rather than using DBus for things, but looking at the varlink specs and at zlink, as well as interacting with this using clients, I like it. Seems like a fairly clean and simple protocol without the complexity of the DBus and the DBus daemon, and using JSON presumably should make serialization a bit cleaner than dealing with the DBus type system. So that all seems good.

One thing I've been thinking of that would be helpful for some features is if our subscription to Pipewire events provided a way to know which client changed the volume/etc. But as far as I can tell that isn't possible (I guess even the session manager client like WirePlumber doesn't have that information; it just sets permissions for clients, which the Pipewire daemon itself enforces).

In any case, presumably everything here should be extendable since serde defaults to ignoring unrecognized fields, and #[serde(other)] is used on enums.

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Mar 18, 2026

My experience with zlink so far is that it's almost identical to zbus when using the included client proxy and service macros. zbus' creator is certainly pushing for zlink to be the future of zbus. It may require a bit of extra boilerplate to set up subscription methods since there's no concept of signals or objects in varlink, but being able to create and pass file descriptors to clients and then managing subscribed client connections directly has a lot of advantages.

The service can be bound to any type that supports async read & write I/O. I'm using a unix socket through tokio here. Clients can send and receive file descriptors which could be any kind of socket we want with any wire format of our choosing. It could even be possible to tunnel connections over TCP/UDP/QUIC/web sockets.

For the subscription method for the audio interface I'm using anonymous pipes with RON as the wire format with a basic streaming codec. I'm not sure if we want to use RON for everything but it's a stable format that supports more of serde's features than JSON. Being able to manage connected clients directly lets us leverage tokio to broadcast messages to them concurrently.

Not requiring a broker to broadcast events also has a lot of privacy and performance potential. Clients that interact with this won't have their activities broadcasted to every DBus client monitoring for events from the system/session broker, and therefore varlink services and clients won't cause DBus connections to potentially stall. PackageKit had the tendency to stall the DBus broker in the past which would cause GNOME Shell to hang. So the less we rely on DBus for our GUIs, the better.

One of the features I see a lot of potential for is the batched processing. The JSON protocol varlink uses allows clients to batch many method calls into a single message, and the server can then batch its responses to those calls in a single message. The service and proxy macros let us annotate when a varlink interface method is expected to have one, many, or no responses too. These could be useful for cosmic-config to enable clients to request a list of namespaces and config files which the server could batch its responses to. Perhaps a single client connection would be all that's necessary for an app to declare its interests when subscribing, too.

@mmstick mmstick requested review from a team March 23, 2026 14:51
wash2
wash2 previously approved these changes Mar 23, 2026
wash2
wash2 previously approved these changes Mar 24, 2026
Copy link
Copy Markdown
Member

@jacobgkau jacobgkau left a comment

Choose a reason for hiding this comment

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

The headset selection dialog no longer pops up when I plug a combo headphone+mic headset into a headset port (my input device remains set to Internal Microphone, and there's no way for me to choose the headset microphone).

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Mar 31, 2026

@jacobgkau Do you have an output from pw-dump? Need to check what parameters the routes have on that system to differentiate the ports.

@jacobgkau
Copy link
Copy Markdown
Member

With varlink (not working):

On master (working):

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Mar 31, 2026

I see that there is only a headset source route but no headset sink route. I don't have any hardware that lacks a headset sink route but maybe e0a288c will work for systems that have only a headset source route.

wash2
wash2 previously approved these changes Apr 1, 2026
Copy link
Copy Markdown
Member

@jacobgkau jacobgkau left a comment

Choose a reason for hiding this comment

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

When I plug a headset in, the input device doesn't update after confirming it as a headset. It only updates (to show "Headset Microphone") next time I change the volume. It should update after clicking confirm on the dialog (or when selecting Headset in the dialog).

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented May 20, 2026

Is the headset microphone active before adjusting the microphone volume? Or is the applet or settings page not updating its copy until the volume change?

@jacobgkau
Copy link
Copy Markdown
Member

Testing with GNOME Sound Recorder, the input is still actually coming from the internal microphone until I adjust the volume, and then it's switching to the headset when Settings reflects that.

Here are logs while doing this: csd-logs.txt

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented May 20, 2026

I think this happened because the headset and headphone profiles have the same profile index. So when setting a profile that was already active, the active profile event wasn't emitted until the volume change.

The active profile event handler checks if a headset/headphone profile was applied and only then does it request to set the route. So we can check if the profile was already active at the time of selecting a headset profile and then set the route immediately if it is already active.

@hojjatabdollahi
Copy link
Copy Markdown

This solves the issue of not switching to headset mic for me (it used to stay on internal mic since both are on the same profile).

1- But the auto-switch policy is not there yet (It may be best if we could delegate that decision to wireplumber somehow).

When my bluetooth headphone connects, I have to manually switch to it.
If I switch to my bluetooth headphone and then plug in a wired headphone and choose headphone/headset it doesn't switch to it, stays on bluetooth.

Here's what I understand from researching it (and from experience):

  • If a bluetooth device gets connected you should switch to it (this normally is done by wireplumber's "switch on connect" policy). This isn't happening currently.
  • If an analog headphone is plugged into the builtin jack, that's the default output, you'd need to switch to it.
  • USB audio devices are considered external devices, and you must manually switch to them if you want.
  • Not all newly connected devices win! For example "hdmi" is blocked by default: https://docs.pipewire.org/devel/page_pulse_module_switch_on_connect.html, so connecting a monitor doesn't steal your audio!

2- There is no way to switch between internal mic and headset mic. Both audio-applet and cosmic-settings only show the current source. They only show one route per node. They should show both internal mic and headset mic. I have to use pwvucontrol to switch sources.

@jacobgkau
Copy link
Copy Markdown
Member

The latest change did get the heatset mic properly activated upon selecting Headset and clicking Confirm (for the oryp8's analog ports).

Copy link
Copy Markdown
Member

@jacobgkau jacobgkau left a comment

Choose a reason for hiding this comment

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

@hojjatabdollahi Testing with a Bluetooth headset on the currently released version of cosmic-settings-daemon et al, I don't see the active output or input device automatically change to the headset after pairing/connecting. Can you please clarify if you're referring to a regression or simply desired behavior?

@mmstick I see you highlighted showing the OSD for Bluetooth headsets as a feature in this PR's description. I'm not seeing the OSD appear after pairing the headset. Additionally, I'm seeing the Sound page go mostly blank after connecting, as shown below (the app also freezes up with 100% CPU usage when I enter the page in this state, requiring me to force-quit Settings to use any other pages):

Screenshot_2026-06-02_09-17-11

When this happens, the applet initially shows the headphones as an available output option, but attempting to select them freezes the applet.

The OSD wouldn't be required in this PR assuming it wasn't a feature before, but the blank/crashing Sound page and freezing applet are blockers.

@jacobgkau
Copy link
Copy Markdown
Member

I don't see much happening in the cosmic-settings-daemon debug log when connecting the Bluetooth headset and viewing the broken Sound page, but here it is, in case it's helpful: cosmic-settings-daemon-bluetooth-70513d8.txt

@jacobgkau
Copy link
Copy Markdown
Member

Here is env RUST_LOG=debug cosmic-settings &> cosmic-settings-bluetooth-sound.txt, which is very noisy but may have more relevant details about the Sound page freezing up: cosmic-settings-bluetooth-sound.txt

(I started COSMIC Settings to the Bluetooth page with that command, turned on the headset and let it connect, then attempted to switch to the Sound page, before killing COSMIC Settings after it froze.)

@hojjatabdollahi
Copy link
Copy Markdown

@jacobgkau it's a desired behavior.

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Jun 2, 2026

Bluetooth OSD device support was disabled in e4a6e20. I can revert it if we want the daemon to manage this instead.

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Jun 2, 2026

I wonder if the recent rebase caused this freezing issue. It wasn't happened 2 weeks ago. Checking.

@jacobgkau
Copy link
Copy Markdown
Member

Bluetooth OSD device support was disabled in e4a6e20. I can revert it if we want the daemon to manage this instead.

Might be simpler to wait until later for that as a new feature, since we're just trying to get the varlink interface over the finish line at this point.

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Jun 2, 2026

In the cosmic-settings logs, 29K profiles were received from the settings daemon. I've added checks for new routes and profiles with an index of 0 and using that condition to clear cached routes and profiles.

Copy link
Copy Markdown
Member

@jacobgkau jacobgkau left a comment

Choose a reason for hiding this comment

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

The same Bluetooth headset & laptop now work correctly. I have output & input via Bluetooth, and can change the Bluetooth headset's profile.

It looks like the Balance slider is currently broken on the varlink branch. Attempting to drag it in Settings doesn't move the slider, it's just stuck in the middle. (This happens with just the internal speakers and is unrelated to Bluetooth.)

When I reverted to master, changed the Balance to about halfway to the right, then installed the varlink branch again, the slider's still stuck, but it's instead stuck all the way to the right (although it doesn't sound like the balance is actually all the way to the right).

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Jun 3, 2026

Sounds like we're close. Looking into this.

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Jun 3, 2026

Fix pushed to cosmic-settings

Copy link
Copy Markdown
Member

@jacobgkau jacobgkau left a comment

Choose a reason for hiding this comment

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

The balance slider is working now, but there's still a slight usability regression. When dragging the slider past the right edge, it pops back to the center-- this does not happen on master, and it makes it difficult to set the balance fully to the right side.

Before:

2026-06-04.11-02-45.mp4

After:

2026-06-04.11-10-36.mp4

@mmstick
Copy link
Copy Markdown
Member Author

mmstick commented Jun 4, 2026

Pushed fix to cosmic-settings again

@mmstick mmstick requested a review from jacobgkau June 4, 2026 19:02
Copy link
Copy Markdown
Member

@jacobgkau jacobgkau left a comment

Choose a reason for hiding this comment

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

@mmstick When the balance is set all the way to the right, changing the volume reverts it to the middle. This doesn't happen when it's set all the way to the left, or when it's set only partially to the right.

2026-06-05.10-42-28.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Changing the volume slider on the volume panel icon doesn't actually change the volume

5 participants