emacs-diffs
[Top][All Lists]
Advanced

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

feature/android cf24b61985c 10/10: Update Android port


From: Po Lu
Subject: feature/android cf24b61985c 10/10: Update Android port
Date: Wed, 15 Feb 2023 09:53:31 -0500 (EST)

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

    Update Android port
    
    * doc/emacs/input.texi (On-Screen Keyboards):
    * doc/lispref/commands.texi (Misc Events): Improve documentation
    of text conversion stuff.
    * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit)
    (endBatchEdit, commitCompletion, commitText, deleteSurroundingText)
    (finishComposingText, getSelectedText, getTextAfterCursor)
    (EmacsInputConnection, setComposingRegion, performEditorAction)
    (getExtractedText): Condition debug code on DEBUG_IC.
    * java/org/gnu/emacs/EmacsService.java (EmacsService, updateIC):
    Likewise.
    * lisp/bindings.el (global-map):
    * lisp/electric.el (global-map): Make `text-conversion'
    `analyze-text-conversion'.
    * lisp/progmodes/prog-mode.el (prog-mode): Enable text
    conversion in input methods.
    * lisp/simple.el (analyze-text-conversion): New function.
    * lisp/textmodes/text-mode.el (text-conversion-style)
    (text-mode): Likewise.
    * src/androidterm.c (android_handle_ime_event): Handle
    set_point_and_mark.
    (android_sync_edit): Give Emacs 100 ms instead.
    (android_perform_conversion_query): Skip the active region, not
    the conversion region.
    (getSelectedText): Implement properly.
    (android_update_selection): Expose mark to input methods.
    (android_reset_conversion): Handle `text-conversion-style'.
    * src/buffer.c (init_buffer_once, syms_of_buffer): Add buffer
    local variable `text-conversion-style'.
    * src/buffer.h (struct buffer, bset_text_conversion_style): New
    fields.
    * src/emacs.c (android_emacs_init): Call syms_of_textconv.
    * src/frame.h (enum text_conversion_operation): Rename
    TEXTCONV_SET_POINT.
    * src/lisp.h: Export syms_of_textconv.
    
    * src/marker.c (set_marker_internal): Force redisplay when the
    mark is set and the buffer is visible on builds that use text
    conversion.  Explain why.
    
    * src/textconv.c (copy_buffer): Fix copying past gap.
    (get_mark): New function.
    (textconv_query): Implement new flag.
    (sync_overlay): New function.  Display conversion text in an
    overlay.
    (record_buffer_change, really_commit_text)
    (really_set_composing_text, really_set_composing_region)
    (really_delete_surrounding_text, really_set_point)
    (handle_pending_conversion_events_1, decrement_inside)
    (handle_pending_conversion_events, textconv_set_point)
    (get_extracted_text, register_textconv_interface): Various fixes
    and improvements.
    
    * src/textconv.h (struct textconv_interface): Update
    documentation.
    * src/window.h (GCALIGNED_STRUCT): New field `prev_mark'.
    * src/xdisp.c (mark_window_display_accurate_1): Handle
    prev_mark.
---
 doc/emacs/input.texi                         |   3 +
 doc/lispref/commands.texi                    |  48 +++-
 java/org/gnu/emacs/EmacsInputConnection.java |  68 ++++--
 java/org/gnu/emacs/EmacsService.java         |  14 +-
 lisp/bindings.el                             |   2 +-
 lisp/electric.el                             |   1 +
 lisp/progmodes/prog-mode.el                  |   2 +
 lisp/simple.el                               |  52 +++++
 lisp/textmodes/text-mode.el                  |   6 +
 src/androidterm.c                            |  88 ++++++--
 src/buffer.c                                 |  20 ++
 src/buffer.h                                 |  11 +
 src/emacs.c                                  |   3 +
 src/frame.h                                  |   2 +-
 src/lisp.h                                   |   1 +
 src/marker.c                                 |  26 +++
 src/textconv.c                               | 315 ++++++++++++++++++++++++---
 src/textconv.h                               |  11 +-
 src/window.h                                 |   4 +
 src/xdisp.c                                  |  11 +-
 20 files changed, 607 insertions(+), 81 deletions(-)

diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
index 2463a75edcd..154b7025ff4 100644
--- a/doc/emacs/input.texi
+++ b/doc/emacs/input.texi
@@ -121,6 +121,9 @@ screen, and send the appropriate key events to Emacs after 
completion.
   However, on screen keyboard input methods directly perform edits to
 the selected window of each frame; this is known as ``text
 conversion'', or ``string conversion'' under the X Window System.
+Emacs enables these input methods whenever the buffer local value of
+@code{text-conversion-style} is non-@code{nil}, normally inside
+derivatives of @code{text-mode} and @code{prog-mode}.
 
   Text conversion is performed asynchronously whenever Emacs receives
 a request to perform the conversion from the input method.  After the
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 2807d3d61b2..5fb2217a03b 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2205,9 +2205,51 @@ A few other event types represent occurrences within the 
system.
 This kind of event is sent @strong{after} a system-wide input method
 performs an edit to one or more buffers.
 
-Once the event is sent, the input method may already have made changes
-to multiple frames. @c TODO: allow querying which frames to which
-@c changes have been made.
+@vindex text-conversion-edits
+Once the event is sent, the input method may already have made
+changes to multiple buffers inside many different frames.  To
+determine which buffers have been changed, and what edits have
+been made to them, use the variable
+@code{text-conversion-edits}, which is set prior to each
+@code{text-conversion} event being sent; it is a list of the
+form:
+
+@indentedblock
+@w{@code{(@var{buffer} @var{beg} @var{end} @var{ephemeral})}}
+@end indentedblock
+
+Where @var{ephemeral} is the buffer which was modified,
+@var{beg} and @var{end} are the positions of the edit at the
+time it was completed, and @var{ephemeral} is either a string,
+containing any text which was inserted, @code{t}, meaning that
+the edit is a temporary edit made by the input method, and
+@code{nil}, meaning that some text was deleted.
+
+@vindex text-conversion-style
+Whether or not this event is sent depends on the value of the
+buffer-local variable @code{text-conversion-style}, which determines
+how an input method that wishes to make edits to buffer contents will
+behave.
+
+This variable can have one three values:
+
+@table @code
+@item nil
+This means that the input method will be disabled entirely, and key
+events will be sent instead of text conversion events.
+
+@item action
+This means that the input method will be enabled, but @key{RET} will
+be sent wherever the input method wanted to insert a new line.
+
+@item t
+This, or any other value, means that the input method will be enabled
+and make edits terminated by @code{text-conversion} events.
+@end itemize
+
+Changes to the value of this variable will only take effect upon
+the next redisplay after the buffer becomes the selected buffer
+of a frame.
 
 @cindex @code{delete-frame} event
 @item (delete-frame (@var{frame}))
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java 
b/java/org/gnu/emacs/EmacsInputConnection.java
index 3cf4419838b..5eb56d5aa71 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -55,7 +55,9 @@ public class EmacsInputConnection extends BaseInputConnection
   public boolean
   beginBatchEdit ()
   {
-    Log.d (TAG, "beginBatchEdit");
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "beginBatchEdit");
+
     EmacsNative.beginBatchEdit (windowHandle);
     return true;
   }
@@ -64,7 +66,9 @@ public class EmacsInputConnection extends BaseInputConnection
   public boolean
   endBatchEdit ()
   {
-    Log.d (TAG, "endBatchEdit");
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "endBatchEdit");
+
     EmacsNative.endBatchEdit (windowHandle);
     return true;
   }
@@ -73,7 +77,9 @@ public class EmacsInputConnection extends BaseInputConnection
   public boolean
   commitCompletion (CompletionInfo info)
   {
-    Log.d (TAG, "commitCompletion: " + info);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "commitCompletion: " + info);
+
     EmacsNative.commitCompletion (windowHandle,
                                  info.getText ().toString (),
                                  info.getPosition ());
@@ -84,7 +90,9 @@ public class EmacsInputConnection extends BaseInputConnection
   public boolean
   commitText (CharSequence text, int newCursorPosition)
   {
-    Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
+
     EmacsNative.commitText (windowHandle, text.toString (),
                            newCursorPosition);
     return true;
@@ -94,8 +102,10 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   deleteSurroundingText (int leftLength, int rightLength)
   {
-    Log.d (TAG, ("deleteSurroundingText: "
-                + leftLength + " " + rightLength));
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("deleteSurroundingText: "
+                  + leftLength + " " + rightLength));
+
     EmacsNative.deleteSurroundingText (windowHandle, leftLength,
                                       rightLength);
     return true;
@@ -105,7 +115,8 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   finishComposingText ()
   {
-    Log.d (TAG, "finishComposingText");
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "finishComposingText");
 
     EmacsNative.finishComposingText (windowHandle);
     return true;
@@ -115,7 +126,8 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public String
   getSelectedText (int flags)
   {
-    Log.d (TAG, "getSelectedText: " + flags);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getSelectedText: " + flags);
 
     return EmacsNative.getSelectedText (windowHandle, flags);
   }
@@ -124,27 +136,44 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public String
   getTextAfterCursor (int length, int flags)
   {
-    Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
+    String string;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
 
-    return EmacsNative.getTextAfterCursor (windowHandle, length,
-                                          flags);
+    string = EmacsNative.getTextAfterCursor (windowHandle, length,
+                                            flags);
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "   --> " + string);
+
+    return string;
   }
 
   @Override
   public String
   getTextBeforeCursor (int length, int flags)
   {
-    Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
+    String string;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
+
+    string = EmacsNative.getTextBeforeCursor (windowHandle, length,
+                                             flags);
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "   --> " + string);
 
-    return EmacsNative.getTextBeforeCursor (windowHandle, length,
-                                           flags);
+    return string;
   }
 
   @Override
   public boolean
   setComposingText (CharSequence text, int newCursorPosition)
   {
-    Log.d (TAG, "setComposingText: " + newCursorPosition);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "setComposingText: " + newCursorPosition);
 
     EmacsNative.setComposingText (windowHandle, text.toString (),
                                  newCursorPosition);
@@ -155,7 +184,8 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   setComposingRegion (int start, int end)
   {
-    Log.d (TAG, "setComposingRegion: " + start + " " + end);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "setComposingRegion: " + start + " " + end);
 
     EmacsNative.setComposingRegion (windowHandle, start, end);
     return true;
@@ -165,7 +195,8 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public boolean
   performEditorAction (int editorAction)
   {
-    Log.d (TAG, "performEditorAction: " + editorAction);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "performEditorAction: " + editorAction);
 
     EmacsNative.performEditorAction (windowHandle, editorAction);
     return true;
@@ -175,7 +206,8 @@ public class EmacsInputConnection extends 
BaseInputConnection
   public ExtractedText
   getExtractedText (ExtractedTextRequest request, int flags)
   {
-    Log.d (TAG, "getExtractedText: " + request + " " + flags);
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getExtractedText: " + request + " " + flags);
 
     return EmacsNative.getExtractedText (windowHandle, request,
                                         flags);
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 855a738a30f..2acb3ead086 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -88,6 +88,8 @@ public class EmacsService extends Service
   /* Display metrics used by font backends.  */
   public DisplayMetrics metrics;
 
+  public static final boolean DEBUG_IC = false;
+
   @Override
   public int
   onStartCommand (Intent intent, int flags, int startId)
@@ -612,10 +614,11 @@ public class EmacsService extends Service
            int newSelectionEnd, int composingRegionStart,
            int composingRegionEnd)
   {
-    Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
-                + " " + newSelectionEnd + " "
-                + composingRegionStart + " "
-                + composingRegionEnd));
+    if (DEBUG_IC)
+      Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
+                  + " " + newSelectionEnd + " "
+                  + composingRegionStart + " "
+                  + composingRegionEnd));
     window.view.imManager.updateSelection (window.view,
                                           newSelectionStart,
                                           newSelectionEnd,
@@ -626,7 +629,8 @@ public class EmacsService extends Service
   public void
   resetIC (EmacsWindow window, int icMode)
   {
-    Log.d (TAG, "resetIC: " + window);
+    if (DEBUG_IC)
+      Log.d (TAG, "resetIC: " + window);
 
     window.view.setICMode (icMode);
     window.view.imManager.restartInput (window.view);
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 057c870958d..eec51a4e413 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -1522,7 +1522,7 @@ if `inhibit-field-text-motion' is non-nil."
 (define-key special-event-map [sigusr2] 'ignore)
 
 ;; Text conversion
-(define-key global-map [text-conversion] 'ignore)
+(define-key global-map [text-conversion] 'analyze-text-conversion)
 
 ;; Don't look for autoload cookies in this file.
 ;; Local Variables:
diff --git a/lisp/electric.el b/lisp/electric.el
index bac3f5a2b3c..3865d1d5234 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -294,6 +294,7 @@ or comment."
 
 ;;;###autoload
 (define-key global-map "\C-j" 'electric-newline-and-maybe-indent)
+
 ;;;###autoload
 (defun electric-newline-and-maybe-indent ()
   "Insert a newline.
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index 04071703184..7a53399ad14 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -336,6 +336,8 @@ support it."
   (setq-local require-final-newline mode-require-final-newline)
   (setq-local parse-sexp-ignore-comments t)
   (add-hook 'context-menu-functions 'prog-context-menu 10 t)
+  ;; Enable text conversion in this buffer.
+  (setq-local text-conversion-style t)
   ;; Any programming language is always written left to right.
   (setq bidi-paragraph-direction 'left-to-right))
 
diff --git a/lisp/simple.el b/lisp/simple.el
index bed6dfb8292..6a12585a55d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10864,6 +10864,58 @@ If the buffer doesn't exist, create it first."
   "Change value in PLIST of PROP to VAL, comparing with `equal'."
   (declare (obsolete plist-put "29.1"))
   (plist-put plist prop val #'equal))
+
+
+
+;; Text conversion support.  See textconv.c for more details about
+;; what this is.
+
+
+;; Actually in textconv.c.
+(defvar text-conversion-edits)
+
+(defun analyze-text-conversion ()
+  "Analyze the results of the previous text conversion event.
+
+For each insertion:
+
+  - Look for the insertion of a string starting or ending with a
+    character inside `auto-fill-chars', and fill the text around
+    it if `auto-fill-mode' is enabled.
+
+  - Look for the insertion of a new line, and cause automatic
+    line breaking of the previous line when `auto-fill-mode' is
+    enabled.
+
+  - Look for the insertion of a new line, and indent this new
+    line if `electric-indent-mode' is enabled."
+  (interactive)
+  (dolist (edit text-conversion-edits)
+    ;; Filter out ephemeral edits and deletions.
+    (when (and (not (eq (nth 1 edit) (nth 2 edit)))
+               (stringp (nth 3 edit)))
+      (with-current-buffer (car edit)
+        (let* ((inserted (nth 3 edit))
+               ;; Get the first and last characters.
+               (start (aref inserted 0))
+               (end (aref inserted (1- (length inserted))))
+               ;; Figure out whether or not to auto-fill.
+               (auto-fill-p (or (aref auto-fill-chars start)
+                                (aref auto-fill-chars end)))
+               ;; Figure out whether or not a newline was inserted.
+               (newline-p (string-search "\n" inserted)))
+          (save-excursion
+            (if (and auto-fill-function newline-p)
+                (progn (goto-char (nth 2 edit))
+                       (previous-logical-line)
+                       (funcall auto-fill-function))
+              (when (and auto-fill-function auto-fill-p)
+                (progn (goto-char (nth 2 edit))
+                       (funcall auto-fill-function)))))
+          (when (and electric-indent-mode newline-p)
+            (goto-char (nth 2 edit))
+            (indent-according-to-mode)))))))
+
 
 
 (provide 'simple)
diff --git a/lisp/textmodes/text-mode.el b/lisp/textmodes/text-mode.el
index 48cefc74d06..ccba1b063ab 100644
--- a/lisp/textmodes/text-mode.el
+++ b/lisp/textmodes/text-mode.el
@@ -41,6 +41,9 @@
   "Non-nil if this buffer's major mode is a variant of Text mode.")
 (make-obsolete-variable 'text-mode-variant 'derived-mode-p "27.1")
 
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
 (defvar text-mode-syntax-table
   (let ((st (make-syntax-table)))
     (modify-syntax-entry ?\" ".   " st)
@@ -125,6 +128,9 @@ You can thus get the full benefit of adaptive filling
 Turning on Text mode runs the normal hook `text-mode-hook'."
   (setq-local text-mode-variant t)
   (setq-local require-final-newline mode-require-final-newline)
+
+  ;; Enable text conversion in this buffer.
+  (setq-local text-conversion-style t)
   (add-hook 'context-menu-functions 'text-mode-context-menu 10 t))
 
 (define-derived-mode paragraph-indent-text-mode text-mode "Parindent"
diff --git a/src/androidterm.c b/src/androidterm.c
index 767b7d8240c..0c990d3d2d2 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -621,8 +621,9 @@ android_handle_ime_event (union android_event *event, 
struct frame *f)
       break;
 
     case ANDROID_IME_SET_POINT:
-      textconv_set_point (f, event->ime.position,
-                         event->ime.counter);
+      textconv_set_point_and_mark (f, event->ime.start,
+                                  event->ime.end,
+                                  event->ime.counter);
       break;
 
     case ANDROID_IME_START_BATCH_EDIT:
@@ -4305,7 +4306,7 @@ static sem_t edit_sem;
 
    Every time one of the text retrieval functions is called and an
    editing request is made, Emacs gives the main thread approximately
-   50 ms to process it, in order to mostly keep the input method in
+   100 ms to process it, in order to mostly keep the input method in
    sync with the buffer contents.  */
 
 static void
@@ -4319,7 +4320,7 @@ android_sync_edit (void)
     return;
 
   start = current_timespec ();
-  end = timespec_add (start, make_timespec (0, 50000000));
+  end = timespec_add (start, make_timespec (0, 100000000));
 
   while (true)
     {
@@ -4550,8 +4551,7 @@ android_perform_conversion_query (void *data)
   if (!f)
     return;
 
-  textconv_query (f, &context->query,
-                 TEXTCONV_SKIP_CONVERSION_REGION);
+  textconv_query (f, &context->query, TEXTCONV_SKIP_ACTIVE_REGION);
 
   /* context->query.text will have been set even if textconv_query
      returns 1.  */
@@ -4648,13 +4648,6 @@ android_text_to_string (JNIEnv *env, char *buffer, 
ptrdiff_t n,
   return string;
 }
 
-JNIEXPORT jstring JNICALL
-NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
-                              jshort window)
-{
-  return NULL;
-}
-
 JNIEXPORT jstring JNICALL
 NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window,
                                  jint length, jint flags)
@@ -4805,8 +4798,8 @@ NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, 
jshort window,
   event.ime.serial = ++event_serial;
   event.ime.window = window;
   event.ime.operation = ANDROID_IME_SET_POINT;
-  event.ime.start = 0;
-  event.ime.end = 0;
+  event.ime.start = start;
+  event.ime.end = end;
   event.ime.length = 0;
   event.ime.position = start;
   event.ime.text = NULL;
@@ -5068,6 +5061,34 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject 
ignored_object,
   return object;
 }
 
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
+                              jshort window)
+{
+  struct android_get_extracted_text_context context;
+  jstring string;
+
+  context.hint_max_chars = -1;
+  context.token = 0;
+  context.text = NULL;
+  context.window = window;
+
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_get_extracted_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);
+
+  return string;
+}
+
 #ifdef __clang__
 #pragma clang diagnostic pop
 #else
@@ -5083,7 +5104,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject 
ignored_object,
 static void
 android_update_selection (struct frame *f, struct window *w)
 {
-  ptrdiff_t start, end, point;
+  ptrdiff_t start, end, point, mark;
+  struct buffer *b;
 
   if (MARKERP (f->conversion.compose_region_start))
     {
@@ -5103,12 +5125,20 @@ android_update_selection (struct frame *f, struct 
window *w)
   if (!w)
     w = XWINDOW (f->selected_window);
 
-  /* Figure out where the point is.  */
+  /* Figure out where the point and mark are.  If the mark is not
+     active, then point is set to equal mark.  */
+  b = XBUFFER (w->contents);
   point = min (w->last_point, TYPE_MAXIMUM (jint));
+  mark = ((!NILP (BVAR (b, mark_active))
+          && w->last_mark != -1)
+         ? min (w->last_mark, TYPE_MAXIMUM (jint))
+         : point);
 
-  /* Send the update.  */
-  android_update_ic (FRAME_ANDROID_WINDOW (f), point, point,
-                    start, end);
+  /* Send the update.  Android doesn't have a concept of ``point'' and
+     ``mark''; instead, it only has a selection, where the start of
+     the selection is less than or equal to the end.  */
+  android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark),
+                    max (point, mark), start, end);
 }
 
 /* Notice that the input method connection to F should be reset as a
@@ -5117,16 +5147,32 @@ android_update_selection (struct frame *f, struct 
window *w)
 static void
 android_reset_conversion (struct frame *f)
 {
+  enum android_ic_mode mode;
+  struct window *w;
+  struct buffer *buffer;
+
   /* Reset the input method.
 
      Pick an appropriate ``input mode'' based on whether or not the
      minibuffer window is selected; this controls whether or not
      ``RET'' inserts a newline or sends an actual key event.  */
+
+  w = XWINDOW (f->selected_window);
+  buffer = XBUFFER (WINDOW_BUFFER (w));
+
+  if (NILP (BVAR (buffer, text_conversion_style)))
+    mode = ANDROID_IC_MODE_NULL;
+  else if (EQ (BVAR (buffer, text_conversion_style),
+              Qaction))
+    mode = ANDROID_IC_MODE_ACTION;
+  else
+    mode = ANDROID_IC_MODE_TEXT;
+
   android_reset_ic (FRAME_ANDROID_WINDOW (f),
                    (EQ (f->selected_window,
                         f->minibuffer_window)
                     ? ANDROID_IC_MODE_ACTION
-                    : ANDROID_IC_MODE_TEXT));
+                    : mode));
 
   /* Move its selection to the specified position.  */
   android_update_selection (f, NULL);
diff --git a/src/buffer.c b/src/buffer.c
index 38648519ba0..af4aa583c96 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4710,6 +4710,7 @@ init_buffer_once (void)
 #ifdef HAVE_TREE_SITTER
   XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
 #endif
+  XSETFASTINT (BVAR (&buffer_local_flags, text_conversion_style), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), 
idx); ++idx;
 
   /* buffer_local_flags contains no pointers, so it's safe to treat it
@@ -4780,6 +4781,9 @@ init_buffer_once (void)
   bset_extra_line_spacing (&buffer_defaults, Qnil);
 #ifdef HAVE_TREE_SITTER
   bset_ts_parser_list (&buffer_defaults, Qnil);
+#endif
+#ifdef HAVE_TEXT_CONVERSION
+  bset_text_conversion_style (&buffer_defaults, Qnil);
 #endif
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
@@ -5852,6 +5856,22 @@ If t, displays a cursor related to the usual cursor type
 You can also specify the cursor type as in the `cursor-type' variable.
 Use Custom to set this variable and update the display.  */);
 
+  /* While this is defined here, each *term.c module must implement
+     the logic itself.  */
+
+  DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
+                                                    text_conversion_style),
+                    Qnil,
+   "How the on screen keyboard's input method should insert in this buffer.\n\
+When nil, the input method will be disabled and an ordinary keyboard\n\
+will be displayed in its place.\n\
+When the symbol `action', the input method will insert text directly, but\n\
+will send `return' key events instead of inserting new line characters.\n\
+Any other value means that the input method will insert text directly.\n\
+\n\
+This variable does not take immediate effect when set; rather, it takes\n\
+effect upon the next redisplay after the selected window or buffer changes.");
+
   DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
               doc: /* List of functions called with no args to query before 
killing a buffer.
 The buffer being killed will be current while the functions are running.
diff --git a/src/buffer.h b/src/buffer.h
index e700297a264..e71ffe28045 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -566,6 +566,11 @@ struct buffer
   /* A list of tree-sitter parsers for this buffer.  */
   Lisp_Object ts_parser_list_;
 #endif
+
+  /* What type of text conversion the input method should apply to
+     this buffer.  */
+  Lisp_Object text_conversion_style_;
+
   /* Cursor type to display in non-selected windows.
      t means to use hollow box cursor.
      See `cursor-type' for other values.  */
@@ -842,6 +847,12 @@ bset_width_table (struct buffer *b, Lisp_Object val)
   b->width_table_ = val;
 }
 
+INLINE void
+bset_text_conversion_style (struct buffer *b, Lisp_Object val)
+{
+  b->text_conversion_style_ = val;
+}
+
 /* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
    the max (resp. min) p such that
 
diff --git a/src/emacs.c b/src/emacs.c
index d7de3c85bbe..2f953510a3d 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2000,6 +2000,9 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #ifdef HAVE_WINDOW_SYSTEM
       init_fringe_once ();     /* Swap bitmaps if necessary.  */
 #endif /* HAVE_WINDOW_SYSTEM */
+#ifdef HAVE_TEXT_CONVERSION
+      syms_of_textconv ();
+#endif
     }
 
   init_alloc ();
diff --git a/src/frame.h b/src/frame.h
index 27484936ff1..ca4cca17d74 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -86,7 +86,7 @@ enum text_conversion_operation
     TEXTCONV_FINISH_COMPOSING_TEXT,
     TEXTCONV_SET_COMPOSING_TEXT,
     TEXTCONV_SET_COMPOSING_REGION,
-    TEXTCONV_SET_POINT,
+    TEXTCONV_SET_POINT_AND_MARK,
     TEXTCONV_DELETE_SURROUNDING_TEXT,
   };
 
diff --git a/src/lisp.h b/src/lisp.h
index 2dc51d32481..a39ca8cc541 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5234,6 +5234,7 @@ extern void reset_frame_state (struct frame *);
 extern void report_selected_window_change (struct frame *);
 extern void report_point_change (struct frame *, struct window *,
                                 struct buffer *);
+extern void syms_of_textconv (void);
 #endif
 
 #ifdef HAVE_NATIVE_COMP
diff --git a/src/marker.c b/src/marker.c
index e42c49a5434..7b15cd62f1e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -23,6 +23,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "lisp.h"
 #include "character.h"
 #include "buffer.h"
+#include "window.h"
 
 /* Record one cached position found recently by
    buf_charpos_to_bytepos or buf_bytepos_to_charpos.  */
@@ -566,6 +567,31 @@ set_marker_internal (Lisp_Object marker, Lisp_Object 
position,
 
       attach_marker (m, b, charpos, bytepos);
     }
+
+#ifdef HAVE_TEXT_CONVERSION
+
+  /* If B is the buffer's mark and there is a window displaying B, and
+     text conversion is enabled while the mark is active, redisplay
+     the buffer.
+
+     propagate_window_redisplay will propagate this redisplay to the
+     window, which will eventually reach
+     mark_window_display_accurate_1.  At that point,
+     report_point_change will be told to update the mark as seen by
+     the input method.
+
+     This is done all the way in (the seemingly irrelevant) redisplay
+     because the selection reported to the input method is actually what
+     is visible on screen, namely w->last_point.  */
+
+  if (m->buffer
+      && EQ (marker, BVAR (m->buffer, mark))
+      && !NILP (BVAR (m->buffer, mark_active))
+      && buffer_window_count (m->buffer))
+    bset_redisplay (m->buffer);
+
+#endif
+
   return marker;
 }
 
diff --git a/src/textconv.c b/src/textconv.c
index a39748457d3..835d03f3037 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -89,13 +89,27 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
   size = end0 - beg0;
   memcpy (buffer, BYTE_POS_ADDR (beg0), size);
   if (beg1 != -1)
-    memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
+    memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
 }
 
 
 
 /* Conversion query.  */
 
+/* Return the position of the active mark, or -1 if there is no mark
+   or it is not active.  */
+
+static ptrdiff_t
+get_mark (void)
+{
+  if (!NILP (BVAR (current_buffer, mark_active))
+      && XMARKER (BVAR (current_buffer, mark))->buffer)
+    return marker_position (BVAR (current_buffer,
+                                 mark));
+
+  return -1;
+}
+
 /* Perform the text conversion operation specified in QUERY and return
    the results.
 
@@ -109,6 +123,9 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
    If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past
    the conversion region in the specified direction if it is inside.
 
+   If FLAGS & TEXTCONV_SKIP_ACTIVE_REGION, then also move PT past the
+   region if the mark is active.
+
    Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
    or if deleting the text was successful, and 1 otherwise.  */
 
@@ -169,6 +186,39 @@ textconv_query (struct frame *f, struct 
textconv_callback_struct *query,
        }
     }
 
+  /* Likewise for the region if the mark is active.  */
+
+  if (flags & TEXTCONV_SKIP_ACTIVE_REGION)
+    {
+      temp = get_mark ();
+
+      if (temp == -1)
+       goto escape;
+
+      start = min (temp, PT);
+      end = max (temp, PT);
+
+      if (pos >= start && pos < end)
+       {
+         switch (query->direction)
+           {
+           case TEXTCONV_FORWARD_CHAR:
+           case TEXTCONV_FORWARD_WORD:
+           case TEXTCONV_CARET_DOWN:
+           case TEXTCONV_NEXT_LINE:
+           case TEXTCONV_LINE_START:
+             pos = end;
+             break;
+
+           default:
+             pos = max (BEGV, start);
+             break;
+           }
+       }
+    }
+
+ escape:
+
   /* If pos is outside the accessible part of the buffer or if it
      overflows, move back to point or to the extremes of the
      accessible region.  */
@@ -335,6 +385,55 @@ textconv_query (struct frame *f, struct 
textconv_callback_struct *query,
   return 0;
 }
 
+/* Update the overlay displaying the conversion area on F after a
+   change to the conversion region.  */
+
+static void
+sync_overlay (struct frame *f)
+{
+  if (MARKERP (f->conversion.compose_region_start))
+    {
+      if (NILP (f->conversion.compose_region_overlay))
+       {
+         f->conversion.compose_region_overlay
+           = Fmake_overlay (f->conversion.compose_region_start,
+                            f->conversion.compose_region_end, Qnil,
+                            Qt, Qnil);
+         Foverlay_put (f->conversion.compose_region_overlay,
+                       Qface, Qunderline);
+       }
+
+      Fmove_overlay (f->conversion.compose_region_overlay,
+                    f->conversion.compose_region_start,
+                    f->conversion.compose_region_end, Qnil);
+    }
+  else if (!NILP (f->conversion.compose_region_overlay))
+    {
+      Fdelete_overlay (f->conversion.compose_region_overlay);
+      f->conversion.compose_region_overlay = Qnil;
+    }
+}
+
+/* Record a change to the current buffer as a result of an
+   asynchronous text conversion operation on F.
+
+   Consult the doc string of `text-conversion-edits' for the meaning
+   of BEG, END, and EPHEMERAL.  */
+
+static void
+record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
+                     Lisp_Object ephemeral)
+{
+  Lisp_Object buffer;
+
+  XSETBUFFER (buffer, current_buffer);
+
+  Vtext_conversion_edits
+    = Fcons (list4 (buffer, make_fixnum (beg),
+                   make_fixnum (end), ephemeral),
+            Vtext_conversion_edits);
+}
+
 /* Reset F's text conversion state.  Delete any overlays or
    markers inside.  */
 
