[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 5e20b114ef3: Implement dead key combination on Android
From: |
Po Lu |
Subject: |
master 5e20b114ef3: Implement dead key combination on Android |
Date: |
Sat, 2 Mar 2024 01:06:10 -0500 (EST) |
branch: master
commit 5e20b114ef32d504f4429fd35ecd0d5dcf3bd8db
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
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)
---
src/android.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/androidgui.h | 12 +++++-
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
jmethodID constructor;
};
+struct android_key_character_map
+{
+ jclass class;
+ jmethodID get_dead_char;
+};
+
/* The API level of the current device. */
static int android_api_level;
@@ -203,6 +209,9 @@ static struct android_emacs_window window_class;
/* Various methods associated with the EmacsCursor class. */
static struct android_emacs_cursor cursor_class;
+/* Various methods associated with the KeyCharacterMap class. */
+static struct android_key_character_map key_character_map_class;
+
/* The time at which Emacs was installed, which also supplies the
mtime of asset files. */
struct timespec emacs_installation_time;
@@ -1865,6 +1874,32 @@ android_init_emacs_cursor (void)
#undef FIND_METHOD
}
+static void
+android_init_key_character_map (void)
+{
+ jclass old;
+
+ key_character_map_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "android/view/KeyCharacterMap");
+ eassert (key_character_map_class.class);
+
+ old = key_character_map_class.class;
+ key_character_map_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!key_character_map_class.class)
+ emacs_abort ();
+
+ key_character_map_class.get_dead_char
+ = (*android_java_env)->GetStaticMethodID (android_java_env,
+ key_character_map_class.class,
+ "getDeadChar", "(II)I");
+ eassert (key_character_map_class.get_dead_char);
+}
+
JNIEXPORT void JNICALL
NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv,
jobject dump_file_object)
@@ -1913,6 +1948,7 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object,
jarray argv,
android_init_emacs_drawable ();
android_init_emacs_window ();
android_init_emacs_cursor ();
+ android_init_key_character_map ();
/* Set HOME to the app data directory. */
setenv ("HOME", android_files_dir, 1);
@@ -5376,11 +5412,51 @@ android_translate_coordinates (android_window src, int
x,
ANDROID_DELETE_LOCAL_REF (coordinates);
}
+/* Return the character produced by combining the diacritic character
+ DCHAR with the key-producing character C in *VALUE. Value is 1 if
+ there is no character for this combination, 0 otherwise. */
+
+static int
+android_get_dead_char (unsigned int dchar, unsigned int c,
+ unsigned int *value)
+{
+ jmethodID method;
+ jclass class;
+ jint result;
+
+ /* Call getDeadChar. */
+ class = key_character_map_class.class;
+ method = key_character_map_class.get_dead_char;
+ result = (*android_java_env)->CallStaticIntMethod (android_java_env,
+ class, method,
+ (jint) dchar,
+ (jint) c);
+
+ if (result)
+ {
+ *value = result;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return a Unicode string in BUFFER_RETURN, a buffer of size
+ WCHARS_BUFFER, from the key press event EVENT, much like
+ XmbLookupString. If EVENT represents a key press without a
+ corresponding Unicode character, return its keysym in *KEYSYM_RETURN.
+ Return the action taken in *STATUS_RETURN.
+
+ COMPOSE_STATUS, if non-NULL, should point to a structure for
+ temporary information to be stored in during dead key
+ composition. */
+
int
android_wc_lookup_string (android_key_pressed_event *event,
wchar_t *buffer_return, int wchars_buffer,
int *keysym_return,
- enum android_lookup_status *status_return)
+ enum android_lookup_status *status_return,
+ struct android_compose_status *compose_status)
{
enum android_lookup_status status;
int rc;
@@ -5389,6 +5465,7 @@ android_wc_lookup_string (android_key_pressed_event
*event,
jsize size;
size_t i;
JNIEnv *env;
+ unsigned int unicode_char;
env = android_java_env;
status = ANDROID_LOOKUP_NONE;
@@ -5402,6 +5479,13 @@ android_wc_lookup_string (android_key_pressed_event
*event,
{
if (event->unicode_char)
{
+ /* KeyCharacterMap.COMBINING_ACCENT. */
+ if ((event->unicode_char & 0x80000000) && compose_status)
+ goto dead_key;
+
+ /* Remove combining accent bits. */
+ unicode_char = event->unicode_char & ~0x80000000;
+
if (wchars_buffer < 1)
{
*status_return = ANDROID_BUFFER_OVERFLOW;
@@ -5409,7 +5493,31 @@ android_wc_lookup_string (android_key_pressed_event
*event,
}
else
{
- buffer_return[0] = event->unicode_char;
+ /* If COMPOSE_STATUS holds a diacritic mark unicode_char
+ ought to be combined with, and this combination is
+ valid, return the result alone with no keysym. */
+
+ if (compose_status
+ && compose_status->chars_matched
+ && !android_get_dead_char (compose_status->accent,
+ unicode_char,
+ &unicode_char))
+ {
+ buffer_return[0] = unicode_char;
+ *status_return = ANDROID_LOOKUP_CHARS;
+ compose_status->chars_matched = 0;
+ return 1;
+ }
+ else if (compose_status && compose_status->chars_matched)
+ {
+ /* If the combination is valid the compose status must
+ be reset and no character returned. */
+ compose_status->chars_matched = 0;
+ status = ANDROID_LOOKUP_NONE;
+ return 0;
+ }
+
+ buffer_return[0] = unicode_char;
status = ANDROID_LOOKUP_CHARS;
rc = 1;
}
@@ -5426,7 +5534,6 @@ android_wc_lookup_string (android_key_pressed_event
*event,
}
*status_return = status;
-
return rc;
}
@@ -5482,6 +5589,15 @@ android_wc_lookup_string (android_key_pressed_event
*event,
*status_return = status;
return rc;
+
+ dead_key:
+ /* event->unicode_char is a dead key, which are diacritic marks that
+ should not be directly inserted but instead be combined with a
+ subsequent character before insertion. */
+ *status_return = ANDROID_LOOKUP_NONE;
+ compose_status->chars_matched = 1;
+ compose_status->accent = event->unicode_char & ~0x80000000;
+ return 0;
}
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
enum android_stack_mode stack_mode;
};
+struct android_compose_status
+{
+ /* Accent character to be combined with another. */
+ unsigned int accent;
+
+ /* Number of characters matched. */
+ int chars_matched;
+};
+
extern int android_pending (void);
extern void android_next_event (union android_event *);
extern bool android_check_if_event (union android_event *,
@@ -707,7 +716,8 @@ extern void android_translate_coordinates (android_window,
int,
int, int *, int *);
extern int android_wc_lookup_string (android_key_pressed_event *,
wchar_t *, int, int *,
- enum android_lookup_status *);
+ enum android_lookup_status *,
+ struct android_compose_status *);
extern void android_recreate_activity (android_window);
extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t,
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,
int keysym;
ptrdiff_t nchars, i;
struct window *w;
+ static struct android_compose_status compose_status;
/* It is okay for this to not resemble handle_one_xevent so much.
Differences in event handling code are much less nasty than
@@ -947,6 +948,14 @@ handle_one_android_event (struct android_display_info
*dpyinfo,
extra_keyboard_modifiers);
modifiers = event->xkey.state;
+ /* In case Meta is ComposeCharacter, clear its status. According
+ to Markus Ehrnsperger
+ Markus.Ehrnsperger@lehrstuhl-bross.physik.uni-muenchen.de this
+ enables ComposeCharacter to work whether or not it is combined
+ with Meta. */
+ if (modifiers & ANDROID_ALT_MASK)
+ memset (&compose_status, 0, sizeof (compose_status));
+
/* Common for all keysym input events. */
XSETFRAME (inev.ie.frame_or_window, any);
inev.ie.modifiers
@@ -960,7 +969,8 @@ handle_one_android_event (struct android_display_info
*dpyinfo,
nchars = android_wc_lookup_string (&event->xkey, copy_bufptr,
copy_bufsiz, &keysym,
- &status_return);
+ &status_return,
+ &compose_status);
/* android_lookup_string can't be called twice, so there's no
way to recover from buffer overflow. */
@@ -1000,6 +1010,13 @@ handle_one_android_event (struct android_display_info
*dpyinfo,
}
}
+ /* If a compose sequence is in progress, we break here.
+ Otherwise, chars_matched is always 0. */
+ if (compose_status.chars_matched > 0 && nchars == 0)
+ break;
+
+ memset (&compose_status, 0, sizeof (compose_status));
+
if (nchars == 1 && copy_bufptr[0] >= 32)
{
/* Deal with characters. */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master 5e20b114ef3: Implement dead key combination on Android,
Po Lu <=