Mercurial > minori
view dep/animia/src/util/osx.cc @ 198:bc1ae1810855
dep/animia: switch from using classes to global functions
the old idea was ok, but sort of hackish; this method doesn't use classes
at all, and this way (especially important!) we can do wayland stuff AND x11
at the same time, which wasn't really possible without stupid workarounds in
the other method
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 24 Dec 2023 02:59:42 -0500 |
parents | 8548dc425697 |
children | a7d0d543b334 |
line wrap: on
line source
#include "animia/util/osx.h" #include <string> #include <memory> #include <sys/sysctl.h> #include <libproc.h> namespace animia::internal::osx::util { #ifdef HAVE_COREFOUNDATION /* all of these LaunchServices things use *internal functions* that are subject * to change. Granted, it's not very likely that these will change very much * because I'm fairly sure Apple uses them lots in their own internal code. */ typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t); typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef); static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr; static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr; /* retrieved from LaunchServicesSPI.h in WebKit */ static constexpr int kLSDefaultSessionID = -2; static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices"); /* retrieved dynamically */ static CFStringRef kLSDisplayNameKey = nullptr; static CFStringRef kLSPIDKey = nullptr; static bool GetLaunchServicesPrivateSymbols() { CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID); if (!launch_services_bundle) return false; LSCopyApplicationInformation = (LSCopyApplicationInformationSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation")); if (!LSCopyApplicationInformation) return false; LSASNCreateWithPid = (LSASNCreateWithPidSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid")); if (!LSASNCreateWithPid) return false; kLSDisplayNameKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey")); if (!kLSDisplayNameKey) return false; kLSPIDKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey")); if (!kLSPIDKey) return false; return true; } static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) { if (!LSCopyApplicationInformation || !LSASNCreateWithPid) if (!GetLaunchServicesPrivateSymbols()) return false; CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid); if (!asn) return false; CFArrayRef request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL); if (!request_array) { CFRelease(asn); return false; } CFDictionaryRef dictionary = LSCopyApplicationInformation(kLSDefaultSessionID, asn, request_array); CFRelease(request_array); CFRelease(asn); if (!dictionary) return false; { CFStringRef rstr; if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr) return false; if (!StringFromCFString(rstr, result)) { CFRelease(rstr); return false; } CFRelease(rstr); } result.resize(result.find('\0')); return true; } bool StringFromCFString(CFStringRef string, std::string& result) { if (!string) return false; result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1); if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8)) return false; return true; } #endif // HAVE_COREFOUNDATION static bool GetProcessArgs(pid_t pid, std::string& args) { /* sysctl shouldn't touch these, so we define them as const */ const int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)}; const size_t mib_size = sizeof(mib)/sizeof(*mib); /* Get the initial size of the array * * NOTE: it IS possible for this value to change inbetween calls to sysctl(). * Unfortunately, I couldn't care less about handling this. :) */ size_t size; { int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0); if (ret) return false; } /* Reserve the space for it in args */ args.resize(size); /* Get the contents of argc and argv */ { int ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0); if (ret) return false; } /* Is the size big enough to hold at least argc? */ if (size < sizeof(int)) return false; args.resize(size); return true; } static bool GetProcessNameFromArgs(pid_t pid, std::string& result) { if (!GetProcessArgs(pid, result)) return false; /* Get argc using memcpy */ int argc; memcpy(&result, &result.front(), sizeof(argc)); /* Do we even have argv[0]? */ if (argc < 1) return false; /* Find the first null character */ size_t null_pos = result.find('\0', sizeof(argc)); if (null_pos == std::string::npos) return false; /* Find the last slash */ size_t last_slash = result.rfind('/', null_pos); if (last_slash == std::string::npos) return false; /* Return our result */ result = result.substr(last_slash + 1, null_pos - last_slash - 1); return true; } static bool GetProcessNameFromKernel(pid_t pid, std::string& result) { result.resize(2 * MAXCOMLEN); int size = proc_name(pid, &result.front(), result.length()); if (!size) return false; result.resize(size); return true; } bool GetProcessName(pid_t pid, std::string& result) { #ifdef HAVE_COREFOUNDATION if (LaunchServicesGetProcessName(pid, result)) return true; #endif // HAVE_COREFOUNDATION /* Try parsing the arguments, this prevents the process name being cut off to 2*MAXCOMLEN (32 chars) */ if (GetProcessNameFromArgs(pid, result)) return true; /* Then attempt getting it from the kernel, which results in the process name being cut to 32 chars (worse, 16 chars if p_name is unavailable) */ if (GetProcessNameFromKernel(pid, result)) return true; return false; } }