Mercurial > minori
annotate dep/animone/src/win/quartz.cc @ 327:b5d6c27c308f
anime: refactor Anime::SeriesSeason to Season class
ToLocalString has also been altered to take in both season
and year because lots of locales actually treat formatting
seasons differently! most notably is Russian which adds a
suffix at the end to notate seasons(??)
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 13 Jun 2024 01:49:18 -0400 |
parents | 246017a7907a |
children | a7d4e5107531 |
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; | |
268 { | |
269 CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid); | |
270 if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.name)) | |
299
246017a7907a
dep/animone: clean up OS X code
Paper <paper@paper.us.eu.org>
parents:
272
diff
changeset
|
271 fd::GetProcessName(proc.pid, proc.name); |
258 | 272 } |
273 | |
274 Window win; | |
275 { | |
276 CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id); | |
277 | |
278 if (!GetProcessBundleIdentifier(proc.pid, win.class_name)) | |
299
246017a7907a
dep/animone: clean up OS X code
Paper <paper@paper.us.eu.org>
parents:
272
diff
changeset
|
279 /* XXX is this right? */ |
258 | 280 CFDictionaryGetValue(window, CFSTR("kCGWindowName"), win.class_name); |
281 | |
282 GetWindowTitle(win.id, proc.pid, win.text); | |
283 } | |
284 | |
285 if (!window_proc(proc, win)) { | |
286 CFRelease(windows); | |
287 return false; | |
288 } | |
289 } | |
290 | |
291 CFRelease(windows); | |
292 | |
293 return true; | |
294 } | |
295 | |
296 } // namespace animone::internal::quartz |