src/video/cocoa/SDL_cocoakeyboard.m
author Sam Lantinga <slouken@libsdl.org>
Sat, 02 Jan 2016 10:10:34 -0800
changeset 9998 f67cf37e9cd4
parent 9975 bb853b9549d0
child 10176 3115d38204aa
permissions -rw-r--r--
Updated copyright to 2016
slouken@1931
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9998
     3
  Copyright (C) 1997-2016 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@2268
    32
slouken@7191
    33
/*#define DEBUG_IME NSLog */
slouken@6044
    34
#define DEBUG_IME(...)
slouken@1959
    35
slime73@9959
    36
@interface SDLTranslatorResponder : NSView <NSTextInputClient> {
slouken@3280
    37
    NSString *_markedText;
slouken@3280
    38
    NSRange   _markedRange;
slouken@3280
    39
    NSRange   _selectedRange;
slouken@3280
    40
    SDL_Rect  _inputRect;
slouken@2289
    41
}
slime73@9959
    42
- (void)doCommandBySelector:(SEL)myselector;
slime73@9959
    43
- (void)setInputRect:(SDL_Rect *)rect;
slouken@2289
    44
@end
slouken@2289
    45
slouken@2289
    46
@implementation SDLTranslatorResponder
slouken@3280
    47
slime73@9959
    48
- (void)setInputRect:(SDL_Rect *)rect
slouken@3280
    49
{
slouken@3280
    50
    _inputRect = *rect;
slouken@3280
    51
}
slouken@3280
    52
slime73@9959
    53
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
slouken@3280
    54
{
slime73@9959
    55
    /* TODO: Make use of replacementRange? */
slime73@9959
    56
slouken@3280
    57
    const char *str;
slouken@3280
    58
slouken@3280
    59
    DEBUG_IME(@"insertText: %@", aString);
slouken@3280
    60
slouken@3280
    61
    /* Could be NSString or NSAttributedString, so we have
slouken@3280
    62
     * to test and convert it before return as SDL event */
slouken@8986
    63
    if ([aString isKindOfClass: [NSAttributedString class]]) {
slouken@3280
    64
        str = [[aString string] UTF8String];
slouken@8986
    65
    } else {
slouken@3280
    66
        str = [aString UTF8String];
slouken@8986
    67
    }
slouken@3280
    68
slouken@4465
    69
    SDL_SendKeyboardText(str);
slouken@3280
    70
}
slouken@3280
    71
slime73@9959
    72
- (void)insertText:(id)insertString
slime73@9959
    73
{
slime73@9959
    74
    /* This method is part of NSTextInput and not NSTextInputClient, but
slime73@9959
    75
     * apparently it still might be called in OS X 10.5 and can cause beeps if
slime73@9959
    76
     * the implementation is missing: http://crbug.com/47890 */
slime73@9959
    77
    [self insertText:insertString replacementRange:NSMakeRange(0, 0)];
slime73@9959
    78
}
slime73@9959
    79
slime73@9959
    80
- (void)doCommandBySelector:(SEL)myselector
slouken@3280
    81
{
slouken@7191
    82
    /* No need to do anything since we are not using Cocoa
slouken@7191
    83
       selectors to handle special keys, instead we use SDL
slouken@7191
    84
       key events to do the same job.
slouken@7191
    85
    */
slouken@3280
    86
}
slouken@3280
    87
slime73@9959
    88
- (BOOL)hasMarkedText
slouken@3280
    89
{
slouken@3280
    90
    return _markedText != nil;
slouken@3280
    91
}
slouken@3280
    92
slime73@9959
    93
- (NSRange)markedRange
slouken@3280
    94
{
slouken@3280
    95
    return _markedRange;
slouken@3280
    96
}
slouken@3280
    97
slime73@9959
    98
- (NSRange)selectedRange
slouken@3280
    99
{
slouken@3280
   100
    return _selectedRange;
slouken@3280
   101
}
slouken@3280
   102
