src/video/cocoa/SDL_cocoakeyboard.m
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
child 10986 b93491afe676
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
slouken@1931
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 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
slime73@9959
    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
slime73@9959
   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@10177
   146
    if ([window respondsToSelector:@selector(convertRectToScreen:)]) {
slime73@10177
   147
        rect = [window convertRectToScreen:rect];
slime73@9959
   148
    } else {
slime73@10177
   149
        rect.origin = [window convertBaseToScreen:rect.origin];
slime73@9959
   150
    }
slouken@3280
   151
slouken@3280
   152
    return rect;
slouken@3280
   153
}
slouken@3280
   154
slime73@9959
   155
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
slouken@3280
   156
{
slime73@9959
   157
    DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length);
slouken@3280
   158
    return nil;
slouken@3280
   159
}
slouken@3280
   160
slime73@9959
   161
- (NSInteger)conversationIdentifier
slouken@3280
   162
{
jorgen@7507
   163
    return (NSInteger) self;
slouken@3280
   164
}
slouken@3280
   165
slouken@7191
   166
/* This method returns the index for character that is
slouken@7191
   167
 * nearest to thePoint.  thPoint is in screen coordinate system.
slouken@7191
   168
 */
slime73@9959
   169
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
slouken@3280
   170
{
slouken@3280
   171
    DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
slouken@3280
   172
    return 0;
slouken@3280
   173
}
slouken@3280
   174
slouken@7191
   175
/* This method is the key to attribute extension.
slouken@7191
   176
 * We could add new attributes through this method.
slouken@7191
   177
 * NSInputServer examines the return value of this
slouken@7191
   178
 * method & constructs appropriate attributed string.
slouken@7191
   179
 */
slime73@9959
   180
- (NSArray *)validAttributesForMarkedText
slouken@3280
   181
{
slouken@3280
   182
    return [NSArray array];
slouken@3280
   183
}
slouken@3280
   184
slouken@2289
   185
@end
slouken@2289
   186
slouken@10459
   187
/*------------------------------------------------------------------------------
slouken@10459
   188
Set up a HID callback to properly detect Caps Lock up/down events.
slouken@10459
   189
Derived from:
slouken@10459
   190
http://stackoverflow.com/questions/7190852/using-iohidmanager-to-get-modifier-key-events
slouken@10459
   191
*/
slouken@10459
   192
slouken@10459
   193
static IOHIDManagerRef s_hidManager = NULL;
slouken@10459
   194
slouken@10459
   195
static void
slouken@10459
   196
HIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
slouken@10459
   197
{
slouken@10554
   198
    if (context != s_hidManager) {
slouken@10554
   199
        /* An old callback, ignore it (related to bug 2157 below) */
slouken@10554
   200
        return;
slouken@10554
   201
    }
slouken@10554
   202
slouken@10459
   203
    IOHIDElementRef elem = IOHIDValueGetElement(value);
slouken@10459
   204
    if (IOHIDElementGetUsagePage(elem) != kHIDPage_KeyboardOrKeypad
slouken@10459
   205
        || IOHIDElementGetUsage(elem) != kHIDUsage_KeyboardCapsLock) {
slouken@10459
   206
        return;
slouken@10459
   207
    }
slouken@10519
   208
    CFIndex pressed = IOHIDValueGetIntegerValue(value);
slouken@10459
   209
    SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
slouken@10459
   210
}
slouken@10459
   211
slouken@10459
   212
static CFDictionaryRef
slouken@10459
   213
CreateHIDDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage)
slouken@10459
   214
{
slouken@10459
   215
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
slouken@10459
   216
        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
slouken@10459
   217
    if (dict) {
slouken@10459
   218
        CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
slouken@10459
   219
        if (number) {
slouken@10459
   220
            CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
slouken@10459
   221
            CFRelease(number);
slouken@10459
   222
            number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
slouken@10459
   223
            if (number) {
slouken@10459
   224
                CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
slouken@10459
   225
                CFRelease(number);
slouken@10459
   226
                return dict;
slouken@10459
   227
            }
slouken@10459
   228
        }
slouken@10459
   229
        CFRelease(dict);
slouken@10459
   230
    }
slouken@10459
   231
    return NULL;
slouken@10459
   232
}
slouken@10459
   233
slouken@10459
   234
static void
slouken@10459
   235
