src/video/cocoa/SDL_cocoakeyboard.m
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Jan 2018 10:03:25 -0800
changeset 11811 5d94cb6b24d3
parent 11604 634c34c79fbc
child 12503 806492103856
permissions -rw-r--r--
Updated copyright for 2018
slouken@1931
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
slouken@1931
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@1931
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@1931
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@1931
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@1931
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_COCOA
slouken@6044
    24
slouken@1931
    25
#include "SDL_cocoavideo.h"
slouken@1931
    26
slime73@9915
    27
#include "../../events/SDL_events_c.h"
slouken@1931
    28
#include "../../events/SDL_keyboard_c.h"
slouken@2305
    29
#include "../../events/scancodes_darwin.h"
slouken@1931
    30
slouken@2268
    31
#include <Carbon/Carbon.h>
slouken@10459
    32
#include <IOKit/hid/IOHIDLib.h>
slouken@2268
    33
slouken@7191
    34
/*#define DEBUG_IME NSLog */
slouken@6044
    35
#define DEBUG_IME(...)
slouken@1959
    36
slime73@9959
    37
@interface SDLTranslatorResponder : NSView <NSTextInputClient> {
slouken@3280
    38
    NSString *_markedText;
slouken@3280
    39
    NSRange   _markedRange;
slouken@3280
    40
    NSRange   _selectedRange;
slouken@3280
    41
    SDL_Rect  _inputRect;
slouken@2289
    42
}
slime73@9959
    43
- (void)doCommandBySelector:(SEL)myselector;
slime73@9959
    44
- (void)setInputRect:(SDL_Rect *)rect;
slouken@2289
    45
@end
slouken@2289
    46
slouken@2289
    47
@implementation SDLTranslatorResponder
slouken@3280
    48
slime73@9959
    49
- (void)setInputRect:(SDL_Rect *)rect
slouken@3280
    50
{
slouken@3280
    51
    _inputRect = *rect;
slouken@3280
    52
}
slouken@3280
    53
slime73@9959
    54
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
slouken@3280
    55
{
slime73@9959
    56
    /* TODO: Make use of replacementRange? */
slime73@9959
    57
slouken@3280
    58
    const char *str;
slouken@3280
    59
slouken@3280
    60
    DEBUG_IME(@"insertText: %@", aString);
slouken@3280
    61
slouken@3280
    62
    /* Could be NSString or NSAttributedString, so we have
slouken@3280
    63
     * to test and convert it before return as SDL event */
slouken@8986
    64
    if ([aString isKindOfClass: [NSAttributedString class]]) {
slouken@3280
    65
        str = [[aString string] UTF8String];
slouken@8986
    66
    } else {
slouken@3280
    67
        str = [aString UTF8String];
slouken@8986
    68
    }
slouken@3280
    69
slouken@4465
    70
    SDL_SendKeyboardText(str);
slouken@3280
    71
}
slouken@3280
    72
slime73@9959
    73
- (void)doCommandBySelector:(SEL)myselector
slouken@3280
    74
{
slouken@7191
    75
    /* No need to do anything since we are not using Cocoa
slouken@7191
    76
       selectors to handle special keys, instead we use SDL
slouken@7191
    77
       key events to do the same job.
slouken@7191
    78
    */
slouken@3280
    79
}
slouken@3280
    80
slime73@9959
    81
- (BOOL)hasMarkedText
slouken@3280
    82
{
slouken@3280
    83
    return _markedText != nil;
slouken@3280
    84
}
slouken@3280
    85
slime73@9959
    86
- (NSRange)markedRange
slouken@3280
    87
{
slouken@3280
    88
    return _markedRange;
slouken@3280
    89
}
slouken@3280
    90
slime73@9959
    91
- (NSRange)selectedRange
slouken@3280
    92
{
slouken@3280
    93
    return _selectedRange;
slouken@3280
    94
}
slouken@3280
    95
slouken@11604
    96
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
slouken@3280
    97
{
slime73@10177
    98
    if ([aString isKindOfClass:[NSAttributedString class]]) {
slouken@3280
    99
        aString = [aString string];
slouken@8986
   100
    }
slouken@3280
   101
slouken@8986
   102
    if ([aString length] == 0) {
slouken@3280
   103
        [self unmarkText];
slouken@3280
   104
        return;
slouken@3280
   105
    }
slouken@3280
   106
slouken@8986
   107
    if (_markedText != aString) {
slouken@3280
   108
        [_markedText release];
slouken@3280
   109
        _markedText = [aString retain];
slouken@3280
   110
    }
slouken@3280
   111
slime73@9959
   112
    _selectedRange = selectedRange;
slouken@3280
   113
    _markedRange = NSMakeRange(0, [aString length]);
slouken@3280
   114
slouken@4465
   115
    SDL_SendEditingText([aString UTF8String],
slime73@10339
   116
                        (int) selectedRange.location, (int) selectedRange.length);
slouken@3280
   117
slouken@3280
   118
    DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText,
slouken@3280
   119
          selRange.location, selRange.length);