slime73@9959
   103
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange;
slouken@3280
   104
{
slouken@8986
   105
    if ([aString isKindOfClass: [NSAttributedString class]]) {
slouken@3280
   106
        aString = [aString string];
slouken@8986
   107
    }
slouken@3280
   108
slouken@8986
   109
    if ([aString length] == 0) {
slouken@3280
   110
        [self unmarkText];
slouken@3280
   111
        return;
slouken@3280
   112
    }
slouken@3280
   113
slouken@8986
   114
    if (_markedText != aString) {
slouken@3280
   115
        [_markedText release];
slouken@3280
   116
        _markedText = [aString retain];
slouken@3280
   117
    }
slouken@3280
   118
slime73@9959
   119
    _selectedRange = selectedRange;
slouken@3280
   120
    _markedRange = NSMakeRange(0, [aString length]);
slouken@3280
   121
slouken@4465
   122
    SDL_SendEditingText([aString UTF8String],
slime73@9959
   123
                        selectedRange.location, selectedRange.length);
slouken@3280
   124
slouken@3280
   125
    DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText,
slouken@3280
   126
          selRange.location, selRange.length);
slouken@3280
   127
}
slouken@3280
   128
slime73@9959
   129
- (void)unmarkText
slouken@3280
   130
{
slouken@3280
   131
    [_markedText release];
slouken@3280
   132
    _markedText = nil;
gzjjgod@4637
   133
gzjjgod@4637
   134
    SDL_SendEditingText("", 0, 0);
slouken@3280
   135
}
slouken@3280
   136
slime73@9959
   137
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
slouken@3280
   138
{
slouken@4436
   139
    NSWindow *window = [self window];
slime73@9959
   140
    NSRect contentRect = [window contentRectForFrameRect:[window frame]];
slouken@4436
   141
    float windowHeight = contentRect.size.height;
slouken@3280
   142
    NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h,
slouken@3280
   143
                             _inputRect.w, _inputRect.h);
slouken@3280
   144
slime73@9959
   145
    if (actualRange) {
slime73@9959
   146
        *actualRange = aRange;
slime73@9959
   147
    }
slime73@9959
   148
slouken@3280
   149
    DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@",
slime73@9959
   150
            aRange.location, aRange.length, windowHeight,
slouken@3280
   151
            NSStringFromRect(rect));
slime73@9959
   152
slime73@9959
   153
    if ([[self window] respondsToSelector:@selector(convertRectToScreen:)]) {
slime73@9959
   154
        rect = [[self window] convertRectToScreen:rect];
slime73@9959
   155
    } else {
slime73@9959
   156
        rect.origin = [[self window] convertBaseToScreen:rect.origin];
slime73@9959
   157
    }
slouken@3280
   158
slouken@3280
   159
    return rect;
slouken@3280
   160
}
slouken@3280
   161
slime73@9959
   162
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
slouken@3280
   163
{
slime73@9959
   164
    DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length);
slouken@3280
   165
    return nil;
slouken@3280
   166
}
slouken@3280
   167
slime73@9959
   168
- (NSInteger)conversationIdentifier
slouken@3280
   169
{
jorgen@7507
   170
    return (NSInteger) self;
slouken@3280
   171
}
slouken@3280
   172
slouken@7191
   173
/* This method returns the index for character that is
slouken@7191
   174
 * nearest to thePoint.  thPoint is in screen coordinate system.
slouken@7191
   175
 */
slime73@9959
   176
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
slouken@3280
   177
{
slouken@3280
   178
    DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
slouken@3280
   179
    return 0;
slouken@3280
   180
}
slouken@3280
   181
slouken@7191
   182
/* This method is the key to attribute extension.
slouken@7191
   183
 * We could add new attributes through this method.
slouken@7191
   184
 * NSInputServer examines the return value of this
slouken@7191
   185
 * method & constructs appropriate attributed string.
slouken@7191
   186
 */
slime73@9959
   187
- (NSArray *)validAttributesForMarkedText
slouken@3280
   188
{
slouken@3280
   189
    return [NSArray array];
slouken@3280
   190
}
slouken@3280
   191
