Mac OSX 鼠標(biāo)鍵盤事件的監(jiān)聽和模擬

本文轉(zhuǎn)自(原文太雜亂,這里調(diào)整了格式及內(nèi)容):
http://enkichen.com/2018/09/12/osx-mouse-keyboard-event/

最近完成了 Mac OSX 平臺下的遠程控制功能扫步,期間找了不少資料赚窃,這里做個總結(jié)劲装,主要涉及到一下知識點:

  1. OSX 的事件機制
  2. OSX/iOS 響應(yīng)鏈者鏈
  3. 鼠標(biāo)事件的監(jiān)聽及模擬(鼠標(biāo)單擊蠢琳、雙擊捻爷、拖動姻僧、滾動等事件)
  4. 鍵盤事件的監(jiān)聽及模擬(包括組合鍵的模擬)
  5. Keycode 鍵盤編碼(統(tǒng)一 Windows规丽、OSX、瀏覽器端鍵盤按鍵的編碼值)

事件分發(fā)機制

在 OSX 系統(tǒng)中鼠標(biāo)和鍵盤的活動事件都會產(chǎn)生底層的系統(tǒng)事件撇贺,首先傳遞到 IOKit 框架處理后存儲到隊列中赌莺,通知 Window Server 服務(wù)層處理。Window Server 存儲到 FIFO 優(yōu)先隊列中松嘶,然后逐一轉(zhuǎn)發(fā)到當(dāng)前活動窗口或者能響應(yīng)這個事件的應(yīng)用程序去處理艘狭。

在 OSX 或者 iOS 程序中,都會有一個 Main Run Loop 的線程翠订,RunLoop 循環(huán)中會遍歷 event 消息隊列巢音,逐一分發(fā)這些事件到應(yīng)用中合適的對象去處理。具體來說就是調(diào)用 NSAppsendEvent: 方法發(fā)送消息到NSWindow尽超,NSWindow 再分發(fā)到 NSView 視圖對象官撼,由其鼠標(biāo)或鍵盤事件響應(yīng)方法去處理。

事件響應(yīng)鏈

在 OSX 和 iOS 程序中響應(yīng)者鏈?zhǔn)?Application Kit 事件處理架構(gòu)的中心機制似谁,它由一系列鏈接在一起的響應(yīng)者對象組成傲绣,事件或者動作消息可以沿著這些對象進行傳遞掠哥。如果一個響應(yīng)者對象不能處理某個事件或動作,也就是說秃诵,它不響應(yīng)那個消息龙致,或者不認識那個事件,則將該消息重新發(fā)送給鏈中的下一個響應(yīng)者顷链。消息沿著響應(yīng)者鏈向上目代、向更高級別的對象傳遞,直到最終被處理(如果最終還是沒有被處理嗤练,就會被拋棄)榛了。

事件響應(yīng)者 Responders 類為核心應(yīng)用程序架構(gòu)的三個主要模式或機制定義了一個接口:

  • 它聲明了一些處理事件消息(也就是源自用戶事件的消息,比如象鼠標(biāo)點擊或按鍵按下這樣的事件)的方法煞抬。
  • 它聲明了數(shù)十個處理動作消息的方法霜大,它們和標(biāo)準(zhǔn)的鍵綁定(比如那些在文本內(nèi)部移動插入點的綁定)密切相關(guān)。動作消息會被派發(fā)到目標(biāo)對象革答;如果目標(biāo)沒有被指定战坤,應(yīng)用程序會負責(zé)檢索合適的響應(yīng)者。
  • 它定義了一套在應(yīng)用程序中指派和管理響應(yīng)者的方法残拐。這些響應(yīng)者組成了我們所知道的響應(yīng)者鏈途茫,即一系列響應(yīng)者,事件或動作消息在它們之間傳遞溪食,直到找到能夠?qū)λ鼈冞M行處理的對象囊卜。

