]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Thu, 16 Feb 2023 15:57:01 +0000 (23:57 +0800)
committerPo Lu <luangruo@yahoo.com>
Thu, 16 Feb 2023 15:57:01 +0000 (23:57 +0800)
* 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.

24 files changed:
doc/emacs/android.texi
doc/emacs/input.texi
doc/lispref/commands.texi
java/org/gnu/emacs/EmacsInputConnection.java
java/org/gnu/emacs/EmacsSurfaceView.java
java/org/gnu/emacs/EmacsView.java
lisp/comint.el
lisp/international/fontset.el
lisp/isearch.el
lisp/minibuffer.el
lisp/simple.el
lisp/subr.el
src/androidterm.c
src/buffer.c
src/keyboard.c
src/lisp.h
src/lread.c
src/sfnt.c
src/sfntfont-android.c
src/sfntfont.c
src/textconv.c
src/textconv.h
src/window.h
src/xdisp.c

index 308bd70c59c70be60c5e531c6200d8de3778fae4..35f0ba55cf44b88df0ea9957e6d532f208cee217 100644 (file)
@@ -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
index 154b7025ff4c8b9c91f64a12b82e37c8d197bff8..87ed1f4f8a90c78d890b483eb889c0df6f009c13 100644 (file)
@@ -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.
index 5fb2217a03b7c1fc200bd0c7894ae216d392a8ca..898cc9a944bb71591d566f876aaeed50f42975f3 100644 (file)
@@ -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}))
index 5eb56d5aa71b73c731e3e51150b0bee917dbf153..e2a1589469564d0919d7455a75f9576ea8eb82d6 100644 (file)
@@ -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;
+  }
+
 \f
   /* Override functions which are not implemented.  */
 
index 2d80be0881add82f63751d12a6e936664e8b27f7..62e927094e4e5140f1128a52d2e5c16b3e42d96d 100644 (file)
@@ -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)
       {
index 52da6d41f7d5555c227941f71a942d53a8afd18f..5ea8b0dcc0e210657fdbe14d1a5f60ce5fdd0276 100644 (file)
@@ -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.  */
index 9d2c245247fca247691ce8d356ba9948d075ee1f..e1786e7e670b2990e72cd65d3708e609084c97ad 100644 (file)
@@ -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.
index eb1c7f53d366ddf05d34c6700e8c15c8ca28f97d..8fa67d994776d08dce16d4c65b2d201b6e1f6deb 100644 (file)
        (symbol . [#x201C #x2200 #x2500])
        (braille #x2800)
        (ideographic-description #x2FF0)
-       (cjk-misc #x300E)
+       (cjk-misc #x300E #xff0c)
        (kana #x304B)
        (bopomofo #x3105)
        (kanbun #x319D)
          (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*")
               (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))
index 80c7a3b3d3f3668341e710a32d3120ae54e2e217..a17b22fd627b52f9890ed21358fccba097e3415f 100644 (file)
@@ -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,
index 8c1a0d4b21c96cb995ec2bf31f5cb91ca23684c1..ef0eb1ca108f0473e1191ce8d733370e00b46119 100644 (file)
@@ -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.
 
index 6a12585a55dbc2f2059f2122721d5c8351803f3c..e5bf90a8055f6c870e628d0b888703ba9850697b 100644 (file)
@@ -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)))))))
 
 \f
 
index f4f041ff32d7488d4edccd66266587876fc9d454..8e594fafa8ca17cdd2adc551a1abc69a54ffbed0 100644 (file)
@@ -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))
index 0c990d3d2d2304cd5ea6a0d97c5d484fc9d4b100..c6f75ec9219ce794528c0441c9d197c827cdb1d5 100644 (file)
@@ -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);
index af4aa583c96c89a6bcf1ac5e537c0f4bd0807cb6..393b8c5340a78513a974862672e7cc3fccc2df28 100644 (file)
@@ -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.
index 538fdffc199eb4ef106a72d24248fee3bd65d3db..a8062adc46878bb56c483168adce3357f28ee7ce 100644 (file)
@@ -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;
index a39ca8cc5413aeef8036bbd2fa6caba6d7ee8906..56ef338a5b14c68cc5504e9b3665d4dc5940c93b 100644 (file)
@@ -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
 
index 6abcea556bf36858b7ce8f62e026afc0a419c5f3..150d8a01e10691714de47151ee78fb56c04d9e33 100644 (file)
@@ -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,
index 38f6984b93cec0f2f933d8f834661c211025d50e..b6f4a48ea8be74b086c9e383856645b9599d7e88 100644 (file)
@@ -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);
index feea92827d9062061079ddf2a04bd424a9e9e141..c28a911bfba793d22913bf4296af9f5af4b9240c 100644 (file)
@@ -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
index a5ed54394a29f933761fa9db395013e8f86009b1..f9344067f1a34c9e6c427a96fe7b4bfd3fb454bf 100644 (file)
@@ -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.  */
index 835d03f303728c3a7f15e537944a17ad7363b00e..5090b0a33b6633ea86ba1e542e92eac96fd9a249 100644 (file)
@@ -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"
 
 \f
 
@@ -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;
+}
+
 \f
 
 /* 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)
 
 \f
 
+/* 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;
+}
+
+\f
+
 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);
 }
index 034c663521aefb9446dffcfa2f6d103f8fef93f6..16d13deb092d1181b2809a2d358dacf3b870e1bd 100644 (file)
@@ -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 *);
 
index 463c7f89b9bbf4b9e74a979af4695468540ef04e..36b1bfb7283b0d23aa37f984c7c2cda28907bad8 100644 (file)
@@ -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;
index 525eaa64b3a6fdf0982b16068cfdd73389346a37..a17fa97ee20fd96e9652dd6db914919f8846b794 100644 (file)
@@ -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