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:
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)
|
@ -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)
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue