comparison dep/animia/src/win/quartz.cc @ 191:0fc126d52de4

animia: multiple stylistic choices win.class_name is now used to store bundle IDs on OS X, add some little explanations here and there for dumb stuff
author Paper <mrpapersonic@gmail.com>
date Thu, 07 Dec 2023 01:48:04 -0500
parents 2d5823df870f
children 50108040d792
comparison
equal deleted inserted replaced
190:2d5823df870f 191:0fc126d52de4
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 */
1 #include "animia/win/quartz.h" 8 #include "animia/win/quartz.h"
2 #include "animia/util/osx.h" 9 #include "animia/util/osx.h"
3 #include "animia.h" 10 #include "animia.h"
4 11
5 #include <objc/runtime.h> 12 #include <objc/runtime.h>
6 #include <objc/message.h> 13 #include <objc/message.h>
7 14
8 #include <CoreFoundation/CoreFoundation.h> 15 #include <CoreFoundation/CoreFoundation.h>
9 #include <CoreGraphics/CoreGraphics.h> 16 #include <CoreGraphics/CoreGraphics.h>
10
11 #include <iostream>
12 17
13 namespace animia::internal::quartz { 18 namespace animia::internal::quartz {
14 19
15 typedef id (*object_message_send)(id, SEL, ...); 20 typedef id (*object_message_send)(id, SEL, ...);
16 typedef id (*class_message_send)(Class, SEL, ...); 21 typedef id (*class_message_send)(Class, SEL, ...);
18 static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend); 23 static const object_message_send obj_send = reinterpret_cast<object_message_send>(objc_msgSend);
19 static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend); 24 static const class_message_send cls_send = reinterpret_cast<class_message_send>(objc_msgSend);
20 25
21 static bool GetWindowTitle(unsigned int wid, std::string& result) { 26 static bool GetWindowTitle(unsigned int wid, std::string& result) {
22 // NSApplication* app = [NSApplication sharedApplication]; 27 // NSApplication* app = [NSApplication sharedApplication];
23 id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication")); 28 const id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
24 29
25 // NSWindow* window = [app windowWithWindowNumber: wid]; 30 // NSWindow* window = [app windowWithWindowNumber: wid];
26 id window = obj_send(app, sel_getUid("windowWithWindowNumber:"), wid); 31 const id window = obj_send(app, sel_getUid("windowWithWindowNumber:"), wid);
27 if (!window) 32 if (!window)
28 return false; 33 return false;
29 34
30 // NSString* title = [window title]; 35 // NSString* title = [window title];
31 // does this have to be freed? 36 const CFStringRef title = reinterpret_cast<CFStringRef>(obj_send(window, sel_getUid("title")));
32 CFStringRef title = reinterpret_cast<CFStringRef>(obj_send(window, sel_getUid("title")));
33 if (!title) 37 if (!title)
34 return false; 38 return false;
35 39
36 // return [title UTF8String]; 40 // return [title UTF8String];
37 return osx::util::StringFromCFString(title, result); 41 return osx::util::StringFromCFString(title, result);
42 }
43
44 static bool GetWindowBundleIdentifier(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);
38 } 62 }
39 63
40 bool QuartzWinTools::EnumerateWindows(window_proc_t window_proc) { 64 bool QuartzWinTools::EnumerateWindows(window_proc_t window_proc) {
41 if (!window_proc) 65 if (!window_proc)
42 return false; 66 return false;
72 { 96 {
73 CFNumberRef num = nullptr; 97 CFNumberRef num = nullptr;
74 if (CFDictionaryGetValueIfPresent(window, CFSTR("kCGWindowNumber"), reinterpret_cast<const void**>(&num)) && num) 98 if (CFDictionaryGetValueIfPresent(window, CFSTR("kCGWindowNumber"), reinterpret_cast<const void**>(&num)) && num)
75 osx::util::GetCFNumber(num, win.id); 99 osx::util::GetCFNumber(num, win.id);
76 } 100 }
77 { 101 if (!GetWindowBundleIdentifier(proc.pid, win.class_name)) {
102 // Fallback to the Quartz window name, which is unlikely to be filled, but it
103 // *could* be.
78 CFStringRef str = nullptr; 104 CFStringRef str = nullptr;
79 if (CFDictionaryGetValueIfPresent(window, CFSTR("kCGWindowName"), reinterpret_cast<const void**>(&str)) && str) 105 if (CFDictionaryGetValueIfPresent(window, CFSTR("kCGWindowName"), reinterpret_cast<const void**>(&str)) && str)
80 osx::util::StringFromCFString(str, win.class_name); 106 osx::util::StringFromCFString(str, win.class_name);
81 } 107 }
82 GetWindowTitle(win.id, win.text); 108 GetWindowTitle(win.id, win.text);