bug#81506: [PATCH] Extend w32 window events to expose IME composition string interface

dANiuu zHaO <[email protected]> Sun, 2 Aug 2026 11:13:35 +0800
Newsgroups gmane.emacs.bugs
Message-ID <CAEOE9jeBpLZ+87066uq7-vteWo6k105uG_eWq_7ivmjq04n8mQ@mail.gmail.com>
--00000000000083cec5065807cffa
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Thank you for your review. I have already implemented the ideas regarding
bug#68339, but there are still a few issues. I will post the relevant
patches on bug#68339 later. When the time comes that you think the current
patch can be considered for further development, I hope you could send me
an email to let me know.

Eli Zaretskii <[email protected]> =E4=BA=8E2026=E5=B9=B48=E6=9C=882=E6=97=A5=E5=
=91=A8=E6=97=A5 00:01=E5=86=99=E9=81=93=EF=BC=9A

> > From: dANiuu zHaO <[email protected]>
> > Date: Fri, 31 Jul 2026 23:32:09 +0800
> > Cc: [email protected]
> >
> > [PATCH 3] Implement preedit text rendering for the IME window on w32
> >
> > > This is the second patch.
> > > I fixed the static buffer issue from yesterday by switching to
> on-demand allocation.>
> > > For w32term.c, I removed the direct execution of Lisp code and instea=
d
> communicate by writing to inev's
> > args, following the preedit handling approach in xterm.c.
> > > I kept the w32-ime-preedit flag to toggle the default IME window.
> > > The hook-based approach has been completely removed. Everything now
> works through event reading.
> >
> > This is a version based on the previous patch. This version adds the
> following features:
> >
> > - When composing CJK strings, the IME window no longer jitters along
> with the cursor, keeping the behavior
> > consistent with the native Windows input method behavior.
> > - When composing CJK strings, while Emacs renders the preedit text, the
> cursor can respond to arrow keys =E2=80=94
> > specifically <left> and <right> behavior.
> >
> > If you have any questions or concerns about the code, please feel free
> to contact me.
> >
> > I have set up a dedicated example repository to document the developmen=
t
> of this patch from scratch:
> > https://github.com/zHaOdANiuu/emacs-w32-draw-preedit.patch
> >
> > Later on, I will attempt to implement the font size feature described i=
n
> > https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3D68339
> > , in order to improve the overall Windows input method experience in
> Emacs.
>
> Thanks.  I would prefer to start with what was described in bug#68339.
> The reason is twofold: (1) it is a less radical change, in that the
> window shown by the IME is still used, we just adjust its font size;
> (2) modifying overlays to present the preedit text will cause the
> following redisplay of the selected Emacs window to be more thorough,
> thus more expensive and slower.  I'm also not sure what this means if
> and when the relevant text is in a non-selected window (if this is at
> all possible).  This is why I prefer to begin with a more conservative
> change that fixes the original problem by adjusting the font of the
> preedit window, and leave these more extensive changes (really,
> improvements) for later.
>
> A few comments to the code:
>
> > --- a/src/w32term.c
> > +++ b/src/w32term.c
> > @@ -5387,6 +5387,45 @@ w32_read_socket (struct terminal *terminal,
> >           }
> >         break;
> >
> > +     case WM_IME_COMPOSITION:
> > +       {
> > +         f =3D w32_window_to_frame (dpyinfo, msg.msg.hwnd);
> > +         Lisp_Object str =3D Qnil;
> > +         int size =3D (int) msg.msg.lParam;
> > +
> > +         if (size > 0)
> > +           {
> > +             int cursor_pos;
> > +             int utf8_len;
> > +             char *utf8_buf;
> > +             wchar_t *wbuf =3D (wchar_t *) msg.msg.wParam;
> > +             utf8_len =3D WideCharToMultiByte (CP_UTF8, 0,
> > +                                             wbuf, size /
> sizeof(wchar_t),
> > +                                             NULL, 0,
> > +                                             NULL, NULL);
> > +             utf8_buf =3D alloca (utf8_len);
> > +             utf8_len =3D WideCharToMultiByte(CP_UTF8, 0,
> > +                                            wbuf, size /
> sizeof(wchar_t),
> > +                                            utf8_buf, utf8_len,
> > +                                            NULL, NULL);
> > +             str =3D make_string_from_utf8 (utf8_buf, utf8_len);
> > +
> > +                /* offaet end, read cursor position.  */
> > +             memcpy (&cursor_pos, (char *) wbuf + size, sizeof
> (cursor_pos));
> > +                /* see xic_preedit_draw_callback function.  */
> > +             Fput_text_property (make_fixnum (min (SCHARS (str), max
> (0, cursor_pos))),
> > +                                 make_fixnum (min (SCHARS (str), max
> (0, cursor_pos) + 1)),
> > +                                 Qcursor, Qt, str);
> > +           }
> > +
> > +         inev.kind =3D PREEDIT_TEXT_EVENT;
> > +         inev.arg =3D str;
> > +         inev.modifiers =3D msg.dwModifiers;
> > +         XSETFRAME (inev.frame_or_window, f);
> > +         inev.timestamp =3D msg.msg.time;
>
> I believe the processing of the string and the Fput_text_property call
> should be moved to where this input event is processed in
> make_lispy_event.  The input event structure should have the original
> preedit data passed via the inev.arg member, and all the rest of the
> processing above should be in make_lispy_event.
>
> > +  DEFVAR_BOOL ("w32-ime-preedit",
> > +            w32_ime_preedit,
> > +            doc: /* Non-nil means report IME preedit strings to
> > +`w32-ime-composition-hook'.  When nil, the IME preedit is not
> > +reported and the IME works as normal.  */);
>
> The first line of the doc string should be a single complete sentence,
> so that commands like 'apropos', which show only the first line, could
> present helpful summary of the variable's doc.
>
> More importantly, this doc string explains the technical details of
> the effect of the variable, whereas it should explain the effect in
> user-level terms.
>

--00000000000083cec5065807cffa
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Thank you for your review. I have already implemented the =
ideas regarding bug#68339, but there are still a few issues. I will post th=
e relevant patches on bug#68339 later. When the time comes that you think t=
he current patch can be considered for further development, I hope you coul=
d send me an email to let me know.</div><br><div class=3D"gmail_quote gmail=
_quote_container"><div dir=3D"ltr" class=3D"gmail_attr">Eli Zaretskii &lt;<=
a href=3D"mailto:[email protected]">[email protected]</a>&gt; =E4=BA=8E2026=E5=B9=B48=
=E6=9C=882=E6=97=A5=E5=91=A8=E6=97=A5 00:01=E5=86=99=E9=81=93=EF=BC=9A<br><=
/div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bo=
rder-left:1px solid rgb(204,204,204);padding-left:1ex">&gt; From: dANiuu zH=
aO &lt;<a href=3D"mailto:[email protected]" target=3D"_blank">zhaodaniu1=
@gmail.com</a>&gt;<br>
&gt; Date: Fri, 31 Jul 2026 23:32:09 +0800<br>
&gt; Cc: <a href=3D"mailto:[email protected]" target=3D"_blank">81506@d=
ebbugs.gnu.org</a><br>
&gt; <br>
&gt; [PATCH 3] Implement preedit text rendering for the IME window on w32<b=
r>
&gt; <br>
&gt; &gt; This is the second patch.<br>
&gt; &gt; I fixed the static buffer issue from yesterday by switching to on=
-demand allocation.&gt; <br>
&gt; &gt; For w32term.c, I removed the direct execution of Lisp code and in=
stead communicate by writing to inev&#39;s<br>
&gt; args, following the preedit handling approach in xterm.c.<br>
&gt; &gt; I kept the w32-ime-preedit flag to toggle the default IME window.=
<br>
&gt; &gt; The hook-based approach has been completely removed. Everything n=
ow works through event reading.<br>
&gt; <br>
&gt; This is a version based on the previous patch. This version adds the f=
ollowing features:<br>
&gt; <br>
&gt; - When composing CJK strings, the IME window no longer jitters along w=
ith the cursor, keeping the behavior<br>
&gt; consistent with the native Windows input method behavior.<br>
&gt; - When composing CJK strings, while Emacs renders the preedit text, th=
e cursor can respond to arrow keys =E2=80=94<br>
&gt; specifically &lt;left&gt; and &lt;right&gt; behavior.<br>
&gt; <br>
&gt; If you have any questions or concerns about the code, please feel free=
 to contact me.<br>
&gt; <br>
&gt; I have set up a dedicated example repository to document the developme=
nt of this patch from scratch:<br>
&gt; <a href=3D"https://github.com/zHaOdANiuu/emacs-w32-draw-preedit.patch"=
 rel=3D"noreferrer" target=3D"_blank">https://github.com/zHaOdANiuu/emacs-w=
32-draw-preedit.patch</a><br>
&gt; <br>
&gt; Later on, I will attempt to implement the font size feature described =
in <br>
&gt; <a href=3D"https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3D68339" rel=
=3D"noreferrer" target=3D"_blank">https://debbugs.gnu.org/cgi/bugreport.cgi=
?bug=3D68339</a><br>
&gt; , in order to improve the overall Windows input method experience in E=
macs.<br>
<br>
Thanks.=C2=A0 I would prefer to start with what was described in bug#68339.=
<br>
The reason is twofold: (1) it is a less radical change, in that the<br>
window shown by the IME is still used, we just adjust its font size;<br>
(2) modifying overlays to present the preedit text will cause the<br>
following redisplay of the selected Emacs window to be more thorough,<br>
thus more expensive and slower.=C2=A0 I&#39;m also not sure what this means=
 if<br>
and when the relevant text is in a non-selected window (if this is at<br>
all possible).=C2=A0 This is why I prefer to begin with a more conservative=
<br>
change that fixes the original problem by adjusting the font of the<br>
preedit window, and leave these more extensive changes (really,<br>
improvements) for later.<br>
<br>
A few comments to the code:<br>
<br>
&gt; --- a/src/w32term.c<br>
&gt; +++ b/src/w32term.c<br>
&gt; @@ -5387,6 +5387,45 @@ w32_read_socket (struct terminal *terminal,<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;<br>
&gt;=C2=A0 <br>
&gt; +=C2=A0 =C2=A0 =C2=A0case WM_IME_COMPOSITION:<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0{<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0f =3D w32_window_to_frame (dpyinfo,=
 msg.msg.hwnd);<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Lisp_Object str =3D Qnil;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int size =3D (int) msg.msg.lParam;<=
br>
&gt; +<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (size &gt; 0)<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int cursor_pos;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int utf8_len;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0char *utf8_buf;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0wchar_t *wbuf =3D (wc=
har_t *) msg.msg.wParam;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0utf8_len =3D WideChar=
ToMultiByte (CP_UTF8, 0,<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0wbuf, size / sizeof(wchar_t),<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0NULL, 0,<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0NULL, NULL);<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0utf8_buf =3D alloca (=
utf8_len);<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0utf8_len =3D WideChar=
ToMultiByte(CP_UTF8, 0,<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 wbuf, size / sizeof(wchar_t),<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 utf8_buf, utf8_len,<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 NULL, NULL);<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0str =3D make_string_f=
rom_utf8 (utf8_buf, utf8_len);<br>
&gt; +<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* offaet end=
, read cursor position.=C2=A0 */<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy (&amp;cursor_p=
os, (char *) wbuf + size, sizeof (cursor_pos));<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* see xic_pr=
eedit_draw_callback function.=C2=A0 */<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Fput_text_property (m=
ake_fixnum (min (SCHARS (str), max (0, cursor_pos))),<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0make_fixnum (min (SCHARS (=
str), max (0, cursor_pos) + 1)),<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Qcursor, Qt, str);<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}<br>
&gt; +<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.kind =3D PREEDIT_TEXT_EVENT;<b=
r>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.arg =3D str;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.modifiers =3D msg.dwModifiers;=
<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0XSETFRAME (inev.frame_or_window, f)=
;<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.timestamp =3D msg.msg.time;<br=
>
<br>
I believe the processing of the string and the Fput_text_property call<br>
should be moved to where this input event is processed in<br>
make_lispy_event.=C2=A0 The input event structure should have the original<=
br>
preedit data passed via the inev.arg member, and all the rest of the<br>
processing above should be in make_lispy_event.<br>
<br>
&gt; +=C2=A0 DEFVAR_BOOL (&quot;w32-ime-preedit&quot;,<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 w32_ime_preedit,<br>
&gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 doc: /* Non-nil means repor=
t IME preedit strings to<br>
&gt; +`w32-ime-composition-hook&#39;.=C2=A0 When nil, the IME preedit is no=
t<br>
&gt; +reported and the IME works as normal.=C2=A0 */);<br>
<br>
The first line of the doc string should be a single complete sentence,<br>
so that commands like &#39;apropos&#39;, which show only the first line, co=
uld<br>
present helpful summary of the variable&#39;s doc.<br>
<br>
More importantly, this doc string explains the technical details of<br>
the effect of the variable, whereas it should explain the effect in<br>
user-level terms.<br>
</blockquote></div>

--00000000000083cec5065807cffa--