Subject: bug#10299: Emacs doesn't handle Unicode characters in keyboard layout on MS Windows

From: Joakim Hårsman <[email-masked]>
Date: Sat, 17 Dec 2011 13:52:29 +0100

On 16 December 2011 12:26, Eli Zaretskii <[email-masked]> wrote:
>> Date: Fri, 16 Dec 2011 12:14:50 +0100
>> From: Dani Moncayo <[email-masked]>
>> Cc: Eli Zaretskii <[email-masked]>, [email-masked]
>>
>> > I think I have a MSYS sh.exe somewhere in my path which caused the
>> > build to fail if I didn't explicitly set SHELL.
>>
>> Maybe if you try "mingw32-make" instead of "make", you will be able to
>> build Emacs successfully (see
>> http://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00025.html)
>
> If his make.exe reacts correctly to SHELL=cmd.exe, it is most probably
> a MinGW Make, just renamed to make.exe.  (FWIW, my MinGW compiled Make
> is also called make.exe.)

Ok, I tried switching to RegisterClassW, but that didn't really help,
after calling TranslateMessage I still got WM_CHAR with a question
mark. So I tried switching to CreateWindowW and GetMessageW as well,
and that did the trick. Here's my changes:

D:\Dev_projects\emacs\trunk\nt>bzr diff
=== modified file 'src/w32fns.c'
--- src/w32fns.c        2011-12-04 08:02:42 +0000
+++ src/w32fns.c        2011-12-17 11:39:10 +0000
@@ -1785,7 +1785,7 @@
 static BOOL
 w32_init_class (HINSTANCE hinst)
 {
-  WNDCLASS wc;
+  WNDCLASSW wc;

   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = (WNDPROC) w32_wnd_proc;
@@ -1796,9 +1796,9 @@
   wc.hCursor = w32_load_cursor (IDC_ARROW);
   wc.hbrBackground = NULL; /* GetStockObject (WHITE_BRUSH);  */
   wc.lpszMenuName = NULL;
-  wc.lpszClassName = EMACS_CLASS;
+  wc.lpszClassName = L"Emacs";

-  return (RegisterClass (&wc));
+  return (RegisterClassW (&wc));
 }

 static HWND
@@ -1853,7 +1853,7 @@
     }

   FRAME_W32_WINDOW (f) = hwnd
