emacs-diffs
[Top][All Lists]
Advanced

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

feature/android 63339a9577f: Update Android port


From: Po Lu
Subject: feature/android 63339a9577f: Update Android port
Date: Tue, 6 Jun 2023 23:04:41 -0400 (EDT)

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

    Update Android port
    
    * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit)
    (endBatchEdit, commitCompletion, commitText, deleteSurroundingText)
    (finishComposingText, getSelectedText, getTextAfterCursor)
    (getTextBeforeCursor, setComposingText, setComposingRegion)
    (performEditorAction, performContextMenuAction, getExtractedText)
    (setSelection, sendKeyEvent, deleteSurroundingTextInCodePoints)
    (requestCursorUpdates): Ensure that the input connection is up
    to date.
    (getSurroundingText): New function.
    * java/org/gnu/emacs/EmacsNative.java (getSurroundingText):
    Export new C function.
    * java/org/gnu/emacs/EmacsService.java (resetIC): Invalidate
    previously created input connections.
    * java/org/gnu/emacs/EmacsView.java (EmacsView)
    (onCreateInputConnection): Signify that input connections are
    now up to date.
    * src/androidterm.c (struct
    android_get_surrounding_text_context): New structure.
    (android_get_surrounding_text, NATIVE_NAME):
    * src/textconv.c (get_surrounding_text):
    * src/textconv.h: New functions.
---
 java/org/gnu/emacs/EmacsInputConnection.java | 105 ++++++++++++++++++++
 java/org/gnu/emacs/EmacsNative.java          |   4 +
 java/org/gnu/emacs/EmacsService.java         |   1 +
 java/org/gnu/emacs/EmacsView.java            |  13 +++
 src/androidterm.c                            | 143 +++++++++++++++++++++++++++
 src/textconv.c                               | 100 +++++++++++++++++++
 src/textconv.h                               |   4 +
 7 files changed, 370 insertions(+)

diff --git a/java/org/gnu/emacs/EmacsInputConnection.java 
b/java/org/gnu/emacs/EmacsInputConnection.java
index 9ced7cb7aaf..73c93c67ac7 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -23,7 +23,9 @@ import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.SurroundingText;
 import android.view.inputmethod.TextSnapshot;
+
 import android.view.KeyEvent;
 
 import android.os.Build;
@@ -88,6 +90,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   beginBatchEdit ()
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "beginBatchEdit");
 
@@ -99,6 +105,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   endBatchEdit ()
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "endBatchEdit");
 
@@ -110,6 +120,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   commitCompletion (CompletionInfo info)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "commitCompletion: " + info);
 
@@ -125,6 +139,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   {
     int[] selection;
 
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
 
@@ -156,6 +174,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   deleteSurroundingText (int leftLength, int rightLength)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, ("deleteSurroundingText: "
                   + leftLength + " " + rightLength));
@@ -169,6 +191,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   finishComposingText ()
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "finishComposingText");
 
@@ -180,6 +206,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public String
   getSelectedText (int flags)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "getSelectedText: " + flags);
 
@@ -192,6 +222,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   {
     String string;
 
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
 
@@ -210,6 +244,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   {
     String string;
 
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
 
@@ -226,6 +264,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   setComposingText (CharSequence text, int newCursorPosition)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, ("setComposingText: "
                   + text + " ## " + newCursorPosition));
@@ -239,6 +281,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   setComposingRegion (int start, int end)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "setComposingRegion: " + start + " " + end);
 
@@ -250,6 +296,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   performEditorAction (int editorAction)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "performEditorAction: " + editorAction);
 
@@ -263,6 +313,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   {
     int action;
 
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "performContextMenuAction: " + contextMenuAction);
 
@@ -310,6 +364,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
     ExtractedText text;
     int[] selection;
 
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", "
             + request.hintMaxLines + " " + flags);
@@ -360,6 +418,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   setSelection (int start, int end)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "setSelection: " + start + " " + end);
 
@@ -371,6 +433,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   sendKeyEvent (KeyEvent key)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "sendKeyEvent: " + key);
 
@@ -381,6 +447,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   deleteSurroundingTextInCodePoints (int beforeLength, int afterLength)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     /* This can be implemented the same way as
        deleteSurroundingText.  */
     return this.deleteSurroundingText (beforeLength, afterLength);
@@ -390,6 +460,10 @@ public final class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   requestCursorUpdates (int cursorUpdateMode)
   {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
 
@@ -397,6 +471,37 @@ public final class EmacsInputConnection extends 
BaseInputConnection
     return true;
   }
 
