msvcrt: Use the remquo()/remquof() implementation from the bundled musl library.
[wine.git] / dlls / winemac.drv / ime.c
blob71062a357c5389fae61ec271d86ea72a28ccef60
1 /*
2 * The IME for interfacing with Mac input methods
4 * Copyright 2008, 2013 CodeWeavers, Aric Stewart
5 * Copyright 2013 Ken Thomases for CodeWeavers Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * Notes:
24 * The normal flow for IMM/IME Processing is as follows.
25 * 1) The Keyboard Driver generates key messages which are first passed to
26 * the IMM and then to IME via ImeProcessKey. If the IME returns 0 then
27 * it does not want the key and the keyboard driver then generates the
28 * WM_KEYUP/WM_KEYDOWN messages. However if the IME is going to process the
29 * key it returns non-zero.
30 * 2) If the IME is going to process the key then the IMM calls ImeToAsciiEx to
31 * process the key. the IME modifies the HIMC structure to reflect the
32 * current state and generates any messages it needs the IMM to process.
33 * 3) IMM checks the messages and send them to the application in question. From
34 * here the IMM level deals with if the application is IME aware or not.
37 #include "macdrv_dll.h"
38 #include "imm.h"
39 #include "immdev.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(imm);
44 #define FROM_MACDRV ((HIMC)0xcafe1337)
46 typedef struct ime_private
48 BOOL bInComposition;
49 BOOL bInternalState;
50 HFONT textfont;
51 HWND hwndDefault;
53 UINT repeat;
54 } IMEPRIVATE, *LPIMEPRIVATE;
56 static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e',' ','M','a','c',' ','I','M','E',0};
58 static HIMC *hSelectedFrom = NULL;
59 static INT hSelectedCount = 0;
61 /* MSIME messages */
62 static UINT WM_MSIME_SERVICE;
63 static UINT WM_MSIME_RECONVERTOPTIONS;
64 static UINT WM_MSIME_MOUSE;
65 static UINT WM_MSIME_RECONVERTREQUEST;
66 static UINT WM_MSIME_RECONVERT;
67 static UINT WM_MSIME_QUERYPOSITION;
68 static UINT WM_MSIME_DOCUMENTFEED;
70 static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length )
72 COMPOSITIONSTRING *string;
73 WCHAR *text = NULL;
74 UINT len, off;
76 if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL;
77 len = result ? string->dwResultStrLen : string->dwCompStrLen;
78 off = result ? string->dwResultStrOffset : string->dwCompStrOffset;
80 if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) )))
82 memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) );
83 text[len] = 0;
84 *length = len;
87 ImmUnlockIMCC( ctx->hCompStr );
88 return text;
91 static HWND input_context_get_ui_hwnd( INPUTCONTEXT *ctx )
93 struct ime_private *priv;
94 HWND hwnd;
95 if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL;
96 hwnd = priv->hwndDefault;
97 ImmUnlockIMCC( ctx->hPrivate );
98 return hwnd;
101 static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc )
103 struct ime_private *priv;
104 HFONT font = NULL;
105 if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL;
106 if (priv->textfont) font = SelectObject( hdc, priv->textfont );
107 ImmUnlockIMCC( ctx->hPrivate );
108 return font;
111 static HIMC RealIMC(HIMC hIMC)
113 if (hIMC == FROM_MACDRV)
115 INT i;
116 HWND wnd = GetFocus();
117 HIMC winHimc = ImmGetContext(wnd);
118 for (i = 0; i < hSelectedCount; i++)
119 if (winHimc == hSelectedFrom[i])
120 return winHimc;
121 return NULL;
123 else
124 return hIMC;
127 static LPINPUTCONTEXT LockRealIMC(HIMC hIMC)
129 HIMC real_imc = RealIMC(hIMC);
130 if (real_imc)
131 return ImmLockIMC(real_imc);
132 else
133 return NULL;
136 static BOOL UnlockRealIMC(HIMC hIMC)
138 HIMC real_imc = RealIMC(hIMC);
139 if (real_imc)
140 return ImmUnlockIMC(real_imc);
141 else
142 return FALSE;
145 static HIMCC ImeCreateBlankCompStr(void)
147 HIMCC rc;
148 LPCOMPOSITIONSTRING ptr;
149 rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING));
150 ptr = ImmLockIMCC(rc);
151 memset(ptr, 0, sizeof(COMPOSITIONSTRING));
152 ptr->dwSize = sizeof(COMPOSITIONSTRING);
153 ImmUnlockIMCC(rc);
154 return rc;
157 static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset,
158 LPBYTE target, LPBYTE source, DWORD* lenParam,
159 DWORD* offsetParam, BOOL wchars)
161 if (origLen > 0 && origOffset > 0)
163 int truelen = origLen;
164 if (wchars)
165 truelen *= sizeof(WCHAR);
167 memcpy(&target[currentOffset], &source[origOffset], truelen);
169 *lenParam = origLen;
170 *offsetParam = currentOffset;
171 currentOffset += truelen;
173 return currentOffset;
176 static HIMCC updateCompStr(HIMCC old, LPCWSTR compstr, DWORD len, DWORD *flags)
178 /* We need to make sure the CompStr, CompClause and CompAttr fields are all
179 * set and correct. */
180 int needed_size;
181 HIMCC rc;
182 LPBYTE newdata = NULL;
183 LPBYTE olddata = NULL;
184 LPCOMPOSITIONSTRING new_one;
185 LPCOMPOSITIONSTRING lpcs = NULL;
186 INT current_offset = 0;
188 TRACE("%s, %li\n", debugstr_wn(compstr, len), len);
190 if (old == NULL && compstr == NULL && len == 0)
191 return NULL;
193 if (compstr == NULL && len != 0)
195 ERR("compstr is NULL however we have a len! Please report\n");
196 len = 0;
199 if (old != NULL)
201 olddata = ImmLockIMCC(old);
202 lpcs = (LPCOMPOSITIONSTRING)olddata;
205 needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) +
206 len + sizeof(DWORD) * 2;
208 if (lpcs != NULL)
210 needed_size += lpcs->dwCompReadAttrLen;
211 needed_size += lpcs->dwCompReadClauseLen;
212 needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR);
213 needed_size += lpcs->dwResultReadClauseLen;
214 needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR);
215 needed_size += lpcs->dwResultClauseLen;
216 needed_size += lpcs->dwResultStrLen * sizeof(WCHAR);
217 needed_size += lpcs->dwPrivateSize;
219 rc = ImmCreateIMCC(needed_size);
220 newdata = ImmLockIMCC(rc);
221 new_one = (LPCOMPOSITIONSTRING)newdata;
223 new_one->dwSize = needed_size;
224 current_offset = sizeof(COMPOSITIONSTRING);
225 if (lpcs != NULL)
227 current_offset = updateField(lpcs->dwCompReadAttrLen,
228 lpcs->dwCompReadAttrOffset,
229 current_offset, newdata, olddata,
230 &new_one->dwCompReadAttrLen,
231 &new_one->dwCompReadAttrOffset, FALSE);
233 current_offset = updateField(lpcs->dwCompReadClauseLen,
234 lpcs->dwCompReadClauseOffset,
235 current_offset, newdata, olddata,
236 &new_one->dwCompReadClauseLen,
237 &new_one->dwCompReadClauseOffset, FALSE);
239 current_offset = updateField(lpcs->dwCompReadStrLen,
240 lpcs->dwCompReadStrOffset,
241 current_offset, newdata, olddata,
242 &new_one->dwCompReadStrLen,
243 &new_one->dwCompReadStrOffset, TRUE);
245 /* new CompAttr, CompClause, CompStr, dwCursorPos */
246 new_one->dwDeltaStart = 0;
247 new_one->dwCursorPos = lpcs->dwCursorPos;
249 current_offset = updateField(lpcs->dwResultReadClauseLen,
250 lpcs->dwResultReadClauseOffset,
251 current_offset, newdata, olddata,
252 &new_one->dwResultReadClauseLen,
253 &new_one->dwResultReadClauseOffset, FALSE);
255 current_offset = updateField(lpcs->dwResultReadStrLen,
256 lpcs->dwResultReadStrOffset,
257 current_offset, newdata, olddata,
258 &new_one->dwResultReadStrLen,
259 &new_one->dwResultReadStrOffset, TRUE);
261 current_offset = updateField(lpcs->dwResultClauseLen,
262 lpcs->dwResultClauseOffset,
263 current_offset, newdata, olddata,
264 &new_one->dwResultClauseLen,
265 &new_one->dwResultClauseOffset, FALSE);
267 current_offset = updateField(lpcs->dwResultStrLen,
268 lpcs->dwResultStrOffset,
269 current_offset, newdata, olddata,
270 &new_one->dwResultStrLen,
271 &new_one->dwResultStrOffset, TRUE);
273 current_offset = updateField(lpcs->dwPrivateSize,
274 lpcs->dwPrivateOffset,
275 current_offset, newdata, olddata,
276 &new_one->dwPrivateSize,
277 &new_one->dwPrivateOffset, FALSE);
279 else
281 new_one->dwCursorPos = len;
282 *flags |= GCS_CURSORPOS;
285 /* set new data */
286 /* CompAttr */
287 new_one->dwCompAttrLen = len;
288 if (len > 0)
290 new_one->dwCompAttrOffset = current_offset;
291 memset(&newdata[current_offset], ATTR_INPUT, len);
292 current_offset += len;
295 /* CompClause */
296 if (len > 0)
298 new_one->dwCompClauseLen = sizeof(DWORD) * 2;
299 new_one->dwCompClauseOffset = current_offset;
300 *(DWORD*)&newdata[current_offset] = 0;
301 current_offset += sizeof(DWORD);
302 *(DWORD*)&newdata[current_offset] = len;
303 current_offset += sizeof(DWORD);
305 else
306 new_one->dwCompClauseLen = 0;
308 /* CompStr */
309 new_one->dwCompStrLen = len;
310 if (len > 0)
312 new_one->dwCompStrOffset = current_offset;
313 memcpy(&newdata[current_offset], compstr, len * sizeof(WCHAR));
317 ImmUnlockIMCC(rc);
318 if (lpcs)
319 ImmUnlockIMCC(old);
321 return rc;
324 static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len)
326 /* we need to make sure the ResultStr and ResultClause fields are all
327 * set and correct */
328 int needed_size;
329 HIMCC rc;
330 LPBYTE newdata = NULL;
331 LPBYTE olddata = NULL;
332 LPCOMPOSITIONSTRING new_one;
333 LPCOMPOSITIONSTRING lpcs = NULL;
334 INT current_offset = 0;
336 TRACE("%s, %li\n", debugstr_wn(resultstr, len), len);
338 if (old == NULL && resultstr == NULL && len == 0)
339 return NULL;
341 if (resultstr == NULL && len != 0)
343 ERR("resultstr is NULL however we have a len! Please report\n");
344 len = 0;
347 if (old != NULL)
349 olddata = ImmLockIMCC(old);
350 lpcs = (LPCOMPOSITIONSTRING)olddata;
353 needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) +
354 sizeof(DWORD) * 2;
356 if (lpcs != NULL)
358 needed_size += lpcs->dwCompReadAttrLen;
359 needed_size += lpcs->dwCompReadClauseLen;
360 needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR);
361 needed_size += lpcs->dwCompAttrLen;
362 needed_size += lpcs->dwCompClauseLen;
363 needed_size += lpcs->dwCompStrLen * sizeof(WCHAR);
364 needed_size += lpcs->dwResultReadClauseLen;
365 needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR);
366 needed_size += lpcs->dwPrivateSize;
368 rc = ImmCreateIMCC(needed_size);
369 newdata = ImmLockIMCC(rc);
370 new_one = (LPCOMPOSITIONSTRING)newdata;
372 new_one->dwSize = needed_size;
373 current_offset = sizeof(COMPOSITIONSTRING);
374 if (lpcs != NULL)
376 current_offset = updateField(lpcs->dwCompReadAttrLen,
377 lpcs->dwCompReadAttrOffset,
378 current_offset, newdata, olddata,
379 &new_one->dwCompReadAttrLen,
380 &new_one->dwCompReadAttrOffset, FALSE);
382 current_offset = updateField(lpcs->dwCompReadClauseLen,
383 lpcs->dwCompReadClauseOffset,
384 current_offset, newdata, olddata,
385 &new_one->dwCompReadClauseLen,
386 &new_one->dwCompReadClauseOffset, FALSE);
388 current_offset = updateField(lpcs->dwCompReadStrLen,
389 lpcs->dwCompReadStrOffset,
390 current_offset, newdata, olddata,
391 &new_one->dwCompReadStrLen,
392 &new_one->dwCompReadStrOffset, TRUE);
394 current_offset = updateField(lpcs->dwCompAttrLen,
395 lpcs->dwCompAttrOffset,
396 current_offset, newdata, olddata,
397 &new_one->dwCompAttrLen,
398 &new_one->dwCompAttrOffset, FALSE);
400 current_offset = updateField(lpcs->dwCompClauseLen,
401 lpcs->dwCompClauseOffset,
402 current_offset, newdata, olddata,
403 &new_one->dwCompClauseLen,
404 &new_one->dwCompClauseOffset, FALSE);
406 current_offset = updateField(lpcs->dwCompStrLen,
407 lpcs->dwCompStrOffset,
408 current_offset, newdata, olddata,
409 &new_one->dwCompStrLen,
410 &new_one->dwCompStrOffset, TRUE);
412 new_one->dwCursorPos = lpcs->dwCursorPos;
413 new_one->dwDeltaStart = 0;
415 current_offset = updateField(lpcs->dwResultReadClauseLen,
416 lpcs->dwResultReadClauseOffset,
417 current_offset, newdata, olddata,
418 &new_one->dwResultReadClauseLen,
419 &new_one->dwResultReadClauseOffset, FALSE);
421 current_offset = updateField(lpcs->dwResultReadStrLen,
422 lpcs->dwResultReadStrOffset,
423 current_offset, newdata, olddata,
424 &new_one->dwResultReadStrLen,
425 &new_one->dwResultReadStrOffset, TRUE);
427 /* new ResultClause , ResultStr */
429 current_offset = updateField(lpcs->dwPrivateSize,
430 lpcs->dwPrivateOffset,
431 current_offset, newdata, olddata,
432 &new_one->dwPrivateSize,
433 &new_one->dwPrivateOffset, FALSE);
436 /* set new data */
437 /* ResultClause */
438 if (len > 0)
440 new_one->dwResultClauseLen = sizeof(DWORD) * 2;
441 new_one->dwResultClauseOffset = current_offset;
442 *(DWORD*)&newdata[current_offset] = 0;
443 current_offset += sizeof(DWORD);
444 *(DWORD*)&newdata[current_offset] = len;
445 current_offset += sizeof(DWORD);
447 else
448 new_one->dwResultClauseLen = 0;
450 /* ResultStr */
451 new_one->dwResultStrLen = len;
452 if (len > 0)
454 new_one->dwResultStrOffset = current_offset;
455 memcpy(&newdata[current_offset], resultstr, len * sizeof(WCHAR));
457 ImmUnlockIMCC(rc);
458 if (lpcs)
459 ImmUnlockIMCC(old);
461 return rc;
464 static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam)
466 LPINPUTCONTEXT lpIMC;
467 LPTRANSMSG lpTransMsg;
469 lpIMC = LockRealIMC(hIMC);
470 if (lpIMC == NULL)
471 return;
473 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
474 if (!lpIMC->hMsgBuf)
475 return;
477 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
478 if (!lpTransMsg)
479 return;
481 lpTransMsg += lpIMC->dwNumMsgBuf;
482 lpTransMsg->message = msg;
483 lpTransMsg->wParam = wParam;
484 lpTransMsg->lParam = lParam;
486 ImmUnlockIMCC(lpIMC->hMsgBuf);
487 lpIMC->dwNumMsgBuf++;
489 ImmGenerateMessage(RealIMC(hIMC));
490 UnlockRealIMC(hIMC);
493 static BOOL GenerateMessageToTransKey(TRANSMSGLIST *lpTransBuf, UINT *uNumTranMsgs,
494 UINT msg, WPARAM wParam, LPARAM lParam)
496 LPTRANSMSG ptr;
498 if (*uNumTranMsgs + 1 >= lpTransBuf->uMsgCount)
499 return FALSE;
501 ptr = lpTransBuf->TransMsg + *uNumTranMsgs;
502 ptr->message = msg;
503 ptr->wParam = wParam;
504 ptr->lParam = lParam;
505 (*uNumTranMsgs)++;
507 return TRUE;
511 static BOOL IME_RemoveFromSelected(HIMC hIMC)
513 int i;
514 for (i = 0; i < hSelectedCount; i++)
516 if (hSelectedFrom[i] == hIMC)
518 if (i < hSelectedCount - 1)
519 memmove(&hSelectedFrom[i], &hSelectedFrom[i + 1], (hSelectedCount - i - 1) * sizeof(HIMC));
520 hSelectedCount--;
521 return TRUE;
524 return FALSE;
527 static void IME_AddToSelected(HIMC hIMC)
529 hSelectedCount++;
530 if (hSelectedFrom)
531 hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount * sizeof(HIMC));
532 else
533 hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC));
534 hSelectedFrom[hSelectedCount - 1] = hIMC;
537 static void UpdateDataInDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd, BOOL showable)
539 LPCOMPOSITIONSTRING compstr;
541 if (lpIMC->hCompStr)
542 compstr = ImmLockIMCC(lpIMC->hCompStr);
543 else
544 compstr = NULL;
546 if (compstr == NULL || compstr->dwCompStrLen == 0)
547 ShowWindow(hwnd, SW_HIDE);
548 else if (showable)
549 ShowWindow(hwnd, SW_SHOWNOACTIVATE);
551 RedrawWindow(hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE);
553 if (compstr != NULL)
554 ImmUnlockIMCC(lpIMC->hCompStr);
557 BOOL WINAPI ImeDestroy(UINT uForce)
559 TRACE("\n");
560 HeapFree(GetProcessHeap(), 0, hSelectedFrom);
561 hSelectedFrom = NULL;
562 hSelectedCount = 0;
563 return TRUE;
566 BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState)
568 LPINPUTCONTEXT lpIMC;
569 BOOL inIME;
571 TRACE("hIMC %p vKey 0x%04x lKeyData 0x%08Ix lpbKeyState %p\n", hIMC, vKey, lKeyData, lpbKeyState);
573 switch (vKey)
575 case VK_SHIFT:
576 case VK_CONTROL:
577 case VK_CAPITAL:
578 case VK_MENU:
579 return FALSE;
582 inIME = MACDRV_CALL(ime_using_input_method, NULL);
583 lpIMC = LockRealIMC(hIMC);
584 if (lpIMC)
586 LPIMEPRIVATE myPrivate;
587 HWND hwnd = input_context_get_ui_hwnd( lpIMC );
588 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
590 if (inIME && !myPrivate->bInternalState)
591 ImmSetOpenStatus(RealIMC(FROM_MACDRV), TRUE);
592 else if (!inIME && myPrivate->bInternalState)
594 ShowWindow( hwnd, SW_HIDE );
595 ImmDestroyIMCC(lpIMC->hCompStr);
596 lpIMC->hCompStr = ImeCreateBlankCompStr();
597 ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE);
600 myPrivate->repeat = (lKeyData >> 30) & 0x1;
602 myPrivate->bInternalState = inIME;
603 ImmUnlockIMCC(lpIMC->hPrivate);
605 UnlockRealIMC(hIMC);
607 return inIME;
610 BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect)
612 LPINPUTCONTEXT lpIMC;
613 TRACE("%p %s\n", hIMC, fSelect ? "TRUE" : "FALSE");
615 if (hIMC == FROM_MACDRV)
617 ERR("ImeSelect should never be called from Cocoa\n");
618 return FALSE;
621 if (!hIMC)
622 return TRUE;
624 /* not selected */
625 if (!fSelect)
626 return IME_RemoveFromSelected(hIMC);
628 IME_AddToSelected(hIMC);
630 /* Initialize our structures */
631 lpIMC = LockRealIMC(hIMC);
632 if (lpIMC != NULL)
634 LPIMEPRIVATE myPrivate;
635 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
636 if (myPrivate->bInComposition)
637 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
638 if (myPrivate->bInternalState)
639 ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE);
640 myPrivate->bInComposition = FALSE;
641 myPrivate->bInternalState = FALSE;
642 myPrivate->textfont = NULL;
643 myPrivate->hwndDefault = NULL;
644 myPrivate->repeat = 0;
645 ImmUnlockIMCC(lpIMC->hPrivate);
646 UnlockRealIMC(hIMC);
649 return TRUE;
652 UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState,
653 TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC)
655 struct process_text_input_params params;
656 UINT vkey;
657 LPINPUTCONTEXT lpIMC;
658 LPIMEPRIVATE myPrivate;
659 HWND hwnd;
660 UINT repeat;
661 int done = 0;
663 TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC);
665 vkey = LOWORD(uVKey);
667 if (vkey == VK_KANA || vkey == VK_KANJI || vkey == VK_MENU)
669 TRACE("Skipping metakey\n");
670 return 0;
673 lpIMC = LockRealIMC(hIMC);
674 hwnd = input_context_get_ui_hwnd( lpIMC );
675 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
676 if (!myPrivate->bInternalState)
678 ImmUnlockIMCC(lpIMC->hPrivate);
679 UnlockRealIMC(hIMC);
680 return 0;
683 repeat = myPrivate->repeat;
684 ImmUnlockIMCC(lpIMC->hPrivate);
685 UnlockRealIMC(hIMC);
687 TRACE("Processing Mac 0x%04x\n", vkey);
688 params.himc = HandleToULong(hIMC);
689 params.vkey = uVKey;
690 params.scan = uScanCode;
691 params.repeat = repeat;
692 params.key_state = lpbKeyState;
693 params.done = &done;
694 MACDRV_CALL(ime_process_text_input, &params);
696 while (!done)
697 MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0);
699 if (done < 0)
701 UINT msgs = 0;
702 UINT msg = (uScanCode & 0x8000) ? WM_KEYUP : WM_KEYDOWN;
704 /* KeyStroke not processed by the IME
705 * so we need to rebuild the KeyDown message and pass it on to WINE
707 if (!GenerateMessageToTransKey(lpdwTransKey, &msgs, msg, vkey, MAKELONG(0x0001, uScanCode)))
708 GenerateIMEMessage(hIMC, msg, vkey, MAKELONG(0x0001, uScanCode));
710 return msgs;
712 else if ((lpIMC = LockRealIMC(hIMC)))
714 UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE );
715 UnlockRealIMC(hIMC);
717 return 0;
720 BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
722 BOOL bRet = FALSE;
723 LPINPUTCONTEXT lpIMC;
725 TRACE("%p %li %li %li\n", hIMC, dwAction, dwIndex, dwValue);
727 lpIMC = LockRealIMC(hIMC);
728 if (lpIMC == NULL)
729 return FALSE;
731 switch (dwAction)
733 case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break;
734 case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break;
735 case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break;
736 case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break;
737 case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break;
738 case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break;
739 case NI_CONTEXTUPDATED:
740 switch (dwValue)
742 case IMC_SETCOMPOSITIONWINDOW: FIXME("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONWINDOW\n"); break;
743 case IMC_SETCONVERSIONMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETCONVERSIONMODE\n"); break;
744 case IMC_SETSENTENCEMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETSENTENCEMODE\n"); break;
745 case IMC_SETCANDIDATEPOS: FIXME("NI_CONTEXTUPDATED: IMC_SETCANDIDATEPOS\n"); break;
746 case IMC_SETCOMPOSITIONFONT:
748 LPIMEPRIVATE myPrivate;
749 TRACE("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONFONT\n");
751 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
752 if (myPrivate->textfont)
754 DeleteObject(myPrivate->textfont);
755 myPrivate->textfont = NULL;
757 myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W);
758 ImmUnlockIMCC(lpIMC->hPrivate);
760 break;
761 case IMC_SETOPENSTATUS:
763 LPIMEPRIVATE myPrivate;
764 TRACE("NI_CONTEXTUPDATED: IMC_SETOPENSTATUS\n");
766 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
767 if (lpIMC->fOpen != myPrivate->bInternalState && myPrivate->bInComposition)
769 if(lpIMC->fOpen == FALSE)
771 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
772 myPrivate->bInComposition = FALSE;
774 else
776 GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0);
777 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, 0);
780 myPrivate->bInternalState = lpIMC->fOpen;
781 bRet = TRUE;
783 break;
784 default: FIXME("NI_CONTEXTUPDATED: Unknown\n"); break;
786 break;
787 case NI_COMPOSITIONSTR:
788 switch (dwIndex)
790 case CPS_COMPLETE:
792 HIMCC newCompStr;
793 LPIMEPRIVATE myPrivate;
794 WCHAR *str;
795 UINT len;
797 TRACE("NI_COMPOSITIONSTR: CPS_COMPLETE\n");
799 /* clear existing result */
800 newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0);
802 ImmDestroyIMCC(lpIMC->hCompStr);
803 lpIMC->hCompStr = newCompStr;
805 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
806 if ((str = input_context_get_comp_str( lpIMC, FALSE, &len )))
808 WCHAR param = str[0];
809 DWORD flags = GCS_COMPSTR;
811 newCompStr = updateResultStr( lpIMC->hCompStr, str, len );
812 ImmDestroyIMCC(lpIMC->hCompStr);
813 lpIMC->hCompStr = newCompStr;
814 newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags);
815 ImmDestroyIMCC(lpIMC->hCompStr);
816 lpIMC->hCompStr = newCompStr;
818 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, flags);
820 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param,
821 GCS_RESULTSTR | GCS_RESULTCLAUSE);
823 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
824 free( str );
826 else if (myPrivate->bInComposition)
827 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
830 myPrivate->bInComposition = FALSE;
831 ImmUnlockIMCC(lpIMC->hPrivate);
833 bRet = TRUE;
835 break;
836 case CPS_CONVERT: FIXME("NI_COMPOSITIONSTR: CPS_CONVERT\n"); break;
837 case CPS_REVERT: FIXME("NI_COMPOSITIONSTR: CPS_REVERT\n"); break;
838 case CPS_CANCEL:
840 LPIMEPRIVATE myPrivate;
842 TRACE("NI_COMPOSITIONSTR: CPS_CANCEL\n");
844 MACDRV_CALL(ime_clear, NULL);
845 if (lpIMC->hCompStr)
846 ImmDestroyIMCC(lpIMC->hCompStr);
848 lpIMC->hCompStr = ImeCreateBlankCompStr();
850 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
851 if (myPrivate->bInComposition)
853 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
854 myPrivate->bInComposition = FALSE;
856 ImmUnlockIMCC(lpIMC->hPrivate);
857 bRet = TRUE;
859 break;
860 default: FIXME("NI_COMPOSITIONSTR: Unknown\n"); break;
862 break;
863 default: FIXME("Unknown Message\n"); break;
866 UnlockRealIMC(hIMC);
867 return bRet;
870 static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid)
872 LPINPUTCONTEXT lpIMC;
873 DWORD flags = 0;
874 WCHAR wParam = 0;
875 LPIMEPRIVATE myPrivate;
876 BOOL sendMessage = TRUE;
878 TRACE("(%p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen);
881 * Explanation:
882 * this sets the composition string in the imm32.dll level
883 * of the composition buffer.
884 * TODO: set the Cocoa window's marked text string and tell text input context
887 lpIMC = LockRealIMC(hIMC);
889 if (lpIMC == NULL)
890 return FALSE;
892 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
894 if (dwIndex == SCS_SETSTR)
896 HIMCC newCompStr;
898 if (!myPrivate->bInComposition)
900 GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0);
901 myPrivate->bInComposition = TRUE;
904 /* clear existing result */
905 newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0);
906 ImmDestroyIMCC(lpIMC->hCompStr);
907 lpIMC->hCompStr = newCompStr;
909 flags = GCS_COMPSTR;
911 if (dwCompLen && lpComp)
913 newCompStr = updateCompStr(lpIMC->hCompStr, (LPCWSTR)lpComp, dwCompLen / sizeof(WCHAR), &flags);
914 ImmDestroyIMCC(lpIMC->hCompStr);
915 lpIMC->hCompStr = newCompStr;
917 wParam = ((const WCHAR*)lpComp)[0];
918 flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART;
920 if (cursor_valid)
922 LPCOMPOSITIONSTRING compstr;
923 compstr = ImmLockIMCC(lpIMC->hCompStr);
924 compstr->dwCursorPos = cursor_pos;
925 ImmUnlockIMCC(lpIMC->hCompStr);
926 flags |= GCS_CURSORPOS;
929 else
931 NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
932 sendMessage = FALSE;
937 if (sendMessage) {
938 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags);
939 ImmUnlockIMCC(lpIMC->hPrivate);
940 UnlockRealIMC(hIMC);
943 return TRUE;
946 BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen,
947 LPCVOID lpRead, DWORD dwReadLen)
949 TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
951 if (lpRead && dwReadLen)
952 FIXME("Reading string unimplemented\n");
954 return IME_SetCompositionString(hIMC, dwIndex, lpComp, dwCompLen, 0, FALSE);
957 static void IME_NotifyComplete(void* hIMC)
959 NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
962 /*****
963 * Internal functions to help with IME window management
965 static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd)
967 PAINTSTRUCT ps;
968 RECT rect;
969 HDC hdc;
970 HMONITOR monitor;
971 MONITORINFO mon_info;
972 INT offX = 0, offY = 0;
973 LPINPUTCONTEXT lpIMC;
974 WCHAR *str;
975 UINT len;
977 lpIMC = ImmLockIMC(hIMC);
978 if (lpIMC == NULL)
979 return;
981 hdc = BeginPaint(hwnd, &ps);
983 GetClientRect(hwnd, &rect);
984 FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
986 if ((str = input_context_get_comp_str( lpIMC, FALSE, &len )))
988 HFONT font = input_context_select_ui_font( lpIMC, hdc );
989 SIZE size;
990 POINT pt;
992 GetTextExtentPoint32W( hdc, str, len, &size );
993 pt.x = size.cx;
994 pt.y = size.cy;
995 LPtoDP(hdc, &pt, 1);
998 * How this works based on tests on windows:
999 * CFS_POINT: then we start our window at the point and grow it as large
1000 * as it needs to be for the string.
1001 * CFS_RECT: we still use the ptCurrentPos as a starting point and our
1002 * window is only as large as we need for the string, but we do not
1003 * grow such that our window exceeds the given rect. Wrapping if
1004 * needed and possible. If our ptCurrentPos is outside of our rect
1005 * then no window is displayed.
1006 * CFS_FORCE_POSITION: appears to behave just like CFS_POINT
1007 * maybe because the default MSIME does not do any IME adjusting.
1009 if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT)
1011 POINT cpt = lpIMC->cfCompForm.ptCurrentPos;
1012 ClientToScreen(lpIMC->hWnd, &cpt);
1013 rect.left = cpt.x;
1014 rect.top = cpt.y;
1015 rect.right = rect.left + pt.x;
1016 rect.bottom = rect.top + pt.y;
1017 monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY);
1019 else /* CFS_DEFAULT */
1021 /* Windows places the default IME window in the bottom left */
1022 HWND target = lpIMC->hWnd;
1023 if (!target) target = GetFocus();
1025 GetWindowRect(target, &rect);
1026 rect.top = rect.bottom;
1027 rect.right = rect.left + pt.x + 20;
1028 rect.bottom = rect.top + pt.y + 20;
1029 offX=offY=10;
1030 monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY);
1033 if (lpIMC->cfCompForm.dwStyle == CFS_RECT)
1035 RECT client;
1036 client =lpIMC->cfCompForm.rcArea;
1037 MapWindowPoints(lpIMC->hWnd, 0, (POINT *)&client, 2);
1038 IntersectRect(&rect, &rect, &client);
1039 /* TODO: Wrap the input if needed */
1042 if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT)
1044 /* make sure we are on the desktop */
1045 mon_info.cbSize = sizeof(mon_info);
1046 GetMonitorInfoW(monitor, &mon_info);
1048 if (rect.bottom > mon_info.rcWork.bottom)
1050 int shift = rect.bottom - mon_info.rcWork.bottom;
1051 rect.top -= shift;
1052 rect.bottom -= shift;
1054 if (rect.left < 0)
1056 rect.right -= rect.left;
1057 rect.left = 0;
1059 if (rect.right > mon_info.rcWork.right)
1061 int shift = rect.right - mon_info.rcWork.right;
1062 rect.left -= shift;
1063 rect.right -= shift;
1067 SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left,
1068 rect.bottom - rect.top, SWP_NOACTIVATE);
1070 TextOutW( hdc, offX, offY, str, len );
1072 if (font) SelectObject( hdc, font );
1073 free( str );
1076 ImmUnlockIMCC(lpIMC->hCompStr);
1078 EndPaint(hwnd, &ps);
1079 ImmUnlockIMC(hIMC);
1082 static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam)
1084 INPUTCONTEXT *ctx;
1085 TRACE("IME message WM_IME_COMPOSITION 0x%Ix\n", lParam);
1086 if (lParam & GCS_RESULTSTR) return;
1087 if (!(ctx = ImmLockIMC( hIMC ))) return;
1088 UpdateDataInDefaultIMEWindow( ctx, hwnd, TRUE );
1089 ImmUnlockIMC(hIMC);
1092 static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd)
1094 LPINPUTCONTEXT lpIMC;
1096 lpIMC = ImmLockIMC(hIMC);
1097 if (lpIMC == NULL)
1098 return;
1100 TRACE("IME message WM_IME_STARTCOMPOSITION\n");
1101 lpIMC->hWnd = GetFocus();
1102 ShowWindow(hwnd, SW_SHOWNOACTIVATE);
1103 ImmUnlockIMC(hIMC);
1106 static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1108 switch (wParam)
1110 case IMN_OPENSTATUSWINDOW:
1111 FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n");
1112 break;
1113 case IMN_CLOSESTATUSWINDOW:
1114 FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n");
1115 break;
1116 case IMN_OPENCANDIDATE:
1117 FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n");
1118 break;
1119 case IMN_CHANGECANDIDATE:
1120 FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n");
1121 break;
1122 case IMN_CLOSECANDIDATE:
1123 FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n");
1124 break;
1125 case IMN_SETCONVERSIONMODE:
1126 FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n");
1127 break;
1128 case IMN_SETSENTENCEMODE:
1129 FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n");
1130 break;
1131 case IMN_SETOPENSTATUS:
1132 FIXME("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n");
1133 break;
1134 case IMN_SETCANDIDATEPOS:
1135 FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n");
1136 break;
1137 case IMN_SETCOMPOSITIONFONT:
1138 FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n");
1139 break;
1140 case IMN_SETCOMPOSITIONWINDOW:
1141 FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n");
1142 break;
1143 case IMN_GUIDELINE:
1144 FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n");
1145 break;
1146 case IMN_SETSTATUSWINDOWPOS:
1147 FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n");
1148 break;
1149 default:
1150 FIXME("WM_IME_NOTIFY:<Unknown 0x%Ix>\n", wParam);
1151 break;
1153 return 0;
1156 static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1158 LRESULT rc = 0;
1159 HIMC hIMC;
1161 TRACE("Incoming Message 0x%x (0x%08Ix, 0x%08Ix)\n", msg, wParam, lParam);
1164 * Each UI window contains the current Input Context.
1165 * This Input Context can be obtained by calling GetWindowLong
1166 * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message.
1167 * The UI window can refer to this Input Context and handles the
1168 * messages.
1171 hIMC = (HIMC)GetWindowLongPtrW(hwnd, IMMGWL_IMC);
1173 /* if we have no hIMC there are many messages we cannot process */
1174 if (hIMC == NULL)
1176 switch (msg) {
1177 case WM_IME_STARTCOMPOSITION:
1178 case WM_IME_ENDCOMPOSITION:
1179 case WM_IME_COMPOSITION:
1180 case WM_IME_NOTIFY:
1181 case WM_IME_CONTROL:
1182 case WM_IME_COMPOSITIONFULL:
1183 case WM_IME_SELECT:
1184 case WM_IME_CHAR:
1185 return 0L;
1186 default:
1187 break;
1191 switch (msg)
1193 case WM_CREATE:
1195 LPIMEPRIVATE myPrivate;
1196 LPINPUTCONTEXT lpIMC;
1198 SetWindowTextA(hwnd, "Wine Ime Active");
1200 lpIMC = ImmLockIMC(hIMC);
1201 if (lpIMC)
1203 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
1204 myPrivate->hwndDefault = hwnd;
1205 ImmUnlockIMCC(lpIMC->hPrivate);
1207 ImmUnlockIMC(hIMC);
1209 return TRUE;
1211 case WM_PAINT:
1212 PaintDefaultIMEWnd(hIMC, hwnd);
1213 return FALSE;
1215 case WM_NCCREATE:
1216 return TRUE;
1218 case WM_SETFOCUS:
1219 if (wParam)
1220 SetFocus((HWND)wParam);
1221 else
1222 FIXME("Received focus, should never have focus\n");
1223 break;
1224 case WM_IME_COMPOSITION:
1225 DefaultIMEComposition(hIMC, hwnd, lParam);
1226 break;
1227 case WM_IME_STARTCOMPOSITION:
1228 DefaultIMEStartComposition(hIMC, hwnd);
1229 break;
1230 case WM_IME_ENDCOMPOSITION:
1231 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_ENDCOMPOSITION", wParam, lParam);
1232 ShowWindow(hwnd, SW_HIDE);
1233 break;
1234 case WM_IME_SELECT:
1235 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_SELECT", wParam, lParam);
1236 break;
1237 case WM_IME_CONTROL:
1238 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_CONTROL", wParam, lParam);
1239 rc = 1;
1240 break;
1241 case WM_IME_NOTIFY:
1242 rc = ImeHandleNotify(hIMC, hwnd, msg, wParam, lParam);
1243 break;
1244 default:
1245 TRACE("Non-standard message 0x%x\n", msg);
1247 /* check the MSIME messages */
1248 if (msg == WM_MSIME_SERVICE)
1250 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_SERVICE", wParam, lParam);
1251 rc = FALSE;
1253 else if (msg == WM_MSIME_RECONVERTOPTIONS)
1255 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTOPTIONS", wParam, lParam);
1257 else if (msg == WM_MSIME_MOUSE)
1259 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_MOUSE", wParam, lParam);
1261 else if (msg == WM_MSIME_RECONVERTREQUEST)
1263 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTREQUEST", wParam, lParam);
1265 else if (msg == WM_MSIME_RECONVERT)
1267 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERT", wParam, lParam);
1269 else if (msg == WM_MSIME_QUERYPOSITION)
1271 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_QUERYPOSITION", wParam, lParam);
1273 else if (msg == WM_MSIME_DOCUMENTFEED)
1275 TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_DOCUMENTFEED", wParam, lParam);
1277 /* DefWndProc if not an IME message */
1278 if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
1279 (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP)))
1280 rc = DefWindowProcW(hwnd, msg, wParam, lParam);
1282 return rc;
1285 static BOOL WINAPI register_classes( INIT_ONCE *once, void *param, void **context )
1287 WNDCLASSW wndClass;
1288 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
1289 wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW;
1290 wndClass.lpfnWndProc = (WNDPROC) IME_WindowProc;
1291 wndClass.cbClsExtra = 0;
1292 wndClass.cbWndExtra = 2 * sizeof(LONG_PTR);
1293 wndClass.hInstance = macdrv_module;
1294 wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
1295 wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION);
1296 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1297 wndClass.lpszMenuName = 0;
1298 wndClass.lpszClassName = UI_CLASS_NAME;
1300 RegisterClassW(&wndClass);
1302 WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
1303 WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
1304 WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
1305 WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
1306 WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
1307 WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
1308 WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
1309 return TRUE;
1312 BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, DWORD flags)
1314 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
1316 TRACE("\n");
1317 InitOnceExecuteOnce( &init_once, register_classes, NULL, NULL );
1318 lpIMEInfo->dwPrivateDataSize = sizeof(IMEPRIVATE);
1319 lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
1320 lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE;
1321 lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC;
1322 lpIMEInfo->fdwUICaps = UI_CAP_2700;
1323 /* Tell App we cannot accept ImeSetCompositionString calls */
1324 /* FIXME: Can we? */
1325 lpIMEInfo->fdwSCSCaps = 0;
1326 lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION;
1328 lstrcpyW(lpszUIClass, UI_CLASS_NAME);
1330 return TRUE;
1333 /* Interfaces to other parts of the Mac driver */
1335 /***********************************************************************
1336 * macdrv_ime_set_text
1338 NTSTATUS WINAPI macdrv_ime_set_text(void *arg, ULONG size)
1340 struct ime_set_text_params *params = arg;
1341 ULONG length = (size - offsetof(struct ime_set_text_params, text)) / sizeof(WCHAR);
1342 void *himc = param_ptr(params->himc);
1343 HWND hwnd = UlongToHandle(params->hwnd);
1345 if (!himc) himc = RealIMC(FROM_MACDRV);
1347 if (length)
1349 if (himc)
1350 IME_SetCompositionString(himc, SCS_SETSTR, params->text, length * sizeof(WCHAR),
1351 params->cursor_pos, !params->complete);
1352 else
1354 INPUT input;
1355 unsigned int i;
1357 input.type = INPUT_KEYBOARD;
1358 input.ki.wVk = 0;
1359 input.ki.time = 0;
1360 input.ki.dwExtraInfo = 0;
1362 for (i = 0; i < length; i++)
1364 input.ki.wScan = params->text[i];
1365 input.ki.dwFlags = KEYEVENTF_UNICODE;
1366 __wine_send_input(hwnd, &input, NULL);
1368 input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
1369 __wine_send_input(hwnd, &input, NULL);
1374 if (params->complete)
1375 IME_NotifyComplete(himc);
1376 return 0;
1379 /**************************************************************************
1380 * macdrv_ime_query_char_rect
1382 NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size)
1384 struct ime_query_char_rect_params *params = arg;
1385 struct ime_query_char_rect_result *result = param_ptr(params->result);
1386 void *himc = param_ptr(params->himc);
1387 IMECHARPOSITION charpos;
1388 BOOL ret = FALSE;
1390 result->location = params->location;
1391 result->length = params->length;
1393 if (!himc) himc = RealIMC(FROM_MACDRV);
1395 charpos.dwSize = sizeof(charpos);
1396 charpos.dwCharPos = params->location;
1397 if (ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos))
1399 int i;
1401 SetRect(&result->rect, charpos.pt.x, charpos.pt.y, 0, charpos.pt.y + charpos.cLineHeight);
1403 /* iterate over rest of length to extend rect */
1404 for (i = 1; i < params->length; i++)
1406 charpos.dwSize = sizeof(charpos);
1407 charpos.dwCharPos = params->location + i;
1408 if (!ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos) ||
1409 charpos.pt.y != result->rect.top)
1411 result->length = i;
1412 break;
1415 result->rect.right = charpos.pt.x;
1418 ret = TRUE;
1421 if (!ret)
1423 LPINPUTCONTEXT ic = ImmLockIMC(himc);
1425 if (ic)
1427 WCHAR *str;
1428 HWND hwnd;
1429 UINT len;
1431 if ((hwnd = input_context_get_ui_hwnd( ic )) && IsWindowVisible( hwnd ) &&
1432 (str = input_context_get_comp_str( ic, FALSE, &len )))
1434 HDC dc = GetDC( hwnd );
1435 HFONT font = input_context_select_ui_font( ic, dc );
1436 SIZE size;
1438 if (result->location > len) result->location = len;
1439 if (result->location + result->length > len) result->length = len - result->location;
1441 GetTextExtentPoint32W( dc, str, result->location, &size );
1442 charpos.rcDocument.left = size.cx;
1443 charpos.rcDocument.top = 0;
1444 GetTextExtentPoint32W( dc, str, result->location + result->length, &size );
1445 charpos.rcDocument.right = size.cx;
1446 charpos.rcDocument.bottom = size.cy;
1448 if (ic->cfCompForm.dwStyle == CFS_DEFAULT)
1449 OffsetRect(&charpos.rcDocument, 10, 10);
1451 LPtoDP(dc, (POINT*)&charpos.rcDocument, 2);
1452 MapWindowPoints( hwnd, 0, (POINT *)&charpos.rcDocument, 2 );
1453 result->rect = charpos.rcDocument;
1454 ret = TRUE;
1456 if (font) SelectObject( dc, font );
1457 ReleaseDC( hwnd, dc );
1458 free( str );
1462 ImmUnlockIMC(himc);
1465 if (!ret)
1467 GUITHREADINFO gti;
1468 gti.cbSize = sizeof(gti);
1469 if (GetGUIThreadInfo(0, &gti))
1471 MapWindowPoints(gti.hwndCaret, 0, (POINT*)&gti.rcCaret, 2);
1472 result->rect = gti.rcCaret;
1473 ret = TRUE;
1477 if (ret && result->length && result->rect.left == result->rect.right)
1478 result->rect.right++;
1480 return ret;