diff 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
line wrap: on
line diff
--- a/dep/animia/src/win/quartz.cc	Wed Dec 06 21:26:13 2023 -0500
+++ b/dep/animia/src/win/quartz.cc	Thu Dec 07 01:48:04 2023 -0500
@@ -1,3 +1,10 @@
+/*
+ * 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"
@@ -8,8 +15,6 @@
 #include <CoreFoundation/CoreFoundation.h>
 #include <CoreGraphics/CoreGraphics.h>
 
-#include <iostream>
-
 namespace animia::internal::quartz {
 
 typedef id (*object_message_send)(id, SEL, ...);
@@ -20,16 +25,15 @@
 
 static bool GetWindowTitle(unsigned int wid, std::string& result) {
 	// NSApplication* app = [NSApplication sharedApplication];
-	id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
+	const id app = cls_send(objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
 
 	// NSWindow* window = [app windowWithWindowNumber: wid];
-	id window = obj_send(app, sel_getUid("windowWithWindowNumber:"), wid);
+	const id window = obj_send(app, sel_getUid("windowWithWindowNumber:"), wid);
 	if (!window)
 		return false;
 
 	// NSString* title = [window title];
-	// does this have to be freed?
-	CFStringRef title = reinterpret_cast<CFStringRef>(obj_send(window, sel_getUid("title")));
+	const CFStringRef title = reinterpret_cast<CFStringRef>(obj_send(window, sel_getUid("title")));
 	if (!title)
 		return false;
 
@@ -37,6 +41,26 @@
 	return osx::util::StringFromCFString(title, result);
 }
 
+static bool GetWindowBundleIdentifier(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);
+}
+
 bool QuartzWinTools::EnumerateWindows(window_proc_t window_proc) {
 	if (!window_proc)
 		return false;
@@ -74,7 +98,9 @@
 				if (CFDictionaryGetValueIfPresent(window, CFSTR("kCGWindowNumber"), reinterpret_cast<const void**>(&num)) && num)
 					osx::util::GetCFNumber(num, win.id);
 			}
-			{
+			if (!GetWindowBundleIdentifier(proc.pid, win.class_name)) {
+				// Fallback to the Quartz window name, which is unlikely to be filled, but it
+				// *could* be.
 				CFStringRef str = nullptr;
 				if (CFDictionaryGetValueIfPresent(window, CFSTR("kCGWindowName"), reinterpret_cast<const void**>(&str)) && str)
 					osx::util::StringFromCFString(str, win.class_name);