emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android 889b61b9991: Update Android port


From: Po Lu
Subject: feature/android 889b61b9991: Update Android port
Date: Sat, 6 May 2023 23:10:46 -0400 (EDT)

branch: feature/android
commit 889b61b99918d1c6313d4f884de2e2cb3ab466c9
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Update Android port
    
    * java/org/gnu/emacs/EmacsInputConnection.java
    (requestCursorUpdates):
    * java/org/gnu/emacs/EmacsNative.java (requestCursorUpdates):
    * java/org/gnu/emacs/EmacsService.java (updateCursorAnchorInfo):
    New functions.
    * src/android.c (struct android_emacs_service)
    (android_init_emacs_service): Add new method.
    (android_update_cursor_anchor_info): New function.
    * src/androidfns.c (android_set_preeditarea): New function.
    * src/androidgui.h (enum android_ime_operation): New operation
    `REQUEST_CURSOR_UPDATES'.
    (struct android_ime_event): Document new meaning of `length'.
    * src/androidterm.c (android_request_cursor_updates): New
    function.
    (android_handle_ime_event): Handle new operations.
    (handle_one_android_event, android_draw_window_cursor): Update
    the preedit area if needed, like on X.
    (requestCursorUpdates): New function.
    * src/androidterm.h (struct android_output): New field
    `need_cursor_updates'.
---
 java/org/gnu/emacs/EmacsInputConnection.java | 11 ++++
 java/org/gnu/emacs/EmacsNative.java          |  1 +
 java/org/gnu/emacs/EmacsService.java         | 32 ++++++++++
 src/android.c                                | 34 +++++++++++
 src/androidfns.c                             | 28 +++++++++
 src/androidgui.h                             | 15 ++++-
 src/androidterm.c                            | 88 ++++++++++++++++++++++++++++
 src/androidterm.h                            |  5 ++
 8 files changed, 213 insertions(+), 1 deletion(-)

diff --git a/java/org/gnu/emacs/EmacsInputConnection.java 
b/java/org/gnu/emacs/EmacsInputConnection.java
index d13b48288ce..21bbaca5d07 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -324,6 +324,17 @@ public final class EmacsInputConnection extends 
BaseInputConnection
     return this.deleteSurroundingText (beforeLength, afterLength);
   }
 
+  @Override
+  public boolean
+  requestCursorUpdates (int cursorUpdateMode)
+  {
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
+
+    EmacsNative.requestCursorUpdates (windowHandle, cursorUpdateMode);
+    return true;
+  }
+
 
   /* Override functions which are not implemented.  */
 
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index 7d13ff99abb..e699dda9ad4 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -209,6 +209,7 @@ public final class EmacsNative
                                                       ExtractedTextRequest req,
                                                       int flags);
   public static native void requestSelectionUpdate (short window);
+  public static native void requestCursorUpdates (short window, int mode);
 
 
   /* Return the current value of the selection, or -1 upon
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 33436892caa..30ef71540a9 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -25,10 +25,12 @@ import java.io.UnsupportedEncodingException;
 
 import java.util.List;
 
+import android.graphics.Matrix;
 import android.graphics.Point;
 
 import android.view.InputDevice;
 import android.view.KeyEvent;
+import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.ExtractedText;
 
 import android.app.Notification;
@@ -635,6 +637,36 @@ public final class EmacsService extends Service
     window.view.imManager.restartInput (window.view);
   }
 
+  public void
+  updateCursorAnchorInfo (EmacsWindow window, float x,
+                         float y, float yBaseline,
+                         float yBottom)
+  {
+    CursorAnchorInfo info;
+    CursorAnchorInfo.Builder builder;
+    Matrix matrix;
+    int[] offsets;
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+      return;
+
+    offsets = new int[2];
+    builder = new CursorAnchorInfo.Builder ();
+    matrix = new Matrix (window.view.getMatrix ());
+    window.view.getLocationOnScreen (offsets);
+    matrix.postTranslate (offsets[0], offsets[1]);
+    builder.setMatrix (matrix);
+    builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom,
+                                       0);
+    info = builder.build ();
+
+    if (DEBUG_IC)
+      Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y
+                  + " " + yBaseline + "-" + yBottom));
+
+    window.view.imManager.updateCursorAnchorInfo (window.view, info);
+  }
+
   /* Open a content URI described by the bytes BYTES, a non-terminated
      string; make it writable if WRITABLE, and readable if READABLE.
      Truncate the file if TRUNCATE.
diff --git a/src/android.c b/src/android.c
index 129ad6b5767..8a41a7cdec5 100644
--- a/src/android.c
+++ b/src/android.c
@@ -113,6 +113,7 @@ struct android_emacs_service
   jmethodID query_battery;
   jmethodID display_toast;
   jmethodID update_extracted_text;
+  jmethodID update_cursor_anchor_info;
 };
 
 struct android_emacs_pixmap
@@ -2209,6 +2210,8 @@ android_init_emacs_service (void)
   FIND_METHOD (update_extracted_text, "updateExtractedText",
               "(Lorg/gnu/emacs/EmacsWindow;"
               "Landroid/view/inputmethod/ExtractedText;I)V");
+  FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo",
+              "(Lorg/gnu/emacs/EmacsWindow;FFFF)V");
 #undef FIND_METHOD
 }
 
@@ -6277,6 +6280,37 @@ android_update_extracted_text (android_window window, 
void *text,
   android_exception_check_1 (text);
 }
 
+/* Report the position of the cursor to the input method connection on
+   WINDOW.
+
+   X is the horizontal position of the end of the insertion marker.  Y
+   is the top of the insertion marker.  Y_BASELINE is the baseline of
+   the row containing the insertion marker, and Y_BOTTOM is the bottom
+   of the insertion marker.  */
+
+void
+android_update_cursor_anchor_info (android_window window, float x,
+                                  float y, float y_baseline,
+                                  float y_bottom)
+{
+  jobject object;
+  jmethodID method;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  method = service_class.update_cursor_anchor_info;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                method,
+                                                object,
+                                                (jfloat) x,
+                                                (jfloat) y,
+                                                (jfloat) y_baseline,
+                                                (jfloat) y_bottom);
+  android_exception_check ();
+}
+
 
 
 /* Window decoration management functions.  */
diff --git a/src/androidfns.c b/src/androidfns.c
index 3bd34edd5b9..60b0549e7d1 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -3000,6 +3000,34 @@ for more details about these values.  */)
                make_fixnum (state.temperature));
 }
 
+
+
+/* Miscellaneous input method related stuff.  */
+
+/* Report X, Y, by the phys cursor width and height as the cursor
+   anchor rectangle for W's frame.  */
+
+void
+android_set_preeditarea (struct window *w, int x, int y)
+{
+  struct frame *f;
+
+  f = WINDOW_XFRAME (w);
+
+  /* Convert the window coordinates to the frame's coordinate
+     space.  */
+  x = (WINDOW_TO_FRAME_PIXEL_X (w, x)
+       + WINDOW_LEFT_FRINGE_WIDTH (w)
+       + WINDOW_LEFT_MARGIN_WIDTH (w));
+  y = WINDOW_TO_FRAME_PIXEL_Y (w, y);
+
+  /* Note that calculating the baseline is too hard, so the bottom of
+     the cursor is used instead.  */
+  android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x,
+                                    y, y + w->phys_cursor_height,
+                                    y + w->phys_cursor_height);
+}
+
 #endif
 
 
diff --git a/src/androidgui.h b/src/androidgui.h
index ddd8e9fcf72..6db25098398 100644
--- a/src/androidgui.h
+++ b/src/androidgui.h
@@ -432,6 +432,13 @@ enum android_ime_operation
     ANDROID_IME_START_BATCH_EDIT,
     ANDROID_IME_END_BATCH_EDIT,
     ANDROID_IME_REQUEST_SELECTION_UPDATE,
+    ANDROID_IME_REQUEST_CURSOR_UPDATES,
+  };
+
+enum
+  {
+    ANDROID_CURSOR_UPDATE_IMMEDIATE = 1,
+    ANDROID_CURSOR_UPDATE_MONITOR   = (1 << 1),
   };
 
 struct android_ime_event
@@ -452,7 +459,11 @@ struct android_ime_event
      indices, and may actually mean ``left'' and ``right''.  */
   ptrdiff_t start, end, position;
 
-  /* The number of characters in TEXT.  */
+  /* The number of characters in TEXT.
+
+     If OPERATION is ANDROID_IME_REQUEST_CURSOR_UPDATES, then this is
+     actually the cursor update mode associated with that
+     operation.  */
   size_t length;
 
   /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced
@@ -620,6 +631,8 @@ extern void android_update_ic (android_window, ptrdiff_t, 
ptrdiff_t,
 extern void android_reset_ic (android_window, enum android_ic_mode);
 extern void android_update_extracted_text (android_window, void *,
                                           int);
+extern void android_update_cursor_anchor_info (android_window, float,
+                                              float, float, float);
 extern int android_set_fullscreen (android_window, bool);
 
 enum android_cursor_shape
diff --git a/src/androidterm.c b/src/androidterm.c
index 8ba7fb6a798..6f7c06875ca 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -632,6 +632,40 @@ android_decode_utf16 (unsigned short *utf16, size_t n)
   return coding.dst_object;
 }
 
+/* Handle a cursor update request for F from the input method.
+   MODE specifies whether or not an update should be sent immediately,
+   and whether or not they are needed in the future.
+
+   If MODE & ANDROID_CURSOR_UPDATE_IMMEDIATE, report the position of
+   F's old selected window's phys cursor now.
+
+   If MODE & ANDROID_CURSOR_UPDATE_MONITOR, set
+   `need_cursor_updates'.  */
+
+static void
+android_request_cursor_updates (struct frame *f, int mode)
+{
+  struct window *w;
+
+  if (mode & ANDROID_CURSOR_UPDATE_IMMEDIATE
+      && WINDOWP (WINDOW_LIVE_P (f->old_selected_window)
+                 ? f->old_selected_window
+                 : f->selected_window))
+    {
+      /* Prefer the old selected window, as its selection is what was
+        reported to the IME previously.  */
+
+      w = XWINDOW (WINDOW_LIVE_P (f->old_selected_window)
+                  ? f->old_selected_window
+                  : f->selected_window);
+      android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+    }
+
+  /* Now say whether or not updates are needed in the future.  */
+  FRAME_OUTPUT_DATA (f)->need_cursor_updates
+    = (mode & ANDROID_CURSOR_UPDATE_MONITOR);
+}
+
 /* Handle a single input method event EVENT, delivered to the frame
    F.
 
@@ -705,6 +739,10 @@ android_handle_ime_event (union android_event *event, 
struct frame *f)
     case ANDROID_IME_REQUEST_SELECTION_UPDATE:
       request_point_update (f, event->ime.counter);
       break;
+
+    case ANDROID_IME_REQUEST_CURSOR_UPDATES:
+      android_request_cursor_updates (f, event->ime.length);
+      break;
     }
 }
 
@@ -724,6 +762,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
   double scroll_unit;
   int keysym;
   ptrdiff_t nchars, i;
+  struct window *w;
 
   /* It is okay for this to not resemble handle_one_xevent so much.
      Differences in event handling code are much less nasty than
@@ -812,6 +851,12 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
            inev.ie.kind = MOVE_FRAME_EVENT;
            XSETFRAME (inev.ie.frame_or_window, f);
          }
+
+      if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+       {
+         w = XWINDOW (f->selected_window);
+         android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+       }
       }
 
       goto OTHER;
@@ -954,6 +999,16 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
       goto done_keysym;
 
     done_keysym:
+
+      /* Now proceed to tell the input method the current position of
+        the cursor, if required.  */
+
+      if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+       {
+         w = XWINDOW (f->selected_window);
+         android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+       }
+
       goto OTHER;
 
     case ANDROID_FOCUS_IN:
