macOS: Add support for dead keys
I have modified GLFW to support dead keys on macOS. That was painful. Fixes #465
This commit is contained in:
parent
d4f426d2ce
commit
32f16ee5f5
@ -208,6 +208,9 @@ static GLFWbool updateUnicodeDataNS(void)
|
|||||||
_glfw.ns.unicodeData = nil;
|
_glfw.ns.unicodeData = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (_GLFWwindow *window = _glfw.windowListHead; window; window = window->next)
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
|
|
||||||
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
||||||
if (!_glfw.ns.inputSource)
|
if (!_glfw.ns.inputSource)
|
||||||
{
|
{
|
||||||
|
|||||||
2
glfw/cocoa_platform.h
vendored
2
glfw/cocoa_platform.h
vendored
@ -101,6 +101,8 @@ typedef struct _GLFWwindowNS
|
|||||||
|
|
||||||
// The text input filter callback
|
// The text input filter callback
|
||||||
GLFWcocoatextinputfilterfun textInputFilterCallback;
|
GLFWcocoatextinputfilterfun textInputFilterCallback;
|
||||||
|
// Dead key state
|
||||||
|
UInt32 deadKeyState;
|
||||||
} _GLFWwindowNS;
|
} _GLFWwindowNS;
|
||||||
|
|
||||||
// Cocoa-specific global data
|
// Cocoa-specific global data
|
||||||
|
|||||||
@ -719,20 +719,98 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
[super updateTrackingAreas];
|
[super updateTrackingAreas];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline UInt32
|
||||||
|
convert_cocoa_to_carbon_modifiers(NSUInteger flags) {
|
||||||
|
UInt32 mods = 0;
|
||||||
|
if (flags & NSEventModifierFlagShift)
|
||||||
|
mods |= shiftKey;
|
||||||
|
if (flags & NSEventModifierFlagControl)
|
||||||
|
mods |= controlKey;
|
||||||
|
if (flags & NSEventModifierFlagOption)
|
||||||
|
mods |= optionKey;
|
||||||
|
if (flags & NSEventModifierFlagCommand)
|
||||||
|
mods |= cmdKey;
|
||||||
|
if (flags & NSEventModifierFlagCapsLock)
|
||||||
|
mods |= alphaLock;
|
||||||
|
|
||||||
|
return (mods >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
convert_utf16_to_utf8(UniChar *src, UniCharCount src_length, char *dest, size_t dest_sz) {
|
||||||
|
CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
|
||||||
|
src,
|
||||||
|
src_length,
|
||||||
|
kCFAllocatorNull);
|
||||||
|
CFStringGetCString(string,
|
||||||
|
dest,
|
||||||
|
dest_sz,
|
||||||
|
kCFStringEncodingUTF8);
|
||||||
|
CFRelease(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline GLFWbool
|
||||||
|
is_ascii_control_char(char x) {
|
||||||
|
return x == 0 || (1 <= x && x <= 31) || x == 127;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent *)event
|
- (void)keyDown:(NSEvent *)event
|
||||||
{
|
{
|
||||||
const unsigned int scancode = [event keyCode];
|
const unsigned int scancode = [event keyCode];
|
||||||
|
const NSUInteger flags = [event modifierFlags];
|
||||||
|
const int mods = translateFlags(flags);
|
||||||
const int key = translateKey(scancode, GLFW_TRUE);
|
const int key = translateKey(scancode, GLFW_TRUE);
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
const GLFWbool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, scancode) != 1;
|
||||||
_glfw.ns.text[0] = 0;
|
_glfw.ns.text[0] = 0;
|
||||||
if (!window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, scancode) != 1) {
|
if (!_glfw.ns.unicodeData) {
|
||||||
|
// Using the cocoa API for key handling is disabled, as there is no
|
||||||
|
// reliable way to handle dead keys using it. Only use it if the
|
||||||
|
// keyboard unicode data is not available.
|
||||||
|
if (process_text) {
|
||||||
// this will call insertText with the text for this event, if any
|
// this will call insertText with the text for this event, if any
|
||||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
if ((1 <= _glfw.ns.text[0] && _glfw.ns.text[0] <= 31) || (unsigned)_glfw.ns.text[0] == 127) _glfw.ns.text[0] = 0; // dont send text for ascii control codes
|
}
|
||||||
|
} else {
|
||||||
|
static UniChar text[256];
|
||||||
|
UniCharCount char_count = 0;
|
||||||
|
if (UCKeyTranslate(
|
||||||
|
[(NSData*) _glfw.ns.unicodeData bytes],
|
||||||
|
scancode,
|
||||||
|
kUCKeyActionDown,
|
||||||
|
convert_cocoa_to_carbon_modifiers(flags),
|
||||||
|
LMGetKbdType(),
|
||||||
|
(process_text ? 0 : kUCKeyTranslateNoDeadKeysMask),
|
||||||
|
&(window->ns.deadKeyState),
|
||||||
|
sizeof(text)/sizeof(text[0]),
|
||||||
|
&char_count,
|
||||||
|
text
|
||||||
|
) != noErr) {
|
||||||
|
debug_key(@"UCKeyTranslate failed for scancode: 0x%x (%s) %s\n", scancode, safe_name_for_scancode(scancode), format_mods(mods));
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (process_text) {
|
||||||
|
// We check if cocoa wants to insert text, as UCKeyTranslate
|
||||||
|
// inserts text even when the cmd key is pressed. For instance,
|
||||||
|
// cmd+a will result in the text a.
|
||||||
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
|
debug_key(@"char_count: %lu cocoa text: %s\n", char_count, format_text(_glfw.ns.text));
|
||||||
|
GLFWbool cocoa_wants_to_insert_text = !is_ascii_control_char(_glfw.ns.text[0]);
|
||||||
|
_glfw.ns.text[0] = 0;
|
||||||
|
if (char_count && cocoa_wants_to_insert_text) convert_utf16_to_utf8(text, char_count, _glfw.ns.text, sizeof(_glfw.ns.text));
|
||||||
|
} else {
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
|
}
|
||||||
|
if (window->ns.deadKeyState && char_count == 0) {
|
||||||
|
debug_key(@"Ignoring dead key. deadKeyState: 0x%x scancode: 0x%x (%s) %s\n",
|
||||||
|
window->ns.deadKeyState, scancode, safe_name_for_scancode(scancode), format_mods(mods));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
debug_key(@"scancode: 0x%x (%s) %stext: %s glfw_key: %s\n",
|
debug_key(@"scancode: 0x%x (%s) %stext: %s glfw_key: %s\n",
|
||||||
scancode, safe_name_for_scancode(scancode), format_mods(mods),
|
scancode, safe_name_for_scancode(scancode), format_mods(mods),
|
||||||
format_text(_glfw.ns.text), _glfwGetKeyName(key));
|
format_text(_glfw.ns.text), _glfwGetKeyName(key));
|
||||||
|
if (is_ascii_control_char(_glfw.ns.text[0])) _glfw.ns.text[0] = 0; // dont send text for ascii control codes
|
||||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods, _glfw.ns.text, 0);
|
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods, _glfw.ns.text, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1235,6 +1313,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|||||||
const _GLFWctxconfig* ctxconfig,
|
const _GLFWctxconfig* ctxconfig,
|
||||||
const _GLFWfbconfig* fbconfig)
|
const _GLFWfbconfig* fbconfig)
|
||||||
{
|
{
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
if (!initializeAppKit())
|
if (!initializeAppKit())
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
|
|
||||||
@ -1779,16 +1858,7 @@ const char* _glfwPlatformGetScancodeName(int scancode)
|
|||||||
if (!characterCount)
|
if (!characterCount)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
|
convert_utf16_to_utf8(characters, characterCount, _glfw.ns.keyName, sizeof(_glfw.ns.keyName));
|
||||||
characters,
|
|
||||||
characterCount,
|
|
||||||
kCFAllocatorNull);
|
|
||||||
CFStringGetCString(string,
|
|
||||||
_glfw.ns.keyName,
|
|
||||||
sizeof(_glfw.ns.keyName),
|
|
||||||
kCFStringEncodingUTF8);
|
|
||||||
CFRelease(string);
|
|
||||||
|
|
||||||
return _glfw.ns.keyName;
|
return _glfw.ns.keyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user