aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2024-03-02 14:04:56 +0800
committerPo Lu2024-03-02 14:04:56 +0800
commit5e20b114ef32d504f4429fd35ecd0d5dcf3bd8db (patch)
tree83215f4df69308e61c490172f4a68ff0fea63452 /src
parent8b96503b6e8514f1f9f92895a0707c78b1bbd1fd (diff)
downloademacs-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.c122
-rw-r--r--src/androidgui.h12
-rw-r--r--src/androidterm.c19
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
126struct 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. */
127static int android_api_level; 133static 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. */
204static struct android_emacs_cursor cursor_class; 210static struct android_emacs_cursor cursor_class;
205 211
212/* Various methods associated with the KeyCharacterMap class. */
213static 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. */
208struct timespec emacs_installation_time; 217struct 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
1877static void
1878android_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
1868JNIEXPORT void JNICALL 1903JNIEXPORT void JNICALL
1869NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, 1904NATIVE_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
5419static int
5420android_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
5379int 5454int
5380android_wc_lookup_string (android_key_pressed_event *event, 5455android_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
615struct 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
615extern int android_pending (void); 624extern int android_pending (void);
616extern void android_next_event (union android_event *); 625extern void android_next_event (union android_event *);
617extern bool android_check_if_event (union android_event *, 626extern 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 *);
708extern int android_wc_lookup_string (android_key_pressed_event *, 717extern 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 *);
711extern void android_recreate_activity (android_window); 721extern void android_recreate_activity (android_window);
712extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, 722extern 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. */