slouken@2289
   192
@end
slouken@2289
   193
slouken@7191
   194
/* This is a helper function for HandleModifierSide. This
slouken@1959
   195
 * function reverts back to behavior before the distinction between
slouken@1959
   196
 * sides was made.
slouken@1959
   197
 */
slouken@1959
   198
static void
slouken@4465
   199
HandleNonDeviceModifier(unsigned int device_independent_mask,
slouken@1959
   200
                        unsigned int oldMods,
slouken@1959
   201
                        unsigned int newMods,
slouken@5218
   202
                        SDL_Scancode scancode)
slouken@1959
   203
{
slouken@1959
   204
    unsigned int oldMask, newMask;
slouken@7191
   205
slouken@7191
   206
    /* Isolate just the bits we care about in the depedent bits so we can
slouken@1959
   207
     * figure out what changed
slouken@7191
   208
     */
slouken@1959
   209
    oldMask = oldMods & device_independent_mask;
slouken@1959
   210
    newMask = newMods & device_independent_mask;
slouken@7191
   211
slouken@1959
   212
    if (oldMask && oldMask != newMask) {
slouken@4465
   213
        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@1959
   214
    } else if (newMask && oldMask != newMask) {
slouken@4465
   215
        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@1959
   216
    }
slouken@1959
   217
}
slouken@1959
   218
slouken@7191
   219
/* This is a helper function for HandleModifierSide.
slouken@1959
   220
 * This function sets the actual SDL_PrivateKeyboard event.
slouken@1959
   221
 */
slouken@1959
   222
static void
slouken@4465
   223
HandleModifierOneSide(unsigned int oldMods, unsigned int newMods,
slouken@7191
   224
                      SDL_Scancode scancode,
slouken@1959
   225
                      unsigned int sided_device_dependent_mask)
slouken@1959
   226
{
slouken@1959
   227
    unsigned int old_dep_mask, new_dep_mask;
slouken@1959
   228
slouken@7191
   229
    /* Isolate just the bits we care about in the depedent bits so we can
slouken@1959
   230
     * figure out what changed
slouken@7191
   231
     */
slouken@1959
   232
    old_dep_mask = oldMods & sided_device_dependent_mask;
slouken@1959
   233
    new_dep_mask = newMods & sided_device_dependent_mask;
slouken@1959
   234
slouken@1959
   235
    /* We now know that this side bit flipped. But we don't know if
slouken@7191
   236
     * it went pressed to released or released to pressed, so we must
slouken@1959
   237
     * find out which it is.
slouken@1959
   238
     */
slouken@1959
   239
    if (new_dep_mask && old_dep_mask != new_dep_mask) {
slouken@4465
   240
        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@1959
   241
    } else {
slouken@4465
   242
        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@1959
   243
    }
slouken@1959
   244
}
slouken@1959
   245
slouken@1959
   246
/* This is a helper function for DoSidedModifiers.
slouken@7191
   247
 * This function will figure out if the modifier key is the left or right side,
slouken@7191
   248
 * e.g. left-shift vs right-shift.
slouken@1959
   249
 */
slouken@1959
   250
static void
slouken@7191
   251
HandleModifierSide(int device_independent_mask,
slouken@7191
   252
                   unsigned int oldMods, unsigned int newMods,
slouken@7191
   253
                   SDL_Scancode left_scancode,
slouken@5218
   254
                   SDL_Scancode right_scancode,
slouken@7191
   255
                   unsigned int left_device_dependent_mask,
slouken@1959
   256
                   unsigned int right_device_dependent_mask)