slouken@3280
   120
}
slouken@3280
   121
slime73@9959
   122
- (void)unmarkText
slouken@3280
   123
{
slouken@3280
   124
    [_markedText release];
slouken@3280
   125
    _markedText = nil;
gzjjgod@4637
   126
gzjjgod@4637
   127
    SDL_SendEditingText("", 0, 0);
slouken@3280
   128
}
slouken@3280
   129
slouken@11604
   130
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
slouken@3280
   131
{
slouken@4436
   132
    NSWindow *window = [self window];
slime73@9959
   133
    NSRect contentRect = [window contentRectForFrameRect:[window frame]];
slouken@4436
   134
    float windowHeight = contentRect.size.height;
slouken@3280
   135
    NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h,
slouken@3280
   136
                             _inputRect.w, _inputRect.h);
slouken@3280
   137
slime73@9959
   138
    if (actualRange) {
slime73@9959
   139
        *actualRange = aRange;
slime73@9959
   140
    }
slime73@9959
   141
slouken@3280
   142
    DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@",
slime73@9959
   143
            aRange.location, aRange.length, windowHeight,
slouken@3280
   144
            NSStringFromRect(rect));
slime73@9959
   145
slime73@11145
   146
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
slime73@11145
   147
    if (![window respondsToSelector:@selector(convertRectToScreen:)]) {
slime73@11145
   148
        rect.origin = [window convertBaseToScreen:rect.origin];
slime73@11145
   149
    } else
slime73@11145
   150
#endif
slime73@11145
   151
    {
slime73@10177
   152
        rect = [window convertRectToScreen:rect];
slime73@9959
   153
    }
slouken@3280
   154
slouken@3280
   155
    return rect;
slouken@3280
   156
}
slouken@3280
   157
slouken@11604
   158
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
slouken@3280
   159
{
slime73@9959
   160
    DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length);
slouken@3280
   161
    return nil;
slouken@3280
   162
}
slouken@3280
   163
slime73@9959
   164
- (NSInteger)conversationIdentifier
slouken@3280
   165
{
jorgen@7507
   166
    return (NSInteger) self;
slouken@3280
   167
}
slouken@3280
   168
slouken@7191
   169
/* This method returns the index for character that is
slouken@7191
   170
 * nearest to thePoint.  thPoint is in screen coordinate system.
slouken@7191
   171
 */
slime73@9959
   172
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
slouken@3280
   173
{
slouken@3280
   174
    DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
slouken@3280
   175
    return 0;
slouken@3280
   176
}
slouken@3280
   177
slouken@7191
   178
/* This method is the key to attribute extension.
slouken@7191
   179
 * We could add new attributes through this method.
slouken@7191
   180
 * NSInputServer examines the return value of this
slouken@7191
   181
 * method & constructs appropriate attributed string.
slouken@7191
   182
 */
slime73@9959
   183
- (NSArray *)validAttributesForMarkedText
slouken@3280
   184
{
slouken@3280
   185
    return [NSArray array];
slouken@3280
   186
}
slouken@3280
   187
slouken@2289
   188
@end
slouken@2289
   189
slouken@10459
   190
/*------------------------------------------------------------------------------
slouken@10459
   191
Set up a HID callback to properly detect Caps Lock up/down events.
slouken@10459
   192
Derived from:
slouken@10459
   193
http://stackoverflow.com/questions/7190852/using-iohidmanager-to-get-modifier-key-events
slouken@10459
   194
*/
slouken@10459
   195
slouken@10459
   196
static IOHIDManagerRef s_hidManager = NULL;
slouken@10459
   197
slouken@10459
   198
static void
slouken@10459
   199
HIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
slouken@10459
   200
{
slouken@10554
   201
    if (context != s_hidManager) {
slouken@10554
   202
        /* An old callback, ignore it (related to bug 2157 below) */
slouken@10554
   203
        return;
slouken@10554
   204
    }
slouken@10554
   205
slouken@10459
   206
    IOHIDElementRef elem = IOHIDValueGetElement(value);
slouken@10459
   207
    if (IOHIDElementGetUsagePage(elem) != kHIDPage_KeyboardOrKeypad
slouken@10459
   208
        || IOHIDElementGetUsage(elem) != kHIDUsage_KeyboardCapsLock) {
slouken@10459
   209
        return;
slouken@10459
   210
    }
slouken@10519
   211
    CFIndex pressed = IOHIDValueGetIntegerValue(value);
slouken@10459
   212
    SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
slouken@10459
   213
}
slouken@10459
   214
