Mercurial > minori
comparison dep/animone/src/win/quartz.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 | f01b6e9c8fa2 |
comparison
equal
deleted
inserted
replaced
266:1a6a5d3a94cd | 268:382b50754fe4 |
---|---|
15 #include <CoreFoundation/CoreFoundation.h> | 15 #include <CoreFoundation/CoreFoundation.h> |
16 #include <CoreGraphics/CoreGraphics.h> | 16 #include <CoreGraphics/CoreGraphics.h> |
17 | 17 |
18 namespace animone::internal::quartz { | 18 namespace animone::internal::quartz { |
19 | 19 |
20 template<typename T> | |
21 using CFPtr = std::unique_ptr<T, CFDecontructor<T>>; | |
22 | |
20 #if __LP64__ | 23 #if __LP64__ |
21 typedef long NSInteger; | 24 typedef long NSInteger; |
22 #else | 25 #else |
23 typedef int NSInteger; | 26 typedef int NSInteger; |
24 #endif | 27 #endif |
56 | 59 |
57 return true; | 60 return true; |
58 } | 61 } |
59 | 62 |
60 template<typename T> | 63 template<typename T> |
64 static bool GetCFNumber(CFNumberRef num, T& result) { | |
65 if (!num) | |
66 return false; | |
67 | |
68 int64_t res; | |
69 if (!CFNumberGetValue(num, static_cast<CFNumberType>(4), &res)) | |
70 return false; | |
71 | |
72 result = static_cast<T>(res); | |
73 return true; | |
74 } | |
75 | |
76 static bool StringFromCFString(CFStringRef string, std::string& result) { | |
77 if (!string) | |
78 return false; | |
79 | |
80 result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1); | |
81 if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8)) | |
82 return false; | |
83 | |
84 return true; | |
85 } | |
86 | |
87 template<typename T> | |
61 static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) { | 88 static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) { |
62 CFTypeRef data = nullptr; | 89 CFTypeRef data = nullptr; |
63 if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data) | 90 if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data) |
64 return false; | 91 return false; |
65 | 92 |
66 if constexpr (std::is_arithmetic<T>::value) | 93 if constexpr (std::is_arithmetic<T>::value) |
67 osx::util::GetCFNumber(reinterpret_cast<CFNumberRef>(data), out); | 94 GetCFNumber(reinterpret_cast<CFNumberRef>(data), out); |
68 else if constexpr (std::is_same<T, std::string>::value) | 95 else if constexpr (std::is_same<T, std::string>::value) |
69 osx::util::StringFromCFString(reinterpret_cast<CFStringRef>(data), out); | 96 StringFromCFString(reinterpret_cast<CFStringRef>(data), out); |
70 else | 97 else |
71 return false; | 98 return false; |
72 | 99 |
73 return true; | 100 return true; |
74 } | 101 } |
102 | |
103 template<typename T> | |
104 struct CFDeconstructor { | |
105 using pointer = T; | |
106 void operator()(pointer t) const { ::CFRelease(t); }; | |
107 }; | |
75 | 108 |
76 static bool GetWindowTitleAccessibility(unsigned int wid, pid_t pid, std::string& result) { | 109 static bool GetWindowTitleAccessibility(unsigned int wid, pid_t pid, std::string& result) { |
77 CGRect bounds = {0}; | 110 CGRect bounds = {0}; |
78 { | 111 { |
79 const CGWindowID wids[1] = {wid}; | 112 const CGWindowID wids[1] = {wid}; |
117 AXValueRef val; | 150 AXValueRef val; |
118 if (AXUIElementCopyAttributeValue(window, kAXPositionAttribute, reinterpret_cast<CFTypeRef*>(&val)) == | 151 if (AXUIElementCopyAttributeValue(window, kAXPositionAttribute, reinterpret_cast<CFTypeRef*>(&val)) == |
119 kAXErrorSuccess) { | 152 kAXErrorSuccess) { |
120 CGPoint point; | 153 CGPoint point; |
121 if (!AXValueGetValue(val, kAXValueTypeCGPoint, reinterpret_cast<CFTypeRef>(&point)) || | 154 if (!AXValueGetValue(val, kAXValueTypeCGPoint, reinterpret_cast<CFTypeRef>(&point)) || |
122 (point.x != bounds.origin.x || point.y != bounds.origin.y)) | 155 (point.x != bounds.origin.x || point.y != bounds.origin.y)) { |
156 CFRelease(val); | |
123 continue; | 157 continue; |
124 } else | 158 } |
159 } else { | |
160 CFRelease(val); | |
125 continue; | 161 continue; |
162 } | |
163 | |
164 CFRelease(val); | |
126 | 165 |
127 if (AXUIElementCopyAttributeValue(window, kAXSizeAttribute, reinterpret_cast<CFTypeRef*>(&val)) == | 166 if (AXUIElementCopyAttributeValue(window, kAXSizeAttribute, reinterpret_cast<CFTypeRef*>(&val)) == |
128 kAXErrorSuccess) { | 167 kAXErrorSuccess) { |
129 CGSize size; | 168 CGSize size; |
130 if (!AXValueGetValue(val, kAXValueTypeCGSize, reinterpret_cast<CFTypeRef>(&size)) || | 169 if (!AXValueGetValue(val, kAXValueTypeCGSize, reinterpret_cast<CFTypeRef>(&size)) || |
131 (size.width != bounds.size.width || size.height != bounds.size.height)) | 170 (size.width != bounds.size.width || size.height != bounds.size.height)) { |
171 CFRelease(val); | |
132 continue; | 172 continue; |
133 } else | 173 } |
174 } else { | |
175 CFRelease(val); | |
134 continue; | 176 continue; |
177 } | |
178 | |
179 CFRelease(val); | |
135 | 180 |
136 CFStringRef title; | 181 CFStringRef title; |
137 if (AXUIElementCopyAttributeValue(window, kAXTitleAttribute, reinterpret_cast<CFTypeRef*>(&title)) == | 182 if (AXUIElementCopyAttributeValue(window, kAXTitleAttribute, reinterpret_cast<CFTypeRef*>(&title)) == |
138 kAXErrorSuccess) | 183 kAXErrorSuccess) { |
139 return osx::util::StringFromCFString(title, result); | 184 bool success = StringFromCFString(title, result); |
185 CFRelease(title); | |
186 return success; | |
187 } | |
140 } | 188 } |
141 | 189 |
142 return false; | 190 return false; |
143 } | 191 } |
144 | 192 |
150 CFStringRef t = nullptr; | 198 CFStringRef t = nullptr; |
151 CGSCopyWindowProperty(CGSDefaultConnectionForThread(), wid, CFSTR("kCGSWindowTitle"), &t); | 199 CGSCopyWindowProperty(CGSDefaultConnectionForThread(), wid, CFSTR("kCGSWindowTitle"), &t); |
152 title.reset(t); | 200 title.reset(t); |
153 } | 201 } |
154 | 202 |
155 if (title && CFStringGetLength(title.get()) && osx::util::StringFromCFString(title.get(), result)) | 203 if (title && CFStringGetLength(title.get()) && StringFromCFString(title.get(), result)) |
156 return true; | 204 return true; |
157 } | 205 } |
158 | 206 |
159 /* then try linking to a window using the accessibility API */ | 207 /* then try linking to a window using the accessibility API */ |
160 return AXIsProcessTrusted() ? GetWindowTitleAccessibility(wid, pid, result) : false; | 208 return AXIsProcessTrusted() ? GetWindowTitleAccessibility(wid, pid, result) : false; |
169 | 217 |
170 CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier"))); | 218 CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier"))); |
171 if (!bundle_id) | 219 if (!bundle_id) |
172 return false; | 220 return false; |
173 | 221 |
174 result = osx::util::StringFromCFString(bundle_id, result); | 222 return StringFromCFString(bundle_id, result); |
175 return true; | |
176 } | 223 } |
177 | 224 |
178 static bool GetProcessBundleIdentifierOld(pid_t pid, std::string& result) { | 225 static bool GetProcessBundleIdentifierOld(pid_t pid, std::string& result) { |
179 /* OS X 10.2; deprecated in 10.9 */ | 226 /* OS X 10.2; deprecated in 10.9 */ |
180 ProcessSerialNumber psn; | 227 ProcessSerialNumber psn; |
187 | 234 |
188 CFStringRef value = reinterpret_cast<CFStringRef>(CFDictionaryGetValue(dict, CFSTR("CFBundleIdentifier"))); | 235 CFStringRef value = reinterpret_cast<CFStringRef>(CFDictionaryGetValue(dict, CFSTR("CFBundleIdentifier"))); |
189 if (!value) | 236 if (!value) |
190 return false; | 237 return false; |
191 | 238 |
192 result = osx::util::StringFromCFString(value, result); | 239 return StringFromCFString(value, result); |
193 return true; | |
194 } | 240 } |
195 | 241 |
196 static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) { | 242 static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) { |
197 /* The Bundle ID is essentially OS X's solution to Windows' | 243 /* The Bundle ID is essentially OS X's solution to Windows' |
198 * "class name"; theoretically, it should be different for | 244 * "class name"; theoretically, it should be different for |