QuitHIDCallback()
slouken@10459
   236
{
slouken@10459
   237
    if (!s_hidManager) {
slouken@10459
   238
        return;
slouken@10459
   239
    }
slouken@10554
   240
slouken@10554
   241
#if 0 /* Releasing here causes a crash on Mac OS X 10.10 and earlier,
slouken@10554
   242
       * so just leak it for now. See bug 2157 for details.
slouken@10554
   243
       */
slouken@10459
   244
    IOHIDManagerUnscheduleFromRunLoop(s_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
slouken@10459
   245
    IOHIDManagerRegisterInputValueCallback(s_hidManager, NULL, NULL);
slouken@10459
   246
    IOHIDManagerClose(s_hidManager, 0);
slouken@10552
   247
slouken@10459
   248
    CFRelease(s_hidManager);
slouken@10552
   249
#endif
slouken@10459
   250
    s_hidManager = NULL;
slouken@10459
   251
}
slouken@10459
   252
slouken@10459
   253
static void
slouken@10459
   254
InitHIDCallback()
slouken@10459
   255
{
slouken@10459
   256
    s_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
slouken@10459
   257
    if (!s_hidManager) {
slouken@10459
   258
        return;
slouken@10459
   259
    }
slouken@10459
   260
    CFDictionaryRef keyboard = NULL, keypad = NULL;
slouken@10459
   261
    CFArrayRef matches = NULL;
slouken@10459
   262
    keyboard = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
slouken@10459
   263
    if (!keyboard) {
slouken@10459
   264
        goto fail;
slouken@10459
   265
    }
slouken@10459
   266
    keypad = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
slouken@10459
   267
    if (!keypad) {
slouken@10459
   268
        goto fail;
slouken@10459
   269
    }
slouken@10459
   270
    CFDictionaryRef matchesList[] = { keyboard, keypad };
slouken@10459
   271
    matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 2, NULL);
slouken@10459
   272
    if (!matches) {
slouken@10459
   273
        goto fail;
slouken@10459
   274
    }
slouken@10459
   275
    IOHIDManagerSetDeviceMatchingMultiple(s_hidManager, matches);
slouken@10554
   276
    IOHIDManagerRegisterInputValueCallback(s_hidManager, HIDCallback, s_hidManager);
slouken@10459
   277
    IOHIDManagerScheduleWithRunLoop(s_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
slouken@10459
   278
    if (IOHIDManagerOpen(s_hidManager, kIOHIDOptionsTypeNone) == kIOReturnSuccess) {
slouken@10459
   279
        goto cleanup;
slouken@10459
   280
    }
slouken@10459
   281
slouken@10459
   282
fail:
slouken@10459
   283
    QuitHIDCallback();
slouken@10459
   284
slouken@10459
   285
cleanup:
slouken@10459
   286
    if (matches) {
slouken@10459
   287
        CFRelease(matches);
slouken@10459
   288
    }
slouken@10459
   289
    if (keypad) {
slouken@10459
   290
        CFRelease(keypad);
slouken@10459
   291
    }
slouken@10459
   292
    if (keyboard) {
slouken@10459
   293
        CFRelease(keyboard);
slouken@10459
   294
    }
slouken@10459
   295
}
slouken@10459
   296
slouken@7191
   297
/* This is a helper function for HandleModifierSide. This
slouken@1959
   298
 * function reverts back to behavior before the distinction between
slouken@1959
   299
 * sides was made.
slouken@1959
   300
 */
slouken@1959
   301
static void
slouken@4465
   302
HandleNonDeviceModifier(unsigned int device_independent_mask,
slouken@1959
   303
                        unsigned int oldMods,
slouken@1959
   304
                        unsigned int newMods,
slouken@5218
   305
                        SDL_Scancode scancode)
slouken@1959
   306
{
slouken@1959
   307
    unsigned int oldMask, newMask;
slouken@7191
   308
slouken@7191
   309
    /* Isolate just the bits we care about in the depedent bits so we can
slouken@1959
   310
     * figure out what changed
slouken@7191
   311
     */
slouken@1959
   312
    oldMask = oldMods & device_independent_mask;
slouken@1959
   313
    newMask = newMods & device_independent_mask;
slouken@7191
   314
slouken@1959
   315
    if (oldMask && oldMask != newMask) {
slouken@4465
   316
        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@1959
   317
    } else if (newMask && oldMask != newMask) {
slouken@4465
   318
        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@1959
   319
    }
slouken@1959
   320
}
slouken@1959
   321
slouken@7191
   322
/* This is a helper function for HandleModifierSide.
slouken@1959
   323
 * This function sets the actual SDL_PrivateKeyboard event.
slouken@1959
   324
 */
slouken@1959
   325
static void
slouken@4465
   326
HandleModifierOneSide(unsigned int oldMods, unsigned int newMods,
slouken@7191
   327
                      SDL_Scancode scancode,
slouken@1959
   328
                      unsigned int sided_device_dependent_mask)
slouken@1959
   329
{
slouken@1959
   330
    unsigned int old_dep_mask, new_dep_mask;
slouken@1959
   331
slouken@7191
   332
    /* Isolate just the bits we care about in the depedent bits so we can
slouken@1959
   333
     * figure out what changed
slouken@7191
   334
     */
slouken@1959
   335
    old_dep_mask = oldMods & sided_device_dependent_mask;
slouken@1959
   336
    new_dep_mask = newMods & sided_device_dependent_mask;
slouken@1959
   337
slouken@1959
   338
    /* We now know that this side bit flipped. But we don't know if
slouken@7191
   339
     * it went pressed to released or released to pressed, so we must
slouken@1959
   340
     * find out which it is.
slouken@1959
   341
     */
slouken@1959
   342
    if (new_dep_mask && old_dep_mask != new_dep_mask) {
slouken@4465
   343
        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@1959
   344
    } else {
slouken@4465
   345
        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@1959
   346
    }
slouken@1959
   347
}
slouken@1959
   348
slouken@1959
   349
/* This is a helper function for DoSidedModifiers.
slouken@7191
   350
 * This function will figure out if the modifier key is the left or right side,
slouken@7191
   351
 * e.g. left-shift vs right-shift.
slouken@1959
   352
 */
slouken@1959
   353
static void
slouken@7191
   354
HandleModifierSide(int device_independent_mask,
slouken@7191
   355
                   unsigned int oldMods, unsigned int newMods,
slouken@7191
   356
                   SDL_Scancode left_scancode,
slouken@5218
   357
                   SDL_Scancode right_scancode,
slouken@7191
   358
                   unsigned int left_device_dependent_mask,
slouken@1959
   359
                   unsigned int right_device_dependent_mask)
slouken@1959
   360
{
slouken@1959
   361
    unsigned int device_dependent_mask = (left_device_dependent_mask |
slouken@1959
   362
                                         right_device_dependent_mask);
slouken@1959
   363
    unsigned int diff_mod;
slouken@7191
   364
slouken@7191
   365
    /* On the basis that the device independent mask is set, but there are
slouken@7191
   366
     * no device dependent flags set, we'll assume that we can't detect this
slouken@1959
   367
     * keyboard and revert to the unsided behavior.
slouken@1959
   368
     */
slouken@1959
   369
    if ((device_dependent_mask & newMods) == 0) {
slouken@1959
   370
        /* Revert to the old behavior */
slouken@4465
   371
        HandleNonDeviceModifier(device_independent_mask, oldMods, newMods, left_scancode);
slouken@1959
   372
        return;
slouken@1959
   373
    }
slouken@1959
   374
slouken@1959
   375
    /* XOR the previous state against the new state to see if there's a change */
slouken@1959
   376
    diff_mod = (device_dependent_mask & oldMods) ^
slouken@1959
   377
               (device_dependent_mask & newMods);
slouken@1959
   378
    if (diff_mod) {
slouken@7191
   379
        /* A change in state was found. Isolate the left and right bits
slouken@1959
   380
         * to handle them separately just in case the values can simulataneously
slouken@1959
   381
         * change or if the bits don't both exist.
slouken@1959
   382
         */
slouken@1959
   383
        if (left_device_dependent_mask & diff_mod) {
slouken@4465
   384
            HandleModifierOneSide(oldMods, newMods, left_scancode, left_device_dependent_mask);
slouken@1959
   385
        }
slouken@1959
   386
        if (right_device_dependent_mask & diff_mod) {
slouken@4465
   387
            HandleModifierOneSide(oldMods, newMods, right_scancode, right_device_dependent_mask);
slouken@1959
   388
        }
slouken@1959
   389
    }
slouken@1959
   390
}
slouken@7191
   391
slouken@1959
   392
/* This is a helper function for DoSidedModifiers.
slouken@7191
   393
 * This function will release a key press in the case that
slouken@7191
   394
 * it is clear that the modifier has been released (i.e. one side
slouken@1959
   395
 * can't still be down).
slouken@1959
   396
 */
slouken@1959
   397
static void
slouken@7191
   398
ReleaseModifierSide(unsigned int device_independent_mask,
slouken@1959
   399
                    unsigned int oldMods, unsigned int newMods,
slouken@7191
   400
                    SDL_Scancode left_scancode,
slouken@5218
   401
                    SDL_Scancode right_scancode,
slouken@7191
   402
                    unsigned int left_device_dependent_mask,
slouken@1959
   403
                    unsigned int right_device_dependent_mask)
slouken@1959
   404
{
slouken@1959
   405
    unsigned int device_dependent_mask = (left_device_dependent_mask |
slouken@1959
   406
                                          right_device_dependent_mask);
slouken@1959
   407
slouken@7191
   408
    /* On the basis that the device independent mask is set, but there are
slouken@7191
   409
     * no device dependent flags set, we'll assume that we can't detect this
slouken@1959
   410
     * keyboard and revert to the unsided behavior.
slouken@1959
   411
     */
slouken@1959
   412
    if ((device_dependent_mask & oldMods) == 0) {
slouken@7191
   413
        /* In this case, we can't detect the keyboard, so use the left side
slouken@7191
   414
         * to represent both, and release it.
slouken@1959
   415
         */
slouken@4465
   416
        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
slouken@1959
   417
        return;
slouken@1959
   418
    }
slouken@1959
   419
slouken@7191
   420
    /*
slouken@1959
   421
     * This could have been done in an if-else case because at this point,
slouken@7191
   422
     * we know that all keys have been released when calling this function.
slouken@1959
   423
     * But I'm being paranoid so I want to handle each separately,
slouken@1959
   424
     * so I hope this doesn't cause other problems.
slouken@1959
   425
     */
slouken@1959
   426
    if ( left_device_dependent_mask & oldMods ) {
slouken@4465
   427
        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
slouken@1959
   428
    }
slouken@1959
   429
    if ( right_device_dependent_mask & oldMods ) {
slouken@4465
   430
        SDL_SendKeyboardKey(SDL_RELEASED, right_scancode);
slouken@1959
   431
    }
slouken@1959
   432
}
slouken@1959
   433
slouken@7191
   434
/* This function will handle the modifier keys and also determine the
slouken@1959
   435
 * correct side of the key.
slouken@1959
   436
 */
slouken@1959
   437
static void
slouken@4465
   438
DoSidedModifiers(unsigned short scancode,
slouken@1959
   439
                 unsigned int oldMods, unsigned int newMods)
slouken@1959
   440
{
slouken@7191
   441
    /* Set up arrays for the key syms for the left and right side. */
slouken@5218
   442
    const SDL_Scancode left_mapping[]  = {
slouken@2303
   443
        SDL_SCANCODE_LSHIFT,
slouken@2303
   444
        SDL_SCANCODE_LCTRL,
slouken@2303
   445
        SDL_SCANCODE_LALT,
slouken@2303
   446
        SDL_SCANCODE_LGUI
slouken@2303
   447
    };
slouken@5218
   448
    const SDL_Scancode right_mapping[] = {
slouken@2303
   449
        SDL_SCANCODE_RSHIFT,
slouken@2303
   450
        SDL_SCANCODE_RCTRL,
slouken@2303
   451
        SDL_SCANCODE_RALT,
slouken@2303
   452
        SDL_SCANCODE_RGUI
slouken@2303
   453
    };
slouken@7191
   454
    /* Set up arrays for the device dependent masks with indices that
slouken@7191
   455
     * correspond to the _mapping arrays
slouken@1959
   456
     */
slouken@1959
   457
    const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
slouken@1959
   458
    const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
slouken@1959
   459
slouken@1959
   460
    unsigned int i, bit;
slouken@1959
   461
slouken@1959
   462
    /* Iterate through the bits, testing each against the old modifiers */
slouken@1959
   463
    for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
slouken@1959
   464
        unsigned int oldMask, newMask;
slouken@7191
   465
slouken@1959
   466
        oldMask = oldMods & bit;
slouken@1959
   467
        newMask = newMods & bit;
slouken@7191
   468
slouken@1959
   469
        /* If the bit is set, we must always examine it because the left
slouken@1959
   470
         * and right side keys may alternate or both may be pressed.
slouken@1959
   471
         */
slouken@1959
   472
        if (newMask) {
slouken@4465
   473
            HandleModifierSide(bit, oldMods, newMods,
slouken@1959
   474
                               left_mapping[i], right_mapping[i],
slouken@1959
   475
                               left_device_mapping[i], right_device_mapping[i]);
slouken@1959
   476
        }
slouken@1959
   477
        /* If the state changed from pressed to unpressed, we must examine
slouken@1959
   478
            * the device dependent bits to release the correct keys.
slouken@1959
   479
            */
slouken@1959
   480
        else if (oldMask && oldMask != newMask) {
slouken@4465
   481
            ReleaseModifierSide(bit, oldMods, newMods,
slouken@1959
   482
                              left_mapping[i], right_mapping[i],
slouken@1959
   483
                              left_device_mapping[i], right_device_mapping[i]);
slouken@1959
   484
        }
slouken@1959
   485
    }
slouken@1959
   486
}
slouken@1959
   487
