/* * 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 #include #include #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; }