@@ -438,8 +537,10 @@ really_commit_text (struct frame *f, EMACS_INT position,
       /* Replace its contents.  */
       start = marker_position (f->conversion.compose_region_start);
       end = marker_position (f->conversion.compose_region_end);
-      safe_del_range (start, end);
+      del_range (start, end);
+      record_buffer_change (start, start, Qnil);
       Finsert (1, &text);
+      record_buffer_change (start, PT, text);
 
       /* Move to a the position specified in POSITION.  */
 
@@ -493,6 +594,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
         location.  */
       wanted = PT;
       Finsert (1, &text);
+      record_buffer_change (wanted, PT, text);
 
       if (position < 0)
        {
@@ -520,6 +622,8 @@ really_commit_text (struct frame *f, EMACS_INT position,
        }
     }
 
+  /* This should deactivate the mark.  */
+  call0 (Qdeactivate_mark);
   unbind_to (count, Qnil);
 }
 
@@ -575,12 +679,11 @@ really_set_composing_text (struct frame *f, ptrdiff_t 
position,
 
   if (!MARKERP (f->conversion.compose_region_start))
     {
-      f->conversion.compose_region_start = Fmake_marker ();
-      f->conversion.compose_region_end = Fmake_marker ();
-      Fset_marker (f->conversion.compose_region_start,
-                  Fpoint (), Qnil);
-      Fset_marker (f->conversion.compose_region_end,
-                  Fpoint (), Qnil);
+      f->conversion.compose_region_start
+       = build_marker (current_buffer, PT, PT_BYTE);
+      f->conversion.compose_region_end
+       = build_marker (current_buffer, PT, PT_BYTE);
+
       Fset_marker_insertion_type (f->conversion.compose_region_end,
                                  Qt);
 
@@ -595,13 +698,19 @@ really_set_composing_text (struct frame *f, ptrdiff_t 
position,
       Fwiden ();
       start = marker_position (f->conversion.compose_region_start);
       end = marker_position (f->conversion.compose_region_end);
-      safe_del_range (start, end);
+      del_range (start, end);
       set_point (start);
+
+      if (start != end)
+       record_buffer_change (start, start, Qnil);
     }
 
   /* Insert the new text.  */
   Finsert (1, &text);
 
+  if (start != PT)
+    record_buffer_change (start, PT, Qnil);
+
   /* Now move point to an appropriate location.  */
   if (position < 0)
     {
@@ -632,6 +741,12 @@ really_set_composing_text (struct frame *f, ptrdiff_t 
position,
 
   set_point (wanted);
 
+  /* This should deactivate the mark.  */
+  call0 (Qdeactivate_mark);
+
+  /* Move the composition overlay.  */
+  sync_overlay (f);
+
   /* If PT hasn't changed, the conversion region definitely has.
      Otherwise, redisplay will update the input method instead.  */
 
@@ -693,18 +808,21 @@ really_set_composing_region (struct frame *f, ptrdiff_t 
start,
               make_fixnum (start), Qnil);
   Fset_marker (f->conversion.compose_region_end,
               make_fixnum (end), Qnil);
+  sync_overlay (f);
 
   unbind_to (count, Qnil);
 }
 
-/* Delete LEFT and RIGHT chars around point.  */
+/* Delete LEFT and RIGHT chars around point or the active mark,
+   whichever is larger, avoiding the composing region if
+   necessary.  */
 
 static void
 really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
                                ptrdiff_t right)
 {
   specpdl_ref count;
-  ptrdiff_t start, end;
+  ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
 
   /* If F's old selected window is no longer live, fail.  */
 
@@ -719,21 +837,71 @@ really_delete_surrounding_text (struct frame *f, 
ptrdiff_t left,
      redisplay.  */
   Fselect_window (f->old_selected_window, Qt);
 
-  start = max (BEGV, PT - left);
-  end = min (ZV, PT + right);
+  /* Figure out where to start deleting from.  */
+
+  a = get_mark ();
+
+  if (a != -1 && a != PT)
+    lstart = rstart = max (a, PT);
+  else
+    lstart = rstart = PT;
+
+  /* Avoid the composing text.  This behavior is identical to how
+     Android's BaseInputConnection actually implements avoiding the
+     composing span.  */
+
+  if (MARKERP (f->conversion.compose_region_start))
+    {
+      a = marker_position (f->conversion.compose_region_start);
+      b = marker_position (f->conversion.compose_region_end);
+
+      a1 = min (a, b);
+      b1 = max (a, b);
+
+      lstart = min (lstart, min (PT, a1));
+      rstart = max (rstart, max (PT, b1));
+    }
+
+  if (lstart == rstart)
+    {
+      start = max (BEGV, lstart - left);
+      end = min (ZV, rstart + right);
+
+      del_range (start, end);
+      record_buffer_change (start, start, Qnil);
+    }
+  else
+    {
+      start = max (BEGV, lstart - left);
+      end = lstart;
+
+      del_range (start, end);
+      record_buffer_change (start, start, Qnil);
+
+      start = rstart;
+      end = min (ZV, rstart + right);
+      del_range (start, end);
+      record_buffer_change (start, start, Qnil);      
+    }
+
+  /* if the mark is now equal to start, deactivate it.  */
+
+  if (get_mark () == PT)
+    call0 (Qdeactivate_mark);
 
-  safe_del_range (start, end);
   unbind_to (count, Qnil);
 }
 
-/* Set point in F to POSITION.
+/* Set point in F to POSITION.  If MARK is not POSITION, activate the
+   mark and set MARK to that as well.
 
    If it has not changed, signal an update through the text input
    interface, which is necessary for the IME to acknowledge that the
    change has completed.  */
 
 static void
-really_set_point (struct frame *f, ptrdiff_t point)
+really_set_point_and_mark (struct frame *f, ptrdiff_t point,
+                          ptrdiff_t mark)
 {
   specpdl_ref count;
 
@@ -763,6 +931,11 @@ really_set_point (struct frame *f, ptrdiff_t point)
     /* Set the point.  */
     Fgoto_char (make_fixnum (point));
 
+  if (mark == point && BVAR (current_buffer, mark_active))
+    call0 (Qdeactivate_mark);
+  else
+    call1 (Qpush_mark, make_fixnum (mark));
+
   unbind_to (count, Qnil);
 }
 
@@ -845,8 +1018,9 @@ handle_pending_conversion_events_1 (struct frame *f,
                                   XFIXNUM (XCDR (data)));
       break;
 
-    case TEXTCONV_SET_POINT:
-      really_set_point (f, XFIXNUM (data));
+    case TEXTCONV_SET_POINT_AND_MARK:
+      really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
+                                XFIXNUM (XCDR (data)));
       break;
 
     case TEXTCONV_DELETE_SURROUNDING_TEXT:
@@ -858,6 +1032,17 @@ handle_pending_conversion_events_1 (struct frame *f,
   unbind_to (count, Qnil);
 }
 
+/* Decrement the variable pointed to by *PTR.  */
+
+static void
+decrement_inside (void *ptr)
+{
+  int *i;
+
+  i = ptr;
+  (*i)--;
+}
+
 /* Process any outstanding text conversion events.
    This may run Lisp or signal.  */
 
@@ -868,9 +1053,22 @@ handle_pending_conversion_events (void)
   Lisp_Object tail, frame;
   struct text_conversion_action *action, *next;
   bool handled;
+  static int inside;
+  specpdl_ref count;
 
   handled = false;
 
+  /* Reset Vtext_conversion_edits.  Do not do this if called
+     reentrantly.  */
+
+  if (!inside)
+    Vtext_conversion_edits = Qnil;
+
+  inside++;
+
+  count = SPECPDL_INDEX ();
+  record_unwind_protect_ptr (decrement_inside, &inside);
+
   FOR_EACH_FRAME (tail, frame)
     {
       f = XFRAME (frame);
@@ -905,6 +1103,8 @@ handle_pending_conversion_events (void)
          handled = true;
        }
     }
+
+  unbind_to (count, Qnil);
 }
 
 /* Start a ``batch edit'' in F.  During a batch edit, point_changed
@@ -1057,13 +1257,13 @@ set_composing_region (struct frame *f, ptrdiff_t start,
   input_pending = true;
 }
 
-/* Move point in F's selected buffer to POINT.
+/* Move point in F's selected buffer to POINT and maybe push MARK.
 
    COUNTER means the same as in `start_batch_edit'.  */
 
 void