slouken@1959
   257
{
slouken@1959
   258
    unsigned int device_dependent_mask = (left_device_dependent_mask |
slouken@1959
   259
                                         right_device_dependent_mask);
slouken@1959
   260
    unsigned int diff_mod;
slouken@7191
   261
slouken@7191
   262
    /* On the basis that the device independent mask is set, but there are
slouken@7191
   263
     * no device dependent flags set, we'll assume that we can't detect this
slouken@1959
   264
     * keyboard and revert to the unsided behavior.
slouken@1959
   265
     */
slouken@1959
   266
    if ((device_dependent_mask & newMods) == 0) {
slouken@1959
   267
        /* Revert to the old behavior */
slouken@4465
   268
        HandleNonDeviceModifier(device_independent_mask, oldMods, newMods, left_scancode);
slouken@1959
   269
        return;
slouken@1959
   270
    }
slouken@1959
   271
slouken@1959
   272
    /* XOR the previous state against the new state to see if there's a change */
slouken@1959
   273
    diff_mod = (device_dependent_mask & oldMods) ^
slouken@1959
   274
               (device_dependent_mask & newMods);
slouken@1959
   275
    if (diff_mod) {
slouken@7191
   276
        /* A change in state was found. Isolate the left and right bits
slouken@1959
   277
         * to handle them separately just in case the values can simulataneously
slouken@1959
   278
         * change or if the bits don't both exist.
slouken@1959
   279
         */
slouken@1959
   280
        if (left_device_dependent_mask & diff_mod) {
slouken@4465
   281
            HandleModifierOneSide(oldMods, newMods, left_scancode, left_device_dependent_mask);
slouken@1959
   282
        }
slouken@1959
   283
        if (right_device_dependent_mask & diff_mod) {
slouken@4465
   284
            HandleModifierOneSide(oldMods, newMods, right_scancode, right_device_dependent_mask);
slouken@1959
   285
        }
slouken@1959
   286
    }
slouken@1959
   287
}
slouken@7191
   288
slouken@1959
   289
/* This is a helper function for DoSidedModifiers.
slouken@7191
   290
 * This function will release a key press in the case that
slouken@7191
   291
 * it is clear that the modifier has been released (i.e. one side
slouken@1959
   292
 * can't still be down).
slouken@1959
   293
 */
slouken@1959
   294
static void
slouken@7191
   295
ReleaseModifierSide(unsigned int device_independent_mask,
slouken@1959
   296
                    unsigned int oldMods, unsigned int newMods,
slouken@7191
   297
                    SDL_Scancode left_scancode,
slouken@5218
   298
                    SDL_Scancode right_scancode,
slouken@7191
   299
                    unsigned int left_device_dependent_mask,
slouken@1959
   300
                    unsigned int right_device_dependent_mask)
slouken@1959
   301
{
slouken@1959
   302
    unsigned int device_dependent_mask = (left_device_dependent_mask |
slouken@1959
   303
                                          right_device_dependent_mask);
slouken@1959
   304
slouken@7191
   305
    /* On the basis that the device independent mask is set, but there are
slouken@7191
   306
     * no device dependent flags set, we'll assume that we can't detect this
slouken@1959
   307
     * keyboard and revert to the unsided behavior.
slouken@1959
   308
     */
slouken@1959
   309
    if ((device_dependent_mask & oldMods) == 0) {
slouken@7191
   310
        /* In this case, we can't detect the keyboard, so use the left side
slouken@7191
   311
         * to represent both, and release it.
slouken@1959
   312
         */
slouken@4465
   313
        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
slouken@1959
   314
        return;
slouken@1959
   315
    }
slouken@1959
   316
slouken@7191
   317
    /*
slouken@1959
   318
     * This could have been done in an if-else case because at this point,
slouken@7191
   319
     * we know that all keys have been released when calling this function.
slouken@1959
   320
     * But I'm being paranoid so I want to handle each separately,
slouken@1959
   321
     * so I hope this doesn't cause other problems.
slouken@1959
   322
     */
slouken@1959
   323
    if ( left_device_dependent_mask & oldMods ) {
slouken@4465
   324
        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
slouken@1959
   325
    }
slouken@1959
   326
    if ( right_device_dependent_mask & oldMods ) {
slouken@4465
   327
        SDL_SendKeyboardKey(SDL_RELEASED, right_scancode);
slouken@1959
   328
    }
slouken@1959
   329
}
slouken@1959
   330
