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