emacs-diffs
[Top][All Lists]
Advanced

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

feature/android 2dcce30290d 1/2: Update Android port


From: Po Lu
Subject: feature/android 2dcce30290d 1/2: Update Android port
Date: Thu, 16 Feb 2023 10:57:39 -0500 (EST)

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

    Update Android port
    
    * doc/emacs/android.texi (Android Fonts):
    * doc/emacs/input.texi (On-Screen Keyboards):
    * doc/lispref/commands.texi (Misc Events): Update documentation.
    * java/org/gnu/emacs/EmacsInputConnection.java (setSelection):
    New function.
    * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
    (reconfigureFrontBuffer): Make bitmap references weak
    references.
    * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Don't
    clear surfaceView bitmap.
    * lisp/comint.el (comint-mode):
    * lisp/international/fontset.el (script-representative-chars)
    (setup-default-fontset): Improve detection of CJK fonts.
    * lisp/isearch.el (set-text-conversion-style): New variable.
    (isearch-mode, isearch-done): Save and restore the text
    conversion style.
    * lisp/minibuffer.el (minibuffer-mode): Set an appropriate text
    conversion style.
    * lisp/simple.el (analyze-text-conversion): Run
    post-self-insert-hook properly.
    * lisp/subr.el (read-char-from-minibuffer): Disable text
    conversion when reading character.
    * src/androidterm.c (show_back_buffer): Don't check that F is
    not garbaged.
    (android_update_selection, android_reset_conversion): Use the
    ephemeral last point and handle text conversion being disabled.
    * src/buffer.c (syms_of_buffer): Convert old style DEFVAR.
    * src/keyboard.c (kbd_buffer_get_event): Handle text conversion
    first.
    * src/lisp.h: Update prototypes.
    * src/lread.c (read_filtered_event): Temporarily disable text
    conversion.
    * src/sfnt.c (sfnt_decompose_glyph_1, sfnt_decompose_glyph_2):
    New functions.
    (sfnt_decompose_glyph, sfnt_decompose_instructed_outline):
    Refactor contour decomposition to those two functions.
    (main): Update tests.
    * src/sfntfont-android.c (system_font_directories): Add empty
    field.
    (Fandroid_enumerate_fonts, init_sfntfont_android): Enumerate
    fonts in a user fonts directory.
    * src/sfntfont.c (struct sfnt_font_desc): New field
    `num_glyphs'.
    (sfnt_enum_font_1): Set num_glyphs and avoid duplicate fonts.
    (sfntfont_glyph_valid): New function.
    (sfntfont_lookup_char, sfntfont_list_1): Make sure glyphs found
    are valid.
    
    * src/textconv.c (sync_overlay, really_commit_text)
    (really_set_composing_text, really_set_composing_region)
    (really_delete_surrounding_text, really_set_point_and_mark)
    (handle_pending_conversion_events_1)
    (handle_pending_conversion_events, conversion_disabled_p)
    (disable_text_conversion, resume_text_conversion)
    (Fset_text_conversion_style, syms_of_textconv): Update to
    respect new options.
    * src/textconv.h:
    * src/window.h (GCALIGNED_STRUCT): New field
    `ephemeral_last_point'.
    * src/xdisp.c (mark_window_display_accurate_1): Set it.
---
 doc/emacs/android.texi                       |   8 +-
 doc/emacs/input.texi                         |   7 +
 doc/lispref/commands.texi                    |  11 +-
 java/org/gnu/emacs/EmacsInputConnection.java |  14 +-
 java/org/gnu/emacs/EmacsSurfaceView.java     |  17 +-
 java/org/gnu/emacs/EmacsView.java            |   8 +-
 lisp/comint.el                               |   3 +
 lisp/international/fontset.el                |  14 +-
 lisp/isearch.el                              |  17 +
 lisp/minibuffer.el                           |   5 +-
 lisp/simple.el                               |  11 +-
 lisp/subr.el                                 |  25 +-
 src/androidterm.c                            |  25 +-
 src/buffer.c                                 |  22 +-
 src/keyboard.c                               |  39 +-
 src/lisp.h                                   |   2 +
 src/lread.c                                  |  26 +-
 src/sfnt.c                                   | 625 ++++++++++++++-------------
 src/sfntfont-android.c                       |  13 +-
 src/sfntfont.c                               |  87 +++-
 src/textconv.c                               | 234 +++++++++-
 src/textconv.h                               |   1 +
 src/window.h                                 |  15 +
 src/xdisp.c                                  |   3 +
 24 files changed, 804 insertions(+), 428 deletions(-)

diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index 308bd70c59c..35f0ba55cf4 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -446,10 +446,10 @@ application via cut-and-paste.
 named @code{sfnt-android} and @code{android}.
 
 Upon startup, Emacs enumerates all the TrueType format fonts in the
-directory @file{/system/fonts}; this is where the Android system
-places fonts.  Emacs assumes there will always be a font named ``Droid
-Sans Mono'', and then defaults to using this font.  These fonts are
-then rendered by the @code{sfnt-android} font driver.
+directory @file{/system/fonts}, and the @file{fonts} directory inside
+the Emacs home directory.  Emacs assumes there will always be a font
+named ``Droid Sans Mono'', and then defaults to using this font.
+These fonts are then displayed by the @code{sfnt-android} font driver.
 
 When running on Android, Emacs currently lacks support for OpenType
 fonts.  This means that only a subset of the fonts installed on the
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
index 154b7025ff4..87ed1f4f8a9 100644
--- a/doc/emacs/input.texi
+++ b/doc/emacs/input.texi
@@ -129,3 +129,10 @@ derivatives of @code{text-mode} and @code{prog-mode}.
 a request to perform the conversion from the input method.  After the
 conversion completes, a @code{text-conversion} event is sent.
 @xref{Misc Events,,, elisp, the Emacs Reference Manual}.
+
+@vindex text-conversion-face
+  If the input method needs to work on a region of the buffer, then
+the region becomes known as the ``composing region'' (or
+``preconversion region''.)  The variable @code{text-conversion-face}
+describes whether or not to display the composing region in a specific
+face.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 5fb2217a03b..898cc9a944b 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2231,7 +2231,7 @@ 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:
+This variable can have one of three values:
 
 @table @code
 @item nil
@@ -2245,11 +2245,16 @@ 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
+@end table
 
+@findex disable-text-conversion
 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.
+of a frame.  If you need to disable text conversion in a way
+that takes immediate effect, call the function
+@code{set-text-conversion-style} instead.  This can potentially
+lock up the input method for a significant amount of time, so do
+not do this lightly!
 
 @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 5eb56d5aa71..e2a15894695 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -173,7 +173,8 @@ public class EmacsInputConnection extends 
BaseInputConnection
   setComposingText (CharSequence text, int newCursorPosition)
   {
     if (EmacsService.DEBUG_IC)
-      Log.d (TAG, "setComposingText: " + newCursorPosition);
+      Log.d (TAG, ("setComposingText: "
+                  + text + " ## " + newCursorPosition));
 
     EmacsNative.setComposingText (windowHandle, text.toString (),
                                  newCursorPosition);
@@ -213,6 +214,17 @@ public class EmacsInputConnection extends 
BaseInputConnection
                                         flags);
   }
 
+  @Override
+  public boolean
+  setSelection (int start, int end)
+  {
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "setSelection: " + start + " " + end);
+
+    EmacsNative.setSelection (windowHandle, start, end);
+    return true;
+  }
+
 
   /* Override functions which are not implemented.  */
 
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
index 2d80be0881a..62e927094e4 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -28,6 +28,8 @@ import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Paint;
 
+import java.lang.ref.WeakReference;
+
 /* This originally extended SurfaceView.  However, doing so proved to
    be too slow, and Android's surface view keeps up to three of its
    own back buffers, which use too much memory (up to 96 MB for a
@@ -39,7 +41,7 @@ public class EmacsSurfaceView extends View
   private EmacsView view;
   private Bitmap frontBuffer;
   private Canvas bitmapCanvas;
-  private Bitmap bitmap;
+  private WeakReference<Bitmap> bitmap;
   private Paint bitmapPaint;
 
   public
@@ -49,10 +51,11 @@ public class EmacsSurfaceView extends View
 
     this.view = view;
     this.bitmapPaint = new Paint ();
+    this.bitmap = new WeakReference<Bitmap> (null);
   }
 
   private void
-  copyToFrontBuffer (Rect damageRect)
+  copyToFrontBuffer (Bitmap bitmap, Rect damageRect)
   {
     if (damageRect != null)
       bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect,
@@ -73,7 +76,7 @@ public class EmacsSurfaceView extends View
        bitmapCanvas = null;
       }
 
-    this.bitmap = bitmap;
+    this.bitmap = new WeakReference<Bitmap> (bitmap);
 
     /* Next, create the new front buffer if necessary.  */
 
