more macos stuff
This commit is contained in:
parent
46074a94a2
commit
9b155fd8db
|
@ -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(); }
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary* metadata = *metadataResult;
|
||||||
|
if (!metadata) {
|
||||||
|
result = std::unexpected(NowPlayingError { NowPlayingCode::NoPlayers });
|
||||||
|
dispatch_semaphore_signal(semaphore);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"];
|
NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"];
|
||||||
|
|
||||||
if (title)
|
|
||||||
return strdup([title UTF8String]);
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn GetCurrentPlayingArtist() -> const char* {
|
|
||||||
NSDictionary* metadata = [Bridge currentPlayingMetadata];
|
|
||||||
|
|
||||||
if (metadata == nil)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"];
|
NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"];
|
||||||
|
|
||||||
if (artist)
|
if (!title && !artist)
|
||||||
return strdup([artist UTF8String]);
|
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]);
|
||||||
|
|
||||||
return nullptr;
|
dispatch_semaphore_signal(semaphore);
|
||||||
|
}];
|
||||||
|
|
||||||
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetMacOSVersion() -> std::expected<const char*, const char*> { return [Bridge macOSVersion]; }
|
fn GetMacOSVersion() -> std::expected<string, string> { return [Bridge macOSVersion]; }
|
||||||
// NOLINTEND(misc-use-internal-linkage)
|
// NOLINTEND(misc-use-internal-linkage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue