Mercurial > minori
annotate dep/animone/src/win/quartz.cc @ 337:a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
1: animone now has its own syntax divergent from anisthesia,
making different platforms actually have their own sections
2: process names in animone are now called `comm' (this will
probably break things). this is what its called in bsd/linux
so I'm just going to use it everywhere
3: the X11 code now checks for the existence of a UTF-8 window title
and passes it if available
4: ANYTHING THATS NOT LINUX IS 100% UNTESTED AND CAN AND WILL BREAK!
I still actually need to test the bsd code. to be honest I'm probably
going to move all of the bsds into separate files because they're
all essentially different operating systems at this point
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Wed, 19 Jun 2024 12:51:15 -0400 |
parents | 246017a7907a |
children | adb79bdde329 |
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; |
a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
Paper <paper@paper.us.eu.org>
parents:
299
diff
changeset
|
275 CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id); |
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 |