-textconv_set_point (struct frame *f, ptrdiff_t point,
-                   unsigned long counter)
+textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
+                            ptrdiff_t mark, unsigned long counter)
 {
   struct text_conversion_action *action, **last;
 
@@ -1071,8 +1271,9 @@ textconv_set_point (struct frame *f, ptrdiff_t point,
     point = MOST_POSITIVE_FIXNUM;
 
   action = xmalloc (sizeof *action);
-  action->operation = TEXTCONV_SET_POINT;
-  action->data = make_fixnum (point);
+  action->operation = TEXTCONV_SET_POINT_AND_MARK;
+  action->data = Fcons (make_fixnum (point),
+                       make_fixnum (mark));
   action->next = NULL;
   action->counter = counter;
   for (last = &f->conversion.actions; *last; last = &(*last)->next)
@@ -1105,6 +1306,9 @@ delete_surrounding_text (struct frame *f, ptrdiff_t left,
 /* Return N characters of text around point in F's old selected
    window.
 
+   If N is -1, return the text between point and mark instead, given
+   that the mark is active.
+
    Set *N to the actual number of characters returned, *START_RETURN
    to the position of the first character returned, *OFFSET to the
    offset of point within that text, *LENGTH to the actual number of
@@ -1137,13 +1341,38 @@ get_extracted_text (struct frame *f, ptrdiff_t n,
   /* Temporarily switch to F's selected window at the time of the last
      redisplay.  */
   Fselect_window (f->old_selected_window, Qt);
+  buffer = NULL;
 
   /* Figure out the bounds of the text to return.  */
-  start = PT - n / 2;
-  end = PT + n - n / 2;
+  if (n != -1)
+    {
+      start = PT - n / 2;
+      end = PT + n - n / 2;
+    }
+  else
+    {
+      if (!NILP (BVAR (current_buffer, mark_active))
+         && XMARKER (BVAR (current_buffer, mark))->buffer)
+       {
+         start = marker_position (BVAR (current_buffer, mark));
+         end = PT;
+
+         /* Sort start and end.  start_byte is used to hold a
+            temporary value.  */
+
+         if (start > end)
+           {
+             start_byte = end;
+             end = start;
+             start = start_byte;
+           }
+       }
+      else
+       goto finish;
+    }
+
   start = max (start, BEGV);
   end = min (end, ZV);
-  buffer = NULL;
 
   /* Detect overflow.  */
 
@@ -1218,3 +1447,37 @@ register_textconv_interface (struct textconv_interface 
*interface)
 {
   text_interface = interface;
 }
+
+
+
+void
+syms_of_textconv (void)
+{
+  DEFSYM (Qaction, "action");
+  DEFSYM (Qtext_conversion, "text-conversion");
+  DEFSYM (Qpush_mark, "push-mark");
+  DEFSYM (Qunderline, "underline");
+
+  DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
+    doc: /* List of buffers that were last edited as a result of text 
conversion.
+
+This list can be used while handling a `text-conversion' event to
+determine the changes which have taken place.
+
+Each element of the list describes a single edit in a buffer, of the
+form:
+
+    (BUFFER BEG END EPHEMERAL)
+
+If an insertion or a change occured, then BEG and END are buffer
+positions denote the bounds of the text that was changed or inserted.
+If EPHEMERAL is t, then the input method will shortly make more
+changes to the text, so any actions that would otherwise be taken
+(such as indenting or automatically filling text) should not take
+place; otherwise, it is a string describing the text which was
+inserted.
+
+If a deletion occured, then BEG and END are the same, and EPHEMERAL is
+nil.  */);
+  Vtext_conversion_edits = Qnil;
+}
diff --git a/src/textconv.h b/src/textconv.h
index ce003847637..034c663521a 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -35,9 +35,10 @@ struct textconv_interface
      unexpected buffer change occurs.) */
   void (*reset) (struct frame *);
 
-  /* Notice that point has moved in the specified frame's selected
-     window's selected buffer.  The second argument is the window
-     whose point changed, and the third argument is the buffer.  */
+  /* Notice that point or mark has moved in the specified frame's
+     selected window's selected buffer.  The second argument is the
+     window whose point changed, and the third argument is the
+     buffer.  */
   void (*point_changed) (struct frame *, struct window *,
                         struct buffer *);
 
@@ -118,6 +119,7 @@ struct textconv_callback_struct
 };
 
 #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