slouken@1959
   331
/* This is a helper function for DoSidedModifiers.
slouken@1959
   332
 * This function handles the CapsLock case.
slouken@1959
   333
 */
slouken@1959
   334
static void
slouken@4465
   335
HandleCapsLock(unsigned short scancode,
slouken@1959
   336
               unsigned int oldMods, unsigned int newMods)
slouken@1959
   337
{
slouken@1959
   338
    unsigned int oldMask, newMask;
slouken@7191
   339
slouken@1959
   340
    oldMask = oldMods & NSAlphaShiftKeyMask;
slouken@1959
   341
    newMask = newMods & NSAlphaShiftKeyMask;
slouken@1959
   342
slouken@1959
   343
    if (oldMask != newMask) {
icculus@9975
   344
        SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_CAPSLOCK);
icculus@9975
   345
        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
slouken@1959
   346
    }
slouken@1959
   347
}
slouken@1959
   348
slouken@7191
   349
/* This function will handle the modifier keys and also determine the
slouken@1959
   350
 * correct side of the key.
slouken@1959
   351
 */
slouken@1959
   352
static void
slouken@4465
   353
DoSidedModifiers(unsigned short scancode,
slouken@1959
   354
                 unsigned int oldMods, unsigned int newMods)
slouken@1959
   355
{
slouken@7191
   356
    /* Set up arrays for the key syms for the left and right side. */
slouken@5218
   357
    const SDL_Scancode left_mapping[]  = {
slouken@2303
   358
        SDL_SCANCODE_LSHIFT,
slouken@2303
   359
        SDL_SCANCODE_LCTRL,
slouken@2303
   360
        SDL_SCANCODE_LALT,
slouken@2303
   361
        SDL_SCANCODE_LGUI
slouken@2303
   362
    };
slouken@5218
   363
    const SDL_Scancode right_mapping[] = {
slouken@2303
   364
        SDL_SCANCODE_RSHIFT,
slouken@2303
   365
        SDL_SCANCODE_RCTRL,
slouken@2303
   366
        SDL_SCANCODE_RALT,
slouken@2303
   367
        SDL_SCANCODE_RGUI
slouken@2303
   368
    };
slouken@7191
   369
    /* Set up arrays for the device dependent masks with indices that
slouken@7191
   370
     * correspond to the _mapping arrays
slouken@1959
   371
     */
slouken@1959
   372
    const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
slouken@1959
   373
    const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
slouken@1959
   374
slouken@1959
   375
    unsigned int i, bit;
slouken@1959
   376
slouken@1959
   377
    /* Handle CAPSLOCK separately because it doesn't have a left/right side */
slouken@4465
   378
    HandleCapsLock(scancode, oldMods, newMods);
slouken@1959
   379
slouken@1959
   380
    /* Iterate through the bits, testing each against the old modifiers */
slouken@1959
   381
    for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
slouken@1959
   382
        unsigned int oldMask, newMask;
slouken@7191
   383
slouken@1959
   384
        oldMask = oldMods & bit;
slouken@1959
   385
        newMask = newMods & bit;
slouken@7191
   386
slouken@1959
   387
        /* If the bit is set, we must always examine it because the left
slouken@1959
   388
         * and right side keys may alternate or both may be pressed.
slouken@1959
   389
         */
slouken@1959
   390
        if (newMask) {
slouken@4465
   391
            HandleModifierSide(bit, oldMods, newMods,
slouken@1959
   392
                               left_mapping[i], right_mapping[i],
slouken@1959
   393
                               left_device_mapping[i], right_device_mapping[i]);
slouken@1959
   394
        }
slouken@1959
   395
        /* If the state changed from pressed to unpressed, we must examine
slouken@1959
   396
            * the device dependent bits to release the correct keys.
slouken@1959
   397
            */
slouken@1959
   398
        else if (oldMask && oldMask != newMask) {
slouken@4465
   399
            ReleaseModifierSide(bit, oldMods, newMods,
slouken@1959
   400
                              left_mapping[i], right_mapping[i],
slouken@1959
   401
                              left_device_mapping[i], right_device_mapping[i]);
slouken@1959
   402
        }
slouken@1959
   403
    }
