那天下午我在家裡那間錄音間,桌上擺著一台 Elgato Prompter——前一陣子買的題詞機,玻璃面 45 度斜架在鏡頭前面,講稿從 MacBook 投到那片玻璃上,讓我讀字的時候眼睛直視鏡頭。錄到一半我把第二集的稿子拖到延伸螢幕,按下播放——玻璃上的字全是反的。「迎歡看收」這種讀法,根本無法錄。重來、再重來,Elgato 官方軟體裡那顆「水平鏡像」按鈕勾下去沒反應、macOS 系統設定 ➜ 顯示器找不到鏡像翻轉的選項。錄影排程那天卡了一個多小時,最後我把稿子印出來貼在鏡頭旁邊讀,當天那集影片我自己看就知道眼神飄。
隔天我用 Claude Code 寫了 ElgatoMirror。
為什麼題詞機的字必須是反的
題詞機正前方有一片半透明玻璃斜 45 度架在鏡頭前面,鏡頭從玻璃後面拍、我從玻璃前面看講稿。講稿不是直接印上去,是底下螢幕反射上來的——所以螢幕上的字必須先左右翻轉,反射後才會正。原理跟救護車車頭那個反寫的「AMBULANCE」一樣。
問題出在我不用 Elgato 自家那支 prompter app(太陽春、字級配色不能自訂),我用的是把 PDF 開在 macOS 內建「預覽程式」、視窗拖到題詞機這個延伸螢幕。這條路徑下沒人幫我翻轉——延伸螢幕對 macOS 就是普通顯示器,內容原封不動送過去。Elgato Camera Hub 那個「翻轉」管的是攝影機畫面,不是顯示器輸出;系統設定 ➜ 顯示器,從以前到現在沒有「水平鏡像」這個 toggle。翻 Reddit 跟 Elgato 論壇一堆人問同樣的問題,答案五花八門但沒有一個是「macOS 內建可以」。
它做了什麼
ElgatoMirror 是一支 macOS 選單列工具,開起來在右上角放一顆小圖示,點開來選兩件事——要鏡像哪一個螢幕(來源)、把鏡像結果送到哪一個螢幕(目標,通常是題詞機)。按「啟用鏡像」或快速鍵 ⌘M,題詞機那一面玻璃馬上看到正向文字。
scaleX(-1) transform、貼到目標螢幕的全螢幕視窗,延遲幾乎察覺不到。
那個救命快速鍵的故事
⌘⌥M 緊急停用是 v1.0 沒有、後來才加上去的。某次測試我把 MacBook 內建螢幕同時設成來源和目標——把自己的畫面翻轉之後蓋回自己身上。結果整個畫面變成一片翻轉的視窗、選單列被自己遮住、滑鼠點不到任何地方。那五分鐘只能用 Mission Control 切去別的桌面慢慢救。修完之後加了規則:無論鏡像視窗多大,按 ⌘⌥M 一定強制關掉。這條走 NSEvent.addGlobalMonitorForEvents,比視窗事件優先級高,能穿透自己造成的遮蔽。
# 自己編譯(需要 Apple Developer 帳號 + Apple Development 憑證)
git clone https://github.com/AndyJuang/ElgatoMirror.git
cd ElgatoMirror
# 修改 build.sh 裡的憑證名稱為你自己的
./build.sh
open ElgatoMirror.app
# 權限怪怪的?清掉 TCC 快取重來
tccutil reset All com.zhuangzheyun.ElgatoMirror
背後的技術:ScreenCaptureKit + Metal + 一個全螢幕視窗
這支 app 看起來像在「翻轉顯示器訊號」,實際上做的事拆開來其實單純:
- 抓畫面:用 macOS 12.3 開始提供的
ScreenCaptureKit,訂閱來源螢幕的即時 frame stream。這比舊的CGDisplayStream高效,也是 Apple 現在唯一推薦的 API。 - 翻轉:拿到
CMSampleBuffer之後丟給一層 Metal shader,做scaleX = -1的水平翻轉。這比 Core Image 路徑省一輪 CPU↔GPU 來回。 - 顯示:在目標螢幕開一個沒有 title bar、覆蓋整個 display 的
NSWindow,level = .screenSaver確保壓在所有東西上面,把翻轉後的 frame 即時繪製上去。 - 權限:第一次跑會跳系統授權窗,要在系統設定 ➜ 隱私權與安全性 ➜ 螢幕與系統錄音勾起來。這道牆 Apple 從 macOS 10.15 開始就守得很緊,跳不過。
整個專案 build.sh 跑 swiftc 直接組出 .app bundle,沒用 Xcode 專案檔。簽碼必須用 Apple Development 憑證(免費 Apple ID 也有)、不能用 ad-hoc——不然 macOS 每次重新編譯都會把「螢幕錄影」權限重新打掉,每次都得進系統設定再勾一次。這個雷我踩了三次才搞清楚。
用 Claude Code 寫的工作流
我不是 Swift 工程師。寫這支 app 的方式跟我寫 iPhoneMirror、WiFiScope、MacPrism 一樣:
- 描述痛點:「我要做一個 macOS 選單列工具,能把任一個螢幕的畫面水平翻轉後即時顯示到另一個螢幕上,給 Elgato Prompter 題詞機用。」
- Claude Code 提架構:建議用 ScreenCaptureKit 抓畫面、Metal 做翻轉、全螢幕 NSWindow 顯示——順手把舊版 CGDisplayStream 為什麼不該用講了一遍。
- 第一版跑起來,我報告問題:「卡頓有點明顯」「拔掉螢幕沒自動關」「我把自己鏡到自己畫面整個沒救」。
- 它迭代:卡頓改 Metal 路徑;拔螢幕監聽
NSScreen.didChangeNotification;自己鏡自己這件事加⌘⌥M救命鍵 + UI 偵測同螢幕跳 warning。 - v1.0.2 的修正:TCC 權限快取偶爾抽風,這版專門加了 README 一段「跑
tccutil reset All com.zhuangzheyun.ElgatoMirror」的疑難排解。
「I think a lot of people are scared of the idea of letting Claude write code for them. The thing I've found is that if you can describe what you want clearly enough, Claude can do it.」
(很多人不敢讓 Claude 幫你寫程式。但我發現只要你能把要的東西講清楚,它就能做出來。)—— Boris Cherny, Anthropic(Claude Code 共同作者,語出 Latent Space podcast 訪談)
我做的事是:定義要解的痛、看跑起來對不對、報告 bug、加新需求。ScreenCaptureKit 的 frame buffer 格式、Metal pipeline state 怎麼設、NSScreen notification 細節——這些不是我會的,但也不需要我會。
非工程師合集第五件
盤點到目前為止,我用 Claude Code 寫出來、公開放在 GitHub 上的 vibe coding 作品:
- MacPrism——menu bar 系統監控工具,連 AI 額度都看得到
- WiFiScope——WiFi 頻譜分析工具,仿 $19 商業軟體 WiFi Explorer
- iPhoneMirror——上課 demo 手機畫面用的免費投影工具
- MkDown——每天寫 Markdown 寫到崩潰,週末寫一個
- 那個數位官網——你正在看的這個,Web + Serverless + Redis
加上 ElgatoMirror 是第六件。共同點是每一個都來自我自己的具體痛——監控不到 AI 額度、上課 WiFi 卡、QuickTime 黑邊、Markdown 編輯器難用、官網沒人寫,現在多加一條:題詞機鏡像不會動。我從來沒先想「我要學 Swift」,是先想「這件事為什麼這麼煩」、再想「能不能自己做」。對也想試的人,我每篇都重講一次的三件事:從你自己的痛開始、不要怕陌生的技術棧(會不會 Swift 跟能不能寫出 Swift app 是兩回事)、寫完就開源寫個 README,十個人試用回兩個 issue,這支工具會變得更好。
結語
那一片 45 度斜架在鏡頭前面的玻璃,原本是個會卡住整天錄影排程的東西。現在它對我來說,是選單列圖示點兩下、按 ⌘M、開始錄。題詞機上的字正向、鏡頭裡的我直視觀眾、稿子在玻璃跟瞳孔之間。一行 Swift 我也不會寫的時候,這個問題沒救;願意把痛點描述清楚之後,這個問題不再是問題。
需要的話拿去用。覺得有用幫我按個 star、覺得卡哪裡開個 issue 跟我說。