slouken@1959
   488
static void
slouken@1959
   489
HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags)
slouken@1959
   490
{
slouken@1959
   491
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@1959
   492
slouken@1959
   493
    if (modifierFlags == data->modifierFlags) {
slouken@7191
   494
        return;
slouken@1959
   495
    }
slouken@1959
   496
slouken@7947
   497
    DoSidedModifiers(scancode, data->modifierFlags, modifierFlags);
slouken@1959
   498
    data->modifierFlags = modifierFlags;
slouken@1959
   499
}
slouken@1959
   500
slouken@2303
   501
static void
slouken@9898
   502
UpdateKeymap(SDL_VideoData *data, SDL_bool send_event)
slouken@2303
   503
{
slouken@3247
   504
    TISInputSourceRef key_layout;
slouken@2303
   505
    const void *chr_data;
slouken@2303
   506
    int i;
slouken@5218
   507
    SDL_Scancode scancode;
slouken@5220
   508
    SDL_Keycode keymap[SDL_NUM_SCANCODES];
slouken@2303
   509
slouken@2303
   510
    /* See if the keymap needs to be updated */
slouken@3247
   511
    key_layout = TISCopyCurrentKeyboardLayoutInputSource();
slouken@2303
   512
    if (key_layout == data->key_layout) {
slouken@2303
   513
        return;
slouken@2303
   514
    }
slouken@2303
   515
    data->key_layout = key_layout;
slouken@2303
   516
slouken@2303
   517
    SDL_GetDefaultKeymap(keymap);
slouken@2303
   518
jorgen@7507
   519
    /* Try Unicode data first */
slouken@3247
   520
    CFDataRef uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData);
