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.
45 lines
1.0 KiB
Objective-C
45 lines
1.0 KiB
Objective-C
// Copyright (c) 2025 Jonas van den Berg
|
|
// This file is licensed under the BSD 3-Clause License.
|
|
|
|
#include "private/MediaRemote.h"
|
|
#include <limits.h>
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#import "MediaRemoteAdapter.h"
|
|
#import "adapter/env.h"
|
|
#import "adapter/globals.h"
|
|
#import "adapter/now_playing.h"
|
|
#import "utility/helpers.h"
|
|
|
|
static NSArray<NSNumber *> *acceptedModes;
|
|
|
|
__attribute__((constructor)) static void init() {
|
|
acceptedModes = @[
|
|
@(kMRARepeatDisabled),
|
|
@(kMRARepeatTrack),
|
|
@(kMRARepeatPlaylist),
|
|
];
|
|
}
|
|
|
|
static bool isModeAccepted(int mode) {
|
|
return [acceptedModes containsObject:@(mode)];
|
|
}
|
|
|
|
void adapter_repeat(MRARepeatMode mode) {
|
|
|
|
if (!isModeAccepted((int)mode)) {
|
|
failf(@"Invalid repeat mode: %d", (int)mode);
|
|
}
|
|
|
|
g_mediaRemote.setRepeatMode((int)mode);
|
|
|
|
waitForCommandCompletion();
|
|
}
|
|
|
|
static inline int repeat_0_mode() {
|
|
return getEnvFuncParamIntSafe(@"adapter_repeat", 0, @"mode");
|
|
}
|
|
|
|
void adapter_repeat_env() { adapter_repeat((MRARepeatMode)repeat_0_mode()); }
|