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