diff options
| author | Tommaso Negri <me@tommasongr.com> | 2026-08-11 17:50:39 +0200 |
|---|---|---|
| committer | Tommaso Negri <me@tommasongr.com> | 2026-08-11 17:50:39 +0200 |
| commit | a1d62dd085ac70fc90a05095220156897bdd8f82 (patch) | |
| tree | 2af6218e0c9e2a6e399cf5f91e3a0ba05e7c3014 /daemons/volumed.c | |
| parent | 4dcbad96f8dc63c5f48d063606f0384853ac799d (diff) | |
| download | bar-a1d62dd085ac70fc90a05095220156897bdd8f82.tar.gz bar-a1d62dd085ac70fc90a05095220156897bdd8f82.zip | |
refactor daemons + mpd + documentation
Diffstat (limited to 'daemons/volumed.c')
| -rw-r--r-- | daemons/volumed.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/daemons/volumed.c b/daemons/volumed.c new file mode 100644 index 0000000..ca6afa8 --- /dev/null +++ b/daemons/volumed.c @@ -0,0 +1,157 @@ +// volumed — volume daemon for bar +// +// Uses CoreAudio to listen for system volume/mute changes instead of +// a CGEvent tap, so no Accessibility permission is required. +// +// When the volume changes, it posts a Darwin notification that tells +// bar to refresh the "volume" block. +// +// Compile: +// cc -o bar-volumed daemons/volumed.c -framework CoreAudio + +#include <CoreAudio/CoreAudio.h> +#include <notify.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static volatile int keep_running = 1; + +static void handle_signal(int sig) { + (void)sig; + keep_running = 0; +} + +// ── Volume change callback ─────────────────────────────────────────── + +static OSStatus volume_changed( + AudioObjectID object, + UInt32 num_addresses, + const AudioObjectPropertyAddress addresses[], + void *context +) { + (void)object; (void)num_addresses; (void)addresses; (void)context; + notify_post("bar.touch.<block-name>"); + return noErr; +} + +// ── Device change callback ─────────────────────────────────────────── +// Re-registers the volume listener when the output device changes +// (e.g. headphones plugged in). + +static AudioDeviceID current_device = kAudioObjectUnknown; +static void register_volume_listener(AudioDeviceID device); + +static OSStatus device_changed( + AudioObjectID object, + UInt32 num_addresses, + const AudioObjectPropertyAddress addresses[], + void *context +) { + (void)object; (void)num_addresses; (void)addresses; (void)context; + + AudioObjectPropertyAddress addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMain + }; + AudioDeviceID new_device = kAudioObjectUnknown; + UInt32 size = sizeof(new_device); + AudioObjectGetPropertyData( + kAudioObjectSystemObject, &addr, 0, NULL, &size, &new_device); + + if (new_device != current_device && new_device != kAudioObjectUnknown) { + register_volume_listener(new_device); + } + return noErr; +} + +// ── Register listener on a device ──────────────────────────────────── + +static void register_volume_listener(AudioDeviceID device) { + // Remove old listener if any + if (current_device != kAudioObjectUnknown) { + AudioObjectPropertyAddress old_addr = { + kAudioDevicePropertyVolumeScalar, + kAudioDevicePropertyScopeOutput, + 1 // channel 1 + }; + AudioObjectRemovePropertyListener( + current_device, &old_addr, volume_changed, NULL); + } + + current_device = device; + + // Get the preferred stereo channel (usually 1) + UInt32 channel = 1; + AudioObjectPropertyAddress chan = { + kAudioDevicePropertyPreferredChannelsForStereo, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + UInt32 channels[2] = {0, 0}; + UInt32 size = sizeof(channels); + if (AudioObjectGetPropertyData(device, &chan, 0, NULL, &size, &channels) == noErr) { + channel = channels[0]; + } + + // Listen for volume changes on the main output channel + AudioObjectPropertyAddress vol_addr = { + kAudioDevicePropertyVolumeScalar, + kAudioDevicePropertyScopeOutput, + channel + }; + AudioObjectAddPropertyListener(device, &vol_addr, volume_changed, NULL); + + // Also listen for mute changes + AudioObjectPropertyAddress mute_addr = { + kAudioDevicePropertyMute, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + AudioObjectAddPropertyListener(device, &mute_addr, volume_changed, NULL); +} + +// ── Main ───────────────────────────────────────────────────────────── + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + // Listen for output device changes + AudioObjectPropertyAddress dev_addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMain + }; + AudioObjectAddPropertyListener( + kAudioObjectSystemObject, &dev_addr, device_changed, NULL); + + // Register on the current device + UInt32 size = sizeof(current_device); + AudioObjectGetPropertyData( + kAudioObjectSystemObject, &dev_addr, 0, NULL, &size, ¤t_device); + + if (current_device != kAudioObjectUnknown) { + register_volume_listener(current_device); + } + + // Run until SIGINT/SIGTERM + while (keep_running) { + pause(); + } + + // Cleanup + if (current_device != kAudioObjectUnknown) { + AudioObjectPropertyAddress vol_addr = { + kAudioDevicePropertyVolumeScalar, + kAudioDevicePropertyScopeOutput, + 1 // channel 1 + }; + AudioObjectRemovePropertyListener( + current_device, &vol_addr, volume_changed, NULL); + } + + return 0; +} |
