I am writing an application that needs to periodically obtain the PID, process name, window ID and window name of the active window. The program is written in Go, but the issues relate to any FFI into the Mac ecosystem. I have two approaches, via AppleScript and via Objective C.
Both give a 75% solution; either missing the window ID or the window name. I would like to find a single unified solution that gives all four attributes rather than having to cobble together one that depends on both of the approaches that I have.
AppleScript approach; does not provide WindowNumber for most cases.
global activeApp, activePID, activeName, windowName
set windowName to ""
tell application "System Events"
set activeApp to first application process whose frontmost is true
set activePID to unix id of activeApp
set activeName to name of activeApp
tell process activeName
try
tell (1st window whose value of attribute "AXMain" is true)
set windowName to value of attribute "AXTitle"
end tell
end try
end tell
end tell
return ("{\"pid\":" & activePID & ",\"name\":\"" & activeName & "\",\"window\":\"" & windowName & "\"}" as text)
Objective C approach; does not provide WindowName for most cases.
#include <Cocoa/Cocoa.h>
#include <CoreGraphics/CGWindow.h>
struct details {
int wid;
int pid;
const char* name;
const char* window;
};
int activeWindow(struct details *d)
{
if (d == NULL) {
return 0;
}
NSArray *windows = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements|kCGWindowListOptionOnScreenOnly,kCGNullWindowID);
for(NSDictionary *window in windows){
int WindowLayer = [[window objectForKey:(NSString *)kCGWindowLayer] intValue];
if (WindowLayer == 0) {
d->wid = [[window objectForKey:(NSString *)kCGWindowNumber] intValue];
d->pid = [[window objectForKey:(NSString *)kCGWindowOwnerPID] intValue];
d->name = [[window objectForKey:(NSString *)kCGWindowOwnerName] UTF8String];
d->window = [[window objectForKey:(NSString *)kCGWindowName] UTF8String];
return 1;
}
}
return 0;
}
From a Developer forum topic, your Objective-C approach should work if the application has been given Screen Recording permission.
There are a few windows (such as those in menu items) that have weird names, and some just don't have them, but most application and document windows I've tried do have names. There doesn't appear to be an entitlement for Screen Recording, so you would need to direct the user to add the application to the privacy list.
When testing with Xcode 14.1 in macOS Monterey 12.6.5, the kCGWindowName
key in the window list dictionary is included when permission has been granted, otherwise it is left out.