Mercurial > sdl-ios-xcode
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; |