Mercurial > minori
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/win/quartz.cc Tue Jan 02 06:05:06 2024 -0500 @@ -0,0 +1,125 @@ +/* + * win/quartz.cc: support for macOS (the Quartz Compositor) + * + * This file does not require an Objective-C++ compiler, + * but it *does* require an Objective-C runtime and linking + * with AppKit in order to receive proper window titles. +*/ +#include "animia/win/quartz.h" +#include "animia/util/osx.h" +#include "animia.h" + +#include <objc/runtime.h> +#include <objc/message.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <CoreGraphics/CoreGraphics.h> + +namespace animia::internal::quartz { + +typedef id (*object_message_send)(id, SEL, ...); +typedef id (*class_message_send)(Class, SEL, ...); + +static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend); +static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend); + +static bool GetWindowTitle(unsigned int wid, std::string& result) { + // NSApplication* app = [NSApplication sharedApplication]; + const id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication")); + + // NSWindow* window = [app windowWithWindowNumber: wid]; + const id window = obj_send(app, sel_getUid("windowWithWindowNumber:"), wid); + if (!window) + return false; + + // NSString* title = [window title]; + const CFStringRef title = reinterpret_cast<CFStringRef>(obj_send(window, sel_getUid("title"))); + if (!title) + return false; + + // return [title UTF8String]; + return osx::util::StringFromCFString(title, result); +} + +static bool GetProcessBundleIdentifier(pid_t pid, std::string& result) { + /* The Bundle ID is essentially OS X's solution to Windows' + * "class name"; theoretically, it should be different for + * each program, although it requires an app bundle. + */ + + // NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier: pid]; + const id app = cls_send(objc_getClass("NSRunningApplication"), sel_getUid("runningApplicationWithProcessIdentifier:"), pid); + if (!app) + return false; + + // NSString* bundle_id = [app bundleIdentifier]; + const CFStringRef bundle_id = reinterpret_cast<CFStringRef>(obj_send(app, sel_getUid("bundleIdentifier"))); + if (!bundle_id) + return false; + + // return [bundle_id UTF8String]; + return osx::util::StringFromCFString(bundle_id, result); +} + +template<typename T> +static bool CFDictionaryGetValue(CFDictionaryRef thedict, CFStringRef key, T& out) { + CFTypeRef data = nullptr; + if (!CFDictionaryGetValueIfPresent(thedict, key, reinterpret_cast<const void**>(&data)) || !data) + return false; + + if constexpr (std::is_arithmetic<T>::value) + osx::util::GetCFNumber(reinterpret_cast<CFNumberRef>(data), out); + else if constexpr (std::is_same<T, std::string>::value) + osx::util::StringFromCFString(reinterpret_cast<CFStringRef>(data), out); + else + return false; + + return true; +} + +bool EnumerateWindows(window_proc_t window_proc) { + if (!window_proc) + return false; + + const CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID); + if (!windows) + return false; + + const CFIndex count = CFArrayGetCount(windows); + for (CFIndex i = 0; i < count; i++) { + CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows, i)); + if (!window) + continue; + + Process proc; + { + CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerPID"), proc.pid); + if (!CFDictionaryGetValue(window, CFSTR("kCGWindowOwnerName"), proc.name)) + osx::util::GetProcessName(proc.pid, proc.name); + } + + Window win; + { + CFDictionaryGetValue(window, CFSTR("kCGWindowNumber"), win.id); + + if (!GetProcessBundleIdentifier(proc.pid, win.class_name)) { + // Fallback to the Quartz window name, which is unlikely to be filled, but it + // *could* be. + CFDictionaryGetValue(window, CFSTR("kCGWindowName"), win.class_name); + } + + GetWindowTitle(win.id, win.text); + } + + if (!window_proc(proc, win)) { + CFRelease(windows); + return false; + } + } + + CFRelease(windows); + + return true; +} + +} // namespace animia::win::detail