Blender  V3.3
GHOST_ImeWin32.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * The Original Code is Copyright 2010 The Chromium Authors. All rights reserved. */
3 
8 #ifdef WITH_INPUT_IME
9 
10 # include "GHOST_ImeWin32.h"
11 # include "GHOST_C-api.h"
12 # include "GHOST_WindowWin32.h"
13 # include "utfconv.h"
14 
15 /* ISO_639-1 2-Letter Abbreviations. */
16 # define IMELANG_ENGLISH "en"
17 # define IMELANG_CHINESE "zh"
18 # define IMELANG_JAPANESE "ja"
19 # define IMELANG_KOREAN "ko"
20 
21 GHOST_ImeWin32::GHOST_ImeWin32()
22  : is_composing_(false),
23  language_(IMELANG_ENGLISH),
24  conversion_modes_(IME_CMODE_ALPHANUMERIC),
25  sentence_mode_(IME_SMODE_NONE),
26  system_caret_(false),
27  caret_rect_(-1, -1, 0, 0),
28  is_first(true),
29  is_enable(true)
30 {
31 }
32 
33 GHOST_ImeWin32::~GHOST_ImeWin32()
34 {
35 }
36 
37 void GHOST_ImeWin32::UpdateInputLanguage()
38 {
39  /* Get the current input locale full name. */
40  WCHAR locale[LOCALE_NAME_MAX_LENGTH];
41  LCIDToLocaleName(
42  MAKELCID(LOWORD(::GetKeyboardLayout(0)), SORT_DEFAULT), locale, LOCALE_NAME_MAX_LENGTH, 0);
43  /* Get the 2-letter ISO-63901 abbreviation of the input locale name. */
44  WCHAR language_u16[W32_ISO639_LEN];
45  GetLocaleInfoEx(locale, LOCALE_SISO639LANGNAME, language_u16, W32_ISO639_LEN);
46  /* Store this as a UTF-8 string. */
47  WideCharToMultiByte(
48  CP_UTF8, 0, language_u16, W32_ISO639_LEN, language_, W32_ISO639_LEN, NULL, NULL);
49 }
50 
51 BOOL GHOST_ImeWin32::IsLanguage(const char name[W32_ISO639_LEN])
52 {
53  return (strcmp(name, language_) == 0);
54 }
55 
56 void GHOST_ImeWin32::UpdateConversionStatus(HWND window_handle)
57 {
58  HIMC imm_context = ::ImmGetContext(window_handle);
59  if (imm_context) {
60  if (::ImmGetOpenStatus(imm_context)) {
61  ::ImmGetConversionStatus(imm_context, &conversion_modes_, &sentence_mode_);
62  }
63  else {
64  conversion_modes_ = IME_CMODE_ALPHANUMERIC;
65  sentence_mode_ = IME_SMODE_NONE;
66  }
67  ::ImmReleaseContext(window_handle, imm_context);
68  }
69  else {
70  conversion_modes_ = IME_CMODE_ALPHANUMERIC;
71  sentence_mode_ = IME_SMODE_NONE;
72  }
73 }
74 
75 bool GHOST_ImeWin32::IsEnglishMode()
76 {
77  return (conversion_modes_ & IME_CMODE_NOCONVERSION) ||
78  !(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE));
79 }
80 
81 bool GHOST_ImeWin32::IsImeKeyEvent(char ascii, GHOST_TKey key)
82 {
83  if (!(IsEnglishMode())) {
84  /* In Chinese, Japanese, Korean, all alpha keys are processed by IME. */
85  if ((ascii >= 'A' && ascii <= 'Z') || (ascii >= 'a' && ascii <= 'z')) {
86  return true;
87  }
88  if (IsLanguage(IMELANG_JAPANESE) && (ascii >= ' ' && ascii <= '~')) {
89  return true;
90  }
91  if (IsLanguage(IMELANG_CHINESE)) {
92  if (ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii) && !(key == GHOST_kKeyNumpadPeriod)) {
93  return true;
94  }
95  if (conversion_modes_ & IME_CMODE_FULLSHAPE && (ascii >= '0' && ascii <= '9')) {
96  /* When in Full Width mode the number keys are also converted. */
97  return true;
98  }
99  }
100  }
101  return false;
102 }
103 
104 void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
105 {
118  if (!system_caret_ && (IsLanguage(IMELANG_CHINESE) || IsLanguage(IMELANG_JAPANESE))) {
119  system_caret_ = ::CreateCaret(window_handle, NULL, 1, 1);
120  }
121  /* Restore the positions of the IME windows. */
122  UpdateImeWindow(window_handle);
123 }
124 
125 void GHOST_ImeWin32::SetImeWindowStyle(
126  HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled)
127 {
138  *handled = TRUE;
139  lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
140  ::DefWindowProc(window_handle, message, wparam, lparam);
141 }
142 
143 void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle)
144 {
145  /* Destroy the system caret if we have created for this IME input context. */
146  if (system_caret_) {
147  ::DestroyCaret();
148  system_caret_ = false;
149  }
150 }
151 
152 void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context)
153 {
154  int x = caret_rect_.m_l;
155  int y = caret_rect_.m_t;
156  const int kCaretMargin = 1;
169  CANDIDATEFORM candidate_position = {0, CFS_CANDIDATEPOS, {x, y}, {0, 0, 0, 0}};
170  ::ImmSetCandidateWindow(imm_context, &candidate_position);
171  if (system_caret_) {
172  ::SetCaretPos(x, y);
173  }
174  if (IsLanguage(IMELANG_KOREAN)) {
181  y += kCaretMargin;
182  }
189  CANDIDATEFORM exclude_rectangle = {
190  0, CFS_EXCLUDE, {x, y}, {x, y, x + caret_rect_.getWidth(), y + caret_rect_.getHeight()}};
191  ::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
192 }
193 
194 void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle)
195 {
196  /* Just move the IME window attached to the given window. */
197  if (caret_rect_.m_l >= 0 && caret_rect_.m_t >= 0) {
198  HIMC imm_context = ::ImmGetContext(window_handle);
199  if (imm_context) {
200  MoveImeWindow(window_handle, imm_context);
201  ::ImmReleaseContext(window_handle, imm_context);
202  }
203  }
204 }
205 
206 void GHOST_ImeWin32::CleanupComposition(HWND window_handle)
207 {
213  if (is_composing_) {
214  HIMC imm_context = ::ImmGetContext(window_handle);
215  if (imm_context) {
216  ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
217  ::ImmReleaseContext(window_handle, imm_context);
218  }
219  ResetComposition(window_handle);
220  }
221 }
222 
223 void GHOST_ImeWin32::CheckFirst(HWND window_handle)
224 {
225  if (is_first) {
226  this->EndIME(window_handle);
227  is_first = false;
228  }
229 }
230 
231 void GHOST_ImeWin32::ResetComposition(HWND window_handle)
232 {
233  /* Currently, just reset the composition status. */
234  is_composing_ = false;
235 }
236 
237 void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context)
238 {
244  if (is_composing_) {
245  ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
246  ResetComposition(window_handle);
247  }
248 }
249 
250 void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition)
251 {
262  int target_start = -1;
263  int target_end = -1;
264  if (IsLanguage(IMELANG_KOREAN)) {
265  if (lparam & CS_NOMOVECARET) {
266  target_start = 0;
267  target_end = 1;
268  }
269  }
270  else if (IsLanguage(IMELANG_CHINESE)) {
271  int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0);
272  if (clause_size) {
273  static std::vector<unsigned long> clauses;
274  clause_size = clause_size / sizeof(clauses[0]);
275  clauses.resize(clause_size);
276  ImmGetCompositionStringW(
277  imm_context, GCS_COMPCLAUSE, &clauses[0], sizeof(clauses[0]) * clause_size);
278  if (composition->cursor_position == composition->ime_string.size()) {
279  target_start = clauses[clause_size - 2];
280  target_end = clauses[clause_size - 1];
281  }
282  else {
283  for (int i = 0; i < clause_size - 1; i++) {
284  if (clauses[i] == composition->cursor_position) {
285  target_start = clauses[i];
286  target_end = clauses[i + 1];
287  break;
288  }
289  }
290  }
291  }
292  else {
293  if (composition->cursor_position != -1) {
294  target_start = composition->cursor_position;
295  target_end = composition->ime_string.size();
296  }
297  }
298  }
299  else if (IsLanguage(IMELANG_JAPANESE)) {
306  if (lparam & GCS_COMPATTR) {
307  int attribute_size = ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, NULL, 0);
308  if (attribute_size > 0) {
309  char *attribute_data = new char[attribute_size];
310  if (attribute_data) {
311  ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, attribute_data, attribute_size);
312  for (target_start = 0; target_start < attribute_size; ++target_start) {
313  if (IsTargetAttribute(attribute_data[target_start]))
314  break;
315  }
316  for (target_end = target_start; target_end < attribute_size; ++target_end) {
317  if (!IsTargetAttribute(attribute_data[target_end]))
318  break;
319  }
320  if (target_start == attribute_size) {
326  target_end = target_start;
327  target_start = 0;
328  }
329  if (target_start != -1 && target_start < attribute_size &&
330  attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED) {
331  composition->cursor_position = target_start;
332  }
333  }
334  delete[] attribute_data;
335  }
336  }
337  }
338  composition->target_start = target_start;
339  composition->target_end = target_end;
340 }
341 
342 bool GHOST_ImeWin32::GetString(HIMC imm_context,
343  WPARAM lparam,
344  int type,
345  ImeComposition *composition)
346 {
347  bool result = false;
348  if (lparam & type) {
349  int string_size = ::ImmGetCompositionStringW(imm_context, type, NULL, 0);
350  if (string_size > 0) {
351  int string_length = string_size / sizeof(wchar_t);
352  wchar_t *string_data = new wchar_t[string_length + 1];
353  string_data[string_length] = '\0';
354  if (string_data) {
355  /* Fill the given ImeComposition object. */
356  ::ImmGetCompositionStringW(imm_context, type, string_data, string_size);
357  composition->string_type = type;
358  composition->ime_string = string_data;
359  result = true;
360  }
361  delete[] string_data;
362  }
363  }
364  return result;
365 }
366 
367 bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition)
368 {
369  bool result = false;
370  HIMC imm_context = ::ImmGetContext(window_handle);
371  if (imm_context) {
372  /* Copy the result string to the ImeComposition object. */
373  result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
378  composition->cursor_position = -1;
379  composition->target_start = -1;
380  composition->target_end = -1;
381  ::ImmReleaseContext(window_handle, imm_context);
382  }
383  return result;
384 }
385 
386 bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition)
387 {
388  bool result = false;
389  HIMC imm_context = ::ImmGetContext(window_handle);
390  if (imm_context) {
391  /* Copy the composition string to the ImeComposition object. */
392  result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
393 
394  /* Retrieve the cursor position in the IME composition. */
395  int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS, NULL, 0);
396  composition->cursor_position = cursor_position;
397  composition->target_start = -1;
398  composition->target_end = -1;
399 
400  /* Retrieve the target selection and Update the ImeComposition object. */
401  GetCaret(imm_context, lparam, composition);
402 
403  /* Mark that there is an ongoing composition. */
404  is_composing_ = true;
405 
406  ::ImmReleaseContext(window_handle, imm_context);
407  }
408  return result;
409 }
410 
411 void GHOST_ImeWin32::EndIME(HWND window_handle)
412 {
420  if (!is_enable)
421  return;
422  is_enable = false;
423  CleanupComposition(window_handle);
424  ::ImmAssociateContextEx(window_handle, NULL, 0);
425  eventImeData.composite_len = 0;
426 }
427 
428 void GHOST_ImeWin32::BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete)
429 {
430  if (is_enable && complete)
431  return;
432  is_enable = true;
439  ::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT);
440  /* Complete the ongoing composition and move the IME windows. */
441  HIMC imm_context = ::ImmGetContext(window_handle);
442  if (imm_context) {
443  if (complete) {
452  CompleteComposition(window_handle, imm_context);
453  }
459  if (caret_rect.m_l >= 0 && caret_rect.m_t >= 0) {
460  caret_rect_ = caret_rect;
461  MoveImeWindow(window_handle, imm_context);
462  }
463  ::ImmReleaseContext(window_handle, imm_context);
464  }
465 }
466 
467 static void convert_utf16_to_utf8_len(std::wstring s, int &len)
468 {
469  if (len >= 0 && len <= s.size())
470  len = count_utf_8_from_16(s.substr(0, len).c_str()) - 1;
471  else
472  len = -1;
473 }
474 
475 static size_t updateUtf8Buf(ImeComposition &info)
476 {
477  size_t len = count_utf_8_from_16(info.ime_string.c_str());
478  info.utf8_buf.resize(len);
479  conv_utf_16_to_8(info.ime_string.c_str(), &info.utf8_buf[0], len);
480  convert_utf16_to_utf8_len(info.ime_string, info.cursor_position);
481  convert_utf16_to_utf8_len(info.ime_string, info.target_start);
482  convert_utf16_to_utf8_len(info.ime_string, info.target_end);
483  return len - 1;
484 }
485 
486 void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
487 {
488  int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo);
489  int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo);
490  /* convert wchar to utf8 */
491  if (res) {
492  eventImeData.result_len = (GHOST_TUserDataPtr)updateUtf8Buf(resultInfo);
493  eventImeData.result = &resultInfo.utf8_buf[0];
494  }
495  else {
496  eventImeData.result = 0;
497  eventImeData.result_len = 0;
498  }
499  if (comp) {
500  eventImeData.composite_len = (GHOST_TUserDataPtr)updateUtf8Buf(compInfo);
501  eventImeData.composite = &compInfo.utf8_buf[0];
502  eventImeData.cursor_position = compInfo.cursor_position;
503  eventImeData.target_start = compInfo.target_start;
504  eventImeData.target_end = compInfo.target_end;
505  }
506  else {
507  eventImeData.composite = 0;
508  eventImeData.composite_len = 0;
509  eventImeData.cursor_position = -1;
510  eventImeData.target_start = -1;
511  eventImeData.target_end = -1;
512  }
513 }
514 
515 #endif // WITH_INPUT_IME
GHOST C-API function and type declarations.
void * GHOST_TUserDataPtr
Definition: GHOST_Types.h:72
GHOST_TKey
Definition: GHOST_Types.h:259
@ GHOST_kKeyNumpadPeriod
Definition: GHOST_Types.h:364
typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
int32_t m_l
Definition: GHOST_Rect.h:156
int32_t m_t
Definition: GHOST_Rect.h:158
int len
Definition: draw_manager.c:108
size_t count_utf_8_from_16(const wchar_t *string16)
Definition: utfconv.c:10
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:115