當(dāng) Application Kit 在應(yīng)用程序中構(gòu)造對象時,會為每個窗口建立響應(yīng)者鏈错沃。響應(yīng)者鏈中的基本對象是NSWindow對象及其視圖層次栅组。在視圖層次中級別較低的視圖將比級別更高的視圖優(yōu)先獲得處理事件或動作消息的機會。NSWindow 中保有一個第一響應(yīng)者的引用枢析,它通常是當(dāng)前窗口中處于選擇狀態(tài)的視圖玉掸,窗口通常把響應(yīng)消息的機會首先給它。對于事件消息醒叁,響應(yīng)者鏈通常以發(fā)生事件的窗口對應(yīng)的 NSWindow 對象作為結(jié)束司浪,雖然其它對象也可以作為下一個響應(yīng)者被加入到 NSWindow 對象的后面。

從層級上看離觀察者最近的視圖優(yōu)先響應(yīng)事件辐益,通過 viewhitTest 方法檢測断傲,滿足 hitTest 方法的的子視圖優(yōu)先響應(yīng)事件。

NSApplication, NSWindow, NSDrawer, NSWindowController, NSView 以及繼承于 NSView 的所有控件對象都直接或間接繼承了 Responders 類智政,所以這些類都能處理鼠標(biāo)和鍵盤事件。

iOS 程序相比于 OSX 程序會有點不一樣:

  1. OSX 程序可能存在多個窗口箱蝠,會有多個響應(yīng)者鏈续捂,iPhone 的應(yīng)用程序就一個窗口垦垂,所以只會有一個響應(yīng)者鏈。
  2. 在 iOS 程序中與加速計牙瓢、陀螺儀和磁力計相關(guān)的運動事件不遵循響應(yīng)者鏈劫拗,Core Motion 會將這些事件直接傳遞給我們指定的對象。有關(guān)更多信息矾克,可以參看 Core Motion Framework页慷。

相關(guān)類的解析說明

NSResponder

NSResponder 在這里是非常重要的一個類,其中定義了鼠標(biāo)鍵盤觸控板等多種事件胁附,這里列舉一些鼠標(biāo)跟鍵盤的主要方法:

// 鼠標(biāo)按下事件
- (void)mouseDown:(NSEvent *)event;
// 鼠標(biāo)右鍵按下事件
- (void)rightMouseDown:(NSEvent *)event;
// 鼠標(biāo)抬起事件
- (void)mouseUp:(NSEvent *)event;
// 鼠標(biāo)右鍵抬起事件
- (void)rightMouseUp:(NSEvent *)event;
// 鼠標(biāo)移動事件
- (void)mouseMoved:(NSEvent *)event;
// 鼠標(biāo)拖拽事件
- (void)mouseDragged:(NSEvent *)event;
// 鼠標(biāo)滾動事件
- (void)scrollWheel:(NSEvent *)event;
// 鼠標(biāo)右鍵拖拽事件
- (void)rightMouseDragged:(NSEvent *)event;
// 鼠標(biāo)進入監(jiān)控區(qū)域事件
- (void)mouseEntered:(NSEvent *)event;
// 鼠標(biāo)離開監(jiān)控區(qū)域事件
- (void)mouseExited:(NSEvent *)event;
// 鍵盤按下事件
- (void)keyDown:(NSEvent *)event;
// 鍵盤按下事件
- (void)keyUp:(NSEvent *)event;
// 鍵盤控制鍵的按下標(biāo)記狀態(tài)發(fā)送改變酒繁,后面用該方法來獲取控制按下事件,參考 NSEventModifierFlags 定義
- (void)flagsChanged:(NSEvent *)event;

NSResponder 除了定義基本的響應(yīng)事件外控妻,還定義了很多其他事件方法州袒。具體請參考 NSResponder.h 的頭文件定義。

NSEvent

NSEvent 類描述了事件的具體信息弓候,這里列舉跟鼠標(biāo)和鍵盤相關(guān)的一些字段的介紹:

