Obtaining Window ID on macOS

しむどん 2024-10-16

I am considering specifying windows and manipulating them on macOS. To do this, I researched how to list window IDs. When creating a program that operates using macOS features, implementing it in Objective-C or Swift is more straightforward since documentation is available on the official website.

Preliminary Battle

In fact, there are others who have done similar things. GetWindowID implements the functionality to obtain window IDs. While this code is quite small, I will extract the essential parts from that code and confirm its functionality.

Implementing Window ID Retrieval in Objective-C

I will use Cocoa and CoreGraphics to retrieve the window ID.

#include <Cocoa/Cocoa.h>
#include <CoreGraphics/CGWindow.h>

int main(int argc, char **argv) {
  NSArray *windows = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements,
							   kCGNullWindowID);
  for(NSDictionary *window in windows) {
    NSString *currentApp = window[(NSString *)kCGWindowOwnerName];
    NSString *currentWindowTitle = window[(NSString *)kCGWindowName];

    printf("%10d : %-50s : %s\n",
	   [window[(NSString *)kCGWindowNumber] intValue],
	   currentApp.UTF8String,
	   currentWindowTitle.UTF8String);
  }
  printf("Okay\n");
}

I use clang to build it. Since I am using the Cocoa and CoreGraphics frameworks, I pass them as optional arguments.

clang ListWindowId.m -o ListWindowIdObc -framework CoreGraphics -framework Cocoa

When this is executed, it outputs a list in the format Window ID : Application Name : Window Title to standard output.

Implementing Window ID Retrieval in Swift

If it can be done in Objective-C, it can certainly be done in Swift as well. Let’s also check the implementation method in Swift.

import Cocoa
import CoreGraphics

func main() {
    let windows = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as! [[String: Any]]
    for window in windows {
        let windowId = window[kCGWindowNumber as String] as! Int
        let appName = window[kCGWindowOwnerName as String] as! String
        let title = (window[kCGWindowName as String] as? String) ?? ""
        print(String(format:"%d: %@ : %@", windowId, appName, title))
    }
}

main()

To run it without producing a binary file, use the swift command.

swift ListWindowId.swift

Displaying the List in Emacs

Let’s make it possible to verify this implementation from Emacs.

(setq macos-window-list-executable
      (let ((exec-path (list (expand-file-name "~/ng/symdon/articles/posts/1729078231"))))
	(executable-find "ListWindowIdObc")))

(defun macos-window-list ()
  (interactive)
  (shell-command macos-window-list-executable))

(provide 'macos-window-list)
;; macos-window-list.el ends here