more macos stuff

This commit is contained in:
Mars 2025-02-19 15:34:30 -05:00
parent 46074a94a2
commit 9b155fd8db
Signed by: pupbrained
GPG key ID: 874E22DF2F9DFCB5
4 changed files with 63 additions and 66 deletions

View file

@ -17,12 +17,7 @@ fn GetMemInfo() -> expected<u64, string> {
return mem; return mem;
} }
fn GetNowPlaying() -> expected<string, NowPlayingError> { fn GetNowPlaying() -> expected<string, NowPlayingError> { return GetCurrentPlayingInfo(); }
if (const char* title = GetCurrentPlayingTitle(); const char* artist = GetCurrentPlayingArtist())
return "Now Playing: " + string(artist) + " - " + string(title);
return "No song playing";
}
fn GetOSVersion() -> expected<string, string> { return GetMacOSVersion(); } fn GetOSVersion() -> expected<string, string> { return GetMacOSVersion(); }

View file

@ -2,24 +2,25 @@
#ifdef __APPLE__ #ifdef __APPLE__
#include <expected>
#include <string>
#include "../../util/macros.h" #include "../../util/macros.h"
#ifdef __OBJC__ #ifdef __OBJC__
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <expected>
@interface Bridge : NSObject @interface Bridge : NSObject
+ (NSDictionary*)currentPlayingMetadata; + (void)fetchCurrentPlayingMetadata:(void (^)(std::expected<NSDictionary*, const char*>))completion;
+ (std::expected<const char*, const char*>)macOSVersion; + (std::expected<string, string>)macOSVersion;
@end @end
#else #else
extern "C++" { extern "C++" {
fn GetCurrentPlayingTitle() -> const char*; fn GetCurrentPlayingInfo() -> std::expected<std::string, NowPlayingError>;
fn GetCurrentPlayingArtist() -> const char*; fn GetMacOSVersion() -> std::expected<std::string, const char*>;
fn GetMacOSVersion() -> std::expected<const char*, const char*>;
} }
#endif #endif

View file

