如何将ASCII字符转换为CGKeyCode?
我需要一个函数,给定一个字符,返回与当前键盘布局上该字符的位置关联的 CGKeyCode 。例如,给定“b”,如果使用 US QWERTY,则应返回 kVK_ANSI_B
,如果使用 Dvorak,则应返回 kVK_ANSI_N
。
Win32 API 具有函数 VkKeyScan()
用于此目的; X11 具有函数
XStringToKeySym()
。 CG API中有这样的功能吗?
我需要这个才能将参数传递给 CGEventCreateKeyboardEvent()
。我尝试过使用 CGEventKeyboardSetUnicodeString()
,但显然不支持修饰符标志(我需要)。
我对此进行了广泛的搜索,但找不到合适的答案。目前我正在使用以下代码(在线找到),有效,但并不完全优雅(并且很难破译如何简化),我不想在生产代码中使用它:
#include <stdint.h>
#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>
CGKeyCode keyCodeForCharWithLayout(const char c,
const UCKeyboardLayout *uchrHeader);
CGKeyCode keyCodeForChar(const char c)
{
CFDataRef currentLayoutData;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
if (currentKeyboard == NULL) {
fputs("Could not find keyboard layout\n", stderr);
return UINT16_MAX;
}
currentLayoutData = TISGetInputSourceProperty(currentKeyboard,
kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (currentLayoutData == NULL) {
fputs("Could not find layout data\n", stderr);
return UINT16_MAX;
}
return keyCodeForCharWithLayout(c,
(const UCKeyboardLayout *)CFDataGetBytePtr(currentLayoutData));
}
/* Beware! Messy, incomprehensible code ahead!
* TODO: XXX: FIXME! Please! */
CGKeyCode keyCodeForCharWithLayout(const char c,
const UCKeyboardLayout *uchrHeader)
{
uint8_t *uchrData = (uint8_t *)uchrHeader;
UCKeyboardTypeHeader *uchrKeyboardList = uchrHeader->keyboardTypeList;
/* Loop through the keyboard type list. */
ItemCount i, j;
for (i = 0; i < uchrHeader->keyboardTypeCount; ++i) {
/* Get a pointer to the keyToCharTable structure. */
UCKeyToCharTableIndex *uchrKeyIX = (UCKeyToCharTableIndex *)
(uchrData + (uchrKeyboardList[i].keyToCharTableIndexOffset));
/* Not sure what this is for but it appears to be a safeguard... */
UCKeyStateRecordsIndex *stateRecordsIndex;
if (uchrKeyboardList[i].keyStateRecordsIndexOffset != 0) {
stateRecordsIndex = (UCKeyStateRecordsIndex *)
(uchrData + (uchrKeyboardList[i].keyStateRecordsIndexOffset));
if ((stateRecordsIndex->keyStateRecordsIndexFormat) !=
kUCKeyStateRecordsIndexFormat) {
stateRecordsIndex = NULL;
}
} else {
stateRecordsIndex = NULL;
}
/* Make sure structure is a table that can be searched. */
if ((uchrKeyIX->keyToCharTableIndexFormat) != kUCKeyToCharTableIndexFormat) {
continue;
}
/* Check the table of each keyboard for character */
for (j = 0; j < uchrKeyIX->keyToCharTableCount; ++j) {
UCKeyOutput *keyToCharData =
(UCKeyOutput *)(uchrData + (uchrKeyIX->keyToCharTableOffsets[j]));
/* Check THIS table of the keyboard for the character. */
UInt16 k;
for (k = 0; k < uchrKeyIX->keyToCharTableSize; ++k) {
/* Here's the strange safeguard again... */
if ((keyToCharData[k] & kUCKeyOutputTestForIndexMask) ==
kUCKeyOutputStateIndexMask) {
long keyIndex = (keyToCharData[k] & kUCKeyOutputGetIndexMask);
if (stateRecordsIndex != NULL &&
keyIndex <= (stateRecordsIndex->keyStateRecordCount)) {
UCKeyStateRecord *stateRecord = (UCKeyStateRecord *)
(uchrData +
(stateRecordsIndex->keyStateRecordOffsets[keyIndex]));
if ((stateRecord->stateZeroCharData) == c) {
return (CGKeyCode)k;
}
} else if (keyToCharData[k] == c) {
return (CGKeyCode)k;
}
} else if (((keyToCharData[k] & kUCKeyOutputTestForIndexMask)
!= kUCKeyOutputSequenceIndexMask) &&
keyToCharData[k] != 0xFFFE &&
keyToCharData[k] != 0xFFFF &&
keyToCharData[k] == c) {
return (CGKeyCode)k;
}
}
}
}
return UINT16_MAX;
}
是否有a.)(最好)我忽略的标准函数,或b.)(几乎当然)我自己写的更优雅的方式?
I need a function that, given a character, returns the CGKeyCode
associated with the position of that character on the current keyboard layout. E.g., given "b", it should return kVK_ANSI_B
if using U.S. QWERTY, or kVK_ANSI_N
if using Dvorak.
The Win32 API has the function VkKeyScan()
for this purpose; X11 has the function XStringToKeySym()
. Is there such a function in the CG API?
I need this in order to pass a parameter to CGEventCreateKeyboardEvent()
. I've tried using CGEventKeyboardSetUnicodeString()
instead, but that apparently does not support modifier flags (which I need).
I have searched extensively for this but cannot find a decent answer. Currently I am using the following code (found online), which works, but is not exactly elegant (and rather difficult to decipher how to simplify) and I would prefer not to use it in production code:
#include <stdint.h>
#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>
CGKeyCode keyCodeForCharWithLayout(const char c,
const UCKeyboardLayout *uchrHeader);
CGKeyCode keyCodeForChar(const char c)
{
CFDataRef currentLayoutData;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
if (currentKeyboard == NULL) {
fputs("Could not find keyboard layout\n", stderr);
return UINT16_MAX;
}
currentLayoutData = TISGetInputSourceProperty(currentKeyboard,
kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (currentLayoutData == NULL) {
fputs("Could not find layout data\n", stderr);
return UINT16_MAX;
}
return keyCodeForCharWithLayout(c,
(const UCKeyboardLayout *)CFDataGetBytePtr(currentLayoutData));
}
/* Beware! Messy, incomprehensible code ahead!
* TODO: XXX: FIXME! Please! */
CGKeyCode keyCodeForCharWithLayout(const char c,
const UCKeyboardLayout *uchrHeader)
{
uint8_t *uchrData = (uint8_t *)uchrHeader;
UCKeyboardTypeHeader *uchrKeyboardList = uchrHeader->keyboardTypeList;
/* Loop through the keyboard type list. */
ItemCount i, j;
for (i = 0; i < uchrHeader->keyboardTypeCount; ++i) {
/* Get a pointer to the keyToCharTable structure. */
UCKeyToCharTableIndex *uchrKeyIX = (UCKeyToCharTableIndex *)
(uchrData + (uchrKeyboardList[i].keyToCharTableIndexOffset));
/* Not sure what this is for but it appears to be a safeguard... */
UCKeyStateRecordsIndex *stateRecordsIndex;
if (uchrKeyboardList[i].keyStateRecordsIndexOffset != 0) {
stateRecordsIndex = (UCKeyStateRecordsIndex *)
(uchrData + (uchrKeyboardList[i].keyStateRecordsIndexOffset));
if ((stateRecordsIndex->keyStateRecordsIndexFormat) !=
kUCKeyStateRecordsIndexFormat) {
stateRecordsIndex = NULL;
}
} else {
stateRecordsIndex = NULL;
}
/* Make sure structure is a table that can be searched. */
if ((uchrKeyIX->keyToCharTableIndexFormat) != kUCKeyToCharTableIndexFormat) {
continue;
}
/* Check the table of each keyboard for character */
for (j = 0; j < uchrKeyIX->keyToCharTableCount; ++j) {
UCKeyOutput *keyToCharData =
(UCKeyOutput *)(uchrData + (uchrKeyIX->keyToCharTableOffsets[j]));
/* Check THIS table of the keyboard for the character. */
UInt16 k;
for (k = 0; k < uchrKeyIX->keyToCharTableSize; ++k) {
/* Here's the strange safeguard again... */
if ((keyToCharData[k] & kUCKeyOutputTestForIndexMask) ==
kUCKeyOutputStateIndexMask) {
long keyIndex = (keyToCharData[k] & kUCKeyOutputGetIndexMask);
if (stateRecordsIndex != NULL &&
keyIndex <= (stateRecordsIndex->keyStateRecordCount)) {
UCKeyStateRecord *stateRecord = (UCKeyStateRecord *)
(uchrData +
(stateRecordsIndex->keyStateRecordOffsets[keyIndex]));
if ((stateRecord->stateZeroCharData) == c) {
return (CGKeyCode)k;
}
} else if (keyToCharData[k] == c) {
return (CGKeyCode)k;
}
} else if (((keyToCharData[k] & kUCKeyOutputTestForIndexMask)
!= kUCKeyOutputSequenceIndexMask) &&
keyToCharData[k] != 0xFFFE &&
keyToCharData[k] != 0xFFFF &&
keyToCharData[k] == c) {
return (CGKeyCode)k;
}
}
}
}
return UINT16_MAX;
}
Is there a.) (preferably) a standard function I am overlooking, or b.) (almost certainly) a more elegant way write my own?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这就是我最终使用的。干净多了。
This is what I ended up using. Much cleaner.
对于像我这样搜索迈克尔提议的更新版本的人来说,这就是我最终自己做的事情(对我来说,它解决了一些段错误问题,可能是因为垃圾收集器是在此版本中完成其工作)。
第一个函数来自Convert Virtual Key Code to unicode string。
我使用
NSNumber
获取可为 null 的对象,然后可以针对nil
测试charToKeyCode(c)
返回的值,然后使用访问>(CGKeyCode)[charToKeyCode(c) intValue]
。For those like me who searched for a more up-to-date version of what Michael proposed, here is what I ended up doing myself (for me it solved some segfault issue, probably because the garbage collector is doing its job with this version).
The first function comes from Convert Virtual Key Code to unicode string.
I used
NSNumber
to get a nullable object, the value returned bycharToKeyCode(c)
can then be tested againstnil
and then accessed with(CGKeyCode)[charToKeyCode(c) intValue]
.原始代码来自这里:
http://ritter.ist.psu.edu/projects/RUI/macosx /rui.c
original code from here:
http://ritter.ist.psu.edu/projects/RUI/macosx/rui.c
我模拟了这个不依赖于 Carbon 例程的版本。
事实上,它走得更远,因为它将映射任何可以键入的字符回其虚拟键代码和修饰符组合。
例如,如果您使用以下命令调用它,
它将返回一个字典,内容如下:
其中表示“pi”符号是由虚拟键代码 35 按下选项键(在标准美国键盘上:⌥P)生成的。
I mocked up version of this that doesn't rely on Carbon routines.
In fact, it goes a bit farther, because it will map any character that can be typed back to its virtual key code and modifier combination.
For example, if you call it with the following
it will return a dictionary that reads:
Which says that the 'pi' symbol is produced by virtual key code 35 with the option key down (on a standard US keyboard: ⌥P).
就这个问题而言,似乎有 3 种类型的键可以生成 CGKeyCode:
我重复了 Ted Wrigley 的回答,为所有 3 种类型创建正确的 Swift 初始化程序。来自
CGEvent(keyboardEventSource:virtualKey:keyDown:)
的示例 现在可以写成:For the purposes of this question there appear to be 3 types of keys that produce a
CGKeyCode
:I riffed on Ted Wrigley's answer to create proper Swift initialisers for all 3 types. The example from
CGEvent(keyboardEventSource:virtualKey:keyDown:)
can now be written:您自己的解决方案在小补丁后也可以在 Qt 下正常工作(转换为
CFDataRef
):替换
为
可避免错误:
从 'void*' 到 'const __CFData*' 的无效转换
Your own solution works fine under Qt too after a small patch (casting to
CFDataRef
):Replacing
with
avoids the error:
invalid conversion from 'void*' to 'const __CFData*'