msxml3: Fix memory leak in test.
[wine] / dlls / winex11.drv / xim.c
1 /*
2  * Functions for further XIM control
3  *
4  * Copyright 2003 CodeWeavers, Aric Stewart
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25
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"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
37
38 /* this must match with imm32/imm.c */
39 #define FROM_IME 0xcafe1337
40
41 BOOL ximInComposeMode=FALSE;
42
43 typedef struct tagInputContextData
44 {
45     BOOL            bInternalState;
46     BOOL            bRead;
47     BOOL            bInComposition;
48     HFONT           textfont;
49
50     DWORD           dwLock;
51     INPUTCONTEXT    IMC;
52 } InputContextData;
53
54 static HIMC root_context;
55 static XIMStyle ximStyle = 0;
56 static XIMStyle ximStyleRoot = 0;
57
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;
65
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);
74
75 /* WINE specific messages from the xim in x11drv level */
76
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)
84
85 /*
86  * here are the functions that sort of marshall calls into IMM32.DLL
87  */
88 static void LoadImmDll(void)
89 {
90     hImmDll = LoadLibraryA("imm32.dll");
91
92     pImmAssociateContext = (void *)GetProcAddress(hImmDll, "ImmAssociateContext");
93     if (!pImmAssociateContext)
94         WARN("IMM: pImmAssociateContext not found in DLL\n");
95
96     pImmCreateContext = (void *)GetProcAddress(hImmDll, "ImmCreateContext");
97     if (!pImmCreateContext)
98         WARN("IMM: pImmCreateContext not found in DLL\n");
99
100     pImmSetOpenStatus = (void *)GetProcAddress( hImmDll, "ImmSetOpenStatus");
101     if (!pImmSetOpenStatus)
102         WARN("IMM: pImmSetOpenStatus not found in DLL\n");
103
104     pImmSetCompositionString =(void *)GetProcAddress(hImmDll, "ImmSetCompositionStringW");
105
106     if (!pImmSetCompositionString)
107         WARN("IMM: pImmSetCompositionStringW not found in DLL\n");
108
109     pImmGetCompositionString =(void *)GetProcAddress(hImmDll, "ImmGetCompositionStringW");
110
111     if (!pImmGetCompositionString)
112         WARN("IMM: pImmGetCompositionStringW not found in DLL\n");
113
114     pImmNotifyIME = (void *)GetProcAddress( hImmDll, "ImmNotifyIME");
115
116     if (!pImmNotifyIME)
117         WARN("IMM: pImmNotifyIME not found in DLL\n");
118 }
119
120 static BOOL X11DRV_ImmSetInternalString(DWORD dwIndex, DWORD dwOffset,
121                                         DWORD selLength, LPWSTR lpComp, DWORD dwCompLen)
122 {
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;
128
129     TRACE("( %i, %i, %d, %p, %d):\n", dwOffset, selLength, dwIndex, lpComp, dwCompLen );
130
131     if (dwIndex == GCS_COMPSTR)
132     {
133         unsigned int i,j;
134         LPBYTE ptr_new;
135         LPBYTE ptr_old;
136
137         if ((dwCompLen == 0) && (selLength == 0))
138         {
139             /* DO Nothing */
140         }
141         /* deletion occurred */
142         else if ((dwCompLen== 0) && (selLength != 0))
143         {
144             if (dwCompStringLength)
145             {
146                 for (i = 0; i < byte_selection; i++)
147                 {
148                     if (byte_offset+byte_selection+i <
149                         dwCompStringLength)
150                     {
151                         CompositionString[byte_offset + i] =
152                         CompositionString[byte_offset + byte_selection + i];
153                     }
154                     else
155                         CompositionString[byte_offset + i] = 0;
156                 }
157                 /* clean up the end */
158                 dwCompStringLength -= byte_selection;
159
160                 i = dwCompStringLength;
161                 while (i < dwCompStringSize)
162                 {
163                     CompositionString[i++] = 0;
164                 }
165             }
166         }
167         else
168         {
169             int byte_expansion = byte_length - byte_selection;
170
171             if (byte_expansion + dwCompStringLength >= dwCompStringSize)
172             {
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);
183
184                 memset(&(CompositionString[dwCompStringSize]), 0,
185                         byte_expansion);
186
187                 dwCompStringSize += byte_expansion;
188             }
189
190             ptr_new =  ((LPBYTE)lpComp);
191             ptr_old = CompositionString + byte_offset + byte_selection;
192
193             dwCompStringLength += byte_expansion;
194
195             for (j=0,i = byte_offset; i < dwCompStringSize; i++)
196             {
197                 if (j < byte_length)
198                 {
199                     CompositionString[i] = ptr_new[j++];
200                 }
201                 else
202                 {
203                     if (ptr_old < CompositionString + dwCompStringSize)
204                     {
205                         CompositionString[i] = *ptr_old;
206                         ptr_old++;
207                             }
208                     else
209                         CompositionString[i] = 0;
210                 }
211             }
212         }
213
214         if (pImmSetCompositionString)
215             rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR,
216                                  (LPWSTR)CompositionString, dwCompStringLength,
217                                   NULL, 0);
218     }
219     else if ((dwIndex == GCS_RESULTSTR) && (lpComp) && (dwCompLen))
220     {
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);
226
227         if (pImmSetCompositionString)
228             rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR,
229                                  (LPWSTR)ResultString, dwResultStringSize,
230                                   NULL, 0);
231
232         if (pImmNotifyIME)
233             pImmNotifyIME((HIMC)FROM_IME, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
234     }
235
236     return rc;
237 }
238
239 void X11DRV_XIMLookupChars( const char *str, DWORD count )
240 {
241     DWORD dwOutput;
242     WCHAR wcOutput[64];
243     HWND focus;
244
245     dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, str, count, wcOutput, sizeof(wcOutput)/sizeof(WCHAR));
246
247     if (pImmAssociateContext && (focus = GetFocus()))
248         pImmAssociateContext(focus,root_context);
249
250     X11DRV_ImmSetInternalString(GCS_RESULTSTR,0,0,wcOutput,dwOutput);
251 }
252
253 static void X11DRV_ImmSetOpenStatus(BOOL fOpen)
254 {
255     if (fOpen == FALSE)
256     {
257         if (dwCompStringSize)
258             HeapFree(GetProcessHeap(),0,CompositionString);
259
260         dwCompStringSize = 0;
261         dwCompStringLength = 0;
262         CompositionString = NULL;
263
264         if (dwResultStringSize)
265             HeapFree(GetProcessHeap(),0,ResultString);
266
267         dwResultStringSize = 0;
268         ResultString = NULL;
269     }
270
271     if (pImmSetOpenStatus)
272         pImmSetOpenStatus((HIMC)FROM_IME,fOpen);
273 }
274
275 static int XIMPreEditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
276 {
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;
283 }
284
285 static void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
286 {
287     TRACE("PreeditDoneCallback %p\n",ic);
288     ximInComposeMode = FALSE;
289     X11DRV_ImmSetOpenStatus(FALSE);
290     dwPreeditPos = 0;
291 }
292
293 static void XIMPreEditDrawCallback(XIM ic, XPointer client_data,
294                                    XIMPreeditDrawCallbackStruct *P_DR)
295 {
296     DWORD dwOutput;
297     WCHAR wcOutput[64];
298
299     TRACE("PreEditDrawCallback %p\n",ic);
300
301     if (P_DR)
302     {
303         int sel = P_DR->chg_first;
304         int len = P_DR->chg_length;
305         if (P_DR->text)
306         {
307             if (! P_DR->text->encoding_is_wchar)
308             {
309                 TRACE("multibyte\n");
310                 dwOutput = MultiByteToWideChar(CP_UNIXCP, 0,
311                            P_DR->text->string.multi_byte, -1,
312                            wcOutput, 64);
313
314                 /* ignore null */
315                 dwOutput --;
316                 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, wcOutput, dwOutput);
317             }
318             else
319             {
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);
324             }
325         }
326         else
327             X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, NULL, 0);
328     }
329     TRACE("Finished\n");
330 }
331
332 static void XIMPreEditCaretCallback(XIC ic, XPointer client_data,
333                                     XIMPreeditCaretCallbackStruct *P_C)
334 {
335     TRACE("PreeditCaretCallback %p\n",ic);
336
337     if (P_C)
338     {
339         int pos = pImmGetCompositionString(root_context, GCS_CURSORPOS, NULL, 0);
340         TRACE("pos: %d\n", pos);
341         switch(P_C->direction)
342         {
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;
367         }
368         SendMessageW(((InputContextData*)root_context)->IMC.hWnd,
369                      EM_SETSEL, dwPreeditPos + pos, dwPreeditPos + pos);
370         P_C->position = pos;
371     }
372     TRACE("Finished\n");
373 }
374
375 void X11DRV_ForceXIMReset(HWND hwnd)
376 {
377     XIC ic = X11DRV_get_ic(hwnd);
378     if (ic)
379     {
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();
386     }
387 }
388
389 /***********************************************************************
390 *           X11DRV Ime creation
391 */
392 XIM X11DRV_SetupXIM(Display *display, const char *input_style)
393 {
394     XIMStyle ximStyleRequest, ximStyleCallback, ximStyleNone;
395     XIMStyles *ximStyles = NULL;
396     INT i;
397     XIM xim;
398
399     ximStyleRequest = STYLE_CALLBACK;
400
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;
407
408     wine_tsx11_lock();
409
410     if(!XSupportsLocale())
411     {
412         WARN("X does not support locale.\n");
413         goto err;
414     }
415     if(XSetLocaleModifiers("") == NULL)
416     {
417         WARN("Could not set locale modifiers.\n");
418         goto err;
419     }
420
421     xim = XOpenIM(display, NULL, NULL, NULL);
422     if (xim == NULL)
423     {
424         WARN("Could not open input method.\n");
425         goto err;
426     }
427
428     TRACE("X display of IM = %p\n", XDisplayOfIM(xim));
429     TRACE("Using %s locale of Input Method\n", XLocaleOfIM(xim));
430
431     XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL);
432     if (ximStyles == 0)
433     {
434         WARN("Could not find supported input style.\n");
435     }
436     else
437     {
438         TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles);
439
440         ximStyleRoot = 0;
441         ximStyleNone = 0;
442         ximStyleCallback = 0;
443
444         for (i = 0; i < ximStyles->count_styles; ++i)
445         {
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))
455             {
456                 ximStyle = ximStyleRequest;
457                 TRACE("Setting Style: ximStyle = ximStyleRequest\n");
458             }
459             else if (!ximStyleRoot &&(ximStyles->supported_styles[i] ==
460                      STYLE_ROOT))
461             {
462                 ximStyleRoot = STYLE_ROOT;
463                 TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n");
464             }
465             else if (!ximStyleCallback &&(ximStyles->supported_styles[i] ==
466                      STYLE_CALLBACK))
467             {
468                 ximStyleCallback = STYLE_CALLBACK;
469                 TRACE("Setting Style: ximStyleCallback = STYLE_CALLBACK\n");
470             }
471             else if (!ximStyleNone && (ximStyles->supported_styles[i] ==
472                      STYLE_NONE))
473             {
474                 TRACE("Setting Style: ximStyleNone = STYLE_NONE\n");
475                 ximStyleNone = STYLE_NONE;
476             }
477         }
478         XFree(ximStyles);
479
480         if (ximStyle == 0)
481             ximStyle = ximStyleRoot;
482
483         if (ximStyle == 0)
484             ximStyle = ximStyleNone;
485
486         if (ximStyleCallback == 0)
487         {
488             TRACE("No callback style avalable\n");
489             ximStyleCallback = ximStyle;
490         }
491
492     }
493
494     wine_tsx11_unlock();
495
496     if(!hImmDll)
497     {
498         LoadImmDll();
499
500         if (pImmCreateContext)
501         {
502             root_context = pImmCreateContext();
503             if (pImmAssociateContext)
504                 pImmAssociateContext(0,root_context);
505         }
506     }
507
508     return xim;
509
510 err:
511     wine_tsx11_unlock();
512     return NULL;
513 }
514
515
516 XIC X11DRV_CreateIC(XIM xim, Display *display, Window win)
517 {
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()));
527
528     wine_tsx11_lock();
529
530     /* use complex and slow XIC initialization method only for CJK */
531     if (langid != LANG_CHINESE &&
532         langid != LANG_JAPANESE &&
533         langid != LANG_KOREAN)
534     {
535         xic = XCreateIC(xim,
536                         XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
537                         XNClientWindow, win,
538                         XNFocusWindow, win,
539                         NULL);
540         wine_tsx11_unlock();
541         return xic;
542     }
543
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;
553
554     if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0)
555     {
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);
564     }
565     else
566     {
567         preedit = XVaCreateNestedList(0,
568                         XNPreeditStartCallback, &P_StartCB,
569                         XNPreeditDoneCallback, &P_DoneCB,
570                         XNPreeditDrawCallback, &P_DrawCB,
571                         XNPreeditCaretCallback, &P_CaretCB,
572                         NULL);
573
574         TRACE("preedit = %p\n", preedit);
575     }
576
577     if ((ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0)
578     {
579         status = XVaCreateNestedList(0,
580             NULL);
581         TRACE("status = %p\n", status);
582      }
583
584     if (preedit != NULL && status != NULL)
585     {
586         xic = XCreateIC(xim,
587               XNInputStyle, ximStyle,
588               XNPreeditAttributes, preedit,
589               XNStatusAttributes, status,
590               XNClientWindow, win,
591               XNFocusWindow, win,
592               NULL);
593      }
594     else if (preedit != NULL)
595     {
596         xic = XCreateIC(xim,
597               XNInputStyle, ximStyle,
598               XNPreeditAttributes, preedit,
599               XNClientWindow, win,
600               XNFocusWindow, win,
601               NULL);
602     }
603     else if (status != NULL)
604     {
605         xic = XCreateIC(xim,
606               XNInputStyle, ximStyle,
607               XNStatusAttributes, status,
608               XNClientWindow, win,
609               XNFocusWindow, win,
610               NULL);
611     }
612     else
613     {
614         xic = XCreateIC(xim,
615               XNInputStyle, ximStyle,
616               XNClientWindow, win,
617               XNFocusWindow, win,
618               NULL);
619     }
620
621     TRACE("xic = %p\n", xic);
622
623     if (preedit != NULL)
624         XFree(preedit);
625     if (status != NULL)
626         XFree(status);
627
628     wine_tsx11_unlock();
629
630     return xic;
631 }