win32u: Move NtUserGetMessage implementation from user32.
[wine.git] / dlls / winemac.drv / ime.c
blob8b89c0089fc978ac0c0df29ac325eedb22949159
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 "config.h"
39 #include <stdarg.h>
41 #include "macdrv.h"
42 #include "winuser.h"
43 #include "imm.h"
44 #include "ddk/imm.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(imm);
48 #define FROM_MACDRV ((HIMC)0xcafe1337)
50 typedef struct _IMEPRIVATE {
51 BOOL bInComposition;
52 BOOL bInternalState;
53 HFONT textfont;
54 HWND hwndDefault;
56 UINT repeat;
57 } IMEPRIVATE, *LPIMEPRIVATE;
59 typedef struct _tagTRANSMSG {
60 UINT message;
61 WPARAM wParam;
62 LPARAM lParam;
63 } TRANSMSG, *LPTRANSMSG;
65 static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e',' ','M','a','c',' ','I','M','E',0};
67 static HIMC *hSelectedFrom = NULL;
68 static INT hSelectedCount = 0;
70 /* MSIME messages */
71 static UINT WM_MSIME_SERVICE;
72 static UINT WM_MSIME_RECONVERTOPTIONS;
73 static UINT WM_MSIME_MOUSE;
74 static UINT WM_MSIME_RECONVERTREQUEST;
75 static UINT WM_MSIME_RECONVERT;
76 static UINT WM_MSIME_QUERYPOSITION;
77 static UINT WM_MSIME_DOCUMENTFEED;
79 static HIMC RealIMC(HIMC hIMC)
81 if (hIMC == FROM_MACDRV)
83 INT i;
84 HWND wnd = GetFocus();
85 HIMC winHimc = ImmGetContext(wnd);
86 for (i = 0; i < hSelectedCount; i++)
87 if (winHimc == hSelectedFrom[i])
88 return winHimc;
89 return NULL;
91 else
92 return hIMC;
95 static LPINPUTCONTEXT LockRealIMC(HIMC hIMC)
97 HIMC real_imc = RealIMC(hIMC);
98 if (real_imc)
99 return ImmLockIMC(real_imc);
100 else
101 return NULL;
104 static BOOL UnlockRealIMC(HIMC hIMC)
106 HIMC real_imc = RealIMC(hIMC);
107 if (real_imc)
108 return ImmUnlockIMC(real_imc);
109 else
110 return FALSE;
113 static HIMCC ImeCreateBlankCompStr(void)
115 HIMCC rc;
116 LPCOMPOSITIONSTRING ptr;
117 rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING));
118 ptr = ImmLockIMCC(rc);
119 memset(ptr, 0, sizeof(COMPOSITIONSTRING));
120 ptr->dwSize = sizeof(COMPOSITIONSTRING);
121 ImmUnlockIMCC(rc);
122 return rc;
125 static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset,
126 LPBYTE target, LPBYTE source, DWORD* lenParam,
127 DWORD* offsetParam, BOOL wchars)
129 if (origLen > 0 && origOffset > 0)
131 int truelen = origLen;
132 if (wchars)
133 truelen *= sizeof(WCHAR);
135 memcpy(&target[currentOffset], &source[origOffset], truelen);
137 *lenParam = origLen;
138 *offsetParam = currentOffset;
139 currentOffset += truelen;
141 return currentOffset;
144 static HIMCC updateCompStr(HIMCC old, LPCWSTR compstr, DWORD len, DWORD *flags)
146 /* We need to make sure the CompStr, CompClause and CompAttr fields are all
147 * set and correct. */
148 int needed_size;
149 HIMCC rc;
150 LPBYTE newdata = NULL;
151 LPBYTE olddata = NULL;
152 LPCOMPOSITIONSTRING new_one;
153 LPCOMPOSITIONSTRING lpcs = NULL;
154 INT current_offset = 0;
156 TRACE("%s, %i\n", debugstr_wn(compstr, len), len);
158 if (old == NULL && compstr == NULL && len == 0)
159 return NULL;
161 if (compstr == NULL && len != 0)
163 ERR("compstr is NULL however we have a len! Please report\n");
164 len = 0;
167 if (old != NULL)
169 olddata = ImmLockIMCC(old);
170 lpcs = (LPCOMPOSITIONSTRING)olddata;
173 needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) +
174 len + sizeof(DWORD) * 2;
176 if (lpcs != NULL)
178 needed_size += lpcs->dwCompReadAttrLen;
179 needed_size += lpcs->dwCompReadClauseLen;
180 needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR);
181 needed_size += lpcs->dwResultReadClauseLen;
182 needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR);
183 needed_size += lpcs->dwResultClauseLen;
184 needed_size += lpcs->dwResultStrLen * sizeof(WCHAR);
185 needed_size += lpcs->dwPrivateSize;
187 rc = ImmCreateIMCC(needed_size);
188 newdata = ImmLockIMCC(rc);
189 new_one = (LPCOMPOSITIONSTRING)newdata;
191 new_one->dwSize = needed_size;
192 current_offset = sizeof(COMPOSITIONSTRING);
193 if (lpcs != NULL)
195 current_offset = updateField(lpcs->dwCompReadAttrLen,
196 lpcs->dwCompReadAttrOffset,
197 current_offset, newdata, olddata,
198 &new_one->dwCompReadAttrLen,
199 &new_one->dwCompReadAttrOffset, FALSE);
201 current_offset = updateField(lpcs->dwCompReadClauseLen,
202 lpcs->dwCompReadClauseOffset,
203 current_offset, newdata, olddata,
204 &new_one->dwCompReadClauseLen,
205 &new_one->dwCompReadClauseOffset, FALSE);
207 current_offset = updateField(lpcs->dwCompReadStrLen,
208 lpcs->dwCompReadStrOffset,
209 current_offset, newdata, olddata,
210 &new_one->dwCompReadStrLen,
211 &new_one->dwCompReadStrOffset, TRUE);
213 /* new CompAttr, CompClause, CompStr, dwCursorPos */
214 new_one->dwDeltaStart = 0;
215 new_one->dwCursorPos = lpcs->dwCursorPos;
217 current_offset = updateField(lpcs->dwResultReadClauseLen,
218 lpcs->dwResultReadClauseOffset,
219 current_offset, newdata, olddata,
220 &new_one->dwResultReadClauseLen,
221 &new_one->dwResultReadClauseOffset, FALSE);
223 current_offset = updateField(lpcs->dwResultReadStrLen,
224 lpcs->dwResultReadStrOffset,
225 current_offset, newdata, olddata,
226 &new_one->dwResultReadStrLen,
227 &new_one->dwResultReadStrOffset, TRUE);
229 current_offset = updateField(lpcs->dwResultClauseLen,
230 lpcs->dwResultClauseOffset,
231 current_offset, newdata, olddata,
232 &new_one->dwResultClauseLen,
233 &new_one->dwResultClauseOffset, FALSE);
235 current_offset = updateField(lpcs->dwResultStrLen,
236 lpcs->dwResultStrOffset,
237 current_offset, newdata, olddata,
238 &new_one->dwResultStrLen,
239 &new_one->dwResultStrOffset, TRUE);
241 current_offset = updateField(lpcs->dwPrivateSize,
242 lpcs->dwPrivateOffset,
243 current_offset, newdata, olddata,
244 &new_one->dwPrivateSize,
245 &new_one->dwPrivateOffset, FALSE);
247 else
249 new_one->dwCursorPos = len;
250 *flags |= GCS_CURSORPOS;
253 /* set new data */
254 /* CompAttr */
255 new_one->dwCompAttrLen = len;
256 if (len > 0)
258 new_one->dwCompAttrOffset = current_offset;
259 memset(&newdata[current_offset], ATTR_INPUT, len);
260 current_offset += len;
263 /* CompClause */
264 if (len > 0)
266 new_one->dwCompClauseLen = sizeof(DWORD) * 2;
267 new_one->dwCompClauseOffset = current_offset;
268 *(DWORD*)&newdata[current_offset] = 0;
269 current_offset += sizeof(DWORD);
270 *(DWORD*)&newdata[current_offset] = len;
271 current_offset += sizeof(DWORD);
273 else
274 new_one->dwCompClauseLen = 0;
276 /* CompStr */
277 new_one->dwCompStrLen = len;
278 if (len > 0)
280 new_one->dwCompStrOffset = current_offset;
281 memcpy(&newdata[current_offset], compstr, len * sizeof(WCHAR));
285 ImmUnlockIMCC(rc);
286 if (lpcs)
287 ImmUnlockIMCC(old);
289 return rc;
292 static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len)
294 /* we need to make sure the ResultStr and ResultClause fields are all
295 * set and correct */
296 int needed_size;
297 HIMCC rc;
298 LPBYTE newdata = NULL;
299 LPBYTE olddata = NULL;
300 LPCOMPOSITIONSTRING new_one;
301 LPCOMPOSITIONSTRING lpcs = NULL;
302 INT current_offset = 0;
304 TRACE("%s, %i\n", debugstr_wn(resultstr, len), len);
306 if (old == NULL && resultstr == NULL && len == 0)
307 return NULL;
309 if (resultstr == NULL && len != 0)
311 ERR("resultstr is NULL however we have a len! Please report\n");
312 len = 0;
315 if (old != NULL)
317 olddata = ImmLockIMCC(old);
318 lpcs = (LPCOMPOSITIONSTRING)olddata;
321 needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) +
322 sizeof(DWORD) * 2;
324 if (lpcs != NULL)
326 needed_size += lpcs->dwCompReadAttrLen;
327 needed_size += lpcs->dwCompReadClauseLen;
328 needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR);
329 needed_size += lpcs->dwCompAttrLen;
330 needed_size += lpcs->dwCompClauseLen;
331 needed_size += lpcs->dwCompStrLen * sizeof(WCHAR);
332 needed_size += lpcs->dwResultReadClauseLen;
333 needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR);
334 needed_size += lpcs->dwPrivateSize;
336 rc = ImmCreateIMCC(needed_size);
337 newdata = ImmLockIMCC(rc);
338 new_one = (LPCOMPOSITIONSTRING)newdata;
340 new_one->dwSize = needed_size;
341 current_offset = sizeof(COMPOSITIONSTRING);
342 if (lpcs != NULL)
344 current_offset = updateField(lpcs->dwCompReadAttrLen,
345 lpcs->dwCompReadAttrOffset,
346 current_offset, newdata, olddata,
347 &new_one->dwCompReadAttrLen,
348 &new_one->dwCompReadAttrOffset, FALSE);
350 current_offset = updateField(lpcs->dwCompReadClauseLen,
351 lpcs->dwCompReadClauseOffset,
352 current_offset, newdata, olddata,
353 &new_one->dwCompReadClauseLen,
354 &new_one->dwCompReadClauseOffset, FALSE);
356 current_offset = updateField(lpcs->dwCompReadStrLen,
357 lpcs->dwCompReadStrOffset,
358 current_offset, newdata, olddata,
359 &new_one->dwCompReadStrLen,
360 &new_one->dwCompReadStrOffset, TRUE);
362 current_offset = updateField(lpcs->dwCompAttrLen,
363 lpcs->dwCompAttrOffset,
364 current_offset, newdata, olddata,
365 &new_one->dwCompAttrLen,
366 &new_one->dwCompAttrOffset, FALSE);
368 current_offset = updateField(lpcs->dwCompClauseLen,
369 lpcs->dwCompClauseOffset,
370 current_offset, newdata, olddata,
371 &new_one->dwCompClauseLen,
372 &new_one->dwCompClauseOffset, FALSE);
374 current_offset = updateField(lpcs->dwCompStrLen,
375 lpcs->dwCompStrOffset,
376 current_offset, newdata, olddata,
377 &new_one->dwCompStrLen,
378 &new_one->dwCompStrOffset, TRUE);
380 new_one->dwCursorPos = lpcs->dwCursorPos;
381 new_one->dwDeltaStart = 0;
383 current_offset = updateField(lpcs->dwResultReadClauseLen,
384 lpcs->dwResultReadClauseOffset,
385 current_offset, newdata, olddata,
386 &new_one->dwResultReadClauseLen,
387 &new_one->dwResultReadClauseOffset, FALSE);
389 current_offset = updateField(lpcs->dwResultReadStrLen,
390 lpcs->dwResultReadStrOffset,
391 current_offset, newdata, olddata,
392 &new_one->dwResultReadStrLen,
393 &new_one->dwResultReadStrOffset, TRUE);
395 /* new ResultClause , ResultStr */
397 current_offset = updateField(lpcs->dwPrivateSize,
398 lpcs->dwPrivateOffset,
399 current_offset, newdata, olddata,
400 &new_one->dwPrivateSize,
401 &new_one->dwPrivateOffset, FALSE);
404 /* set new data */
405 /* ResultClause */
406 if (len > 0)
408 new_one->dwResultClauseLen = sizeof(DWORD) * 2;
409 new_one->dwResultClauseOffset = current_offset;
410 *(DWORD*)&newdata[current_offset] = 0;
411 current_offset += sizeof(DWORD);
412 *(DWORD*)&newdata[current_offset] = len;
413 current_offset += sizeof(DWORD);
415 else
416 new_one->dwResultClauseLen = 0;
418 /* ResultStr */
419 new_one->dwResultStrLen = len;
420 if (len > 0)
422 new_one->dwResultStrOffset = current_offset;
423 memcpy(&newdata[current_offset], resultstr, len * sizeof(WCHAR));
425 ImmUnlockIMCC(rc);
426 if (lpcs)
427 ImmUnlockIMCC(old);
429 return rc;
432 static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam)
434 LPINPUTCONTEXT lpIMC;
435 LPTRANSMSG lpTransMsg;
437 lpIMC = LockRealIMC(hIMC);
438 if (lpIMC == NULL)
439 return;
441 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
442 if (!lpIMC->hMsgBuf)
443 return;
445 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
446 if (!lpTransMsg)
447 return;
449 lpTransMsg += lpIMC->dwNumMsgBuf;
450 lpTransMsg->message = msg;
451 lpTransMsg->wParam = wParam;
452 lpTransMsg->lParam = lParam;
454 ImmUnlockIMCC(lpIMC->hMsgBuf);
455 lpIMC->dwNumMsgBuf++;
457 ImmGenerateMessage(RealIMC(hIMC));
458 UnlockRealIMC(hIMC);
461 static BOOL GenerateMessageToTransKey(LPDWORD lpTransBuf, UINT *uNumTranMsgs,
462 UINT msg, WPARAM wParam, LPARAM lParam)
464 LPTRANSMSG ptr;
466 if (*uNumTranMsgs + 1 >= (UINT)*lpTransBuf)
467 return FALSE;
469 ptr = (LPTRANSMSG)(lpTransBuf + 1 + *uNumTranMsgs * 3);
470 ptr->message = msg;
471 ptr->wParam = wParam;
472 ptr->lParam = lParam;
473 (*uNumTranMsgs)++;
475 return TRUE;
479 static BOOL IME_RemoveFromSelected(HIMC hIMC)
481 int i;
482 for (i = 0; i < hSelectedCount; i++)
484 if (hSelectedFrom[i] == hIMC)
486 if (i < hSelectedCount - 1)
487 memmove(&hSelectedFrom[i], &hSelectedFrom[i + 1], (hSelectedCount - i - 1) * sizeof(HIMC));
488 hSelectedCount--;
489 return TRUE;
492 return FALSE;
495 static void IME_AddToSelected(HIMC hIMC)
497 hSelectedCount++;
498 if (hSelectedFrom)
499 hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount * sizeof(HIMC));
500 else
501 hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC));
502 hSelectedFrom[hSelectedCount - 1] = hIMC;
505 static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable)
507 LPCOMPOSITIONSTRING compstr;
508 LPINPUTCONTEXT lpIMC;
510 lpIMC = LockRealIMC(hIMC);
511 if (lpIMC == NULL)
512 return;
514 if (lpIMC->hCompStr)
515 compstr = ImmLockIMCC(lpIMC->hCompStr);
516 else
517 compstr = NULL;
519 if (compstr == NULL || compstr->dwCompStrLen == 0)
520 ShowWindow(hwnd, SW_HIDE);
521 else if (showable)
522 ShowWindow(hwnd, SW_SHOWNOACTIVATE);
524 RedrawWindow(hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE);
526 if (compstr != NULL)
527 ImmUnlockIMCC(lpIMC->hCompStr);
529 UnlockRealIMC(hIMC);
532 BOOL WINAPI ImeConfigure(HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
534 FIXME("(%p, %p, %d, %p): stub\n", hKL, hWnd, dwMode, lpData);
535 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
536 return FALSE;
539 DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSource, LPCANDIDATELIST lpCandList,
540 DWORD dwBufLen, UINT uFlag)
543 FIXME("(%p, %s, %p, %d, %d): stub\n", hIMC, debugstr_w(lpSource), lpCandList,
544 dwBufLen, uFlag);
545 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
546 return 0;
549 BOOL WINAPI ImeDestroy(UINT uForce)
551 TRACE("\n");
552 HeapFree(GetProcessHeap(), 0, hSelectedFrom);
553 hSelectedFrom = NULL;
554 hSelectedCount = 0;
555 return TRUE;
558 LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData)
560 TRACE("%x %p\n", uSubFunc, lpData);
561 return 0;
564 BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState)
566 LPINPUTCONTEXT lpIMC;
567 BOOL inIME;
569 TRACE("hIMC %p vKey 0x%04x lKeyData 0x%08lx lpbKeyState %p\n", hIMC, vKey, lKeyData, lpbKeyState);
571 switch (vKey)
573 case VK_SHIFT:
574 case VK_CONTROL:
575 case VK_CAPITAL:
576 case VK_MENU:
577 return FALSE;
580 inIME = macdrv_using_input_method();
581 lpIMC = LockRealIMC(hIMC);
582 if (lpIMC)
584 LPIMEPRIVATE myPrivate;
585 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
587 if (inIME && !myPrivate->bInternalState)
588 ImmSetOpenStatus(RealIMC(FROM_MACDRV), TRUE);
589 else if (!inIME && myPrivate->bInternalState)
591 ShowWindow(myPrivate->hwndDefault, SW_HIDE);
592 ImmDestroyIMCC(lpIMC->hCompStr);
593 lpIMC->hCompStr = ImeCreateBlankCompStr();
594 ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE);
597 myPrivate->repeat = (lKeyData >> 30) & 0x1;
599 myPrivate->bInternalState = inIME;
600 ImmUnlockIMCC(lpIMC->hPrivate);
602 UnlockRealIMC(hIMC);
604 return inIME;
607 BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect)
609 LPINPUTCONTEXT lpIMC;
610 TRACE("%p %s\n", hIMC, fSelect ? "TRUE" : "FALSE");
612 if (hIMC == FROM_MACDRV)
614 ERR("ImeSelect should never be called from Cocoa\n");
615 return FALSE;
618 if (!hIMC)
619 return TRUE;
621 /* not selected */
622 if (!fSelect)
623 return IME_RemoveFromSelected(hIMC);
625 IME_AddToSelected(hIMC);
627 /* Initialize our structures */
628 lpIMC = LockRealIMC(hIMC);
629 if (lpIMC != NULL)
631 LPIMEPRIVATE myPrivate;
632 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
633 if (myPrivate->bInComposition)
634 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
635 if (myPrivate->bInternalState)
636 ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE);
637 myPrivate->bInComposition = FALSE;
638 myPrivate->bInternalState = FALSE;
639 myPrivate->textfont = NULL;
640 myPrivate->hwndDefault = NULL;
641 myPrivate->repeat = 0;
642 ImmUnlockIMCC(lpIMC->hPrivate);
643 UnlockRealIMC(hIMC);
646 return TRUE;
649 BOOL WINAPI ImeSetActiveContext(HIMC hIMC, BOOL fFlag)
651 static int once;
653 if (!once++)
654 FIXME("(%p, %x): stub\n", hIMC, fFlag);
655 return TRUE;
658 UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState,
659 LPDWORD lpdwTransKey, UINT fuState, HIMC hIMC)
661 UINT vkey;
662 LPINPUTCONTEXT lpIMC;
663 LPIMEPRIVATE myPrivate;
664 HWND hwndDefault;
665 UINT repeat;
666 int done = 0;
668 TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC);
670 vkey = LOWORD(uVKey);
672 if (vkey == VK_KANA || vkey == VK_KANJI || vkey == VK_MENU)
674 TRACE("Skipping metakey\n");
675 return 0;
678 lpIMC = LockRealIMC(hIMC);
679 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
680 if (!myPrivate->bInternalState)
682 ImmUnlockIMCC(lpIMC->hPrivate);
683 UnlockRealIMC(hIMC);
684 return 0;
687 repeat = myPrivate->repeat;
688 hwndDefault = myPrivate->hwndDefault;
689 ImmUnlockIMCC(lpIMC->hPrivate);
690 UnlockRealIMC(hIMC);
692 TRACE("Processing Mac 0x%04x\n", vkey);
693 macdrv_process_text_input(uVKey, uScanCode, repeat, lpbKeyState, hIMC, &done);
695 while (!done)
696 MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0);
698 if (done < 0)
700 UINT msgs = 0;
701 UINT msg = (uScanCode & 0x8000) ? WM_KEYUP : WM_KEYDOWN;
703 /* KeyStroke not processed by the IME
704 * so we need to rebuild the KeyDown message and pass it on to WINE
706 if (!GenerateMessageToTransKey(lpdwTransKey, &msgs, msg, vkey, MAKELONG(0x0001, uScanCode)))
707 GenerateIMEMessage(hIMC, msg, vkey, MAKELONG(0x0001, uScanCode));
709 return msgs;
711 else
712 UpdateDataInDefaultIMEWindow(hIMC, hwndDefault, FALSE);
713 return 0;
716 BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
718 BOOL bRet = FALSE;
719 LPINPUTCONTEXT lpIMC;
721 TRACE("%p %i %i %i\n", hIMC, dwAction, dwIndex, dwValue);
723 lpIMC = LockRealIMC(hIMC);
724 if (lpIMC == NULL)
725 return FALSE;
727 switch (dwAction)
729 case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break;
730 case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break;
731 case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break;
732 case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break;
733 case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break;
734 case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break;
735 case NI_CONTEXTUPDATED:
736 switch (dwValue)
738 case IMC_SETCOMPOSITIONWINDOW: FIXME("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONWINDOW\n"); break;
739 case IMC_SETCONVERSIONMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETCONVERSIONMODE\n"); break;
740 case IMC_SETSENTENCEMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETSENTENCEMODE\n"); break;
741 case IMC_SETCANDIDATEPOS: FIXME("NI_CONTEXTUPDATED: IMC_SETCANDIDATEPOS\n"); break;
742 case IMC_SETCOMPOSITIONFONT:
744 LPIMEPRIVATE myPrivate;
745 TRACE("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONFONT\n");
747 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
748 if (myPrivate->textfont)
750 DeleteObject(myPrivate->textfont);
751 myPrivate->textfont = NULL;
753 myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W);
754 ImmUnlockIMCC(lpIMC->hPrivate);
756 break;
757 case IMC_SETOPENSTATUS:
759 LPIMEPRIVATE myPrivate;
760 TRACE("NI_CONTEXTUPDATED: IMC_SETOPENSTATUS\n");
762 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
763 if (lpIMC->fOpen != myPrivate->bInternalState && myPrivate->bInComposition)
765 if(lpIMC->fOpen == FALSE)
767 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
768 myPrivate->bInComposition = FALSE;
770 else
772 GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0);
773 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, 0);
776 myPrivate->bInternalState = lpIMC->fOpen;
777 bRet = TRUE;
779 break;
780 default: FIXME("NI_CONTEXTUPDATED: Unknown\n"); break;
782 break;
783 case NI_COMPOSITIONSTR:
784 switch (dwIndex)
786 case CPS_COMPLETE:
788 HIMCC newCompStr;
789 DWORD cplen = 0;
790 LPWSTR cpstr;
791 LPCOMPOSITIONSTRING cs = NULL;
792 LPBYTE cdata = NULL;
793 LPIMEPRIVATE myPrivate;
795 TRACE("NI_COMPOSITIONSTR: CPS_COMPLETE\n");
797 /* clear existing result */
798 newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0);
800 ImmDestroyIMCC(lpIMC->hCompStr);
801 lpIMC->hCompStr = newCompStr;
803 if (lpIMC->hCompStr)
805 cdata = ImmLockIMCC(lpIMC->hCompStr);
806 cs = (LPCOMPOSITIONSTRING)cdata;
807 cplen = cs->dwCompStrLen;
808 cpstr = (LPWSTR)&cdata[cs->dwCompStrOffset];
809 ImmUnlockIMCC(lpIMC->hCompStr);
811 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
812 if (cplen > 0)
814 WCHAR param = cpstr[0];
815 DWORD flags = GCS_COMPSTR;
817 newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen);
818 ImmDestroyIMCC(lpIMC->hCompStr);
819 lpIMC->hCompStr = newCompStr;
820 newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags);
821 ImmDestroyIMCC(lpIMC->hCompStr);
822 lpIMC->hCompStr = newCompStr;
824 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, flags);
826 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param,
827 GCS_RESULTSTR | GCS_RESULTCLAUSE);
829 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
831 else if (myPrivate->bInComposition)
832 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
835 myPrivate->bInComposition = FALSE;
836 ImmUnlockIMCC(lpIMC->hPrivate);
838 bRet = TRUE;
840 break;
841 case CPS_CONVERT: FIXME("NI_COMPOSITIONSTR: CPS_CONVERT\n"); break;
842 case CPS_REVERT: FIXME("NI_COMPOSITIONSTR: CPS_REVERT\n"); break;
843 case CPS_CANCEL:
845 LPIMEPRIVATE myPrivate;
847 TRACE("NI_COMPOSITIONSTR: CPS_CANCEL\n");
849 macdrv_clear_ime_text();
850 if (lpIMC->hCompStr)
851 ImmDestroyIMCC(lpIMC->hCompStr);
853 lpIMC->hCompStr = ImeCreateBlankCompStr();
855 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
856 if (myPrivate->bInComposition)
858 GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0);
859 myPrivate->bInComposition = FALSE;
861 ImmUnlockIMCC(lpIMC->hPrivate);
862 bRet = TRUE;
864 break;
865 default: FIXME("NI_COMPOSITIONSTR: Unknown\n"); break;
867 break;
868 default: FIXME("Unknown Message\n"); break;
871 UnlockRealIMC(hIMC);
872 return bRet;
875 BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister)
877 FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister));
878 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
879 return FALSE;
882 BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister)
884 FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszUnregister));
885 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
886 return FALSE;
889 UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf)
891 FIXME("(%d, %p): stub\n", nItem, lpStyleBuf);
892 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
893 return 0;
896 UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc, LPCWSTR lpszReading,
897 DWORD dwStyle, LPCWSTR lpszRegister, LPVOID lpData)
899 FIXME("(%p, %s, %d, %s, %p): stub\n", lpfnEnumProc, debugstr_w(lpszReading), dwStyle,
900 debugstr_w(lpszRegister), lpData);
901 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
902 return 0;
905 static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid)
907 LPINPUTCONTEXT lpIMC;
908 DWORD flags = 0;
909 WCHAR wParam = 0;
910 LPIMEPRIVATE myPrivate;
911 BOOL sendMessage = TRUE;
913 TRACE("(%p, %d, %p, %d):\n", hIMC, dwIndex, lpComp, dwCompLen);
916 * Explanation:
917 * this sets the composition string in the imm32.dll level
918 * of the composition buffer.
919 * TODO: set the Cocoa window's marked text string and tell text input context
922 lpIMC = LockRealIMC(hIMC);
924 if (lpIMC == NULL)
925 return FALSE;
927 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
929 if (dwIndex == SCS_SETSTR)
931 HIMCC newCompStr;
933 if (!myPrivate->bInComposition)
935 GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0);
936 myPrivate->bInComposition = TRUE;
939 /* clear existing result */
940 newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0);
941 ImmDestroyIMCC(lpIMC->hCompStr);
942 lpIMC->hCompStr = newCompStr;
944 flags = GCS_COMPSTR;
946 if (dwCompLen && lpComp)
948 newCompStr = updateCompStr(lpIMC->hCompStr, (LPCWSTR)lpComp, dwCompLen / sizeof(WCHAR), &flags);
949 ImmDestroyIMCC(lpIMC->hCompStr);
950 lpIMC->hCompStr = newCompStr;
952 wParam = ((const WCHAR*)lpComp)[0];
953 flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART;
955 if (cursor_valid)
957 LPCOMPOSITIONSTRING compstr;
958 compstr = ImmLockIMCC(lpIMC->hCompStr);
959 compstr->dwCursorPos = cursor_pos;
960 ImmUnlockIMCC(lpIMC->hCompStr);
961 flags |= GCS_CURSORPOS;
964 else
966 NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
967 sendMessage = FALSE;
972 if (sendMessage) {
973 GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags);
974 ImmUnlockIMCC(lpIMC->hPrivate);
975 UnlockRealIMC(hIMC);
978 return TRUE;
981 BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen,
982 LPCVOID lpRead, DWORD dwReadLen)
984 TRACE("(%p, %d, %p, %d, %p, %d):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
986 if (lpRead && dwReadLen)
987 FIXME("Reading string unimplemented\n");
989 return IME_SetCompositionString(hIMC, dwIndex, lpComp, dwCompLen, 0, FALSE);
992 DWORD WINAPI ImeGetImeMenuItems(HIMC hIMC, DWORD dwFlags, DWORD dwType, LPIMEMENUITEMINFOW lpImeParentMenu,
993 LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize)
995 FIXME("(%p, %x %x %p %p %x): stub\n", hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize);
996 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
997 return 0;
1000 static void IME_NotifyComplete(void* hIMC)
1002 NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1005 /*****
1006 * Internal functions to help with IME window management
1008 static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd)
1010 PAINTSTRUCT ps;
1011 RECT rect;
1012 HDC hdc;
1013 LPCOMPOSITIONSTRING compstr;
1014 LPBYTE compdata = NULL;
1015 HMONITOR monitor;
1016 MONITORINFO mon_info;
1017 INT offX = 0, offY = 0;
1018 LPINPUTCONTEXT lpIMC;
1020 lpIMC = LockRealIMC(hIMC);
1021 if (lpIMC == NULL)
1022 return;
1024 hdc = BeginPaint(hwnd, &ps);
1026 GetClientRect(hwnd, &rect);
1027 FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
1029 compdata = ImmLockIMCC(lpIMC->hCompStr);
1030 compstr = (LPCOMPOSITIONSTRING)compdata;
1032 if (compstr->dwCompStrLen && compstr->dwCompStrOffset)
1034 SIZE size;
1035 POINT pt;
1036 HFONT oldfont = NULL;
1037 LPWSTR CompString;
1038 LPIMEPRIVATE myPrivate;
1040 CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset);
1041 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
1043 if (myPrivate->textfont)
1044 oldfont = SelectObject(hdc, myPrivate->textfont);
1046 ImmUnlockIMCC(lpIMC->hPrivate);
1048 GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size);
1049 pt.x = size.cx;
1050 pt.y = size.cy;
1051 LPtoDP(hdc, &pt, 1);
1054 * How this works based on tests on windows:
1055 * CFS_POINT: then we start our window at the point and grow it as large
1056 * as it needs to be for the string.
1057 * CFS_RECT: we still use the ptCurrentPos as a starting point and our
1058 * window is only as large as we need for the string, but we do not
1059 * grow such that our window exceeds the given rect. Wrapping if
1060 * needed and possible. If our ptCurrentPos is outside of our rect
1061 * then no window is displayed.
1062 * CFS_FORCE_POSITION: appears to behave just like CFS_POINT
1063 * maybe because the default MSIME does not do any IME adjusting.
1065 if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT)
1067 POINT cpt = lpIMC->cfCompForm.ptCurrentPos;
1068 ClientToScreen(lpIMC->hWnd, &cpt);
1069 rect.left = cpt.x;
1070 rect.top = cpt.y;
1071 rect.right = rect.left + pt.x;
1072 rect.bottom = rect.top + pt.y;
1073 monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY);
1075 else /* CFS_DEFAULT */
1077 /* Windows places the default IME window in the bottom left */
1078 HWND target = lpIMC->hWnd;
1079 if (!target) target = GetFocus();
1081 GetWindowRect(target, &rect);
1082 rect.top = rect.bottom;
1083 rect.right = rect.left + pt.x + 20;
1084 rect.bottom = rect.top + pt.y + 20;
1085 offX=offY=10;
1086 monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY);
1089 if (lpIMC->cfCompForm.dwStyle == CFS_RECT)
1091 RECT client;
1092 client =lpIMC->cfCompForm.rcArea;
1093 MapWindowPoints(lpIMC->hWnd, 0, (POINT *)&client, 2);
1094 IntersectRect(&rect, &rect, &client);
1095 /* TODO: Wrap the input if needed */
1098 if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT)
1100 /* make sure we are on the desktop */
1101 mon_info.cbSize = sizeof(mon_info);
1102 GetMonitorInfoW(monitor, &mon_info);
1104 if (rect.bottom > mon_info.rcWork.bottom)
1106 int shift = rect.bottom - mon_info.rcWork.bottom;
1107 rect.top -= shift;
1108 rect.bottom -= shift;
1110 if (rect.left < 0)
1112 rect.right -= rect.left;
1113 rect.left = 0;
1115 if (rect.right > mon_info.rcWork.right)
1117 int shift = rect.right - mon_info.rcWork.right;
1118 rect.left -= shift;
1119 rect.right -= shift;
1123 SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left,
1124 rect.bottom - rect.top, SWP_NOACTIVATE);
1126 TextOutW(hdc, offX, offY, CompString, compstr->dwCompStrLen);
1128 if (oldfont)
1129 SelectObject(hdc, oldfont);
1132 ImmUnlockIMCC(lpIMC->hCompStr);
1134 EndPaint(hwnd, &ps);
1135 UnlockRealIMC(hIMC);
1138 static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam)
1140 TRACE("IME message WM_IME_COMPOSITION 0x%lx\n", lParam);
1141 if (!(lParam & GCS_RESULTSTR))
1142 UpdateDataInDefaultIMEWindow(hIMC, hwnd, TRUE);
1145 static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd)
1147 LPINPUTCONTEXT lpIMC;
1149 lpIMC = LockRealIMC(hIMC);
1150 if (lpIMC == NULL)
1151 return;
1153 TRACE("IME message WM_IME_STARTCOMPOSITION\n");
1154 lpIMC->hWnd = GetFocus();
1155 ShowWindow(hwnd, SW_SHOWNOACTIVATE);
1156 UnlockRealIMC(hIMC);
1159 static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1161 switch (wParam)
1163 case IMN_OPENSTATUSWINDOW:
1164 FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n");
1165 break;
1166 case IMN_CLOSESTATUSWINDOW:
1167 FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n");
1168 break;
1169 case IMN_OPENCANDIDATE:
1170 FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n");
1171 break;
1172 case IMN_CHANGECANDIDATE:
1173 FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n");
1174 break;
1175 case IMN_CLOSECANDIDATE:
1176 FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n");
1177 break;
1178 case IMN_SETCONVERSIONMODE:
1179 FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n");
1180 break;
1181 case IMN_SETSENTENCEMODE:
1182 FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n");
1183 break;
1184 case IMN_SETOPENSTATUS:
1185 FIXME("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n");
1186 break;
1187 case IMN_SETCANDIDATEPOS:
1188 FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n");
1189 break;
1190 case IMN_SETCOMPOSITIONFONT:
1191 FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n");
1192 break;
1193 case IMN_SETCOMPOSITIONWINDOW:
1194 FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n");
1195 break;
1196 case IMN_GUIDELINE:
1197 FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n");
1198 break;
1199 case IMN_SETSTATUSWINDOWPOS:
1200 FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n");
1201 break;
1202 default:
1203 FIXME("WM_IME_NOTIFY:<Unknown 0x%lx>\n", wParam);
1204 break;
1206 return 0;
1209 static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1211 LRESULT rc = 0;
1212 HIMC hIMC;
1214 TRACE("Incoming Message 0x%x (0x%08lx, 0x%08lx)\n", msg, wParam, lParam);
1217 * Each UI window contains the current Input Context.
1218 * This Input Context can be obtained by calling GetWindowLong
1219 * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message.
1220 * The UI window can refer to this Input Context and handles the
1221 * messages.
1224 hIMC = (HIMC)GetWindowLongPtrW(hwnd, IMMGWL_IMC);
1225 if (!hIMC)
1226 hIMC = RealIMC(FROM_MACDRV);
1228 /* if we have no hIMC there are many messages we cannot process */
1229 if (hIMC == NULL)
1231 switch (msg) {
1232 case WM_IME_STARTCOMPOSITION:
1233 case WM_IME_ENDCOMPOSITION:
1234 case WM_IME_COMPOSITION:
1235 case WM_IME_NOTIFY:
1236 case WM_IME_CONTROL:
1237 case WM_IME_COMPOSITIONFULL:
1238 case WM_IME_SELECT:
1239 case WM_IME_CHAR:
1240 return 0L;
1241 default:
1242 break;
1246 switch (msg)
1248 case WM_CREATE:
1250 LPIMEPRIVATE myPrivate;
1251 LPINPUTCONTEXT lpIMC;
1253 SetWindowTextA(hwnd, "Wine Ime Active");
1255 lpIMC = LockRealIMC(hIMC);
1256 if (lpIMC)
1258 myPrivate = ImmLockIMCC(lpIMC->hPrivate);
1259 myPrivate->hwndDefault = hwnd;
1260 ImmUnlockIMCC(lpIMC->hPrivate);
1262 UnlockRealIMC(hIMC);
1264 return TRUE;
1266 case WM_PAINT:
1267 PaintDefaultIMEWnd(hIMC, hwnd);
1268 return FALSE;
1270 case WM_NCCREATE:
1271 return TRUE;
1273 case WM_SETFOCUS:
1274 if (wParam)
1275 SetFocus((HWND)wParam);
1276 else
1277 FIXME("Received focus, should never have focus\n");
1278 break;
1279 case WM_IME_COMPOSITION:
1280 DefaultIMEComposition(hIMC, hwnd, lParam);
1281 break;
1282 case WM_IME_STARTCOMPOSITION:
1283 DefaultIMEStartComposition(hIMC, hwnd);
1284 break;
1285 case WM_IME_ENDCOMPOSITION:
1286 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_IME_ENDCOMPOSITION", wParam, lParam);
1287 ShowWindow(hwnd, SW_HIDE);
1288 break;
1289 case WM_IME_SELECT:
1290 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_IME_SELECT", wParam, lParam);
1291 break;
1292 case WM_IME_CONTROL:
1293 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_IME_CONTROL", wParam, lParam);
1294 rc = 1;
1295 break;
1296 case WM_IME_NOTIFY:
1297 rc = ImeHandleNotify(hIMC, hwnd, msg, wParam, lParam);
1298 break;
1299 default:
1300 TRACE("Non-standard message 0x%x\n", msg);
1302 /* check the MSIME messages */
1303 if (msg == WM_MSIME_SERVICE)
1305 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_SERVICE", wParam, lParam);
1306 rc = FALSE;
1308 else if (msg == WM_MSIME_RECONVERTOPTIONS)
1310 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_RECONVERTOPTIONS", wParam, lParam);
1312 else if (msg == WM_MSIME_MOUSE)
1314 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_MOUSE", wParam, lParam);
1316 else if (msg == WM_MSIME_RECONVERTREQUEST)
1318 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_RECONVERTREQUEST", wParam, lParam);
1320 else if (msg == WM_MSIME_RECONVERT)
1322 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_RECONVERT", wParam, lParam);
1324 else if (msg == WM_MSIME_QUERYPOSITION)
1326 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_QUERYPOSITION", wParam, lParam);
1328 else if (msg == WM_MSIME_DOCUMENTFEED)
1330 TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_DOCUMENTFEED", wParam, lParam);
1332 /* DefWndProc if not an IME message */
1333 if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
1334 (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP)))
1335 rc = DefWindowProcW(hwnd, msg, wParam, lParam);
1337 return rc;
1340 static BOOL WINAPI register_classes( INIT_ONCE *once, void *param, void **context )
1342 WNDCLASSW wndClass;
1343 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
1344 wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW;
1345 wndClass.lpfnWndProc = (WNDPROC) IME_WindowProc;
1346 wndClass.cbClsExtra = 0;
1347 wndClass.cbWndExtra = 2 * sizeof(LONG_PTR);
1348 wndClass.hInstance = macdrv_module;
1349 wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
1350 wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION);
1351 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1352 wndClass.lpszMenuName = 0;
1353 wndClass.lpszClassName = UI_CLASS_NAME;
1355 RegisterClassW(&wndClass);
1357 WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
1358 WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
1359 WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
1360 WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
1361 WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
1362 WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
1363 WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
1364 return TRUE;
1367 BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, LPCWSTR lpszOption)
1369 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
1371 TRACE("\n");
1372 InitOnceExecuteOnce( &init_once, register_classes, NULL, NULL );
1373 lpIMEInfo->dwPrivateDataSize = sizeof(IMEPRIVATE);
1374 lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
1375 lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE;
1376 lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC;
1377 lpIMEInfo->fdwUICaps = UI_CAP_2700;
1378 /* Tell App we cannot accept ImeSetCompositionString calls */
1379 /* FIXME: Can we? */
1380 lpIMEInfo->fdwSCSCaps = 0;
1381 lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION;
1383 lstrcpyW(lpszUIClass, UI_CLASS_NAME);
1385 return TRUE;
1388 /* Interfaces to other parts of the Mac driver */
1390 /***********************************************************************
1391 * macdrv_im_set_text
1393 void macdrv_im_set_text(const macdrv_event *event)
1395 HWND hwnd = macdrv_get_window_hwnd(event->window);
1396 void *himc = event->im_set_text.data;
1398 TRACE("win %p/%p himc %p text %s complete %u\n", hwnd, event->window, himc,
1399 debugstr_cf(event->im_set_text.text), event->im_set_text.complete);
1401 if (!himc) himc = RealIMC(FROM_MACDRV);
1403 if (event->im_set_text.text)
1405 CFIndex length = CFStringGetLength(event->im_set_text.text);
1406 const UniChar *chars = CFStringGetCharactersPtr(event->im_set_text.text);
1407 UniChar *buffer = NULL;
1409 if (!chars)
1411 buffer = HeapAlloc(GetProcessHeap(), 0, length * sizeof(*buffer));
1412 CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), buffer);
1413 chars = buffer;
1416 if (himc)
1417 IME_SetCompositionString(himc, SCS_SETSTR, chars, length * sizeof(*chars),
1418 event->im_set_text.cursor_pos, !event->im_set_text.complete);
1419 else
1421 INPUT input;
1422 CFIndex i;
1424 input.type = INPUT_KEYBOARD;
1425 input.ki.wVk = 0;
1426 input.ki.time = 0;
1427 input.ki.dwExtraInfo = 0;
1429 for (i = 0; i < length; i++)
1431 input.ki.wScan = chars[i];
1432 input.ki.dwFlags = KEYEVENTF_UNICODE;
1433 __wine_send_input(hwnd, &input, NULL);
1435 input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
1436 __wine_send_input(hwnd, &input, NULL);
1440 HeapFree(GetProcessHeap(), 0, buffer);
1443 if (event->im_set_text.complete)
1444 IME_NotifyComplete(himc);
1447 /***********************************************************************
1448 * macdrv_sent_text_input
1450 void macdrv_sent_text_input(const macdrv_event *event)
1452 TRACE("handled: %s\n", event->sent_text_input.handled ? "TRUE" : "FALSE");
1453 *event->sent_text_input.done = event->sent_text_input.handled ? 1 : -1;
1457 /**************************************************************************
1458 * query_ime_char_rect
1460 BOOL query_ime_char_rect(macdrv_query* query)
1462 HWND hwnd = macdrv_get_window_hwnd(query->window);
1463 void *himc = query->ime_char_rect.data;
1464 CFRange* range = &query->ime_char_rect.range;
1465 CGRect* rect = &query->ime_char_rect.rect;
1466 IMECHARPOSITION charpos;
1467 BOOL ret = FALSE;
1469 TRACE("win %p/%p himc %p range %ld-%ld\n", hwnd, query->window, himc, range->location,
1470 range->length);
1472 if (!himc) himc = RealIMC(FROM_MACDRV);
1474 charpos.dwSize = sizeof(charpos);
1475 charpos.dwCharPos = range->location;
1476 if (ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos))
1478 int i;
1480 *rect = CGRectMake(charpos.pt.x, charpos.pt.y, 0, charpos.cLineHeight);
1482 /* iterate over rest of length to extend rect */
1483 for (i = 1; i < range->length; i++)
1485 charpos.dwSize = sizeof(charpos);
1486 charpos.dwCharPos = range->location + i;
1487 if (!ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos) ||
1488 charpos.pt.y != rect->origin.y)
1490 range->length = i;
1491 break;
1494 rect->size.width = charpos.pt.x - rect->origin.x;
1497 ret = TRUE;
1500 if (!ret)
1502 LPINPUTCONTEXT ic = ImmLockIMC(himc);
1504 if (ic)
1506 LPIMEPRIVATE private = ImmLockIMCC(ic->hPrivate);
1507 LPBYTE compdata = ImmLockIMCC(ic->hCompStr);
1508 LPCOMPOSITIONSTRING compstr = (LPCOMPOSITIONSTRING)compdata;
1509 LPWSTR str = (LPWSTR)(compdata + compstr->dwCompStrOffset);
1511 if (private->hwndDefault && compstr->dwCompStrOffset &&
1512 IsWindowVisible(private->hwndDefault))
1514 HDC dc = GetDC(private->hwndDefault);
1515 HFONT oldfont = NULL;
1516 SIZE size;
1518 if (private->textfont)
1519 oldfont = SelectObject(dc, private->textfont);
1521 if (range->location > compstr->dwCompStrLen)
1522 range->location = compstr->dwCompStrLen;
1523 if (range->location + range->length > compstr->dwCompStrLen)
1524 range->length = compstr->dwCompStrLen - range->location;
1526 GetTextExtentPoint32W(dc, str, range->location, &size);
1527 charpos.rcDocument.left = size.cx;
1528 charpos.rcDocument.top = 0;
1529 GetTextExtentPoint32W(dc, str, range->location + range->length, &size);
1530 charpos.rcDocument.right = size.cx;
1531 charpos.rcDocument.bottom = size.cy;
1533 if (ic->cfCompForm.dwStyle == CFS_DEFAULT)
1534 OffsetRect(&charpos.rcDocument, 10, 10);
1536 LPtoDP(dc, (POINT*)&charpos.rcDocument, 2);
1537 MapWindowPoints(private->hwndDefault, 0, (POINT*)&charpos.rcDocument, 2);
1538 *rect = cgrect_from_rect(charpos.rcDocument);
1539 ret = TRUE;
1541 if (oldfont)
1542 SelectObject(dc, oldfont);
1543 ReleaseDC(private->hwndDefault, dc);
1546 ImmUnlockIMCC(ic->hCompStr);
1547 ImmUnlockIMCC(ic->hPrivate);
1550 ImmUnlockIMC(himc);
1553 if (!ret)
1555 GUITHREADINFO gti;
1556 gti.cbSize = sizeof(gti);
1557 if (GetGUIThreadInfo(0, &gti))
1559 MapWindowPoints(gti.hwndCaret, 0, (POINT*)&gti.rcCaret, 2);
1560 *rect = cgrect_from_rect(gti.rcCaret);
1561 ret = TRUE;
1565 if (ret && range->length && !rect->size.width)
1566 rect->size.width = 1;
1568 TRACE(" -> %s range %ld-%ld rect %s\n", ret ? "TRUE" : "FALSE", range->location,
1569 range->length, wine_dbgstr_cgrect(*rect));
1571 return ret;