mirror of
https://github.com/glfw/glfw.git
synced 2024-11-26 20:11:58 +00:00
macOS: Support IME
This commit re-organizes 31b12b7f79a5aa8bd8f8eb1488a050ab894ca289. * Use dynamic load for TIS functions and stop using Carbon. * Generalize platform-specific features to _GLFWplatform. * Add caret-position info to preedit-callback. * Handle UTF16 data correctly. * Implement `firstRectForCharacterRange:actualRange:` to display preedit candidate window correctly. * Suppress _glfwInputKey during preediting. * Ensure preedit cleared after committed. * Fix wrong length of markedRange. * Improve IME status APIs. * Refactor code shapes and variable names. Co-authored-by: Takuro Ashie <ashie@clear-code.com> Co-authored-by: xfangfang <2553041586@qq.com>
This commit is contained in:
parent
e947cb7b52
commit
2368112f98
@ -150,12 +150,11 @@ endif()
|
||||
|
||||
if (GLFW_BUILD_COCOA)
|
||||
target_link_libraries(glfw PRIVATE "-framework Cocoa"
|
||||
"-framework Carbon"
|
||||
"-framework IOKit"
|
||||
"-framework CoreFoundation")
|
||||
|
||||
set(glfw_PKG_DEPS "")
|
||||
set(glfw_PKG_LIBS "-framework Cocoa -framework Carbon -framework IOKit -framework CoreFoundation")
|
||||
set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation")
|
||||
endif()
|
||||
|
||||
if (GLFW_BUILD_WAYLAND)
|
||||
|
@ -349,22 +349,70 @@ static GLFWbool initializeTIS(void)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
CFStringRef* kCategoryKeyboardInputSource =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISCategoryKeyboardInputSource"));
|
||||
CFStringRef* kPropertyInputSourceCategory =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyInputSourceCategory"));
|
||||
CFStringRef* kPropertyInputSourceID =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyInputSourceID"));
|
||||
CFStringRef* kPropertyInputSourceIsSelectCapable =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyInputSourceIsSelectCapable"));
|
||||
CFStringRef* kPropertyInputSourceType =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyInputSourceType"));
|
||||
CFStringRef* kPropertyUnicodeKeyLayoutData =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyUnicodeKeyLayoutData"));
|
||||
CFStringRef* kTypeKeyboardInputMethodModeEnabled =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISTypeKeyboardInputMethodModeEnabled"));
|
||||
_glfw.ns.tis.CopyCurrentASCIICapableKeyboardInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentASCIICapableKeyboardInputSource"));
|
||||
_glfw.ns.tis.CopyCurrentKeyboardInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentKeyboardInputSource"));
|
||||
_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
|
||||
_glfw.ns.tis.CopyInputSourceForLanguage =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyInputSourceForLanguage"));
|
||||
_glfw.ns.tis.CreateASCIICapableInputSourceList =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCreateASCIICapableInputSourceList"));
|
||||
_glfw.ns.tis.CreateInputSourceList =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCreateInputSourceList"));
|
||||
_glfw.ns.tis.GetInputSourceProperty =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISGetInputSourceProperty"));
|
||||
_glfw.ns.tis.SelectInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISSelectInputSource"));
|
||||
_glfw.ns.tis.GetKbdType =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("LMGetKbdType"));
|
||||
|
||||
if (!kPropertyUnicodeKeyLayoutData ||
|
||||
if (!kCategoryKeyboardInputSource||
|
||||
!kPropertyInputSourceCategory ||
|
||||
!kPropertyInputSourceID ||
|
||||
!kPropertyInputSourceIsSelectCapable||
|
||||
!kPropertyInputSourceType||
|
||||
!kPropertyUnicodeKeyLayoutData ||
|
||||
!kTypeKeyboardInputMethodModeEnabled ||
|
||||
!TISCopyCurrentASCIICapableKeyboardInputSource ||
|
||||
!TISCopyCurrentKeyboardInputSource ||
|
||||
!TISCopyCurrentKeyboardLayoutInputSource ||
|
||||
!TISCopyInputSourceForLanguage ||
|
||||
!TISCreateASCIICapableInputSourceList ||
|
||||
!TISCreateInputSourceList ||
|
||||
!TISGetInputSourceProperty ||
|
||||
!TISSelectInputSource ||
|
||||
!LMGetKbdType)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
@ -372,8 +420,20 @@ static GLFWbool initializeTIS(void)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.ns.tis.kCategoryKeyboardInputSource =
|
||||
*kCategoryKeyboardInputSource;
|
||||
_glfw.ns.tis.kPropertyInputSourceCategory =
|
||||
*kPropertyInputSourceCategory;
|
||||
_glfw.ns.tis.kPropertyInputSourceID =
|
||||
*kPropertyInputSourceID;
|
||||
_glfw.ns.tis.kPropertyInputSourceIsSelectCapable =
|
||||
*kPropertyInputSourceIsSelectCapable;
|
||||
_glfw.ns.tis.kPropertyInputSourceType =
|
||||
*kPropertyInputSourceType;
|
||||
_glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
|
||||
*kPropertyUnicodeKeyLayoutData;
|
||||
_glfw.ns.tis.kTypeKeyboardInputMethodModeEnabled =
|
||||
*kTypeKeyboardInputMethodModeEnabled;
|
||||
|
||||
return updateUnicodeData();
|
||||
}
|
||||
|
@ -109,11 +109,29 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta
|
||||
#define GLFW_NSGL_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl;
|
||||
|
||||
// HIToolbox.framework pointer typedefs
|
||||
#define kTISCategoryKeyboardInputSource _glfw.ns.tis.kCategoryKeyboardInputSource
|
||||
#define kTISPropertyInputSourceCategory _glfw.ns.tis.kPropertyInputSourceCategory
|
||||
#define kTISPropertyInputSourceID _glfw.ns.tis.kPropertyInputSourceID
|
||||
#define kTISPropertyInputSourceIsSelectCapable _glfw.ns.tis.kPropertyInputSourceIsSelectCapable
|
||||
#define kTISPropertyInputSourceType _glfw.ns.tis.kPropertyInputSourceType
|
||||
#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData
|
||||
#define kTISTypeKeyboardInputMethodModeEnabled _glfw.ns.tis.kTypeKeyboardInputMethodModeEnabled
|
||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentASCIICapableKeyboardInputSource)(void);
|
||||
#define TISCopyCurrentASCIICapableKeyboardInputSource _glfw.ns.tis.CopyCurrentASCIICapableKeyboardInputSource
|
||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardInputSource)(void);
|
||||
#define TISCopyCurrentKeyboardInputSource _glfw.ns.tis.CopyCurrentKeyboardInputSource
|
||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
|
||||
#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource
|
||||
typedef TISInputSourceRef (*PFN_TISCopyInputSourceForLanguage)(CFStringRef);
|
||||
#define TISCopyInputSourceForLanguage _glfw.ns.tis.CopyInputSourceForLanguage
|
||||
typedef CFArrayRef (*PFN_TISCreateASCIICapableInputSourceList)(void);
|
||||
#define TISCreateASCIICapableInputSourceList _glfw.ns.tis.CreateASCIICapableInputSourceList
|
||||
typedef CFArrayRef (*PEN_TISCreateInputSourceList)(CFDictionaryRef,Boolean);
|
||||
#define TISCreateInputSourceList _glfw.ns.tis.CreateInputSourceList
|
||||
typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);
|
||||
#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty
|
||||
typedef OSStatus (*PFN_TISSelectInputSource)(TISInputSourceRef);
|
||||
#define TISSelectInputSource _glfw.ns.tis.SelectInputSource
|
||||
typedef UInt8 (*PFN_LMGetKbdType)(void);
|
||||
#define LMGetKbdType _glfw.ns.tis.GetKbdType
|
||||
|
||||
@ -184,10 +202,22 @@ typedef struct _GLFWlibraryNS
|
||||
|
||||
struct {
|
||||
CFBundleRef bundle;
|
||||
PFN_TISCopyCurrentASCIICapableKeyboardInputSource CopyCurrentASCIICapableKeyboardInputSource;
|
||||
PFN_TISCopyCurrentKeyboardInputSource CopyCurrentKeyboardInputSource;
|
||||
PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource;
|
||||
PFN_TISCopyInputSourceForLanguage CopyInputSourceForLanguage;
|
||||
PFN_TISCreateASCIICapableInputSourceList CreateASCIICapableInputSourceList;
|
||||
PEN_TISCreateInputSourceList CreateInputSourceList;
|
||||
PFN_TISGetInputSourceProperty GetInputSourceProperty;
|
||||
PFN_TISSelectInputSource SelectInputSource;
|
||||
PFN_LMGetKbdType GetKbdType;
|
||||
CFStringRef kCategoryKeyboardInputSource;
|
||||
CFStringRef kPropertyInputSourceCategory;
|
||||
CFStringRef kPropertyInputSourceID;
|
||||
CFStringRef kPropertyInputSourceIsSelectCapable;
|
||||
CFStringRef kPropertyInputSourceType;
|
||||
CFStringRef kPropertyUnicodeKeyLayoutData;
|
||||
CFStringRef kTypeKeyboardInputMethodModeEnabled;
|
||||
} tis;
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
|
@ -321,7 +321,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)imeStatusChangeNotified:(NSNotification *)notification {
|
||||
- (void)imeStatusChangeNotified:(NSNotification *)notification
|
||||
{
|
||||
_glfwInputIMEStatus(window);
|
||||
}
|
||||
|
||||
@ -569,6 +570,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
const int key = translateKey([event keyCode]);
|
||||
const int mods = translateFlags([event modifierFlags]);
|
||||
|
||||
if (![self hasMarkedText])
|
||||
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
|
||||
|
||||
[self interpretKeyEvents:@[event]];
|
||||
@ -662,7 +664,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
- (NSRange)markedRange
|
||||
{
|
||||
if ([markedText length] > 0)
|
||||
return NSMakeRange(0, [markedText length] - 1);
|
||||
return NSMakeRange(0, [markedText length]);
|
||||
else
|
||||
return kEmptyRange;
|
||||
}
|
||||
@ -684,59 +686,92 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
|
||||
NSString* markedTextString = markedText.string;
|
||||
|
||||
NSUInteger i, length = [markedTextString length];
|
||||
int ctext = window->ctext;
|
||||
while (ctext < length+1) {
|
||||
ctext = (ctext == 0) ? 1 : ctext*2;
|
||||
}
|
||||
if (ctext != window->ctext) {
|
||||
unsigned int* preeditText = realloc(window->preeditText, sizeof(unsigned int)*ctext);
|
||||
if (preeditText == NULL) {
|
||||
return;
|
||||
}
|
||||
window->preeditText = preeditText;
|
||||
window->ctext = ctext;
|
||||
}
|
||||
window->ntext = length;
|
||||
window->preeditText[length] = 0;
|
||||
for (i = 0; i < length; i++)
|
||||
NSUInteger textLen = [markedTextString length];
|
||||
_GLFWpreedit* preedit = &window->preedit;
|
||||
int textBufferCount = preedit->textBufferCount;
|
||||
while (textBufferCount < textLen + 1)
|
||||
textBufferCount = textBufferCount == 0 ? 1 : textBufferCount * 2;
|
||||
if (textBufferCount != preedit->textBufferCount)
|
||||
{
|
||||
const unichar codepoint = [markedTextString characterAtIndex:i];
|
||||
window->preeditText[i] = codepoint;
|
||||
}
|
||||
int focusedBlock = 0;
|
||||
NSInteger offset = 0;
|
||||
window->nblocks = 0;
|
||||
while (offset < length) {
|
||||
NSRange effectiveRange;
|
||||
NSDictionary *attributes = [markedText attributesAtIndex:offset effectiveRange:&effectiveRange];
|
||||
|
||||
if (window->nblocks == window->cblocks) {
|
||||
int cblocks = window->cblocks * 2;
|
||||
int* blocks = realloc(window->preeditAttributeBlocks, sizeof(int)*cblocks);
|
||||
if (blocks == NULL) {
|
||||
unsigned int* preeditText = _glfw_realloc(preedit->text,
|
||||
sizeof(unsigned int) * textBufferCount);
|
||||
if (preeditText == NULL)
|
||||
return;
|
||||
preedit->text = preeditText;
|
||||
preedit->textBufferCount = textBufferCount;
|
||||
}
|
||||
window->preeditAttributeBlocks = blocks;
|
||||
window->cblocks = cblocks;
|
||||
|
||||
// NSString handles text data in UTF16 by default, so we have to convert them
|
||||
// to UTF32. Not only the encoding, but also the number of characters and
|
||||
// the position of each block.
|
||||
int currentBlockIndex = 0;
|
||||
int currentBlockLength = 0;
|
||||
int currentBlockLocation = 0;
|
||||
int focusedBlockIndex = 0;
|
||||
NSInteger preeditTextLength = 0;
|
||||
NSRange range = NSMakeRange(0, textLen);
|
||||
while (range.length)
|
||||
{
|
||||
uint32_t codepoint = 0;
|
||||
NSRange currentBlockRange;
|
||||
[markedText attributesAtIndex:range.location
|
||||
effectiveRange:¤tBlockRange];
|
||||
|
||||
if (preedit->blockSizesBufferCount < 1 + currentBlockIndex)
|
||||
{
|
||||
int blockBufferCount = (preedit->blockSizesBufferCount == 0)
|
||||
? 1 : preedit->blockSizesBufferCount * 2;
|
||||
int* blocks = _glfw_realloc(preedit->blockSizes,
|
||||
sizeof(int) * blockBufferCount);
|
||||
if (blocks == NULL)
|
||||
return;
|
||||
preedit->blockSizes = blocks;
|
||||
preedit->blockSizesBufferCount = blockBufferCount;
|
||||
}
|
||||
window->preeditAttributeBlocks[window->nblocks] = effectiveRange.length;
|
||||
offset += effectiveRange.length;
|
||||
if (effectiveRange.length == 0) {
|
||||
break;
|
||||
|
||||
if (currentBlockLocation != currentBlockRange.location)
|
||||
{
|
||||
currentBlockLocation = currentBlockRange.location;
|
||||
preedit->blockSizes[currentBlockIndex++] = currentBlockLength;
|
||||
currentBlockLength = 0;
|
||||
if (selectedRange.location == currentBlockRange.location)
|
||||
focusedBlockIndex = currentBlockIndex;
|
||||
}
|
||||
NSNumber* underline = (NSNumber*) [attributes objectForKey:@"NSUnderline"];
|
||||
if ([underline intValue] != 1) {
|
||||
focusedBlock = window->nblocks;
|
||||
|
||||
if ([markedTextString getBytes:&codepoint
|
||||
maxLength:sizeof(codepoint)
|
||||
usedLength:NULL
|
||||
encoding:NSUTF32StringEncoding
|
||||
options:0
|
||||
range:range
|
||||
remainingRange:&range])
|
||||
{
|
||||
if (codepoint >= 0xf700 && codepoint <= 0xf7ff)
|
||||
continue;
|
||||
|
||||
preedit->text[preeditTextLength++] = codepoint;
|
||||
currentBlockLength++;
|
||||
}
|
||||
window->nblocks++;
|
||||
}
|
||||
_glfwInputPreedit(window, focusedBlock);
|
||||
preedit->blockSizes[currentBlockIndex] = currentBlockLength;
|
||||
preedit->blockSizesCount = 1 + currentBlockIndex;
|
||||
preedit->textCount = preeditTextLength;
|
||||
preedit->text[preeditTextLength] = 0;
|
||||
preedit->focusedBlockIndex = focusedBlockIndex;
|
||||
// The caret is always at the last of preedit in macOS.
|
||||
preedit->caretIndex = preeditTextLength;
|
||||
|
||||
_glfwInputPreedit(window);
|
||||
}
|
||||
|
||||
- (void)unmarkText
|
||||
{
|
||||
[[markedText mutableString] setString:@""];
|
||||
window->preedit.blockSizesCount = 0;
|
||||
window->preedit.textCount = 0;
|
||||
window->preedit.focusedBlockIndex = 0;
|
||||
window->preedit.caretIndex = 0;
|
||||
_glfwInputPreedit(window);
|
||||
}
|
||||
|
||||
- (NSArray*)validAttributesForMarkedText
|
||||
@ -758,8 +793,19 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)range
|
||||
actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
const NSRect frame = [window->ns.view frame];
|
||||
return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0);
|
||||
int x = window->preedit.cursorPosX;
|
||||
int y = window->preedit.cursorPosY;
|
||||
int w = window->preedit.cursorWidth;
|
||||
int h = window->preedit.cursorHeight;
|
||||
|
||||
const NSRect frame =
|
||||
[window->ns.object contentRectForFrameRect:[window->ns.object frame]];
|
||||
|
||||
return NSMakeRect(frame.origin.x + x,
|
||||
// The y-axis is upward on macOS, so this conversion is needed.
|
||||
frame.origin.y + frame.size.height - y - h,
|
||||
w,
|
||||
h);
|
||||
}
|
||||
|
||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||
@ -793,6 +839,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
_glfwInputChar(window, codepoint, mods, plain);
|
||||
}
|
||||
}
|
||||
|
||||
[self unmarkText];
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)selector
|
||||
@ -1038,10 +1086,10 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: window->ns.delegate
|
||||
addObserver:window->ns.delegate
|
||||
selector:@selector(imeStatusChangeNotified:)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object: nil];
|
||||
object:nil];
|
||||
|
||||
return GLFW_TRUE;
|
||||
|
||||
@ -1055,7 +1103,7 @@ void _glfwDestroyWindowCocoa(_GLFWwindow* window)
|
||||
if (_glfw.ns.disabledCursorWindow == window)
|
||||
_glfw.ns.disabledCursorWindow = NULL;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: window->ns.delegate];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:window->ns.delegate];
|
||||
|
||||
[window->ns.object orderOut:nil];
|
||||
|
||||
@ -1946,19 +1994,114 @@ const char* _glfwGetClipboardStringCocoa(void)
|
||||
|
||||
void _glfwUpdatePreeditCursorRectangleCocoa(_GLFWwindow* window)
|
||||
{
|
||||
// Do nothing. Instead, implement `firstRectForCharacterRange` callback
|
||||
// to update the position.
|
||||
}
|
||||
|
||||
void _glfwResetPreeditTextCocoa(_GLFWwindow* window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
NSTextInputContext* context = [NSTextInputContext currentInputContext];
|
||||
[context discardMarkedText];
|
||||
[window->ns.view unmarkText];
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (active)
|
||||
{
|
||||
NSArray* locales = CFBridgingRelease(CFLocaleCopyPreferredLanguages());
|
||||
// Select the most preferred locale.
|
||||
CFStringRef locale = (__bridge CFStringRef) [locales firstObject];
|
||||
if (locale)
|
||||
{
|
||||
TISInputSourceRef source = TISCopyInputSourceForLanguage(locale);
|
||||
if (source)
|
||||
{
|
||||
CFStringRef sourceType = TISGetInputSourceProperty(source,
|
||||
kTISPropertyInputSourceType);
|
||||
|
||||
if (sourceType != kTISTypeKeyboardInputMethodModeEnabled)
|
||||
TISSelectInputSource(source);
|
||||
else
|
||||
{
|
||||
// Some IMEs return a input-method that has input-method-modes for `TISCopyInputSourceForLanguage()`.
|
||||
// We can't select these input-methods directly, but need to find
|
||||
// a input-method-mode of the input-method.
|
||||
// Example:
|
||||
// - Input Method: com.apple.inputmethod.SCIM
|
||||
// - Input Mode: com.apple.inputmethod.SCIM.ITABC
|
||||
NSString* sourceID =
|
||||
(__bridge NSString *) TISGetInputSourceProperty(source, kTISPropertyInputSourceID);
|
||||
NSDictionary* properties = @{
|
||||
(__bridge NSString *) kTISPropertyInputSourceCategory: (__bridge NSString *) kTISCategoryKeyboardInputSource,
|
||||
(__bridge NSString *) kTISPropertyInputSourceIsSelectCapable: @YES,
|
||||
};
|
||||
NSArray* selectableSources =
|
||||
CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef) properties, NO));
|
||||
for (id sourceCandidate in selectableSources)
|
||||
{
|
||||
TISInputSourceRef sourceCandidateRef = (__bridge TISInputSourceRef) sourceCandidate;
|
||||
NSString* sourceCandidateID =
|
||||
(__bridge NSString *) TISGetInputSourceProperty(sourceCandidateRef, kTISPropertyInputSourceID);
|
||||
if ([sourceCandidateID hasPrefix:sourceID])
|
||||
{
|
||||
TISSelectInputSource(sourceCandidateRef);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TISInputSourceRef source = TISCopyCurrentASCIICapableKeyboardInputSource();
|
||||
TISSelectInputSource(source);
|
||||
CFRelease(source);
|
||||
}
|
||||
|
||||
// `NSTextInputContextKeyboardSelectionDidChangeNotification` is sometimes
|
||||
// not called immediately after this, so call the callback here.
|
||||
_glfwInputIMEStatus(window);
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
int _glfwGetIMEStatusCocoa(_GLFWwindow* window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
NSArray* asciiInputSources =
|
||||
CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
||||
|
||||
TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
|
||||
NSString* currentSourceID =
|
||||
(__bridge NSString *) TISGetInputSourceProperty(currentSource,
|
||||
kTISPropertyInputSourceID);
|
||||
CFRelease(currentSource);
|
||||
|
||||
for (int i = 0; i < [asciiInputSources count]; i++)
|
||||
{
|
||||
TISInputSourceRef asciiSource =
|
||||
(__bridge TISInputSourceRef) [asciiInputSources objectAtIndex:i];
|
||||
NSString* asciiSourceID =
|
||||
(__bridge NSString *) TISGetInputSourceProperty(asciiSource,
|
||||
kTISPropertyInputSourceID);
|
||||
if ([asciiSourceID compare:currentSourceID] == NSOrderedSame)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs)
|
||||
@ -2114,49 +2257,6 @@ VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance,
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||
{
|
||||
NSTextInputContext *context = [NSTextInputContext currentInputContext];
|
||||
[context discardMarkedText];
|
||||
[window->ns.view unmarkText];
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||
{
|
||||
// Mac OS has several input sources.
|
||||
// this code assumes input methods not in ascii capable inputs using IME.
|
||||
NSArray* asciiInputSources = CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
||||
TISInputSourceRef asciiSource = (__bridge TISInputSourceRef)([asciiInputSources firstObject]);
|
||||
if (active) {
|
||||
NSArray* allInputSources = CFBridgingRelease(TISCreateInputSourceList(NULL, false));
|
||||
NSString* asciiSourceID = (__bridge NSString *)(TISGetInputSourceProperty(asciiSource, kTISPropertyInputSourceID));
|
||||
int i;
|
||||
int count = [allInputSources count];
|
||||
for (i = 0; i < count; i++) {
|
||||
TISInputSourceRef source = (__bridge TISInputSourceRef)([allInputSources objectAtIndex: i]);
|
||||
NSString* sourceID = (__bridge NSString *)(TISGetInputSourceProperty(source, kTISPropertyInputSourceID));
|
||||
if ([asciiSourceID compare: sourceID] != NSOrderedSame) {
|
||||
TISSelectInputSource(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (asciiSource) {
|
||||
TISSelectInputSource(asciiSource);
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
|
||||
NSString* currentSourceID = (__bridge NSString *)(TISGetInputSourceProperty(currentSource, kTISPropertyInputSourceID));
|
||||
NSArray* asciiInputSources = CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
||||
TISInputSourceRef asciiSource = (__bridge TISInputSourceRef)([asciiInputSources firstObject]);
|
||||
if (asciiSource) {
|
||||
NSString* asciiSourceID = (__bridge NSString *)(TISGetInputSourceProperty(asciiSource, kTISPropertyInputSourceID));
|
||||
return ([asciiSourceID compare: currentSourceID] == NSOrderedSame) ? GLFW_FALSE : GLFW_TRUE;
|
||||
}
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
Loading…
Reference in New Issue
Block a user