@ -3,31 +3,30 @@
#import <dispatch/dispatch.h> #import <dispatch/dispatch.h>
#include <expected> #include <expected>
#import <objc/runtime.h> #import <objc/runtime.h>
#include <string>
#import "bridge.h" #import "bridge.h"
#include "../../util/macros.h"
using MRMediaRemoteGetNowPlayingInfoFunction = using MRMediaRemoteGetNowPlayingInfoFunction =
void (*)(dispatch_queue_t queue, void (^handler)(NSDictionary* information)); void (*)(dispatch_queue_t queue, void (^handler)(NSDictionary* information));
@implementation Bridge @implementation Bridge
+ (NSDictionary*)currentPlayingMetadata { + (void)fetchCurrentPlayingMetadata:(void (^)(std::expected<NSDictionary*, const char*>))completion {
CFURLRef ref = CFURLCreateWithFileSystemPath( CFURLRef ref = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault, CFSTR("/System/Library/PrivateFrameworks/MediaRemote.framework"), kCFURLPOSIXPathStyle, false kCFAllocatorDefault, CFSTR("/System/Library/PrivateFrameworks/MediaRemote.framework"), kCFURLPOSIXPathStyle, false
); );
if (!ref) { if (!ref) {
ERROR_LOG("Failed to load MediaRemote framework"); completion(std::unexpected("Failed to create CFURL for MediaRemote framework"));
return nil; return;
} }
CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, ref); CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, ref);
CFRelease(ref); CFRelease(ref);
if (!bundle) { if (!bundle) {
ERROR_LOG("Failed to load MediaRemote framework"); completion(std::unexpected("Failed to create bundle for MediaRemote framework"));
return nil; return;
} }
auto mrMediaRemoteGetNowPlayingInfo = std::bit_cast<MRMediaRemoteGetNowPlayingInfoFunction>( auto mrMediaRemoteGetNowPlayingInfo = std::bit_cast<MRMediaRemoteGetNowPlayingInfoFunction>(
@ -35,86 +34,87 @@ using MRMediaRemoteGetNowPlayingInfoFunction =
); );
if (!mrMediaRemoteGetNowPlayingInfo) { if (!mrMediaRemoteGetNowPlayingInfo) {
ERROR_LOG("Failed to get function pointer for MRMediaRemoteGetNowPlayingInfo");
CFRelease(bundle); CFRelease(bundle);
return nil; completion(std::unexpected("Failed to get MRMediaRemoteGetNowPlayingInfo function pointer"));
return;
} }
__block NSDictionary* nowPlayingInfo = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
mrMediaRemoteGetNowPlayingInfo( mrMediaRemoteGetNowPlayingInfo(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(NSDictionary* information) { ^(NSDictionary* information) {
nowPlayingInfo = [information copy]; NSDictionary* nowPlayingInfo = information; // Immutable, no copy needed
dispatch_semaphore_signal(semaphore); CFRelease(bundle);
completion(
nowPlayingInfo ? std::expected<NSDictionary*, const char*>(nowPlayingInfo)
: std::unexpected("No now playing information")
);
} }
); );
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
CFRelease(bundle);
return nowPlayingInfo;
} }
+ (std::expected<const char*, const char*>)macOSVersion { + (std::expected<string, string>)macOSVersion {
NSProcessInfo* processInfo = [NSProcessInfo processInfo]; NSProcessInfo* processInfo = [NSProcessInfo processInfo];
NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion]; NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion];
// Build version number string NSString* versionNumber = nil;
NSString* versionNumber = nullptr; if (osVersion.patchVersion == 0) {
if (osVersion.patchVersion == 0)
versionNumber = [NSString stringWithFormat:@"%ld.%ld", osVersion.majorVersion, osVersion.minorVersion]; versionNumber = [NSString stringWithFormat:@"%ld.%ld", osVersion.majorVersion, osVersion.minorVersion];
else } else {
versionNumber = [NSString versionNumber = [NSString
stringWithFormat:@"%ld.%ld.%ld", osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion]; stringWithFormat:@"%ld.%ld.%ld", osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion];
}
// Map major version to name
NSDictionary* versionNames = NSDictionary* versionNames =
@{ @11 : @"Big Sur", @12 : @"Monterey", @13 : @"Ventura", @14 : @"Sonoma", @15 : @"Sequoia" }; @{ @11 : @"Big Sur", @12 : @"Monterey", @13 : @"Ventura", @14 : @"Sonoma", @15 : @"Sequoia" };
NSNumber* majorVersion = @(osVersion.majorVersion);
NSString* versionName = versionNames[majorVersion];
if (!versionName) NSNumber* majorVersion = @(osVersion.majorVersion);
return std::unexpected("Unsupported macOS version"); NSString* versionName = versionNames[majorVersion] ? versionNames[majorVersion] : @"Unknown";
NSString* fullVersion = [NSString stringWithFormat:@"macOS %@ %@", versionNumber, versionName]; NSString* fullVersion = [NSString stringWithFormat:@"macOS %@ %@", versionNumber, versionName];
return strdup([fullVersion UTF8String]); return std::string([fullVersion UTF8String]);
} }
@end @end
extern "C++" { extern "C++" {
// NOLINTBEGIN(misc-use-internal-linkage) // NOLINTBEGIN(misc-use-internal-linkage)
fn GetCurrentPlayingTitle() -> const char* { fn GetCurrentPlayingInfo() -> std::expected<std::string, NowPlayingError> {
NSDictionary* metadata = [Bridge currentPlayingMetadata]; __block std::expected<std::string, NowPlayingError> result;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
if (metadata == nil) [Bridge fetchCurrentPlayingMetadata:^(std::expected<NSDictionary*, const char*> metadataResult) {
return nullptr; if (!metadataResult) {
result = std::unexpected(NowPlayingError { metadataResult.error() });
dispatch_semaphore_signal(semaphore);
return;
}
NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"]; NSDictionary* metadata = *metadataResult;
if (!metadata) {
result = std::unexpected(NowPlayingError { NowPlayingCode::NoPlayers });
dispatch_semaphore_signal(semaphore);
return;
}
if (title) NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"];
return strdup([title UTF8String]); NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"];
return nullptr; if (!title && !artist)
result = std::unexpected("No metadata");
else if (!title)
result = std::string([artist UTF8String]);
else if (!artist)
result = std::string([title UTF8String]);
else
result = std::string([[NSString stringWithFormat:@"%@ - %@", title, artist] UTF8String]);
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return result;
} }
fn GetCurrentPlayingArtist() -> const char* { fn GetMacOSVersion() -> std::expected<string, string> { return [Bridge macOSVersion]; }
NSDictionary* metadata = [Bridge currentPlayingMetadata];
if (metadata == nil)
return nullptr;
NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"];
if (artist)
return strdup([artist UTF8String]);
return nullptr;
}
fn GetMacOSVersion() -> std::expected<const char*, const char*> { return [Bridge macOSVersion]; }
// NOLINTEND(misc-use-internal-linkage) // NOLINTEND(misc-use-internal-linkage)
} }

View file

@ -3,6 +3,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <variant>
#ifdef __linux__ #ifdef __linux__
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>