IME Win32: Fix Duplicated Initial Character

When entering characters using IME on Windows, Japanese and Chinese
will both usually result in the first keystroke being duplicated. The
problem is that we are informed too late, after the first key is
pressed, that we are IME composing. This patch ensures we are entering
non-English characters using ImmGetConversionStatus() and then deals
with editing keys (like arrows and backspace) on a per-language basis.

see D11929 for more details.

Differential Revision: https://developer.blender.org/D11929

Reviewed by Brecht Van Lommel
This commit is contained in:
Takahiro Shizuki 2021-08-01 11:52:22 -07:00 committed by Harley Acheson
parent 79277986c2
commit 836aeebf70
Notes: blender-bot 2023-02-14 10:32:59 +01:00
Referenced by issue #90374, Blender doesn't start, probably after 'GHOST/X11: enable EGL' commit
Referenced by issue #89831, When IME is On, passing key inputs excluding caret operation to IME. (Windows)
3 changed files with 91 additions and 2 deletions

View File

@ -34,6 +34,8 @@ GHOST_ImeWin32::GHOST_ImeWin32()
: is_composing_(false),
ime_status_(false),
input_language_id_(LANG_USER_DEFAULT),
conversion_modes_(IME_CMODE_ALPHANUMERIC),
sentence_mode_(IME_SMODE_NONE),
system_caret_(false),
caret_rect_(-1, -1, 0, 0),
is_first(true),
@ -59,6 +61,63 @@ bool GHOST_ImeWin32::SetInputLanguage()
return ime_status_;
}
WORD GHOST_ImeWin32::GetInputLanguage()
{
return input_language_id_;
}
void GHOST_ImeWin32::UpdateConversionStatus(HWND window_handle)
{
HIMC imm_context = ::ImmGetContext(window_handle);
if (imm_context) {
if (::ImmGetOpenStatus(imm_context)) {
::ImmGetConversionStatus(imm_context, &conversion_modes_, &sentence_mode_);
}
else {
conversion_modes_ = IME_CMODE_ALPHANUMERIC;
sentence_mode_ = IME_SMODE_NONE;
}
::ImmReleaseContext(window_handle, imm_context);
}
else {
conversion_modes_ = IME_CMODE_ALPHANUMERIC;
sentence_mode_ = IME_SMODE_NONE;
}
}
bool GHOST_ImeWin32::IsEnglishMode()
{
return (conversion_modes_ & IME_CMODE_NOCONVERSION) ||
!(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE));
}
bool GHOST_ImeWin32::IsImeKeyEvent(char ascii)
{
if (!(IsEnglishMode())) {
/* In Chinese, Japanese, Korena, all alpha keys are processed by IME. */
if ((ascii >= 'A' && ascii <= 'Z') || (ascii >= 'a' && ascii <= 'z')) {
return true;
}
switch (PRIMARYLANGID(GetInputLanguage())) {
/* In Japanese, all symbolic characters are also processed by IME. */
case LANG_JAPANESE: {
if (ascii >= ' ' && ascii <= '~') {
return true;
}
break;
}
/* In Chinese, some symbolic characters are also processed by IME. */
case LANG_CHINESE: {
if (ascii && strchr("!\"$'(),.:;<>?[\\]^_`", ascii)) {
return true;
}
break;
}
}
}
return false;
}
void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
{
/**

View File

@ -156,6 +156,18 @@ class GHOST_ImeWin32 {
*/
bool SetInputLanguage();
/* Returns the current input language id. */
WORD GetInputLanguage();
/* Saves the current conversion status. */
void UpdateConversionStatus(HWND window_handle);
/* Is the IME currently in conversion mode? */
bool IsEnglishMode();
/* Checks a key whether IME has to do handling. */
bool IsImeKeyEvent(char ascii);
/**
* Create the IME windows, and allocate required resources for them.
* Parameters
@ -371,6 +383,12 @@ class GHOST_ImeWin32 {
*/
LANGID input_language_id_;
/* Current Conversion Mode Values. Retrieved with ImmGetConversionStatus. */
DWORD conversion_modes_;
/* Current Sentence Mode. Retrieved with ImmGetConversionStatus. */
DWORD sentence_mode_;
/**
* Represents whether or not the current input context has created a system
* caret to set the position of its IME candidate window.

View File

@ -1219,6 +1219,12 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0];
}
#ifdef WITH_INPUT_IME
if (window->getImeInput()->IsImeKeyEvent(ascii)) {
return NULL;
}
#endif /* WITH_INPUT_IME */
event = new GHOST_EventKey(system->getMilliSeconds(),
keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
window,
@ -1419,6 +1425,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
system->handleKeyboardChange();
#ifdef WITH_INPUT_IME
window->getImeInput()->SetInputLanguage();
window->getImeInput()->UpdateConversionStatus(hwnd);
#endif
break;
}
@ -1455,6 +1462,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
////////////////////////////////////////////////////////////////////////
// IME events, processed, read more in GHOST_IME.h
////////////////////////////////////////////////////////////////////////
case WM_IME_NOTIFY: {
/* Update conversion status when IME is changed or input mode is changed. */
if (wParam == IMN_SETOPENSTATUS || wParam == IMN_SETCONVERSIONMODE) {
window->getImeInput()->UpdateConversionStatus(hwnd);
}
break;
}
case WM_IME_SETCONTEXT: {
GHOST_ImeWin32 *ime = window->getImeInput();
ime->SetInputLanguage();
@ -1466,8 +1480,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WM_IME_STARTCOMPOSITION: {
GHOST_ImeWin32 *ime = window->getImeInput();
eventHandled = true;
/* remove input event before start comp event, avoid redundant input */
eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
ime->CreateImeWindow(hwnd);
ime->ResetComposition(hwnd);
event = processImeEvent(GHOST_kEventImeCompositionStart, window, &ime->eventImeData);