bug#68339: [patch] Set the IME window font to an appropriate size

dANiuu zHaO <[email protected]> Fri, 7 Aug 2026 11:36:41 +0800
Newsgroups gmane.emacs.bugs
Message-ID <CAEOE9jcJ_0bsaPf1YpRuejwZjgwPzD92UuPXtFgp-AeoS8Uftg@mail.gmail.com>
[PATCH 4] Set the IME window font to an appropriate size

I think I've fixed the issue, but someone still needs to test it, just to
be safe.

Of the patches I posted earlier, the most effective approach was based on
Eli's description: using face-remap on the default face to obtain the
accurate font size and font name. It supports mappings like Emacs's
built-in variable-pitch-mode perfectly. However, my previous implementation
had threading issues.

So I went to figure out how the update logic for w32_system_caret_height
works. I discovered something important: w32_system_caret_height equals
w->phys_cursor_height, i.e., the pixel height. In Emacs, this manifests as
the cursor height always being that of the tallest character in a line of
text — in other words, the cursor height is always equal to the line height.

And since the FRAME_LINE_HEIGHT macro can obtain the frame's text line
height, we can derive the following:

> lf.lfHeight *= w32_system_caret_height / FRAME_LINE_HEIGHT (f);

w32_system_caret_height is dynamic, but FRAME_LINE_HEIGHT is not — it
returns the default line height rather than the face-remapped one. So
dividing the two gives us the scaling ratio, which solves the scaling
problem for lfHeight.

However, this approach can't handle built-in mappings like
variable-pitch-mode for fonts — it would cause a mismatch between the font
used in the buffer and the font in the IME window.

That said, in w32term.c (w32_draw_window_cursor), I found that after
updating cursor-related data, it sends a WM_IME_STARTCOMPOSITION
notification. This means w32_draw_window_cursor runs on the Lisp thread,
not the input thread. So calling lookup_basic_face from within this
function should be thread-safe. I declared a global variable for the input
thread to access (it could also be passed via PostMessage; I did it this
way just to be consistent with the declaration style of
w32_system_caret_height and similar variables).

I've updated the patch and tested it locally—everything looks good. Still,
it would be great if someone else could test it just to be safe.

dANiuu zHaO <[email protected]> 于2026年8月6日周四 00:00写道:

