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