push ad05fa8ea86b4a1581adad6c24ad723042d385d2
[wine/hacks.git] / dlls / winex11.drv / xim.c
bloba586af28da02e043ab187c77917e5a57e70453c9
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 DWORD dwOutput;
297 WCHAR wcOutput[64];
299 TRACE("PreEditDrawCallback %p\n",ic);
301 if (P_DR)
303 int sel = P_DR->chg_first;
304 int len = P_DR->chg_length;
305 if (P_DR->text)
307 if (! P_DR->text->encoding_is_wchar)
309 TRACE("multibyte\n");
310 dwOutput = MultiByteToWideChar(CP_UNIXCP, 0,
311 P_DR->text->string.multi_byte, -1,
312 wcOutput, 64);
314 /* ignore null */
315 dwOutput --;
316 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, wcOutput, dwOutput);
318 else
320 FIXME("wchar PROBIBILY WRONG\n");
321 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len,
322 (LPWSTR)P_DR->text->string.wide_char,
323 P_DR->text->length);
326 else
327 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, NULL, 0);
329 TRACE("Finished\n");
332 static void XIMPreEditCaretCallback(XIC ic, XPointer client_data,
333 XIMPreeditCaretCallbackStruct *P_C)
335 TRACE("PreeditCaretCallback %p\n",ic);
337 if (P_C)
339 int pos = pImmGetCompositionString(root_context, GCS_CURSORPOS, NULL, 0);
340 TRACE("pos: %d\n", pos);
341 switch(P_C->direction)
343 case XIMForwardChar:
344 case XIMForwardWord:
345 pos++;
346 break;
347 case XIMBackwardChar:
348 case XIMBackwardWord:
349 pos--;
350 break;
351 case XIMLineStart:
352 pos = 0;
353 break;
354 case XIMAbsolutePosition:
355 pos = P_C->position;
356 break;
357 case XIMDontChange:
358 P_C->position = pos;
359 return;
360 case XIMCaretUp:
361 case XIMCaretDown:
362 case XIMPreviousLine:
363 case XIMNextLine:
364 case XIMLineEnd:
365 FIXME("Not implemented\n");
366 break;
368 SendMessageW(((InputContextData*)root_context)->IMC.hWnd,
369 EM_SETSEL, dwPreeditPos + pos, dwPreeditPos + pos);
370 P_C->position = pos;
372 TRACE("Finished\n");
375 void X11DRV_ForceXIMReset(HWND hwnd)
377 XIC ic = X11DRV_get_ic(hwnd);
378 if (ic)
380 char* leftover;
381 TRACE("Forcing Reset %p\n",ic);
382 wine_tsx11_lock();
383 leftover = XmbResetIC(ic);
384 XFree(leftover);
385 wine_tsx11_unlock();
389 /***********************************************************************
390 * X11DRV Ime creation
392 XIM X11DRV_SetupXIM(Display *display, const char *input_style)
394 XIMStyle ximStyleRequest, ximStyleCallback, ximStyleNone;
395 XIMStyles *ximStyles = NULL;
396 INT i;
397 XIM xim;
399 ximStyleRequest = STYLE_CALLBACK;
401 if (!strcasecmp(input_style, "offthespot"))
402 ximStyleRequest = STYLE_OFFTHESPOT;
403 else if (!strcasecmp(input_style, "overthespot"))
404 ximStyleRequest = STYLE_OVERTHESPOT;
405 else if (!strcasecmp(input_style, "root"))
406 ximStyleRequest = STYLE_ROOT;
408 wine_tsx11_lock();
410 if(!XSupportsLocale())
412 WARN("X does not support locale.\n");
413 goto err;
415 if(XSetLocaleModifiers("") == NULL)
417 WARN("Could not set locale modifiers.\n");
418 goto err;
421 xim = XOpenIM(display, NULL, NULL, NULL);
422 if (xim == NULL)
424 WARN("Could not open input method.\n");
425 goto err;
428 TRACE("X display of IM = %p\n", XDisplayOfIM(xim));
429 TRACE("Using %s locale of Input Method\n", XLocaleOfIM(xim));
431 XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL);
432 if (ximStyles == 0)
434 WARN("Could not find supported input style.\n");
436 else
438 TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles);
440 ximStyleRoot = 0;
441 ximStyleNone = 0;
442 ximStyleCallback = 0;
444 for (i = 0; i < ximStyles->count_styles; ++i)
446 int style = ximStyles->supported_styles[i];
447 TRACE("ximStyles[%d] = %s%s%s%s%s\n", i,
448 (style&XIMPreeditArea)?"XIMPreeditArea ":"",
449 (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"",
450 (style&XIMPreeditPosition)?"XIMPreeditPosition ":"",
451 (style&XIMPreeditNothing)?"XIMPreeditNothing ":"",
452 (style&XIMPreeditNone)?"XIMPreeditNone ":"");
453 if (!ximStyle && (ximStyles->supported_styles[i] ==
454 ximStyleRequest))
456 ximStyle = ximStyleRequest;
457 TRACE("Setting Style: ximStyle = ximStyleRequest\n");
459 else if (!ximStyleRoot &&(ximStyles->supported_styles[i] ==
460 STYLE_ROOT))
462 ximStyleRoot = STYLE_ROOT;
463 TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n");
465 else if (!ximStyleCallback &&(ximStyles->supported_styles[i] ==
466 STYLE_CALLBACK))
468 ximStyleCallback = STYLE_CALLBACK;
469 TRACE("Setting Style: ximStyleCallback = STYLE_CALLBACK\n");
471 else if (!ximStyleNone && (ximStyles->supported_styles[i] ==
472 STYLE_NONE))
474 TRACE("Setting Style: ximStyleNone = STYLE_NONE\n");
475 ximStyleNone = STYLE_NONE;
478 XFree(ximStyles);
480 if (ximStyle == 0)
481 ximStyle = ximStyleRoot;
483 if (ximStyle == 0)
484 ximStyle = ximStyleNone;
486 if (ximStyleCallback == 0)
488 TRACE("No callback style avalable\n");
489 ximStyleCallback = ximStyle;
494 wine_tsx11_unlock();
496 if(!hImmDll)
498 LoadImmDll();
500 if (pImmCreateContext)
502 root_context = pImmCreateContext();
503 if (pImmAssociateContext)
504 pImmAssociateContext(0,root_context);
508 return xim;
510 err:
511 wine_tsx11_unlock();
512 return NULL;
516 XIC X11DRV_CreateIC(XIM xim, Display *display, Window win)
518 XPoint spot = {0};
519 XVaNestedList preedit = NULL;
520 XVaNestedList status = NULL;
521 XIC xic;
522 XIMCallback P_StartCB;
523 XIMCallback P_DoneCB;
524 XIMCallback P_DrawCB;
525 XIMCallback P_CaretCB;
526 LANGID langid = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
528 wine_tsx11_lock();
530 /* use complex and slow XIC initialization method only for CJK */
531 if (langid != LANG_CHINESE &&
532 langid != LANG_JAPANESE &&
533 langid != LANG_KOREAN)
535 xic = XCreateIC(xim,
536 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
537 XNClientWindow, win,
538 XNFocusWindow, win,
539 NULL);
540 wine_tsx11_unlock();
541 return xic;
544 /* create callbacks */
545 P_StartCB.client_data = NULL;
546 P_StartCB.callback = (XIMProc)XIMPreEditStartCallback;
547 P_DoneCB.client_data = NULL;
548 P_DoneCB.callback = (XIMProc)XIMPreEditDoneCallback;
549 P_DrawCB.client_data = NULL;
550 P_DrawCB.callback = (XIMProc)XIMPreEditDrawCallback;
551 P_CaretCB.client_data = NULL;
552 P_CaretCB.callback = (XIMProc)XIMPreEditCaretCallback;
554 if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0)
556 preedit = XVaCreateNestedList(0,
557 XNSpotLocation, &spot,
558 XNPreeditStartCallback, &P_StartCB,
559 XNPreeditDoneCallback, &P_DoneCB,
560 XNPreeditDrawCallback, &P_DrawCB,
561 XNPreeditCaretCallback, &P_CaretCB,
562 NULL);
563 TRACE("preedit = %p\n", preedit);
565 else
567 preedit = XVaCreateNestedList(0,
568 XNPreeditStartCallback, &P_StartCB,
569 XNPreeditDoneCallback, &P_DoneCB,
570 XNPreeditDrawCallback, &P_DrawCB,
571 XNPreeditCaretCallback, &P_CaretCB,
572 NULL);
574 TRACE("preedit = %p\n", preedit);
577 if ((ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0)
579 status = XVaCreateNestedList(0,
580 NULL);
581 TRACE("status = %p\n", status);
584 if (preedit != NULL && status != NULL)
586 xic = XCreateIC(xim,
587 XNInputStyle, ximStyle,
588 XNPreeditAttributes, preedit,
589 XNStatusAttributes, status,
590 XNClientWindow, win,
591 XNFocusWindow, win,
592 NULL);
594 else if (preedit != NULL)
596 xic = XCreateIC(xim,
597 XNInputStyle, ximStyle,
598 XNPreeditAttributes, preedit,
599 XNClientWindow, win,
600 XNFocusWindow, win,
601 NULL);
603 else if (status != NULL)
605 xic = XCreateIC(xim,
606 XNInputStyle, ximStyle,
607 XNStatusAttributes, status,
608 XNClientWindow, win,
609 XNFocusWindow, win,
610 NULL);
612 else
614 xic = XCreateIC(xim,
615 XNInputStyle, ximStyle,
616 XNClientWindow, win,
617 XNFocusWindow, win,
618 NULL);
621 TRACE("xic = %p\n", xic);
623 if (preedit != NULL)
624 XFree(preedit);
625 if (status != NULL)
626 XFree(status);
628 wine_tsx11_unlock();
630 return xic;