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 |