slouken@10459
   215
static CFDictionaryRef
slouken@10459
   216
CreateHIDDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage)
slouken@10459
   217
{
slouken@10459
   218
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
slouken@10459
   219
        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
slouken@10459
   220
    if (dict) {
slouken@10459
   221
        CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
slouken@10459
   222
        if (number) {
slouken@10459
   223
            CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
slouken@10459
   224
            CFRelease(number);
slouken@10459
   225
            number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
slouken@10459
   226
            if (number) {
slouken@10459
   227
                CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
slouken@10459
   228
                CFRelease(number);
slouken@10459
   229
                return dict;
slouken@10459
   230
            }
slouken@10459
   231
        }
slouken@10459
   232
        CFRelease(dict);
slouken@10459
   233
    }
slouken@10459
   234
    return NULL;
slouken@10459
   235
}
slouken@10459
   236
slouken@10459
   237
static void
slouken@10459
   238
QuitHIDCallback()
slouken@10459
   239
{
slouken@10459
   240
    if (!s_hidManager) {
slouken@10459
   241
        return;
slouken@10459
   242
    }
slouken@10554
   243
slouken@10554
   244
#if 0 /* Releasing here causes a crash on Mac OS X 10.10 and earlier,
slouken@10554
   245
       * so just leak it for now. See bug 2157 for details.
slouken@10554
   246
       */
slouken@10459
   247
    IOHIDManagerUnscheduleFromRunLoop(s_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
slouken@10459
   248
    IOHIDManagerRegisterInputValueCallback(s_hidManager, NULL, NULL);
slouken@10459
   249
    IOHIDManagerClose(s_hidManager, 0);
slouken@10552
   250
slouken@10459
   251
    CFRelease(s_hidManager);
slouken@10552
   252
#endif
slouken@10459
   253
    s_hidManager = NULL;
slouken@10459
   254
}
slouken@10459
   255
slouken@10459
   256
static void
slouken@10459
   257
InitHIDCallback()
slouken@10459
   258
{
slouken@10459
   259
    s_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
slouken@10459
   260
    if (!s_hidManager) {
slouken@10459
   261
        return;
slouken@10459
   262
    }
slouken@10459
   263
    CFDictionaryRef keyboard = NULL, keypad = NULL;
slouken@10459
   264
    CFArrayRef matches = NULL;
slouken@10459
   265
    keyboard = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
slouken@10459
   266
    if (!keyboard) {
slouken@10459
   267
        goto fail;
slouken@10459
   268
    }
slouken@10459
   269
    keypad = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
slouken@10459
   270
    if (!keypad) {
slouken@10459
   271
        goto fail;
slouken@10459
   272
    }
slouken@10459
   273
    CFDictionaryRef matchesList[] = { keyboard, keypad };
slouken@10459
   274
    matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 2, NULL);
slouken@10459
   275
    if (!matches) {
slouken@10459
   276
        goto fail;
slouken@10459
   277
    }
slouken@10459
   278
    IOHIDManagerSetDeviceMatchingMultiple(s_hidManager, matches);
slouken@10554
   279
    IOHIDManagerRegisterInputValueCallback(s_hidManager, HIDCallback, s_hidManager);
slouken@10459
   280
    IOHIDManagerScheduleWithRunLoop(s_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
slouken@10459
   281
    if (IOHIDManagerOpen(s_hidManager, kIOHIDOptionsTypeNone) == kIOReturnSuccess) {
slouken@10459
   282
        goto cleanup;
slouken@10459
   283
    }
slouken@10459
   284
slouken@10459
   285
fail:
slouken@10459
   286
    QuitHIDCallback();
slouken@10459
   287
slouken@10459
   288
cleanup:
slouken@10459
   289
    if (matches) {
slouken@10459
   290
        CFRelease(matches);
slouken@10459
   291
    }
slouken@10459
   292
    if (keypad) {
slouken@10459
   293
        CFRelease(keypad);
slouken@10459
   294
    }
slouken@10459
   295
    if (keyboard) {
slouken@10459
   296
        CFRelease(keyboard);
slouken@10459
   297
    }
slouken@10459
   298
}
slouken@10459
   299
slouken@7191
   300
/* This is a helper function for HandleModifierSide. This
slouken@1959
   301
 * function reverts back to behavior before the distinction between
slouken@1959
   302
 * sides was made.
slouken@1959
   303
 */
slouken@1959
   304
static void
slouken@4465
   305
HandleNonDeviceModifier(unsigned int device_independent_mask,
slouken@1959
   306
                        unsigned int oldMods,
slouken@1959
   307
                        unsigned int newMods,
slouken@5218
   308
                        SDL_Scancode scancode)
slouken@1959
   309
{
slouken@1959
   310
    unsigned int oldMask, newMask;
slouken@7191
   311
slouken@7191
   312
    /* Isolate just the bits we care about in the depedent bits so we can
slouken@1959
   313
     * figure out what changed
slouken@7191
   314
     */
slouken@1959
   315
    oldMask = oldMods & device_independent_mask;
slouken@1959
   316
    newMask = newMods & device_independent_mask;
slouken@7191
   317
slouken@1959
   318
    if (oldMask && oldMask != newMask) {
slouken@4465
   319
        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@1959
   320
    } else if (newMask && oldMask != newMask) {
slouken@4465
   321
        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@1959
   322
    }
slouken@1959
   323
}
slouken@1959
   324
slouken@7191
   325
/* This is a helper function for HandleModifierSide.
slouken@1959
   326
 * This function sets the actual SDL_PrivateKeyboard event.
slouken@1959
   327
 */
slouken@1959
   328
static void
slouken@4465
   329
HandleModifierOneSide(unsigned int oldMods, unsigned int newMods,
slouken@7191
   330
                      SDL_Scancode scancode,
slouken@1959
   331
                      unsigned int sided_device_dependent_mask)
slouken@1959
   332
{
slouken@1959
   333
    unsigned int old_dep_mask, new_dep_mask;
slouken@1959
   334
slouken@7191
   335
    /* Isolate just the bits we care about in the depedent bits so we can
slouken@1959
   336
     * figure out what changed
slouken@7191
   337
     */
slouken@1959
   338
    old_dep_mask = oldMods & sided_device_dependent_mask;
slouken@1959
   339
    new_dep_mask = newMods & sided_device_dependent_mask;
slouken@1959
   340
slouken@1959
   341
    /* We now know that this side bit flipped. But we don't know if
slouken@7191
   342
     * it went pressed to released or released to pressed, so we must
slouken@1959
   343
     * find out which it is.
slouken@1959
   344
     */
slouken@1959
   345
    if (new_dep_mask && old_dep_mask != new_dep_mask) {
slouken@4465
   346
        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@1959
   347
    } else {
slouken@4465
   348
        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@1959
   349
    }
slouken@1959
   350
}
slouken@1959
   351
slouken@1959
   352
/* This is a helper function for DoSidedModifiers.
slouken@7191
   353
 * This function will figure out if the modifier key is the left or right side,
slouken@7191
   354
 * e.g. left-shift vs right-shift.
slouken@1959
   355
 */
slouken@1959
   356
static void
slouken@7191
   357
HandleModifierSide(int device_independent_mask,
slouken@7191
   358
                   unsigned int oldMods, unsigned int newMods,
slouken@7191
   359
                   SDL_Scancode left_scancode,
slouken@5218
   360
                   SDL_Scancode right_scancode,
slouken@7191
   361
                   unsigned int left_device_dependent_mask,
slouken@1959
   362
                   unsigned int right_device_dependent_mask)
slouken@1959
   363
{
slouken@1959
   364
    unsigned int device_dependent_mask = (left_device_dependent_mask |
slouken@1959
   365
                                         right_device_dependent_mask);
slouken@1959
   366
    unsigned int diff_mod;
slouken@7191
   367
slouken@7191
   368
    /* On the basis that the device independent mask is set, but there are
slouken@7191
   369
     * no device dependent flags set, we'll assume that we can't detect this
slouken@1959
   370
     * keyboard and revert to the unsided behavior.
slouken@1959
   371
     */
slouken@1959
   372
    if ((device_dependent_mask & newMods) == 0) {
slouken@1959
   373
        /* Revert to the old behavior */
slouken@4465
   374
        HandleNonDeviceModifier(device_independent_mask, oldMods, newMods, left_scancode);
slouken@1959
   375
        return;
slouken@1959
   376
    }
slouken@1959
   377
slouken@1959
   378
    /* XOR the previous state against the new state to see if there's a change */
slouken@1959
   379
    diff_mod = (device_dependent_mask & oldMods) ^
slouken@1959
   380
               (device_dependent_mask & newMods);
slouken@1959
   381
    if (diff_mod) {
slouken@7191
   382
        /* A change in state was found. Isolate the left and right bits
slouken@1959
   383
         * to handle them separately just in case the values can simulataneously
slouken@1959
   384
         * change or if the bits don't both exist.
slouken@1959
   385
         */
slouken@1959
   386
        if (left_device_dependent_mask & diff_mod) {
slouken@4465
   387
            HandleModifierOneSide(oldMods, newMods, left_scancode, left_device_dependent_mask);
slouken@1959
   388
        }
slouken@1959
   389
        if (right_device_dependent_mask & diff_mod) {
slouken@4465
   390
            HandleModifierOneSide(oldMods, newMods, right_scancode, right_device_dependent_mask);
slouken@1959
   391
        }
slouken@1959
   392
    }
slouken@1959
   393
}
slouken@7191
   394
slouken@1959
   395
/* This is a helper function for DoSidedModifiers.
slouken@7191
   396
 * This function will release a key press in the case that
slouken@7191
   397
 * it is clear that the modifier has been released (i.e. one side
slouken@1959
   398
 * can't still be down).
slouken@1959
   399
 */
slouken@1959
   400
static void
slouken@7191
   401
ReleaseModifierSide(unsigned int device_independent_mask,
slouken@1959
   402
                    unsigned int oldMods, unsigned int newMods,
slouken@7191
   403
                    SDL_Scancode left_scancode,
slouken@5218
   404
                    SDL_Scancode right_scancode,
slouken@7191
   405
                    unsigned int left_device_dependent_mask,
slouken@1959
   406
                    unsigned int right_device_dependent_mask)
slouken@1959
   407
{
slouken@1959
   408
    unsigned int device_dependent_mask = (left_device_dependent_mask |
slouken@1959
   409
                                          right_device_dependent_mask);
slouken@1959
   410
slouken@7191
   411
    /* On the basis that the device independent mask is set, but there are
slouken@7191
   412
     * no device dependent flags set, we'll assume that we can't detect this
slouken@1959
   413
     * keyboard and revert to the unsided behavior.
slouken@1959
   414
     */
slouken@1959
   415
    if ((device_dependent_mask & oldMods) == 0) {
slouken@7191
   416
        /* In this case, we can't detect the keyboard, so use the left side
slouken@7191
   417
         * to represent both, and release it.
slouken@1959
   418
         */
slouken@4465
   419
        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
slouken@1959
   420
        return;
slouken@1959
   421
    }
slouken@1959
   422
slouken@7191
   423
    /*
slouken@1959
   424
     * This could have been done in an if-else case because at this point,
slouken@7191
   425
     * we know that all keys have been released when calling this function.
slouken@1959
   426
     * But I'm being paranoid so I want to handle each separately,
slouken@1959
   427
     * so I hope this doesn't cause other problems.
slouken@1959
   428
     */
slouken@1959
   429
    if ( left_device_dependent_mask & oldMods ) {
slouken@4465
   430
        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
slouken@1959
   431
    }
slouken@1959
   432
    if ( right_device_dependent_mask & oldMods ) {
slouken@4465
   433
        SDL_SendKeyboardKey(SDL_RELEASED, right_scancode);
slouken@1959
   434
    }
slouken@1959
   435
}
slouken@1959
   436
slouken@7191
   437
/* This function will handle the modifier keys and also determine the
slouken@1959
   438
 * correct side of the key.
slouken@1959
   439
 */
slouken@1959
   440
static void
slouken@4465
   441
DoSidedModifiers(unsigned short scancode,
slouken@1959
   442
                 unsigned int oldMods, unsigned int newMods)
slouken@1959
   443
{
slouken@7191
   444
    /* Set up arrays for the key syms for the left and right side. */
slouken@5218
   445
    const SDL_Scancode left_mapping[]  = {
slouken@2303
   446
        SDL_SCANCODE_LSHIFT,
slouken@2303
   447
        SDL_SCANCODE_LCTRL,
slouken@2303
   448
        SDL_SCANCODE_LALT,
slouken@2303
   449
        SDL_SCANCODE_LGUI
slouken@2303
   450
    };
slouken@5218
   451
    const SDL_Scancode right_mapping[] = {
slouken@2303
   452
        SDL_SCANCODE_RSHIFT,
slouken@2303
   453
        SDL_SCANCODE_RCTRL,
slouken@2303
   454
        SDL_SCANCODE_RALT,
slouken@2303
   455
        SDL_SCANCODE_RGUI
slouken@2303
   456
    };
slouken@7191
   457
    /* Set up arrays for the device dependent masks with indices that
slouken@7191
   458
     * correspond to the _mapping arrays
slouken@1959
   459
     */
slouken@1959
   460
    const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
slouken@1959
   461
    const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
slouken@1959
   462
slouken@1959
   463
    unsigned int i, bit;
slouken@1959
   464
slouken@1959
   465
    /* Iterate through the bits, testing each against the old modifiers */
slime73@11144
   466
    for (i = 0, bit = NSEventModifierFlagShift; bit <= NSEventModifierFlagCommand; bit <<= 1, ++i) {
slouken@1959
   467
        unsigned int oldMask, newMask;
slouken@7191
   468
slouken@1959
   469
        oldMask = oldMods & bit;
slouken@1959
   470
        newMask = newMods & bit;
slouken@7191
   471
slouken@1959
   472
        /* If the bit is set, we must always examine it because the left
slouken@1959
   473
         * and right side keys may alternate or both may be pressed.
slouken@1959
   474
         */
slouken@1959
   475
        if (newMask) {
slouken@4465
   476
            HandleModifierSide(bit, oldMods, newMods,
slouken@1959
   477
                               left_mapping[i], right_mapping[i],
slouken@1959
   478
                               left_device_mapping[i], right_device_mapping[i]);
slouken@1959
   479
        }
slouken@1959
   480
        /* If the state changed from pressed to unpressed, we must examine
slouken@1959
   481
            * the device dependent bits to release the correct keys.
slouken@1959
   482
            */
slouken@1959
   483
        else if (oldMask && oldMask != newMask) {
slouken@4465
   484
            ReleaseModifierSide(bit, oldMods, newMods,
slouken@1959
   485
                              left_mapping[i], right_mapping[i],
slouken@1959
   486
                              left_device_mapping[i], right_device_mapping[i]);
slouken@1959
   487
        }
slouken@1959
   488
    }
slouken@1959
   489
}
slouken@1959
   490
slouken@1959
   491
static void
slouken@1959
   492
HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags)
slouken@1959
   493
{
slouken@1959
   494
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@1959
   495
slouken@1959
   496
    if (modifierFlags == data->modifierFlags) {
slouken@7191
   497
        return;
slouken@1959
   498
    }
slouken@1959
   499
slouken@7947
   500
    DoSidedModifiers(scancode, data->modifierFlags, modifierFlags);
slouken@1959
   501
    data->modifierFlags = modifierFlags;
slouken@1959
   502
}
slouken@1959
   503
