comparison src/video/x11/SDL_x11events.c @ 3978:b966761fef6c SDL-1.2

Significantly improved XIM support. Fixes Bugzilla #429. Selected notes from the patch's README: = FIXES = This patch fixes the above issues as follows. == X11 events == Moved XFilterEvent just after XNextEvent so that all events are passed to it. Also, XFilterEvent will receive masks indicated by IM through XNFilterEvents IC value as well as masks surpplied by SDL. X11_KeyRepeat is called between XNextEvent and XFilterEvent, after testing an event is a KeyRelease. I'm not 100% comfortable to do so, but I couldn't find a better timing to call it, and use of the function is inevitable. == Xutf8LookupString == Used a longer buffer to receive UTF-8 string. If it is insufficient, a dynamic storage of the requested size will be allocated. The initial size of the buffer is set to 32, because the Japanese text converted from the most widely used benchmark key sequence for Japanese IM, "WATASHINONAMAEHANAKANODESU." has ten Japanese characters in it, that occupies 30 bytes when encoded in UTF-8. == SDL_keysym.unicode == On Windows version of SDL implementation, SDL_keysym.unicode stores UTF-16 encoded unicode characters, one UTF-16 encoding unit per an SDL event. A Unicode supplementary characters are sent to an application as two events. (One with a high surrogate and another with a low surrogate.) The behavior seems reasonable since it is upward compatible with existing handling of BMP characters. I wrote a UTF-8 to UTF-16 conversion function for the purpose. It is designed with the execution speed in mind, having a minimum set of features that my patch requires.
author Ryan C. Gordon <icculus@icculus.org>
date Mon, 25 Jun 2007 19:58:32 +0000
parents c5c3c772f5aa
children f61a20d195f7
comparison
equal deleted inserted replaced
3977:722db1bd733c 3978:b966761fef6c
174 */ 174 */
175 c |= utf8[i] & 0x3F; 175 c |= utf8[i] & 0x3F;
176 } 176 }
177 return c; 177 return c;
178 } 178 }
179
180 /* Given a UTF-8 encoded string pointed to by utf8 of length length in
181 bytes, returns the corresponding UTF-16 encoded string in the
182 buffer pointed to by utf16. The maximum number of UTF-16 encoding
183 units (i.e., Unit16s) allowed in the buffer is specified in
184 utf16_max_length. The return value is the number of UTF-16
185 encoding units placed in the output buffer pointed to by utf16.
186
187 In case of an error, -1 is returned, leaving some unusable partial
188 results in the output buffer.
189
190 The caller must estimate the size of utf16 buffer by itself before
191 calling this function. Insufficient output buffer is considered as
192 an error, and once an error occured, this function doesn't give any
193 clue how large the result will be.
194
195 The error cases include following:
196
197 - Invalid byte sequences were in the input UTF-8 bytes. The caller
198 has no way to know what point in the input buffer was the
199 errornous byte.
200
201 - The input contained a character (a valid UTF-8 byte sequence)
202 whose scalar value exceeded the range that UTF-16 can represent
203 (i.e., characters whose Unicode scalar value above 0x110000).
204
205 - The output buffer has no enough space to hold entire utf16 data.
206
207 Please note:
208
209 - '\0'-termination is not assumed both on the input UTF-8 string
210 and on the output UTF-16 string; any legal zero byte in the input
211 UTF-8 string will be converted to a 16-bit zero in output. As a
212 side effect, the last UTF-16 encoding unit stored in the output
213 buffer will have a non-zero value if the input UTF-8 was not
214 '\0'-terminated.
215
216 - UTF-8 aliases are *not* considered as an error. They are
217 converted to UTF-16. For example, 0xC0 0xA0, 0xE0 0x80 0xA0,
218 and 0xF0 0x80 0x80 0xA0 are all mapped to a single UTF-16
219 encoding unit 0x0020.
220
221 - Three byte UTF-8 sequences whose value corresponds to a surrogate
222 code or other reserved scalar value are not considered as an
223 error either. They may cause an invalid UTF-16 data (e.g., those
224 containing unpaired surrogates).
225
226 */
227
228 static int Utf8ToUtf16(const Uint8 *utf8, const int utf8_length, Uint16 *utf16, const int utf16_max_length) {
229
230 /* p moves over the output buffer. max_ptr points to the next to the last slot of the buffer. */
231 Uint16 *p = utf16;
232 Uint16 const *const max_ptr = utf16 + utf16_max_length;
233
234 /* end_of_input points to the last byte of input as opposed to the next to the last byte. */
235 Uint8 const *const end_of_input = utf8 + utf8_length - 1;
236
237 while (utf8 <= end_of_input) {
238 if (p >= max_ptr) {
239 /* No more output space. */
240 return -1;
241 }
242 Uint8 const c = *utf8;
243 if (c < 0x80) {
244 /* One byte ASCII. */
245 *p++ = c;
246 utf8 += 1;
247 } else if (c < 0xC0) {
248 /* Follower byte without preceeding leader bytes. */
249 return -1;
250 } else if (c < 0xE0) {
251 /* Two byte sequence. We need one follower byte. */
252 if (end_of_input - utf8 < 1 || (((utf8[1] ^ 0x80)) & 0xC0)) {
253 return -1;
254 }
255 *p++ = (Uint16)(0xCF80 + (c << 6) + utf8[1]);
256 utf8 += 2;
257 } else if (c < 0xF0) {
258 /* Three byte sequence. We need two follower byte. */
259 if (end_of_input - utf8 < 2 || (((utf8[1] ^ 0x80) | (utf8[2] ^ 0x80)) & 0xC0)) {
260 return -1;
261 }
262 *p++ = (Uint16)(0xDF80 + (c << 12) + (utf8[1] << 6) + utf8[2]);
263 utf8 += 3;
264 } else if (c < 0xF8) {
265 int plane;
266 /* Four byte sequence. We need three follower bytes. */
267 if (end_of_input - utf8 < 3 || (((utf8[1] ^ 0x80) | (utf8[2] ^0x80) | (utf8[3] ^ 0x80)) & 0xC0)) {
268 return -1;
269 }
270 plane = (-0xC8 + (c << 2) + (utf8[1] >> 4));
271 if (plane == 0) {
272 /* This four byte sequence is an alias that
273 corresponds to a Unicode scalar value in BMP.
274 It fits in an UTF-16 encoding unit. */
275 *p++ = (Uint16)(0xDF80 + (utf8[1] << 12) + (utf8[2] << 6) + utf8[3]);
276 } else if (plane <= 16) {
277 /* This is a legal four byte sequence that corresponds to a surrogate pair. */
278 if (p + 1 >= max_ptr) {
279 /* No enough space on the output buffer for the pair. */
280 return -1;
281 }
282 *p++ = (Uint16)(0xE5B8 + (c << 8) + (utf8[1] << 2) + (utf8[2] >> 4));
283 *p++ = (Uint16)(0xDB80 + ((utf8[2] & 0x0F) << 6) + utf8[3]);
284 } else {
285 /* This four byte sequence is out of UTF-16 code space. */
286 return -1;
287 }
288 utf8 += 4;
289 } else {
290 /* Longer sequence or unused byte. */
291 return -1;
292 }
293 }
294 return p - utf16;
295 }
296
179 #endif 297 #endif
180 298
181 /* Check to see if this is a repeated key. 299 /* Check to see if this is a repeated key.
182 (idea shamelessly lifted from GII -- thanks guys! :) 300 (idea shamelessly lifted from GII -- thanks guys! :)
183 */ 301 */
273 XEvent xevent; 391 XEvent xevent;
274 392
275 SDL_memset(&xevent, '\0', sizeof (XEvent)); /* valgrind fix. --ryan. */ 393 SDL_memset(&xevent, '\0', sizeof (XEvent)); /* valgrind fix. --ryan. */
276 XNextEvent(SDL_Display, &xevent); 394 XNextEvent(SDL_Display, &xevent);
277 395
396 /* Discard KeyRelease and KeyPress events generated by auto-repeat.
397 We need to do it before passing event to XFilterEvent. Otherwise,
398 KeyRelease aware IMs are confused... */
399 if ( xevent.type == KeyRelease
400 && X11_KeyRepeat(SDL_Display, &xevent) ) {
401 return 0;
402 }
403
404 #ifdef X_HAVE_UTF8_STRING
405 /* If we are translating with IM, we need to pass all events
406 to XFilterEvent, and discard those filtered events immediately. */
407 if ( SDL_TranslateUNICODE
408 && SDL_IM != NULL
409 && XFilterEvent(&xevent, None) ) {
410 return 0;
411 }
412 #endif
413
278 posted = 0; 414 posted = 0;
279 switch (xevent.type) { 415 switch (xevent.type) {
280 416
281 /* Gaining mouse coverage? */ 417 /* Gaining mouse coverage? */
282 case EnterNotify: { 418 case EnterNotify: {
356 switch_waiting = 0x01; 492 switch_waiting = 0x01;
357 switch_time = SDL_GetTicks() + 200; 493 switch_time = SDL_GetTicks() + 200;
358 } 494 }
359 break; 495 break;
360 496
497 /* Some IM requires MappingNotify to be passed to
498 XRefreshKeyboardMapping by the app. */
499 case MappingNotify: {
500 XRefreshKeyboardMapping(&xevent.xmapping);
501 }
502 break;
503
361 /* Generated upon EnterWindow and FocusIn */ 504 /* Generated upon EnterWindow and FocusIn */
362 case KeymapNotify: { 505 case KeymapNotify: {
363 #ifdef DEBUG_XEVENTS 506 #ifdef DEBUG_XEVENTS
364 printf("KeymapNotify!\n"); 507 printf("KeymapNotify!\n");
365 #endif 508 #endif
407 } 550 }
408 break; 551 break;
409 552
410 /* Key press? */ 553 /* Key press? */
411 case KeyPress: { 554 case KeyPress: {
412 static SDL_keysym saved_keysym;
413 SDL_keysym keysym; 555 SDL_keysym keysym;
414 KeyCode keycode = xevent.xkey.keycode; 556 KeyCode keycode = xevent.xkey.keycode;
415 557
416 #ifdef DEBUG_XEVENTS 558 #ifdef DEBUG_XEVENTS
417 printf("KeyPress (X11 keycode = 0x%X)\n", xevent.xkey.keycode); 559 printf("KeyPress (X11 keycode = 0x%X)\n", xevent.xkey.keycode);
418 #endif 560 #endif
419 /* Get the translated SDL virtual keysym */ 561 /* If we're not doing translation, we're done! */
420 if ( keycode ) { 562 if ( !SDL_TranslateUNICODE ) {
563 /* Get the translated SDL virtual keysym and put it on the queue.*/
421 keysym.scancode = keycode; 564 keysym.scancode = keycode;
422 keysym.sym = X11_TranslateKeycode(SDL_Display, keycode); 565 keysym.sym = X11_TranslateKeycode(SDL_Display, keycode);
423 keysym.mod = KMOD_NONE; 566 keysym.mod = KMOD_NONE;
424 keysym.unicode = 0; 567 keysym.unicode = 0;
425 } else {
426 keysym = saved_keysym;
427 }
428
429 /* If we're not doing translation, we're done! */
430 if ( !SDL_TranslateUNICODE ) {
431 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym); 568 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
432 break;
433 }
434
435 if ( XFilterEvent(&xevent, None) ) {
436 if ( xevent.xkey.keycode ) {
437 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
438 } else {
439 /* Save event to be associated with IM text
440 In 1.3 we'll have a text event instead.. */
441 saved_keysym = keysym;
442 }
443 break; 569 break;
444 } 570 }
445 571
446 /* Look up the translated value for the key event */ 572 /* Look up the translated value for the key event */
447 #ifdef X_HAVE_UTF8_STRING 573 #ifdef X_HAVE_UTF8_STRING
448 if ( SDL_IC != NULL ) { 574 if ( SDL_IC != NULL ) {
449 static Status state; 575 Status status;
576 KeySym xkeysym;
577 int i;
450 /* A UTF-8 character can be at most 6 bytes */ 578 /* A UTF-8 character can be at most 6 bytes */
451 char keybuf[6]; 579 /* ... It's true, but Xutf8LookupString can
452 if ( Xutf8LookupString(SDL_IC, &xevent.xkey, 580 return more than one characters. Moreover,
453 keybuf, sizeof(keybuf), 581 the spec. put no upper bound, so we should
454 NULL, &state) ) { 582 be ready for longer strings. */
455 keysym.unicode = Utf8ToUcs4((Uint8*)keybuf); 583 char keybuf[32];
584 char *keydata = keybuf;
585 int count;
586 Uint16 utf16buf[32];
587 Uint16 *utf16data = utf16buf;
588 int utf16size;
589 int utf16length;
590
591 count = Xutf8LookupString(SDL_IC, &xevent.xkey, keydata, sizeof(keybuf), &xkeysym, &status);
592 if (XBufferOverflow == status) {
593 /* The IM has just generated somewhat long
594 string. We need a longer buffer in this
595 case. */
596 keydata = SDL_malloc(count);
597 if ( keydata == NULL ) {
598 SDL_OutOfMemory();
599 break;
600 }
601 count = Xutf8LookupString(SDL_IC, &xevent.xkey, keydata, count, &xkeysym, &status);
602 }
603
604 switch (status) {
605
606 case XBufferOverflow: {
607 /* Oops! We have allocated the bytes as
608 requested by Xutf8LookupString, so the
609 length of the buffer must be
610 sufficient. This case should never
611 happen! */
612 SDL_SetError("Xutf8LookupString indicated a double buffer overflow!");
613 break;
614 }
615
616 case XLookupChars:
617 case XLookupBoth: {
618 if (0 == count) {
619 break;
620 }
621
622 /* We got a converted string from IM. Make
623 sure to deliver all characters to the
624 application as SDL events. Note that
625 an SDL event can only carry one UTF-16
626 encoding unit, and a surrogate pair is
627 delivered as two SDL events. I guess
628 this behaviour is probably _imported_
629 from Windows or MacOS. To do so, we need
630 to convert the UTF-8 data into UTF-16
631 data (not UCS4/UTF-32!). We need an
632 estimate of the number of UTF-16 encoding
633 units here. The worst case is pure ASCII
634 string. Assume so. */
635 /* In 1.3 SDL may have a text event instead, that
636 carries the whole UTF-8 string with it. */
637 utf16size = count * sizeof(Uint16);
638 if (utf16size > sizeof(utf16buf)) {
639 utf16data = (Uint16 *) SDL_malloc(utf16size);
640 if (utf16data == NULL) {
641 SDL_OutOfMemory();
642 break;
643 }
644 }
645 utf16length = Utf8ToUtf16((Uint8 *)keydata, count, utf16data, utf16size);
646 if (utf16length < 0) {
647 /* The keydata contained an invalid byte
648 sequence. It should be a bug of the IM
649 or Xlib... */
650 SDL_SetError("Oops! Xutf8LookupString returned an invalid UTF-8 sequence!");
651 break;
652 }
653
654 /* Deliver all UTF-16 encoding units. At
655 this moment, SDL event queue has a
656 fixed size (128 events), and an SDL
657 event can hold just one UTF-16 encoding
658 unit. So, if we receive more than 128
659 UTF-16 encoding units from a commit,
660 exceeded characters will be lost. */
661 for (i = 0; i < utf16length - 1; i++) {
662 keysym.scancode = 0;
663 keysym.sym = SDLK_UNKNOWN;
664 keysym.mod = KMOD_NONE;
665 keysym.unicode = utf16data[i];
666 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
667 }
668 /* The keysym for the last character carries the
669 scancode and symbol that corresponds to the X11
670 keycode. */
671 if (utf16length > 0) {
672 keysym.scancode = keycode;
673 keysym.sym = (keycode ? X11_TranslateKeycode(SDL_Display, keycode) : 0);
674 keysym.mod = KMOD_NONE;
675 keysym.unicode = utf16data[utf16length - 1];
676 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
677 }
678 break;
679 }
680
681 case XLookupKeySym: {
682 /* I'm not sure whether it is possible that
683 a zero keycode makes XLookupKeySym
684 status. What I'm sure is that a
685 combination of a zero scan code and a non
686 zero sym makes SDL_PrivateKeyboard
687 strange state... So, just discard it.
688 If this doesn't work, I'm receiving bug
689 reports, and I can know under what
690 condition this case happens. */
691 if (keycode) {
692 keysym.scancode = keycode;
693 keysym.sym = X11_TranslateKeycode(SDL_Display, keycode);
694 keysym.mod = KMOD_NONE;
695 keysym.unicode = 0;
696 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
697 }
698 break;
699 }
700
701 case XLookupNone: {
702 /* IM has eaten the event. */
703 break;
704 }
705
706 default:
707 /* An unknown status from Xutf8LookupString. */
708 SDL_SetError("Oops! Xutf8LookupStringreturned an unknown status");
709 }
710
711 /* Release dynamic buffers if allocated. */
712 if (keydata != NULL && keybuf != keydata) {
713 SDL_free(keydata);
714 }
715 if (utf16data != NULL && utf16buf != utf16data) {
716 SDL_free(utf16data);
456 } 717 }
457 } 718 }
458 else 719 else
459 #endif 720 #endif
460 { 721 {
470 * this (perhaps null keypress events with a 731 * this (perhaps null keypress events with a
471 * unicode value) 732 * unicode value)
472 */ 733 */
473 keysym.unicode = (Uint8)keybuf[0]; 734 keysym.unicode = (Uint8)keybuf[0];
474 } 735 }
475 } 736
476 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym); 737 posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
738 }
477 } 739 }
478 break; 740 break;
479 741
480 /* Key release? */ 742 /* Key release? */
481 case KeyRelease: { 743 case KeyRelease: {
482 SDL_keysym keysym; 744 SDL_keysym keysym;
483 KeyCode keycode = xevent.xkey.keycode; 745 KeyCode keycode = xevent.xkey.keycode;
484 746
747 if (keycode == 0) {
748 /* There should be no KeyRelease for keycode == 0,
749 since it is a notification from IM but a real
750 keystroke. */
751 /* We need to emit some diagnostic message here. */
752 break;
753 }
754
485 #ifdef DEBUG_XEVENTS 755 #ifdef DEBUG_XEVENTS
486 printf("KeyRelease (X11 keycode = 0x%X)\n", xevent.xkey.keycode); 756 printf("KeyRelease (X11 keycode = 0x%X)\n", xevent.xkey.keycode);
487 #endif 757 #endif
488 /* Check to see if this is a repeated key */
489 if ( X11_KeyRepeat(SDL_Display, &xevent) ) {
490 break;
491 }
492 758
493 /* Get the translated SDL virtual keysym */ 759 /* Get the translated SDL virtual keysym */
494 keysym.scancode = keycode; 760 keysym.scancode = keycode;
495 keysym.sym = X11_TranslateKeycode(SDL_Display, keycode); 761 keysym.sym = X11_TranslateKeycode(SDL_Display, keycode);
496 keysym.mod = KMOD_NONE; 762 keysym.mod = KMOD_NONE;