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