@@ -92,20 +95,20 @@ public class EmacsSurfaceView extends View
        bitmapCanvas = new Canvas (frontBuffer);
 
        /* And copy over the bitmap contents.  */
-       copyToFrontBuffer (null);
+       copyToFrontBuffer (bitmap, null);
       }
     else if (bitmap != null)
       /* Just copy over the bitmap contents.  */
-      copyToFrontBuffer (null);
+      copyToFrontBuffer (bitmap, null);
   }
 
   public synchronized void
   setBitmap (Bitmap bitmap, Rect damageRect)
   {
-    if (bitmap != this.bitmap)
+    if (bitmap != this.bitmap.get ())
       reconfigureFrontBuffer (bitmap);
     else if (bitmap != null)
-      copyToFrontBuffer (damageRect);
+      copyToFrontBuffer (bitmap, damageRect);
 
     if (bitmap != null)
       {
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 52da6d41f7d..5ea8b0dcc0e 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -186,13 +186,7 @@ public class EmacsView extends ViewGroup
     /* Explicitly free the old bitmap's memory.  */
 
     if (oldBitmap != null)
-      {
-       oldBitmap.recycle ();
-
-       /* Make sure to set the view's bitmap to the new bitmap, or
-          ugly flicker can result.  */
-       surfaceView.setBitmap (bitmap, null);
-      }
+      oldBitmap.recycle ();
 
     /* Some Android versions still don't free the bitmap until the
        next GC.  */
diff --git a/lisp/comint.el b/lisp/comint.el
index 9d2c245247f..e1786e7e670 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -694,6 +694,9 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
   (setq-local comint-last-input-start (point-min-marker))
   (setq-local comint-last-input-end (point-min-marker))
   (setq-local comint-last-output-start (make-marker))
+  ;; It is ok to let the input method edit prompt text, but RET must
+  ;; be processed by Emacs.
+  (setq text-conversion-style 'action)
   (make-local-variable 'comint-last-prompt)
   (make-local-variable 'comint-prompt-regexp)        ; Don't set; default
   (make-local-variable 'comint-input-ring-size)      ; ...to global val.
diff --git a/lisp/international/fontset.el b/lisp/international/fontset.el
index eb1c7f53d36..8fa67d99477 100644
--- a/lisp/international/fontset.el
+++ b/lisp/international/fontset.el
@@ -200,7 +200,7 @@
        (symbol . [#x201C #x2200 #x2500])
        (braille #x2800)
        (ideographic-description #x2FF0)
-       (cjk-misc #x300E)
+       (cjk-misc #x300E #xff0c)
        (kana #x304B)
        (bopomofo #x3105)
        (kanbun #x319D)
@@ -683,7 +683,11 @@
          (nil . "JISX0213.2000-2")
          (nil . "JISX0213.2004-1")
          ,(font-spec :registry "iso10646-1" :lang 'ja)
-         ,(font-spec :registry "iso10646-1" :lang 'zh))
+         ,(font-spec :registry "iso10646-1" :lang 'zh)
+          ;; This is required, as otherwise many TrueType fonts with
+          ;; CJK characters but no corresponding ``design language''
+          ;; declaration can't be found.
+          ,(font-spec :registry "iso10646-1" :script 'han))
 
      (cjk-misc (nil . "GB2312.1980-0")
               (nil . "JISX0208*")
@@ -702,7 +706,11 @@
               (nil . "JISX0213.2000-1")
               (nil . "JISX0213.2000-2")
               ,(font-spec :registry "iso10646-1" :lang 'ja)
-              ,(font-spec :registry "iso10646-1" :lang 'zh))
+              ,(font-spec :registry "iso10646-1" :lang 'zh)
+               ;; This is required, as otherwise many TrueType fonts
+               ;; with CJK characters but no corresponding ``design
+               ;; language'' declaration can't be found.
+               ,(font-spec :registry "iso10646-1" :script 'cjk-misc))
 
      (hangul (nil . "KSC5601.1987-0")
             ,(font-spec :registry "iso10646-1" :lang 'ko))
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 80c7a3b3d3f..a17b22fd627 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -244,6 +244,10 @@ If you use `add-function' to modify this variable, you can 
use the
 `isearch-message-prefix' advice property to specify the prefix string
 displayed in the search message.")
 
+(defvar isearch-text-conversion-style nil
+  "Value of `text-conversion-style' before Isearch mode
+was enabled in this buffer.")
+
 ;; Search ring.
 
 (defvar search-ring nil
@@ -1221,6 +1225,8 @@ active region is added to the search string."
 ;;                          isearch-forward-regexp isearch-backward-regexp)
 ;;  "List of commands for which isearch-mode does not recursive-edit.")
 
+(declare-function set-text-conversion-style "textconv.c")
+
 (defun isearch-mode (forward &optional regexp op-fun recursive-edit 
regexp-function)
   "Start Isearch minor mode.
 It is called by the function `isearch-forward' and other related functions.
@@ -1342,6 +1348,13 @@ used to set the value of `isearch-regexp-function'."
                      'keyboard)))
     (frame-toggle-on-screen-keyboard (selected-frame) nil))
 
+  ;; Disable text conversion so that isearch can behave correctly.
+
+  (when (fboundp 'set-text-conversion-style)
+    (setq isearch-text-conversion-style
+          text-conversion-style)
+    (set-text-conversion-style nil))
+
   ;; isearch-mode can be made modal (in the sense of not returning to
   ;; the calling function until searching is completed) by entering
   ;; a recursive-edit and exiting it when done isearching.
@@ -1475,6 +1488,10 @@ NOPUSH is t and EDIT is t."
         (setq isearch-tool-bar-old-map nil))
     (kill-local-variable 'tool-bar-map))
 
+  ;; Restore the previous text conversion style.
+  (when (fboundp 'set-text-conversion-style)
+    (set-text-conversion-style isearch-text-conversion-style))
+
   (force-mode-line-update)
 
   ;; If we ended in the middle of some intangible text,
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 8c1a0d4b21c..ef0eb1ca108 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2918,7 +2918,10 @@ For customizing this mode, it is better to use
 `minibuffer-setup-hook' and `minibuffer-exit-hook' rather than
 the mode hook of this mode."
   :syntax-table nil
-  :interactive nil)
+  :interactive nil
+  ;; Enable text conversion, but always make sure `RET' does
+  ;; something.
+  (setq text-conversion-style 'action))
 
 ;;; Completion tables.
 
diff --git a/lisp/simple.el b/lisp/simple.el
index 6a12585a55d..e5bf90a8055 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10887,8 +10887,9 @@ For each insertion:
     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."
+  - Run `post-self-insert-functions' for the last character of
+    any inserted text so that modes such as `electric-pair-mode'
+    can work."
   (interactive)
   (dolist (edit text-conversion-edits)
     ;; Filter out ephemeral edits and deletions.
@@ -10912,9 +10913,9 @@ For each insertion:
               (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)))))))
+          (goto-char (nth 2 edit))
+          (let ((last-command-event end))
+            (run-hooks 'post-self-insert-hook)))))))
 
 
 
diff --git a/lisp/subr.el b/lisp/subr.el
index f4f041ff32d..8e594fafa8c 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3424,6 +3424,9 @@ an error message."
     (minibuffer-message "Wrong answer")
     (sit-for 2)))
 
+;; Defined in textconv.c.
+(defvar overriding-text-conversion-style)
+
 (defun read-char-from-minibuffer (prompt &optional chars history)
   "Read a character from the minibuffer, prompting for it with PROMPT.
 Like `read-char', but uses the minibuffer to read and return a character.
@@ -3438,7 +3441,15 @@ while calling this function, then pressing `help-char'
 causes it to evaluate `help-form' and display the result.
 There is no need to explicitly add `help-char' to CHARS;
 `help-char' is bound automatically to `help-form-show'."
-  (let* ((map (if (consp chars)
+
+  ;; If text conversion is enabled in this buffer, then it will only
+  ;; be disabled the next time `force-mode-line-update' happens.
+  (when (and overriding-text-conversion-style
+             text-conversion-style)
+    (force-mode-line-update))
+
+  (let* ((overriding-text-conversion-style nil)
+         (map (if (consp chars)
                   (or (gethash (list help-form (cons help-char chars))
                                read-char-from-minibuffer-map-hash)
                       (let ((map (make-sparse-keymap))
@@ -3450,15 +3461,15 @@ There is no need to explicitly add `help-char' to CHARS;
                         ;; being a command char.
                         (when help-form
                           (define-key map (vector help-char)
-                            (lambda ()
-                              (interactive)
-                              (let ((help-form msg)) ; lexically bound msg
-                                (help-form-show)))))
+                                      (lambda ()
+                                        (interactive)
+                                        (let ((help-form msg)) ; lexically 
bound msg
+                                          (help-form-show)))))
                         (dolist (char chars)
                           (define-key map (vector char)
-                            #'read-char-from-minibuffer-insert-char))
+                                      #'read-char-from-minibuffer-insert-char))
                         (define-key map [remap self-insert-command]
-                          #'read-char-from-minibuffer-insert-other)
+                                    #'read-char-from-minibuffer-insert-other)
                         (puthash (list help-form (cons help-char chars))
                                  map read-char-from-minibuffer-map-hash)
                         map))
diff --git a/src/androidterm.c b/src/androidterm.c
index 0c990d3d2d2..c6f75ec9219 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -258,10 +258,6 @@ show_back_buffer (struct frame *f)
 {
   struct android_swap_info swap_info;
 
-  /* Somehow Android frames can be swapped while garbaged.  */
-  if (FRAME_GARBAGED_P (f))
-    return;
-
   memset (&swap_info, 0, sizeof (swap_info));
   swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
   swap_info.swap_action = ANDROID_COPIED;
@@ -5128,7 +5124,8 @@ android_update_selection (struct frame *f, struct window 
*w)
   /* 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));
+  point = min (w->ephemeral_last_point,
+              TYPE_MAXIMUM (jint));
   mark = ((!NILP (BVAR (b, mark_active))
           && w->last_mark != -1)
          ? min (w->last_mark, TYPE_MAXIMUM (jint))
@@ -5150,6 +5147,7 @@ android_reset_conversion (struct frame *f)
   enum android_ic_mode mode;
   struct window *w;
   struct buffer *buffer;
+  Lisp_Object style;
 
   /* Reset the input method.
 
@@ -5160,19 +5158,20 @@ android_reset_conversion (struct frame *f)
   w = XWINDOW (f->selected_window);
   buffer = XBUFFER (WINDOW_BUFFER (w));
 
-  if (NILP (BVAR (buffer, text_conversion_style)))
+  style = (EQ (find_symbol_value (Qoverriding_text_conversion_style),
+              Qlambda)
+          ? BVAR (buffer, text_conversion_style)
+          : find_symbol_value (Qoverriding_text_conversion_style));
+
+  if (NILP (style) || conversion_disabled_p ())
     mode = ANDROID_IC_MODE_NULL;
-  else if (EQ (BVAR (buffer, text_conversion_style),
-              Qaction))
+  else if (EQ (style, Qaction) || EQ (f->selected_window,
+                                     f->minibuffer_window))
     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
-                    : mode));
+  android_reset_ic (FRAME_ANDROID_WINDOW (f), mode);
 
   /* Move its selection to the specified position.  */
   android_update_selection (f, NULL);
diff --git a/src/buffer.c b/src/buffer.c
index af4aa583c96..393b8c5340a 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5862,15 +5862,19 @@ Use Custom to set this variable and update the display. 
 */);
   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.");
+    doc: /* How the on screen keyboard's input method should insert in this 
buffer.
+When nil, the input method will be disabled and an ordinary keyboard
+will be displayed in its place.
+When the symbol `action', the input method will insert text directly, but
+will send `return' key events instead of inserting new line characters.
+Any other value means that the input method will insert text directly.
+
+If you need to make non-buffer local changes to this variable, use
+`overriding-text-conversion-style', which see.
+
+This variable does not take immediate effect when set; rather, it
+takes 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.
diff --git a/src/keyboard.c b/src/keyboard.c
index 538fdffc199..a8062adc468 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4050,14 +4050,6 @@ kbd_buffer_get_event (KBOARD **kbp,
     x_handle_pending_selection_requests ();
 #endif
 
-#ifdef HAVE_TEXT_CONVERSION
-  /* Handle pending ``text conversion'' requests from an input
-     method.  */
-
-  if (had_pending_conversion_events)
-    handle_pending_conversion_events ();
-#endif
-
   if (CONSP (Vunread_command_events))
     {
       Lisp_Object first;
@@ -4067,6 +4059,24 @@ kbd_buffer_get_event (KBOARD **kbp,
       return first;
     }
 
+#ifdef HAVE_TEXT_CONVERSION
+  /* There are pending text conversion operations.  Text conversion
+     events should be generated before processing any other keyboard
+     input.  */
+  if (had_pending_conversion_events)
+    {
+      handle_pending_conversion_events ();
+      obj = Qtext_conversion;
+
+      /* See the comment in handle_pending_conversion_events_1.
+         Note that in addition, text conversion events are not
+         generated if no edits were actually made.  */
+      if (conversion_disabled_p ()
+         || NILP (Vtext_conversion_edits))
+       obj = Qnil;
+    }
+  else
+#endif
   /* At this point, we know that there is a readable event available
      somewhere.  If the event queue is empty, then there must be a
      mouse movement enabled and available.  */
@@ -4414,25 +4424,12 @@ kbd_buffer_get_event (KBOARD **kbp,
 #ifdef HAVE_X_WINDOWS
   else if (had_pending_selection_requests)
     obj = Qnil;
-#endif
-#ifdef HAVE_TEXT_CONVERSION
-  /* This is an internal event used to prevent Emacs from becoming
-     idle immediately after a text conversion operation.  */
-  else if (had_pending_conversion_events)
-    obj = Qtext_conversion;
 #endif
   else
     /* We were promised by the above while loop that there was
        something for us to read!  */
     emacs_abort ();
 
-#ifdef HAVE_TEXT_CONVERSION
-  /* While not implemented as keyboard commands, changes made by the
-     input method still mean that Emacs is no longer idle.  */
-  if (had_pending_conversion_events)
-    timer_stop_idle ();
-#endif
-
   input_pending = readable_events (0);
 
   Vlast_event_frame = internal_last_event_frame;
diff --git a/src/lisp.h b/src/lisp.h
index a39ca8cc541..56ef338a5b1 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5234,6 +5234,8 @@ 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 disable_text_conversion (void);
+extern void resume_text_conversion (void);
 extern void syms_of_textconv (void);
 #endif
 
diff --git a/src/lread.c b/src/lread.c
index 6abcea556bf..150d8a01e10 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -672,7 +672,11 @@ static void substitute_in_interval (INTERVAL, void *);
    if the character warrants that.
 
    If SECONDS is a number, wait that many seconds for input, and
-   return Qnil if no input arrives within that time.  */
+   return Qnil if no input arrives within that time.
+
+   If text conversion is enabled and ASCII_REQUIRED && ERROR_NONASCII,
+   temporarily disable any input method which wants to perform
+   edits.  */
 
 static Lisp_Object
 read_filtered_event (bool no_switch_frame, bool ascii_required,
@@ -680,12 +684,28 @@ read_filtered_event (bool no_switch_frame, bool 
ascii_required,
 {
   Lisp_Object val, delayed_switch_frame;
   struct timespec end_time;
+#ifdef HAVE_TEXT_CONVERSION
+  specpdl_ref count;
+#endif
 
 #ifdef HAVE_WINDOW_SYSTEM
   if (display_hourglass_p)
     cancel_hourglass ();
 #endif
 
+#ifdef HAVE_TEXT_CONVERSION
+  count = SPECPDL_INDEX ();
+
+  /* Don't use text conversion when trying to just read a
+     character.  */
+
+  if (ascii_required && error_nonascii)
+    {
+      disable_text_conversion ();
+      record_unwind_protect_void (resume_text_conversion);
+    }
+#endif
+
   delayed_switch_frame = Qnil;
 
   /* Compute timeout.  */
@@ -761,7 +781,11 @@ read_filtered_event (bool no_switch_frame, bool 
ascii_required,
 
 #endif
 
+#ifdef HAVE_TEXT_CONVERSION
+  return unbind_to (count, val);
+#else
   return val;
+#endif
 }
 
 DEFUN ("read-char", Fread_char, Sread_char, 0, 3, 0,
diff --git a/src/sfnt.c b/src/sfnt.c
index 38f6984b93c..b6f4a48ea8b 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -2708,6 +2708,281 @@ sfnt_lerp_half (struct sfnt_point *control1, struct 
sfnt_point *control2,
   result->y = control1->y + ((control2->y - control1->y) >> 1);
 }
 
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+   HERE and LAST.  Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+   with DCONTEXT as an argument.  Apply SCALE to each point; SCALE
+   should be the factor necessary to turn points into 16.16 fixed
+   point.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+sfnt_decompose_glyph_1 (size_t here, size_t last,
+                       sfnt_move_to_proc move_to,
+                       sfnt_line_to_proc line_to,
+                       sfnt_curve_to_proc curve_to,
+                       void *dcontext,
+                       sfnt_fword *x,
+                       sfnt_fword *y, unsigned char *flags,
+                       int scale)
+{
+  struct sfnt_point control1, control2, start, mid;
+  size_t i;
+
+  /* The contour is empty.  */
+
+  if (here == last)
+    return 1;
+
+  /* Move the pen to the start of the contour.  Apparently some fonts
+     have off the curve points as the start of a contour, so when that
+     happens lerp between the first and last points.  */
+
+  if (flags[here] & 01) /* On Curve */
+    {
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      start = control1;
+    }
+  else if (flags[last] & 01)
+    {
+      /* Start at the last point if it is on the curve.  Here, the
+         start really becomes the middle of a spline.  */
+      control1.x = x[last] * scale;
+      control1.y = y[last] * scale;
+      start = control1;
+
+      /* Curve back one point early.  */
+      last -= 1;
+      here -= 1;
+    }
+  else
+    {
+      /* Lerp between the start and the end.  */
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      control2.x = x[last] * scale;
+      control2.y = y[last] * scale;
+      sfnt_lerp_half (&control1, &control2, &start);
+
+      /* In either of these cases, start iterating from just here as
+        opposed to here + 1, since logically the contour now starts
+        from the last curve.  */
+      here -= 1;
+    }
+
+  /* Move to the start.  */
+  move_to (start, dcontext);
+
+  /* Now handle each point between here + 1 and last.  */
+
+  i = here;
+  while (++i <= last)
+    {
+      /* If the point is on the curve, then draw a line here from the
+        last control point.  */
+
+      if (flags[i] & 01)
+       {
+         control1.x = x[i] * scale;
+         control1.y = y[i] * scale;
+
+         line_to (control1, dcontext);
+
+         /* Move to the next point.  */
+         continue;
+       }
+
+      /* Off the curve points are more interesting.  They are handled
+        one by one, with points in between being interpolated, until
+        either the last point is reached or an on-curve point is
+        processed.  First, load the initial control points.  */
+
+      control1.x = x[i] * scale;
+      control1.y = y[i] * scale;
+
+      while (++i <= last)
+       {
+         /* Load this point.  */
+         control2.x = x[i] * scale;
+         control2.y = y[i] * scale;
+
+         /* If this point is on the curve, curve directly to this
+            point.  */
+
+         if (flags[i] & 01)
+           {
+             curve_to (control1, control2, dcontext);
+             goto continue_loop;
+           }
+
+         /* Calculate the point between here and the previous
+            point.  */
+         sfnt_lerp_half (&control1, &control2, &mid);
+
+         /* Curve over there.  */
+         curve_to (control1, mid, dcontext);
+
+         /* Reload the control point.  */
+         control1 = control2;
+       }
+
+      /* Close the contour by curving back to start.  */
+      curve_to (control1, start, dcontext);
+
+      /* Don't close the contour twice.  */
+      goto exit;
+
+    continue_loop:
+      continue;
+    }
+
+  /* Close the contour with a line back to start.  */
+  line_to (start, dcontext);
+
+ exit:
+  return 0;
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+   HERE and LAST.  Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+   with DCONTEXT as an argument.  Apply SCALE to each point; SCALE
+   should be the factor necessary to turn points into 16.16 fixed
+   point.
+
+   This is the version of sfnt_decompose_glyph_1 which takes
+   sfnt_fixed (or sfnt_f26dot6) as opposed to sfnt_fword.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+sfnt_decompose_glyph_2 (size_t here, size_t last,
+                       sfnt_move_to_proc move_to,
+                       sfnt_line_to_proc line_to,
+                       sfnt_curve_to_proc curve_to,
+                       void *dcontext,
+                       sfnt_fixed *x,
+                       sfnt_fixed *y, unsigned char *flags,
+                       int scale)
+{
+  struct sfnt_point control1, control2, start, mid;
+  size_t i;
+
+  /* The contour is empty.  */
+
+  if (here == last)
+    return 1;
+
+  /* Move the pen to the start of the contour.  Apparently some fonts
+     have off the curve points as the start of a contour, so when that
+     happens lerp between the first and last points.  */
+
+  if (flags[here] & 01) /* On Curve */
+    {
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      start = control1;
+    }
+  else if (flags[last] & 01)
+    {
+      /* Start at the last point if it is on the curve.  Here, the
+         start really becomes the middle of a spline.  */
+      control1.x = x[last] * scale;
+      control1.y = y[last] * scale;
+      start = control1;
+
+      /* Curve back one point early.  */
+      last -= 1;
+      here -= 1;
+    }
+  else
+    {
+      /* Lerp between the start and the end.  */
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      control2.x = x[last] * scale;
+      control2.y = y[last] * scale;
+      sfnt_lerp_half (&control1, &control2, &start);
+
+      /* In either of these cases, start iterating from just here as
+        opposed to here + 1, since logically the contour now starts
+        from the last curve.  */
+      here -= 1;
+    }
+
+  /* Move to the start.  */
+  move_to (start, dcontext);
+
+  /* Now handle each point between here + 1 and last.  */
+
+  i = here;
+  while (++i <= last)
+    {
+      /* If the point is on the curve, then draw a line here from the
+        last control point.  */
+
+      if (flags[i] & 01)
+       {
+         control1.x = x[i] * scale;
+         control1.y = y[i] * scale;
+
+         line_to (control1, dcontext);
+
+         /* Move to the next point.  */
+         continue;
+       }
+
+      /* Off the curve points are more interesting.  They are handled
+        one by one, with points in between being interpolated, until
+        either the last point is reached or an on-curve point is
+        processed.  First, load the initial control points.  */
+
+      control1.x = x[i] * scale;
+      control1.y = y[i] * scale;
+
+      while (++i <= last)
+       {
+         /* Load this point.  */
+         control2.x = x[i] * scale;
+         control2.y = y[i] * scale;
+
+         /* If this point is on the curve, curve directly to this
+            point.  */
+
+         if (flags[i] & 01)
+           {
+             curve_to (control1, control2, dcontext);
+             goto continue_loop;
+           }
+
+         /* Calculate the point between here and the previous
+            point.  */
+         sfnt_lerp_half (&control1, &control2, &mid);
+
+         /* Curve over there.  */
+         curve_to (control1, mid, dcontext);
+
+         /* Reload the control point.  */
+         control1 = control2;
+       }
+
+      /* Close the contour by curving back to start.  */
+      curve_to (control1, start, dcontext);
+
+      /* Don't close the contour twice.  */
+      goto exit;
+
+    continue_loop:
+      continue;
+    }
+
+  /* Close the contour with a line back to start.  */
+  line_to (start, dcontext);
+
+ exit:
+  return 0;
+}
+
 /* Decompose GLYPH into its individual components.  Call MOVE_TO to
    move to a specific location.  For each line encountered, call
    LINE_TO to draw a line to that location.  For each spline
@@ -2736,10 +3011,8 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph,
                      sfnt_free_glyph_proc free_glyph,
                      void *dcontext)
 {
-  size_t here, start, last;
-  struct sfnt_point pen, control1, control2;
+  size_t here, last, n;
   struct sfnt_compound_glyph_context context;
-  size_t n;
 
   if (glyph->simple)
     {
@@ -2756,113 +3029,23 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph,
             of the last point in the contour.  */
          last = glyph->simple->end_pts_of_contours[n];
 
-         /* Move to the start.  */
-         pen.x = glyph->simple->x_coordinates[here] * 65536;
-         pen.y = glyph->simple->y_coordinates[here] * 65536;
-         move_to (pen, dcontext);
-
-         /* Record start so the contour can be closed.  */
-         start = here;
+         /* Make sure here and last make sense.  */
 
-         /* If there is only one point in a contour, draw a one pixel
-            wide line.  */
-         if (last == here)
-           {
-             line_to (pen, dcontext);
-             here++;
-
-             continue;
-           }
-
-         if (here > last)
-           /* Indices moved backwards.  */
+         if (here > last || last >= glyph->simple->number_of_points)
            return 1;
 
-         /* Now start reading points.  If the next point is on the
-            curve, then it is actually a line.  */
-         for (++here; here <= last; ++here)
-           {
-             /* Make sure here is within bounds.  */
-             if (here >= glyph->simple->number_of_points)
-               return 1;
-
-             if (glyph->simple->flags[here] & 01) /* On Curve */
-               {
-                 pen.x = glyph->simple->x_coordinates[here] * 65536;
-                 pen.y = glyph->simple->y_coordinates[here] * 65536;
-
-                 /* See if the last point was on the curve.  If it
-                    wasn't, then curve from there to here.  */
-                 if (!(glyph->simple->flags[here - 1] & 01))
-                   {
-                     control1.x
-                       = glyph->simple->x_coordinates[here - 1] * 65536;
-                     control1.y
-                       = glyph->simple->y_coordinates[here - 1] * 65536;
-                     curve_to (control1, pen, dcontext);
-                   }
-                 else
-                   /* Otherwise, this is an ordinary line from there
-                      to here.  */
-                   line_to (pen, dcontext);
-
-                 continue;
-               }
-
-             /* If the last point was on the curve, then there's
-                nothing extraordinary to do yet.  */
-             if (glyph->simple->flags[here - 1] & 01)
-               ;
-             else
-               {
-                 /* Otherwise, interpolate the point halfway between
-                    the last and current points and make that point
-                    the pen.  */
-                 control1.x = glyph->simple->x_coordinates[here - 1] * 65536;
-                 control1.y = glyph->simple->y_coordinates[here - 1] * 65536;
-                 control2.x = glyph->simple->x_coordinates[here] * 65536;
-                 control2.y = glyph->simple->y_coordinates[here] * 65536;
-                 sfnt_lerp_half (&control1, &control2, &pen);
-                 curve_to (control1, pen, dcontext);
-               }
-           }
-
-         /* Now close the contour if there is more than one point
-            inside it.  */
-         if (start != here - 1)
-           {
-             /* Restore here after the for loop increased it.  */
-             here --;
-
-             /* Previously, this would check whether or not start is
-                an ``on curve'' point, but that is not necessary.
-
-                If a contour is not closed and the edge building
-                process skips the second to last vertex, then the
-                outline can end up with missing edges.  */
-
-             pen.x = glyph->simple->x_coordinates[start] * 65536;
-             pen.y = glyph->simple->y_coordinates[start] * 65536;
-
-             /* See if the last point (in this case, `here') was
-                on the curve.  If it wasn't, then curve from
-                there to here.  */
-             if (!(glyph->simple->flags[here] & 01))
-               {
-                 control1.x
-                   = glyph->simple->x_coordinates[here] * 65536;
-                 control1.y
-                   = glyph->simple->y_coordinates[here] * 65536;
-                 curve_to (control1, pen, dcontext);
-               }
-             else
-               /* Otherwise, this is an ordinary line from there
-                  to here.  */
-               line_to (pen, dcontext);
+         /* Now perform the decomposition.  */
+         if (sfnt_decompose_glyph_1 (here, last, move_to,
+                                     line_to, curve_to,
+                                     dcontext,
+                                     glyph->simple->x_coordinates,
+                                     glyph->simple->y_coordinates,
+                                     glyph->simple->flags,
+                                     65536))
+           return 1;
 
-             /* Restore here to where it was earlier.  */
-             here++;
-           }
+         /* Move forward to the start of the next contour.  */
+         here = last + 1;
        }
 
       return 0;
@@ -2898,108 +3081,22 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph,
         of the last point in the contour.  */
       last = context.contour_end_points[n];
 
-      /* Move to the start.  */
-      pen.x = context.x_coordinates[here];
-      pen.y = context.y_coordinates[here];
-      move_to (pen, dcontext);
-
-      /* Record start so the contour can be closed.  */
-      start = here;
-
-      /* If there is only one point in a contour, draw a one pixel
-        wide line.  */
-      if (last == here)
-       {
-         line_to (pen, dcontext);
-         here++;
-
-         continue;
-       }
+      /* Make sure here and last make sense.  */
 
-      if (here > last)
-       /* Indices moved backwards.  */
+      if (here > last || last >= context.num_points)
        goto fail;
 
-      /* Now start reading points.  If the next point is on the
-        curve, then it is actually a line.  */
-      for (++here; here <= last; ++here)
-       {
-         /* Make sure here is within bounds.  */
-         if (here >= context.num_points)
-           return 1;
-
-         if (context.flags[here] & 01) /* On Curve */
-           {
-             pen.x = context.x_coordinates[here];
-             pen.y = context.y_coordinates[here];
-
-             /* See if the last point was on the curve.  If it
-                wasn't, then curve from there to here.  */
-             if (!(context.flags[here - 1] & 01))
-               {
-                 control1.x = context.x_coordinates[here - 1];
-                 control1.y = context.y_coordinates[here - 1];
-                 curve_to (control1, pen, dcontext);
-               }
-             else
-               /* Otherwise, this is an ordinary line from there
-                  to here.  */
-               line_to (pen, dcontext);
-
-             continue;
-           }
-
-         /* If the last point was on the curve, then there's
-            nothing extraordinary to do yet.  */
-         if (context.flags[here - 1] & 01)
-           ;
-         else
-           {
-             /* Otherwise, interpolate the point halfway between
-                the last and current points and make that point
-                the pen.  */
-             control1.x = context.x_coordinates[here - 1];
-             control1.y = context.y_coordinates[here - 1];
-             control2.x = context.x_coordinates[here];
-             control2.y = context.y_coordinates[here];
-             sfnt_lerp_half (&control1, &control2, &pen);
-             curve_to (control1, pen, dcontext);
-           }
-       }
-
-      /* Now close the contour if there is more than one point
-        inside it.  */
-      if (start != here - 1)
-       {
-         /* Restore here after the for loop increased it.  */
-         here --;
-
-         /* Previously, this would check whether or not start is an
-            ``on curve'' point, but that is not necessary.
-
-            If a contour is not closed and the edge building process
-            skips the second to last vertex, then the outline can end
-            up with missing edges.  */
-
-         pen.x = context.x_coordinates[start];
-         pen.y = context.y_coordinates[start];
-
-         /* See if the last point (in this case, `here') was on the
-            curve.  If it wasn't, then curve from there to here.  */
-         if (!(context.flags[here] & 01))
-           {
-             control1.x = context.x_coordinates[here];
-             control1.y = context.y_coordinates[here];
-             curve_to (control1, pen, dcontext);
-           }
-         else
-           /* Otherwise, this is an ordinary line from there
-              to here.  */
-           line_to (pen, dcontext);
+      /* Now perform the decomposition.  */
+      if (sfnt_decompose_glyph_2 (here, last, move_to,
+                                 line_to, curve_to,
+                                 dcontext,
+                                 context.x_coordinates,
+                                 context.y_coordinates,
+                                 context.flags, 1))
+       goto fail;
 
-         /* Restore here to where it was earlier.  */
-         here++;
-       }
+      /* Move forward.  */
+      here = last + 1;
     }
 
  early:
@@ -10499,9 +10596,7 @@ sfnt_decompose_instructed_outline (struct 
sfnt_instructed_outline *outline,
                                   sfnt_curve_to_proc curve_to,
                                   void *dcontext)
 {
-  size_t here, start, last;
-  struct sfnt_point pen, control1, control2;
-  size_t n;
+  size_t here, last, n;
 
   if (!outline->num_contours)
     return 0;
@@ -10515,109 +10610,20 @@ sfnt_decompose_instructed_outline (struct 
sfnt_instructed_outline *outline,
         of the last point in the contour.  */
       last = outline->contour_end_points[n];
 
-      /* Move to the start.  */
-      pen.x = outline->x_points[here] * 1024;
-      pen.y = outline->y_points[here] * 1024;
-      move_to (pen, dcontext);
-
-      /* Record start so the contour can be closed.  */
-      start = here;
-
-      /* If there is only one point in a contour, draw a one pixel
-        wide line.  */
-      if (last == here)
-       {
-         line_to (pen, dcontext);
-         here++;
-
-         continue;
-       }
+      /* Make sure here and last make sense.  */
 
-      if (here > last)
-       /* Indices moved backwards.  */
+      if (here > last || last >= outline->num_points)
        goto fail;
 
-      /* Now start reading points.  If the next point is on the
-        curve, then it is actually a line.  */
-      for (++here; here <= last; ++here)
-       {
-         /* Make sure here is within bounds.  */
-         if (here >= outline->num_points)
-           return 1;
-
-         if (outline->flags[here] & 01) /* On Curve */
-           {
-             pen.x = outline->x_points[here] * 1024;
-             pen.y = outline->y_points[here] * 1024;
-
-             /* See if the last point was on the curve.  If it
-                wasn't, then curve from there to here.  */
-             if (!(outline->flags[here - 1] & 01))
-               {
-                 control1.x = outline->x_points[here - 1] * 1024;
-                 control1.y = outline->y_points[here - 1] * 1024;
-                 curve_to (control1, pen, dcontext);
-               }
-             else
-               /* Otherwise, this is an ordinary line from there
-                  to here.  */
-               line_to (pen, dcontext);
-
-             continue;
-           }
-
-         /* If the last point was on the curve, then there's
-            nothing extraordinary to do yet.  */
-         if (outline->flags[here - 1] & 01)
-           ;
-         else
-           {
-             /* Otherwise, interpolate the point halfway between
-                the last and current points and make that point
-                the pen.  */
-             control1.x = outline->x_points[here - 1] * 1024;
-             control1.y = outline->y_points[here - 1] * 1024;
-             control2.x = outline->x_points[here] * 1024;
-             control2.y = outline->y_points[here] * 1024;
-             sfnt_lerp_half (&control1, &control2, &pen);
-             curve_to (control1, pen, dcontext);
-           }
-       }
-
-      /* Now close the contour if there is more than one point
-        inside it.  */
-      if (start != here - 1)
-       {
-         /* Restore here after the for loop increased it.  */
-         here --;
-
-         /* Previously, this would check whether or not start is an
-            ``on curve'' point, but that is not necessary.
-
-            If a contour is not closed and the edge building process
-            skips the second to last vertex, then the outline can end
-            up with missing edges.  */
-
-         pen.x = outline->x_points[start] * 1024;
-         pen.y = outline->y_points[start] * 1024;
-
-         /* See if the last point (in this case, `here') was
-            on the curve.  If it wasn't, then curve from
-            there to here.  */
-         if (!(outline->flags[here] & 01))
-           {
-             control1.x = outline->x_points[here] * 1024;
-             control1.y = outline->y_points[here] * 1024;
-             curve_to (control1, pen, dcontext);
-           }
-         else
-           /* Otherwise, this is an ordinary line from there
-              to here.  */
-           line_to (pen, dcontext);
+      if (sfnt_decompose_glyph_2 (here, last, move_to,
+                                 line_to, curve_to, dcontext,
+                                 outline->x_points,
+                                 outline->y_points,
+                                 outline->flags, 1024))
+       goto fail;
 
-         /* Restore here to where it was earlier.  */
-         here++;
-       }
+      /* Move forward to the start of the next contour.  */
+      here = last + 1;
 
       /* here may be a phantom point when outlining a compound glyph,
         as they can have phantom points mixed in with contours.
@@ -15530,6 +15536,9 @@ main (int argc, char **argv)
       return 1;
     }
 
+  fprintf (stderr, "number of subtables: %"PRIu16"\n",
+          table->num_subtables);
+
   for (i = 0; i < table->num_subtables; ++i)
     {
       fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
@@ -15540,8 +15549,8 @@ main (int argc, char **argv)
                 data[i]->format);
     }
 
-#define FANCY_PPEM 12
-#define EASY_PPEM  12
+#define FANCY_PPEM 40
+#define EASY_PPEM  40
 
   interpreter = NULL;
   head = sfnt_read_head_table (fd, font);
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
index feea92827d9..c28a911bfba 100644
--- a/src/sfntfont-android.c
+++ b/src/sfntfont-android.c
@@ -49,9 +49,11 @@ struct sfntfont_android_scanline_buffer
 };
 
 /* Array of directories to search for system fonts.  */
-const char *system_font_directories[] =
+static char *system_font_directories[] =
   {
     "/system/fonts",
+    /* This should be filled in by init_sfntfont_android.  */
+    (char[PATH_MAX]) { },
   };
 
 /* The font cache.  */
@@ -691,6 +693,10 @@ loaded before character sets are made available.  */)
     {
       dir = opendir (system_font_directories[i]);
 
+      __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                          "Loading fonts from: %s",
+                          system_font_directories[i]);
+
       if (!dir)
        continue;
 
@@ -752,6 +758,11 @@ init_sfntfont_android (void)
                      build_string ("Droid Sans Mono")),
               Fcons (build_string ("Sans Serif"),
                      build_string ("Droid Sans")));
+
+  /* Set up the user fonts directory.  This directory is ``fonts'' in
+     the Emacs files directory.  */
+  snprintf (system_font_directories[1], PATH_MAX, "%s/fonts",
+           android_get_home_directory ());
 }
 
 void
diff --git a/src/sfntfont.c b/src/sfntfont.c
index a5ed54394a2..f9344067f1a 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -87,6 +87,10 @@ struct sfnt_font_desc
 
   /* The offset of the table directory within PATH.  */
   off_t offset;
+
+  /* The number of glyphs in this font.  Used to catch invalid cmap
+     tables.  This is actually the number of glyphs - 1.  */
+  int num_glyphs;
 };
 
 /* List of fonts.  */
@@ -517,10 +521,11 @@ sfnt_enum_font_1 (int fd, const char *file,
                  struct sfnt_offset_subtable *subtables,
                  off_t offset)
 {
-  struct sfnt_font_desc *desc;
+  struct sfnt_font_desc *desc, **next, *prev;
   struct sfnt_head_table *head;
   struct sfnt_name_table *name;
   struct sfnt_meta_table *meta;
+  struct sfnt_maxp_table *maxp;
   Lisp_Object family, style;
 
   /* Create the font desc and copy in the file name.  */
@@ -543,12 +548,16 @@ sfnt_enum_font_1 (int fd, const char *file,
   if (!name)
     goto bail2;
 
+  maxp = sfnt_read_maxp_table (fd, subtables);
+  if (!maxp)
+    goto bail3;
+
   /* meta is not required, nor present on many non-Apple fonts.  */
   meta = sfnt_read_meta_table (fd, subtables);
 
   /* Decode the family and style from the name table.  */
   if (sfnt_decode_family_style (name, &family, &style))
-    goto bail3;
+    goto bail4;
 
   /* Set the family.  */
   desc->family = family;
@@ -556,6 +565,9 @@ sfnt_enum_font_1 (int fd, const char *file,
   desc->char_cache = Qnil;
   desc->subtable.platform_id = 500;
 
+  /* Set the largest glyph identifier.  */
+  desc->num_glyphs = maxp->num_glyphs;
+
   /* Parse the style.  */
   sfnt_parse_style (style, desc);
 
@@ -584,13 +596,32 @@ sfnt_enum_font_1 (int fd, const char *file,
   desc->next = system_fonts;
   system_fonts = desc;
 
+  /* Remove any fonts which have the same style as this one.  */
+
+  next = &system_fonts->next;
+  prev = *next;
+  for (; *next; prev = *next)
+    {
+      if (!NILP (Fstring_equal (prev->style, desc->style))
+         && !NILP (Fstring_equal (prev->family, desc->family)))
+       {
+         *next = prev->next;
+         xfree (prev);
+       }
+      else
+       next = &prev->next;
+    }
+
   xfree (meta);
+  xfree (maxp);
   xfree (name);
   xfree (head);
   return 0;
 
- bail3:
+ bail4:
   xfree (meta);
+  xfree (maxp);
+ bail3:
   xfree (name);
  bail2:
   xfree (head);
@@ -602,6 +633,8 @@ sfnt_enum_font_1 (int fd, const char *file,
 /* Enumerate the font FILE into the list of system fonts.  Return 1 if
    it could not be enumerated, 0 otherwise.
 
+   Remove any font whose family and style is a duplicate of this one.
+
    FILE can either be a TrueType collection file containing TrueType
    fonts, or a TrueType font itself.  */
 
@@ -960,6 +993,25 @@ sfntfont_read_cmap (struct sfnt_font_desc *desc,
   emacs_close (fd);
 }
 
+/* Return whether or not CHARACTER has an associated mapping in CMAP,
+   and the mapping points to a valid glyph.  DESC is the font
+   descriptor associated with the font.  */
+
+static bool
+sfntfont_glyph_valid (struct sfnt_font_desc *desc,
+                     sfnt_char font_character,
+                     struct sfnt_cmap_encoding_subtable_data *cmap)
+{
+  sfnt_glyph glyph;
+
+  glyph = sfnt_lookup_glyph (font_character, cmap);
+
+  if (!glyph)
+    return false;
+
+  return glyph <= desc->num_glyphs;
+}
+
 /* Look up a character CHARACTER in the font description DESC.  Cache
    the results.  Return true if the character exists, false otherwise.
 
@@ -1013,8 +1065,10 @@ sfntfont_lookup_char (struct sfnt_font_desc *desc, 
Lisp_Object character,
   if (font_character == CHARSET_INVALID_CODE (charset))
     return false;
 
-  /* Now return whether or not the glyph is present.  */
-  present = sfnt_lookup_glyph (font_character, *cmap) != 0;
+  /* Now return whether or not the glyph is present.  Noto Sans
+     Georgian comes with a corrupt format 4 cmap table that somehow
+     tries to express glyphs greater than 65565.  */
+  present = sfntfont_glyph_valid (desc, font_character, *cmap);
 
   /* Cache the result.  Store Qlambda when not present, Qt
      otherwise.  */
@@ -1133,22 +1187,23 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, 
Lisp_Object spec)
        {
          tem = XCDR (tem);
 
-         /* tem is a list of each characters, one of which must be
+         /* tem is a list of each characters, all of which must be
             present in the font.  */
          FOR_EACH_TAIL_SAFE (tem)
            {
-             if (FIXNUMP (XCAR (tem)))
-               {
-                 if (!sfntfont_lookup_char (desc, XCAR (tem), &cmap,
-                                            &subtable))
-                   goto fail;
-
-                 /* One character is enough to pass a font.  Don't
-                    look at too many.  */
-                 break;
-               }
+             if (FIXNUMP (XCAR (tem))
+                 && !sfntfont_lookup_char (desc, XCAR (tem), &cmap,
+                                           &subtable))
+               goto fail;
            }
+
+         /* One or more characters are missing.  */
+         if (!NILP (tem))
+           goto fail;
        }
+      /* Fail if there are no matching fonts at all.  */
+      else if (NILP (tem))
+       goto fail;
     }
 
   /* Now check that the language is supported.  */
diff --git a/src/textconv.c b/src/textconv.c
index 835d03f3037..5090b0a33b6 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -35,6 +35,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "textconv.h"
 #include "buffer.h"
 #include "syntax.h"
+#include "blockinput.h"
 
 
 
@@ -47,6 +48,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 static struct textconv_interface *text_interface;
 
+/* How many times text conversion has been disabled.  */
+
+static int suppress_conversion_count;
+
 /* Flags used to determine what must be sent after a batch edit
    ends.  */
 
@@ -391,7 +396,8 @@ textconv_query (struct frame *f, struct 
textconv_callback_struct *query,
 static void
 sync_overlay (struct frame *f)
 {
-  if (MARKERP (f->conversion.compose_region_start))
+  if (MARKERP (f->conversion.compose_region_start)
+      && !NILP (Vtext_conversion_face))
     {
       if (NILP (f->conversion.compose_region_overlay))
        {
@@ -400,7 +406,7 @@ sync_overlay (struct frame *f)
                             f->conversion.compose_region_end, Qnil,
                             Qt, Qnil);
          Foverlay_put (f->conversion.compose_region_overlay,
-                       Qface, Qunderline);
+                       Qface, Vtext_conversion_face);
        }
 
       Fmove_overlay (f->conversion.compose_region_overlay,
@@ -514,6 +520,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
 {
   specpdl_ref count;
   ptrdiff_t wanted, start, end;
+  struct window *w;
 
   /* If F's old selected window is no longer live, fail.  */
 
@@ -624,6 +631,10 @@ really_commit_text (struct frame *f, EMACS_INT position,
 
   /* This should deactivate the mark.  */
   call0 (Qdeactivate_mark);
+
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
   unbind_to (count, Qnil);
 }
 
@@ -760,6 +771,10 @@ really_set_composing_text (struct frame *f, ptrdiff_t 
position,
        text_interface->compose_region_changed (f);
     }
 
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
   unbind_to (count, Qnil);
 }
 
@@ -771,6 +786,7 @@ really_set_composing_region (struct frame *f, ptrdiff_t 
start,
                             ptrdiff_t end)
 {
   specpdl_ref count;
+  struct window *w;
 
   /* If F's old selected window is no longer live, fail.  */
 
@@ -810,6 +826,10 @@ really_set_composing_region (struct frame *f, ptrdiff_t 
start,
               make_fixnum (end), Qnil);
   sync_overlay (f);
 
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
   unbind_to (count, Qnil);
 }
 
@@ -823,6 +843,7 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t 
left,
 {
   specpdl_ref count;
   ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
+  struct window *w;
 
   /* If F's old selected window is no longer live, fail.  */
 
@@ -889,6 +910,10 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t 
left,
   if (get_mark () == PT)
     call0 (Qdeactivate_mark);
 
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
   unbind_to (count, Qnil);
 }
 
@@ -904,6 +929,7 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point,
                           ptrdiff_t mark)
 {
   specpdl_ref count;
+  struct window *w;
 
   /* If F's old selected window is no longer live, fail.  */
 
@@ -922,7 +948,7 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point,
     {
       if (f->conversion.batch_edit_count > 0)
        f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
-      else
+      else if (text_interface && text_interface->point_changed)
        text_interface->point_changed (f,
                                       XWINDOW (f->old_selected_window),
                                       current_buffer);
@@ -936,6 +962,10 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t 
point,
   else
     call1 (Qpush_mark, make_fixnum (mark));
 
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
   unbind_to (count, Qnil);
 }
 
@@ -949,9 +979,11 @@ complete_edit (void *token)
 }
 
 /* Process and free the text conversion ACTION.  F must be the frame
-   on which ACTION will be performed.  */
+   on which ACTION will be performed.
 
-static void
+   Value is the window which was used, or NULL.  */
+
+static struct window *
 handle_pending_conversion_events_1 (struct frame *f,
                                    struct text_conversion_action *action)
 {
@@ -969,9 +1001,23 @@ handle_pending_conversion_events_1 (struct frame *f,
   token = action->counter;
   xfree (action);
 
+  /* Text conversion events can still arrive immediately after
+     `conversion_disabled_p' becomes true.  In that case, process all
+     events, but don't perform any associated actions.  */
+
+  if (conversion_disabled_p ())
+    return NULL;
+
   /* Make sure completion is signalled.  */
   count = SPECPDL_INDEX ();
   record_unwind_protect_ptr (complete_edit, &token);
+  w = NULL;
+
+  if (WINDOW_LIVE_P (f->old_selected_window))
+    {
+      w = XWINDOW (f->old_selected_window);
+      buffer = XBUFFER (WINDOW_BUFFER (w));
+    }
 
   switch (operation)
     {
@@ -987,12 +1033,7 @@ handle_pending_conversion_events_1 (struct frame *f,
        break;
 
       if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE)
-       {
-         w = XWINDOW (f->old_selected_window);
-         buffer = XBUFFER (WINDOW_BUFFER (w));
-
-         text_interface->point_changed (f, w, buffer);
-       }
+       text_interface->point_changed (f, w, buffer);
 
       if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE)
        text_interface->compose_region_changed (f);
@@ -1030,6 +1071,8 @@ handle_pending_conversion_events_1 (struct frame *f,
     }
 
   unbind_to (count, Qnil);
+
+  return w;
 }
 
 /* Decrement the variable pointed to by *PTR.  */
@@ -1055,6 +1098,8 @@ handle_pending_conversion_events (void)
   bool handled;
   static int inside;
   specpdl_ref count;
+  ptrdiff_t last_point;
+  struct window *w;
 
   handled = false;
 
@@ -1065,6 +1110,8 @@ handle_pending_conversion_events (void)
     Vtext_conversion_edits = Qnil;
 
   inside++;
+  last_point = -1;
+  w = NULL;
 
   count = SPECPDL_INDEX ();
   record_unwind_protect_ptr (decrement_inside, &inside);
@@ -1077,16 +1124,26 @@ handle_pending_conversion_events (void)
         process them in bottom to up order.  */
       while (true)
        {
-         /* Redisplay in between if there is more than one
-            action.
-
-            This can read input.  This function must be reentrant
-            here.  */
-
-         if (handled)
-           redisplay ();
+         /* Update the input method if handled &&
+            w->ephemeral_last_point != last_point.  */
+         if (w && (last_point != w->ephemeral_last_point))
+           {
+             if (handled
+                 && last_point != -1
+                 && text_interface
+                 && text_interface->point_changed)
+               {
+                 if (f->conversion.batch_edit_count > 0)
+                   f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+                 else
+                   text_interface->point_changed (f, NULL, NULL);
+               }
+
+             last_point = w->ephemeral_last_point;
+           }
 
-         /* Reload action.  */
+         /* Reload action.  This needs to be reentrant as buffer
+            modification functions can call `read-char'.  */
          action = f->conversion.actions;
 
          /* If there are no more actions, break.  */
@@ -1099,7 +1156,7 @@ handle_pending_conversion_events (void)
          f->conversion.actions = next;
 
          /* Handle and free the action.  */
-         handle_pending_conversion_events_1 (f, action);
+         w = handle_pending_conversion_events_1 (f, action);
          handled = true;
        }
     }
@@ -1399,6 +1456,16 @@ get_extracted_text (struct frame *f, ptrdiff_t n,
   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.  */
+
+bool
+conversion_disabled_p (void)
+{
+  return suppress_conversion_count > 0;
+}
+
 
 
 /* Window system interface.  These are called from the rest of
@@ -1440,6 +1507,60 @@ report_point_change (struct frame *f, struct window 
*window,
     text_interface->point_changed (f, window, buffer);
 }
 
+/* Temporarily disable text conversion.  Must be paired with a
+   corresponding call to resume_text_conversion.  */
+
+void
+disable_text_conversion (void)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  suppress_conversion_count++;
+
+  if (!text_interface || suppress_conversion_count > 1)
+    return;
+
+  /* Loop through and reset the input method on each window system
+     frame.  It should call conversion_disabled_p and then DTRT.  */
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      f = XFRAME (frame);
+      reset_frame_state (f);
+
+      if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+       text_interface->reset (f);
+    }
+}
+
+/* Undo the effect of the last call to `disable_text_conversion'.  */
+
+void
+resume_text_conversion (void)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  suppress_conversion_count--;
+  eassert (suppress_conversion_count >= 0);
+
+  if (!text_interface || suppress_conversion_count)
+    return;
+
+  /* Loop through and reset the input method on each window system
+     frame.  It should call conversion_disabled_p and then DTRT.  */
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      f = XFRAME (frame);
+      reset_frame_state (f);
+
+      if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+       text_interface->reset (f);
+    }
+}
+
 /* Register INTERFACE as the text conversion interface.  */
 
 void
@@ -1450,6 +1571,59 @@ register_textconv_interface (struct textconv_interface 
*interface)
 
 
 
+/* Lisp interface.  */
+
+DEFUN ("set-text-conversion-style", Fset_text_conversion_style,
+       Sset_text_conversion_style, 1, 1, 0,
+       doc: /* Set the text conversion style in the current buffer.
+
+Set `text-conversion-mode' to VALUE, then force any input method
+editing frame displaying this buffer to stop itself.
+
+This can lead to a significant amount of time being taken by the input
+method resetting itself, so you should not use this function lightly;
+instead, set `text-conversion-mode' before your buffer is displayed,
+and let redisplay manage the input method appropriately.  */)
+  (Lisp_Object value)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+  Lisp_Object buffer;
+
+  bset_text_conversion_style (current_buffer, value);
+
+  if (!text_interface)
+    return Qnil;
+
+  /* If there are any seleted windows displaying this buffer, reset
+     text conversion on their associated frames.  */
+
+  if (buffer_window_count (current_buffer))
+    {
+      buffer = Fcurrent_buffer ();
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         f = XFRAME (frame);
+
+         if (WINDOW_LIVE_P (f->old_selected_window)
+             && FRAME_WINDOW_P (f)
+             && EQ (XWINDOW (f->old_selected_window)->contents,
+                    buffer))
+           {
+             block_input ();
+             reset_frame_state (f);
+             text_interface->reset (f);
+             unblock_input ();
+           }
+       }
+    }
+
+  return Qnil;
+}
+
+
+
 void
 syms_of_textconv (void)
 {
@@ -1457,6 +1631,7 @@ syms_of_textconv (void)
   DEFSYM (Qtext_conversion, "text-conversion");
   DEFSYM (Qpush_mark, "push-mark");
   DEFSYM (Qunderline, "underline");
+  DEFSYM (Qoverriding_text_conversion_style, 
"overriding-text-conversion-style");
 
   DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
     doc: /* List of buffers that were last edited as a result of text 
conversion.
@@ -1480,4 +1655,21 @@ inserted.
 If a deletion occured, then BEG and END are the same, and EPHEMERAL is
 nil.  */);
   Vtext_conversion_edits = Qnil;
+
+  DEFVAR_LISP ("overriding-text-conversion-style",
+              Voverriding_text_conversion_style,
+    doc: /* Non-buffer local version of `text-conversion-style'.
+
+If this variable is the symbol `lambda', it means to consult the
+buffer local variable `text-conversion-style' to determine whether or
+not to activate the input method.  Otherwise, its value is used in
+preference to any buffer local value of `text-conversion-style'.  */);
+  Voverriding_text_conversion_style = Qlambda;
+
+  DEFVAR_LISP ("text-conversion-face", Vtext_conversion_face,
+    doc: /* Face in which to display temporary edits by an input method.
+nil means to display no indication of a temporary edit.  */);
+  Vtext_conversion_face = Qunderline;
+
+  defsubr (&Sset_text_conversion_style);
 }
diff --git a/src/textconv.h b/src/textconv.h
index 034c663521a..16d13deb092 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -140,6 +140,7 @@ extern void delete_surrounding_text (struct frame *, 
ptrdiff_t,
                                     ptrdiff_t, unsigned long);
 extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
                                 ptrdiff_t *, ptrdiff_t *, ptrdiff_t *);
+extern bool conversion_disabled_p (void);
 
 extern void register_textconv_interface (struct textconv_interface *);
 
diff --git a/src/window.h b/src/window.h
index 463c7f89b9b..36b1bfb7283 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,21 @@ struct window
        it should be positive.  */
     ptrdiff_t last_point;
 
+#ifdef HAVE_TEXT_CONVERSION
+    /* ``ephemeral'' last point position.  This is used while
+       processing text conversion events.
+
+       `last_point' is normally used during redisplay to indicate the
+       position of point as seem by the input method.  However, it is
+       not updated if consequtive conversions are processed at the
+       same time.
+
+       This `ephemeral_last_point' field is either the last point as
+       set in redisplay or the last point after a text editing
+       operation.  */
+    ptrdiff_t ephemeral_last_point;
+#endif
+
     /* Value of mark in the selected window at the time of the last
        redisplay.  */
     ptrdiff_t last_mark;
diff --git a/src/xdisp.c b/src/xdisp.c
index 525eaa64b3a..a17fa97ee20 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -17315,6 +17315,9 @@ mark_window_display_accurate_1 (struct window *w, bool 
accurate_p)
        w->last_mark = -1;
 
 #ifdef HAVE_TEXT_CONVERSION
+      /* See the description of this field in struct window.  */
+      w->ephemeral_last_point = w->last_point;
+
       /* Point motion is only propagated to the input method for use
         in text conversion during a redisplay.  While this can lead
         to inconsistencies when point has moved but the change has



reply via email to

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