winex11: Add IME functionality to winex11drv.
[wine.git] / dlls / winex11.drv / xim.c
blob44aefa88a9e2467d2969a11f106ad100168ffe1f
1 /*
2 * Functions for further XIM control
4 * Copyright 2003 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
21 #include "config.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "winnls.h"
31 #include "x11drv.h"
32 #include "imm.h"
33 #include "ddk/imm.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
38 /* this must match with imm32/imm.c */
39 #define FROM_IME 0xcafe1337
41 BOOL ximInComposeMode=FALSE;
43 typedef struct tagInputContextData
45 BOOL bInternalState;
46 BOOL bRead;
47 BOOL bInComposition;
48 HFONT textfont;
50 DWORD dwLock;
51 INPUTCONTEXT IMC;
52 } InputContextData;
54 static HIMC root_context;
55 static XIMStyle ximStyle = 0;
56 static XIMStyle ximStyleRoot = 0;
58 /* moved here from imm32 for dll separation */
59 static DWORD dwCompStringLength = 0;
60 static LPBYTE CompositionString = NULL;
61 static DWORD dwCompStringSize = 0;
62 static LPBYTE ResultString = NULL;
63 static DWORD dwResultStringSize = 0;
64 static DWORD dwPreeditPos = 0;
66 static HMODULE hImmDll = NULL;
67 static HIMC (WINAPI *pImmAssociateContext)(HWND,HIMC);
68 static HIMC (WINAPI *pImmCreateContext)(void);
69 static VOID (WINAPI *pImmSetOpenStatus)(HIMC,BOOL);
70 static BOOL (WINAPI *pImmSetCompositionString)(HIMC, DWORD, LPWSTR,
71 DWORD, LPWSTR, DWORD);
72 static LONG (WINAPI *pImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD);
73 static VOID (WINAPI *pImmNotifyIME)(HIMC, DWORD, DWORD, DWORD);
75 /* WINE specific messages from the xim in x11drv level */
77 #define STYLE_OFFTHESPOT (XIMPreeditArea | XIMStatusArea)
78 #define STYLE_OVERTHESPOT (XIMPreeditPosition | XIMStatusNothing)
79 #define STYLE_ROOT (XIMPreeditNothing | XIMStatusNothing)
80 /* this uses all the callbacks to utilize full IME support */
81 #define STYLE_CALLBACK (XIMPreeditCallbacks | XIMStatusNothing)
82 /* inorder to enable deadkey support */
83 #define STYLE_NONE (XIMPreeditNothing | XIMStatusNothing)
86 * here are the functions that sort of marshall calls into IMM32.DLL
88 static void LoadImmDll(void)
90 hImmDll = LoadLibraryA("imm32.dll");
92 pImmAssociateContext = (void *)GetProcAddress(hImmDll, "ImmAssociateContext");
93 if (!pImmAssociateContext)
94 WARN("IMM: pImmAssociateContext not found in DLL\n");
96 pImmCreateContext = (void *)GetProcAddress(hImmDll, "ImmCreateContext");
97 if (!pImmCreateContext)
98 WARN("IMM: pImmCreateContext not found in DLL\n");
100 pImmSetOpenStatus = (void *)GetProcAddress( hImmDll, "ImmSetOpenStatus");
101 if (!pImmSetOpenStatus)
102 WARN("IMM: pImmSetOpenStatus not found in DLL\n");
104 pImmSetCompositionString =(void *)GetProcAddress(hImmDll, "ImmSetCompositionStringW");
106 if (!pImmSetCompositionString)
107 WARN("IMM: pImmSetCompositionStringW not found in DLL\n");
109 pImmGetCompositionString =(void *)GetProcAddress(hImmDll, "ImmGetCompositionStringW");
111 if (!pImmGetCompositionString)
112 WARN("IMM: pImmGetCompositionStringW not found in DLL\n");
114 pImmNotifyIME = (void *)GetProcAddress( hImmDll, "ImmNotifyIME");
116 if (!pImmNotifyIME)
117 WARN("IMM: pImmNotifyIME not found in DLL\n");
120 static BOOL X11DRV_ImmSetInternalString(DWORD dwIndex, DWORD dwOffset,
121 DWORD selLength, LPWSTR lpComp, DWORD dwCompLen)
123 /* Composition strings are edited in chunks */
124 unsigned int byte_length = dwCompLen * sizeof(WCHAR);
125 unsigned int byte_offset = dwOffset * sizeof(WCHAR);
126 unsigned int byte_selection = selLength * sizeof(WCHAR);
127 BOOL rc = FALSE;
129 TRACE("( %i, %i, %d, %p, %d):\n", dwOffset, selLength, dwIndex, lpComp, dwCompLen );
131 if (dwIndex == GCS_COMPSTR)
133 unsigned int i,j;
134 LPBYTE ptr_new;
135 LPBYTE ptr_old;
137 if ((dwCompLen == 0) && (selLength == 0))
139 /* DO Nothing */
141 /* deletion occurred */
142 else if ((dwCompLen== 0) && (selLength != 0))
144 if (dwCompStringLength)
146 for (i = 0; i < byte_selection; i++)
148 if (byte_offset+byte_selection+i <
149 dwCompStringLength)
151 CompositionString[byte_offset + i] =
152 CompositionString[byte_offset + byte_selection + i];
154 else
155 CompositionString[byte_offset + i] = 0;
157 /* clean up the end */
158 dwCompStringLength -= byte_selection;
160 i = dwCompStringLength;
161 while (i < dwCompStringSize)
163 CompositionString[i++] = 0;
167 else
169 int byte_expansion = byte_length - byte_selection;
171 if (byte_expansion + dwCompStringLength >= dwCompStringSize)
173 if (CompositionString)
174 CompositionString =
175 HeapReAlloc(GetProcessHeap(), 0,
176 CompositionString,
177 dwCompStringSize +
178 byte_expansion);
179 else
180 CompositionString =
181 HeapAlloc(GetProcessHeap(), 0, dwCompStringSize +
182 byte_expansion);
184 memset(&(CompositionString[dwCompStringSize]), 0,
185 byte_expansion);
187 dwCompStringSize += byte_expansion;
190 ptr_new = ((LPBYTE)lpComp);
191 ptr_old = CompositionString + byte_offset + byte_selection;
193 dwCompStringLength += byte_expansion;
195 for (j=0,i = byte_offset; i < dwCompStringSize; i++)
197 if (j < byte_length)
199 CompositionString[i] = ptr_new[j++];
201 else
203 if (ptr_old < CompositionString + dwCompStringSize)
205 CompositionString[i] = *ptr_old;
206 ptr_old++;
208 else
209 CompositionString[i] = 0;
214 if (pImmSetCompositionString)
215 rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR,
216 (LPWSTR)CompositionString, dwCompStringLength,
217 NULL, 0);
219 else if ((dwIndex == GCS_RESULTSTR) && (lpComp) && (dwCompLen))
221 if (dwResultStringSize)
222 HeapFree(GetProcessHeap(),0,ResultString);
223 dwResultStringSize= byte_length;
224 ResultString= HeapAlloc(GetProcessHeap(),0,byte_length);
225 memcpy(ResultString,lpComp,byte_length);
227 if (pImmSetCompositionString)
228 rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR,
229 (LPWSTR)ResultString, dwResultStringSize,
230 NULL, 0);
232 if (pImmNotifyIME)
233 pImmNotifyIME((HIMC)FROM_IME, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
236 return rc;
239 void X11DRV_XIMLookupChars( const char *str, DWORD count )
241 DWORD dwOutput;
242 WCHAR wcOutput[64];
243 HWND focus;
245 dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, str, count, wcOutput, sizeof(wcOutput)/sizeof(WCHAR));
247 if (pImmAssociateContext && (focus = GetFocus()))
248 pImmAssociateContext(focus,root_context);
250 X11DRV_ImmSetInternalString(GCS_RESULTSTR,0,0,wcOutput,dwOutput);
253 static void X11DRV_ImmSetOpenStatus(BOOL fOpen)
255 if (fOpen == FALSE)
257 if (dwCompStringSize)
258 HeapFree(GetProcessHeap(),0,CompositionString);
260 dwCompStringSize = 0;
261 dwCompStringLength = 0;
262 CompositionString = NULL;
264 if (dwResultStringSize)
265 HeapFree(GetProcessHeap(),0,ResultString);
267 dwResultStringSize = 0;
268 ResultString = NULL;
271 if (pImmSetOpenStatus)
272 pImmSetOpenStatus((HIMC)FROM_IME,fOpen);
275 static int XIMPreEditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
277 TRACE("PreEditStartCallback %p\n",ic);
278 X11DRV_ImmSetOpenStatus(TRUE);
279 ximInComposeMode = TRUE;
280 SendMessageW(((InputContextData*)root_context)->IMC.hWnd,
281 EM_GETSEL, 0, (LPARAM)&dwPreeditPos);
282 return -1;
285 static void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
287 TRACE("PreeditDoneCallback %p\n",ic);
288 ximInComposeMode = FALSE;
289 X11DRV_ImmSetOpenStatus(FALSE);
290 dwPreeditPos = 0;
293 static void XIMPreEditDrawCallback(XIM ic, XPointer client_data,
294 XIMPreeditDrawCallbackStruct *P_DR)
296 TRACE("PreEditDrawCallback %p\n",ic);
298 if (P_DR)
300 int sel = P_DR->chg_first;
301 int len = P_DR->chg_length;
302 if (P_DR->text)
304 if (! P_DR->text->encoding_is_wchar)
306 DWORD dwOutput;
307 WCHAR *wcOutput;
309 TRACE("multibyte\n");
310 dwOutput = MultiByteToWideChar(CP_UNIXCP, 0,
311 P_DR->text->string.multi_byte, -1,
312 NULL, 0);
313 wcOutput = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR) * dwOutput);
314 if (wcOutput)
316 dwOutput = MultiByteToWideChar(CP_UNIXCP, 0,
317 P_DR->text->string.multi_byte, -1,
318 wcOutput, dwOutput);
320 /* ignore null */
321 dwOutput --;
322 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, wcOutput, dwOutput);
323 HeapFree(GetProcessHeap(), 0, wcOutput);
326 else
328 FIXME("wchar PROBIBILY WRONG\n");
329 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len,
330 (LPWSTR)P_DR->text->string.wide_char,
331 P_DR->text->length);
334 else
335 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, NULL, 0);
337 TRACE("Finished\n");
340 static void XIMPreEditCaretCallback(XIC ic, XPointer client_data,
341 XIMPreeditCaretCallbackStruct *P_C)
343 TRACE("PreeditCaretCallback %p\n",ic);
345 if (P_C)
347 int pos = pImmGetCompositionString(root_context, GCS_CURSORPOS, NULL, 0);
348 TRACE("pos: %d\n", pos);
349 switch(P_C->direction)
351 case XIMForwardChar:
352 case XIMForwardWord:
353 pos++;
354 break;
355 case XIMBackwardChar:
356 case XIMBackwardWord:
357 pos--;
358 break;
359 case XIMLineStart:
360 pos = 0;
361 break;
362 case XIMAbsolutePosition:
363 pos = P_C->position;
364 break;
365 case XIMDontChange:
366 P_C->position = pos;
367 return;
368 case XIMCaretUp:
369 case XIMCaretDown:
370 case XIMPreviousLine:
371 case XIMNextLine:
372 case XIMLineEnd:
373 FIXME("Not implemented\n");
374 break;
376 SendMessageW(((InputContextData*)root_context)->IMC.hWnd,
377 EM_SETSEL, dwPreeditPos + pos, dwPreeditPos + pos);
378 P_C->position = pos;
380 TRACE("Finished\n");
383 void X11DRV_ForceXIMReset(HWND hwnd)
385 XIC ic = X11DRV_get_ic(hwnd);
386 if (ic)
388 char* leftover;
389 TRACE("Forcing Reset %p\n",ic);
390 wine_tsx11_lock();
391 leftover = XmbResetIC(ic);
392 XFree(leftover);
393 wine_tsx11_unlock();
397 /***********************************************************************
398 * X11DRV Ime creation
400 XIM X11DRV_SetupXIM(Display *display, const char *input_style)
402 XIMStyle ximStyleRequest, ximStyleCallback, ximStyleNone;
403 XIMStyles *ximStyles = NULL;
404 INT i;
405 XIM xim;
407 ximStyleRequest = STYLE_CALLBACK;
409 if (!strcasecmp(input_style, "offthespot"))
410 ximStyleRequest = STYLE_OFFTHESPOT;
411 else if (!strcasecmp(input_style, "overthespot"))
412 ximStyleRequest = STYLE_OVERTHESPOT;
413 else if (!strcasecmp(input_style, "root"))
414 ximStyleRequest = STYLE_ROOT;
416 wine_tsx11_lock();
418 if(!XSupportsLocale())
420 WARN("X does not support locale.\n");
421 goto err;
423 if(XSetLocaleModifiers("") == NULL)
425 WARN("Could not set locale modifiers.\n");
426 goto err;
429 xim = XOpenIM(display, NULL, NULL, NULL);
430 if (xim == NULL)
432 WARN("Could not open input method.\n");
433 goto err;
436 TRACE("X display of IM = %p\n", XDisplayOfIM(xim));
437 TRACE("Using %s locale of Input Method\n", XLocaleOfIM(xim));
439 XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL);
440 if (ximStyles == 0)
442 WARN("Could not find supported input style.\n");
444 else
446 TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles);
448 ximStyleRoot = 0;
449 ximStyleNone = 0;
450 ximStyleCallback = 0;
452 for (i = 0; i < ximStyles->count_styles; ++i)
454 int style = ximStyles->supported_styles[i];
455 TRACE("ximStyles[%d] = %s%s%s%s%s\n", i,
456 (style&XIMPreeditArea)?"XIMPreeditArea ":"",
457 (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"",
458 (style&XIMPreeditPosition)?"XIMPreeditPosition ":"",
459 (style&XIMPreeditNothing)?"XIMPreeditNothing ":"",
460 (style&XIMPreeditNone)?"XIMPreeditNone ":"");
461 if (!ximStyle && (ximStyles->supported_styles[i] ==
462 ximStyleRequest))
464 ximStyle = ximStyleRequest;
465 TRACE("Setting Style: ximStyle = ximStyleRequest\n");
467 else if (!ximStyleRoot &&(ximStyles->supported_styles[i] ==
468 STYLE_ROOT))
470 ximStyleRoot = STYLE_ROOT;
471 TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n");
473 else if (!ximStyleCallback &&(ximStyles->supported_styles[i] ==
474 STYLE_CALLBACK))
476 ximStyleCallback = STYLE_CALLBACK;
477 TRACE("Setting Style: ximStyleCallback = STYLE_CALLBACK\n");
479 else if (!ximStyleNone && (ximStyles->supported_styles[i] ==
480 STYLE_NONE))
482 TRACE("Setting Style: ximStyleNone = STYLE_NONE\n");
483 ximStyleNone = STYLE_NONE;
486 XFree(ximStyles);
488 if (ximStyle == 0)
489 ximStyle = ximStyleRoot;
491 if (ximStyle == 0)
492 ximStyle = ximStyleNone;
494 if (ximStyleCallback == 0)
496 TRACE("No callback style avalable\n");
497 ximStyleCallback = ximStyle;
502 wine_tsx11_unlock();
503 IME_XIMPresent(TRUE);
504 IME_UpdateAssociation(NULL);
506 if(!hImmDll)
508 LoadImmDll();
510 if (pImmCreateContext)
512 root_context = pImmCreateContext();
513 if (pImmAssociateContext)
514 pImmAssociateContext(0,root_context);
518 return xim;
520 err:
521 wine_tsx11_unlock();
522 return NULL;
526 XIC X11DRV_CreateIC(XIM xim, Display *display, Window win)
528 XPoint spot = {0};
529 XVaNestedList preedit = NULL;
530 XVaNestedList status = NULL;
531 XIC xic;
532 XIMCallback P_StartCB;
533 XIMCallback P_DoneCB;
534 XIMCallback P_DrawCB;
535 XIMCallback P_CaretCB;
536 LANGID langid = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
538 wine_tsx11_lock();
540 /* use complex and slow XIC initialization method only for CJK */
541 if (langid != LANG_CHINESE &&
542 langid != LANG_JAPANESE &&
543 langid != LANG_KOREAN)
545 xic = XCreateIC(xim,
546 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
547 XNClientWindow, win,
548 XNFocusWindow, win,
549 NULL);
550 wine_tsx11_unlock();
551 return xic;
554 /* create callbacks */
555 P_StartCB.client_data = NULL;
556 P_StartCB.callback = (XIMProc)XIMPreEditStartCallback;
557 P_DoneCB.client_data = NULL;
558 P_DoneCB.callback = (XIMProc)XIMPreEditDoneCallback;
559 P_DrawCB.client_data = NULL;
560 P_DrawCB.callback = (XIMProc)XIMPreEditDrawCallback;
561 P_CaretCB.client_data = NULL;
562 P_CaretCB.callback = (XIMProc)XIMPreEditCaretCallback;
564 if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0)
566 preedit = XVaCreateNestedList(0,
567 XNSpotLocation, &spot,
568 XNPreeditStartCallback, &P_StartCB,
569 XNPreeditDoneCallback, &P_DoneCB,
570 XNPreeditDrawCallback, &P_DrawCB,
571 XNPreeditCaretCallback, &P_CaretCB,
572 NULL);
573 TRACE("preedit = %p\n", preedit);
575 else
577 preedit = XVaCreateNestedList(0,
578 XNPreeditStartCallback, &P_StartCB,
579 XNPreeditDoneCallback, &P_DoneCB,
580 XNPreeditDrawCallback, &P_DrawCB,
581 XNPreeditCaretCallback, &P_CaretCB,
582 NULL);
584 TRACE("preedit = %p\n", preedit);
587 if ((ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0)
589 status = XVaCreateNestedList(0,
590 NULL);
591 TRACE("status = %p\n", status);
594 if (preedit != NULL && status != NULL)
596 xic = XCreateIC(xim,
597 XNInputStyle, ximStyle,
598 XNPreeditAttributes, preedit,
599 XNStatusAttributes, status,
600 XNClientWindow, win,
601 XNFocusWindow, win,
602 NULL);
604 else if (preedit != NULL)
606 xic = XCreateIC(xim,
607 XNInputStyle, ximStyle,
608 XNPreeditAttributes, preedit,
609 XNClientWindow, win,
610 XNFocusWindow, win,
611 NULL);
613 else if (status != NULL)
615 xic = XCreateIC(xim,
616 XNInputStyle, ximStyle,
617 XNStatusAttributes, status,
618 XNClientWindow, win,
619 XNFocusWindow, win,
620 NULL);
622 else
624 xic = XCreateIC(xim,
625 XNInputStyle, ximStyle,
626 XNClientWindow, win,
627 XNFocusWindow, win,
628 NULL);
631 TRACE("xic = %p\n", xic);
633 if (preedit != NULL)
634 XFree(preedit);
635 if (status != NULL)
636 XFree(status);
638 wine_tsx11_unlock();
640 return xic;