Mercurial > minori
view dep/animia/src/win/quartz.cc @ 198:bc1ae1810855
dep/animia: switch from using classes to global functions
the old idea was ok, but sort of hackish; this method doesn't use classes
at all, and this way (especially important!) we can do wayland stuff AND x11
at the same time, which wasn't really possible without stupid workarounds in
the other method
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 24 Dec 2023 02:59:42 -0500 |
parents | c4ca035c565d |
children | a7d0d543b334 |
line wrap: on
line source
/* * 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