slouken@2303
   504
static void
slouken@9898
   505
UpdateKeymap(SDL_VideoData *data, SDL_bool send_event)
slouken@2303
   506
{
slouken@3247
   507
    TISInputSourceRef key_layout;
slouken@2303
   508
    const void *chr_data;
slouken@2303
   509
    int i;
slouken@5218
   510
    SDL_Scancode scancode;
slouken@5220
   511
    SDL_Keycode keymap[SDL_NUM_SCANCODES];
slouken@2303
   512
slouken@2303
   513
    /* See if the keymap needs to be updated */
slouken@3247
   514
    key_layout = TISCopyCurrentKeyboardLayoutInputSource();
slouken@2303
   515
    if (key_layout == data->key_layout) {
slouken@2303
   516
        return;
slouken@2303
   517
    }
slouken@2303
   518
    data->key_layout = key_layout;
slouken@2303
   519
slouken@2303
   520
    SDL_GetDefaultKeymap(keymap);
slouken@2303
   521
jorgen@7507
   522
    /* Try Unicode data first */
slouken@3247
   523
    CFDataRef uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData);
slouken@8986
   524
    if (uchrDataRef) {
slouken@3247
   525
        chr_data = CFDataGetBytePtr(uchrDataRef);
slouken@8986
   526
    } else {
slouken@3247
   527
        goto cleanup;
slouken@8986
   528
    }