slouken@1959
   404
}
slouken@1959
   405
slouken@1959
   406
static void
slouken@1959
   407
HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags)
slouken@1959
   408
{
slouken@1959
   409
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@1959
   410
slouken@1959
   411
    if (modifierFlags == data->modifierFlags) {
slouken@7191
   412
        return;
slouken@1959
   413
    }
slouken@1959
   414
slouken@7947
   415
    DoSidedModifiers(scancode, data->modifierFlags, modifierFlags);
slouken@1959
   416
    data->modifierFlags = modifierFlags;
slouken@1959
   417
}
slouken@1959
   418
slouken@2303
   419
static void
slouken@9898
   420
UpdateKeymap(SDL_VideoData *data, SDL_bool send_event)
slouken@2303
   421
{
slouken@3247
   422
    TISInputSourceRef key_layout;
slouken@2303
   423
    const void *chr_data;
slouken@2303
   424
    int i;
slouken@5218
   425
    SDL_Scancode scancode;
slouken@5220
   426
    SDL_Keycode keymap[SDL_NUM_SCANCODES];
slouken@2303
   427
slouken@2303
   428
    /* See if the keymap needs to be updated */
slouken@3247
   429
    key_layout = TISCopyCurrentKeyboardLayoutInputSource();
slouken@2303
   430
    if (key_layout == data->key_layout) {
slouken@2303
   431
        return;
slouken@2303
   432
    }
slouken@2303
   433
    data->key_layout = key_layout;
slouken@2303
   434
slouken@2303
   435
    SDL_GetDefaultKeymap(keymap);
slouken@2303
   436
jorgen@7507
   437
    /* Try Unicode data first */
slouken@3247
   438
    CFDataRef uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData);
slouken@8986
   439
    if (uchrDataRef) {
slouken@3247
   440
        chr_data = CFDataGetBytePtr(uchrDataRef);
slouken@8986
   441
    } else {
slouken@3247
   442
        goto cleanup;
slouken@8986
   443
    }
jorgen@7507
   444
slouken@2303
   445
    if (chr_data) {
slouken@2303
   446
        UInt32 keyboard_type = LMGetKbdType();
slouken@2303
   447
        OSStatus err;
slouken@2303
   448
slouken@2305
   449
        for (i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
slouken@2303
   450
            UniChar s[8];
slouken@2303
   451
            UniCharCount len;
slouken@2303
   452
            UInt32 dead_key_state;
slouken@2303
   453
slouken@2303
   454
            /* Make sure this scancode is a valid character scancode */
slouken@2305
   455
            scancode = darwin_scancode_table[i];
slouken@2303
   456
            if (scancode == SDL_SCANCODE_UNKNOWN ||
slouken@2303
   457
                (keymap[scancode] & SDLK_SCANCODE_MASK)) {
slouken@2303
   458
                continue;
slouken@2303
   459
            }
slouken@2303
   460
slouken@2303
   461
            dead_key_state = 0;
slouken@3247
   462
            err = UCKeyTranslate ((UCKeyboardLayout *) chr_data,
slouken@3247
   463
                                  i, kUCKeyActionDown,
slouken@2303
   464
                                  0, keyboard_type,
slouken@2303
   465
                                  kUCKeyTranslateNoDeadKeysMask,
slouken@2303
   466
                                  &dead_key_state, 8, &len, s);
slouken@8986
   467
            if (err != noErr) {
slouken@2303
   468
                continue;
slouken@8986
   469
            }
slouken@2303
   470
slouken@2303
   471
            if (len > 0 && s[0] != 0x10) {
slouken@2303
   472
                keymap[scancode] = s[0];
slouken@2303
   473
            }
slouken@2303
   474
        }
slouken@4465
   475
        SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
slouken@9898
   476
        if (send_event) {
slouken@9898
   477
            SDL_SendKeymapChangedEvent();
slouken@9898
   478
        }
slouken@2303
   479
        return;
slouken@2303
   480
    }
