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.
This commit is contained in:
parent
cf24b61985
commit
2dcce30290
24 changed files with 803 additions and 427 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}))
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)))))))
|
||||
|
||||
|
||||
|
||||
|
|
25
lisp/subr.el
25
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))
|
||||
|
|
|
@ -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);
|
||||
|
|
22
src/buffer.c
22
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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
26
src/lread.c
26
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,
|
||||
|
|
625
src/sfnt.c
625
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);
|
||||
/* Make sure here and last make sense. */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
/* Make sure here and last make sense. */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
/* Make sure here and last make sense. */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 (sfnt_decompose_glyph_2 (here, last, move_to,
|
||||
line_to, curve_to, dcontext,
|
||||
outline->x_points,
|
||||
outline->y_points,
|
||||
outline->flags, 1024))
|
||||
goto fail;
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
232
src/textconv.c
232
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.
|
||||
/* 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);
|
||||
}
|
||||
|
||||
This can read input. This function must be reentrant
|
||||
here. */
|
||||
last_point = w->ephemeral_last_point;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
redisplay ();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
||||
|
|
15
src/window.h
15
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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue