macOSでウィンドウIDを取得する

しむどん 2024-10-16

macOSでウィンドウを指定し、ウィンドウを操作する事を考えている。そのためにウィンドウIDを一覧で表示する方法を調べる事にした。macOSの機能を使って動作するプログラムを作る場合、Objective-CかSwiftで実装する方が公式ドキュメントで説明が記述されているためやりやすい。

前哨戦

そもそも、同じような事をしている人達はいる。GetWindowIDは正にウィンドウIDを取得する機能を実装していた。このコードはとても小さいのだけれ、その小さいコードの中で、更に本質的な部分のみを抽出して動作を確認する。

Objective-CでウィンドウIDの取得を実装する

CocoaとCoreGraphicsを使用して、ウィンドウ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");
}

ビルドにはclangを使う。フレームワークにCocoaとCoreGraphicsを使用しているため、オプション引数として渡している。

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

これを実行すると ウィンドウID : アプリケーション名 : ウィンドウタイトル という形式で、一覧を標準出力に出力する。

SwiftでウィンドウIDの取得を実装する

Objective-C でできるのであれば、当然Swiftでもできる。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()

バイナリファイルを出力せず実行するには、 swift コマンドを使う。

swift ListWindowId.swift

Emacsで一覧を表示する

この実装を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