From a1d62dd085ac70fc90a05095220156897bdd8f82 Mon Sep 17 00:00:00 2001 From: Tommaso Negri Date: Tue, 11 Aug 2026 17:50:39 +0200 Subject: refactor daemons + mpd + documentation --- daemons/volumed.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 daemons/volumed.c (limited to 'daemons/volumed.c') 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 +#include +#include +#include +#include +#include + +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."); + 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; +} -- cgit v1.2.3