2 * The IME for interfacing with XIM
4 * Copyright 2008 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * The normal flow for IMM/IME Processing is as follows.
24 * 1) The Keyboard Driver generates key messages which are first passed to
25 * the IMM and then to IME via ImeProcessKey. If the IME returns 0 then
26 * it does not want the key and the keyboard driver then generates the
27 * WM_KEYUP/WM_KEYDOWN messages. However if the IME is going to process the
28 * key it returns non-zero.
29 * 2) If the IME is going to process the key then the IMM calls ImeToAsciiEx to
30 * process the key. the IME modifies the HIMC structure to reflect the
31 * current state and generates any messages it needs the IMM to process.
32 * 3) IMM checks the messages and send them to the application in question. From
33 * here the IMM level deals with if the application is IME aware or not.
35 * This flow does not work well for the X11 driver and XIM.
36 * (It works fine for Mac)
37 * As such we will have to reroute step 1. Instead the x11drv driver will
38 * generate an XIM events and call directly into this IME implimenetaion.
39 * As such we will have to use the alternative ImmGenerateMessage path to be
40 * generate the messages that we want the IMM layer to send to the application.
51 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(imm);
59 #define FROM_X11 ((HIMC)0xcafe1337)
61 typedef struct _IMEPRIVATE {
66 } IMEPRIVATE, *LPIMEPRIVATE;
68 typedef struct _tagTRANSMSG {
72 } TRANSMSG, *LPTRANSMSG;
74 static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e','X','1','1','I','M','E',0};
76 static HIMC *hSelectedFrom = NULL;
77 static INT hSelectedCount = 0;
80 static UINT WM_MSIME_SERVICE;
81 static UINT WM_MSIME_RECONVERTOPTIONS;
82 static UINT WM_MSIME_MOUSE;
83 static UINT WM_MSIME_RECONVERTREQUEST;
84 static UINT WM_MSIME_RECONVERT;
85 static UINT WM_MSIME_QUERYPOSITION;
86 static UINT WM_MSIME_DOCUMENTFEED;
88 static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
90 static void UpdateDataInDefaultIMEWindow(HIMC hHIMC, HWND hwnd, BOOL showable);
92 static HIMC RealIMC(HIMC hIMC)
97 HWND wnd = GetFocus();
98 HIMC winHimc = ImmGetContext(wnd);
99 for (i = 0; i < hSelectedCount; i++)
100 if (winHimc == hSelectedFrom[i])
108 static LPINPUTCONTEXT LockRealIMC(HIMC hIMC)
110 HIMC real_imc = RealIMC(hIMC);
112 return ImmLockIMC(real_imc);
117 static BOOL UnlockRealIMC(HIMC hIMC)
119 HIMC real_imc = RealIMC(hIMC);
121 return ImmUnlockIMC(real_imc);
126 static void IME_RegisterClasses(void)
134 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
135 wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW;
136 wndClass.lpfnWndProc = IME_WindowProc;
137 wndClass.cbClsExtra = 0;
138 wndClass.cbWndExtra = 2 * sizeof(LONG_PTR);
139 wndClass.hInstance = x11drv_module;
140 wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
141 wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION);
142 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW +1);
143 wndClass.lpszMenuName = 0;
144 wndClass.lpszClassName = UI_CLASS_NAME;
146 RegisterClassW(&wndClass);
148 WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
149 WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
150 WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
151 WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
152 WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
153 WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
154 WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
157 void IME_UnregisterClasses(void)
159 UnregisterClassW(UI_CLASS_NAME, x11drv_module);
162 static HIMCC ImeCreateBlankCompStr(void)
165 LPCOMPOSITIONSTRING ptr;
166 rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING));
167 ptr = ImmLockIMCC(rc);
168 memset(ptr,0,sizeof(COMPOSITIONSTRING));
169 ptr->dwSize = sizeof(COMPOSITIONSTRING);
174 static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset,
175 LPBYTE target, LPBYTE source, DWORD* lenParam,
176 DWORD* offsetParam, BOOL wchars )
178 if (origLen > 0 && origOffset > 0)
180 int truelen = origLen;
182 truelen *= sizeof(WCHAR);
184 memcpy(&target[currentOffset], &source[origOffset], truelen);
187 *offsetParam = currentOffset;
188 currentOffset += truelen;
190 return currentOffset;
193 static HIMCC updateCompStr(HIMCC old, LPWSTR compstr, DWORD len)
195 /* we need to make sure the CompStr, CompClaus and CompAttr fields are all
199 LPBYTE newdata = NULL;
200 LPBYTE olddata = NULL;
201 LPCOMPOSITIONSTRING new_one;
202 LPCOMPOSITIONSTRING lpcs = NULL;
203 INT current_offset = 0;
205 TRACE("%s, %i\n",debugstr_wn(compstr,len),len);
207 if (old == NULL && compstr == NULL && len == 0)
210 if (compstr == NULL && len != 0)
212 ERR("compstr is NULL however we have a len! Please report\n");
218 olddata = ImmLockIMCC(old);
219 lpcs = (LPCOMPOSITIONSTRING)olddata;
222 needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) +
223 len + sizeof(DWORD) * 2;
227 needed_size += lpcs->dwCompReadAttrLen;
228 needed_size += lpcs->dwCompReadClauseLen;
229 needed_size += lpcs->dwCompReadStrLen * sizeof(DWORD);
230 needed_size += lpcs->dwResultReadClauseLen;
231 needed_size += lpcs->dwResultReadStrLen * sizeof(DWORD);
232 needed_size += lpcs->dwResultClauseLen;
233 needed_size += lpcs->dwResultStrLen * sizeof(DWORD);
234 needed_size += lpcs->dwPrivateSize;
236 rc = ImmCreateIMCC(needed_size);
237 newdata = ImmLockIMCC(rc);
238 new_one = (LPCOMPOSITIONSTRING)newdata;
240 new_one->dwSize = needed_size;
241 current_offset = sizeof(COMPOSITIONSTRING);
244 current_offset = updateField(lpcs->dwCompReadAttrLen,
245 lpcs->dwCompReadAttrOffset,
246 current_offset, newdata, olddata,
247 &new_one->dwCompReadAttrLen,
248 &new_one->dwCompReadAttrOffset, FALSE);
250 current_offset = updateField(lpcs->dwCompReadClauseLen,
251 lpcs->dwCompReadClauseOffset,
252 current_offset, newdata, olddata,
253 &new_one->dwCompReadClauseLen,
254 &new_one->dwCompReadClauseOffset, FALSE);
256 current_offset = updateField(lpcs->dwCompReadStrLen,
257 lpcs->dwCompReadStrOffset,
258 current_offset, newdata, olddata,
259 &new_one->dwCompReadStrLen,
260 &new_one->dwCompReadStrOffset, TRUE);
262 /* new CompAttr, CompClause, CompStr, dwCursorPos */
263 new_one->dwDeltaStart = 0;
265 current_offset = updateField(lpcs->dwResultReadClauseLen,
266 lpcs->dwResultReadClauseOffset,
267 current_offset, newdata, olddata,
268 &new_one->dwResultReadClauseLen,
269 &new_one->dwResultReadClauseOffset, FALSE);
271 current_offset = updateField(lpcs->dwResultReadStrLen,
272 lpcs->dwResultReadStrOffset,
273 current_offset, newdata, olddata,
274 &new_one->dwResultReadStrLen,
275 &new_one->dwResultReadStrOffset, TRUE);
277 current_offset = updateField(lpcs->dwResultClauseLen,
278 lpcs->dwResultClauseOffset,
279 current_offset, newdata, olddata,
280 &new_one->dwResultClauseLen,
281 &new_one->dwResultClauseOffset, FALSE);
283 current_offset = updateField(lpcs->dwResultStrLen,
284 lpcs->dwResultStrOffset,
285 current_offset, newdata, olddata,
286 &new_one->dwResultStrLen,
287 &new_one->dwResultStrOffset, TRUE);
289 current_offset = updateField(lpcs->dwPrivateSize,
290 lpcs->dwPrivateOffset,
291 current_offset, newdata, olddata,
292 &new_one->dwPrivateSize,
293 &new_one->dwPrivateOffset, FALSE);
298 new_one->dwCompAttrLen = len;
301 new_one->dwCompAttrOffset = current_offset;
302 memset(&newdata[current_offset],ATTR_INPUT,len);
303 current_offset += len;
309 new_one->dwCompClauseLen = sizeof(DWORD) * 2;
310 new_one->dwCompClauseOffset = current_offset;
311 *(DWORD*)(&newdata[current_offset]) = 0;
312 current_offset += sizeof(DWORD);
313 *(DWORD*)(&newdata[current_offset]) = len;
314 current_offset += sizeof(DWORD);
318 new_one->dwCompStrLen = len;
321 new_one->dwCompStrOffset = current_offset;
322 memcpy(&newdata[current_offset],compstr,len*sizeof(WCHAR));
326 new_one->dwCursorPos = len;
335 static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len)
337 /* we need to make sure the ResultStr and ResultClause fields are all
341 LPBYTE newdata = NULL;
342 LPBYTE olddata = NULL;
343 LPCOMPOSITIONSTRING new_one;
344 LPCOMPOSITIONSTRING lpcs = NULL;
345 INT current_offset = 0;
347 TRACE("%s, %i\n",debugstr_wn(resultstr,len),len);
349 if (old == NULL && resultstr == NULL && len == 0)
352 if (resultstr == NULL && len != 0)
354 ERR("resultstr is NULL however we have a len! Please report\n");
360 olddata = ImmLockIMCC(old);
361 lpcs = (LPCOMPOSITIONSTRING)olddata;
364 needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) +
369 needed_size += lpcs->dwCompReadAttrLen;
370 needed_size += lpcs->dwCompReadClauseLen;
371 needed_size += lpcs->dwCompReadStrLen * sizeof(DWORD);
372 needed_size += lpcs->dwCompAttrLen;
373 needed_size += lpcs->dwCompClauseLen;
374 needed_size += lpcs->dwCompStrLen * sizeof(DWORD);
375 needed_size += lpcs->dwResultReadClauseLen;
376 needed_size += lpcs->dwResultReadStrLen * sizeof(DWORD);
377 needed_size += lpcs->dwPrivateSize;
379 rc = ImmCreateIMCC(needed_size);
380 newdata = ImmLockIMCC(rc);
381 new_one = (LPCOMPOSITIONSTRING)newdata;
383 new_one->dwSize = needed_size;
384 current_offset = sizeof(COMPOSITIONSTRING);
387 current_offset = updateField(lpcs->dwCompReadAttrLen,
388 lpcs->dwCompReadAttrOffset,
389 current_offset, newdata, olddata,
390 &new_one->dwCompReadAttrLen,
391 &new_one->dwCompReadAttrOffset, FALSE);
393 current_offset = updateField(lpcs->dwCompReadClauseLen,
394 lpcs->dwCompReadClauseOffset,
395 current_offset, newdata, olddata,
396 &new_one->dwCompReadClauseLen,
397 &new_one->dwCompReadClauseOffset, FALSE);
399 current_offset = updateField(lpcs->dwCompReadStrLen,
400 lpcs->dwCompReadStrOffset,
401 current_offset, newdata, olddata,
402 &new_one->dwCompReadStrLen,
403 &new_one->dwCompReadStrOffset, TRUE);
405 current_offset = updateField(lpcs->dwCompAttrLen,
406 lpcs->dwCompAttrOffset,
407 current_offset, newdata, olddata,
408 &new_one->dwCompAttrLen,
409 &new_one->dwCompAttrOffset, FALSE);
411 current_offset = updateField(lpcs->dwCompClauseLen,
412 lpcs->dwCompClauseOffset,
413 current_offset, newdata, olddata,
414 &new_one->dwCompClauseLen,
415 &new_one->dwCompClauseOffset, FALSE);
417 current_offset = updateField(lpcs->dwCompStrLen,
418 lpcs->dwCompStrOffset,
419 current_offset, newdata, olddata,
420 &new_one->dwCompStrLen,
421 &new_one->dwCompStrOffset, TRUE);
423 new_one->dwCursorPos = lpcs->dwCursorPos;
424 new_one->dwDeltaStart = 0;
426 current_offset = updateField(lpcs->dwResultReadClauseLen,
427 lpcs->dwResultReadClauseOffset,
428 current_offset, newdata, olddata,
429 &new_one->dwResultReadClauseLen,
430 &new_one->dwResultReadClauseOffset, FALSE);
432 current_offset = updateField(lpcs->dwResultReadStrLen,
433 lpcs->dwResultReadStrOffset,
434 current_offset, newdata, olddata,
435 &new_one->dwResultReadStrLen,
436 &new_one->dwResultReadStrOffset, TRUE);
438 /* new ResultClause , ResultStr */
440 current_offset = updateField(lpcs->dwPrivateSize,
441 lpcs->dwPrivateOffset,
442 current_offset, newdata, olddata,
443 &new_one->dwPrivateSize,
444 &new_one->dwPrivateOffset, FALSE);
451 new_one->dwResultClauseLen = sizeof(DWORD) * 2;
452 new_one->dwResultClauseOffset = current_offset;
453 *(DWORD*)(&newdata[current_offset]) = 0;
454 current_offset += sizeof(DWORD);
455 *(DWORD*)(&newdata[current_offset]) = len;
456 current_offset += sizeof(DWORD);
460 new_one->dwResultStrLen = len;
463 new_one->dwResultStrOffset = current_offset;
464 memcpy(&newdata[current_offset],resultstr,len*sizeof(WCHAR));
473 static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam,
476 LPINPUTCONTEXT lpIMC;
477 LPTRANSMSG lpTransMsg;
479 lpIMC = LockRealIMC(hIMC);
483 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) *
488 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
492 lpTransMsg += lpIMC->dwNumMsgBuf;
493 lpTransMsg->message = msg;
494 lpTransMsg->wParam = wParam;
495 lpTransMsg->lParam = lParam;
497 ImmUnlockIMCC(lpIMC->hMsgBuf);
498 lpIMC->dwNumMsgBuf++;
500 ImmGenerateMessage(RealIMC(hIMC));
504 static void GenerateIMECHARMessages(HIMC hIMC, LPWSTR String, DWORD length)
506 LPINPUTCONTEXT lpIMC;
507 LPTRANSMSG lpTransMsg;
513 lpIMC = LockRealIMC(hIMC);
517 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf,
518 (lpIMC->dwNumMsgBuf + length) *
523 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
527 lpTransMsg += lpIMC->dwNumMsgBuf;
528 for (i = 0; i < length; i++)
530 lpTransMsg->message = WM_IME_CHAR;
531 lpTransMsg->wParam = String[i];
532 lpTransMsg->lParam = 1;
536 ImmUnlockIMCC(lpIMC->hMsgBuf);
537 lpIMC->dwNumMsgBuf+=length;
539 ImmGenerateMessage(RealIMC(hIMC));
543 static BOOL IME_RemoveFromSelected(HIMC hIMC)
546 for (i = 0; i < hSelectedCount; i++)
547 if (hSelectedFrom[i] == hIMC)
549 if (i < hSelectedCount - 1)
550 memmove(&hSelectedFrom[i], &hSelectedFrom[i+1], (hSelectedCount - i - 1)*sizeof(HIMC));
557 static void IME_AddToSelected(HIMC hIMC)
561 hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount*sizeof(HIMC));
563 hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC));
564 hSelectedFrom[hSelectedCount-1] = hIMC;
567 BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass,
571 IME_RegisterClasses();
572 lpIMEInfo->dwPrivateDataSize = sizeof (IMEPRIVATE);
573 lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
574 lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE;
575 lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC;
576 lpIMEInfo->fdwUICaps = UI_CAP_2700;
577 /* Tell App we cannot accept ImeSetCompositionString calls */
578 lpIMEInfo->fdwSCSCaps = 0;
579 lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION;
581 lstrcpyW(lpszUIClass,UI_CLASS_NAME);
586 BOOL WINAPI ImeConfigure(HKL hKL,HWND hWnd, DWORD dwMode, LPVOID lpData)
588 FIXME("(%p, %p, %d, %p): stub\n", hKL, hWnd, dwMode, lpData);
589 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
593 DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSource,
594 LPCANDIDATELIST lpCandList, DWORD dwBufLen, UINT uFlag)
597 FIXME("(%p, %s, %p, %d, %d): stub\n", hIMC, debugstr_w(lpSource),
598 lpCandList, dwBufLen, uFlag);
599 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
603 BOOL WINAPI ImeDestroy(UINT uForce)
606 HeapFree(GetProcessHeap(),0,hSelectedFrom);
607 hSelectedFrom = NULL;
612 LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData)
614 FIXME("(%p, %d, %p): stub\n", hIMC, uSubFunc, lpData);
615 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
619 BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData,
620 CONST LPBYTE lpbKeyState)
622 /* See the comment at the head of this file */
623 TRACE("We do no processing via this route\n");
627 BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect)
629 LPINPUTCONTEXT lpIMC;
630 TRACE("%p %s\n",hIMC,(fSelect)?"TRUE":"FALSE");
632 if (hIMC == FROM_X11)
634 ERR("ImeSelect should never be called from X11\n");
643 return IME_RemoveFromSelected(hIMC);
645 IME_AddToSelected(hIMC);
647 /* Initialize our structures */
648 lpIMC = LockRealIMC(hIMC);
651 LPIMEPRIVATE myPrivate;
652 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
653 myPrivate->bInComposition = FALSE;
654 myPrivate->bInternalState = FALSE;
655 myPrivate->textfont = NULL;
656 myPrivate->hwndDefault = NULL;
657 ImmUnlockIMCC(lpIMC->hPrivate);
664 BOOL WINAPI ImeSetActiveContext(HIMC hIMC,BOOL fFlag)
666 FIXME("(%p, %x): stub\n", hIMC, fFlag);
670 UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode,
671 CONST LPBYTE lpbKeyState, LPDWORD lpdwTransKey,
672 UINT fuState, HIMC hIMC)
674 /* See the comment at the head of this file */
675 TRACE("We do no processing via this route\n");
679 BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
682 LPINPUTCONTEXT lpIMC;
684 TRACE("%p %i %i %i\n",hIMC,dwAction,dwIndex,dwValue);
686 lpIMC = LockRealIMC(hIMC);
692 case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break;
693 case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break;
694 case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break;
695 case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break;
696 case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break;
697 case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break;
698 case NI_CONTEXTUPDATED:
701 case IMC_SETCOMPOSITIONWINDOW: FIXME("IMC_SETCOMPOSITIONWINDOW\n"); break;
702 case IMC_SETCONVERSIONMODE: FIXME("IMC_SETCONVERSIONMODE\n"); break;
703 case IMC_SETSENTENCEMODE: FIXME("IMC_SETSENTENCEMODE\n"); break;
704 case IMC_SETCANDIDATEPOS: FIXME("IMC_SETCANDIDATEPOS\n"); break;
705 case IMC_SETCOMPOSITIONFONT:
707 LPIMEPRIVATE myPrivate;
708 TRACE("IMC_SETCOMPOSITIONFONT\n");
710 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
711 if (myPrivate->textfont)
713 DeleteObject(myPrivate->textfont);
714 myPrivate->textfont = NULL;
716 myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W);
717 ImmUnlockIMCC(lpIMC->hPrivate);
720 case IMC_SETOPENSTATUS:
722 LPIMEPRIVATE myPrivate;
723 TRACE("IMC_SETOPENSTATUS\n");
725 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
726 if (lpIMC->fOpen != myPrivate->bInternalState &&
727 myPrivate->bInComposition)
729 if(lpIMC->fOpen == FALSE)
731 X11DRV_ForceXIMReset(lpIMC->hWnd);
732 GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION,0,0);
733 myPrivate->bInComposition = FALSE;
737 GenerateIMEMessage(hIMC,WM_IME_STARTCOMPOSITION,0,0);
738 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, 0);
741 myPrivate->bInternalState = lpIMC->fOpen;
745 default: FIXME("Unknown\n"); break;
748 case NI_COMPOSITIONSTR:
756 LPCOMPOSITIONSTRING cs = NULL;
758 LPIMEPRIVATE myPrivate;
760 TRACE("CPS_COMPLETE\n");
762 /* clear existing result */
763 newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0);
765 ImmDestroyIMCC(lpIMC->hCompStr);
766 lpIMC->hCompStr = newCompStr;
770 cdata = ImmLockIMCC(lpIMC->hCompStr);
771 cs = (LPCOMPOSITIONSTRING)cdata;
772 cplen = cs->dwCompStrLen;
773 cpstr = (LPWSTR)&(cdata[cs->dwCompStrOffset]);
774 ImmUnlockIMCC(lpIMC->hCompStr);
778 WCHAR param = cpstr[0];
780 newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen);
781 ImmDestroyIMCC(lpIMC->hCompStr);
782 lpIMC->hCompStr = newCompStr;
783 newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0);
784 ImmDestroyIMCC(lpIMC->hCompStr);
785 lpIMC->hCompStr = newCompStr;
787 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0,
790 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param,
791 GCS_RESULTSTR|GCS_RESULTCLAUSE);
794 GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION, 0, 0);
796 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
797 myPrivate->bInComposition = FALSE;
798 ImmUnlockIMCC(lpIMC->hPrivate);
803 case CPS_CONVERT: FIXME("CPS_CONVERT\n"); break;
804 case CPS_REVERT: FIXME("CPS_REVERT\n"); break;
807 LPIMEPRIVATE myPrivate;
809 TRACE("CPS_CANCEL\n");
811 X11DRV_ForceXIMReset(lpIMC->hWnd);
814 ImmDestroyIMCC(lpIMC->hCompStr);
815 lpIMC->hCompStr = ImeCreateBlankCompStr();
817 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
818 if (myPrivate->bInComposition)
820 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
821 myPrivate->bInComposition = FALSE;
823 ImmUnlockIMCC(lpIMC->hPrivate);
827 default: FIXME("Unknown\n"); break;
830 default: FIXME("Unknown Message\n"); break;
837 BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle,
838 LPCWSTR lpszRegister)
840 FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle,
841 debugstr_w(lpszRegister));
842 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
846 BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle,
847 LPCWSTR lpszUnregister)
849 FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle,
850 debugstr_w(lpszUnregister));
851 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
855 UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf)
857 FIXME("(%d, %p): stub\n", nItem, lpStyleBuf);
858 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
862 UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc,
863 LPCWSTR lpszReading, DWORD dwStyle,
864 LPCWSTR lpszRegister, LPVOID lpData)
866 FIXME("(%p, %s, %d, %s, %p): stub\n", lpfnEnumProc,
867 debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister),
869 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
873 BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp,
874 DWORD dwCompLen, LPCVOID lpRead,
877 LPINPUTCONTEXT lpIMC;
880 LPIMEPRIVATE myPrivate;
882 TRACE("(%p, %d, %p, %d, %p, %d):\n",
883 hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
886 if (hIMC != FROM_X11)
887 FIXME("PROBLEM: This only sets the wine level string\n");
891 * this sets the composition string in the imm32.dll level
892 * of the composition buffer. we cannot manipulate the xim level
893 * buffer, which means that once the xim level buffer changes again
894 * any call to this function from the application will be lost
897 if (lpRead && dwReadLen)
898 FIXME("Reading string unimplemented\n");
900 lpIMC = LockRealIMC(hIMC);
905 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
907 if (dwIndex == SCS_SETSTR)
911 if (!myPrivate->bInComposition)
913 GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0);
914 myPrivate->bInComposition = TRUE;
919 if (dwCompLen && lpComp)
921 newCompStr = updateCompStr(lpIMC->hCompStr, (LPWSTR)lpComp, dwCompLen / sizeof(WCHAR));
922 ImmDestroyIMCC(lpIMC->hCompStr);
923 lpIMC->hCompStr = newCompStr;
925 wParam = ((const WCHAR*)lpComp)[0];
926 flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART;
930 newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0);
931 ImmDestroyIMCC(lpIMC->hCompStr);
932 lpIMC->hCompStr = newCompStr;
936 UpdateDataInDefaultIMEWindow(hIMC, myPrivate->hwndDefault,FALSE);
938 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags);
939 ImmUnlockIMCC(lpIMC->hPrivate);
945 DWORD WINAPI ImeGetImeMenuItems(HIMC hIMC, DWORD dwFlags, DWORD dwType,
946 LPIMEMENUITEMINFOW lpImeParentMenu, LPIMEMENUITEMINFOW lpImeMenu,
949 FIXME("(%p, %x %x %p %p %x): stub\n", hIMC, dwFlags, dwType,
950 lpImeParentMenu, lpImeMenu, dwSize);
951 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
955 /* Interfaces to XIM and other parts of winex11drv */
957 void IME_SetOpenStatus(BOOL fOpen)
959 LPINPUTCONTEXT lpIMC;
960 LPIMEPRIVATE myPrivate;
962 lpIMC = LockRealIMC(FROM_X11);
966 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
968 if (myPrivate->bInternalState && fOpen == FALSE)
970 ShowWindow(myPrivate->hwndDefault, SW_HIDE);
971 ImmDestroyIMCC(lpIMC->hCompStr);
972 lpIMC->hCompStr = ImeCreateBlankCompStr();
975 ImmUnlockIMCC(lpIMC->hPrivate);
976 UnlockRealIMC(FROM_X11);
978 if (myPrivate->bInComposition && fOpen == FALSE)
980 GenerateIMEMessage(FROM_X11, WM_IME_ENDCOMPOSITION, 0, 0);
981 myPrivate->bInComposition = FALSE;
984 if (!myPrivate->bInternalState && fOpen == TRUE)
985 ImmSetOpenStatus(RealIMC(FROM_X11), fOpen);
988 INT IME_GetCursorPos(void)
990 LPINPUTCONTEXT lpIMC;
992 LPCOMPOSITIONSTRING compstr;
997 lpIMC = LockRealIMC(FROM_X11);
1000 compstr = ImmLockIMCC(lpIMC->hCompStr);
1001 rc = compstr->dwCursorPos;
1002 ImmUnlockIMCC(lpIMC->hCompStr);
1004 UnlockRealIMC(FROM_X11);
1008 void IME_SetCursorPos(DWORD pos)
1010 LPINPUTCONTEXT lpIMC;
1011 LPCOMPOSITIONSTRING compstr;
1016 lpIMC = LockRealIMC(FROM_X11);
1020 compstr = ImmLockIMCC(lpIMC->hCompStr);
1023 UnlockRealIMC(FROM_X11);
1027 compstr->dwCursorPos = pos;
1028 ImmUnlockIMCC(lpIMC->hCompStr);
1029 UnlockRealIMC(FROM_X11);
1030 GenerateIMEMessage(FROM_X11, WM_IME_COMPOSITION, pos, GCS_CURSORPOS);
1034 void IME_UpdateAssociation(HWND focus)
1036 ImmGetContext(focus);
1038 if (!focus || !hSelectedFrom)
1041 ImmAssociateContext(focus,RealIMC(FROM_X11));
1045 BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen,
1046 LPCVOID lpRead, DWORD dwReadLen)
1048 return ImeSetCompositionString(FROM_X11, dwIndex, lpComp, dwCompLen,
1052 BOOL IME_NotifyIME(DWORD dwAction, DWORD dwIndex, DWORD dwValue)
1054 return NotifyIME(FROM_X11, dwAction, dwIndex, dwValue);
1058 * Internal functions to help with IME window management
1060 static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd)
1065 LPCOMPOSITIONSTRING compstr;
1066 LPBYTE compdata = NULL;
1068 MONITORINFO mon_info;
1070 LPINPUTCONTEXT lpIMC;
1072 lpIMC = LockRealIMC(hIMC);
1076 hdc = BeginPaint(hwnd,&ps);
1078 GetClientRect(hwnd,&rect);
1079 FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
1081 compdata = ImmLockIMCC(lpIMC->hCompStr);
1082 compstr = (LPCOMPOSITIONSTRING)compdata;
1084 if (compstr->dwCompStrLen && compstr->dwCompStrOffset)
1088 HFONT oldfont = NULL;
1090 LPIMEPRIVATE myPrivate;
1092 CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset);
1093 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
1095 if (myPrivate->textfont)
1096 oldfont = SelectObject(hdc,myPrivate->textfont);
1098 ImmUnlockIMCC(lpIMC->hPrivate);
1100 GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size);
1106 * How this works based on tests on windows:
1107 * CFS_POINT: then we start our window at the point and grow it as large
1108 * as it needs to be for the string.
1109 * CFS_RECT: we still use the ptCurrentPos as a starting point and our
1110 * window is only as large as we need for the string, but we do not
1111 * grow such that our window exceeds the given rect. Wrapping if
1112 * needed and possible. If our ptCurrentPos is outside of our rect
1113 * then no window is displayed.
1114 * CFS_FORCE_POSITION: appears to behave just like CFS_POINT
1115 * maybe becase the default MSIME does not do any IME adjusting.
1117 if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT)
1119 POINT cpt = lpIMC->cfCompForm.ptCurrentPos;
1120 ClientToScreen(lpIMC->hWnd,&cpt);
1123 rect.right = rect.left + pt.x;
1124 rect.bottom = rect.top + pt.y;
1125 monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY);
1127 else /* CFS_DEFAULT */
1129 /* Windows places the default IME window in the bottom left */
1130 HWND target = lpIMC->hWnd;
1131 if (!target) target = GetFocus();
1133 GetWindowRect(target,&rect);
1134 rect.top = rect.bottom;
1135 rect.right = rect.left + pt.x + 20;
1136 rect.bottom = rect.top + pt.y + 20;
1138 monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY);
1141 if (lpIMC->cfCompForm.dwStyle == CFS_RECT)
1144 client =lpIMC->cfCompForm.rcArea;
1145 MapWindowPoints( lpIMC->hWnd, 0, (POINT *)&client, 2 );
1146 IntersectRect(&rect,&rect,&client);
1147 /* TODO: Wrap the input if needed */
1150 if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT)
1152 /* make sure we are on the desktop */
1153 mon_info.cbSize = sizeof(mon_info);
1154 GetMonitorInfoW(monitor, &mon_info);
1156 if (rect.bottom > mon_info.rcWork.bottom)
1158 int shift = rect.bottom - mon_info.rcWork.bottom;
1160 rect.bottom -= shift;
1164 rect.right -= rect.left;
1167 if (rect.right > mon_info.rcWork.right)
1169 int shift = rect.right - mon_info.rcWork.right;
1171 rect.right -= shift;
1175 SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE);
1177 TextOutW(hdc, offX,offY, CompString, compstr->dwCompStrLen);
1180 SelectObject(hdc,oldfont);
1183 ImmUnlockIMCC(lpIMC->hCompStr);
1186 UnlockRealIMC(hIMC);
1189 static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable)
1191 LPCOMPOSITIONSTRING compstr;
1192 LPINPUTCONTEXT lpIMC;
1194 lpIMC = LockRealIMC(hIMC);
1198 if (lpIMC->hCompStr)
1199 compstr = ImmLockIMCC(lpIMC->hCompStr);
1203 if (compstr == NULL || compstr->dwCompStrLen == 0)
1204 ShowWindow(hwnd,SW_HIDE);
1206 ShowWindow(hwnd,SW_SHOWNOACTIVATE);
1208 RedrawWindow(hwnd,NULL,NULL,RDW_ERASENOW|RDW_INVALIDATE);
1210 if (compstr != NULL)
1211 ImmUnlockIMCC(lpIMC->hCompStr);
1213 UnlockRealIMC(hIMC);
1216 static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam)
1218 TRACE("IME message WM_IME_COMPOSITION 0x%lx\n", lParam);
1219 if (lParam & GCS_RESULTSTR)
1221 LPCOMPOSITIONSTRING compstr;
1225 LPINPUTCONTEXT lpIMC;
1227 lpIMC = LockRealIMC(hIMC);
1231 TRACE("Posting result as IME_CHAR\n");
1232 compdata = ImmLockIMCC(lpIMC->hCompStr);
1233 compstr = (LPCOMPOSITIONSTRING)compdata;
1234 ResultStr = (LPWSTR)(compdata + compstr->dwResultStrOffset);
1235 GenerateIMECHARMessages(hIMC, ResultStr, compstr->dwResultStrLen);
1236 ImmUnlockIMCC(lpIMC->hCompStr);
1238 /* clear the buffer */
1239 newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0);
1240 ImmDestroyIMCC(lpIMC->hCompStr);
1241 lpIMC->hCompStr = newCompStr;
1242 UnlockRealIMC(hIMC);
1245 UpdateDataInDefaultIMEWindow(hIMC,hwnd,TRUE);
1248 static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd )
1250 LPINPUTCONTEXT lpIMC;
1252 lpIMC = LockRealIMC(hIMC);
1256 TRACE("IME message WM_IME_STARTCOMPOSITION\n");
1257 lpIMC->hWnd = GetFocus();
1258 ShowWindow(hwnd,SW_SHOWNOACTIVATE);
1259 UnlockRealIMC(hIMC);
1262 static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam,
1267 case IMN_OPENSTATUSWINDOW:
1268 FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n");
1270 case IMN_CLOSESTATUSWINDOW:
1271 FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n");
1273 case IMN_OPENCANDIDATE:
1274 FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n");
1276 case IMN_CHANGECANDIDATE:
1277 FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n");
1279 case IMN_CLOSECANDIDATE:
1280 FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n");
1282 case IMN_SETCONVERSIONMODE:
1283 FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n");
1285 case IMN_SETSENTENCEMODE:
1286 FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n");
1288 case IMN_SETOPENSTATUS:
1289 FIXME("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n");
1291 case IMN_SETCANDIDATEPOS:
1292 FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n");
1294 case IMN_SETCOMPOSITIONFONT:
1295 FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n");
1297 case IMN_SETCOMPOSITIONWINDOW:
1298 FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n");
1301 FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n");
1303 case IMN_SETSTATUSWINDOWPOS:
1304 FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n");
1307 FIXME("WM_IME_NOTIFY:<Unknown 0x%lx>\n",wParam);
1313 static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam,
1319 TRACE("Incoming Message 0x%x (0x%08lx, 0x%08lx)\n", msg, wParam, lParam);
1322 * Each UI window contains the current Input Context.
1323 * This Input Context can be obtained by calling GetWindowLong
1324 * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message.
1325 * The UI window can refer to this Input Context and handles the
1329 hIMC = (HIMC)GetWindowLongPtrW(hwnd,IMMGWL_IMC);
1331 hIMC = RealIMC(FROM_X11);
1333 /* if we have no hIMC there are many messages we cannot process */
1337 case WM_IME_STARTCOMPOSITION:
1338 case WM_IME_ENDCOMPOSITION:
1339 case WM_IME_COMPOSITION:
1341 case WM_IME_CONTROL:
1342 case WM_IME_COMPOSITIONFULL:
1355 LPIMEPRIVATE myPrivate;
1356 LPINPUTCONTEXT lpIMC;
1358 SetWindowTextA(hwnd,"Wine Ime Active");
1360 lpIMC = LockRealIMC(hIMC);
1363 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
1364 myPrivate->hwndDefault = hwnd;
1365 ImmUnlockIMCC(lpIMC->hPrivate);
1367 UnlockRealIMC(hIMC);
1372 PaintDefaultIMEWnd(hIMC, hwnd);
1380 SetFocus((HWND)wParam);
1382 FIXME("Received focus, should never have focus\n");
1384 case WM_IME_COMPOSITION:
1385 DefaultIMEComposition(hIMC, hwnd, lParam);
1387 case WM_IME_STARTCOMPOSITION:
1388 DefaultIMEStartComposition(hIMC, hwnd);
1390 case WM_IME_ENDCOMPOSITION:
1391 TRACE("IME message %s, 0x%lx, 0x%lx\n",
1392 "WM_IME_ENDCOMPOSITION", wParam, lParam);
1393 ShowWindow(hwnd,SW_HIDE);
1396 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_IME_SELECT", wParam, lParam);
1398 case WM_IME_CONTROL:
1399 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_IME_CONTROL", wParam, lParam);
1403 rc = ImeHandleNotify(hIMC,hwnd,msg,wParam,lParam);
1406 TRACE("Non-standard message 0x%x\n",msg);
1408 /* check the MSIME messages */
1409 if (msg == WM_MSIME_SERVICE)
1411 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_SERVICE", wParam, lParam);
1414 else if (msg == WM_MSIME_RECONVERTOPTIONS)
1416 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_RECONVERTOPTIONS", wParam, lParam);
1418 else if (msg == WM_MSIME_MOUSE)
1420 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_MOUSE", wParam, lParam);
1422 else if (msg == WM_MSIME_RECONVERTREQUEST)
1424 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_RECONVERTREQUEST", wParam, lParam);
1426 else if (msg == WM_MSIME_RECONVERT)
1428 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_RECONVERT", wParam, lParam);
1430 else if (msg == WM_MSIME_QUERYPOSITION)
1432 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_QUERYPOSITION", wParam, lParam);
1434 else if (msg == WM_MSIME_DOCUMENTFEED)
1436 TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_DOCUMENTFEED", wParam, lParam);
1438 /* DefWndProc if not an IME message */
1439 if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
1440 (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP)))
1441 rc = DefWindowProcW(hwnd,msg,wParam,lParam);