slouken@8986
   521
    if (uchrDataRef) {
slouken@3247
   522
        chr_data = CFDataGetBytePtr(uchrDataRef);
slouken@8986
   523
    } else {
slouken@3247
   524
        goto cleanup;
slouken@8986
   525
    }
jorgen@7507
   526
slouken@2303
   527
    if (chr_data) {
slouken@2303
   528
        UInt32 keyboard_type = LMGetKbdType();
slouken@2303
   529
        OSStatus err;
slouken@2303
   530
slouken@2305
   531
        for (i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
slouken@2303
   532
            UniChar s[8];
slouken@2303
   533
            UniCharCount len;
slouken@2303
   534
            UInt32 dead_key_state;
slouken@2303
   535
slouken@2303
   536
            /* Make sure this scancode is a valid character scancode */
slouken@2305
   537
            scancode = darwin_scancode_table[i];
slouken@2303
   538
            if (scancode == SDL_SCANCODE_UNKNOWN ||
slouken@2303
   539
                (keymap[scancode] & SDLK_SCANCODE_MASK)) {
slouken@2303
   540
                continue;
slouken@2303
   541
            }
slouken@2303
   542
slouken@2303
   543
            dead_key_state = 0;
slouken@3247
   544
            err = UCKeyTranslate ((UCKeyboardLayout *) chr_data,
slouken@3247
   545
                                  i, kUCKeyActionDown,
slouken@2303
   546
                                  0, keyboard_type,
slouken@2303
   547
                                  kUCKeyTranslateNoDeadKeysMask,
slouken@2303
   548
                                  &dead_key_state, 8, &len, s);
slouken@8986
   549
            if (err != noErr) {
slouken@2303
   550
                continue;
slouken@8986
   551
            }
slouken@2303
   552
slouken@2303
   553
            if (len > 0 && s[0] != 0x10) {
slouken@2303
   554
                keymap[scancode] = s[0];
slouken@2303
   555
            }
slouken@2303
   556
        }
slouken@4465
   557
        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
slouken@9898
   558
        if (send_event) {
slouken@9898
   559
            SDL_SendKeymapChangedEvent();
slouken@9898
   560
        }
slouken@2303
   561
        return;
slouken@2303
   562
    }