@@ -4321,6 +4376,10 @@ android_draw_window_cursor (struct window *w, struct 
glyph_row *glyph_row,
                            int x, int y, enum text_cursor_kinds cursor_type,
                            int cursor_width, bool on_p, bool active_p)
 {
+  struct frame *f;
+
+  f = WINDOW_XFRAME (w);
+
   if (on_p)
     {
       w->phys_cursor_type = cursor_type;
@@ -4362,6 +4421,13 @@ android_draw_window_cursor (struct window *w, struct 
glyph_row *glyph_row,
              emacs_abort ();
            }
        }
+
+      /* Now proceed to tell the input method the current position of
+        the cursor, if required.  */
+
+      if (FRAME_OUTPUT_DATA (f)->need_cursor_updates
+         && w == XWINDOW (f->selected_window))
+       android_set_preeditarea (w, x, y);
     }
 }
 
@@ -5404,6 +5470,28 @@ NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, 
jobject object,
   android_write_event (&event);
 }
 
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object,
+                                   jshort window, jint mode)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_REQUEST_CURSOR_UPDATES;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = mode;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
 #ifdef __clang__
 #pragma clang diagnostic pop
 #else
diff --git a/src/androidterm.h b/src/androidterm.h
index 9396d5fe315..e3738fb2192 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -231,6 +231,10 @@ struct android_output
      because the frame contents have been dirtied.  */
   bool_bf need_buffer_flip : 1;
 
+  /* Whether or not the input method should be notified every time the
+     position of this frame's selected window changes.  */
+  bool_bf need_cursor_updates : 1;
+
   /* Relief GCs, colors etc.  */
   struct relief {
     struct android_gc *gc;
@@ -383,6 +387,7 @@ extern struct android_display_info *x_display_list;
 
 extern void android_free_gcs (struct frame *);
 extern void android_default_font_parameter (struct frame *, Lisp_Object);
+extern void android_set_preeditarea (struct window *, int, int);
 
 /* Defined in androidterm.c.  */
 



reply via email to

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