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 |