slouken@2303
   563
slouken@3247
   564
cleanup:
slouken@3247
   565
    CFRelease(key_layout);
slouken@2303
   566
}
slouken@2303
   567
slouken@1931
   568
void
slouken@1931
   569
Cocoa_InitKeyboard(_THIS)
slouken@1931
   570
{
slouken@1931
   571
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@1931
   572
slouken@9898
   573
    UpdateKeymap(data, SDL_FALSE);
slouken@7191
   574
slouken@2268
   575
    /* Set our own names for the platform-dependent but layout-independent keys */
slouken@2303
   576
    /* This key is NumLock on the MacBook keyboard. :) */
slouken@2303
   577
    /*SDL_SetScancodeName(SDL_SCANCODE_NUMLOCKCLEAR, "Clear");*/
slouken@2303
   578
    SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option");
slouken@2303
   579
    SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command");
slouken@2303
   580
    SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option");
slouken@2303
   581
    SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command");
icculus@9967
   582
slime73@10176
   583
    data->modifierFlags = [NSEvent modifierFlags];
slime73@10176
   584
    SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSAlphaShiftKeyMask) != 0);
slouken@10459
   585
slouken@10459
   586
    InitHIDCallback();
slouken@1931
   587
}
slouken@1931
   588
slouken@1931
   589
