winex11.drv: Constify the pen dash data.
[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
65 static HMODULE hImmDll = NULL;
66 static HIMC (WINAPI *pImmAssociateContext)(HWND,HIMC);
67 static HIMC (WINAPI *pImmCreateContext)(void);
68 static VOID (WINAPI *pImmSetOpenStatus)(HIMC,BOOL);
69 static BOOL (WINAPI *pImmSetCompositionString)(HIMC, DWORD, LPWSTR,
70                                                DWORD, LPWSTR, DWORD);
71 static LONG (WINAPI *pImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD);
72 static VOID (WINAPI *pImmNotifyIME)(HIMC, DWORD, DWORD, DWORD);
73
74 /* WINE specific messages from the xim in x11drv level */
75
76 #define STYLE_OFFTHESPOT (XIMPreeditArea | XIMStatusArea)
77 #define STYLE_OVERTHESPOT (XIMPreeditPosition | XIMStatusNothing)
78 #define STYLE_ROOT (XIMPreeditNothing | XIMStatusNothing)
79 /* this uses all the callbacks to utilize full IME support */
80 #define STYLE_CALLBACK (XIMPreeditCallbacks | XIMStatusNothing)
81 /* inorder to enable deadkey support */
82 #define STYLE_NONE (XIMPreeditNothing | XIMStatusNothing)
83
84 /*
85  * here are the functions that sort of marshall calls into IMM32.DLL
86  */
87 static void LoadImmDll(void)
88 {
89     hImmDll = LoadLibraryA("imm32.dll");
90
91     pImmAssociateContext = (void *)GetProcAddress(hImmDll, "ImmAssociateContext");
92     if (!pImmAssociateContext)
93         WARN("IMM: pImmAssociateContext not found in DLL\n");
94
95     pImmCreateContext = (void *)GetProcAddress(hImmDll, "ImmCreateContext");
96     if (!pImmCreateContext)
97         WARN("IMM: pImmCreateContext not found in DLL\n");
98
99     pImmSetOpenStatus = (void *)GetProcAddress( hImmDll, "ImmSetOpenStatus");
100     if (!pImmSetOpenStatus)
101         WARN("IMM: pImmSetOpenStatus not found in DLL\n");
102
103     pImmSetCompositionString =(void *)GetProcAddress(hImmDll, "ImmSetCompositionStringW");
104
105     if (!pImmSetCompositionString)
106         WARN("IMM: pImmSetCompositionStringW not found in DLL\n");
107
108     pImmGetCompositionString =(void *)GetProcAddress(hImmDll, "ImmGetCompositionStringW");
109
110     if (!pImmGetCompositionString)
111         WARN("IMM: pImmGetCompositionStringW not found in DLL\n");
112
113     pImmNotifyIME = (void *)GetProcAddress( hImmDll, "ImmNotifyIME");
114
115     if (!pImmNotifyIME)
116         WARN("IMM: pImmNotifyIME not found in DLL\n");
117 }
118
119 static BOOL X11DRV_ImmSetInternalString(DWORD dwIndex, DWORD dwOffset,
120                                         DWORD selLength, LPWSTR lpComp, DWORD dwCompLen)
121 {
122     /* Composition strings are edited in chunks */
123     unsigned int byte_length = dwCompLen * sizeof(WCHAR);
124     unsigned int byte_offset = dwOffset * sizeof(WCHAR);
125     unsigned int byte_selection = selLength * sizeof(WCHAR);
126     BOOL rc = FALSE;
127
128     TRACE("( %i, %i, %d, %p, %d):\n", dwOffset, selLength, dwIndex, lpComp, dwCompLen );
129
130     if (dwIndex == GCS_COMPSTR)
131     {
132         unsigned int i,j;
133         LPBYTE ptr_new;
134         LPBYTE ptr_old;
135
136         if ((dwCompLen == 0) && (selLength == 0))
137         {
138             /* DO Nothing */
139         }
140         /* deletion occurred */
141         else if ((dwCompLen== 0) && (selLength != 0))
142         {
143             if (dwCompStringLength)
144             {
145                 for (i = 0; i < byte_selection; i++)
146                 {
147                     if (byte_offset+byte_selection+i <
148                         dwCompStringLength)
149                     {
150                         CompositionString[byte_offset + i] =
151                         CompositionString[byte_offset + byte_selection + i];
152                     }
153                     else
154                         CompositionString[byte_offset + i] = 0;
155                 }
156                 /* clean up the end */
157                 dwCompStringLength -= byte_selection;
158
159                 i = dwCompStringLength;
160                 while (i < dwCompStringSize)
161                 {
162                     CompositionString[i++] = 0;
163                 }
164             }
165         }
166         else
167         {
168             int byte_expansion = byte_length - byte_selection;
169
170             if (byte_expansion + dwCompStringLength >= dwCompStringSize)
171             {
172                 if (CompositionString)
173                     CompositionString =
174                         HeapReAlloc(GetProcessHeap(), 0,
175                                     CompositionString,
176                                     dwCompStringSize +
177                                     byte_expansion);
178                 else
179                      CompositionString =
180                         HeapAlloc(GetProcessHeap(), 0, dwCompStringSize +
181                                     byte_expansion);
182
183                 memset(&(CompositionString[dwCompStringSize]), 0,
184                         byte_expansion);
185
186                 dwCompStringSize += byte_expansion;
187             }
188
189             ptr_new =  ((LPBYTE)lpComp);
190             ptr_old = CompositionString + byte_offset + byte_selection;
191
192             dwCompStringLength += byte_expansion;
193
194             for (j=0,i = byte_offset; i < dwCompStringSize; i++)
195             {
196                 if (j < byte_length)
197                 {
198                     CompositionString[i] = ptr_new[j++];
199                 }
200                 else
201                 {
202                     if (ptr_old < CompositionString + dwCompStringSize)
203                     {
204                         CompositionString[i] = *ptr_old;
205                         ptr_old++;
206                             }
207                     else
208                         CompositionString[i] = 0;
209                 }
210             }
211         }
212
213         if (pImmSetCompositionString)
214             rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR,
215                                  (LPWSTR)CompositionString, dwCompStringLength,
216                                   NULL, 0);
217     }
218     else if ((dwIndex == GCS_RESULTSTR) && (lpComp) && (dwCompLen))
219     {
220         if (dwResultStringSize)
221             HeapFree(GetProcessHeap(),0,ResultString);
222         dwResultStringSize= byte_length;
223         ResultString= HeapAlloc(GetProcessHeap(),0,byte_length);
224         memcpy(ResultString,lpComp,byte_length);
225
226         if (pImmSetCompositionString)
227             rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR,
228                                  (LPWSTR)ResultString, dwResultStringSize,
229                                   NULL, 0);
230
231         if (pImmNotifyIME)
232             pImmNotifyIME((HIMC)FROM_IME, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
233     }
234
235     return rc;
236 }
237
238 void X11DRV_XIMLookupChars( const char *str, DWORD count )
239 {
240     DWORD dwOutput;
241     WCHAR wcOutput[64];
242     HWND focus;
243
244     dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, str, count, wcOutput, sizeof(wcOutput)/sizeof(WCHAR));
245
246     if (pImmAssociateContext && (focus = GetFocus()))
247         pImmAssociateContext(focus,root_context);
248
249     X11DRV_ImmSetInternalString(GCS_RESULTSTR,0,0,wcOutput,dwOutput);
250 }
251
252 static void X11DRV_ImmSetOpenStatus(BOOL fOpen)
253 {
254     if (fOpen == FALSE)
255     {
256         if (dwCompStringSize)
257             HeapFree(GetProcessHeap(),0,CompositionString);
258
259         dwCompStringSize = 0;
260         dwCompStringLength = 0;
261         CompositionString = NULL;
262
263         if (dwResultStringSize)
264             HeapFree(GetProcessHeap(),0,ResultString);
265
266         dwResultStringSize = 0;
267         ResultString = NULL;
268     }
269
270     if (pImmSetOpenStatus)
271         pImmSetOpenStatus((HIMC)FROM_IME,fOpen);
272 }
273
274 static int XIMPreEditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
275 {
276     TRACE("PreEditStartCallback %p\n",ic);
277     X11DRV_ImmSetOpenStatus(TRUE);
278     ximInComposeMode = TRUE;
279     return -1;
280 }
281
282 static void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
283 {
284     TRACE("PreeditDoneCallback %p\n",ic);
285     ximInComposeMode = FALSE;
286     X11DRV_ImmSetOpenStatus(FALSE);
287 }
288
289 static void XIMPreEditDrawCallback(XIM ic, XPointer client_data,
290                                    XIMPreeditDrawCallbackStruct *P_DR)
291 {
292     DWORD dwOutput;
293     WCHAR wcOutput[64];
294
295     TRACE("PreEditDrawCallback %p\n",ic);
296
297     if (P_DR)
298     {
299         int sel = P_DR->chg_first;
300         int len = P_DR->chg_length;
301         if (P_DR->text)
302         {
303             if (! P_DR->text->encoding_is_wchar)
304             {
305                 TRACE("multibyte\n");
306                 dwOutput = MultiByteToWideChar(CP_UNIXCP, 0,
307                            P_DR->text->string.multi_byte, -1,
308                            wcOutput, 64);
309
310                 /* ignore null */
311                 dwOutput --;
312                 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, wcOutput, dwOutput);
313             }
314             else
315             {
316                 FIXME("wchar PROBIBILY WRONG\n");
317                 X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len,
318                                              (LPWSTR)P_DR->text->string.wide_char,
319                                              P_DR->text->length);
320             }
321         }
322         else
323             X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, NULL, 0);
324     }
325     TRACE("Finished\n");
326 }
327
328 static void XIMPreEditCaretCallback(XIC ic, XPointer client_data,
329                                     XIMPreeditCaretCallbackStruct *P_C)
330 {
331     TRACE("PreeditCaretCallback %p\n",ic);
332
333     if (P_C)
334     {
335         int pos = pImmGetCompositionString(root_context, GCS_CURSORPOS, NULL, 0);
336         TRACE("pos: %d\n", pos);
337         switch(P_C->direction)
338         {
339             case XIMForwardChar:
340             case XIMForwardWord:
341                 pos++;
342                 break;
343             case XIMBackwardChar:
344             case XIMBackwardWord:
345                 pos--;
346                 break;
347             case XIMLineStart:
348                 pos = 0;
349                 break;
350             case XIMAbsolutePosition:
351                 pos = P_C->position;
352                 break;
353             case XIMDontChange:
354                 P_C->position = pos;
355                 return;
356             case XIMCaretUp:
357             case XIMCaretDown:
358             case XIMPreviousLine:
359             case XIMNextLine:
360             case XIMLineEnd:
361                 FIXME("Not implemented\n");
362                 break;
363         }
364         SendMessageW(((InputContextData*)root_context)->IMC.hWnd,
365                      EM_SETSEL, pos, pos);
366         P_C->position = pos;
367     }
368     TRACE("Finished\n");
369 }
370
371 void X11DRV_ForceXIMReset(HWND hwnd)
372 {
373     XIC ic = X11DRV_get_ic(hwnd);
374     if (ic)
375     {
376         char* leftover;
377         TRACE("Forcing Reset %p\n",ic);
378         wine_tsx11_lock();
379         leftover = XmbResetIC(ic);
380         XFree(leftover);
381         wine_tsx11_unlock();
382     }
383 }
384
385 /***********************************************************************
386 *           X11DRV Ime creation
387 */
388 XIM X11DRV_SetupXIM(Display *display, const char *input_style)
389 {
390     XIMStyle ximStyleRequest, ximStyleCallback, ximStyleNone;
391     XIMStyles *ximStyles = NULL;
392     INT i;
393     XIM xim;
394
395     ximStyleRequest = STYLE_CALLBACK;
396
397     if (!strcasecmp(input_style, "offthespot"))
398         ximStyleRequest = STYLE_OFFTHESPOT;
399     else if (!strcasecmp(input_style, "overthespot"))
400         ximStyleRequest = STYLE_OVERTHESPOT;
401     else if (!strcasecmp(input_style, "root"))
402         ximStyleRequest = STYLE_ROOT;
403
404     wine_tsx11_lock();
405
406     if(!XSupportsLocale())
407     {
408         WARN("X does not support locale.\n");
409         goto err;
410     }
411     if(XSetLocaleModifiers("") == NULL)
412     {
413         WARN("Could not set locale modifiers.\n");
414         goto err;
415     }
416
417     xim = XOpenIM(display, NULL, NULL, NULL);
418     if (xim == NULL)
419     {
420         WARN("Could not open input method.\n");
421         goto err;
422     }
423
424     TRACE("X display of IM = %p\n", XDisplayOfIM(xim));
425     TRACE("Using %s locale of Input Method\n", XLocaleOfIM(xim));
426
427     XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL);
428     if (ximStyles == 0)
429     {
430         WARN("Could not find supported input style.\n");
431     }
432     else
433     {
434         TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles);
435
436         ximStyleRoot = 0;
437         ximStyleNone = 0;
438         ximStyleCallback = 0;
439
440         for (i = 0; i < ximStyles->count_styles; ++i)
441         {
442             int style = ximStyles->supported_styles[i];
443             TRACE("ximStyles[%d] = %s%s%s%s%s\n", i,
444                         (style&XIMPreeditArea)?"XIMPreeditArea ":"",
445                         (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"",
446                         (style&XIMPreeditPosition)?"XIMPreeditPosition ":"",
447                         (style&XIMPreeditNothing)?"XIMPreeditNothing ":"",
448                         (style&XIMPreeditNone)?"XIMPreeditNone ":"");
449             if (!ximStyle && (ximStyles->supported_styles[i] ==
450                                 ximStyleRequest))
451             {
452                 ximStyle = ximStyleRequest;
453                 TRACE("Setting Style: ximStyle = ximStyleRequest\n");
454             }
455             else if (!ximStyleRoot &&(ximStyles->supported_styles[i] ==
456                      STYLE_ROOT))
457             {
458                 ximStyleRoot = STYLE_ROOT;
459                 TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n");
460             }
461             else if (!ximStyleCallback &&(ximStyles->supported_styles[i] ==
462                      STYLE_CALLBACK))
463             {
464                 ximStyleCallback = STYLE_CALLBACK;
465                 TRACE("Setting Style: ximStyleCallback = STYLE_CALLBACK\n");
466             }
467             else if (!ximStyleNone && (ximStyles->supported_styles[i] ==
468                      STYLE_NONE))
469             {
470                 TRACE("Setting Style: ximStyleNone = STYLE_NONE\n");
471                 ximStyleNone = STYLE_NONE;
472             }
473         }
474         XFree(ximStyles);
475
476         if (ximStyle == 0)
477             ximStyle = ximStyleRoot;
478
479         if (ximStyle == 0)
480             ximStyle = ximStyleNone;
481
482         if (ximStyleCallback == 0)
483         {
484             TRACE("No callback style avalable\n");
485             ximStyleCallback = ximStyle;
486         }
487
488     }
489
490     wine_tsx11_unlock();
491
492     if(!hImmDll)
493     {
494         LoadImmDll();
495
496         if (pImmCreateContext)
497         {
498             root_context = pImmCreateContext();
499             if (pImmAssociateContext)
500                 pImmAssociateContext(0,root_context);
501         }
502     }
503
504     return xim;
505
506 err:
507     wine_tsx11_unlock();
508     return NULL;
509 }
510
511
512 XIC X11DRV_CreateIC(XIM xim, Display *display, Window win)
513 {
514     XPoint spot = {0};
515     XVaNestedList preedit = NULL;
516     XVaNestedList status = NULL;
517     XIC xic;
518     XIMCallback P_StartCB;
519     XIMCallback P_DoneCB;
520     XIMCallback P_DrawCB;
521     XIMCallback P_CaretCB;
522     LANGID langid = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
523
524     wine_tsx11_lock();
525
526     /* use complex and slow XIC initialization method only for CJK */
527     if (langid != LANG_CHINESE &&
528         langid != LANG_JAPANESE &&
529         langid != LANG_KOREAN)
530     {
531         xic = XCreateIC(xim,
532                         XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
533                         XNClientWindow, win,
534                         XNFocusWindow, win,
535                         NULL);
536         wine_tsx11_unlock();
537         return xic;
538     }
539
540     /* create callbacks */
541     P_StartCB.client_data = NULL;
542     P_StartCB.callback = (XIMProc)XIMPreEditStartCallback;
543     P_DoneCB.client_data = NULL;
544     P_DoneCB.callback = (XIMProc)XIMPreEditDoneCallback;
545     P_DrawCB.client_data = NULL;
546     P_DrawCB.callback = (XIMProc)XIMPreEditDrawCallback;
547     P_CaretCB.client_data = NULL;
548     P_CaretCB.callback = (XIMProc)XIMPreEditCaretCallback;
549
550     if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0)
551     {
552         preedit = XVaCreateNestedList(0,
553                         XNSpotLocation, &spot,
554                         XNPreeditStartCallback, &P_StartCB,
555                         XNPreeditDoneCallback, &P_DoneCB,
556                         XNPreeditDrawCallback, &P_DrawCB,
557                         XNPreeditCaretCallback, &P_CaretCB,
558                         NULL);
559         TRACE("preedit = %p\n", preedit);
560     }
561     else
562     {
563         preedit = XVaCreateNestedList(0,
564                         XNPreeditStartCallback, &P_StartCB,
565                         XNPreeditDoneCallback, &P_DoneCB,
566                         XNPreeditDrawCallback, &P_DrawCB,
567                         XNPreeditCaretCallback, &P_CaretCB,
568                         NULL);
569
570         TRACE("preedit = %p\n", preedit);
571     }
572
573     if ((ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0)
574     {
575         status = XVaCreateNestedList(0,
576             NULL);
577         TRACE("status = %p\n", status);
578      }
579
580     if (preedit != NULL && status != NULL)
581     {
582         xic = XCreateIC(xim,
583               XNInputStyle, ximStyle,
584               XNPreeditAttributes, preedit,
585               XNStatusAttributes, status,
586               XNClientWindow, win,
587               XNFocusWindow, win,
588               NULL);
589      }
590     else if (preedit != NULL)
591     {
592         xic = XCreateIC(xim,
593               XNInputStyle, ximStyle,
594               XNPreeditAttributes, preedit,
595               XNClientWindow, win,
596               XNFocusWindow, win,
597               NULL);
598     }
599     else if (status != NULL)
600     {
601         xic = XCreateIC(xim,
602               XNInputStyle, ximStyle,
603               XNStatusAttributes, status,
604               XNClientWindow, win,
605               XNFocusWindow, win,
606               NULL);
607     }
608     else
609     {
610         xic = XCreateIC(xim,
611               XNInputStyle, ximStyle,
612               XNClientWindow, win,
613               XNFocusWindow, win,
614               NULL);
615     }
616
617     TRACE("xic = %p\n", xic);
618
619     if (preedit != NULL)
620         XFree(preedit);
621     if (status != NULL)
622         XFree(status);
623
624     wine_tsx11_unlock();
625
626     return xic;
627 }