+  @Override
+  public SurroundingText
+  getSurroundingText (int beforeLength, int afterLength,
+                     int flags)
+  {
+    SurroundingText text;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("getSurroundingText: " + beforeLength + ", "
+                  + afterLength));
+
+    text = EmacsNative.getSurroundingText (windowHandle, beforeLength,
+                                          afterLength, flags);
+
+    if (text != null)
+      Log.d (TAG, ("getSurroundingText: "
+                  + text.getSelectionStart ()
+                  + ","
+                  + text.getSelectionEnd ()
+                  + "+"
+                  + text.getOffset ()
+                  + ": "
+                  + text.getText ()));
+
+    return text;
+  }
+
 
   /* Override functions which are not implemented.  */
 
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index cb1c6caa79a..cb89cf6808a 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -25,6 +25,7 @@ import android.graphics.Bitmap;
 
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.SurroundingText;
 
 public final class EmacsNative
 {
@@ -222,6 +223,9 @@ public final class EmacsNative
   public static native void requestSelectionUpdate (short window);
   public static native void requestCursorUpdates (short window, int mode);
   public static native void clearInputFlags (short window);
+  public static native SurroundingText getSurroundingText (short window,
+                                                          int left, int right,
+                                                          int flags);
 
 
   /* 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 2074a5b7c2b..48e39f8b355 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -748,6 +748,7 @@ public final class EmacsService extends Service
     window.view.setICMode (icMode);
 
     icBeginSynchronous ();
+    window.view.icGeneration++;
     window.view.imManager.restartInput (window.view);
     icEndSynchronous ();
   }
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index a78dec08839..d432162132d 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -111,6 +111,13 @@ public final class EmacsView extends ViewGroup
      details.  */
   private int icMode;
 
+  /* The number of calls to `resetIC' to have taken place the last
+     time an InputConnection was created.  */
+  public long icSerial;
+
+  /* The number of calls to `recetIC' that have taken place.  */
+  public volatile long icGeneration;
+
   public
   EmacsView (EmacsWindow window)
   {
@@ -627,6 +634,12 @@ public final class EmacsView extends ViewGroup
        return null;
       }
 
+    /* Set icSerial.  If icSerial < icGeneration, the input connection
+       has been reset, and future input should be ignored until a new
+       connection is created.  */
+
+    icSerial = icGeneration;
+
     /* Reset flags set by the previous input method.  */
 
     EmacsNative.clearInputFlags (window.handle);
diff --git a/src/androidterm.c b/src/androidterm.c
index 2a054715d6a..77f2bd1c7a0 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -5636,6 +5636,149 @@ NATIVE_NAME (clearInputFlags) (JNIEnv *env, jobject 
object,
   android_write_event (&event);
 }
 
+
+
+/* Context for a call to `getSurroundingText'.  */
+
+struct android_get_surrounding_text_context
+{
+  /* Number of characters before the region to return.  */
+  int before_length;
+
+  /* Number of characters after the region to return.  */
+  int after_length;
+
+  /* The returned text, or NULL.  */
+  char *text;
+
+  /* The size of that text in characters and bytes.  */
+  ptrdiff_t length, bytes;
+
+  /* Offsets into that text.  */
+  ptrdiff_t offset, start, end;
+
+  /* The window.  */
+  android_window window;
+};
+
+/* Return the surrounding text in the surrounding text context
+   specified by DATA.  */
+
+static void
+android_get_surrounding_text (void *data)
+{
+  struct android_get_surrounding_text_context *request;
+  struct frame *f;
+  ptrdiff_t temp;
+
+  request = data;
+
+  /* Find the frame associated with the window.  */
+  f = android_window_to_frame (NULL, request->window);
+
+  if (!f)
+    return;
+
+  /* Now get the surrounding text.  */
+  request->text
+    = get_surrounding_text (f, request->before_length,
+                           request->after_length, &request->length,
+                           &request->bytes, &request->offset,
+                           &request->start, &request->end);
+
+  /* Sort request->start and request->end for compatibility with some
+     bad input methods.  */
+
+  if (request->end < request->start)
+    {
+      temp = request->start;
+      request->start = request->end;
+      request->end = temp;
+    }
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object,
+                                 jshort window, jint before_length,
+                                 jint after_length, jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  static jclass class;
+  static jmethodID constructor;
+
+  struct android_get_surrounding_text_context context;
+  jstring string;
+  jobject object;
+
+  /* Initialize CLASS if it has not yet been initialized.  */
+
+  if (!class)
+    {
+      class
+       = (*env)->FindClass (env, ("android/view/inputmethod"
+                                  "/SurroundingText"));
+
+#if __ANDROID_API__ < 31
+      /* If CLASS cannot be found, the version of Android currently
+        running is too old.  */
+
+      if (!class)
+       {
+         (*env)->ExceptionClear (env);
+         return NULL;
+       }
+#else /* __ANDROID_API__ >= 31 */
+      assert (class);
+#endif /* __ANDROID_API__ < 31 */
+
+      class = (*env)->NewGlobalRef (env, class);
+      if (!class)
+       return NULL;
+
+      /* Now look for its constructor.  */
+      constructor = (*env)->GetMethodID (env, class, "<init>",
+                                        "(Ljava/lang/CharSequence;III)V");
+      assert (constructor);
+    }
+
+  context.before_length = before_length;
+  context.after_length = after_length;
+  context.window = window;
+  context.text = NULL;
+
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_get_surrounding_text,
+                                  &context))
+    return NULL;
+
+  if (!context.text)
+    return NULL;
+
+  /* Encode the returned text.  */
+  string = android_text_to_string (env, context.text, context.length,
+                                  context.bytes);
+  free (context.text);
+
+  if (!string)
+    return NULL;
+
+  /* Create an SurroundingText object containing this information.  */
+  object = (*env)->NewObject (env, class, constructor, string,
+                             (jint) min (context.start,
+                                         TYPE_MAXIMUM (jint)),
+                             (jint) min (context.end,
+                                         TYPE_MAXIMUM (jint)),
+                             /* Adjust point offsets to fit into
+                                Android's 0-based indexing. */
+                             (jint) min (context.offset - 1,
+                                         TYPE_MAXIMUM (jint)));
+  if (!object)
+    return NULL;
+
+  return object;
+}
+
 #ifdef __clang__
 #pragma clang diagnostic pop
 #else
