本文轉(zhuǎn)自(原文太雜亂,這里調(diào)整了格式及內(nèi)容):
http://enkichen.com/2018/09/12/osx-mouse-keyboard-event/
最近完成了 Mac OSX 平臺下的遠(yuǎn)程控制功能,期間找了不少資料,這里做個(gè)總結(jié),主要涉及到一下知識點(diǎn):
- OSX 的事件機(jī)制
- OSX/iOS 響應(yīng)鏈者鏈
- 鼠標(biāo)事件的監(jiān)聽及模擬(鼠標(biāo)單擊、雙擊、拖動、滾動等事件)
- 鍵盤事件的監(jiān)聽及模擬(包括組合鍵的模擬)
- Keycode 鍵盤編碼(統(tǒng)一 Windows、OSX、瀏覽器端鍵盤按鍵的編碼值)
事件分發(fā)機(jī)制
在 OSX 系統(tǒng)中鼠標(biāo)和鍵盤的活動事件都會產(chǎn)生底層的系統(tǒng)事件,首先傳遞到 IOKit 框架處理后存儲到隊(duì)列中,通知 Window Server 服務(wù)層處理。Window Server 存儲到 FIFO 優(yōu)先隊(duì)列中,然后逐一轉(zhuǎn)發(fā)到當(dāng)前活動窗口或者能響應(yīng)這個(gè)事件的應(yīng)用程序去處理。
在 OSX 或者 iOS 程序中,都會有一個(gè) Main Run Loop 的線程,RunLoop 循環(huán)中會遍歷 event 消息隊(duì)列,逐一分發(fā)這些事件到應(yīng)用中合適的對象去處理。具體來說就是調(diào)用 NSApp 的 sendEvent: 方法發(fā)送消息到NSWindow,NSWindow 再分發(fā)到 NSView 視圖對象,由其鼠標(biāo)或鍵盤事件響應(yīng)方法去處理。
事件響應(yīng)鏈
在 OSX 和 iOS 程序中響應(yīng)者鏈?zhǔn)?Application Kit 事件處理架構(gòu)的中心機(jī)制,它由一系列鏈接在一起的響應(yīng)者對象組成,事件或者動作消息可以沿著這些對象進(jìn)行傳遞。如果一個(gè)響應(yīng)者對象不能處理某個(gè)事件或動作,也就是說,它不響應(yīng)那個(gè)消息,或者不認(rèn)識那個(gè)事件,則將該消息重新發(fā)送給鏈中的下一個(gè)響應(yīng)者。消息沿著響應(yīng)者鏈向上、向更高級別的對象傳遞,直到最終被處理(如果最終還是沒有被處理,就會被拋棄)。
事件響應(yīng)者 Responders 類為核心應(yīng)用程序架構(gòu)的三個(gè)主要模式或機(jī)制定義了一個(gè)接口:
- 它聲明了一些處理事件消息(也就是源自用戶事件的消息,比如象鼠標(biāo)點(diǎn)擊或按鍵按下這樣的事件)的方法。
- 它聲明了數(shù)十個(gè)處理動作消息的方法,它們和標(biāo)準(zhǔn)的鍵綁定(比如那些在文本內(nèi)部移動插入點(diǎn)的綁定)密切相關(guān)。動作消息會被派發(fā)到目標(biāo)對象;如果目標(biāo)沒有被指定,應(yīng)用程序會負(fù)責(zé)檢索合適的響應(yīng)者。
- 它定義了一套在應(yīng)用程序中指派和管理響應(yīng)者的方法。這些響應(yīng)者組成了我們所知道的響應(yīng)者鏈,即一系列響應(yīng)者,事件或動作消息在它們之間傳遞,直到找到能夠?qū)λ鼈冞M(jìn)行處理的對象。
當(dāng) Application Kit 在應(yīng)用程序中構(gòu)造對象時(shí),會為每個(gè)窗口建立響應(yīng)者鏈。響應(yīng)者鏈中的基本對象是NSWindow對象及其視圖層次。在視圖層次中級別較低的視圖將比級別更高的視圖優(yōu)先獲得處理事件或動作消息的機(jī)會。NSWindow 中保有一個(gè)第一響應(yīng)者的引用,它通常是當(dāng)前窗口中處于選擇狀態(tài)的視圖,窗口通常把響應(yīng)消息的機(jī)會首先給它。對于事件消息,響應(yīng)者鏈通常以發(fā)生事件的窗口對應(yīng)的 NSWindow 對象作為結(jié)束,雖然其它對象也可以作為下一個(gè)響應(yīng)者被加入到 NSWindow 對象的后面。
從層級上看離觀察者最近的視圖優(yōu)先響應(yīng)事件,通過 view 的 hitTest 方法檢測,滿足 hitTest 方法的的子視圖優(yōu)先響應(yīng)事件。
NSApplication, NSWindow, NSDrawer, NSWindowController, NSView 以及繼承于 NSView 的所有控件對象都直接或間接繼承了 Responders 類,所以這些類都能處理鼠標(biāo)和鍵盤事件。
iOS 程序相比于 OSX 程序會有點(diǎn)不一樣:
- OSX 程序可能存在多個(gè)窗口,會有多個(gè)響應(yīng)者鏈,iPhone 的應(yīng)用程序就一個(gè)窗口,所以只會有一個(gè)響應(yīng)者鏈。
- 在 iOS 程序中與加速計(jì)、陀螺儀和磁力計(jì)相關(guān)的運(yùn)動事件不遵循響應(yīng)者鏈,Core Motion 會將這些事件直接傳遞給我們指定的對象。有關(guān)更多信息,可以參看 Core Motion Framework。
相關(guān)類的解析說明
NSResponder
NSResponder 在這里是非常重要的一個(gè)類,其中定義了鼠標(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)進(jìn)入監(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;
// 事件的時(shí)間戳
@property (readonly) NSTimeInterval timestamp;
// 鼠標(biāo)點(diǎn)擊的次數(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)滾動時(shí)。分別在 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)的事件;第二是通過重寫 NSWindow 的 sendEvent: 方法; 第三是通過的 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)聽整個(gè)系統(tǒng)的事件或者當(dāng)前應(yīng)用程序內(nèi)的事件。全局事件是異步過程因此無法修改事件,應(yīng)用程序內(nèi)的消息可以在捕獲到消息后,修改事件然后繼續(xù)交由響應(yīng)者鏈中下一個(gè)響應(yīng)者處理。
鼠標(biāo)事件監(jiān)聽
這里介紹鼠標(biāo)的一下事件處理方法和注意事項(xiàng):
- 左/右鍵的按下抬起事件
- 左鍵的雙擊(或者多擊事件)
- 左鍵或者右鍵的拖拽事件
- 鼠標(biāo)移動事件
- 鼠標(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)按鍵的按下抬起事件,只要判斷一下 NSEvent 的 type 屬性即可知道。
當(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)用 NSView 的 convertPoint: 方法來轉(zhuǎn)換坐標(biāo)。
鼠標(biāo)左鍵的 按下 - 抬起 兩個(gè)連續(xù)的動作被定義為單擊事件,clickCount 屬于用于描述當(dāng)前點(diǎn)擊的次數(shù)。在模擬鼠標(biāo)雙擊時(shí),需要用到該字段值,而不能用連續(xù)兩次點(diǎn)擊事件來模擬雙擊。
監(jiān)聽鼠標(biāo)的移動事件時(shí)需要設(shè)置一個(gè)跟蹤區(qū)域,只有在跟蹤區(qū)域內(nèi)的鼠標(biāo)移動事件才會觸發(fā)。可以通過 NSView的 - (void)addTrackingArea:(NSTrackingArea *)trackingArea 方法來設(shè)置跟蹤區(qū)域。同時(shí)需要重寫 - (void)updateTrackingAreas 方法,當(dāng)跟蹤區(qū)域發(fā)送改變時(shí),需要手動將之前的跟蹤區(qū)域移除,再添加新的跟蹤區(qū)域。
鼠標(biāo)的拖拽事件是指用戶按下鼠標(biāo)左鍵或右鍵移動鼠標(biāo),當(dāng)拖拽事件發(fā)生時(shí) mouseMoved: 事件將不會觸發(fā)。
鼠標(biāo)的滾動可以通過 deltaX 和 deltaY 兩個(gè)屬性來獲取分別在水平方向和垂直方向的滾動偏移。
OSX 的坐標(biāo)系統(tǒng)以左下角為 (0,0) 右上角為 (x_max, y_max)
鍵盤事件的監(jiān)聽
鍵盤事件的監(jiān)聽也使用重寫事件響應(yīng)者 Responders 對應(yīng)的方法來實(shí)現(xiàn),需要重寫的方法如下:
- (void)keyDown:(NSEvent *)event;
- (void)keyUp:(NSEvent *)event;
- (void)flagsChanged:(NSEvent *)event;
鍵盤事件重寫上述方法外還需要重寫以下方法:
- (BOOL)acceptsFirstResponder;
該方法用來說明是否成為響應(yīng)者鏈的第一個(gè)響應(yīng)者,這里需要返回 YES 表示成為第一響應(yīng)者,否則無法監(jiān)聽鍵盤消息。
NSEvent 的 characters 描述了當(dāng)前鍵盤按鍵的字符,keyCode 描述了按鍵的值,每個(gè)按鍵的 keyCode 值定義可以在 Carbon/HIToolbox/Events.h 文件中找到對應(yīng)的按鍵的宏定義。
在 keyDown: 和 keyUp: 方法中可以監(jiān)聽到大部分的按鍵的消息,但一些控制鍵需要通過 flagsChanged: 方法來處理,當(dāng) NSEventModifierFlags 定義的按鍵狀態(tài)發(fā)送改變時(shí),該方法就會被觸發(fā)。這里需要注意的是大小寫鎖定鍵 NSEventModifierFlagCapsLock 只有當(dāng)大寫鎖定或者取消鎖定時(shí),該方法才會被調(diào)用,并不會因?yàn)?CapsLock 按鍵按下或者抬起時(shí)觸發(fā)。
keyCode 值在 Windows 和瀏覽器上都有對應(yīng)的鍵盤按鍵的值的定義,當(dāng)需要與其他平臺進(jìn)行通信時(shí),例如遠(yuǎn)程控制時(shí),可以將 Mac 下的 keyCode 值轉(zhuǎn)換成瀏覽器 JS 上的對應(yīng)值定義,因?yàn)闉g覽器和 Windows 平臺的定義是一致的。
CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, kVK_CapsLock)方法可以用來獲取按鍵是否處于按下狀態(tài)。
鼠標(biāo)鍵盤事件的模擬
OSX 下的鼠標(biāo)和鍵盤事件模擬需要用到 CoreGraphics 及 Carbon 框架,在 CoreGraphics 框架中定義了一些用于創(chuàng)建底層事件的方法,Carbon 框架定義了一些跟鍵盤相關(guān)的宏和方法。
在模擬鼠標(biāo)或者鍵盤事件時(shí),都需要使用 CGEventSourceCreate(CGEventSourceStateID stateID) 方法來創(chuàng)建事件源,事件源類型定義了 3 個(gè)類型,如下:
typedef CF_ENUM(int32_t, CGEventSourceStateID) {
kCGEventSourceStatePrivate = -1,
kCGEventSourceStateCombinedSessionState = 0,
kCGEventSourceStateHIDSystemState = 1
};
-
kCGEventSourceStatePrivate:代表專門的應(yīng)用,如遠(yuǎn)程控制程序可以生成和跟蹤事件源狀態(tài)獨(dú)立于其他進(jìn)程。 -
kCGEventSourceStateCombinedSessionState:該狀態(tài)表反映了所有事件源的組合狀態(tài)發(fā)布到當(dāng)前用戶的登錄會話。如果您的程序發(fā)布的事件在一個(gè)登錄會話,您應(yīng)該使用這個(gè)源狀態(tài)當(dāng)你創(chuàng)建一個(gè)事件源。 -
kCGEventSourceStateHIDSystemState:該狀態(tài)表反映了組合硬件輸入源從 HID 系統(tǒng)硬件層面發(fā)送的事件源。生成的事件。 就是外接鍵盤或者 macbook 本機(jī)鍵盤以及一些系統(tǒng)定義的按鍵點(diǎn)擊事件。
這里自己封裝了鼠標(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)滾輪事件只要傳入水平和垂直方向的偏移即可實(shí)現(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ù)用來模擬組合鍵的實(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:沒有任何鍵按下
如果有多個(gè)控制鍵同時(shí)按下,則使用位運(yùn)算的或 | 加上對應(yīng)的鍵值即可。例如模擬 Command + Control + S:
PostKeyboardEvent(kVK_ANSI_S, true, kCGEventFlagMaskCommand | kCGEventFlagMaskControl)
PostKeyboardEvent(kVK_ANSI_S, false, kCGEventFlagMaskNonCoalesced)
大小寫鎖定鍵,無法通過 kVK_CapsLock 按鍵的按下和抬起事件來模擬大小鍵的鎖定,同時(shí)按鍵上的 LED 燈也是不會有變化的。