jorgen@7507
   529
slouken@2303
   530
    if (chr_data) {
slouken@2303
   531
        UInt32 keyboard_type = LMGetKbdType();
slouken@2303
   532
        OSStatus err;
slouken@2303
   533
slouken@2305
   534
        for (i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
slouken@2303
   535
            UniChar s[8];
slouken@2303
   536
            UniCharCount len;
slouken@2303
   537
            UInt32 dead_key_state;
slouken@2303
   538
slouken@2303
   539
            /* Make sure this scancode is a valid character scancode */
slouken@2305
   540
            scancode = darwin_scancode_table[i];
slouken@2303
   541
            if (scancode == SDL_SCANCODE_UNKNOWN ||
slouken@2303
   542
                (keymap[scancode] & SDLK_SCANCODE_MASK)) {
slouken@2303
   543
                continue;
slouken@2303
   544
            }
slouken@2303
   545
slouken@2303
   546
            dead_key_state = 0;
slouken@3247
   547
            err = UCKeyTranslate ((UCKeyboardLayout *) chr_data,
slouken@3247
   548
                                  i, kUCKeyActionDown,
slouken@2303
   549
                                  0, keyboard_type,
slouken@2303
   550
                                  kUCKeyTranslateNoDeadKeysMask,
slouken@2303
   551
                                  &dead_key_state, 8, &len, s);
slouken@8986
   552
            if (err != noErr) {
slouken@2303
   553
                continue;
slouken@8986
   554
            }
slouken@2303
   555
slouken@2303
   556
            if (len > 0 && s[0] != 0x10) {
slouken@2303
   557
                keymap[scancode] = s[0];
slouken@2303
   558
            }
slouken@2303
   559
        }
slouken@4465
   560
        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
slouken@9898
   561
        if (send_event) {
slouken@9898
   562
            SDL_SendKeymapChangedEvent();
slouken@9898
   563
        }
slouken@2303
   564
        return;
slouken@2303
   565
    }