-    = CreateWindow (EMACS_CLASS,
+    = CreateWindowW (L"Emacs",
                    f->namebuf,
                    f->output_data.w32->dwStyle | WS_CLIPCHILDREN,
                    EQ (left, Qunbound) ? CW_USEDEFAULT : XINT (left),
@@ -2248,7 +2248,7 @@

   msh_mousewheel = RegisterWindowMessage (MSH_MOUSEWHEEL);

-  while (GetMessage (&msg, NULL, 0, 0))
+  while (GetMessageW (&msg, NULL, 0, 0))
     {
       if (msg.hwnd == NULL)
        {
@@ -2507,7 +2507,7 @@
      message that has no particular effect. */
   {
     int c = wParam;
-    if (isalpha (c) && wmsg.dwModifiers == ctrl_modifier)
+    if (iswalpha (c) && wmsg.dwModifiers == ctrl_modifier)
       c = make_ctrl_char (c) & 0377;
     if (c == quit_char
        || (wmsg.dwModifiers == 0 &&

I had to switch to iswalpha because isalpha segfaulted when sent
values larger than 255. wParam isn't really the Unicode code point
here either, it's encoded as UTF-16. With this change, Emacs no longer
prints a question mark when I press the special keys, it doesn't print
anything at all.

I figured this is beacause wParam isn't a valid Unicode codepoint
here, like it is when you get a WM_UNICHAR, so I tried a quick hack
converting from UTF-16 and re-posting as a WM_UNICHAR even. I changed
the handling of WM_CHAR in w32_wnd_proc:

    case WM_CHAR:
      if (wParam > 255 )
        {
          /* Hacky conversion from UTF-16 to UCS-4.
             Doesn't handle surrogate pairs. */
          unsigned short lo = wParam & 0x0000FFFF;
          unsigned short hi = (wParam & 0xFFFF0000) >> 8;
          wParam  = hi | lo;

          W32Msg wmsg;
          wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
          signal_user_input ();
          my_post_msg (&wmsg, hwnd, WM_UNICHAR, wParam, lParam);

        }
      else
        post_character_message (hwnd, msg, wParam, lParam,
                                w32_get_key_modifiers (wParam, lParam));

This fixed it! I can now enter the characters with a custom keyboard
layout so I'm happy. There's loads of bugs still (e.g. the frame title
is "e" instead of "Emacs@host" because I just pass the frame's namebuf
to CreateWindowW without converting it to a wide string), but at least
the main issue is fixed.

I guess these changes need to only happen when running on NT for Emacs
to still work on Windows 95 or does Emacs use MSLU? Maybe they should
depend on some lisp variable being set?

Here's my changes in full so far:

=== modified file 'src/w32fns.c'
--- src/w32fns.c        2011-12-04 08:02:42 +0000
+++ src/w32fns.c        2011-12-17 12:47:44 +0000
@@ -1785,7 +1785,7 @@
 static BOOL
 w32_init_class (HINSTANCE hinst)
 {
-  WNDCLASS wc;
+  WNDCLASSW wc;

   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = (WNDPROC) w32_wnd_proc;
@@ -1796,9 +1796,9 @@
   wc.hCursor = w32_load_cursor (IDC_ARROW);
   wc.hbrBackground = NULL; /* GetStockObject (WHITE_BRUSH);  */
   wc.lpszMenuName = NULL;
-  wc.lpszClassName = EMACS_CLASS;
+  wc.lpszClassName = L"Emacs";

-  return (RegisterClass (&wc));
+  return (RegisterClassW (&wc));
 }

 static HWND
@@ -1853,7 +1853,7 @@
     }

   FRAME_W32_WINDOW (f) = hwnd
-    = CreateWindow (EMACS_CLASS,
+    = CreateWindowW (L"Emacs",
                    f->namebuf,
                    f->output_data.w32->dwStyle | WS_CLIPCHILDREN,
                    EQ (left, Qunbound) ? CW_USEDEFAULT : XINT (left),
@@ -2248,7 +2248,7 @@

   msh_mousewheel = RegisterWindowMessage (MSH_MOUSEWHEEL);

-  while (GetMessage (&msg, NULL, 0, 0))
+  while (GetMessageW (&msg, NULL, 0, 0))
     {
       if (msg.hwnd == NULL)
        {
@@ -2507,7 +2507,7 @@
      message that has no particular effect. */
   {
     int c = wParam;
-    if (isalpha (c) && wmsg.dwModifiers == ctrl_modifier)
+    if (iswalpha (c) && wmsg.dwModifiers == ctrl_modifier)
       c = make_ctrl_char (c) & 0377;
     if (c == quit_char
        || (wmsg.dwModifiers == 0 &&
@@ -2915,8 +2915,23 @@

     case WM_SYSCHAR:
     case WM_CHAR:
-      post_character_message (hwnd, msg, wParam, lParam,
-                             w32_get_key_modifiers (wParam, lParam));
+      if (wParam > 255 )
+        {
+          /* Hacky conversion from UTF-16 to UCS-4.
+             Doesn't handle surrogate pairs. */
+          unsigned short lo = wParam & 0x0000FFFF;
+          unsigned short hi = (wParam & 0xFFFF0000) >> 8;
+          wParam  = hi | lo;
+
+          W32Msg wmsg;
+          wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
+          signal_user_input ();
+          my_post_msg (&wmsg, hwnd, WM_UNICHAR, wParam, lParam);
+
+        }
+      else
+        post_character_message (hwnd, msg, wParam, lParam,
+                                w32_get_key_modifiers (wParam, lParam));
       break;

     case WM_UNICHAR: