#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_COCOA
#include "SDL_cocoavideo.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/scancodes_darwin.h"
#include <Carbon/Carbon.h>
#define DEBUG_IME(...)
@interface SDLTranslatorResponder : NSView <NSTextInputClient> {
NSString *_markedText;
NSRange _markedRange;
NSRange _selectedRange;
SDL_Rect _inputRect;
}
- (void)doCommandBySelector:(SEL)myselector;
- (void)setInputRect:(const SDL_Rect *)rect;
@end
@implementation SDLTranslatorResponder
- (void)setInputRect:(const SDL_Rect *)rect
{
_inputRect = *rect;
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{
const char *str;
DEBUG_IME(@"insertText: %@", aString);
if ([aString isKindOfClass: [NSAttributedString class]]) {
str = [[aString string] UTF8String];
} else {
str = [aString UTF8String];
}
if ([self hasMarkedText]) {
[self unmarkText];
}
SDL_SendKeyboardText(str);
}
- (void)doCommandBySelector:(SEL)myselector
{
}
- (BOOL)hasMarkedText
{
return _markedText != nil;
}
- (NSRange)markedRange
{
return _markedRange;
}
- (NSRange)selectedRange
{
return _selectedRange;
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
if ([aString isKindOfClass:[NSAttributedString class]]) {
aString = [aString string];
}
if ([aString length] == 0) {
[self unmarkText];
return;
}
if (_markedText != aString) {
_markedText = aString;
}
_selectedRange = selectedRange;
_markedRange = NSMakeRange(0, [aString length]);
SDL_SendEditingText([aString UTF8String],
(int) selectedRange.location, (int) selectedRange.length);
DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText,
selectedRange.location, selectedRange.length);
}
- (void)unmarkText
{
_markedText = nil;
SDL_SendEditingText("", 0, 0);
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
NSWindow *window = [self window];
NSRect contentRect = [window contentRectForFrameRect:[window frame]];
float windowHeight = contentRect.size.height;
NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h,
_inputRect.w, _inputRect.h);
if (actualRange) {
*actualRange = aRange;
}
DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@",
aRange.location, aRange.length, windowHeight,
NSStringFromRect(rect));
rect = [window convertRectToScreen:rect];
return rect;
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length);
return nil;
}
- (NSInteger)conversationIdentifier
{
return (NSInteger) self;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
return 0;
}
- (NSArray *)validAttributesForMarkedText
{
return [NSArray array];
}
@end
static void
HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags)
{
SDL_Scancode code = darwin_scancode_table[scancode];
const SDL_Scancode codes[] = {
SDL_SCANCODE_LSHIFT,
SDL_SCANCODE_LCTRL,
SDL_SCANCODE_LALT,
SDL_SCANCODE_LGUI,
SDL_SCANCODE_RSHIFT,
SDL_SCANCODE_RCTRL,
SDL_SCANCODE_RALT,
SDL_SCANCODE_RGUI,
SDL_SCANCODE_LSHIFT,
SDL_SCANCODE_LCTRL,
SDL_SCANCODE_LALT,
SDL_SCANCODE_LGUI, };
const unsigned int modifiers[] = {
NX_DEVICELSHIFTKEYMASK,
NX_DEVICELCTLKEYMASK,
NX_DEVICELALTKEYMASK,
NX_DEVICELCMDKEYMASK,
NX_DEVICERSHIFTKEYMASK,
NX_DEVICERCTLKEYMASK,
NX_DEVICERALTKEYMASK,
NX_DEVICERCMDKEYMASK,
NX_SHIFTMASK,
NX_CONTROLMASK,
NX_ALTERNATEMASK,
NX_COMMANDMASK };
for (int i = 0; i < 12; i++)
{
if (code == codes[i])
{
if (modifierFlags & modifiers[i])
SDL_SendKeyboardKey(SDL_PRESSED, code);
else
SDL_SendKeyboardKey(SDL_RELEASED, code);
}
}
}
static void
UpdateKeymap(SDL_VideoData *data, SDL_bool send_event)
{
TISInputSourceRef key_layout;
const void *chr_data;
int i;
SDL_Scancode scancode;
SDL_Keycode keymap[SDL_NUM_SCANCODES];
CFDataRef uchrDataRef;
key_layout = TISCopyCurrentKeyboardLayoutInputSource();
if (key_layout == data.key_layout) {
return;
}
data.key_layout = key_layout;
SDL_GetDefaultKeymap(keymap);
uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData);
if (uchrDataRef) {
chr_data = CFDataGetBytePtr(uchrDataRef);
} else {
goto cleanup;
}
if (chr_data) {
UInt32 keyboard_type = LMGetKbdType();
OSStatus err;
for (i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
UniChar s[8];
UniCharCount len;
UInt32 dead_key_state;
scancode = darwin_scancode_table[i];
if (scancode == SDL_SCANCODE_UNKNOWN ||
(keymap[scancode] & SDLK_SCANCODE_MASK)) {
continue;
}
dead_key_state = 0;
err = UCKeyTranslate ((UCKeyboardLayout *) chr_data,
i, kUCKeyActionDown,
0, keyboard_type,
kUCKeyTranslateNoDeadKeysMask,
&dead_key_state, 8, &len, s);
if (err != noErr) {
continue;
}
if (len > 0 && s[0] != 0x10) {
keymap[scancode] = s[0];
}
}
SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, send_event);
return;
}
cleanup:
CFRelease(key_layout);
}
void
Cocoa_InitKeyboard(_THIS)
{
SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata;
UpdateKeymap(data, SDL_FALSE);
SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option");
SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command");
SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option");
SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command");
data.modifierFlags = (unsigned int)[NSEvent modifierFlags];
SDL_ToggleModState(KMOD_CAPS, (data.modifierFlags & NSEventModifierFlagCapsLock) != 0);
}
void
Cocoa_StartTextInput(_THIS)
{ @autoreleasepool
{
NSView *parentView;
SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata;
SDL_Window *window = SDL_GetKeyboardFocus();
NSWindow *nswindow = nil;
if (window) {
nswindow = ((__bridge SDL_WindowData*)window->driverdata).nswindow;
}
parentView = [nswindow contentView];
if (!data.fieldEdit) {
data.fieldEdit =
[[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
}
if (![[data.fieldEdit superview] isEqual:parentView]) {
[data.fieldEdit removeFromSuperview];
[parentView addSubview: data.fieldEdit];
[nswindow makeFirstResponder: data.fieldEdit];
}
}}
void
Cocoa_StopTextInput(_THIS)
{ @autoreleasepool
{
SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata;
if (data && data.fieldEdit) {
[data.fieldEdit removeFromSuperview];
data.fieldEdit = nil;
}
}}
void
Cocoa_SetTextInputRect(_THIS, const SDL_Rect *rect)
{
SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata;
if (!rect) {
SDL_InvalidParamError("rect");
return;
}
[data.fieldEdit setInputRect:rect];
}
void
Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
{
unsigned short scancode;
SDL_Scancode code;
SDL_VideoData *data = _this ? ((__bridge SDL_VideoData *) _this->driverdata) : nil;
if (!data) {
return;
}
scancode = [event keyCode];
#if 0#endif
if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
scancode = 60 - scancode;
}
if (scancode < SDL_arraysize(darwin_scancode_table)) {
code = darwin_scancode_table[scancode];
} else {
code = SDL_SCANCODE_UNKNOWN;
}
switch ([event type]) {
case NSEventTypeKeyDown:
if (![event isARepeat]) {
UpdateKeymap(data, SDL_TRUE);
}
SDL_SendKeyboardKey(SDL_PRESSED, code);
#ifdef DEBUG_SCANCODES
if (code == SDL_SCANCODE_UNKNOWN) {
SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list <https://discourse.libsdl.org/> or to Christian Walther <cwalther@gmx.ch>. Mac virtual key code is %d.\n", scancode);
}
#endif
if (SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) {
[data.fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
#if 0#endif
}
break;
case NSEventTypeKeyUp:
SDL_SendKeyboardKey(SDL_RELEASED, code);
break;
case NSEventTypeFlagsChanged:
HandleModifiers(_this, scancode, (unsigned int)[event modifierFlags]);
break;
default:
break;
}
}
void
Cocoa_QuitKeyboard(_THIS)
{
}
typedef int CGSConnection;
typedef enum {
CGSGlobalHotKeyEnable = 0,
CGSGlobalHotKeyDisable = 1,
} CGSGlobalHotKeyOperatingMode;
extern CGSConnection _CGSDefaultConnection(void);
extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode);
void
Cocoa_SetWindowKeyboardGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
{
#if SDL_MAC_NO_SANDBOX
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), grabbed ? CGSGlobalHotKeyDisable : CGSGlobalHotKeyEnable);
#endif
}
#endif