Cocoa(OS X)中同时按下多个键

3

我对Mac编程还很陌生,所以希望这不是显而易见的问题。

简而言之,我似乎没有得到多个按键事件。我创建了这个片段,它从未触发断言,因此也从未打印多个按键。但我可以得到单个按键的输出。

- (void)keyDown:(NSEvent *) theEvent {
    NSString *characters = [theEvent characters];
    assert([characters length]<2);
    for (int i=0;i<[characters length];++i) {
      NSLog(@"k=[%d]\n", [characters characterAtIndex:i]);
    }
  }

有人知道我做错了什么吗?如果您感兴趣,我需要在OpenGL查看应用程序中按多个键。也许我对这种应用程序完全没有头绪。

编辑:经过进一步研究,我找到了这个链接: http://www.cocoadev.com/index.pl?GameKeyBoardHandling 根据这里的讨论,这很有道理,因为只有最后一个键会重复。当keyDown事件被触发时,按下的键被放入set中,并在keyUp时被删除。这意味着该集合具有当前按下的键的完整集合,避免了只重复最后按住的键的问题。 这已经“够好了”,所以我现在正在使用这种方法。它似乎运行良好,并且因为它使用标准键盘事件系统(而不是HID),所以不应该有任何兼容性问题。

敬礼, Shane

5个回答

2

有一件事情是,你正在测试事件中的字符数,这几乎总是1个。获得多于1个字符的最常见方法是使用非西方字符的输入法,如使用IME输入法。

不清楚你试图实现什么目标。如果你想要对用户按住一个键做出反应,你会收到多个keyDown:事件。在第一个事件上调用isARepeat将返回NO,在后续事件上将返回YES。

如果你想要检测同时按下多个键,则需要在keyDown:和keyUp:之间自己跟踪键状态。所以如果你同时按下f和g,你会收到一个或多个带有f的keyDown或更多带有g的keyDown:。然后,如果你在仍然按住g的情况下释放了f,你会收到f的keyUp:,同时继续接收g的keyDown:。你可以为每个键设置一个标志,然后在键弹起时清除该标志。

如果以上两种情况都不是你想要做的事情,那么你可能需要进一步解释你要做什么。


如果我每按下一个不同的键就会得到一个NSEvent,那么为什么我没有按住键时得到重复的keyDown事件呢?例如,如果我按住'a',等待一段时间,然后按住's',那么我应该看到两个单独的重复KeyDown事件被触发,对吗?但实际上发生的是,我只得到了最新按住的键。这是我的调试日志(来自我发布的代码)。如果我按住'a'(97),然后等待,再按住's'(115),你会看到97的重复停止了,只有115重复。只能同时重复一个键吗?“ k=[97] k=[97] k=[97] k=[115] k=[115] k=[115] ” - Shane
没错。在文本视图中试一下:aoooooooo。只有最近按下的字符键会重复。 - Peter Hosey
啊,真糟糕。在我以前编写键盘处理器的时候(视频游戏引擎),我会重复按下的任何内容,这对我来说似乎是很自然的。很高兴知道在这种情况下只有最后一个有效。看起来我得走HID的路线了。 - Shane

2
每个 NSEvent 表示一个事件。当按下一个键时,这是一个事件。当按下另一个键时,这是另一个事件。
“characters” 字符串只是一个文本字符串。如果您手动执行此操作,则可以将其插入到文本存储中。它不会告诉您哪些字符键被按下;这是键代码的工作。对于修改键,它是 “modifierFlags” 位掩码的工作。
如果您正在编写游戏,您可能会发现 DDHIDLib 或 Leopard 中的新 HID API 更有用。还要记住,用户可能有多个键盘,因此用户可能会按下两个空格键,例如。
最后,请使用Dvorak测试您的程序。许多试图处理原始键盘事件的应用程序都会出错,导致我们感到困惑甚至根本无法工作。Flash长期存在一个问题,即忽略QWERTY中的标点符号键,即使它们是活动布局中的字母键(在Dvorak中有几个键也是如此)。而且,我们不是唯一不使用QWERTY的人;许多非英语键盘与QWERTY布局不同。

我会看一下DDHIDLib,那看起来很有趣。我想知道OS X的GLUT是做什么用的...无论如何,我只是想获取各种按键的当前状态,无论有多少个按键被按下,这样我就可以控制我的OpenGL视图了。 - Shane
看起来Mac上的GLUT正在使用HID库:http://developer.apple.com/samplecode/glut/listing106.html有趣。 - Shane
“…我只是想获取各种键的当前状态…”听起来你肯定需要HID。 - Peter Hosey

1

好的,被接受的答案链接已经失效了,可能是类似这样的内容:

#import <Carbon/Carbon.h> // for kVK_* names

- (void)keyDown:(NSEvent *event)
{
    unsigned keyCode = [event keyCode];
    if (keyCode < 128) keysDown[keyCode] = YES;
}

- (void)keyUp:(NSEvent *event)
{
    unsigned keyCode = [event keyCode];
    if (keyCode < 128) keysDown[keyCode] = NO;
}

- (void)whereverYouUpdateTheStateOfYourGame
{
    if (keysDown[kVK_ANSI_A] && !keysDown[kVK_ANSI_D]) { /* move left */ }
    if (keysDown[kVK_ANSI_D] && !keysDown[kVK_ANSI_A]) { /* move right */ }
    if (keysDown[kVK_ANSI_W] && !keysDown[kVK_ANSI_S]) { /* move up */ }
    if (keysDown[kVK_ANSI_S] && !keysDown[kVK_ANSI_W]) { /* move down */ }
}

上面的代码来源还指出,由于一些硬件限制,我们需要小心处理。

1

0

在正常情况下,我不相信你可以同时接收多个按键。只有当按下一个键时才会调用keyDown,而且(我认为)不可能同时按下两个键;但是,根据苹果关于NSEvent的文档,单个按键可能会生成多个字符。我的最佳猜测是,多个字符指的是UNICODE代码点,除非NSString是一个UNICODE序列,否则我看不出如何生成多个字符,除非创建自己的事件。

然而,修改键是另一回事。如果你正在寻找修改键,那么你需要像这样的东西:

if ([event modifiers] & NSShiftKeyMask) {
    ...
}

我的最佳猜测是,多个字符是指UNICODE代码点,除了NSString是一个UNICODE序列,因此我无法想象您如何生成多个字符,除非创建自己的事件。首先,应该是Unicode而不是UNICODE。其次,unichar是两个字节;一个unichar只能容纳一个UTF-16代码单元。在基本多文种平面之外(即超过U+FFFF),一个字符必须表示为形成代理对的两个unichar。 - Peter Hosey
好的,但是我至少应该得到每个按住键的重复按键事件。请查看下面的答案,了解更多关于我的意思。 - Shane
你指的是Jon Steinmetz的回答。(顺序可能并非固定,可以根据投票而改变。) - Peter Hosey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接