emacs-diffs
[Top][All Lists]
Advanced

[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.  */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]