diff options
| author | Tommaso Negri <me@tommasongr.com> | 2026-08-11 18:19:02 +0200 |
|---|---|---|
| committer | Tommaso Negri <me@tommasongr.com> | 2026-08-11 18:19:02 +0200 |
| commit | eb51ef01a4bd8bd7bea61a0a5fd524f66cf276cd (patch) | |
| tree | d750764614bf1e5125c4a09ba6c72084b6a5772e | |
| parent | a1d62dd085ac70fc90a05095220156897bdd8f82 (diff) | |
| download | bar-eb51ef01a4bd8bd7bea61a0a5fd524f66cf276cd.tar.gz bar-eb51ef01a4bd8bd7bea61a0a5fd524f66cf276cd.zip | |
add additional daemons
| -rw-r--r-- | Sources/bar/bar.docc/daemons.md | 39 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-batteryd.md | 27 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-brightnessd.md | 27 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-devicesd.md | 30 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-focusd.md | 27 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-networkd.md | 31 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-nowplayingd.md | 29 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/daemons/daemon-spaced.md | 29 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/patches.md | 2 | ||||
| -rw-r--r-- | daemons/batteryd.c | 45 | ||||
| -rw-r--r-- | daemons/brightnessd.c | 75 | ||||
| -rw-r--r-- | daemons/devicesd.c | 75 | ||||
| -rw-r--r-- | daemons/focusd.c | 49 | ||||
| -rw-r--r-- | daemons/networkd.c | 67 | ||||
| -rw-r--r-- | daemons/nowplayingd.c | 55 | ||||
| -rw-r--r-- | daemons/spaced.c | 75 |
16 files changed, 661 insertions, 21 deletions
diff --git a/Sources/bar/bar.docc/daemons.md b/Sources/bar/bar.docc/daemons.md index e99ecfc..c73377b 100644 --- a/Sources/bar/bar.docc/daemons.md +++ b/Sources/bar/bar.docc/daemons.md @@ -21,7 +21,7 @@ notify_post("bar.touch.<block-name>"); The ``Notifier`` system in *bar* receives the notification and re-runs the corresponding block's command. The block itself stays stateless, it just reads and prints the current state whenever it is called. -For example, the <doc:daemon-bar-volumed> daemon listens for CoreAudio volume change events and posts `bar.touch.volume`. +For example, the <doc:daemon-volumed> daemon listens for CoreAudio volume change events and posts `bar.touch.volume`. The volume block in ``Config`` runs once per notification, prints the current volume, and the bar updates. ## How to install @@ -56,27 +56,26 @@ The process is very similar to the one described in the <doc:run-at-login> artic ### Audio - <doc:daemon-volumed> -<!-- - <doc:daemon-bar-micd> --> - -<!-- ### Display --> -<!----> -<!-- - <doc:daemon-bar-brightnessd> --> -<!-- - <doc:daemon-bar-spaced> --> -<!----> -<!-- ### Power --> -<!----> -<!-- - <doc:daemon-bar-batteryd> --> -<!----> -<!-- ### Network --> -<!----> -<!-- - <doc:daemon-bar-networkd> --> + +### Display + +- <doc:daemon-brightnessd> +- <doc:daemon-spaced> + +### Power + +- <doc:daemon-batteryd> + +### Network + +- <doc:daemon-networkd> ### Media +- <doc:daemon-nowplayingd> - <doc:daemon-mpd> -<!-- - <doc:daemon-bar-nowplayingd> --> -<!-- ### System --> -<!----> -<!-- - <doc:daemon-bar-focusd> --> -<!-- - <doc:daemon-bar-devicesd> --> +### System + +- <doc:daemon-devicesd> +- <doc:daemon-focusd> diff --git a/Sources/bar/bar.docc/daemons/daemon-batteryd.md b/Sources/bar/bar.docc/daemons/daemon-batteryd.md new file mode 100644 index 0000000..db7a5dc --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-batteryd.md @@ -0,0 +1,27 @@ +# batteryd + +React to battery and power source changes. + +This daemon listens for power source changes via **IOKit** (`IOPowerSources`) and posts a Darwin notification that tells *bar* to refresh the desired block. +It fires when the battery level changes, the charger is plugged or unplugged, or the machine enters low-power mode. + +## How it works + +The daemon uses `IOPSNotificationCreateRunLoopSource` to register a callback that fires every time the power source state changes. +When the callback fires, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command. + +### Dependencies + +- IOKit.framework, CoreFoundation.framework + +## Compile + +```shell +cc -o bar-batteryd daemons/batteryd.c -framework IOKit -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/daemons/daemon-brightnessd.md b/Sources/bar/bar.docc/daemons/daemon-brightnessd.md new file mode 100644 index 0000000..b617e3b --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-brightnessd.md @@ -0,0 +1,27 @@ +# brightnessd + +React to display brightness changes. + +This daemon listens for display brightness changes via **IOKit** and posts a Darwin notification that tells *bar* to refresh the desired block. +It fires when the brightness level changes through keyboard keys, the Touch Bar, or System Settings. + +## How it works + +The daemon uses IOKit notifications to watch for changes to the display backlight service (`AppleBacklightDisplay` or `AppleIntelPanel`). +When the brightness changes, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command. + +### Dependencies + +- IOKit.framework, CoreFoundation.framework + +## Compile + +```shell +cc -o bar-brightnessd daemons/brightnessd.c -framework IOKit -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/daemons/daemon-devicesd.md b/Sources/bar/bar.docc/daemons/daemon-devicesd.md new file mode 100644 index 0000000..554371d --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-devicesd.md @@ -0,0 +1,30 @@ +# devicesd + +React to USB device hotplug events. + +This daemon listens for USB device connect and disconnect events via **IOKit** and posts a Darwin notification that tells *bar* to refresh the desired block. +It fires when you plug in or unplug any USB device, including Bluetooth controllers. + +## How it works + +The daemon uses IOKit matching notifications to watch for: +- `IOUSBDevice` — USB device connect and disconnect events. +- `IOBluetoothHCIController` — Bluetooth controller events. + +When a device is added or removed, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command. + +### Dependencies + +- IOKit.framework, CoreFoundation.framework + +## Compile + +```shell +cc -o bar-devicesd daemons/devicesd.c -framework IOKit -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/daemons/daemon-focusd.md b/Sources/bar/bar.docc/daemons/daemon-focusd.md new file mode 100644 index 0000000..73dc5f6 --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-focusd.md @@ -0,0 +1,27 @@ +# focusd + +React to Focus and Do Not Disturb mode changes. + +This daemon listens for Focus mode and DND changes via `DistributedNotificationCenter` and posts a Darwin notification that tells *bar* to refresh the desired block. + +## How it works + +The daemon registers a distributed notification observer for `com.apple.notificationcenter.dndprefs_changed`, which macOS fires whenever the Focus mode or Do Not Disturb state changes. + +When the notification fires, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command. + +### Dependencies + +- CoreFoundation.framework + +### Compile + +```shell +cc -o bar-focusd daemons/focusd.c -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/daemons/daemon-networkd.md b/Sources/bar/bar.docc/daemons/daemon-networkd.md new file mode 100644 index 0000000..cb9c765 --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-networkd.md @@ -0,0 +1,31 @@ +# networkd + +React to network state changes. + +This daemon listens for network configuration changes via **SystemConfiguration** and posts a Darwin notification that tells *bar* to refresh the desired block. +It fires when the WiFi connects or disconnects, the IP address changes, or a VPN toggles. + +## How it works + +The daemon uses `SCDynamicStore` to watch for changes to: +- `State:/Network/Global/IPv4` — IPv4 address changes. +- `State:/Network/Global/IPv6` — IPv6 address changes. +- `State:/Network/Interface/en0/AirPort` — WiFi state changes. + +When any of these keys change, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command. + +### Dependencies + +- SystemConfiguration.framework, CoreFoundation.framework + +## Compile + +```shell +cc -o bar-networkd daemons/networkd.c -framework SystemConfiguration -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/daemons/daemon-nowplayingd.md b/Sources/bar/bar.docc/daemons/daemon-nowplayingd.md new file mode 100644 index 0000000..9d33929 --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-nowplayingd.md @@ -0,0 +1,29 @@ +# nowplayingd + +React to music track changes. + +This daemon listens for track change notifications from **Music** and **Spotify** via `DistributedNotificationCenter` and posts a Darwin notification that tells *bar* to refresh the desired block. + +## How it works + +The daemon registers distributed notification observers for: +- `com.apple.Music.playerInfo` — fired by Music.app when the track changes. +- `com.spotify.client.PlaybackStateChanged` — fired by Spotify when the track changes. + +When either notification fires, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command, which can read the current track from any source. + +### Dependencies + +- CoreFoundation.framework + +### Compile + +```shell +cc -o bar-nowplayingd daemons/nowplayingd.c -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/daemons/daemon-spaced.md b/Sources/bar/bar.docc/daemons/daemon-spaced.md new file mode 100644 index 0000000..1b4cea0 --- /dev/null +++ b/Sources/bar/bar.docc/daemons/daemon-spaced.md @@ -0,0 +1,29 @@ +# spaced + +React to desktop space changes. + +This daemon monitors the active desktop space and posts a Darwin notification that tells *bar* to refresh the desired block. +It fires when you switch between Mission Control spaces, full-screen apps, or Stage Manager groups. + +## How it works + +The daemon uses a lightweight 0.5-second poll of a private CoreGraphics function (`CGSCopyActiveMenuBarDisplayIdentifier`) to detect when the active space changes. +When a change is detected, the daemon calls `notify_post("bar.touch.<block-name>")`. + +The ``Notifier`` system in *bar* picks this up and re-runs the block's command. + +> Note: The 0.5-second poll interval is negligible — the daemon uses near-zero CPU while idle. + +### Dependencies + +- CoreFoundation.framework + +## Compile + +```shell +cc -o bar-spaced daemons/spaced.c -framework CoreFoundation +``` + +## Author + +Tommaso Negri [@tommasongr](https://github.com/tommasongr) diff --git a/Sources/bar/bar.docc/patches.md b/Sources/bar/bar.docc/patches.md index 1b05ffe..c4f02e3 100644 --- a/Sources/bar/bar.docc/patches.md +++ b/Sources/bar/bar.docc/patches.md @@ -1,6 +1,6 @@ # Patches -Extend the program with additional functionality. +Extend the program by applying git diffs. A *patch* is a set of source code changes that add a specific feature to a program. Unlike plugins or extensions, patches modify the program directly — you edit the source, rebuild, and the new functionality becomes part of the binary. diff --git a/daemons/batteryd.c b/daemons/batteryd.c new file mode 100644 index 0000000..48e21b3 --- /dev/null +++ b/daemons/batteryd.c @@ -0,0 +1,45 @@ +// batteryd — battery daemon for bar +// +// Uses IOKit to listen for power source changes (battery level, charging +// state, AC plug/unplug) and posts a Darwin notification. +// +// Compile: +// cc -o bar-batteryd daemons/batteryd.c \ +// -framework IOKit -framework CoreFoundation + +#include <IOKit/ps/IOPowerSources.h> +#include <IOKit/ps/IOPSKeys.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; +} + +static void power_source_changed(void *context) { + (void)context; + notify_post("bar.touch.<block-name>"); +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + CFRunLoopSourceRef source = IOPSNotificationCreateRunLoopSource(power_source_changed, NULL); + if (source) { + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + CFRelease(source); + } + + while (keep_running) { + pause(); + } + + return 0; +} diff --git a/daemons/brightnessd.c b/daemons/brightnessd.c new file mode 100644 index 0000000..333dbf4 --- /dev/null +++ b/daemons/brightnessd.c @@ -0,0 +1,75 @@ +// brightnessd — display brightness daemon for bar +// +// Uses IOKit to listen for display brightness changes and posts +// a Darwin notification. +// +// Compile: +// cc -o bar-brightnessd daemons/brightnessd.c \ +// -framework IOKit -framework CoreFoundation + +#include <IOKit/IOKitLib.h> +#include <IOKit/graphics/IOGraphicsLib.h> +#include <notify.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static volatile int keep_running = 1; +static IONotificationPortRef notifyPort = NULL; + +static void handle_signal(int sig) { + (void)sig; + keep_running = 0; +} + +static void brightness_changed(void *refcon, io_iterator_t iterator) { + (void)refcon; + // Drain the iterator + io_object_t obj; + while ((obj = IOIteratorNext(iterator))) { + IOObjectRelease(obj); + } + notify_post("bar.touch.<block-name>"); +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + notifyPort = IONotificationPortCreate(kIOMasterPortDefault); + CFRunLoopSourceRef source = IONotificationPortGetRunLoopSource(notifyPort); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + + // Watch for brightness-related display changes + CFMutableDictionaryRef matching = IOServiceMatching("AppleBacklightDisplay"); + if (!matching) { + // Fallback: try Intel backlight + matching = IOServiceMatching("AppleIntelPanel"); + } + if (!matching) { + matching = IOServiceMatching("IODisplay"); + } + + if (matching) { + io_iterator_t iter; + IOServiceAddMatchingNotification( + notifyPort, kIOFirstMatchNotification, matching, + brightness_changed, NULL, &iter); + brightness_changed(NULL, iter); // drain initial + + // Also watch for termination to catch display disconnect + IOServiceAddMatchingNotification( + notifyPort, kIOTerminatedNotification, matching, + brightness_changed, NULL, &iter); + } + + while (keep_running) { + pause(); + } + + if (notifyPort) { + IONotificationPortDestroy(notifyPort); + } + return 0; +} diff --git a/daemons/devicesd.c b/daemons/devicesd.c new file mode 100644 index 0000000..6b30930 --- /dev/null +++ b/daemons/devicesd.c @@ -0,0 +1,75 @@ +// devicesd — USB device hotplug daemon for bar +// +// Uses IOKit to listen for USB device connect/disconnect events and +// posts a Darwin notification. +// +// Compile: +// cc -o bar-devicesd daemons/devicesd.c \ +// -framework IOKit -framework CoreFoundation + +#include <IOKit/IOKitLib.h> +#include <notify.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static volatile int keep_running = 1; +static IONotificationPortRef notifyPort = NULL; + +static void handle_signal(int sig) { + (void)sig; + keep_running = 0; +} + +static void device_event(void *refcon, io_iterator_t iterator) { + (void)refcon; + io_object_t obj; + while ((obj = IOIteratorNext(iterator))) { + IOObjectRelease(obj); + } + notify_post("bar.touch.<block-name>"); +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + notifyPort = IONotificationPortCreate(kIOMasterPortDefault); + CFRunLoopSourceRef source = IONotificationPortGetRunLoopSource(notifyPort); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + + // Watch for USB devices + CFMutableDictionaryRef matching = IOServiceMatching("IOUSBDevice"); + if (matching) { + io_iterator_t iter; + + IOServiceAddMatchingNotification( + notifyPort, kIOFirstMatchNotification, matching, + device_event, NULL, &iter); + device_event(NULL, iter); // drain initial set + + IOServiceAddMatchingNotification( + notifyPort, kIOTerminatedNotification, matching, + device_event, NULL, &iter); + } + + // Also watch for Bluetooth devices + CFMutableDictionaryRef btMatching = IOServiceMatching("IOBluetoothHCIController"); + if (btMatching) { + io_iterator_t iter; + IOServiceAddMatchingNotification( + notifyPort, kIOFirstMatchNotification, btMatching, + device_event, NULL, &iter); + device_event(NULL, iter); + } + + while (keep_running) { + pause(); + } + + if (notifyPort) { + IONotificationPortDestroy(notifyPort); + } + return 0; +} diff --git a/daemons/focusd.c b/daemons/focusd.c new file mode 100644 index 0000000..9a57564 --- /dev/null +++ b/daemons/focusd.c @@ -0,0 +1,49 @@ +// bar-focusd — Focus / Do Not Disturb daemon for bar +// +// Listens for Focus mode and DND changes via DistributedNotificationCenter +// and posts a Darwin notification. +// +// Compile: +// cc -o bar-focusd daemons/focusd.c -framework CoreFoundation + +#include <CoreFoundation/CoreFoundation.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; +} + +static void focus_changed( + CFNotificationCenterRef center, void *observer, + CFStringRef name, const void *object, CFDictionaryRef userInfo +) { + (void)center; (void)observer; (void)name; (void)object; (void)userInfo; + notify_post("bar.touch.<block-name>"); +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + // DND / Focus mode changes + CFNotificationCenterAddObserver( + center, NULL, focus_changed, + CFSTR("com.apple.notificationcenter.dndprefs_changed"), + NULL, CFNotificationSuspensionBehaviorDeliverImmediately); + + while (keep_running) { + pause(); + } + + CFNotificationCenterRemoveEveryObserver(center, NULL); + return 0; +} diff --git a/daemons/networkd.c b/daemons/networkd.c new file mode 100644 index 0000000..3fea2c7 --- /dev/null +++ b/daemons/networkd.c @@ -0,0 +1,67 @@ +// bar-networkd — network status daemon for bar +// +// Uses SystemConfiguration to listen for network state changes +// (WiFi connect/disconnect, IP address changes, VPN toggle) and +// posts a Darwin notification. +// +// Compile: +// cc -o bar-networkd daemons/networkd.c \ +// -framework SystemConfiguration -framework CoreFoundation + +#include <SystemConfiguration/SystemConfiguration.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; +} + +static void network_changed( + SCDynamicStoreRef store, CFArrayRef changedKeys, void *info +) { + (void)store; (void)changedKeys; (void)info; + notify_post("bar.touch.<block-name>"); +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + SCDynamicStoreContext context = {0, NULL, NULL, NULL, NULL}; + SCDynamicStoreRef store = SCDynamicStoreCreate( + NULL, CFSTR("bar-networkd"), network_changed, &context); + + if (!store) { + fprintf(stderr, "bar-networkd: could not create dynamic store\n"); + return 1; + } + + // Watch for IPv4, IPv6, and WiFi state changes + const void *keys[] = { + CFSTR("State:/Network/Global/IPv4"), + CFSTR("State:/Network/Global/IPv6"), + CFSTR("State:/Network/Interface/en0/AirPort"), + CFSTR("State:/Network/Interface/en1/AirPort"), + }; + CFArrayRef keyArray = CFArrayCreate( + NULL, keys, 4, &kCFTypeArrayCallBacks); + SCDynamicStoreSetNotificationKeys(store, keyArray, NULL); + CFRelease(keyArray); + + CFRunLoopSourceRef source = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + CFRelease(source); + + while (keep_running) { + pause(); + } + + CFRelease(store); + return 0; +} diff --git a/daemons/nowplayingd.c b/daemons/nowplayingd.c new file mode 100644 index 0000000..fc785b3 --- /dev/null +++ b/daemons/nowplayingd.c @@ -0,0 +1,55 @@ +// nowplayingd — now playing daemon for bar +// +// Listens for track change notifications from Music.app and Spotify +// via DistributedNotificationCenter and posts a Darwin notification. +// +// Compile: +// cc -o bar-nowplayingd daemons/nowplayingd.c -framework CoreFoundation + +#include <CoreFoundation/CoreFoundation.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; +} + +static void track_changed( + CFNotificationCenterRef center, void *observer, + CFStringRef name, const void *object, CFDictionaryRef userInfo +) { + (void)center; (void)observer; (void)name; (void)object; (void)userInfo; + notify_post("bar.touch.<block-name>"); +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + // Music.app + CFNotificationCenterAddObserver( + center, NULL, track_changed, + CFSTR("com.apple.Music.playerInfo"), + NULL, CFNotificationSuspensionBehaviorDeliverImmediately); + + // Spotify + CFNotificationCenterAddObserver( + center, NULL, track_changed, + CFSTR("com.spotify.client.PlaybackStateChanged"), + NULL, CFNotificationSuspensionBehaviorDeliverImmediately); + + while (keep_running) { + pause(); + } + + CFNotificationCenterRemoveEveryObserver(center, NULL); + return 0; +} diff --git a/daemons/spaced.c b/daemons/spaced.c new file mode 100644 index 0000000..0729266 --- /dev/null +++ b/daemons/spaced.c @@ -0,0 +1,75 @@ +// spaced — desktop space daemon for bar +// +// Monitors the active desktop space and posts a Darwin notification +// when the space changes. Uses a lightweight 0.5s poll of a CoreGraphics +// private API. +// +// Compile: +// cc -o bar-spaced daemons/spaced.c -framework CoreFoundation + +#include <CoreFoundation/CoreFoundation.h> +#include <dlfcn.h> +#include <notify.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static volatile int keep_running = 1; + +// Private CoreGraphics API +typedef CFStringRef (*CGSCopyActiveMenuBarDisplayIdentifierFunc)(void); + +static CGSCopyActiveMenuBarDisplayIdentifierFunc copyIdentifier = NULL; + +static void handle_signal(int sig) { + (void)sig; + keep_running = 0; +} + +int main(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + // Load the private CGS API at runtime + void *handle = dlopen( + "/System/Library/PrivateFrameworks/CoreGraphics.framework/CoreGraphics", + RTLD_LAZY | RTLD_LOCAL); + + if (!handle) { + fprintf(stderr, "bar-spaced: could not load CoreGraphics framework\n"); + return 1; + } + + copyIdentifier = (CGSCopyActiveMenuBarDisplayIdentifierFunc) + dlsym(handle, "CGSCopyActiveMenuBarDisplayIdentifier"); + + if (!copyIdentifier) { + fprintf(stderr, "bar-spaced: could not find CGS API\n"); + dlclose(handle); + return 1; + } + + CFStringRef last = NULL; + unsigned int pollCount = 0; + + while (keep_running) { + CFStringRef current = copyIdentifier(); + + if (current) { + if (!last || !CFEqual(current, last)) { + notify_post("bar.touch.<block-name>"); + if (last) CFRelease(last); + last = CFStringCreateCopy(NULL, current); + } + CFRelease(current); + } + + usleep(500000); // 0.5 second intervals + pollCount++; + } + + if (last) CFRelease(last); + dlclose(handle); + return 0; +} |