void
slouken@3280
   590
Cocoa_StartTextInput(_THIS)
slouken@9087
   591
{ @autoreleasepool
slouken@3280
   592
{
slouken@3280
   593
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
jorgen@8141
   594
    SDL_Window *window = SDL_GetKeyboardFocus();
jorgen@8141
   595
    NSWindow *nswindow = nil;
slouken@8986
   596
    if (window) {
jorgen@8141
   597
        nswindow = ((SDL_WindowData*)window->driverdata)->nswindow;
slouken@8986
   598
    }
jorgen@8141
   599
jorgen@8141
   600
    NSView *parentView = [nswindow contentView];
slouken@3280
   601
slouken@6848
   602
    /* We only keep one field editor per process, since only the front most
slouken@6848
   603
     * window can receive text input events, so it make no sense to keep more
slouken@6848
   604
     * than one copy. When we switched to another window and requesting for
slouken@6848
   605
     * text input, simply remove the field editor from its superview then add
slouken@6848
   606
     * it to the front most window's content view */
slouken@6848
   607
    if (!data->fieldEdit) {
slouken@6848
   608
        data->fieldEdit =
slouken@6848
   609
            [[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
slouken@6848
   610
    }
slouken@4434
   611
slime73@9959
   612
    if (![[data->fieldEdit superview] isEqual:parentView]) {
slouken@7191
   613
        /* DEBUG_IME(@"add fieldEdit to window contentView"); */
slouken@6848
   614
        [data->fieldEdit removeFromSuperview];
slouken@6848
   615
        [parentView addSubview: data->fieldEdit];
jorgen@8141
   616
        [nswindow makeFirstResponder: data->fieldEdit];
slouken@3280
   617
    }
slouken@9087
   618
}}
slouken@3280
   619
slouken@3280
   620
void
slouken@3280
   621
Cocoa_StopTextInput(_THIS)
slouken@9087
   622
{ @autoreleasepool
slouken@3280
   623
{
slouken@3280
   624
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@3280
   625
slouken@3683
   626
    if (data && data->fieldEdit) {
slouken@6848
   627
        [data->fieldEdit removeFromSuperview];
slouken@6848
   628
        [data->fieldEdit release];
slouken@6848
   629
        data->fieldEdit = nil;
slouken@3683
   630
    }
slouken@9087
   631
}}
slouken@3280
   632
slouken@3280
   633
void
slouken@3280
   634
Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect)
slouken@3280
   635
{
slouken@3280
   636
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@3280
   637
aschiffler@6808
   638
    if (!rect) {
slouken@8986
   639
        SDL_InvalidParamError("rect");
slouken@8986
   640
        return;
aschiffler@6808
   641
    }
aschiffler@6808
   642
slime73@9959
   643
    [data->fieldEdit setInputRect:rect];
slouken@3280
   644
}
slouken@3280
   645
slouken@3280
   646
void
slouken@1959
   647
Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
slouken@1959
   648
{
slouken@1959
   649
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
icculus@8862
   650
    if (!data) {
icculus@8862
   651
        return;  /* can happen when returning from fullscreen Space on shutdown */
icculus@8862
   652
    }
icculus@8862
   653
slouken@1959
   654
    unsigned short scancode = [event keyCode];
slouken@5218
   655
    SDL_Scancode code;
slouken@4465
   656
#if 0
slouken@1959
   657
    const char *text;
slouken@4465
   658
#endif
slouken@1959
   659
slouken@2268
   660
    if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
slouken@2268
   661
        /* see comments in SDL_cocoakeys.h */
slouken@2268
   662
        scancode = 60 - scancode;
slouken@2268
   663
    }
slouken@8986
   664
slouken@2305
   665
    if (scancode < SDL_arraysize(darwin_scancode_table)) {
slouken@2305
   666
        code = darwin_scancode_table[scancode];
slouken@8986
   667
    } else {
slouken@1959
   668
        /* Hmm, does this ever happen?  If so, need to extend the keymap... */
slouken@2303
   669
        code = SDL_SCANCODE_UNKNOWN;
slouken@1959
   670
    }
slouken@1959
   671
slouken@1959
   672
    switch ([event type]) {
slouken@1959
   673
    case NSKeyDown:
slouken@2129
   674
        if (![event isARepeat]) {
slouken@2303
   675
            /* See if we need to rebuild the keyboard layout */
slouken@9898
   676
            UpdateKeymap(data, SDL_TRUE);
slouken@4673
   677
        }
slouken@2303
   678
slouken@4673
   679
        SDL_SendKeyboardKey(SDL_PRESSED, code);
slouken@2268
   680
#if 1
slouken@4673
   681
        if (code == SDL_SCANCODE_UNKNOWN) {
slouken@4673
   682
            fprintf(stderr, "The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL mailing list <sdl@libsdl.org> or to Christian Walther <cwalther@gmx.ch>. Mac virtual key code is %d.\n", scancode);
slouken@4673
   683
        }
slouken@2268
   684
#endif
slouken@2129
   685
        if (SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) {
slouken@2268
   686
            /* 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
   687
            [data->fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
slouken@3280
   688
#if 0
slouken@2129
   689
            text = [[event characters] UTF8String];
slouken@2129
   690
            if(text && *text) {
slouken@4465
   691
                SDL_SendKeyboardText(text);
slouken@2273
   692
                [data->fieldEdit setString:@""];
slouken@2129
   693
            }
slouken@3280
   694
#endif
slouken@1959
   695
        }
slouken@1959
   696
        break;
slouken@1959
   697
    case NSKeyUp:
slouken@4465
   698
        SDL_SendKeyboardKey(SDL_RELEASED, code);
slouken@1959
   699
        break;
slouken@1959
   700
    case NSFlagsChanged:
slouken@2268
   701
        /* FIXME CW 2007-08-14: check if this whole mess that takes up half of this file is really necessary */
slouken@1959
   702
        HandleModifiers(_this, scancode, [event modifierFlags]);
slouken@1959
   703
        break;
slouken@2268
   704
    default: /* just to avoid compiler warnings */
slouken@2268
   705
        break;
slouken@2268
   706
    }
slouken@2268
   707
}
slouken@2268
   708
slouken@1959
   709
void
slouken@1931
   710
Cocoa_QuitKeyboard(_THIS)
slouken@1931
   711
{
slouken@10459
   712
    QuitHIDCallback();
slouken@1931
   713
}
slouken@1931
   714
slouken@6044
   715
#endif /* SDL_VIDEO_DRIVER_COCOA */
slouken@6044
   716
slouken@1931
   717
/* vi: set ts=4 sw=4 expandtab: */