Change DefWindowProc to DefWindowProcW for Unicode#9099
Change DefWindowProc to DefWindowProcW for Unicode#9099ulhc wants to merge 4 commits intoocornut:dockingfrom ulhc:docking
Conversation
Handle WM_IME_CHAR message for input character processing.
Sorry, I didn't describe it clearly. Changing DefWindowProc to DefWindowProcW in ImGui_ImplWin32_WndProcHandler_PlatformWindow method in imgui_impl_win32.cpp can only solve the problem of platform window input created by imgui. WM_IME_CHAR mainly solves the problem of user-created main platform window input (compatible with RegisterClassA or RegisterClassW). |
|
I don't understand, could you clarify ? |
Your english is not an issue here, don't worry :) But: " WM_IME_CHAR primarily addresses input issues when merging an ImGui window into a user window. " I don't know what a "merge an ImGui Window" or "a user window" means, it's ambiguous. You are showing a video where the inputs happens over "Dear ImGui DirectX11 Example", is that the exact code provided in examples/example_win32_directx11/main.cpp ? Because that window is created with CreateWindowW(), and it works for me without the WM_IME_CHAR. How did you build it? |
I changed RegisterClassExW to RegisterClassExA in main.cpp because most of my projects use RegisterClassEx, which automatically selects based on _UNICODE, and I wanted to be compatible with both A and W. |
|
[Q2] Why are you doing ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&ch, sizeof(ch), &wch, 1);sizeof(ch) == 2 ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);added by 0a7054c. Wasn't this patch already supposed to fix this? Also see #1807 (comment). |
After input is complete, WM_IME_COMPOSITION will trigger once with the entire character table, while WM_IME_CHAR is more like WM_CHAR, triggering for each character individually. #3653 Cannot handle input of multiple consecutive characters. |
(RegisterClassA)DBCS: A single-byte or double-byte character value. For a double-byte character, (BYTE)(wParam >> 8) contains the lead byte. Note that the parentheses are necessary because the cast operator has higher precedence than the shift operator. (RegisterClassW)Unicode: A Unicode character value. or ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&ch, 2, &wch, 1);
The wParam received in WM_CHAR is not a complete DBCS but a truncated one, so you need to concatenate it yourself. |
|
I need your help and a more careful exploration and explanation of every paths. This code is used by thousands of people. [Q3] Was 0a7054c actually broken or unsufficient? If no, why. If yes, why didn't people notice? [Q4] If I change every |
|
I guess what I want to understand most is Q3 the status of 0a7054c, what did it fix and/or break? |
It seems to have no effect; it simply processes the double-byte character as '??'. |
…tting non-ASCII values to io.AddInputCharacter(). (#9099)
Whether DefWindowProc is A or W should be determined by RegisterClassEx. If WM_IME_CHAR is handled, then both A and W will be compatible. |
Then how come #5725 (comment) said "working!"
See my code, if I make app MBCS and remove all A/W suffix I only get 0x3F values in WM_IME_CHAR: (Keyboard in japanese mode, inputting any Japanese text) |
I investigated and realized is is because local codepage mostly have 1 byte characters, so it worked for Cyrillic with Code_page_866. |
I seem to be different from you. I'm using a Chinese keyboard, and the result of typing 'の' is: Using a Japanese keyboard, the result of typing 'あ' is: |
|
Just to confirm, is this with my patch over docking? |
|
I found an answer for it: "If your program registers by RegisterClassA() or RegisterClassExA(), the wParam of WM_IME_CHAR gets the particular encoding selected by the “language for non-Unicode programs” setting." "When the “language for non-Unicode programs” is set to “English (United States)”, or any languages whose encoding does not cover the input, and a user enters Eastern Asian characters via an IME, the values in wParam of WM_IME_CHAR depend on how you register the class and how you handle the WM_IME_COMPOSITION message. If WM_IME_COMPOSITION is properly handled, and you register via RegisterClass[Ex]A(), the wParam gets the encoding of the IME. if you register via RegisterClass[Ex]W() the wParam gets the Unicode. Without proper handling of the WM_IME_COMPOSITION message, the wParam gets a “?” to indicate an error condition, regardless how you register the window class, by RegisterClass[Ex]A() or RegisterClass[Ex]W(). " |
|
(1) If I use this: case WM_IME_COMPOSITION:
{
// Handling WM_IME_COMPOSITION ensure that WM_IME_CHAR value is correct even for MBCS apps.
// (see #9099, #3653 and https://stackoverflow.com/questions/77450354 topics)
IMGUI_DEBUG_LOG("WM_IME_COMPOSITION wParam %08X, lParam %08X\n", wParam, lParam);
return ::DefWindowProcW(hwnd, msg, wParam, lParam);
}
case WM_IME_CHAR:
if (::IsWindowUnicode(hwnd) == FALSE)
{
unsigned short ch = (unsigned short)wParam;
if (::IsDBCSLeadByte(HIBYTE(wParam)))
ch = MAKEWORD(HIBYTE(wParam), LOBYTE(wParam));
wchar_t wch = 0;
::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&ch, sizeof(ch), &wch, 1);
IMGUI_DEBUG_LOG("WM_IME_CHAR %08X -> %08X -> %08X\n", wParam, ch, wch);
io.AddInputCharacterUTF16(wch);
return 1;
}
return 0;I get the correct Do you get the same result? (2) If I change handler to: case WM_IME_COMPOSITION:
{
// Handling WM_IME_COMPOSITION ensure that WM_IME_CHAR value is correct even for MBCS apps.
// (see #9099, #3653 and https://stackoverflow.com/questions/77450354 topics)
IMGUI_DEBUG_LOG("WM_IME_COMPOSITION wParam %08X, lParam %08X\n", wParam, lParam);
::DefWindowProcW(hwnd, msg, wParam, lParam);
return 1;
}+ WM_IME_CHAR handler, then I only get the correct (3) This also seems to work for me: case WM_IME_COMPOSITION:
{
// Handling WM_IME_COMPOSITION ensure that WM_IME_CHAR value is correct even for MBCS apps.
// (see #9099, #3653 and https://stackoverflow.com/questions/77450354 topics)
IMGUI_DEBUG_LOG("WM_IME_COMPOSITION wParam %08X, lParam %08X\n", wParam, lParam);
LRESULT result = ::DefWindowProcW(hwnd, msg, wParam, lParam);
return (lParam & GCS_RESULTSTR) ? 1 : result;
}+ WM_IME_CHAR handler. Can you check all three on your system? |
|
(4) Fix WM_CHAR's case WM_CHAR:
if (::IsWindowUnicode(hwnd))
{
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
if (wParam > 0 && wParam < 0x10000)
io.AddInputCharacterUTF16((unsigned short)wParam);
}
else
{
wchar_t wch = 0;
::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 2, &wch, 1);
IMGUI_DEBUG_LOG("WM_CHAR %08X -> wch %08X\n", wParam, wch);
io.AddInputCharacter(wch);
}
return 0;And simply add handler: case WM_IME_COMPOSITION:
{
// Handling WM_IME_COMPOSITION ensure that WM_IME_CHAR value is correct even for MBCS apps.
// (see #9099, #3653 and https://stackoverflow.com/questions/77450354 topics)
IMGUI_DEBUG_LOG("WM_IME_COMPOSITION wParam %08X, lParam %08X\n", wParam, lParam);
LRESULT result = ::DefWindowProcW(hwnd, msg, wParam, lParam);
return (lParam & GCS_RESULTSTR) ? 1 : result;
}No need to add WM_IME_CHAR handler. Can you check if it works for you in all situations? (Unfortunately there's no way to reach LR55 the author of that StackOverflow post, who might know more, since zealous SO admins are flagging answers attempts as "delete" because they want a new question, which ultimately will not reach that person) |
|
The result of typing the character '本' on my local computer:
[01298] WM_IME_COMPOSITION wParam 00000062, lParam 000001B8
[01178] WM_IME_COMPOSITION wParam 00000062, lParam 000001B8
[01095] WM_IME_COMPOSITION wParam 00000062, lParam 000001B8
[01395] WM_IME_COMPOSITION wParam 00000062, lParam 000001B8 On my computer, it seems that only WM_IME_CHAR can receive a complete double-byte character (such as '本' == {BE, B1}, which is a complete double-byte character), while WM_CHAR will split them and then send them, sending B1 first and then BE, causing MultiByteToWideChar to fail to convert them into visible characters correctly. |
|
Or you can try the following code to see if it works (it works on my computer): (1) I modified example (1) (2) |
|
For my (2) and (3) above did you keep the |
|
Actually your (1) is the same as my (3) once you add
The reason I prefer to avoid using |
No, I didn't. If WM_IME_CHAR is added, the output is: (3) |
Yes, they work properly after being added. |
Add handling for WM_IME_COMPOSITION to ensure correct WM_IME_CHAR values in MBCS apps.
|
I want to replace the following in imgui_impl_win32.cpp: RegisterClassExW with RegisterClassEx; CreateWindowExW with CreateWindowEx; DefWindowProcW with DefWindowProc; Are there any potential problems? I haven't found any issues in my tests so far. |
That seem to undo your fist commit now merged as 962cc23 |
|
I have now merged:
I'm open to this in theory but we need carefully testing and it undo your first commit. |
|
Thanks for your help with this! I'm closing now but happy to discuss the last RegisterClassExW -> RegisterClassEx stuff if needed. |
If WM_IME_CHAR is processed and the W suffix is removed from RegisterClassExW and CreateWindowExW, just leave it as is. Removing the W suffix, as you said, is meaningless. Thanks a lot. |
…tting non-ASCII values to io.AddInputCharacter(). (ocornut#9099)
…nt (should be no-op?). Amend 0a7054c (ocornut#5725, ocornut#1807, ocornut#471, ocornut#2815, ocornut#1060 + ocornut#9099, ocornut#3653, ocornut#5961)
…pport Unicode inputs on MBCS Windows. (ocornut#9099, ocornut#3653, ocornut#5961)




The RegisterClassExW referenced at ImGui_ImplWin32_InitMultiViewportSupport is Unicode, while the DefWindowProc referenced at ImGui_ImplWin32_WndProcHandler_PlatformWindow will cause encoding errors when inputting double-byte characters if the character set is multi-byte.
DefWindowProcA:

DefWindowProcW:
