]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Sun, 7 May 2023 03:09:56 +0000 (11:09 +0800)
committerPo Lu <luangruo@yahoo.com>
Sun, 7 May 2023 03:09:56 +0000 (11:09 +0800)
* 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
java/org/gnu/emacs/EmacsNative.java
java/org/gnu/emacs/EmacsService.java
src/android.c
src/androidfns.c
src/androidgui.h
src/androidterm.c
src/androidterm.h

index d13b48288ce07dda5e103b5ae81219a3fee39a44..21bbaca5d075acfc30b4c2a5e79c317db82c0cf6 100644 (file)
@@ -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;
+  }
+
 \f
   /* Override functions which are not implemented.  */
 
index 7d13ff99abb5156a017315b11cf8e16fdee42c2c..e699dda9ad407d84d18440159ff1ff85f3c8d5be 100644 (file)
@@ -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);
 
 \f
   /* Return the current value of the selection, or -1 upon
index 33436892caa4ad849b7db1ff6d043ebb4db19c7a..30ef71540a9d21e4bcbfa1478682a6e231d1e832 100644 (file)
@@ -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.
index 129ad6b576718252ba8038412956bb9e18f3b466..8a41a7cdec544f73ed1242dfe19a521b09fc9079 100644 (file)
@@ -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 ();
+}
+
 \f
 
 /* Window decoration management functions.  */
index 3bd34edd5b98b5a29c00a76ca073efbe51634730..60b0549e7d142b1255109dc8ee68fc7c4f7d7b73 100644 (file)
@@ -3000,6 +3000,34 @@ for more details about these values.  */)
                make_fixnum (state.temperature));
 }
 
+\f
+
+/* 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
 
 \f
index ddd8e9fcf72afd558dd38d05dd4b939e35613916..6db25098398faec62ab27fa31209cbc6babff165 100644 (file)
@@ -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
index 8ba7fb6a7989add95bc00f235b6edbbcf862ccd6..6f7c06875cad13032beedea349368b71c9fa2b62 100644 (file)
@@ -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
index 9396d5fe315d003d24e4faca80f836f92aa073ca..e3738fb2192056f3790157dfbf61ae1506b84140 100644 (file)
@@ -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.  */