// 事件類型
@property (readonly) NSEventType type;
// 鍵盤控制鍵的按下狀態(tài)的標(biāo)記
@property (readonly) NSEventModifierFlags modifierFlags;
// 事件的時間戳
@property (readonly) NSTimeInterval timestamp;
// 鼠標(biāo)點擊的次數(shù)(只有鼠標(biāo)事件郎哭,才可使用)
@property (readonly) NSInteger clickCount;
@property (readonly) NSInteger buttonNumber; 
@property (readonly) NSInteger eventNumber;
// 壓力值
@property (readonly) float pressure;
// 鼠標(biāo)在窗口的位置
@property (readonly) NSPoint locationInWindow;
// 鼠標(biāo)滾動時。分別在 X 和 Y 軸上的偏移 
@property (readonly) CGFloat scrollingDeltaX NS_AVAILABLE_MAC(10_7);
@property (readonly) CGFloat scrollingDeltaY NS_AVAILABLE_MAC(10_7);
// 鍵盤事件的字符編碼和 key code 值
@property (nullable, readonly, copy) NSString *characters;
@property (readonly) unsigned short keyCode;

NSEventType

NSEventType 類型定義了事件的具體類型菇存,如下:

typedef NS_ENUM(NSUInteger, NSEventType) {        /* various types of events */
    NSEventTypeLeftMouseDown             = 1,
    NSEventTypeLeftMouseUp               = 2,
    NSEventTypeRightMouseDown            = 3,
    NSEventTypeRightMouseUp              = 4,
    NSEventTypeMouseMoved                = 5,
    NSEventTypeLeftMouseDragged          = 6,
    NSEventTypeRightMouseDragged         = 7,
    NSEventTypeMouseEntered              = 8,
    NSEventTypeMouseExited               = 9,
    NSEventTypeKeyDown                   = 10,
    NSEventTypeKeyUp                     = 11,
    NSEventTypeFlagsChanged              = 12,
    NSEventTypeAppKitDefined             = 13,
    NSEventTypeSystemDefined             = 14,
    NSEventTypeApplicationDefined        = 15,
    NSEventTypePeriodic                  = 16,
    NSEventTypeCursorUpdate              = 17,
    NSEventTypeScrollWheel               = 22,
    NSEventTypeTabletPoint               = 23,
    NSEventTypeTabletProximity           = 24,
    NSEventTypeOtherMouseDown            = 25,
    NSEventTypeOtherMouseUp              = 26,
    NSEventTypeOtherMouseDragged         = 27,
    /* The following event types are available on some hardware on 10.5.2 and later */
    NSEventTypeGesture NS_ENUM_AVAILABLE_MAC(10_5)       = 29,
    NSEventTypeMagnify NS_ENUM_AVAILABLE_MAC(10_5)       = 30,
    NSEventTypeSwipe   NS_ENUM_AVAILABLE_MAC(10_5)       = 31,
    NSEventTypeRotate  NS_ENUM_AVAILABLE_MAC(10_5)       = 18,
    NSEventTypeBeginGesture NS_ENUM_AVAILABLE_MAC(10_5)  = 19,
    NSEventTypeEndGesture NS_ENUM_AVAILABLE_MAC(10_5)    = 20,
    
#if __LP64__
    NSEventTypeSmartMagnify NS_ENUM_AVAILABLE_MAC(10_8) = 32,
#endif
    NSEventTypeQuickLook NS_ENUM_AVAILABLE_MAC(10_8) = 33,
    
#if __LP64__
    NSEventTypePressure NS_ENUM_AVAILABLE_MAC(10_10_3) = 34,
    NSEventTypeDirectTouch NS_ENUM_AVAILABLE_MAC(10_10) = 37,
#endif
};

NSEventModifierFlags

NSEventModifierFlags 類型描述了一些控制鍵夸研,是否處于按下狀態(tài),定義如下:

/* Device-independent bits found in event modifier flags */
typedef NS_OPTIONS(NSUInteger, NSEventModifierFlags) {
    NSEventModifierFlagCapsLock           = 1 << 16, // Set if Caps Lock key is pressed.
    NSEventModifierFlagShift              = 1 << 17, // Set if Shift key is pressed.
    NSEventModifierFlagControl            = 1 << 18, // Set if Control key is pressed.
    NSEventModifierFlagOption             = 1 << 19, // Set if Option or Alternate key is pressed.
    NSEventModifierFlagCommand            = 1 << 20, // Set if Command key is pressed.
    NSEventModifierFlagNumericPad         = 1 << 21, // Set if any key in the numeric keypad is pressed.
    NSEventModifierFlagHelp               = 1 << 22, // Set if the Help key is pressed.
    NSEventModifierFlagFunction           = 1 << 23, // Set if any function key is pressed.
    
    // Used to retrieve only the device-independent modifier flags, allowing applications to mask off the device-dependent modifier flags, including event coalescing information.
    NSEventModifierFlagDeviceIndependentFlagsMask    = 0xffff0000UL
};

事件的監(jiān)聽方法

鼠標(biāo)鍵盤事件的監(jiān)聽有多種方法依鸥,第一種方法是重寫事件響應(yīng)者 Responders 對應(yīng)的方法來獲取對應(yīng)的事件陈惰;第二是通過重寫 NSWindowsendEvent: 方法; 第三是通過的 NSEvent 提供靜態(tài)方法來監(jiān)聽對應(yīng)的事件:

