1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#!/usr/bin/env swift
// winctx — print the frontmost app + focused-window title as JSON, for autotrack.
//
// Uses the Accessibility (AX) API, which exposes window titles that AppleScript's
// "System Events … name of front window" misses for many apps (Electron, some
// native apps). Falls back to an empty title when AX is denied or the app has
// none — the daemon then tries its osascript path.
//
// Output: {"app":"<name>","title":"<focused window title>"}
// Requires Accessibility permission (System Settings → Privacy → Accessibility)
// for the process that runs it; without it, title comes back empty (app still works).
import Cocoa
import ApplicationServices
func jsonEsc(_ s: String) -> String {
var o = ""
for u in s.unicodeScalars {
switch u {
case "\"": o += "\\\""
case "\\": o += "\\\\"
case "\n": o += "\\n"
case "\r": o += "\\r"
case "\t": o += "\\t"
default:
if u.value < 0x20 { o += String(format: "\\u%04x", u.value) }
else { o.unicodeScalars.append(u) }
}
}
return o
}
func focusedTitle(_ pid: pid_t) -> String {
let axApp = AXUIElementCreateApplication(pid)
var winRef: CFTypeRef?
guard AXUIElementCopyAttributeValue(axApp, kAXFocusedWindowAttribute as CFString, &winRef)
== .success, let win = winRef else { return "" }
var titleRef: CFTypeRef?
guard AXUIElementCopyAttributeValue(win as! AXUIElement, kAXTitleAttribute as CFString,
&titleRef) == .success, let t = titleRef as? String else { return "" }
return t
}
guard let app = NSWorkspace.shared.frontmostApplication else {
print("{\"app\":\"\",\"title\":\"\"}")
exit(0)
}
let name = app.localizedName ?? ""
let title = focusedTitle(app.processIdentifier)
print("{\"app\":\"\(jsonEsc(name))\",\"title\":\"\(jsonEsc(title))\"}")
|