> I'll try everything I can.
> The only remaining issue is the lfHeight value. Once that's resolved,
> it'll be perfect.
>
> Eli Zaretskii <[email protected]> 于 2026年8月5日周三 23:47写道:
>
>> > From: dANiuu zHaO <[email protected]>
>> > Date: Wed, 5 Aug 2026 22:40:23 +0800
>> > Cc: Eli Zaretskii <[email protected]>, [email protected]
>> >
>> > Let me summarize the current progress.
>> >
>> > The first patch — the core code is this:
>> >
>> > > font = FRAME_FONT (f);
>> > > GetObjectW (FONT_HANDLE (font), sizeof (lf), &lf);
>> > > set_ime_composition_font_fn (context, &lf);
>> >
>> > This code has no threading issues. However, when the font has been
>> scaled,
>> > the font height obtained by FRAME_FONT is not the scaled one, but the
>> default font height.
>> > It lacks dynamic adjustment of lf.lfHeight. There is no thread-safety
>> issue.
>> >
>> > The third patch — the code is this:
>> >
>> > > face = FACE_FROM_ID_OR_NULL (f, lookup_basic_face (w, f,
>> DEFAULT_FACE_ID));
>> > > font = face ? face->font : FRAME_FONT (f);
>> > > GetObjectW (FONT_HANDLE (font), sizeof (lf), &lf);
>> > > set_ime_composition_font_fn (context, &lf);
>> >
>> > Through lookup_basic_face, I can access the scaled font, and its height
>> is correct.
>> > However, there are threading issues — it has a probability of
>> triggering the emacs_abort function.
>> > My guess is that the cause is accessing it from within the Win32
>> message loop.
>> >
>> > The third patch — the code is this:
>> >
>> > > font = FRAME_FONT (f);
>> > > GetObjectW (FONT_HANDLE (font), sizeof (lf), &lf);
>> > > lf.lfHeight = -w32_system_caret_height;
>> > > set_ime_composition_font_fn (context, &lf);
>> >
>> > It is the same as the first patch — no thread-safety issues.
>> > However, it does have dynamic adjustment of lf.lfHeight,
>> > which can be dynamically set based on the cursor height.
>> > The problem is that w32_system_caret_height tends to be much larger
>> than the
>> > actual font pixel height — it is oversized.
>> >
>> > > Does it not work even if you start the composition _after_ changing
>> > > the font via the mouse-wheel?  If so, I think it's because FRAME_FONT
>> > > gives you the default font, which doesn't take face-remapping (which
>> > > is how text-scale works in Emacs) into account.  To get the font after
>> > > remapping, you need to do something like:
>> > >
>> > >   . call lookup_basic_face (W, F, DEFAULT_FACE_ID), where W and F and
>> > >     the window and the frame; this gives you a face ID
>> > >   . use FACE_FROM_ID to get 'struct face' from the above ID
>> > >   . obtain the font as face->font
>> > >   . if any of that fails, use FRAME_FONT as you do now
>> >
>> > I think I should listen to Eli and revert to the first patch for now.
>> > I don't have any good ideas at the moment either.
>> > It wouldn't be good if the changes get too complicated.
>>
>> One idea is to introduce a variable, probably exposed to Lisp, which
>> scales w32_system_caret_height down to a reasonable value, which is
>> similar to the scaled font used by the Emacs frame, and then use the
>> height of the caret multiplied by that scale variable as the height
>> for the IME font..
>>
>> Another idea is to look at the code which updates
>> w32_system_caret_height when text-scaling changes, understand why the
>> caret is so large and implement in the same place the scaling of
>> lfHeight similar to what we do to scale w32_system_caret_height, but
>> in a way that produces a better size.
>>
>> If none of this works, we could add to 'struct frame' a new member
>> that holds the height of the scaled default font, and then the input
>> thread could simply access the 'struct frame' of the selected frame
>> and use that value.
>>
>> Does this make sense?
>>
>
0001-Set-the-IME-window-font-to-an-appropriate-size.patch (application/octet-stream, 4.2 KB)
From 5d9922cc681870b606cfcd06c40fb3b666c8746f Mon Sep 17 00:00:00 2001
From: zdn <[email protected]>
Date: Sun, 2 Aug 2026 11:05:02 +0800
Subject: [PATCH] Set the IME window font to an appropriate size.

* src/w32fns.c (w32_wnd_proc): Using FONT_HANDLE to HFONT from
w32_system_remap_font.
* src/w32term.c (w32_draw_window_cursor): Update
w32_system_remap_font by  using lookup_basic_face to obtain the faceID,
then passing it to FACE_FROM_ID_OR_NULL to get the corresponding face struct.
* src/w32term.h: Add w32_system_remap_font point
---
 src/w32fns.c  | 11 +++++++++++
 src/w32term.c |  6 ++++++
 src/w32term.h |  2 ++
 3 files changed, 19 insertions(+)

diff --git a/src/w32fns.c b/src/w32fns.c
index 726fe52..21303a1 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -47,6 +47,7 @@ #define COBJMACROS /* Ask for C definitions for COM.  */
 
 #include "lisp.h"
 #include "w32term.h"
+#include "w32font.h"
 #include "frame.h"
 #include "window.h"
 #include "buffer.h"
@@ -200,6 +201,9 @@ #define CCHDEVICENAME 32
 typedef BOOL (WINAPI * ImmGetOpenStatus_Proc) (IN HIMC);
 typedef BOOL (WINAPI * ImmSetOpenStatus_Proc) (IN HIMC, IN BOOL);
 