diff --git a/src/textconv.c b/src/textconv.c
index 0dcf5bdcea8..1161b781b6a 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -1732,6 +1732,106 @@ get_extracted_text (struct frame *f, ptrdiff_t n,
   return buffer;
 }
 
+/* Return the text between the positions PT - LEFT and PT + RIGHT.  If
+   the mark is active, return the range of text relative to the bounds
+   of the region instead.
+
+   Set *LENGTH to the number of characters returned, *BYTES to the
+   number of bytes returned, *OFFSET to the character position of the
+   returned text, and *START_RETURN and *END_RETURN to the mark and
+   point relative to that position.  */
+
+char *
+get_surrounding_text (struct frame *f, ptrdiff_t left,
+                     ptrdiff_t right, ptrdiff_t *length,
+                     ptrdiff_t *bytes, ptrdiff_t *offset,
+                     ptrdiff_t *start_return,
+                     ptrdiff_t *end_return)
+{
+  specpdl_ref count;
+  ptrdiff_t start, end, start_byte, end_byte, mark, temp;
+  char *buffer;
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return NULL;
+
+  /* Save the excursion, as there will be extensive changes to the
+     selected window.  */
+  count = SPECPDL_INDEX ();
+  record_unwind_protect_excursion ();
+
+  /* Inhibit quitting.  */
+  specbind (Qinhibit_quit, Qt);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+  buffer = NULL;
+
+  /* Figure out the bounds of the text to return.  */
+
+  /* First, obtain start and end.  */
+  end = get_mark ();
+  start = PT;
+
+  /* If the mark is not active, make it start and end.  */
+
+  if (end == -1)
+    end = start;
+
+  /* Now sort start and end.  */
+
+  if (end < start)
+    {
+      temp = start;
+      start = end;
+      end = temp;
+    }
+
+  /* And subtract left and right.  */
+
+  if (INT_SUBTRACT_WRAPV (start, left, &start)
+      || INT_ADD_WRAPV (end, right, &end))
+    goto finish;
+
+  start = max (start, BEGV);
+  end = min (end, ZV);
+
+  /* Detect overflow.  */
+
+  if (!(start <= PT && PT <= end))
+    goto finish;
+
+  /* Convert the character positions to byte positions.  */
+  start_byte = CHAR_TO_BYTE (start);
+  end_byte = CHAR_TO_BYTE (end);
+
+  /* Extract the text from the buffer.  */
+  buffer = xmalloc (end_byte - start_byte);
+  copy_buffer (start, start_byte, end, end_byte,
+              buffer);
+
+  /* Get the mark.  If it's not active, use PT.  */
+
+  mark = get_mark ();
+
+  if (mark == -1)
+    mark = PT;
+
+  /* Return the offsets.  Unlike `get_extracted_text', this need not
+     sort mark and point.  */
+
+  *offset = start;
+  *start_return = mark - start;
+  *end_return = PT - start;
+  *length = end - start;
+  *bytes = end_byte - start_byte;
+
+ finish:
+  unbind_to (count, Qnil);
+  return buffer;
+}
+
 /* Return whether or not text conversion is temporarily disabled.
    `reset' should always call this to determine whether or not to
    disable the input method.  */
diff --git a/src/textconv.h b/src/textconv.h
index 339cefdba92..7550388a723 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -143,6 +143,10 @@ extern void textconv_barrier (struct frame *, unsigned 
long);
 extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
                                 ptrdiff_t *, ptrdiff_t *, ptrdiff_t *,
                                 ptrdiff_t *, bool *);
+extern char *get_surrounding_text (struct frame *, ptrdiff_t,
+                                  ptrdiff_t, ptrdiff_t *,
+                                  ptrdiff_t *, ptrdiff_t *,
+                                  ptrdiff_t *, ptrdiff_t *);
 extern bool conversion_disabled_p (void);
 
 extern void register_textconv_interface (struct textconv_interface *);



reply via email to

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