| /* | |
| * Functions for further XIM control | |
| * | |
| * Copyright 2003 CodeWeavers, Aric Stewart | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "wingdi.h" | |
| #include "winnls.h" | |
| #include "x11drv.h" | |
| #include "imm.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(x11drv); | |
| /* this must match with imm32/imm.c */ | |
| #define FROM_IME 0xcafe1337 | |
| BOOL ximInComposeMode=FALSE; | |
| static HIMC root_context; | |
| static XIMStyle ximStyle = 0; | |
| static XIMStyle ximStyleRoot = 0; | |
| /* moved here from imm32 for dll separation */ | |
| static DWORD dwCompStringLength = 0; | |
| static LPBYTE CompositionString = NULL; | |
| static DWORD dwCompStringSize = 0; | |
| static LPBYTE ResultString = NULL; | |
| static DWORD dwResultStringSize = 0; | |
| static HMODULE hImmDll = NULL; | |
| static HIMC (WINAPI *pImmAssociateContext)(HWND,HIMC); | |
| static HIMC (WINAPI *pImmCreateContext)(void); | |
| static VOID (WINAPI *pImmSetOpenStatus)(HIMC,BOOL); | |
| static BOOL (WINAPI *pImmSetCompositionString)(HIMC, DWORD, LPWSTR, | |
| DWORD, LPWSTR, DWORD); | |
| static VOID (WINAPI *pImmNotifyIME)(HIMC, DWORD, DWORD, DWORD); | |
| /* WINE specific messages from the xim in x11drv level */ | |
| #define STYLE_OFFTHESPOT (XIMPreeditArea | XIMStatusArea) | |
| #define STYLE_OVERTHESPOT (XIMPreeditPosition | XIMStatusNothing) | |
| #define STYLE_ROOT (XIMPreeditNothing | XIMStatusNothing) | |
| /* this uses all the callbacks to utilize full IME support */ | |
| #define STYLE_CALLBACK (XIMPreeditCallbacks | XIMStatusNothing) | |
| /* inorder to enable deadkey support */ | |
| #define STYLE_NONE (XIMPreeditNothing | XIMStatusNothing) | |
| /* | |
| * here are the functions that sort of marshall calls into IMM32.DLL | |
| */ | |
| static void LoadImmDll() | |
| { | |
| hImmDll = LoadLibraryA("imm32.dll"); | |
| pImmAssociateContext = (void *)GetProcAddress(hImmDll, "ImmAssociateContext"); | |
| if (!pImmAssociateContext) | |
| WARN("IMM: pImmAssociateContext not found in DLL\n"); | |
| pImmCreateContext = (void *)GetProcAddress(hImmDll, "ImmCreateContext"); | |
| if (!pImmCreateContext) | |
| WARN("IMM: pImmCreateContext not found in DLL\n"); | |
| pImmSetOpenStatus = (void *)GetProcAddress( hImmDll, "ImmSetOpenStatus"); | |
| if (!pImmSetOpenStatus) | |
| WARN("IMM: pImmSetOpenStatus not found in DLL\n"); | |
| pImmSetCompositionString =(void *)GetProcAddress(hImmDll, "ImmSetCompositionStringW"); | |
| if (!pImmSetCompositionString) | |
| WARN("IMM: pImmSetCompositionStringW not found in DLL\n"); | |
| pImmNotifyIME = (void *)GetProcAddress( hImmDll, "ImmNotifyIME"); | |
| if (!pImmNotifyIME) | |
| WARN("IMM: pImmNotifyIME not found in DLL\n"); | |
| } | |
| static BOOL X11DRV_ImmSetInternalString(DWORD dwIndex, DWORD dwOffset, | |
| DWORD selLength, LPWSTR lpComp, DWORD dwCompLen) | |
| { | |
| /* Composition strings are edited in chunks */ | |
| unsigned int byte_length = dwCompLen * sizeof(WCHAR); | |
| unsigned int byte_offset = dwOffset * sizeof(WCHAR); | |
| unsigned int byte_selection = selLength * sizeof(WCHAR); | |
| BOOL rc = FALSE; | |
| TRACE("( %li, %li, %ld, %p, %ld):\n", dwOffset, selLength, dwIndex, lpComp, | |
| dwCompLen ); | |
| if (dwIndex == GCS_COMPSTR) | |
| { | |
| unsigned int i,j; | |
| LPBYTE ptr_new; | |
| LPBYTE ptr_old; | |
| if ((dwCompLen == 0) && (selLength == 0)) | |
| { | |
| /* DO Nothing */ | |
| } | |
| /* deletion occurred */ | |
| else if ((dwCompLen== 0) && (selLength != 0)) | |
| { | |
| if (dwCompStringLength) | |
| { | |
| for (i = 0; i < byte_selection; i++) | |
| { | |
| if (byte_offset+byte_selection+i < | |
| dwCompStringLength) | |
| { | |
| CompositionString[byte_offset + i] = | |
| CompositionString[byte_offset + byte_selection + i]; | |
| } | |
| else | |
| CompositionString[byte_offset + i] = 0; | |
| } | |
| /* clean up the end */ | |
| dwCompStringLength -= byte_selection; | |
| i = dwCompStringLength; | |
| while (i < dwCompStringSize) | |
| { | |
| CompositionString[i++] = 0; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| int byte_expansion = byte_length - byte_selection; | |
| if (byte_expansion + dwCompStringLength >= dwCompStringSize) | |
| { | |
| if (CompositionString) | |
| CompositionString = | |
| HeapReAlloc(GetProcessHeap(), 0, | |
| CompositionString, | |
| dwCompStringSize + | |
| byte_expansion); | |
| else | |
| CompositionString = | |
| HeapAlloc(GetProcessHeap(), 0, dwCompStringSize + | |
| byte_expansion); | |
| memset(&(CompositionString[dwCompStringSize]), byte_expansion, | |
| 0); | |
| dwCompStringSize += byte_expansion; | |
| } | |
| ptr_new = ((LPBYTE)lpComp); | |
| ptr_old = CompositionString + byte_offset + byte_selection; | |
| dwCompStringLength += byte_expansion; | |
| for (j=0,i = byte_offset; i < dwCompStringSize; i++) | |
| { | |
| if (j < byte_length) | |
| { | |
| CompositionString[i] = ptr_new[j++]; | |
| } | |
| else | |
| { | |
| if (ptr_old < CompositionString + dwCompStringSize) | |
| { | |
| CompositionString[i] = *ptr_old; | |
| ptr_old++; | |
| } | |
| else | |
| CompositionString[i] = 0; | |
| } | |
| } | |
| } | |
| if (pImmSetCompositionString) | |
| rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR, | |
| (LPWSTR)CompositionString, dwCompStringLength, | |
| NULL, 0); | |
| } | |
| else if ((dwIndex == GCS_RESULTSTR) && (lpComp) && (dwCompLen)) | |
| { | |
| if (dwResultStringSize) | |
| HeapFree(GetProcessHeap(),0,ResultString); | |
| dwResultStringSize= byte_length; | |
| ResultString= HeapAlloc(GetProcessHeap(),0,byte_length); | |
| memcpy(ResultString,lpComp,byte_length); | |
| if (pImmSetCompositionString) | |
| rc = pImmSetCompositionString((HIMC)FROM_IME, SCS_SETSTR, | |
| (LPWSTR)ResultString, dwResultStringSize, | |
| NULL, 0); | |
| if (pImmNotifyIME) | |
| pImmNotifyIME((HIMC)FROM_IME, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); | |
| } | |
| return rc; | |
| } | |
| void X11DRV_XIMLookupChars( const char *str, DWORD count ) | |
| { | |
| DWORD dwOutput; | |
| WCHAR wcOutput[64]; | |
| HWND focus; | |
| dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, str, count, wcOutput, sizeof(wcOutput)); | |
| if (pImmAssociateContext && (focus = GetFocus())) | |
| pImmAssociateContext(focus,root_context); | |
| X11DRV_ImmSetInternalString(GCS_RESULTSTR,0,0,wcOutput,dwOutput); | |
| } | |
| static void X11DRV_ImmSetOpenStatus(BOOL fOpen) | |
| { | |
| if (fOpen == FALSE) | |
| { | |
| if (dwCompStringSize) | |
| HeapFree(GetProcessHeap(),0,CompositionString); | |
| dwCompStringSize = 0; | |
| dwCompStringLength = 0; | |
| CompositionString = NULL; | |
| if (dwResultStringSize) | |
| HeapFree(GetProcessHeap(),0,ResultString); | |
| dwResultStringSize = 0; | |
| ResultString = NULL; | |
| } | |
| if (pImmSetOpenStatus) | |
| pImmSetOpenStatus((HIMC)FROM_IME,fOpen); | |
| } | |
| static int XIMPreEditStartCallback(XIC ic, XPointer client_data, XPointer call_data) | |
| { | |
| TRACE("PreEditStartCallback %p\n",ic); | |
| X11DRV_ImmSetOpenStatus(TRUE); | |
| ximInComposeMode = TRUE; | |
| return -1; | |
| } | |
| static void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_data) | |
| { | |
| TRACE("PreeditDoneCallback %p\n",ic); | |
| ximInComposeMode = FALSE; | |
| X11DRV_ImmSetOpenStatus(FALSE); | |
| } | |
| static void XIMPreEditDrawCallback(XIM ic, XPointer client_data, | |
| XIMPreeditDrawCallbackStruct *P_DR) | |
| { | |
| DWORD dwOutput; | |
| WCHAR wcOutput[64]; | |
| TRACE("PreEditDrawCallback %p\n",ic); | |
| if (P_DR) | |
| { | |
| int sel = P_DR->chg_first; | |
| int len = P_DR->chg_length; | |
| if (P_DR->text) | |
| { | |
| if (! P_DR->text->encoding_is_wchar) | |
| { | |
| TRACE("multibyte\n"); | |
| dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, | |
| P_DR->text->string.multi_byte, -1, | |
| wcOutput, 64); | |
| /* ignore null */ | |
| dwOutput --; | |
| X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, wcOutput, dwOutput); | |
| } | |
| else | |
| { | |
| FIXME("wchar PROBIBILY WRONG\n"); | |
| X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, | |
| (LPWSTR)P_DR->text->string.wide_char, | |
| P_DR->text->length); | |
| } | |
| } | |
| else | |
| X11DRV_ImmSetInternalString (GCS_COMPSTR, sel, len, NULL, 0); | |
| } | |
| TRACE("Finished\n"); | |
| } | |
| static void XIMPreEditCaretCallback(XIC ic, XPointer client_data, | |
| XIMPreeditCaretCallbackStruct *P_C) | |
| { | |
| FIXME("PreeditCaretCalback %p\n",ic); | |
| } | |
| void X11DRV_ForceXIMReset(HWND hwnd) | |
| { | |
| XIC ic = X11DRV_get_ic(hwnd); | |
| if (ic) | |
| { | |
| char* leftover; | |
| TRACE("Forcing Reset %p\n",ic); | |
| wine_tsx11_lock(); | |
| leftover = XmbResetIC(ic); | |
| XFree(leftover); | |
| wine_tsx11_unlock(); | |
| } | |
| } | |
| /*********************************************************************** | |
| * X11DRV Ime creation | |
| */ | |
| XIM X11DRV_SetupXIM(Display *display, const char *input_style) | |
| { | |
| XIMStyle ximStyleRequest, ximStyleCallback, ximStyleNone; | |
| XIMStyles *ximStyles = NULL; | |
| INT i; | |
| XIM xim; | |
| ximStyleRequest = STYLE_CALLBACK; | |
| if (!strcasecmp(input_style, "offthespot")) | |
| ximStyleRequest = STYLE_OFFTHESPOT; | |
| else if (!strcasecmp(input_style, "overthespot")) | |
| ximStyleRequest = STYLE_OVERTHESPOT; | |
| else if (!strcasecmp(input_style, "root")) | |
| ximStyleRequest = STYLE_ROOT; | |
| wine_tsx11_lock(); | |
| if(!XSupportsLocale()) | |
| { | |
| WARN("X does not support locale.\n"); | |
| goto err; | |
| } | |
| if(XSetLocaleModifiers("") == NULL) | |
| { | |
| WARN("Could not set locale modifiers.\n"); | |
| goto err; | |
| } | |
| xim = XOpenIM(display, NULL, NULL, NULL); | |
| if (xim == NULL) | |
| { | |
| WARN("Could not open input method.\n"); | |
| goto err; | |
| } | |
| TRACE("X display of IM = %p\n", XDisplayOfIM(xim)); | |
| TRACE("Using %s locale of Input Method\n", XLocaleOfIM(xim)); | |
| XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL); | |
| if (ximStyles == 0) | |
| { | |
| WARN("Could not find supported input style.\n"); | |
| } | |
| else | |
| { | |
| TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles); | |
| ximStyleRoot = 0; | |
| ximStyleNone = 0; | |
| ximStyleCallback = 0; | |
| for (i = 0; i < ximStyles->count_styles; ++i) | |
| { | |
| int style = ximStyles->supported_styles[i]; | |
| TRACE("ximStyles[%d] = %s%s%s%s%s\n", i, | |
| (style&XIMPreeditArea)?"XIMPreeditArea ":"", | |
| (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"", | |
| (style&XIMPreeditPosition)?"XIMPreeditPosition ":"", | |
| (style&XIMPreeditNothing)?"XIMPreeditNothing ":"", | |
| (style&XIMPreeditNone)?"XIMPreeditNone ":""); | |
| if (!ximStyle && (ximStyles->supported_styles[i] == | |
| ximStyleRequest)) | |
| { | |
| ximStyle = ximStyleRequest; | |
| TRACE("Setting Style: ximStyle = ximStyleRequest\n"); | |
| } | |
| else if (!ximStyleRoot &&(ximStyles->supported_styles[i] == | |
| STYLE_ROOT)) | |
| { | |
| ximStyleRoot = STYLE_ROOT; | |
| TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n"); | |
| } | |
| else if (!ximStyleCallback &&(ximStyles->supported_styles[i] == | |
| STYLE_CALLBACK)) | |
| { | |
| ximStyleCallback = STYLE_CALLBACK; | |
| TRACE("Setting Style: ximStyleCallback = STYLE_CALLBACK\n"); | |
| } | |
| else if (!ximStyleNone && (ximStyles->supported_styles[i] == | |
| STYLE_NONE)) | |
| { | |
| TRACE("Setting Style: ximStyleNone = STYLE_NONE\n"); | |
| ximStyleNone = STYLE_NONE; | |
| } | |
| } | |
| XFree(ximStyles); | |
| if (ximStyle == 0) | |
| ximStyle = ximStyleRoot; | |
| if (ximStyle == 0) | |
| ximStyle = ximStyleNone; | |
| if (ximStyleCallback == 0) | |
| { | |
| TRACE("No callback style avalable\n"); | |
| ximStyleCallback = ximStyle; | |
| } | |
| } | |
| wine_tsx11_unlock(); | |
| LoadImmDll(); | |
| if (pImmCreateContext) | |
| { | |
| root_context = pImmCreateContext(); | |
| if (pImmAssociateContext) | |
| pImmAssociateContext(0,root_context); | |
| } | |
| return xim; | |
| err: | |
| wine_tsx11_unlock(); | |
| return NULL; | |
| } | |
| XIC X11DRV_CreateIC(XIM xim, Display *display, Window win) | |
| { | |
| XFontSet fontSet; | |
| char **list; | |
| int count; | |
| XPoint spot = {0}; | |
| XVaNestedList preedit = NULL; | |
| XVaNestedList status = NULL; | |
| XIC xic; | |
| XIMCallback P_StartCB; | |
| XIMCallback P_DoneCB; | |
| XIMCallback P_DrawCB; | |
| XIMCallback P_CaretCB; | |
| LANGID langid = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())); | |
| wine_tsx11_lock(); | |
| /* use complex and slow XIC initialization method only for CJK */ | |
| if (langid != LANG_CHINESE && | |
| langid != LANG_JAPANESE && | |
| langid != LANG_KOREAN) | |
| { | |
| xic = XCreateIC(xim, | |
| XNInputStyle, XIMPreeditNothing | XIMStatusNothing, | |
| XNClientWindow, win, | |
| XNFocusWindow, win, | |
| 0); | |
| wine_tsx11_unlock(); | |
| return xic; | |
| } | |
| fontSet = XCreateFontSet(display, | |
| "*", /*FIXME*/ | |
| &list, &count, NULL); | |
| TRACE("ximFontSet = 0x%x\n", (unsigned int) fontSet); | |
| TRACE("list = 0x%x, count = %d\n", (unsigned int) list, count); | |
| if (list != NULL) | |
| { | |
| int i; | |
| for (i = 0; i < count; ++i) | |
| { | |
| TRACE("list[%d] = %s\n", i, list[i]); | |
| } | |
| XFreeStringList(list); | |
| } | |
| /* create callbacks */ | |
| P_StartCB.client_data = NULL; | |
| P_StartCB.callback = (XIMProc)XIMPreEditStartCallback; | |
| P_DoneCB.client_data = NULL; | |
| P_DoneCB.callback = (XIMProc)XIMPreEditDoneCallback; | |
| P_DrawCB.client_data = NULL; | |
| P_DrawCB.callback = (XIMProc)XIMPreEditDrawCallback; | |
| P_CaretCB.client_data = NULL; | |
| P_CaretCB.callback = (XIMProc)XIMPreEditCaretCallback; | |
| if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0) | |
| { | |
| preedit = XVaCreateNestedList(0, | |
| XNFontSet, fontSet, | |
| XNSpotLocation, &spot, | |
| XNPreeditStartCallback, &P_StartCB, | |
| XNPreeditDoneCallback, &P_DoneCB, | |
| XNPreeditDrawCallback, &P_DrawCB, | |
| XNPreeditCaretCallback, &P_CaretCB, | |
| NULL); | |
| TRACE("preedit = 0x%x\n", (unsigned int) preedit); | |
| } | |
| else | |
| { | |
| preedit = XVaCreateNestedList(0, | |
| XNPreeditStartCallback, &P_StartCB, | |
| XNPreeditDoneCallback, &P_DoneCB, | |
| XNPreeditDrawCallback, &P_DrawCB, | |
| XNPreeditCaretCallback, &P_CaretCB, | |
| NULL); | |
| TRACE("preedit = 0x%x\n", (unsigned int) preedit); | |
| } | |
| if ((ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0) | |
| { | |
| status = XVaCreateNestedList(0, | |
| XNFontSet, fontSet, | |
| NULL); | |
| TRACE("status = 0x%x\n", (unsigned int) status); | |
| } | |
| if (preedit != NULL && status != NULL) | |
| { | |
| xic = XCreateIC(xim, | |
| XNInputStyle, ximStyle, | |
| XNPreeditAttributes, preedit, | |
| XNStatusAttributes, status, | |
| XNClientWindow, win, | |
| XNFocusWindow, win, | |
| NULL); | |
| } | |
| else if (preedit != NULL) | |
| { | |
| xic = XCreateIC(xim, | |
| XNInputStyle, ximStyle, | |
| XNPreeditAttributes, preedit, | |
| XNClientWindow, win, | |
| XNFocusWindow, win, | |
| NULL); | |
| } | |
| else if (status != NULL) | |
| { | |
| xic = XCreateIC(xim, | |
| XNInputStyle, ximStyle, | |
| XNStatusAttributes, status, | |
| XNClientWindow, win, | |
| XNFocusWindow, win, | |
| NULL); | |
| } | |
| else | |
| { | |
| xic = XCreateIC(xim, | |
| XNInputStyle, ximStyle, | |
| XNClientWindow, win, | |
| XNFocusWindow, win, | |
| NULL); | |
| } | |
| TRACE("xic = 0x%x\n", (unsigned int) xic); | |
| if (preedit != NULL) | |
| XFree(preedit); | |
| if (status != NULL) | |
| XFree(status); | |
| wine_tsx11_unlock(); | |
| return xic; | |
| } |