slouken@2303
   566
slouken@3247
   567
cleanup:
slouken@3247
   568
    CFRelease(key_layout);
slouken@2303
   569
}
slouken@2303
   570
slouken@1931
   571
void
slouken@1931
   572
Cocoa_InitKeyboard(_THIS)
slouken@1931
   573
{
slouken@1931
   574
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@1931
   575
slouken@9898
   576
    UpdateKeymap(data, SDL_FALSE);
slouken@7191
   577
slouken@2268
   578
    /* Set our own names for the platform-dependent but layout-independent keys */
slouken@2303
   579
    /* This key is NumLock on the MacBook keyboard. :) */
slouken@2303
   580
    /*SDL_SetScancodeName(SDL_SCANCODE_NUMLOCKCLEAR, "Clear");*/
slouken@2303
   581
    SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option");
slouken@2303
   582
    SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command");
slouken@2303
   583
    SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option");
slouken@2303
   584
    SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command");
icculus@9967
   585
slime73@10176
   586
    data->modifierFlags = [NSEvent modifierFlags];
slime73@11144
   587
    SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSEventModifierFlagCapsLock) != 0);
slouken@10459
   588
slouken@10459
   589
    InitHIDCallback();
slouken@1931
   590
}
slouken@1931
   591
slouken@1931
   592
