Mercurial > minori
comparison dep/animone/src/util/osx.cc @ 268:382b50754fe4
dep/animone: make osx code a bit less hacky
it would be nice if macos actually provided a real API for getting
window titles (outside of the accessibility api). the accessibility
API is a real mess to work with; the user has to give permission to
access it under newer versions.
| author | Paper <paper@paper.us.eu.org> |
|---|---|
| date | Fri, 12 Apr 2024 05:21:45 -0400 |
| parents | 862d0d8619f6 |
| children |
comparison
equal
deleted
inserted
replaced
| 266:1a6a5d3a94cd | 268:382b50754fe4 |
|---|---|
| 6 #include <libproc.h> | 6 #include <libproc.h> |
| 7 #include <sys/sysctl.h> | 7 #include <sys/sysctl.h> |
| 8 | 8 |
| 9 namespace animone::internal::osx::util { | 9 namespace animone::internal::osx::util { |
| 10 | 10 |
| 11 typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t); | 11 static bool GetProcessNameFromProcPidPath(pid_t pid, std::string& result) { |
| 12 typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef); | 12 result.assign(PROC_PIDPATHINFO_MAXSIZE, '\0'); |
| 13 | 13 |
| 14 /* retrieved dynamically from launchservices */ | 14 int ret = proc_pidpath(pid, result.data(), result.size() * sizeof(char)); |
| 15 static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr; | 15 if (ret <= 0) |
| 16 static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr; | |
| 17 | |
| 18 static CFStringRef kLSDisplayNameKey = nullptr; | |
| 19 static CFStringRef kLSPIDKey = nullptr; | |
| 20 | |
| 21 /* retrieved from LaunchServicesSPI.h in WebKit */ | |
| 22 static constexpr int kLSDefaultSessionID = -2; | |
| 23 | |
| 24 static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices"); | |
| 25 | |
| 26 static bool GetLaunchServicesPrivateSymbols() { | |
| 27 CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID); | |
| 28 if (!launch_services_bundle) | |
| 29 return false; | 16 return false; |
| 30 | 17 |
| 31 LSCopyApplicationInformation = reinterpret_cast<LSCopyApplicationInformationSpec>( | 18 /* find the last slash, if there's none, we're done here */ |
| 32 CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation"))); | 19 size_t last_slash = result.rfind('/'); |
| 33 if (!LSCopyApplicationInformation) | 20 if (last_slash == std::string::npos) |
| 34 return false; | 21 return true; |
| 35 | 22 |
| 36 LSASNCreateWithPid = reinterpret_cast<LSASNCreateWithPidSpec>( | 23 result.erase(0, last_slash + 1); |
| 37 CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid"))); | |
| 38 if (!LSASNCreateWithPid) | |
| 39 return false; | |
| 40 | |
| 41 CFStringRef* ptr_kLSDisplayNameKey = reinterpret_cast<CFStringRef*>( | |
| 42 CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey"))); | |
| 43 if (!ptr_kLSDisplayNameKey) | |
| 44 return false; | |
| 45 kLSDisplayNameKey = *ptr_kLSDisplayNameKey; | |
| 46 | |
| 47 CFStringRef* ptr_kLSPIDKey = | |
| 48 reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey"))); | |
| 49 if (!ptr_kLSPIDKey) | |
| 50 return false; | |
| 51 kLSPIDKey = *ptr_kLSPIDKey; | |
| 52 | |
| 53 return true; | 24 return true; |
| 54 } | 25 } |
| 55 | 26 |
| 56 static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) { | 27 static bool GetProcessNameFromProcName(pid_t pid, std::string& result) { |
| 57 if (!LSCopyApplicationInformation || !LSASNCreateWithPid || !kLSDisplayNameKey || !kLSPIDKey) | 28 result.assign(2 * MAXCOMLEN, '\0'); |
| 58 if (!GetLaunchServicesPrivateSymbols()) | |
| 59 return false; | |
| 60 | |
| 61 /* what the hell is an `asn`? */ | |
| 62 CFPtr<CFTypeRef> asn = LSASNCreateWithPid(kCFAllocatorDefault, pid); | |
| 63 if (!asn) | |
| 64 return false; | |
| 65 | |
| 66 CFPtr<CFArrayRef> request_array = CFArrayCreate(NULL, (const void**)kLSDisplayNameKey, 1, NULL); | |
| 67 if (!request_array) | |
| 68 return false; | |
| 69 | |
| 70 CFPtr<CFDictionaryRef> dictionary = | |
| 71 LSCopyApplicationInformation(kLSDefaultSessionID, asn.get(), request_array.get()); | |
| 72 if (!dictionary) | |
| 73 return false; | |
| 74 | |
| 75 { | |
| 76 /* this doesn't need to be free'd */ | |
| 77 CFStringRef rstr; | |
| 78 | |
| 79 if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr) | |
| 80 return false; | |
| 81 | |
| 82 if (!StringFromCFString(rstr, result)) | |
| 83 return false; | |
| 84 } | |
| 85 | |
| 86 result.resize(result.find('\0')); | |
| 87 | |
| 88 return true; | |
| 89 } | |
| 90 | |
| 91 bool StringFromCFString(CFStringRef string, std::string& result) { | |
| 92 if (!string) | |
| 93 return false; | |
| 94 | |
| 95 result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1); | |
| 96 if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8)) | |
| 97 return false; | |
| 98 | |
| 99 return true; | |
| 100 } | |
| 101 | |
| 102 static bool GetProcessArgs(pid_t pid, std::string& args) { | |
| 103 /* sysctl shouldn't touch these, so we define them as const */ | |
| 104 int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)}; | |
| 105 const size_t mib_size = sizeof(mib) / sizeof(*mib); | |
| 106 | |
| 107 /* Get the initial size of the array | |
| 108 * | |
| 109 * NOTE: it IS possible for this value to change inbetween calls to sysctl(). | |
| 110 * Unfortunately, I couldn't care less about handling this. :) | |
| 111 * | |
| 112 * is that really true, though? these should be constant values. but are | |
| 113 * argc and argv *really* constant? | |
| 114 */ | |
| 115 size_t size; | |
| 116 { | |
| 117 int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0); | |
| 118 if (ret) | |
| 119 return false; | |
| 120 } | |
| 121 | |
| 122 /* Reserve the space for it in args */ | |
| 123 args.resize(size); | |
| 124 | |
| 125 /* Get the contents of argc and argv */ | |
| 126 { | |
| 127 int ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0); | |
| 128 if (ret) | |
| 129 return false; | |
| 130 } | |
| 131 | |
| 132 /* Is the size big enough to hold at least argc? */ | |
| 133 if (size < sizeof(int)) | |
| 134 return false; | |
| 135 | |
| 136 args.resize(size); | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 static bool GetProcessNameFromArgs(pid_t pid, std::string& result) { | |
| 141 if (!GetProcessArgs(pid, result)) | |
| 142 return false; | |
| 143 | |
| 144 /* Get argc using memcpy */ | |
| 145 int argc = 0; | |
| 146 memcpy(&argc, &result.front(), sizeof(argc)); | |
| 147 | |
| 148 /* Do we even have argv[0]? */ | |
| 149 if (argc < 1) | |
| 150 return false; | |
| 151 | |
| 152 /* Find the first null character */ | |
| 153 size_t null_pos = result.find('\0', sizeof(argc)); | |
| 154 if (null_pos == std::string::npos) | |
| 155 return false; | |
| 156 | |
| 157 /* Find the last slash */ | |
| 158 size_t last_slash = result.rfind('/', null_pos); | |
| 159 if (last_slash == std::string::npos) | |
| 160 return false; | |
| 161 | |
| 162 /* Return our result */ | |
| 163 result = result.substr(last_slash + 1, null_pos - last_slash - 1); | |
| 164 return true; | |
| 165 } | |
| 166 | |
| 167 static bool GetProcessNameFromKernel(pid_t pid, std::string& result) { | |
| 168 result.resize(2 * MAXCOMLEN); | |
| 169 | 29 |
| 170 int size = proc_name(pid, &result.front(), result.length()); | 30 int size = proc_name(pid, &result.front(), result.length()); |
| 171 if (!size) | 31 |
| 32 /* if size is MAXCOMLEN or 2 * MAXCOMLEN, assume | |
| 33 * this method won't work and our result is truncated */ | |
| 34 if (size <= 0 || size == MAXCOMLEN || size == 2 * MAXCOMLEN) | |
| 172 return false; | 35 return false; |
| 173 | 36 |
| 174 result.resize(size); | 37 result.resize(size); |
| 175 return true; | 38 return true; |
| 176 } | 39 } |
| 177 | 40 |
| 178 bool GetProcessName(pid_t pid, std::string& result) { | 41 bool GetProcessName(pid_t pid, std::string& result) { |
| 179 if (LaunchServicesGetProcessName(pid, result)) | 42 if (GetProcessNameFromProcName(pid, result)) |
| 180 return true; | 43 return true; |
| 181 | 44 |
| 182 /* Try parsing the arguments, this prevents the process name being | 45 if (GetProcessNameFromProcPidPath(pid, result)) |
| 183 * cut off to 2*MAXCOMLEN (32 chars) */ | |
| 184 if (GetProcessNameFromArgs(pid, result)) | |
| 185 return true; | |
| 186 | |
| 187 /* Then attempt getting it from the kernel, which results in the | |
| 188 * process name being cut to 32 chars (worse, 16 chars if p_name is | |
| 189 * unavailable) */ | |
| 190 if (GetProcessNameFromKernel(pid, result)) | |
| 191 return true; | 46 return true; |
| 192 | 47 |
| 193 return false; | 48 return false; |
| 194 } | 49 } |
| 195 | 50 |
