Re: [PATCH] emoji on TUI emacs

Kai Ma <[email protected]> Thu, 30 Jul 2026 18:52:18 +0200
Newsgroups gmane.emacs.devel
Message-ID <[email protected]>
On 7/30/2026 6:13 PM, Eli Zaretskii wrote:
>> Date: Thu, 30 Jul 2026 17:03:58 +0200
>> From: Kai Ma <[email protected]>
>>
>> Today I took some time to look into the emoji problem on TUI emacs.
>> There were some previous bug reports, for example, 79517, 81052.  This
>> is a super annoying problem which makes it almost unusable to edit
>> files containing emojis.  (Brief description: pressing C-n/C-p can
>> cause duplicate rendered lines or empty lines, and it's not possible
>> to clear it easily with C-l or redisplay.)
> The way to make it possible to edit text with Emoji on text-only
> frames is by disabling auto-composition-mode.  Did you try that?  If
> you tried that, and you still find that "almost unusable", please tell
> the details of what you see and preferably also show a simple recipe
> to reproduce the problems you see.

Yes. I'm aware of this workaround. Sorry for not mentioning it in the 
original mail. There are two drawbacks for this workaround:

1. This breaks textual layout.

2. This makes it other composition no longer work.

>> Previous discussions are usually about the width of a grapheme
>> cluster.  But to my surprise, I found that the problem occurs even if
>> the width is correct.  For example, ⚡ (#x26a1) composed with #fe0f
>> has width 2,
>>
>>       (string-width "\x26a1\xfe0f") ;; ==> 2
> Yes, Emoji sequences which end in Variation Selector controls are one
> of a couple of cases which currently cannot be correctly displayed on
> text-only frames when auto-composition-mode is enabled.  Please try
> disabling auto-composition-mode.

That's why I think there's something wrong with the current handling of 
the composition.

>> and it still causes garbled lines.  Therefore, the incorrect width of
>> an emoji is not the _only_ problem.
> It isn't the width of the Emoji, it's the fact that
>
>    (string-width "\x26a1") ;; ==> 2
>
> IOW, adding the VS-15 control leaves the width unaltered.

I'm talking about the composition. The impression I got from previous 
discussions and etc/PROBLEMS are that the problem is *solely* about 
width calculation.  That is not true!

The example in my original mail shows that even if the width is correct, 
the rendering issue still happens.


>> Upon further inspection, I'm
>> pretty sure it's related to composition strings, and it's due to the
>> wrong x index of the next glyph: even if the composition itself has
>> pixel_width=2, the next glyph's index just increases by 1.  So this
>> looks like a genuine problem.
> Sorry, you lost me here: which code and in what function do you allude
> here?

See below. But please correct my misunderstandings.

>> Looking into the source code, in term.c, append_composite_glyph only
>> appends one glyph, but in append_glyph, it appends pixel_width glyphs.
>> Therefore, the attached patch simply copies the existing approach.  I
>> have tested it on files containing many emojis, margins, and bidi
>> texts, and observed no problems so far, but I might miss something so
>> review is appreciated.
> I don't understand why these changes are needed, or what is wrong with
> the original code.

There are clearly two coordinate systems, the terminal one for cells 
(denoted T) and the emacs internal matrix one (denoted E). E is 
generated by append_composite_glyph filling in the glyph row, and T is 
generated by the tty_* functions.

Let's say the buffer contains an emoji ⚡ and there's a character 'a' 
follows it. The current append_composite_glyph assigns coordinates: '⚡' 
E(line,col) and 'a' E(line,col+1).

When it's being output to the terminal, cmplus is used to maintain the T 
coordinates. cmplus is used (in term.c) to advance the tty cursor, but 
'len' argument of 'tty_write_glyphs' etc is determined from the glyph 
matrix. See, for example, in update_text_area in dispnew.c:

if (desired_row->used[TEXT_AREA]) rif->write_glyphs (w, updated_row, 
desired_row->glyphs[TEXT_AREA], TEXT_AREA, desired_row->used[TEXT_AREA]);

The last argument is the number of cells of this write.

In the current implementation, '⚡' at E(line,col) is emitted at 
T(line,col) (assuming nothing precedes it), but 'a' at T(line,col+1), 
despite the fact that _Emacs itself_ thinks ⚡ has width 2.

So IMO this is an internal invariant violation.

> In general, it is wrong to produce as many glyphs as pixel_width for a
> composite glyph, because Emacs cannot know the actual width of the
> composed glyph on display, as shown on text-only frames.  The value of
> pixel_width in this case is a guess based on what characters were
> composed.  So your changes might work for some codepoints and on some
> specific terminal emulators, but they will not work correctly in
> general.

Pardon my ignorance. I don't follow you here.

Let's say the composition has Emacs width N, printed at T(0,0), where 
should the next glyph be? I think it's fair to assume T(0,N).

>> Also, if it's considered a safe fix, is it possible to install this
>> patch on emacs-31, considering how annoying the problem is?
> Sorry, I don't think the changes are correct, let alone safe.  Please
> try disabling auto-composition-mode instead.

Do you have other acceptable alternative solutions in mind?