diff options
| author | Po Lu | 2024-03-02 14:04:56 +0800 |
|---|---|---|
| committer | Po Lu | 2024-03-02 14:04:56 +0800 |
| commit | 5e20b114ef32d504f4429fd35ecd0d5dcf3bd8db (patch) | |
| tree | 83215f4df69308e61c490172f4a68ff0fea63452 /src | |
| parent | 8b96503b6e8514f1f9f92895a0707c78b1bbd1fd (diff) | |
| download | emacs-5e20b114ef32d504f4429fd35ecd0d5dcf3bd8db.tar.gz emacs-5e20b114ef32d504f4429fd35ecd0d5dcf3bd8db.zip | |
Implement dead key combination on Android
* src/android.c (android_init_key_character_map)
(android_get_dead_char): New functions.
(android_wc_lookup_string): New argument COMPOSE_STATE. Ignore
key events with the COMBINING_ACCENT flag set while recording
their character values there, and combine such characters with
the key event when processing a subsequent key event.
* src/androidgui.h (struct android_compose_status): New
structure.
* src/androidterm.c (handle_one_android_event): Port dead key
combination code from X. (bug#69321)
Diffstat (limited to 'src')
| -rw-r--r-- | src/android.c | 122 | ||||
| -rw-r--r-- | src/androidgui.h | 12 | ||||
| -rw-r--r-- | src/androidterm.c | 19 |
3 files changed, 148 insertions, 5 deletions
diff --git a/src/android.c b/src/android.c index 41481afa475..eb6981093be 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -123,6 +123,12 @@ struct android_emacs_cursor | |||
| 123 | jmethodID constructor; | 123 | jmethodID constructor; |
| 124 | }; | 124 | }; |
| 125 | 125 | ||
| 126 | struct android_key_character_map | ||
| 127 | { | ||
| 128 | jclass class; | ||
| 129 | jmethodID get_dead_char; | ||
| 130 | }; | ||
| 131 | |||
| 126 | /* The API level of the current device. */ | 132 | /* The API level of the current device. */ |
| 127 | static int android_api_level; | 133 | static int android_api_level; |
| 128 | 134 | ||
| @@ -203,6 +209,9 @@ static struct android_emacs_window window_class; | |||
| 203 | /* Various methods associated with the EmacsCursor class. */ | 209 | /* Various methods associated with the EmacsCursor class. */ |
| 204 | static struct android_emacs_cursor cursor_class; | 210 | static struct android_emacs_cursor cursor_class; |
| 205 | 211 | ||
| 212 | /* Various methods associated with the KeyCharacterMap class. */ | ||
| 213 | static struct android_key_character_map key_character_map_class; | ||
| 214 | |||
| 206 | /* The time at which Emacs was installed, which also supplies the | 215 | /* The time at which Emacs was installed, which also supplies the |
| 207 | mtime of asset files. */ | 216 | mtime of asset files. */ |
| 208 | struct timespec emacs_installation_time; | 217 | struct timespec emacs_installation_time; |
| @@ -1865,6 +1874,32 @@ android_init_emacs_cursor (void) | |||
| 1865 | #undef FIND_METHOD | 1874 | #undef FIND_METHOD |
| 1866 | } | 1875 | } |
| 1867 | 1876 | ||
| 1877 | static void | ||
| 1878 | android_init_key_character_map (void) | ||
| 1879 | { | ||
| 1880 | jclass old; | ||
| 1881 | |||
| 1882 | key_character_map_class.class | ||
| 1883 | = (*android_java_env)->FindClass (android_java_env, | ||
| 1884 | "android/view/KeyCharacterMap"); | ||
| 1885 | eassert (key_character_map_class.class); | ||
| 1886 | |||
| 1887 | old = key_character_map_class.class; | ||
| 1888 | key_character_map_class.class | ||
| 1889 | = (jclass) (*android_java_env)->NewGlobalRef (android_java_env, | ||
| 1890 | (jobject) old); | ||
| 1891 | ANDROID_DELETE_LOCAL_REF (old); | ||
| 1892 | |||
| 1893 | if (!key_character_map_class.class) | ||
| 1894 | emacs_abort (); | ||
| 1895 | |||
| 1896 | key_character_map_class.get_dead_char | ||
| 1897 | = (*android_java_env)->GetStaticMethodID (android_java_env, | ||
| 1898 | key_character_map_class.class, | ||
| 1899 | "getDeadChar", "(II)I"); | ||
| 1900 | eassert (key_character_map_class.get_dead_char); | ||
| 1901 | } | ||
| 1902 | |||
| 1868 | JNIEXPORT void JNICALL | 1903 | JNIEXPORT void JNICALL |
| 1869 | NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, | 1904 | NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, |
| 1870 | jobject dump_file_object) | 1905 | jobject dump_file_object) |
| @@ -1913,6 +1948,7 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, | |||
| 1913 | android_init_emacs_drawable (); | 1948 | android_init_emacs_drawable (); |
| 1914 | android_init_emacs_window (); | 1949 | android_init_emacs_window (); |
| 1915 | android_init_emacs_cursor (); | 1950 | android_init_emacs_cursor (); |
| 1951 | android_init_key_character_map (); | ||
| 1916 | 1952 | ||
| 1917 | /* Set HOME to the app data directory. */ | 1953 | /* Set HOME to the app data directory. */ |
| 1918 | setenv ("HOME", android_files_dir, 1); | 1954 | setenv ("HOME", android_files_dir, 1); |
| @@ -5376,11 +5412,51 @@ android_translate_coordinates (android_window src, int x, | |||
| 5376 | ANDROID_DELETE_LOCAL_REF (coordinates); | 5412 | ANDROID_DELETE_LOCAL_REF (coordinates); |
| 5377 | } | 5413 | } |
| 5378 | 5414 | ||
| 5415 | /* Return the character produced by combining the diacritic character | ||
| 5416 | DCHAR with the key-producing character C in *VALUE. Value is 1 if | ||
| 5417 | there is no character for this combination, 0 otherwise. */ | ||
| 5418 | |||
| 5419 | static int | ||
| 5420 | android_get_dead_char (unsigned int dchar, unsigned int c, | ||
| 5421 | unsigned int *value) | ||
| 5422 | { | ||
| 5423 | jmethodID method; | ||
| 5424 | jclass class; | ||
| 5425 | jint result; | ||
| 5426 | |||
| 5427 | /* Call getDeadChar. */ | ||
| 5428 | class = key_character_map_class.class; | ||
| 5429 | method = key_character_map_class.get_dead_char; | ||
| 5430 | result = (*android_java_env)->CallStaticIntMethod (android_java_env, | ||
| 5431 | class, method, | ||
| 5432 | (jint) dchar, | ||
| 5433 | (jint) c); | ||
| 5434 | |||
| 5435 | if (result) | ||
| 5436 | { | ||
| 5437 | *value = result; | ||
| 5438 | return 0; | ||
| 5439 | } | ||
| 5440 | |||
| 5441 | return 1; | ||
| 5442 | } | ||
| 5443 | |||
| 5444 | /* Return a Unicode string in BUFFER_RETURN, a buffer of size | ||
| 5445 | WCHARS_BUFFER, from the key press event EVENT, much like | ||
| 5446 | XmbLookupString. If EVENT represents a key press without a | ||
| 5447 | corresponding Unicode character, return its keysym in *KEYSYM_RETURN. | ||
| 5448 | Return the action taken in *STATUS_RETURN. | ||
| 5449 | |||
| 5450 | COMPOSE_STATUS, if non-NULL, should point to a structure for | ||
| 5451 | temporary information to be stored in during dead key | ||
| 5452 | composition. */ | ||
| 5453 | |||
| 5379 | int | 5454 | int |
| 5380 | android_wc_lookup_string (android_key_pressed_event *event, | 5455 | android_wc_lookup_string (android_key_pressed_event *event, |
| 5381 | wchar_t *buffer_return, int wchars_buffer, | 5456 | wchar_t *buffer_return, int wchars_buffer, |
| 5382 | int *keysym_return, | 5457 | int *keysym_return, |
| 5383 | enum android_lookup_status *status_return) | 5458 | enum android_lookup_status *status_return, |
| 5459 | struct android_compose_status *compose_status) | ||
| 5384 | { | 5460 | { |
| 5385 | enum android_lookup_status status; | 5461 | enum android_lookup_status status; |
| 5386 | int rc; | 5462 | int rc; |
| @@ -5389,6 +5465,7 @@ android_wc_lookup_string (android_key_pressed_event *event, | |||
| 5389 | jsize size; | 5465 | jsize size; |
| 5390 | size_t i; | 5466 | size_t i; |
| 5391 | JNIEnv *env; | 5467 | JNIEnv *env; |
| 5468 | unsigned int unicode_char; | ||
| 5392 | 5469 | ||
| 5393 | env = android_java_env; | 5470 | env = android_java_env; |
| 5394 | status = ANDROID_LOOKUP_NONE; | 5471 | status = ANDROID_LOOKUP_NONE; |
| @@ -5402,6 +5479,13 @@ android_wc_lookup_string (android_key_pressed_event *event, | |||
| 5402 | { | 5479 | { |
| 5403 | if (event->unicode_char) | 5480 | if (event->unicode_char) |
| 5404 | { | 5481 | { |
| 5482 | /* KeyCharacterMap.COMBINING_ACCENT. */ | ||
| 5483 | if ((event->unicode_char & 0x80000000) && compose_status) | ||
| 5484 | goto dead_key; | ||
| 5485 | |||
| 5486 | /* Remove combining accent bits. */ | ||
| 5487 | unicode_char = event->unicode_char & ~0x80000000; | ||
| 5488 | |||
| 5405 | if (wchars_buffer < 1) | 5489 | if (wchars_buffer < 1) |
| 5406 | { | 5490 | { |
| 5407 | *status_return = ANDROID_BUFFER_OVERFLOW; | 5491 | *status_return = ANDROID_BUFFER_OVERFLOW; |
| @@ -5409,7 +5493,31 @@ android_wc_lookup_string (android_key_pressed_event *event, | |||
| 5409 | } | 5493 | } |
| 5410 | else | 5494 | else |
| 5411 | { | 5495 | { |
| 5412 | buffer_return[0] = event->unicode_char; | 5496 | /* If COMPOSE_STATUS holds a diacritic mark unicode_char |
| 5497 | ought to be combined with, and this combination is | ||
| 5498 | valid, return the result alone with no keysym. */ | ||
| 5499 | |||
| 5500 | if (compose_status | ||
| 5501 | && compose_status->chars_matched | ||
| 5502 | && !android_get_dead_char (compose_status->accent, | ||
| 5503 | unicode_char, | ||
| 5504 | &unicode_char)) | ||
| 5505 | { | ||
| 5506 | buffer_return[0] = unicode_char; | ||
| 5507 | *status_return = ANDROID_LOOKUP_CHARS; | ||
| 5508 | compose_status->chars_matched = 0; | ||
| 5509 | return 1; | ||
| 5510 | } | ||
| 5511 | else if (compose_status && compose_status->chars_matched) | ||
| 5512 | { | ||
| 5513 | /* If the combination is valid the compose status must | ||
| 5514 | be reset and no character returned. */ | ||
| 5515 | compose_status->chars_matched = 0; | ||
| 5516 | status = ANDROID_LOOKUP_NONE; | ||
| 5517 | return 0; | ||
| 5518 | } | ||
| 5519 | |||
| 5520 | buffer_return[0] = unicode_char; | ||
| 5413 | status = ANDROID_LOOKUP_CHARS; | 5521 | status = ANDROID_LOOKUP_CHARS; |
| 5414 | rc = 1; | 5522 | rc = 1; |
| 5415 | } | 5523 | } |
| @@ -5426,7 +5534,6 @@ android_wc_lookup_string (android_key_pressed_event *event, | |||
| 5426 | } | 5534 | } |
| 5427 | 5535 | ||
| 5428 | *status_return = status; | 5536 | *status_return = status; |
| 5429 | |||
| 5430 | return rc; | 5537 | return rc; |
| 5431 | } | 5538 | } |
| 5432 | 5539 | ||
| @@ -5482,6 +5589,15 @@ android_wc_lookup_string (android_key_pressed_event *event, | |||
| 5482 | 5589 | ||
| 5483 | *status_return = status; | 5590 | *status_return = status; |
| 5484 | return rc; | 5591 | return rc; |
| 5592 | |||
| 5593 | dead_key: | ||
| 5594 | /* event->unicode_char is a dead key, which are diacritic marks that | ||
| 5595 | should not be directly inserted but instead be combined with a | ||
| 5596 | subsequent character before insertion. */ | ||
| 5597 | *status_return = ANDROID_LOOKUP_NONE; | ||
| 5598 | compose_status->chars_matched = 1; | ||
| 5599 | compose_status->accent = event->unicode_char & ~0x80000000; | ||
| 5600 | return 0; | ||
| 5485 | } | 5601 | } |
| 5486 | 5602 | ||
| 5487 | 5603 | ||
diff --git a/src/androidgui.h b/src/androidgui.h index 89317581191..73b60c483d3 100644 --- a/src/androidgui.h +++ b/src/androidgui.h | |||
| @@ -612,6 +612,15 @@ struct android_window_changes | |||
| 612 | enum android_stack_mode stack_mode; | 612 | enum android_stack_mode stack_mode; |
| 613 | }; | 613 | }; |
| 614 | 614 | ||
| 615 | struct android_compose_status | ||
| 616 | { | ||
| 617 | /* Accent character to be combined with another. */ | ||
| 618 | unsigned int accent; | ||
| 619 | |||
| 620 | /* Number of characters matched. */ | ||
| 621 | int chars_matched; | ||
| 622 | }; | ||
| 623 | |||
| 615 | extern int android_pending (void); | 624 | extern int android_pending (void); |
| 616 | extern void android_next_event (union android_event *); | 625 | extern void android_next_event (union android_event *); |
| 617 | extern bool android_check_if_event (union android_event *, | 626 | extern bool android_check_if_event (union android_event *, |
| @@ -707,7 +716,8 @@ extern void android_translate_coordinates (android_window, int, | |||
| 707 | int, int *, int *); | 716 | int, int *, int *); |
| 708 | extern int android_wc_lookup_string (android_key_pressed_event *, | 717 | extern int android_wc_lookup_string (android_key_pressed_event *, |
| 709 | wchar_t *, int, int *, | 718 | wchar_t *, int, int *, |
| 710 | enum android_lookup_status *); | 719 | enum android_lookup_status *, |
| 720 | struct android_compose_status *); | ||
| 711 | extern void android_recreate_activity (android_window); | 721 | extern void android_recreate_activity (android_window); |
| 712 | extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, | 722 | extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, |
| 713 | ptrdiff_t, ptrdiff_t); | 723 | ptrdiff_t, ptrdiff_t); |
diff --git a/src/androidterm.c b/src/androidterm.c index 2bd2b45743d..baf26abe322 100644 --- a/src/androidterm.c +++ b/src/androidterm.c | |||
| @@ -811,6 +811,7 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 811 | int keysym; | 811 | int keysym; |
| 812 | ptrdiff_t nchars, i; | 812 | ptrdiff_t nchars, i; |
| 813 | struct window *w; | 813 | struct window *w; |
| 814 | static struct android_compose_status compose_status; | ||
| 814 | 815 | ||
| 815 | /* It is okay for this to not resemble handle_one_xevent so much. | 816 | /* It is okay for this to not resemble handle_one_xevent so much. |
| 816 | Differences in event handling code are much less nasty than | 817 | Differences in event handling code are much less nasty than |
| @@ -947,6 +948,14 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 947 | extra_keyboard_modifiers); | 948 | extra_keyboard_modifiers); |
| 948 | modifiers = event->xkey.state; | 949 | modifiers = event->xkey.state; |
| 949 | 950 | ||
| 951 | /* In case Meta is ComposeCharacter, clear its status. According | ||
| 952 | to Markus Ehrnsperger | ||
| 953 | Markus.Ehrnsperger@lehrstuhl-bross.physik.uni-muenchen.de this | ||
| 954 | enables ComposeCharacter to work whether or not it is combined | ||
| 955 | with Meta. */ | ||
| 956 | if (modifiers & ANDROID_ALT_MASK) | ||
| 957 | memset (&compose_status, 0, sizeof (compose_status)); | ||
| 958 | |||
| 950 | /* Common for all keysym input events. */ | 959 | /* Common for all keysym input events. */ |
| 951 | XSETFRAME (inev.ie.frame_or_window, any); | 960 | XSETFRAME (inev.ie.frame_or_window, any); |
| 952 | inev.ie.modifiers | 961 | inev.ie.modifiers |
| @@ -960,7 +969,8 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 960 | 969 | ||
| 961 | nchars = android_wc_lookup_string (&event->xkey, copy_bufptr, | 970 | nchars = android_wc_lookup_string (&event->xkey, copy_bufptr, |
| 962 | copy_bufsiz, &keysym, | 971 | copy_bufsiz, &keysym, |
| 963 | &status_return); | 972 | &status_return, |
| 973 | &compose_status); | ||
| 964 | 974 | ||
| 965 | /* android_lookup_string can't be called twice, so there's no | 975 | /* android_lookup_string can't be called twice, so there's no |
| 966 | way to recover from buffer overflow. */ | 976 | way to recover from buffer overflow. */ |
| @@ -1000,6 +1010,13 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 1000 | } | 1010 | } |
| 1001 | } | 1011 | } |
| 1002 | 1012 | ||
| 1013 | /* If a compose sequence is in progress, we break here. | ||
| 1014 | Otherwise, chars_matched is always 0. */ | ||
| 1015 | if (compose_status.chars_matched > 0 && nchars == 0) | ||
| 1016 | break; | ||
| 1017 | |||
| 1018 | memset (&compose_status, 0, sizeof (compose_status)); | ||
| 1019 | |||
| 1003 | if (nchars == 1 && copy_bufptr[0] >= 32) | 1020 | if (nchars == 1 && copy_bufptr[0] >= 32) |
| 1004 | { | 1021 | { |
| 1005 | /* Deal with characters. */ | 1022 | /* Deal with characters. */ |