+ (nullable id)addGlobalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(void (^)(NSEvent*))block`
+ (nullable id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(NSEvent* __nullable (^)(NSEvent*))block
+ (void)removeMonitor:(id)eventMonitor

NSEvent 提供的靜態(tài)方法可以用監(jiān)聽整個系統(tǒng)的事件或者當(dāng)前應(yīng)用程序內(nèi)的事件毕籽。全局事件是異步過程因此無法修改事件抬闯,應(yīng)用程序內(nèi)的消息可以在捕獲到消息后,修改事件然后繼續(xù)交由響應(yīng)者鏈中下一個響應(yīng)者處理关筒。

鼠標(biāo)事件監(jiān)聽

這里介紹鼠標(biāo)的一下事件處理方法和注意事項:

  1. 左/右鍵的按下抬起事件
  2. 左鍵的雙擊(或者多擊事件)
  3. 左鍵或者右鍵的拖拽事件
  4. 鼠標(biāo)移動事件
  5. 鼠標(biāo)的滾動事件

前面介紹了三種監(jiān)聽事件的方法溶握,這里使用重寫 Responders 的方法來監(jiān)聽鼠標(biāo)事件:

- (void)mouseDown:(NSEvent *)event;
- (void)rightMouseDown:(NSEvent *)event;
- (void)mouseUp:(NSEvent *)event;
- (void)rightMouseUp:(NSEvent *)event;
- (void)mouseMoved:(NSEvent *)event;
- (void)mouseDragged:(NSEvent *)event;
- (void)rightMouseDragged:(NSEvent *)event;
- (void)scrollWheel:(NSEvent *)event;

鼠標(biāo)按鍵的按下抬起事件,只要判斷一下 NSEventtype 屬性即可知道蒸播。

當(dāng)前鼠標(biāo)的位置信息可通過 locationInWindow 屬性來獲取睡榆,該坐標(biāo)是當(dāng)前 Window 窗口的坐標(biāo),其中包含了 Window 窗口標(biāo)題欄的高度袍榆,所以如果要想獲取當(dāng)前鼠標(biāo)在當(dāng)前 NSView 中的位置胀屿,需要做一次坐標(biāo)轉(zhuǎn)換,可以調(diào)用 NSViewconvertPoint: 方法來轉(zhuǎn)換坐標(biāo)包雀。

鼠標(biāo)左鍵的 按下 - 抬起 兩個連續(xù)的動作被定義為單擊事件宿崭,clickCount 屬于用于描述當(dāng)前點擊的次數(shù)。在模擬鼠標(biāo)雙擊時才写,需要用到該字段值葡兑,而不能用連續(xù)兩次點擊事件來模擬雙擊奖蔓。

監(jiān)聽鼠標(biāo)的移動事件時需要設(shè)置一個跟蹤區(qū)域,只有在跟蹤區(qū)域內(nèi)的鼠標(biāo)移動事件才會觸發(fā)讹堤∵汉祝可以通過 NSView- (void)addTrackingArea:(NSTrackingArea *)trackingArea 方法來設(shè)置跟蹤區(qū)域。同時需要重寫 - (void)updateTrackingAreas 方法洲守,當(dāng)跟蹤區(qū)域發(fā)送改變時疑务,需要手動將之前的跟蹤區(qū)域移除,再添加新的跟蹤區(qū)域梗醇。

鼠標(biāo)的拖拽事件是指用戶按下鼠標(biāo)左鍵或右鍵移動鼠標(biāo)知允,當(dāng)拖拽事件發(fā)生時 mouseMoved: 事件將不會觸發(fā)。

鼠標(biāo)的滾動可以通過 deltaXdeltaY 兩個屬性來獲取分別在水平方向和垂直方向的滾動偏移婴削。

OSX 的坐標(biāo)系統(tǒng)以左下角為 (0,0) 右上角為 (x_max, y_max)

鍵盤事件的監(jiān)聽

鍵盤事件的監(jiān)聽也使用重寫事件響應(yīng)者 Responders 對應(yīng)的方法來實現(xiàn)廊镜,需要重寫的方法如下:

- (void)keyDown:(NSEvent *)event;
- (void)keyUp:(NSEvent *)event;
- (void)flagsChanged:(NSEvent *)event;

鍵盤事件重寫上述方法外還需要重寫以下方法:

- (BOOL)acceptsFirstResponder;

該方法用來說明是否成為響應(yīng)者鏈的第一個響應(yīng)者,這里需要返回 YES 表示成為第一響應(yīng)者唉俗,否則無法監(jiān)聽鍵盤消息嗤朴。

NSEventcharacters 描述了當(dāng)前鍵盤按鍵的字符,keyCode 描述了按鍵的值虫溜,每個按鍵的 keyCode 值定義可以在 Carbon/HIToolbox/Events.h 文件中找到對應(yīng)的按鍵的宏定義雹姊。

keyDown:keyUp: 方法中可以監(jiān)聽到大部分的按鍵的消息,但一些控制鍵需要通過 flagsChanged: 方法來處理衡楞,當(dāng) NSEventModifierFlags 定義的按鍵狀態(tài)發(fā)送改變時吱雏,該方法就會被觸發(fā)。這里需要注意的是大小寫鎖定鍵 NSEventModifierFlagCapsLock 只有當(dāng)大寫鎖定或者取消鎖定時瘾境,該方法才會被調(diào)用歧杏,并不會因為 CapsLock 按鍵按下或者抬起時觸發(fā)迷守。

keyCode 值在 Windows 和瀏覽器上都有對應(yīng)的鍵盤按鍵的值的定義犬绒,當(dāng)需要與其他平臺進行通信時兑凿,例如遠程控制時凯力,可以將 Mac 下的 keyCode 值轉(zhuǎn)換成瀏覽器 JS 上的對應(yīng)值定義礼华,因為瀏覽器和 Windows 平臺的定義是一致的咐鹤。

CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, kVK_CapsLock) 方法可以用來獲取按鍵是否處于按下狀態(tài)。

鼠標(biāo)鍵盤事件的模擬

OSX 下的鼠標(biāo)和鍵盤事件模擬需要用到 CoreGraphicsCarbon 框架圣絮,在 CoreGraphics 框架中定義了一些用于創(chuàng)建底層事件的方法祈惶,Carbon 框架定義了一些跟鍵盤相關(guān)的宏和方法。

在模擬鼠標(biāo)或者鍵盤事件時,都需要使用 CGEventSourceCreate(CGEventSourceStateID stateID) 方法來創(chuàng)建事件源奸腺,事件源類型定義了 3 個類型餐禁,如下:

typedef CF_ENUM(int32_t, CGEventSourceStateID) {
  kCGEventSourceStatePrivate = -1,
  kCGEventSourceStateCombinedSessionState = 0,
  kCGEventSourceStateHIDSystemState = 1
};
  • kCGEventSourceStatePrivate:代表專門的應(yīng)用血久,如遠程控制程序可以生成和跟蹤事件源狀態(tài)獨立于其他進程帮非。
  • kCGEventSourceStateCombinedSessionState:該狀態(tài)表反映了所有事件源的組合狀態(tài)發(fā)布到當(dāng)前用戶的登錄會話氧吐。如果您的程序發(fā)布的事件在一個登錄會話,您應(yīng)該使用這個源狀態(tài)當(dāng)你創(chuàng)建一個事件源末盔。
  • kCGEventSourceStateHIDSystemState:該狀態(tài)表反映了組合硬件輸入源從 HID 系統(tǒng)硬件層面發(fā)送的事件源筑舅。生成的事件。 就是外接鍵盤或者 macbook 本機鍵盤以及一些系統(tǒng)定義的按鍵點擊事件陨舱。

這里自己封裝了鼠標(biāo)事件翠拣、鼠標(biāo)滾動事件及鍵盤事件的方法游盲,需要引入 <Carbon/Carbon.h><AppKit/AppKit.h> 頭文件

1. 模擬鼠標(biāo)事件:

void PostMouseEvent(CGMouseButton button, CGEventType type, const CGPoint &point, int64_t clickCount)
{
    CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
    CGEventRef theEvent = CGEventCreateMouseEvent(source, type, point, button);
    CGEventSetIntegerValueField(theEvent, kCGMouseEventClickState, clickCount);
    CGEventSetType(theEvent, type);
    CGEventPost(kCGHIDEventTap, theEvent);
    CFRelease(theEvent);
    CFRelease(source);
}

左鍵單擊模擬:

PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDown, CGPointZero, 1);
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseUp, CGPointZero, 1);

左鍵雙擊模擬:

PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDown, CGPointZero, 1);
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseUp, CGPointZero, 1);
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDown, CGPointZero, 2);
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseUp, CGPointZero, 2);

拖拽事件: 如果是拖拽事件,例如左鍵拖拽事件益缎,則需要先發(fā)送左鍵的 kCGEventLeftMouseDown 事件谜慌,然后連續(xù)發(fā)送 kCGEventLeftMouseDragged 事件莺奔,再發(fā)送 kCGEventLeftMouseUp 事件欣范,代碼如下:

PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDown, CGPointZero, 1);
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDragged, CGPointZero, 1);
...
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDragged, CGPointZero, 1);
PostMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseUp, CGPointZero, 1);

模擬其他鼠標(biāo)事件令哟,將枚舉值修改一下即可恼琼。

2. 模擬鼠標(biāo)滾動事件

void PostScrollWheelEvent(int32_t scrollingDeltaX, int32_t scrollingDeltaY)
{
    CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
    CGEventRef theEvent = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitPixel, 2, scrollingDeltaY, scrollingDeltaX);
    CGEventPost(kCGHIDEventTap, theEvent);
    CFRelease(theEvent);
    CFRelease(source);
}

鼠標(biāo)滾輪事件只要傳入水平和垂直方向的偏移即可實現(xiàn)屏富。

3. 模擬鍵盤事件

void PostKeyboardEvent(CGKeyCode virtualKey, bool keyDown, CGEventFlags flags)
{
    CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
    CGEventRef push = CGEventCreateKeyboardEvent(source, virtualKey, keyDown);
    CGEventSetFlags(push, flags);
    CGEventPost(kCGHIDEventTap, push);
    CFRelease(push);
    CFRelease(source);
}

鍵盤事件的模擬需要注意的就是 CGEventFlags flags 參數(shù)晴竞,該參數(shù)用來模擬組合鍵的實現(xiàn),類型定義如下:

typedef CF_OPTIONS(uint64_t, CGEventFlags) { /* Flags for events */
  /* Device-independent modifier key bits. */
  kCGEventFlagMaskAlphaShift =          NX_ALPHASHIFTMASK,
  kCGEventFlagMaskShift =               NX_SHIFTMASK,
  kCGEventFlagMaskControl =             NX_CONTROLMASK,
  kCGEventFlagMaskAlternate =           NX_ALTERNATEMASK,
  kCGEventFlagMaskCommand =             NX_COMMANDMASK,
  /* Special key identifiers. */
  kCGEventFlagMaskHelp =                NX_HELPMASK,
  kCGEventFlagMaskSecondaryFn =         NX_SECONDARYFNMASK,
  /* Identifies key events from numeric keypad area on extended keyboards. */
  kCGEventFlagMaskNumericPad =          NX_NUMERICPADMASK,
  /* Indicates if mouse/pen movement events are not being coalesced */
  kCGEventFlagMaskNonCoalesced =        NX_NONCOALSESCEDMASK
};

解析如下:

  • kCGEventFlagMaskAlphaShift:大小寫鎖定鍵是否處于開啟狀態(tài)
  • kCGEventFlagMaskShift:Shift 鍵是否按下
  • kCGEventFlagMaskControl:Control 鍵是否按下
  • kCGEventFlagMaskAlternate:Alt 鍵是否按下颓鲜,對應(yīng) Mac 鍵盤的 option 鍵
  • kCGEventFlagMaskCommand:Command 鍵是否按下,對應(yīng) Windows 的 WIN 鍵
  • kCGEventFlagMaskHelp:Help 鍵
  • kCGEventFlagMaskSecondaryFn:Fn 鍵
  • kCGEventFlagMaskNumericPad:數(shù)字鍵盤
  • kCGEventFlagMaskNonCoalesced:沒有任何鍵按下

如果有多個控制鍵同時按下甜滨,則使用位運算的或 | 加上對應(yīng)的鍵值即可。例如模擬 Command + Control + S:

PostKeyboardEvent(kVK_ANSI_S, true, kCGEventFlagMaskCommand | kCGEventFlagMaskControl)
PostKeyboardEvent(kVK_ANSI_S, false, kCGEventFlagMaskNonCoalesced)

大小寫鎖定鍵瘤袖,無法通過 kVK_CapsLock 按鍵的按下和抬起事件來模擬大小鍵的鎖定,同時按鍵上的 LED 燈也是不會有變化的。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末艾扮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泡嘴,更是在濱河造成了極大的恐慌甫恩,老刑警劉巖酌予,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磺箕,死亡現(xiàn)場離奇詭異抛虫,居然都是意外死亡松靡,警方通過查閱死者的電腦和手機建椰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門雕欺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棉姐,“玉大人屠列,你說我怎么就攤上這事谅海×嘲В” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵撞蜂,是天一觀的道長。 經(jīng)常有香客問我侥袜,道長,這世上最難降的妖魔是什么枫吧? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任浦旱,我火速辦了婚禮九杂,結(jié)果婚禮上颁湖,老公的妹妹穿的比我還像新娘例隆。我一直安慰自己甥捺,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布镰禾。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屋休。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天备韧,我揣著相機與錄音劫樟,去河邊找鬼盯蝴。 笑死毅哗,一個胖子當(dāng)著我的面吹牛捧挺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尿瞭,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼声搁!你這毒婦竟也來了黑竞?” 一聲冷哼從身側(cè)響起疏旨,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤很魂,失蹤者是張志新(化名)和其女友劉穎檐涝,沒想到半個月后遏匆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡谁榜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年幅聘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窃植。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡帝蒿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巷怜,到底是詐尸還是另有隱情葛超,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布延塑,位于F島的核電站绣张,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏页畦。R本人自食惡果不足惜胖替,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望独令。 院中可真熱鬧端朵,春花似錦燃箭、人聲如沸冲呢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裙戏。三九已至乘凸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間营勤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工壹罚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猖凛。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像辨泳,于是被迫代替她去往敵國和親虱岂。 傳聞我的和親對象是個殘疾皇子漠吻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容