+/* Set IME font.  */
+typedef BOOL (WINAPI * ImmSetCompositionFont_Proc) (IN HIMC, LPLOGFONTW lplf);
+
 typedef HMONITOR (WINAPI * MonitorFromPoint_Proc) (IN POINT pt, IN DWORD flags);
 typedef BOOL (WINAPI * GetMonitorInfo_Proc)
   (IN HMONITOR monitor, OUT struct MONITOR_INFO* info);
@@ -247,6 +251,7 @@ #define CCHDEVICENAME 32
 static ImmGetContext_Proc get_ime_context_fn = NULL;
 static ImmGetOpenStatus_Proc get_ime_open_status_fn = NULL;
 static ImmSetOpenStatus_Proc set_ime_open_status_fn = NULL;
+static ImmSetCompositionFont_Proc set_ime_composition_font_fn = NULL;
 static ImmReleaseContext_Proc release_ime_context_fn = NULL;
 static ImmSetCompositionWindow_Proc set_ime_composition_window_fn = NULL;
 static MonitorFromPoint_Proc monitor_from_point_fn = NULL;
@@ -5032,6 +5037,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       else
 	{
 	  COMPOSITIONFORM form;
+	  LOGFONTW lf;
 	  HIMC context;
 	  struct window *w;
 
@@ -5077,6 +5083,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 	  if (!context)
 	    goto dflt;
 
+	  GetObjectW (FONT_HANDLE (w32_system_remap_font), sizeof (lf), &lf);
+	  set_ime_composition_font_fn (context, &lf);
 	  set_ime_composition_window_fn (context, &form);
 	  release_ime_context_fn (hwnd, context);
 	}
@@ -12332,6 +12340,9 @@ globals_of_w32fns (void)
       get_proc_addr (imm32_lib, "ImmGetOpenStatus");
     set_ime_open_status_fn = (ImmSetOpenStatus_Proc)
       get_proc_addr (imm32_lib, "ImmSetOpenStatus");
+
+    set_ime_composition_font_fn = (ImmSetCompositionFont_Proc)
+      get_proc_addr (imm32_lib, "ImmSetCompositionFontW");
   }
 
   HMODULE hm_kernel32 = GetModuleHandle ("kernel32.dll");
diff --git a/src/w32term.c b/src/w32term.c
index 728f6ce..285146d 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -180,6 +180,8 @@ #define SM_CXVIRTUALSCREEN 78
 #define SM_CYVIRTUALSCREEN 79
 #endif
 
+struct font *w32_system_remap_font;
+
 /* The handle of the frame that currently owns the system caret.  */
 HWND w32_system_caret_hwnd;
 int w32_system_caret_height;
@@ -6670,6 +6672,8 @@ w32_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
       if (active_p)
 	{
 	  struct frame *f = XFRAME (WINDOW_FRAME (w));
+	  int face_id = lookup_basic_face (w, f, DEFAULT_FACE_ID);
+	  struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
 	  HWND hwnd = FRAME_W32_WINDOW (f);
 
 	  w32_system_caret_x
@@ -6682,6 +6686,8 @@ w32_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
 	    + WINDOW_HEADER_LINE_HEIGHT (w);
 	  w32_system_caret_mode_height = WINDOW_MODE_LINE_HEIGHT (w);
 
+	  w32_system_remap_font = face ? face->font : FRAME_FONT (f);
+
 	  PostMessage (hwnd, WM_IME_STARTCOMPOSITION, 0, 0);
 
 	  /* If the size of the active cursor changed, destroy the old
diff --git a/src/w32term.h b/src/w32term.h
index aec957b..7fc5ee8 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -872,6 +872,8 @@ #define TME_LEAVE 0x00000002;
     IN UINT_PTR,
     IN LPCWSTR);
 
+extern struct font *w32_system_remap_font;
+
 extern HWND w32_system_caret_hwnd;
 extern int w32_system_caret_height;
 extern int w32_system_caret_x;
-- 
2.55.0