slouken@2303
   481
slouken@3247
   482
cleanup:
slouken@3247
   483
    CFRelease(key_layout);
slouken@2303
   484
}
slouken@2303
   485
slouken@1931
   486
void
slouken@1931
   487
Cocoa_InitKeyboard(_THIS)
slouken@1931
   488
{
slouken@1931
   489
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@1931
   490
slouken@9898
   491
    UpdateKeymap(data, SDL_FALSE);
slouken@7191
   492
slouken@2268
   493
    /* Set our own names for the platform-dependent but layout-independent keys */
slouken@2303
   494
    /* This key is NumLock on the MacBook keyboard. :) */
slouken@2303
   495
    /*SDL_SetScancodeName(SDL_SCANCODE_NUMLOCKCLEAR, "Clear");*/
slouken@2303
   496
    SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option");
slouken@2303
   497
    SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command");
slouken@2303
   498
    SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option");
slouken@2303
   499
    SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command");
icculus@9967
   500
icculus@9967
   501
    /* On pre-10.6, you might have the initial capslock key state wrong. */
icculus@9967
   502
    if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_6) {
icculus@9967
   503
        data->modifierFlags = [NSEvent modifierFlags];
icculus@9973
   504
        SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSAlphaShiftKeyMask) != 0);
icculus@9967
   505
    }
slouken@1931
   506
}
slouken@1931
   507
slouken@1931
   508
void
slouken@3280
   509