+#define TEXTCONV_SKIP_ACTIVE_REGION    (1 << 1)
 
 extern int textconv_query (struct frame *, struct textconv_callback_struct *,
                           int);
@@ -132,7 +134,8 @@ extern void set_composing_text (struct frame *, Lisp_Object,
                                ptrdiff_t, unsigned long);
 extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
                                  unsigned long);
-extern void textconv_set_point (struct frame *, ptrdiff_t, unsigned long);
+extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t,
+                                        ptrdiff_t, unsigned long);
 extern void delete_surrounding_text (struct frame *, ptrdiff_t,
                                     ptrdiff_t, unsigned long);
 extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
diff --git a/src/window.h b/src/window.h
index 32b5fe14f4f..463c7f89b9b 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,10 @@ struct window
        it should be positive.  */
     ptrdiff_t last_point;
 
+    /* Value of mark in the selected window at the time of the last
+       redisplay.  */
+    ptrdiff_t last_mark;
+
     /* Line number and position of a line somewhere above the top of the
        screen.  If this field is zero, it means we don't have a base line.  */
     ptrdiff_t base_line_number;
diff --git a/src/xdisp.c b/src/xdisp.c
index 4b91fcf31ca..525eaa64b3a 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -17268,7 +17268,7 @@ mark_window_display_accurate_1 (struct window *w, bool 
accurate_p)
 {
   struct buffer *b = XBUFFER (w->contents);
 #ifdef HAVE_TEXT_CONVERSION
-  ptrdiff_t prev_point;
+  ptrdiff_t prev_point, prev_mark;
 #endif
 
   w->last_modified = accurate_p ? BUF_MODIFF (b) : 0;
@@ -17301,6 +17301,7 @@ mark_window_display_accurate_1 (struct window *w, bool 
accurate_p)
 
 #ifdef HAVE_TEXT_CONVERSION
       prev_point = w->last_point;
+      prev_mark = w->last_mark;
 #endif
 
       if (w == XWINDOW (selected_window))
@@ -17308,6 +17309,11 @@ mark_window_display_accurate_1 (struct window *w, bool 
accurate_p)
       else
        w->last_point = marker_position (w->pointm);
 
+      if (XMARKER (BVAR (b, mark))->buffer == b)
+       w->last_mark = marker_position (BVAR (b, mark));
+      else
+       w->last_mark = -1;
+
 #ifdef HAVE_TEXT_CONVERSION
       /* Point motion is only propagated to the input method for use
         in text conversion during a redisplay.  While this can lead
@@ -17323,7 +17329,8 @@ mark_window_display_accurate_1 (struct window *w, bool 
accurate_p)
          during such a change, so the consequences are not that
          severe.  */
 
-      if (prev_point != w->last_point
+      if ((prev_point != w->last_point
+          || prev_mark != w->last_mark)
          && FRAME_WINDOW_P (WINDOW_XFRAME (w))
          && w == XWINDOW (WINDOW_XFRAME (w)->selected_window))
        report_point_change (WINDOW_XFRAME (w), w, b);



reply via email to

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