Skip to content

Commit ba7c9a7

Browse files
committed
Fix macOS screenshot script
1 parent be0a752 commit ba7c9a7

1 file changed

Lines changed: 122 additions & 21 deletions

File tree

.github/actions/screenshot/macos-screenshot.sh

Lines changed: 122 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,106 @@ APP_PID=$!
2525
echo $APP_PID > app.pid
2626
echo "Application launched with PID: $APP_PID"
2727

28-
# Wait for application startup
29-
sleep 3
28+
# CoreGraphics can expose the window id used by screencapture -l even when
29+
# Accessibility does not provide AXWindowNumber on GitHub-hosted runners.
30+
if [ ! -x get_cg_window_info ]; then
31+
cat > get_cg_window_info.swift <<'SWIFT'
32+
import CoreGraphics
33+
import Foundation
3034
31-
# Wait for the window to appear
32-
echo "Waiting for window to appear..."
33-
for i in {1..20}; do
34-
EXISTS=$(osascript -e "tell application \"System Events\" to return exists process \"$APP_NAME\"" || true)
35-
if [ "$EXISTS" = "true" ]; then
36-
HAS_WIN=$(osascript -e "tell application \"System Events\" to tell process \"$APP_NAME\" to return (count of windows) > 0" || true)
37-
if [ "$HAS_WIN" = "true" ]; then
38-
break
39-
fi
40-
fi
41-
sleep 0.5
42-
done
35+
let appName = CommandLine.arguments[1]
36+
let appPid = Int(CommandLine.arguments[2]) ?? 0
4337
44-
if [ "$EXISTS" != "true" ] || [ "$HAS_WIN" != "true" ]; then
45-
echo "ERROR: process or window not found"
46-
osascript -e 'tell application "System Events" to get name of every process' | tr "," "\n" | head -50
47-
# Fallback: full screen capture
48-
screencapture -x "$OUTPUT_FILE"
49-
exit 0
38+
struct Candidate {
39+
let layer: Int
40+
let area: Int
41+
let id: Int
42+
let x: Int
43+
let y: Int
44+
let width: Int
45+
let height: Int
46+
}
47+
48+
func intValue(_ value: Any?) -> Int {
49+
if let number = value as? NSNumber {
50+
return number.intValue
51+
}
52+
if let int = value as? Int {
53+
return int
54+
}
55+
if let double = value as? Double {
56+
return Int(double)
57+
}
58+
return 0
59+
}
60+
61+
func doubleValue(_ value: Any?, default defaultValue: Double = 0) -> Double {
62+
if let number = value as? NSNumber {
63+
return number.doubleValue
64+
}
65+
if let double = value as? Double {
66+
return double
67+
}
68+
if let int = value as? Int {
69+
return Double(int)
70+
}
71+
return defaultValue
72+
}
73+
74+
for _ in 0..<30 {
75+
let info = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[String: Any]] ?? []
76+
var candidates: [Candidate] = []
77+
78+
for window in info {
79+
let ownerPid = intValue(window[kCGWindowOwnerPID as String])
80+
let ownerName = window[kCGWindowOwnerName as String] as? String ?? ""
81+
if ownerPid != appPid && ownerName != appName {
82+
continue
83+
}
84+
85+
let bounds = window[kCGWindowBounds as String] as? [String: Any] ?? [:]
86+
let width = intValue(bounds["Width"])
87+
let height = intValue(bounds["Height"])
88+
let windowID = intValue(window[kCGWindowNumber as String])
89+
let alpha = doubleValue(window[kCGWindowAlpha as String], default: 1)
90+
if width <= 1 || height <= 1 || windowID <= 0 || alpha <= 0 {
91+
continue
92+
}
93+
94+
candidates.append(Candidate(
95+
layer: intValue(window[kCGWindowLayer as String]),
96+
area: width * height,
97+
id: windowID,
98+
x: intValue(bounds["X"]),
99+
y: intValue(bounds["Y"]),
100+
width: width,
101+
height: height
102+
))
103+
}
104+
105+
if let best = candidates.sorted(by: {
106+
if $0.layer == $1.layer {
107+
return $0.area > $1.area
108+
}
109+
return $0.layer < $1.layer
110+
}).first {
111+
print("\(best.id),\(best.x),\(best.y),\(best.width),\(best.height)")
112+
exit(0)
113+
}
114+
115+
Thread.sleep(forTimeInterval: 0.5)
116+
}
117+
118+
print("ERROR: CoreGraphics window not found")
119+
exit(1)
120+
SWIFT
121+
122+
swiftc get_cg_window_info.swift -o get_cg_window_info
50123
fi
51124

125+
# Wait for application startup
126+
sleep 3
127+
52128
# Write AppleScript to a temporary file to avoid heredoc issues.
53129
# AXWindowNumber is the CoreGraphics window id used by screencapture -l, but
54130
# some GitHub-hosted macOS images do not expose it for every Accessibility
@@ -77,7 +153,32 @@ OSA
77153
sed -i '' "s/APP_NAME_PLACEHOLDER/$APP_NAME/g" get_window_info.applescript
78154

79155
# Get window id and rect as id,x,y,w,h
80-
WINDOW_INFO=$(osascript get_window_info.applescript)
156+
echo "Waiting for window to appear..."
157+
WINDOW_INFO=$(./get_cg_window_info "$APP_NAME" "$APP_PID" || true)
158+
if [[ "$WINDOW_INFO" == ERROR:* ]] || [ -z "$WINDOW_INFO" ]; then
159+
echo "$WINDOW_INFO"
160+
echo "CoreGraphics lookup failed; trying Accessibility window lookup."
161+
for i in {1..20}; do
162+
EXISTS=$(osascript -e "tell application \"System Events\" to return exists process \"$APP_NAME\"" || true)
163+
if [ "$EXISTS" = "true" ]; then
164+
HAS_WIN=$(osascript -e "tell application \"System Events\" to tell process \"$APP_NAME\" to return (count of windows) > 0" || true)
165+
if [ "$HAS_WIN" = "true" ]; then
166+
break
167+
fi
168+
fi
169+
sleep 0.5
170+
done
171+
172+
if [ "${EXISTS:-false}" != "true" ] || [ "${HAS_WIN:-false}" != "true" ]; then
173+
echo "ERROR: process or window not found"
174+
osascript -e 'tell application "System Events" to get name of every process' | tr "," "\n" | head -50
175+
# Fallback: full screen capture
176+
screencapture -x "$OUTPUT_FILE"
177+
exit 0
178+
fi
179+
180+
WINDOW_INFO=$(osascript get_window_info.applescript)
181+
fi
81182
case "$WINDOW_INFO" in
82183
ERROR:*) echo "$WINDOW_INFO"; screencapture -x "$OUTPUT_FILE"; exit 0 ;;
83184
esac

0 commit comments

Comments
 (0)