Mercurial > minori
comparison dep/animia/src/win/quartz.cc @ 202:71832ffe425a
animia: re-add kvm fd source
this is all being merged from my wildly out-of-date laptop. SORRY!
in other news, I edited the CI file to install the wayland client
as well, so the linux CI build might finally get wayland stuff.
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Tue, 02 Jan 2024 06:05:06 -0500 |
parents | bc1ae1810855 |
children | a7d0d543b334 |
comparison
equal
deleted
inserted
replaced
201:8f6f8dd2eb23 | 202:71832ffe425a |
---|---|
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 and linking | |
6 * with AppKit in order to receive proper window titles. | |
7 */ | |
8 #include "animia/win/quartz.h" | |
9 #include "animia/util/osx.h" | |
10 #include "animia.h" | |
11 | |
12 #include <objc/runtime.h> | |
13 #include <objc/message.h> | |
14 | |
15 #include <CoreFoundation/CoreFoundation.h> | |
16 #include <CoreGraphics/CoreGraphics.h> | |
17 | |
18 namespace animia::internal::quartz { | |
19 | |
20 typedef id (*object_message_send)(id, SEL, ...); | |
21 typedef id (*class_message_send)(Class, SEL, ...); | |
22 | |
23 static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend); | |
24 static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend); | |
25 | |
26 static bool GetWindowTitle(unsigned int wid, std::string& result) { | |
27 // NSApplication* app = [NSApplication sharedApplication]; | |
28 const id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication")); | |
29 | |
30 // NSWindow* window = [app windowWithWindowNumber: wid]; | |
31 const id window = obj_send(app, sel_getUid("windowWithWindowNumber:"), wid); | |
32 if (!window) | |
33 return false; | |
34 | |
35 // NSString* title = [window title]; | |
36 const CFStringRef title = reinterpret_cast<CFStringRef>(obj_send(window, sel_getUid("title"))); | |
37 if (!title) | |
38 return false; | |
39 | |
40 // return [title UTF8String]; | |
41 return osx::util::StringFromCFString(title, result); | |
42 } | |
43 | |
44 static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) { | |
45 /* The Bundle ID is essentially OS X's solution to Windows' | |
46 * "class name"; theoretically, it should be different for | |
47 * each program, although it requires an app bundle. | |
48 */ | |
49 | |
50 // NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier: pid]; | |
51 const id app = cls_send(objc_getClass("NSRunningApplication"), sel_getUid("runningApplicationWithProcessIdentifier:"), pid); | |
52 if (!app) | |
53 return false; | |
54 | |
55 // NSString* bundle_id = [app bundleIdentifier]; | |
56 const CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier"))); | |
57 if (!bundle_id) | |
58 return false; | |
59 | |
60 // return [bundle_id UTF8String]; | |
61 return osx::util::StringFromCFString(bundle_id, result); | |
62 } | |
63 | |
64 template<typename T> | |
65 static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) { | |
66 CFTypeRef data = nullptr; | |
67 if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data) | |
68 return false; | |
69 | |
70 if constexpr (std::is_arithmetic<T>::value) | |
71 osx::util::GetCFNumber(reinterpret_cast<CFNumberRef>(data), out); | |
72 else if constexpr (std::is_same<T, std::string>::value) | |
73 osx::util::StringFromCFString(reinterpret_cast<CFStringRef>(data), out); | |
74 else | |
75 return false; | |
76 | |
77 return true; | |
78 } | |
79 | |
80 bool EnumerateWindows(window_proc_t window_proc) { | |
81 if (!window_proc) | |
82 return false; | |
83 | |
84 const CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID); | |
85 if (!windows) | |
86 return false; | |
87 | |
88 const CFIndex count = CFArrayGetCount(windows); | |
89 for (CFIndex i = 0; i < count; i++) { | |
90 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows, i)); | |
91 if (!window) | |
92 continue; | |
93 | |
94 Process proc; | |
95 { | |
96 CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid); | |
97 if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.name)) | |
98 osx::util::GetProcessName(proc.pid, proc.name); | |
99 } | |
100 | |
101 Window win; | |
102 { | |
103 CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id); | |
104 | |
105 if (!GetProcessBundleIdentifier(proc.pid, win.class_name)) { | |
106 // Fallback to the Quartz window name, which is unlikely to be filled, but it | |
107 // *could* be. | |
108 CFDictionaryGetValue(window, CFSTR("kCGWindowName"), win.class_name); | |
109 } | |
110 | |
111 GetWindowTitle(win.id, win.text); | |
112 } | |
113 | |
114 if (!window_proc(proc, win)) { | |
115 CFRelease(windows); | |
116 return false; | |
117 } | |
118 } | |
119 | |
120 CFRelease(windows); | |
121 | |
122 return true; | |
123 } | |
124 | |
125 } // namespace animia::win::detail |