Cocoa_StartTextInput(_THIS)
slouken@9087
   510
{ @autoreleasepool
slouken@3280
   511
{
slouken@3280
   512
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
jorgen@8141
   513
    SDL_Window *window = SDL_GetKeyboardFocus();
jorgen@8141
   514
    NSWindow *nswindow = nil;
slouken@8986
   515
    if (window) {
jorgen@8141
   516
        nswindow = ((SDL_WindowData*)window->driverdata)->nswindow;
slouken@8986
   517
    }
jorgen@8141
   518
jorgen@8141
   519
    NSView *parentView = [nswindow contentView];
slouken@3280
   520
slouken@6848
   521
    /* We only keep one field editor per process, since only the front most
slouken@6848
   522
     * window can receive text input events, so it make no sense to keep more
slouken@6848
   523
     * than one copy. When we switched to another window and requesting for
slouken@6848
   524
     * text input, simply remove the field editor from its superview then add
slouken@6848
   525
     * it to the front most window's content view */
slouken@6848
   526
    if (!data->fieldEdit) {
slouken@6848
   527
        data->fieldEdit =
slouken@6848
   528
            [[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
slouken@6848
   529
    }
alexey@6832
   530
slime73@9959
   531
    if (![[data->fieldEdit superview] isEqual:parentView]) {
slouken@7191
   532
        /* DEBUG_IME(@"add fieldEdit to window contentView"); */
slouken@6848
   533
        [data->fieldEdit removeFromSuperview];
slouken@6848
   534
        [parentView addSubview: data->fieldEdit];
jorgen@8141
   535
        [nswindow makeFirstResponder: data->fieldEdit];
slouken@4435
   536
    }
slouken@9087
   537
}}
slouken@3280
   538
slouken@3280
   539
void
slouken@3280
   540
Cocoa_StopTextInput(_THIS)
slouken@9087
   541
{ @autoreleasepool
slouken@3280
   542
{
slouken@3280
   543
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@3280
   544
slouken@3683
   545
    if (data && data->fieldEdit) {
slouken@6848
   546
        [data->fieldEdit removeFromSuperview];
slouken@6848
   547
        [data->fieldEdit release];
slouken@6848
   548
        data->fieldEdit = nil;
slouken@3683
   549
    }
slouken@9087
   550
}}
slouken@3280
   551
slouken@3280
   552
void
slouken@3280
   553
Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect)
slouken@3280
   554
{
slouken@3280
   555
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@3280
   556
aschiffler@6808
   557
    if (!rect) {
slouken@8986
   558
        SDL_InvalidParamError("rect");
slouken@8986
   559
        return;
aschiffler@6808
   560
    }
aschiffler@6808
   561
slime73@9959
   562
    [data->fieldEdit setInputRect:rect];
slouken@3280
   563
}
slouken@3280
   564
slouken@3280
   565
void
slouken@1959
   566
Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
slouken@1959
   567
{
slouken@1959
   568
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
icculus@8862
   569
    if (!data) {
icculus@8862
   570
        return;  /* can happen when returning from fullscreen Space on shutdown */
icculus@8862
   571
    }
icculus@8862
   572
slouken@1959
   573
    unsigned short scancode = [event keyCode];
slouken@5218
   574
    SDL_Scancode code;
slouken@4465
   575
#if 0
slouken@1959
   576
    const char *text;
slouken@4465
   577
#endif
slouken@1959
   578
slouken@2268
   579
    if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
slouken@2268
   580
        /* see comments in SDL_cocoakeys.h */
slouken@2268
   581
        scancode = 60 - scancode;
slouken@2268
   582
    }
slouken@8986
   583
slouken@2305
   584
    if (scancode < SDL_arraysize(darwin_scancode_table)) {
slouken@2305
   585
        code = darwin_scancode_table[scancode];
slouken@8986
   586
    } else {
slouken@1959
   587
        /* Hmm, does this ever happen?  If so, need to extend the keymap... */
slouken@2303
   588
        code = SDL_SCANCODE_UNKNOWN;
slouken@1959
   589
    }
slouken@1959
   590
slouken@1959
   591
    switch ([event type]) {
slouken@1959
   592
    case NSKeyDown:
slouken@2129
   593
        if (![event isARepeat]) {
slouken@2303
   594
            /* See if we need to rebuild the keyboard layout */
slouken@9898
   595
            UpdateKeymap(data, SDL_TRUE);
slouken@4673
   596
        }
slouken@2303
   597
slouken@4673
   598
        SDL_SendKeyboardKey(SDL_PRESSED, code);
slouken@2268
   599
#if 1
slouken@4673
   600
        if (code == SDL_SCANCODE_UNKNOWN) {
slouken@4673
   601
            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
   602
        }
slouken@2268
   603
#endif
slouken@2129
   604
        if (SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) {
slouken@2268
   605
            /* 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
   606
            [data->fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
slouken@3280
   607
#if 0
slouken@2129
   608
            text = [[event characters] UTF8String];
slouken@2129
   609
            if(text && *text) {
slouken@4465
   610
                SDL_SendKeyboardText(text);
slouken@2273
   611
                [data->fieldEdit setString:@""];
slouken@2129
   612
            }
slouken@3280
   613
#endif
slouken@1959
   614
        }
slouken@1959
   615
        break;
slouken@1959
   616
    case NSKeyUp:
slouken@4465
   617
        SDL_SendKeyboardKey(SDL_RELEASED, code);
slouken@1959
   618
        break;
slouken@1959
   619
    case NSFlagsChanged:
slouken@2268
   620
        /* FIXME CW 2007-08-14: check if this whole mess that takes up half of this file is really necessary */
slouken@1959
   621
        HandleModifiers(_this, scancode, [event modifierFlags]);
slouken@1959
   622
        break;
slouken@2268
   623
    default: /* just to avoid compiler warnings */
slouken@2268
   624
        break;
slouken@2268
   625
    }
slouken@2268
   626
}
slouken@2268
   627
slouken@1959
   628
void
slouken@1931
   629
Cocoa_QuitKeyboard(_THIS)
slouken@1931
   630
{
slouken@1931
   631
}
slouken@1931
   632
slouken@6044
   633
#endif /* SDL_VIDEO_DRIVER_COCOA */
slouken@6044
   634
slouken@1931
   635
/* vi: set ts=4 sw=4 expandtab: */