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 <<= a href=3D"mailto:[email protected]">[email protected]</a>> =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">> From: dANiuu zH= aO <<a href=3D"mailto:[email protected]" target=3D"_blank">zhaodaniu1= @gmail.com</a>><br> > Date: Fri, 31 Jul 2026 23:32:09 +0800<br> > Cc: <a href=3D"mailto:[email protected]" target=3D"_blank">81506@d= ebbugs.gnu.org</a><br> > <br> > [PATCH 3] Implement preedit text rendering for the IME window on w32<b= r> > <br> > > This is the second patch.<br> > > I fixed the static buffer issue from yesterday by switching to on= -demand allocation.> <br> > > For w32term.c, I removed the direct execution of Lisp code and in= stead communicate by writing to inev's<br> > args, following the preedit handling approach in xterm.c.<br> > > I kept the w32-ime-preedit flag to toggle the default IME window.= <br> > > The hook-based approach has been completely removed. Everything n= ow works through event reading.<br> > <br> > This is a version based on the previous patch. This version adds the f= ollowing features:<br> > <br> > - When composing CJK strings, the IME window no longer jitters along w= ith the cursor, keeping the behavior<br> > consistent with the native Windows input method behavior.<br> > - When composing CJK strings, while Emacs renders the preedit text, th= e cursor can respond to arrow keys =E2=80=94<br> > specifically <left> and <right> behavior.<br> > <br> > If you have any questions or concerns about the code, please feel free= to contact me.<br> > <br> > I have set up a dedicated example repository to document the developme= nt of this patch from scratch:<br> > <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> > <br> > Later on, I will attempt to implement the font size feature described = in <br> > <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> > , 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'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> > --- a/src/w32term.c<br> > +++ b/src/w32term.c<br> > @@ -5387,6 +5387,45 @@ w32_read_socket (struct terminal *terminal,<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;<br> >=C2=A0 <br> > +=C2=A0 =C2=A0 =C2=A0case WM_IME_COMPOSITION:<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0{<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0f =3D w32_window_to_frame (dpyinfo,= msg.msg.hwnd);<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Lisp_Object str =3D Qnil;<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int size =3D (int) msg.msg.lParam;<= br> > +<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (size > 0)<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int cursor_pos;<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int utf8_len;<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0char *utf8_buf;<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0wchar_t *wbuf =3D (wc= har_t *) msg.msg.wParam;<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0utf8_len =3D WideChar= ToMultiByte (CP_UTF8, 0,<br> > +=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> > +=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> > +=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> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0utf8_buf =3D alloca (= utf8_len);<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0utf8_len =3D WideChar= ToMultiByte(CP_UTF8, 0,<br> > +=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> > +=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> > +=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> > +=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> > +<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* offaet end= , read cursor position.=C2=A0 */<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy (&cursor_p= os, (char *) wbuf + size, sizeof (cursor_pos));<br> > +=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> > +=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> > +=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> > +=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> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}<br> > +<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.kind =3D PREEDIT_TEXT_EVENT;<b= r> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.arg =3D str;<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0inev.modifiers =3D msg.dwModifiers;= <br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0XSETFRAME (inev.frame_or_window, f)= ;<br> > +=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> > +=C2=A0 DEFVAR_BOOL ("w32-ime-preedit",<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 w32_ime_preedit,<br> > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 doc: /* Non-nil means repor= t IME preedit strings to<br> > +`w32-ime-composition-hook'.=C2=A0 When nil, the IME preedit is no= t<br> > +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 'apropos', which show only the first line, co= uld<br> present helpful summary of the variable'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--