Mercurial > minori
annotate dep/animone/src/win/quartz.cc @ 357:ab77d65f23cd
builds/linux: linuxdeploy fails now for some reason
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Mon, 15 Jul 2024 00:43:11 -0400 |
parents | adb79bdde329 |
children |
rev | line source |
---|---|
258 | 1 /* |
2 * win/quartz.cc: support for macOS (the Quartz Compositor) | |
3 * | |
4 * This file does not require an Objective-C++ compiler, | |
5 * but it *does* require an Objective-C runtime. | |
6 */ | |
7 #include "animone/win/quartz.h" | |
299
246017a7907a
dep/animone: clean up OS X code
Paper <paper@paper.us.eu.org>
parents:
272
diff
changeset
|
8 #include "animone/fd.h" |
258 | 9 #include "animone.h" |
10 | |
11 #include <objc/message.h> | |
12 #include <objc/runtime.h> | |
13 | |
14 #include <ApplicationServices/ApplicationServices.h> | |
15 #include <CoreFoundation/CoreFoundation.h> | |
16 #include <CoreGraphics/CoreGraphics.h> | |
17 | |
18 namespace animone::internal::quartz { | |
19 | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
20 template<typename T> |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
21 struct CFDeconstructor { |
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
22 using pointer = T; |
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
23 void operator()(pointer t) const { ::CFRelease(t); }; |
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
24 }; |
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
25 |
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
26 template<typename T> |
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
27 using CFPtr = std::unique_ptr<T, CFDeconstructor<T>>; |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
28 |
258 | 29 #if __LP64__ |
30 typedef long NSInteger; | |
31 #else | |
32 typedef int NSInteger; | |
33 #endif | |
34 typedef int CGSConnection; | |
35 | |
36 typedef CGSConnection (*CGSDefaultConnectionForThreadSpec)(void); | |
37 typedef CGError (*CGSCopyWindowPropertySpec)(const CGSConnection, NSInteger, CFStringRef, CFStringRef*); | |
38 | |
39 static CGSDefaultConnectionForThreadSpec CGSDefaultConnectionForThread = nullptr; | |
40 static CGSCopyWindowPropertySpec CGSCopyWindowProperty = nullptr; | |
41 | |
42 static const CFStringRef kCoreGraphicsBundleID = CFSTR("com.apple.CoreGraphics"); | |
43 | |
44 /* Objective-C */ | |
45 typedef id (*object_message_send)(id, SEL, ...); | |
46 typedef id (*class_message_send)(Class, SEL, ...); | |
47 | |
48 static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend); | |
49 static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend); | |
50 | |
51 static bool GetCoreGraphicsPrivateSymbols() { | |
52 CFBundleRef core_graphics_bundle = CFBundleGetBundleWithIdentifier(kCoreGraphicsBundleID); | |
53 if (!core_graphics_bundle) | |
54 return false; | |
55 | |
56 CGSDefaultConnectionForThread = (CGSDefaultConnectionForThreadSpec)CFBundleGetFunctionPointerForName( | |
57 core_graphics_bundle, CFSTR("CGSDefaultConnectionForThread")); | |
58 if (!CGSDefaultConnectionForThread) | |
59 return false; | |
60 | |
61 CGSCopyWindowProperty = (CGSCopyWindowPropertySpec)CFBundleGetFunctionPointerForName( | |
62 core_graphics_bundle, CFSTR("CGSCopyWindowProperty")); | |
63 if (!CGSCopyWindowProperty) | |
64 return false; | |
65 | |
66 return true; | |
67 } | |
68 | |
69 template<typename T> | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
70 static bool GetCFNumber(CFNumberRef num, T& result) { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
71 if (!num) |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
72 return false; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
73 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
74 int64_t res; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
75 if (!CFNumberGetValue(num, static_cast<CFNumberType>(4), &res)) |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
76 return false; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
77 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
78 result = static_cast<T>(res); |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
79 return true; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
80 } |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
81 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
82 static bool StringFromCFString(CFStringRef string, std::string& result) { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
83 if (!string) |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
84 return false; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
85 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
86 result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1); |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
87 if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8)) |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
88 return false; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
89 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
90 return true; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
91 } |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
92 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
93 template<typename T> |
258 | 94 static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) { |
95 CFTypeRef data = nullptr; | |
96 if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data) | |
97 return false; | |
98 | |
99 if constexpr (std::is_arithmetic<T>::value) | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
100 GetCFNumber(reinterpret_cast<CFNumberRef>(data), out); |
258 | 101 else if constexpr (std::is_same<T, std::string>::value) |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
102 StringFromCFString(reinterpret_cast<CFStringRef>(data), out); |
258 | 103 else |
104 return false; | |
105 | |
106 return true; | |
107 } | |
108 | |
109 static bool GetWindowTitleAccessibility(unsigned int wid, pid_t pid, std::string& result) { | |
110 CGRect bounds = {0}; | |
111 { | |
112 const CGWindowID wids[1] = {wid}; | |
113 CFPtr<CFArrayRef> arr(CFArrayCreate(kCFAllocatorDefault, (CFTypeRef*)wids, 1, NULL)); | |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
114 CFPtr<CFArrayRef> dicts(CGWindowListCreateDescriptionFromArray(arr.get())); |
258 | 115 |
116 if (!dicts.get() || CFArrayGetCount(dicts.get()) < 1) | |
117 return false; | |
118 | |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
119 CFDictionaryRef dict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(dicts.get(), 0)); |
258 | 120 if (!dict) |
121 return false; | |
122 | |
123 CFDictionaryRef bounds_dict = nullptr; | |
124 if (!CFDictionaryGetValueIfPresent(dict, kCGWindowBounds, reinterpret_cast<CFTypeRef*>(&bounds_dict)) || | |
125 !bounds_dict) | |
126 return false; | |
127 | |
128 if (!CGRectMakeWithDictionaryRepresentation(bounds_dict, &bounds)) | |
129 return false; | |
130 } | |
131 | |
132 /* now we can actually do stuff */ | |
133 AXUIElementRef axapp = AXUIElementCreateApplication(pid); | |
134 CFPtr<CFArrayRef> windows; | |
135 { | |
136 CFArrayRef ref; | |
137 if ((AXUIElementCopyAttributeValue(axapp, kAXWindowsAttribute, reinterpret_cast<CFTypeRef*>(&ref)) != | |
138 kAXErrorSuccess) || | |
139 !windows) | |
140 return false; | |
141 | |
142 windows.reset(ref); | |
143 } | |
144 | |
145 const CFIndex count = CFArrayGetCount(windows.get()); | |
146 for (CFIndex i = 0; i < count; i++) { | |
147 const AXUIElementRef window = reinterpret_cast<AXUIElementRef>(CFArrayGetValueAtIndex(windows.get(), i)); | |
148 | |
149 /* does this leak memory? probably. */ | |
150 AXValueRef val; | |
151 if (AXUIElementCopyAttributeValue(window, kAXPositionAttribute, reinterpret_cast<CFTypeRef*>(&val)) == | |
152 kAXErrorSuccess) { | |
153 CGPoint point; | |
272
5437009cb10e
dep/animone: get macOS side building
Paper <paper@paper.us.eu.org>
parents:
271
diff
changeset
|
154 if (!AXValueGetValue(val, static_cast<AXValueType>(kAXValueCGPointType), reinterpret_cast<void*>(&point)) || |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
155 (point.x != bounds.origin.x || point.y != bounds.origin.y)) { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
156 CFRelease(val); |
258 | 157 continue; |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
158 } |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
159 } else { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
160 CFRelease(val); |
258 | 161 continue; |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
162 } |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
163 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
164 CFRelease(val); |
258 | 165 |
166 if (AXUIElementCopyAttributeValue(window, kAXSizeAttribute, reinterpret_cast<CFTypeRef*>(&val)) == | |
167 kAXErrorSuccess) { | |
168 CGSize size; | |
272
5437009cb10e
dep/animone: get macOS side building
Paper <paper@paper.us.eu.org>
parents:
271
diff
changeset
|
169 if (!AXValueGetValue(val, static_cast<AXValueType>(kAXValueCGSizeType), reinterpret_cast<void*>(&size)) || |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
170 (size.width != bounds.size.width || size.height != bounds.size.height)) { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
171 CFRelease(val); |
258 | 172 continue; |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
173 } |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
174 } else { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
175 CFRelease(val); |
258 | 176 continue; |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
177 } |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
178 |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
179 CFRelease(val); |
258 | 180 |
181 CFStringRef title; | |
182 if (AXUIElementCopyAttributeValue(window, kAXTitleAttribute, reinterpret_cast<CFTypeRef*>(&title)) == | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
183 kAXErrorSuccess) { |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
184 bool success = StringFromCFString(title, result); |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
185 CFRelease(title); |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
186 return success; |
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
187 } |
258 | 188 } |
189 | |
190 return false; | |
191 } | |
192 | |
193 static bool GetWindowTitle(unsigned int wid, pid_t pid, std::string& result) { | |
194 /* try using CoreGraphics (only usable on old versions of OS X) */ | |
195 if ((CGSDefaultConnectionForThread && CGSCopyWindowProperty) || GetCoreGraphicsPrivateSymbols()) { | |
196 CFPtr<CFStringRef> title; | |
197 { | |
198 CFStringRef t = nullptr; | |
199 CGSCopyWindowProperty(CGSDefaultConnectionForThread(), wid, CFSTR("kCGSWindowTitle"), &t); | |
200 title.reset(t); | |
201 } | |
202 | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
203 if (title && CFStringGetLength(title.get()) && StringFromCFString(title.get(), result)) |
258 | 204 return true; |
205 } | |
206 | |
207 /* then try linking to a window using the accessibility API */ | |
208 return AXIsProcessTrusted() ? GetWindowTitleAccessibility(wid, pid, result) : false; | |
209 } | |
210 | |
211 static bool GetProcessBundleIdentifierNew(pid_t pid, std::string& result) { | |
212 /* 10.6 and higher */ | |
213 const id app = | |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
214 cls_send((Class)objc_getClass("NSRunningApplication"), sel_getUid("runningApplicationWithProcessIdentifier:"), pid); |
258 | 215 if (!app) |
216 return false; | |
217 | |
218 CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier"))); | |
219 if (!bundle_id) | |
220 return false; | |
221 | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
222 return StringFromCFString(bundle_id, result); |
258 | 223 } |
224 | |
225 static bool GetProcessBundleIdentifierOld(pid_t pid, std::string& result) { | |
226 /* OS X 10.2; deprecated in 10.9 */ | |
227 ProcessSerialNumber psn; | |
228 if (GetProcessForPID(pid, &psn)) | |
229 return false; | |
230 | |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
231 CFPtr<CFDictionaryRef> info(ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask)); |
258 | 232 if (!info) |
233 return false; | |
234 | |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
235 CFStringRef value = reinterpret_cast<CFStringRef>(CFDictionaryGetValue(info.get(), CFSTR("CFBundleIdentifier"))); |
258 | 236 if (!value) |
237 return false; | |
238 | |
268
382b50754fe4
dep/animone: make osx code a bit less hacky
Paper <paper@paper.us.eu.org>
parents:
258
diff
changeset
|
239 return StringFromCFString(value, result); |
258 | 240 } |
241 | |
242 static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) { | |
243 /* The Bundle ID is essentially OS X's solution to Windows' | |
244 * "class name"; theoretically, it should be different for | |
245 * each program, although it requires an app bundle. | |
246 */ | |
247 if (GetProcessBundleIdentifierNew(pid, result)) | |
248 return true; | |
249 | |
271
f01b6e9c8fa2
dep/animone: make OS X code build
Paper <paper@paper.us.eu.org>
parents:
268
diff
changeset
|
250 return GetProcessBundleIdentifierOld(pid, result); |
258 | 251 } |
252 | |
253 bool EnumerateWindows(window_proc_t window_proc) { | |
254 if (!window_proc) | |
255 return false; | |
256 | |
257 const CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID); | |
258 if (!windows) | |
259 return false; | |
260 | |
261 const CFIndex count = CFArrayGetCount(windows); | |
262 for (CFIndex i = 0; i < count; i++) { | |
263 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows, i)); | |
264 if (!window) | |
265 continue; | |
266 | |
267 Process proc; | |
337
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
268 proc.platform = ExecutablePlatform::Xnu; |
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
269 CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid); |
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
270 if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.comm)) |
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
271 fd::GetProcessName(proc.pid, proc.comm); |
258 | 272 |
273 Window win; | |
337
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
274 win.platform = WindowPlatform::Quartz; |
342
adb79bdde329
dep/animone: fix tons of issues
Paper <paper@paper.us.eu.org>
parents:
337
diff
changeset
|
275 CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id.quartz); |
258 | 276 |
337
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
277 GetProcessBundleIdentifier(proc.pid, win.class_name); |
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
278 GetWindowTitle(win.id, proc.pid, win.text); |
258 | 279 |
280 if (!window_proc(proc, win)) { | |
281 CFRelease(windows); | |
282 return false; | |
283 } | |
284 } | |
285 | |
286 CFRelease(windows); | |
287 | |
288 return true; | |
289 } | |
290 | |
291 } // namespace animone::internal::quartz |