Mercurial > minori
view dep/animia/src/util/osx.cc @ 187:9613d72b097e
*: multiple performance improvements
like marking `static const` when it makes sense...
date: change old stupid heap-based method to a structure which should
make copying the thing actually make a copy.
also many performance-based changes, like removing the std::tie
dependency and forward-declaring nlohmann json
*: replace every instance of QString::fromUtf8 to Strings::ToQString.
the main difference is that our function will always convert exactly
what is in the string, while some other times it would only convert
up to the nearest NUL byte
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Wed, 06 Dec 2023 13:43:54 -0500 |
parents | c413e475f496 |
children | 649786bae914 |
line wrap: on
line source
#include "animia/util/osx.h" #include <string> #include <memory> #ifdef HAVE_COREFOUNDATION #include <CoreFoundation/CoreFoundation.h> #endif namespace animia::internal::osx::util { #ifdef HAVE_COREFOUNDATION /* I don't want to have to call CFRelease */ template<typename T> struct CFDeleter { using pointer = T; void operator()(pointer p) { CFRelease(p); } } template<typename T> typedef CFReference = std::unique_ptr<T, CFDeleter>; /* 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; CFReference<CFTypeRef> asn = LSASNCreateWithPid(kCFAllocatorDefault, pid); CFReference<CFArrayRef> request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL); CFReference<CFDictionaryRef> dictionary = LSCopyApplicationInformation(kLaunchServicesMagicConstant, asn, request_array.get()); if (!dictionary.get()) return false; CFReference<CFStringRef> str; { CFStringRef rstr; if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr) return false; str.reset(rstr); } result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(str.get()), kCFStringEncodingUTF8) + 1); if (!CFStringGetCString(str.get(), &result.front(), result.length(), result.length())) return false; result.resize(result.find('\0')); 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); } static bool GetProcessNameFromArgs(pid_t pid, std::string& result) { if (!GetProcessArgs(pid, result)) return false; /* Get argc using memcpy */ int argc; memcpy(&argc, &args.front(), sizeof(argc)); /* Do we even have argv[0]? */ if (argc < 1) return false; /* Find the first null character */ size_t null_pos = args.find('\0', sizeof(argc)); if (null_pos == std::string::npos) return false; /* Find the last slash */ size_t last_slash = args.rfind('/', null_pos); if (last_slash == std::string::npos) return false; /* Return our result */ result = args.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; } static 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; } }