void
slouken@3280
   593
Cocoa_StartTextInput(_THIS)
slouken@9087
   594
{ @autoreleasepool
slouken@3280
   595
{
slouken@3280
   596
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
jorgen@8141
   597
    SDL_Window *window = SDL_GetKeyboardFocus();
jorgen@8141
   598
    NSWindow *nswindow = nil;
slouken@8986
   599
    if (window) {
jorgen@8141
   600
        nswindow = ((SDL_WindowData*)window->driverdata)->nswindow;
slouken@8986
   601
    }
jorgen@8141
   602
jorgen@8141
   603
    NSView *parentView = [nswindow contentView];
slouken@3280
   604
slouken@6848
   605
    /* We only keep one field editor per process, since only the front most
slouken@6848
   606
     * window can receive text input events, so it make no sense to keep more
slouken@6848
   607
     * than one copy. When we switched to another window and requesting for
slouken@6848
   608
     * text input, simply remove the field editor from its superview then add
slouken@6848
   609
     * it to the front most window's content view */
slouken@6848
   610
    if (!data->fieldEdit) {
slouken@6848
   611
        data->fieldEdit =
slouken@6848
   612
            [[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
slouken@6848
   613
    }
alexey@6832
   614
slime73@9959
   615
    if (![[data->fieldEdit superview] isEqual:parentView]) {
slouken@7191
   616
        /* DEBUG_IME(@"add fieldEdit to window contentView"); */
slouken@6848
   617
        [data->fieldEdit removeFromSuperview];
slouken@6848
   618
        [parentView addSubview: data->fieldEdit];
jorgen@8141
   619
        [nswindow makeFirstResponder: data->fieldEdit];
slouken@4435
   620
    }
slouken@9087
   621
}}
slouken@3280
   622
slouken@3280
   623
void
slouken@3280
   624
Cocoa_StopTextInput(_THIS)
slouken@9087
   625
{ @autoreleasepool
slouken@3280
   626
{
slouken@3280
   627
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@3280
   628
slouken@3683
   629
    if (data && data->fieldEdit) {
slouken@6848
   630
        [data->fieldEdit removeFromSuperview];
slouken@6848
   631
        [data->fieldEdit release];
slouken@6848
   632
        data->fieldEdit = nil;
slouken@3683
   633
    }
slouken@9087
   634
}}
slouken@3280
   635
slouken@3280
   636
void
slouken@3280
   637
Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect)
slouken@3280
   638
{
slouken@3280
   639
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@3280
   640
aschiffler@6808
   641
    if (!rect) {
slouken@8986
   642
        SDL_InvalidParamError("rect");
slouken@8986
   643
        return;
aschiffler@6808
   644
    }
aschiffler@6808
   645
slime73@9959
   646
    [data->fieldEdit setInputRect:rect];
slouken@3280
   647
}
slouken@3280
   648
slouken@3280
   649
void
slouken@1959
   650
Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
slouken@1959
   651
{
icculus@10986
   652
    SDL_VideoData *data = _this ? ((SDL_VideoData *) _this->driverdata) : NULL;
icculus@8862
   653
    if (!data) {
icculus@8862
   654
        return;  /* can happen when returning from fullscreen Space on shutdown */
icculus@8862
   655
    }
icculus@8862
   656
slouken@1959
   657
    unsigned short scancode = [event keyCode];
slouken@5218
   658
    SDL_Scancode code;
slouken@4465
   659
#if 0
slouken@1959
   660
    const char *text;
slouken@4465
   661
#endif
slouken@1959
   662
slouken@2268
   663
    if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
slouken@2268
   664
        /* see comments in SDL_cocoakeys.h */
slouken@2268
   665
        scancode = 60 - scancode;
slouken@2268
   666
    }
slouken@8986
   667
slouken@2305
   668
    if (scancode < SDL_arraysize(darwin_scancode_table)) {
slouken@2305
   669
        code = darwin_scancode_table[scancode];
slouken@8986
   670
    } else {
slouken@1959
   671
        /* Hmm, does this ever happen?  If so, need to extend the keymap... */
slouken@2303
   672
        code = SDL_SCANCODE_UNKNOWN;
slouken@1959
   673
    }
slouken@1959
   674
slouken@1959
   675
    switch ([event type]) {
slime73@11144
   676
    case NSEventTypeKeyDown:
slouken@2129
   677
        if (![event isARepeat]) {
slouken@2303
   678
            /* See if we need to rebuild the keyboard layout */
slouken@9898
   679
            UpdateKeymap(data, SDL_TRUE);
slouken@4673
   680
        }
slouken@2303
   681
slouken@4673
   682
        SDL_SendKeyboardKey(SDL_PRESSED, code);
slouken@2268
   683
#if 1
slouken@4673
   684
        if (code == SDL_SCANCODE_UNKNOWN) {
philipp@11035
   685
            fprintf(stderr, "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);
slouken@4673
   686
        }
slouken@2268
   687
#endif
slouken@2129
   688
        if (SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) {
slouken@2268
   689
            /* FIXME CW 2007-08-16: only send those events to the field editor for which we actually want text events, not e.g. esc or function keys. Arrow keys in particular seem to produce crashes sometimes. */
slouken@2169
   690
            [data->fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
slouken@3280
   691
#if 0
slouken@2129
   692
            text = [[event characters] UTF8String];
slouken@2129
   693
            if(text && *text) {
slouken@4465
   694
                SDL_SendKeyboardText(text);
slouken@2273
   695
                [data->fieldEdit setString:@""];
slouken@2129
   696
            }
slouken@3280
   697
#endif
slouken@1959
   698
        }
slouken@1959
   699
        break;
slime73@11144
   700
    case NSEventTypeKeyUp:
slouken@4465
   701
        SDL_SendKeyboardKey(SDL_RELEASED, code);
slouken@1959
   702
        break;
slime73@11144
   703
    case NSEventTypeFlagsChanged:
slouken@2268
   704
        /* FIXME CW 2007-08-14: check if this whole mess that takes up half of this file is really necessary */
slouken@1959
   705
        HandleModifiers(_this, scancode, [event modifierFlags]);
slouken@1959
   706
        break;
slouken@2268
   707
    default: /* just to avoid compiler warnings */
slouken@2268
   708
        break;
slouken@2268
   709
    }
slouken@2268
   710
}
slouken@2268
   711
slouken@1959
   712
void
slouken@1931
   713
Cocoa_QuitKeyboard(_THIS)
slouken@1931
   714
{
slouken@10459
   715
    QuitHIDCallback();
slouken@1931
   716
}
slouken@1931
   717
slouken@6044
   718
#endif /* SDL_VIDEO_DRIVER_COCOA */
slouken@6044
   719
slouken@1931
   720
/* vi: set ts=4 sw=4 expandtab: */