```rust
// 導入 macOS 底層事件循環的核心控制與通用模式常數
use core_foundation::runloop::{CFRunLoop, kCFRunLoopCommonModes};
// 導入處理 Quartz Window Server 滑鼠事件的所有核心組件
use core_graphics::event::{CGEvent, CGEventTap, CGEventTapLocation, CGEventTapOptions, CGEventTapPlacement, CGEventType, CGEventTapProxy};
// 處理所有滑鼠事件的回調函式
fn handler_print(_proxy: CGEventTapProxy, etype: CGEventType, ev: &CGEvent) -> Option<CGEvent> {
// 標記不安全代碼塊,因為直接讀取 C 語言層級的記憶體欄位不受 Rust 安全檢查保護
unsafe {
// 透過 transmute 強行將整數 4 轉為欄位類型,讀取 MouseEventDeltaX (水平位移增量)
let dx = ev.get_integer_value_field(std::mem::transmute(4));
// 透過 transmute 強行將整數 5 轉為欄位類型,讀取 MouseEventDeltaY (垂直位移增量)
let dy = ev.get_integer_value_field(std::mem::transmute(5));
// 透過 transmute 強行將整數 3 轉為欄位類型,讀取 MouseEventButtonNumber (按鈕編號)
let btn = ev.get_integer_value_field(std::mem::transmute(3));
// 將捕獲到的事件類型、位移數據與按鈕編號格式化輸出到終端機
println!("Type: {:?} | Delta: ({:3}, {:3}) | Btn: {}", etype, dx, dy, btn);
// 不安全區塊結束
}
// 回傳原始事件的副本給 OS,代表我們只是觀測而不攔截,確保滑鼠功能正常
Some(ev.clone())
}
// 程式進入點,負責初始化環境與啟動監聽循環
fn main() {
// 定義一個向量陣列,存放我們打算攔截的特定事件類型
let events = vec![
// 右鍵按下的事件 (用於偵測滾輪啟動時機)
CGEventType::RightMouseDown,
// 右鍵放開的事件 (用於恢復一般模式)
CGEventType::RightMouseUp,
// 鼠標移動的事件 (用於獲取紅球位移數據)
CGEventType::MouseMoved,
// 監聽按住右鍵時的移動事件,此時系統會將位移識別為拖拽
CGEventType::RightMouseDragged,
// 事件清單定義結束
];
// 調用系統 API 建立一個 Event Tap (事件鉤子)
let tap = CGEventTap::new(
// 設定監聽層級在 HID (人機介面裝置),這是最接近硬體、最原始的數據源
CGEventTapLocation::HID,
// 將我們的鉤子插入在處理鏈的最頂端,確保我們是第一個拿到數據的程序
CGEventTapPlacement::HeadInsertEventTap,
// 使用預設選項,允許我們觀測、攔截或修改分發中的事件
CGEventTapOptions::Default,
// 將定義好的監聽清單傳入,告訴 OS 我們只對這些特定訊號感興趣
events,
// 指定當事件觸發時,要跳轉執行哪一個處理函式
handler_print,
// 如果建立失敗則拋出錯誤訊息,這通常與 macOS 的輔助功能權限未開啟有關
).expect("錯誤:建立 EventTap 失敗。請檢查系統設定中的輔助功能權限。");
// 標記不安全代碼塊,因為操作 RunLoop 涉及直接與系統核心執行緒交互
unsafe {
// 透過 tap 的通訊埠建立一個可供事件循環監聽的來源 (Source)
let loop_source = tap.mach_port.create_runloop_source(0)
// 若建立來源失敗則終止,通常發生在系統資源耗盡或權限異常時
.expect("無法建立 RunLoop Source");
// 獲取當前執行緒的 RunLoop 並將建立的事件來源掛載到「通用模式」中
CFRunLoop::get_current().add_source(&loop_source, kCFRunLoopCommonModes);
// 在終端機提示使用者程式已進入監控就緒狀態
println!(">>> 探測啟動!請操作木星軌跡球,按下 Ctrl+C 可停止...");
// 啟動阻塞式的事件循環,程式將在此停住並持續監聽直到被手動終止
core_foundation::runloop::CFRunLoopRun();
// 不安全區塊結束
}
// main 函數結束
}
```