Files
onyx/Vendor/mediaremote-adapter/src/utility/Debounce.m
Guido Schmit 433cdcc3dd Phase 4: Medien-Widget mit dreistufiger Kaskade
Adapter für alles inklusive Browser, AppleScript für Spotify und Musik,
Medientasten als letzte Reserve. Die Reihenfolge ist keine Vorliebe, sondern
Absicherung: Apple hat die Now-Playing-Schnittstelle in macOS 15.4 schon
einmal zugemacht. Fällt der Adapter aus, verliert das Widget seine Anzeige —
aber nicht seine Funktion. Deshalb bleiben die Transporttasten auch dann
bedienbar, wenn gar keine Metadaten da sind.

Der Fortschritt zählt zwischen den Ereignissen selbst hoch. Spike B hatte
gezeigt, dass elapsedTime nicht laufend nachgeliefert wird: ein Scrubber, der
stur den letzten Wert anzeigt, steht still, während der Titel läuft. Gerechnet
wird aus asOf und playbackRate, begrenzt auf die Titellänge — sonst zeigt die
Anzeige nach dem Ende weiter steigende Zahlen.

Teilmeldungen (diff = true) ergänzen den Stand, statt ihn zu ersetzen. Eine
Meldung mit nur der Position würde sonst Titel, Interpret und Cover löschen.

In AnyDecodable wird Bool vor Double geprüft. Andernfalls wird "playing": true
zu 1 und die Wiedergabe gilt für immer als angehalten.

Play/Pause schaltet sofort um, statt auf das nächste Ereignis zu warten. Ein
Knopf, der eine halbe Sekunde später reagiert, fühlt sich kaputt an; das echte
Ereignis korrigiert den Stand ohnehin.

Der Adapter liegt als Quellcode unter Vendor/ (BSD-3, Stand in
ONYX-VERSION.txt) und wird beim Bauen kompiliert, statt als fertige Binary
eingecheckt zu werden. Er wird bewusst nicht gelinkt: geladen wird er von
/usr/bin/perl, im eigenen Prozess greift Apples Entitlement-Prüfung.

124 Tests grün.
2026-08-10 21:02:13 +02:00

42 lines
1.0 KiB
Objective-C

// Copyright (c) 2025 Jonas van den Berg
// This file is licensed under the BSD 3-Clause License.
#import "Debounce.h"
@interface Debounce ()
@property(nonatomic, strong) dispatch_queue_t queue;
@property(nonatomic, strong, nullable) dispatch_block_t pendingBlock;
@property(nonatomic, assign, readwrite) NSTimeInterval delay;
@end
@implementation Debounce
- (instancetype)initWithDelay:(NSTimeInterval)delay
queue:(dispatch_queue_t)queue {
self = [super init];
if (self) {
_delay = MAX(0.0, delay);
_queue = queue ?: dispatch_get_main_queue();
}
return self;
}
- (void)call:(dispatch_block_t)block {
[self cancel];
self.pendingBlock = dispatch_block_create(DISPATCH_BLOCK_BARRIER, block);
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delay * NSEC_PER_SEC)),
self.queue, self.pendingBlock);
}
- (void)cancel {
if (self.pendingBlock) {
dispatch_block_cancel(self.pendingBlock);
self.pendingBlock = nil;
}
}
@end