我正在为Mac OSX开发一个打字导师应用程序,即使应用程序没有被关注,也需要将键击转发给它.
有没有办法让系统向应用程序转发击键,可能是通过NSDistributedNotificationCenter?我用谷歌搜索了自己,并且找不到答案......
编辑: 下面的示例代码.
感谢@NSGod指出我正确的方向 - 我最终使用addGlobalMonitorForEventsMatchingMask:handler:方法添加了一个全局事件监视器,它工作得非常好.为了完整起见,我的实现看起来像这样:
// register for keys throughout the device...
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent *event){
NSString *chars = [[event characters] lowercaseString];
unichar character = [chars characterAtIndex:0];
NSLog(@"keydown globally! Which key? This key: %c", character);
}];
Run Code Online (Sandbox Code Playgroud)
对我来说,棘手的部分是使用块,所以我会给出一些描述,以防它帮助任何人:
关于上面代码的注意事项是它是NSEvent上的所有单个方法调用.该块作为参数直接提供给函数.你可以认为它有点像内联委托方法.仅仅因为这需要一段时间才能让我沉沦,我将在这里一步一步地完成它:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
Run Code Online (Sandbox Code Playgroud)
这第一位没问题.您在NSEvent上调用类方法,并告诉它您要监视哪个事件,在本例中为NSKeyDownMask.一个支持的事件类型的面罩的名单,可以发现在这里.
现在,我们来到棘手的部分:处理程序,它需要一个块:
handler:^(NSEvent *event){
Run Code Online (Sandbox Code Playgroud)
我花了一些编译错误才能做到这一点,但是(谢谢Apple)他们是非常有建设性的错误信息.首先要注意的是克拉^.这标志着块的开始.在那之后,在括号内,
NSEvent *event
Run Code Online (Sandbox Code Playgroud)
其中声明了您将在块中使用的变量来捕获事件.你可以打电话给它
NSEvent *someCustomNameForAnEvent
Run Code Online (Sandbox Code Playgroud)
没关系,你只是在块内使用该名称.然后,这就是它的全部内容.确保关闭大括号和括号以完成方法调用:
}];
Run Code Online (Sandbox Code Playgroud)
而且你已经完成了!这真的是一种"单线".在您的应用程序中执行此调用的位置无关紧要 - 我在AppDelegate的applicationDidFinishLaunching方法中执行此操作.然后,在块中,您可以从应用程序中调用其他方法.
游戏需要低级访问键盘输入.在Windows上,有DirectInput.但是Mac OS X游戏开发者使用什么技术呢?
显然,有足够的Mac游戏可以让键盘输入正确,没有众所周知的解决方案的缺点:
解决方案#1:使用keyUp/keyDown事件
-(void)keyUp:(NSEvent*)event;
-(void)keyDown:(NSEvent*)event;
Run Code Online (Sandbox Code Playgroud)
不可接受的缺点:根据"密钥重复率"和"密钥重复延迟"的系统首选项设置重复keyDown事件.这会导致初始keyDown事件,然后暂停,然后以系统首选项设置定义的速率开始重复.此解决方案不能用于连续的键事件.
我想知道是否可以禁用密钥重复行为?我想您可以读取第一个keyDown键,然后记住类中的"keyCode x is key"键,直到收到该键的keyUp事件,从而绕过重复延迟和重复率问题.
解决方案#2:使用Quarts事件选项卡
请参阅Quartz事件服务参考.它似乎足够低级.
不可接受的缺点:要求在通用访问 - 键盘下的系统首选项中启用辅助设备.虽然默认情况下可能会启用,但无法保证在某些系统上可能会将其关闭.
我还读到Quartz事件点击需要应用程序以root身份运行,但没有找到确认.
解决方案#3:Carbon Events/IOKit HID
该碳事件管理器参考标记为传统的,不应该被用于新的开发.
不可接受的缺点:没有人知道未来的Mac OS版本将继续支持Carbon事件多长时间.
除了作为遗留框架的碳之外,这似乎仍然是最好的解决方案.但是使用Carbon Events还有其他任何缺点吗?
问题:
Mac OS X游戏开发人员使用哪种技术来接收低级键盘输入事件?如果他们使用上述技术之一,他们如何解决我提到的缺点?
更新:
我最终转向使用常规NSEvent消息并将它们包装在一个简洁的API中以轮询键盘状态.
我正在尝试创建一个OS X键盘钩子用于辅助技术目的(即不要担心,而不是键盘记录器).
当用户按下某个键时,我想阻止真正的按键并发送假按键(我选择的字符).
我有以下代码:
- (void) hookTheKeyboard {
CGEventMask keyboardMask = CGEventMaskBit(kCGEventKeyDown);
id eventHandler = [NSEvent addGlobalMonitorForEventsMatchingMask:keyboardMask handler:^(NSEvent *keyboardEvent) {
NSLog(@"keyDown: %c", [[keyboardEvent characters] characterAtIndex:0]);
//Want to: Stop the keyboard input
//Want to: Send another key input instead
}];
}
Run Code Online (Sandbox Code Playgroud)
有任何帮助实现这些目标吗?基本上修改NSEvent"keyboardEvent"以发送不同的字符.谢谢.
我正在尝试使用IOHIDManager获取修饰键事件,因为Cocoa flagsChanged事件缺乏(难以区分按下/释放,左/右如果两者都关闭等)这里是我创建管理器并注册回调的代码.
IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
if (CFGetTypeID(hidManager) != IOHIDManagerGetTypeID())
return 1;
CFMutableDictionaryRef capsLock =
myCreateDeviceMatchingDictionary(0x07, 0x39);
CFMutableDictionaryRef lctrl =
myCreateDeviceMatchingDictionary(0x07, 0xE0);
CFMutableDictionaryRef lshift =
myCreateDeviceMatchingDictionary(0x07, 0xE1);
CFMutableDictionaryRef lalt =
myCreateDeviceMatchingDictionary(0x07, 0xE2);
CFMutableDictionaryRef lsuper =
myCreateDeviceMatchingDictionary(0x07, 0xE3);
CFMutableDictionaryRef rctrl =
myCreateDeviceMatchingDictionary(0x07, 0xE4);
CFMutableDictionaryRef rshift =
myCreateDeviceMatchingDictionary(0x07, 0xE5);
CFMutableDictionaryRef ralt =
myCreateDeviceMatchingDictionary(0x07, 0xE6);
CFMutableDictionaryRef rsuper =
myCreateDeviceMatchingDictionary(0x07, 0xE7);
CFMutableDictionaryRef matchesList[] = {
capsLock,
lctrl,
lshift,
lalt,
lsuper,
rctrl,
rshift,
ralt,
rsuper
};
CFArrayRef matches = CFArrayCreate(kCFAllocatorDefault,
(const void **)matchesList, 9, …Run Code Online (Sandbox Code Playgroud) 我知道这可以在Windows上完成,XGrabKey也可以在X11上使用,但是Mac OS X呢?我想创建一个类,该类允许设置即使在应用程序窗口处于非活动状态时也可以调用的快捷键。
这个问题来自如何在 OSX 上挂接/重新映射任意键盘事件?
到目前为止,我可以使用以下方式点击修饰键和大多数其他键:
_eventTap = CGEventTapCreate( kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit( kCGEventKeyDown )
| CGEventMaskBit( kCGEventFlagsChanged )
,
(CGEventTapCallBack)_tapCallback,
(__bridge void *)(self));
Run Code Online (Sandbox Code Playgroud)
值得注意的是,在采取行动之前F3正确报告键码 (160) 。即我可以通过让事件处理程序返回 NULL 来禁用该操作(从而无法传播事件)。
但是,F7 到 F12 和弹出/电源不会触发回调。
如果我添加:
| CGEventMaskBit( NSSystemDefined )
Run Code Online (Sandbox Code Playgroud)
...现在剩余的 Fx 键确实会触发回调(尽管 Power/Eject 仍然不会),但我无法访问事件的 keyCode 方法。
它会产生一个错误:
2015-05-21 12:30:02.044 tap_k[16532:698660] NSSystemDefined: 0 2015-05-21 12:30:02.044 tap_k[16532:698660] * 在 -[NSEvent keyCode]、/SourceCache/AppKit/ 中断言失败AppKit-1347.57/AppKit.subproj/NSEvent.m:2471 2015-05-21 12:30:02.045 tap_k[16532:698660] *由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“发送到事件“NSEvent 的消息无效” : 类型=SysDefined loc=(882,687) 时间=118943.3 标志=0 win=0x0 winNum=0 ctxt=0x0 子类型=8 data1=2560 data2=-1"'
所以要么:
(1)我需要一些其他方式从 …
我现在已经发现如何在OS X上以低级别挂机/点击键盘事件:如何在MacBook键盘上点击(挂钩)F7到F12和Power/Eject
从该答案中打印出代码:
// compile and run from the commandline with:
// clang -framework coreFoundation -framework IOKit ./HID.c -o hid
// sudo ./hid
// This code works with the IOHID library to get notified of keys.
// Still haven't figured out how to truly intercept with
// substitution.
#include <IOKit/hid/IOHIDValue.h>
#include <IOKit/hid/IOHIDManager.h>
void myHIDKeyboardCallback( void* context, IOReturn result, void* sender, IOHIDValueRef value )
{
IOHIDElementRef elem = IOHIDValueGetElement( value );
if (IOHIDElementGetUsagePage(elem) != 0x07)
return;
uint32_t scancode = IOHIDElementGetUsage( …Run Code Online (Sandbox Code Playgroud) 我一直在尝试(从事件处理程序中)确定哪个键盘触发了该事件。我一直在使用这两篇文章:
在第二篇文章中,作者使用Carbon技术成功地分离了键盘,但是使用Cocoa尝试相同的技巧失败了。
在我自己的系统上,两者都失败了,可能是因为我的无线键盘也是苹果制造的,所以可能报告的标识符与内置键盘相同。
在第一篇文章中,有人提出了一种解决方案,该方案在较低级别(可以区分键盘)上监视键盘事件,并将此数据存储在队列中,然后让事件处理程序检索信息。
这看起来有些毛茸茸,所以我只是在这里检查是否有人发现了更好的东西。
这是我的代码,展示了在事件处理程序中区分键盘的失败:
// compile and run from the commandline with:
// clang -fobjc-arc -framework Cocoa -framework Carbon ./tap_k.m -o tap_k
// sudo ./tap_k
#import <Foundation/Foundation.h>
#import <AppKit/NSEvent.h>
//#import <CarbonEventsCore.h>
#include <Carbon/Carbon.h>
typedef CFMachPortRef EventTap;
// - - - - - - - - - - - - - - - - - - - - -
@interface KeyChanger : NSObject
{
@private
EventTap _eventTap;
CFRunLoopSourceRef _runLoopSource;
CGEventRef _lastEvent;
}
@end
// - - - …Run Code Online (Sandbox Code Playgroud) 因此我被要求将一些内部帮助应用程序移植到Mac OS X 10.7.
因为平台相关代码无论如何都很小,但是一个应用程序需要一个系统范围的快捷方式来运行(即RegisterHotkey功能)而且我找不到任何关于如何在Mac上执行此操作的文档.
该程序使用PyQt gui和Python 3.2.而windows的相应代码基本上是:
def register_hotkey(self):
hwnd = int(self.winId())
modifiers, key = self._get_hotkey()
user32.RegisterHotKey(hwnd, self._MESSAGE_ID, modifiers, key)
Run Code Online (Sandbox Code Playgroud)
然后接收热键事件:
def winEvent(self, msg):
if msg.message == w32.WM_HOTKEY:
self.handle_hotkey()
return True, id(msg)
return False, id(msg)
Run Code Online (Sandbox Code Playgroud)
请注意,我不需要python变体,我可以轻松编写一个简单的c扩展 - 所以C/objective-c解决方案也是受欢迎的.
我正在尝试编写一个应用程序来阻止某些关键信号在OSX中传播到OS之外.为了澄清,我想做到这一点,以便用户几乎看到他们在键盘上按下的键被打破了.因此,相关的字母不会出现在textarea中,该键不会激活另一个应用程序中的函数等.任何想法?提前致谢.