diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 76a51257..496bc017 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1324,6 +1324,11 @@ extern "C" { * X11 specific [init hint](@ref GLFW_X11_XCB_VULKAN_SURFACE_hint). */ #define GLFW_X11_XCB_VULKAN_SURFACE 0x00052001 +/*! @brief X11 specific init hint. + * + * X11 specific [init hint](@ref GLFW_X11_ONTHESPOT_hint). + */ +#define GLFW_X11_ONTHESPOT 0x00052002 /*! @brief Wayland specific init hint. * * Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint). @@ -5249,6 +5254,9 @@ GLFWAPI void glfwSetPreeditCursorRectangle(GLFWwindow* window, int x, int y, int * * @param[in] window The window. * + * @remark @x11 Since over-the-spot style is used by default, you don't need + * to use this function. + * * @par Thread Safety * This function may only be called from the main thread. * @@ -5423,6 +5431,9 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * For more information about the callback parameters, see the * [function pointer type](@ref GLFWpreeditfun). * + * @remark @x11 Since over-the-spot style is used by default, you don't need + * to use this function. + * * @par Thread Safety * This function may only be called from the main thread. * @@ -5452,6 +5463,8 @@ GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun * For more information about the callback parameters, see the * [function pointer type](@ref GLFWimestatusfun). * + * @remark @x11 Don't support this function. The callback is not called. + * * @par Thread Safety * This function may only be called from the main thread. * diff --git a/src/init.c b/src/init.c index dbd5a900..6619b0ec 100644 --- a/src/init.c +++ b/src/init.c @@ -61,6 +61,7 @@ static _GLFWinitconfig _glfwInitHints = .x11 = { .xcbVulkanSurface = GLFW_TRUE, + .onTheSpotIMStyle = GLFW_FALSE }, .wl = { @@ -175,6 +176,29 @@ size_t _glfwEncodeUTF8(char* s, uint32_t codepoint) return count; } +// Decode a Unicode code point from a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +uint32_t _glfwDecodeUTF8(const char** s) +{ + uint32_t codepoint = 0, count = 0; + static const uint32_t offsets[] = + { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + do + { + codepoint = (codepoint << 6) + (unsigned char) **s; + (*s)++; + count++; + } while ((**s & 0xc0) == 0x80); + + assert(count <= 6); + return codepoint - offsets[count - 1]; +} + // Splits and translates a text/uri-list into separate file paths // NOTE: This function destroys the provided string // @@ -459,6 +483,9 @@ GLFWAPI void glfwInitHint(int hint, int value) case GLFW_X11_XCB_VULKAN_SURFACE: _glfwInitHints.x11.xcbVulkanSurface = value; return; + case GLFW_X11_ONTHESPOT: + _glfwInitHints.x11.onTheSpotIMStyle = value; + return; case GLFW_WAYLAND_LIBDECOR: _glfwInitHints.wl.libdecorMode = value; return; diff --git a/src/internal.h b/src/internal.h index 93722387..893f780c 100644 --- a/src/internal.h +++ b/src/internal.h @@ -385,6 +385,7 @@ struct _GLFWinitconfig } ns; struct { GLFWbool xcbVulkanSurface; + GLFWbool onTheSpotIMStyle; } x11; struct { int libdecorMode; @@ -1036,6 +1037,7 @@ void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); +uint32_t _glfwDecodeUTF8(const char** s); char** _glfwParseUriList(char* text, int* count); char* _glfw_strdup(const char* source); diff --git a/src/x11_init.c b/src/x11_init.c index 0ccf9454..1268296f 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -445,9 +445,14 @@ static GLFWbool hasUsableInputMethodStyle(void) if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL) return GLFW_FALSE; + if (_glfw.hints.init.x11.onTheSpotIMStyle) + _glfw.x11.imStyle = STYLE_ONTHESPOT; + else + _glfw.x11.imStyle = STYLE_OVERTHESPOT; + for (unsigned int i = 0; i < styles->count_styles; i++) { - if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) + if (styles->supported_styles[i] == _glfw.x11.imStyle) { found = GLFW_TRUE; break; @@ -1455,6 +1460,8 @@ int _glfwInitX11(void) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetErrorHandler"); _glfw.x11.xlib.SetICFocus = (PFN_XSetICFocus) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetICFocus"); + _glfw.x11.xlib.SetICValues = (PFN_XSetICValues) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetICValues"); _glfw.x11.xlib.SetIMValues = (PFN_XSetIMValues) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetIMValues"); _glfw.x11.xlib.SetInputFocus = (PFN_XSetInputFocus) @@ -1485,6 +1492,8 @@ int _glfwInitX11(void) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnmapWindow"); _glfw.x11.xlib.UnsetICFocus = (PFN_XUnsetICFocus) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnsetICFocus"); + _glfw.x11.xlib.VaCreateNestedList = (PFN_XVaCreateNestedList) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XVaCreateNestedList"); _glfw.x11.xlib.VisualIDFromVisual = (PFN_XVisualIDFromVisual) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XVisualIDFromVisual"); _glfw.x11.xlib.WarpPointer = (PFN_XWarpPointer) @@ -1517,6 +1526,8 @@ int _glfwInitX11(void) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XrmUniqueQuark"); _glfw.x11.xlib.UnregisterIMInstantiateCallback = (PFN_XUnregisterIMInstantiateCallback) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnregisterIMInstantiateCallback"); + _glfw.x11.xlib.mbResetIC = (PFN_XmbResetIC) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XmbResetIC"); _glfw.x11.xlib.utf8LookupString = (PFN_Xutf8LookupString) _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "Xutf8LookupString"); _glfw.x11.xlib.utf8SetWMProperties = (PFN_Xutf8SetWMProperties) diff --git a/src/x11_platform.h b/src/x11_platform.h index e82c9f3c..4764dbb3 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -91,6 +91,9 @@ #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 #define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 +#define STYLE_OVERTHESPOT (XIMPreeditNothing | XIMStatusNothing) +#define STYLE_ONTHESPOT (XIMPreeditCallbacks | XIMStatusCallbacks) + typedef XID GLXWindow; typedef XID GLXDrawable; typedef struct __GLXFBConfig* GLXFBConfig; @@ -165,6 +168,7 @@ typedef Status (* PFN_XSendEvent)(Display*,Window,Bool,long,XEvent*); typedef int (* PFN_XSetClassHint)(Display*,Window,XClassHint*); typedef XErrorHandler (* PFN_XSetErrorHandler)(XErrorHandler); typedef void (* PFN_XSetICFocus)(XIC); +typedef char* (* PFN_XSetICValues)(XIC,...); typedef char* (* PFN_XSetIMValues)(XIM,...); typedef int (* PFN_XSetInputFocus)(Display*,Window,int,Time); typedef char* (* PFN_XSetLocaleModifiers)(const char*); @@ -180,6 +184,7 @@ typedef int (* PFN_XUndefineCursor)(Display*,Window); typedef int (* PFN_XUngrabPointer)(Display*,Time); typedef int (* PFN_XUnmapWindow)(Display*,Window); typedef void (* PFN_XUnsetICFocus)(XIC); +typedef XVaNestedList (* PFN_XVaCreateNestedList)(int,...); typedef VisualID (* PFN_XVisualIDFromVisual)(Visual*); typedef int (* PFN_XWarpPointer)(Display*,Window,Window,int,int,unsigned int,unsigned int,int,int); typedef void (* PFN_XkbFreeKeyboard)(XkbDescPtr,unsigned int,Bool); @@ -191,6 +196,7 @@ typedef KeySym (* PFN_XkbKeycodeToKeysym)(Display*,KeyCode,int,int); typedef Bool (* PFN_XkbQueryExtension)(Display*,int*,int*,int*,int*,int*); typedef Bool (* PFN_XkbSelectEventDetails)(Display*,unsigned int,unsigned int,unsigned long,unsigned long); typedef Bool (* PFN_XkbSetDetectableAutoRepeat)(Display*,Bool,Bool*); +typedef char* (* PFN_XmbResetIC)(XIC); typedef void (* PFN_XrmDestroyDatabase)(XrmDatabase); typedef Bool (* PFN_XrmGetResource)(XrmDatabase,const char*,const char*,char**,XrmValue*); typedef XrmDatabase (* PFN_XrmGetStringDatabase)(const char*); @@ -265,6 +271,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XSetClassHint _glfw.x11.xlib.SetClassHint #define XSetErrorHandler _glfw.x11.xlib.SetErrorHandler #define XSetICFocus _glfw.x11.xlib.SetICFocus +#define XSetICValues _glfw.x11.xlib.SetICValues #define XSetIMValues _glfw.x11.xlib.SetIMValues #define XSetInputFocus _glfw.x11.xlib.SetInputFocus #define XSetLocaleModifiers _glfw.x11.xlib.SetLocaleModifiers @@ -280,6 +287,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XUngrabPointer _glfw.x11.xlib.UngrabPointer #define XUnmapWindow _glfw.x11.xlib.UnmapWindow #define XUnsetICFocus _glfw.x11.xlib.UnsetICFocus +#define XVaCreateNestedList _glfw.x11.xlib.VaCreateNestedList #define XVisualIDFromVisual _glfw.x11.xlib.VisualIDFromVisual #define XWarpPointer _glfw.x11.xlib.WarpPointer #define XkbFreeKeyboard _glfw.x11.xkb.FreeKeyboard @@ -291,6 +299,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XkbQueryExtension _glfw.x11.xkb.QueryExtension #define XkbSelectEventDetails _glfw.x11.xkb.SelectEventDetails #define XkbSetDetectableAutoRepeat _glfw.x11.xkb.SetDetectableAutoRepeat +#define XmbResetIC _glfw.x11.xlib.mbResetIC #define XrmDestroyDatabase _glfw.x11.xrm.DestroyDatabase #define XrmGetResource _glfw.x11.xrm.GetResource #define XrmGetStringDatabase _glfw.x11.xrm.GetStringDatabase @@ -577,6 +586,8 @@ typedef struct _GLFWlibraryX11 XContext context; // XIM input method XIM im; + // XIM input method style + XIMStyle imStyle; // The previous X error handler, to be restored later XErrorHandler errorHandler; // Most recent error code received by X error handler @@ -722,6 +733,7 @@ typedef struct _GLFWlibraryX11 PFN_XSetClassHint SetClassHint; PFN_XSetErrorHandler SetErrorHandler; PFN_XSetICFocus SetICFocus; + PFN_XSetICValues SetICValues; PFN_XSetIMValues SetIMValues; PFN_XSetInputFocus SetInputFocus; PFN_XSetLocaleModifiers SetLocaleModifiers; @@ -737,9 +749,11 @@ typedef struct _GLFWlibraryX11 PFN_XUngrabPointer UngrabPointer; PFN_XUnmapWindow UnmapWindow; PFN_XUnsetICFocus UnsetICFocus; + PFN_XVaCreateNestedList VaCreateNestedList; PFN_XVisualIDFromVisual VisualIDFromVisual; PFN_XWarpPointer WarpPointer; PFN_XUnregisterIMInstantiateCallback UnregisterIMInstantiateCallback; + PFN_XmbResetIC mbResetIC; PFN_Xutf8LookupString utf8LookupString; PFN_Xutf8SetWMProperties utf8SetWMProperties; } xlib; diff --git a/src/x11_window.c b/src/x11_window.c index 1d00fb7c..32d836e6 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -410,29 +410,6 @@ static void updateWindowMode(_GLFWwindow* window) } } -// Decode a Unicode code point from a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -static uint32_t decodeUTF8(const char** s) -{ - uint32_t codepoint = 0, count = 0; - static const uint32_t offsets[] = - { - 0x00000000u, 0x00003080u, 0x000e2080u, - 0x03c82080u, 0xfa082080u, 0x82082080u - }; - - do - { - codepoint = (codepoint << 6) + (unsigned char) **s; - (*s)++; - count++; - } while ((**s & 0xc0) == 0x80); - - assert(count <= 6); - return codepoint - offsets[count - 1]; -} - // Convert the specified Latin-1 string to UTF-8 // static char* convertLatin1toUTF8(const char* source) @@ -553,197 +530,241 @@ static void enableCursor(_GLFWwindow* window) updateCursorImage(window); } -// TODO This callback is replaced by _createXIMPreeditCallbacks. Is there a possibility that this clearing process is necessary? // Clear its handle when the input context has been destroyed -// static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData) -// { -// _GLFWwindow* window = (_GLFWwindow*) clientData; -// window->x11.ic = NULL; -// } - -// Update cursor position to decide candidate window -static void _ximChangeCursorPosition(XIC xic, _GLFWwindow* window) +// +static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData) { - XVaNestedList preedit_attr; - XPoint spot; - - spot.x = window->preeditCursorPosX; - spot.y = window->preeditCursorPosY + window->preeditCursorHeight; - preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); - XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL); - XFree(preedit_attr); + _GLFWwindow* window = (_GLFWwindow*) clientData; + window->x11.ic = NULL; } // IME Start callback (do nothing) +// static void _ximPreeditStartCallback(XIC xic, XPointer clientData, XPointer callData) { } // IME Done callback (do nothing) +// static void _ximPreeditDoneCallback(XIC xic, XPointer clientData, XPointer callData) { } // IME Draw callback -static void _ximPreeditDrawCallback(XIC xic, XPointer clientData, XIMPreeditDrawCallbackStruct *callData) +// When using the dafault style: STYLE_OVERTHESPOT, this is not used since applications +// don't need to display preedit texts. +// +static void _ximPreeditDrawCallback(XIC xic, XPointer clientData, XIMPreeditDrawCallbackStruct* callData) { - int i, j, length, ctext, rstart, rend; - XIMText* text; - const char* src; - unsigned int codePoint; - unsigned int* preeditText; - XIMFeedback f; - _GLFWwindow* window = (_GLFWwindow*)clientData; + _GLFWwindow* window = (_GLFWwindow*) clientData; + _GLFWpreedit* preedit = &window->preedit; - // keep cursor position to reduce API call - int cursorX = window->preeditCursorPosX; - int cursorY = window->preeditCursorPosY; - int cursorHeight = window->preeditCursorHeight; - - if (!callData->text) { + if (!callData->text) + { // preedit text is empty - window->ntext = 0; - window->nblocks = 0; - _glfwInputPreedit(window, 0); + preedit->textCount = 0; + preedit->blockSizesCount = 0; + preedit->focusedBlockIndex = 0; + preedit->caretIndex = 0; + _glfwInputPreedit(window); return; - } else { - text = callData->text; - length = callData->chg_length; - if (text->encoding_is_wchar) { - // wchar is not supported - return; - } - ctext = window->ctext; - while (ctext < length+1) { - ctext = (ctext == 0) ? 1 : ctext * 2; - } - if (ctext != window->ctext) { - preeditText = _glfw_realloc(window->preeditText, sizeof(unsigned int)*ctext); - if (preeditText == NULL) { + } + else if (callData->text->encoding_is_wchar) + { + // wchar is not supported + return; + } + else + { + XIMText* text = callData->text; + int textLen = preedit->textCount + text->length - callData->chg_length; + int textBufferCount = preedit->textBufferCount; + int i, j, rstart, rend; + const char* src; + + // realloc preedit text + while (textBufferCount < textLen + 1) + textBufferCount = (textBufferCount == 0) ? 1 : textBufferCount * 2; + if (textBufferCount != preedit->textBufferCount) + { + unsigned int* preeditText = _glfw_realloc(preedit->text, + sizeof(unsigned int) * textBufferCount); + if (preeditText == NULL) return; - } - window->preeditText = preeditText; - window->ctext = ctext; + + preedit->text = preeditText; + preedit->textBufferCount = textBufferCount; } - window->ntext = length; - window->preeditText[length] = 0; - if (window->cblocks == 0) { - window->preeditAttributeBlocks = _glfw_calloc(4, sizeof(int)); - window->cblocks = 4; + preedit->textCount = textLen; + preedit->text[textLen] = 0; + + // realloc block sizes + if (preedit->blockSizesBufferCount == 0) + { + preedit->blockSizes = _glfw_calloc(4, sizeof(int)); + preedit->blockSizesBufferCount = 4; } + + // store preedit text src = text->string.multi_byte; rend = 0; - rstart = length; - for (i = 0, j = 0; i < text->length; i++) { - codePoint = decodeUTF8(&src); - if (i < callData->chg_first || callData->chg_first+length < i) { + rstart = textLen; + for (i = 0, j = callData->chg_first; i < text->length; i++) + { + XIMFeedback f; + + if (i < callData->chg_first || callData->chg_first + textLen < i) continue; - } - window->preeditText[j++] = codePoint; + + preedit->text[j++] = _glfwDecodeUTF8(&src); f = text->feedback[i]; - if ((f & XIMReverse) || (f & XIMHighlight)) { + if ((f & XIMReverse) || (f & XIMHighlight)) + { rend = i; - if (i < rstart) { + if (i < rstart) rstart = i; - } } } - if (rstart == length) { - window->nblocks = 1; - window->preeditAttributeBlocks[0] = length; - window->preeditAttributeBlocks[1] = 0; - _glfwInputPreedit(window, 0); - } else if (rstart == 0) { - if (rend == length -1) { - window->nblocks = 1; - window->preeditAttributeBlocks[0] = length; - window->preeditAttributeBlocks[1] = 0; - _glfwInputPreedit(window, 0); - } else { - window->nblocks = 2; - window->preeditAttributeBlocks[0] = rend + 1; - window->preeditAttributeBlocks[1] = length - rend - 1; - window->preeditAttributeBlocks[2] = 0; - _glfwInputPreedit(window, 0); - } - } else if (rend == length -1) { - window->nblocks = 2; - window->preeditAttributeBlocks[0] = rstart; - window->preeditAttributeBlocks[1] = length - rstart; - window->preeditAttributeBlocks[2] = 0; - _glfwInputPreedit(window, 1); - } else { - window->nblocks = 3; - window->preeditAttributeBlocks[0] = rstart; - window->preeditAttributeBlocks[1] = rend - rstart + 1; - window->preeditAttributeBlocks[2] = length - rend - 1; - window->preeditAttributeBlocks[3] = 0; - _glfwInputPreedit(window, 1); + + // store block sizes + // TODO: It doesn't care callData->chg_first != 0 case although it's quite rare. + if (rstart == textLen) + { + preedit->blockSizesCount = 1; + preedit->blockSizes[0] = textLen; + preedit->blockSizes[1] = 0; + preedit->focusedBlockIndex = 0; + preedit->caretIndex = callData->caret; + _glfwInputPreedit(window); } - if ((cursorX != window->preeditCursorPosX) - || (cursorY != window->preeditCursorPosY) - || (cursorHeight != window->preeditCursorHeight)) { - _ximChangeCursorPosition(xic, window); + else if (rstart == 0) + { + if (rend == textLen -1) + { + preedit->blockSizesCount = 1; + preedit->blockSizes[0] = textLen; + preedit->blockSizes[1] = 0; + preedit->focusedBlockIndex = 0; + preedit->caretIndex = callData->caret; + _glfwInputPreedit(window); + } + else + { + preedit->blockSizesCount = 2; + preedit->blockSizes[0] = rend + 1; + preedit->blockSizes[1] = textLen - rend - 1; + preedit->blockSizes[2] = 0; + preedit->focusedBlockIndex = 0; + preedit->caretIndex = callData->caret; + _glfwInputPreedit(window); + } + } + else if (rend == textLen - 1) + { + preedit->blockSizesCount = 2; + preedit->blockSizes[0] = rstart; + preedit->blockSizes[1] = textLen - rstart; + preedit->blockSizes[2] = 0; + preedit->focusedBlockIndex = 1; + preedit->caretIndex = callData->caret; + _glfwInputPreedit(window); + } + else + { + preedit->blockSizesCount = 3; + preedit->blockSizes[0] = rstart; + preedit->blockSizes[1] = rend - rstart + 1; + preedit->blockSizes[2] = textLen - rend - 1; + preedit->blockSizes[3] = 0; + preedit->focusedBlockIndex = 1; + preedit->caretIndex = callData->caret; + _glfwInputPreedit(window); } } } // IME Caret callback (do nothing) +// static void _ximPreeditCaretCallback(XIC xic, XPointer clientData, XPointer callData) { } +// IME Status Start callback +// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status +// can not be taken. +// static void _ximStatusStartCallback(XIC xic, XPointer clientData, XPointer callData) { - _GLFWwindow* window = (_GLFWwindow*)clientData; + _GLFWwindow* window = (_GLFWwindow*) clientData; window->x11.imeFocus = GLFW_TRUE; } +// IME Status Done callback +// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status +// can not be taken. +// static void _ximStatusDoneCallback(XIC xic, XPointer clientData, XPointer callData) { - _GLFWwindow* window = (_GLFWwindow*)clientData; + _GLFWwindow* window = (_GLFWwindow*) clientData; window->x11.imeFocus = GLFW_FALSE; } +// IME Status Draw callback +// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status +// can not be taken. +// static void _ximStatusDrawCallback(XIC xic, XPointer clientData, XIMStatusDrawCallbackStruct* callData) { - _GLFWwindow* window = (_GLFWwindow*)clientData; + _GLFWwindow* window = (_GLFWwindow*) clientData; _glfwInputIMEStatus(window); } // Create XIM Preedit callback +// When using the dafault style: STYLE_OVERTHESPOT, this is not used since applications +// don't need to display preedit texts. +// static XVaNestedList _createXIMPreeditCallbacks(_GLFWwindow* window) { - window->x11.preeditStartCallback.client_data = (XPointer)window; - window->x11.preeditStartCallback.callback = (XIMProc)_ximPreeditStartCallback; - window->x11.preeditDoneCallback.client_data = (XPointer)window; - window->x11.preeditDoneCallback.callback = (XIMProc)_ximPreeditDoneCallback; - window->x11.preeditDrawCallback.client_data = (XPointer)window; - window->x11.preeditDrawCallback.callback = (XIMProc)_ximPreeditDrawCallback; - window->x11.preeditCaretCallback.client_data = (XPointer)window; - window->x11.preeditCaretCallback.callback = (XIMProc)_ximPreeditCaretCallback; - return XVaCreateNestedList (0, - XNPreeditStartCallback, &window->x11.preeditStartCallback.client_data, - XNPreeditDoneCallback, &window->x11.preeditDoneCallback.client_data, - XNPreeditDrawCallback, &window->x11.preeditDrawCallback.client_data, - XNPreeditCaretCallback, &window->x11.preeditCaretCallback.client_data, - NULL); + window->x11.preeditStartCallback.client_data = (XPointer) window; + window->x11.preeditStartCallback.callback = (XIMProc) _ximPreeditStartCallback; + window->x11.preeditDoneCallback.client_data = (XPointer) window; + window->x11.preeditDoneCallback.callback = (XIMProc) _ximPreeditDoneCallback; + window->x11.preeditDrawCallback.client_data = (XPointer) window; + window->x11.preeditDrawCallback.callback = (XIMProc) _ximPreeditDrawCallback; + window->x11.preeditCaretCallback.client_data = (XPointer) window; + window->x11.preeditCaretCallback.callback = (XIMProc) _ximPreeditCaretCallback; + return XVaCreateNestedList(0, + XNPreeditStartCallback, + &window->x11.preeditStartCallback.client_data, + XNPreeditDoneCallback, + &window->x11.preeditDoneCallback.client_data, + XNPreeditDrawCallback, + &window->x11.preeditDrawCallback.client_data, + XNPreeditCaretCallback, + &window->x11.preeditCaretCallback.client_data, + NULL); } // Create XIM status callback +// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status +// can not be taken. +// static XVaNestedList _createXIMStatusCallbacks(_GLFWwindow* window) { - window->x11.statusStartCallback.client_data = (XPointer)window; - window->x11.statusStartCallback.callback = (XIMProc)_ximStatusStartCallback; - window->x11.statusDoneCallback.client_data = (XPointer)window; - window->x11.statusDoneCallback.callback = (XIMProc)_ximStatusDoneCallback; - window->x11.statusDrawCallback.client_data = (XPointer)window; - window->x11.statusDrawCallback.callback = (XIMProc)_ximStatusDrawCallback; - return XVaCreateNestedList (0, - XNStatusStartCallback, &window->x11.statusStartCallback.client_data, - XNStatusDoneCallback, &window->x11.statusDoneCallback.client_data, - XNStatusDrawCallback, &window->x11.statusDrawCallback.client_data, - NULL); + window->x11.statusStartCallback.client_data = (XPointer) window; + window->x11.statusStartCallback.callback = (XIMProc) _ximStatusStartCallback; + window->x11.statusDoneCallback.client_data = (XPointer) window; + window->x11.statusDoneCallback.callback = (XIMProc) _ximStatusDoneCallback; + window->x11.statusDrawCallback.client_data = (XPointer) window; + window->x11.statusDrawCallback.callback = (XIMProc) _ximStatusDrawCallback; + return XVaCreateNestedList(0, + XNStatusStartCallback, + &window->x11.statusStartCallback.client_data, + XNStatusDoneCallback, + &window->x11.statusDoneCallback.client_data, + XNStatusDrawCallback, + &window->x11.statusDrawCallback.client_data, + NULL); } // Create the X11 window (and its colormap) @@ -1475,7 +1496,7 @@ static void processEvent(XEvent *event) const char* c = chars; chars[count] = '\0'; while (c - chars < count) - _glfwInputChar(window, decodeUTF8(&c), mods, plain); + _glfwInputChar(window, _glfwDecodeUTF8(&c), mods, plain); } if (chars != buffer) @@ -2106,26 +2127,60 @@ void _glfwPushSelectionToManagerX11(void) void _glfwCreateInputContextX11(_GLFWwindow* window) { - XVaNestedList preeditList = _createXIMPreeditCallbacks(window); - XVaNestedList statusList = _createXIMStatusCallbacks(window); + XIMCallback callback; + callback.callback = (XIMProc) inputContextDestroyCallback; + callback.client_data = (XPointer) window; - window->x11.ic = XCreateIC(_glfw.x11.im, - XNInputStyle, - XIMPreeditCallbacks | XIMStatusCallbacks, - XNClientWindow, - window->x11.handle, - XNFocusWindow, - window->x11.handle, - XNPreeditAttributes, - preeditList, - XNStatusAttributes, - statusList, - NULL); - - XFree(preeditList); - XFree(statusList); window->x11.imeFocus = GLFW_FALSE; + if (_glfw.x11.imStyle == STYLE_ONTHESPOT) + { + // On X11, on-the-spot style is unstable. + // Status callbacks are not called and the preedit cursor position + // can not be changed. + XVaNestedList preeditList = _createXIMPreeditCallbacks(window); + XVaNestedList statusList = _createXIMStatusCallbacks(window); + + window->x11.ic = XCreateIC(_glfw.x11.im, + XNInputStyle, + _glfw.x11.imStyle, + XNClientWindow, + window->x11.handle, + XNFocusWindow, + window->x11.handle, + XNPreeditAttributes, + preeditList, + XNStatusAttributes, + statusList, + XNDestroyCallback, + &callback, + NULL); + + XFree(preeditList); + XFree(statusList); + } + else if (_glfw.x11.imStyle == STYLE_OVERTHESPOT) + { + window->x11.ic = XCreateIC(_glfw.x11.im, + XNInputStyle, + _glfw.x11.imStyle, + XNClientWindow, + window->x11.handle, + XNFocusWindow, + window->x11.handle, + XNDestroyCallback, + &callback, + NULL); + } + else + { + // (XIMPreeditNothing | XIMStatusNothing) is considered as STYLE_OVERTHESPOT. + // So this branch should not be used now. + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create input context."); + return; + } + if (window->x11.ic) { XWindowAttributes attribs; @@ -3274,21 +3329,90 @@ const char* _glfwGetClipboardStringX11(void) return getSelectionString(_glfw.x11.CLIPBOARD); } +// When using STYLE_ONTHESPOT, this doesn't work and the cursor position can't be updated +// void _glfwUpdatePreeditCursorRectangleX11(_GLFWwindow* window) { + XVaNestedList preedit_attr; + XPoint spot; + _GLFWpreedit* preedit = &window->preedit; + + if (!window->x11.ic) + return; + + spot.x = preedit->cursorPosX + preedit->cursorWidth; + spot.y = preedit->cursorPosY + preedit->cursorHeight; + preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + XSetICValues(window->x11.ic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); } void _glfwResetPreeditTextX11(_GLFWwindow* window) { + XIC ic = window->x11.ic; + _GLFWpreedit* preedit = &window->preedit; + + /* restore conversion state after resetting ic later */ + XIMPreeditState preedit_state = XIMPreeditUnKnown; + XVaNestedList preedit_attr; + char* result; + + if (!ic) + return; + + // Can not manage IME in the case of over-the-spot. + if (_glfw.x11.imStyle == STYLE_OVERTHESPOT) + return; + + if (preedit->textCount == 0) + return; + + preedit_attr = XVaCreateNestedList(0, XNPreeditState, &preedit_state, NULL); + XGetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + result = XmbResetIC(ic); + + preedit_attr = XVaCreateNestedList(0, XNPreeditState, preedit_state, NULL); + XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + preedit->textCount = 0; + preedit->blockSizesCount = 0; + preedit->focusedBlockIndex = 0; + preedit->caretIndex = 0; + _glfwInputPreedit(window); + + XFree (result); } void _glfwSetIMEStatusX11(_GLFWwindow* window, int active) { + XIC ic = window->x11.ic; + + if (!ic) + return; + + // Can not manage IME in the case of over-the-spot. + if (_glfw.x11.imStyle == STYLE_OVERTHESPOT) + return; + + if (active) + XSetICFocus(ic); + else + XUnsetICFocus(ic); } int _glfwGetIMEStatusX11(_GLFWwindow* window) { - return GLFW_FALSE; + if (!window->x11.ic) + return GLFW_FALSE; + + // Can not manage IME in the case of over-the-spot. + if (_glfw.x11.imStyle == STYLE_OVERTHESPOT) + return GLFW_FALSE; + + return window->x11.imeFocus; } EGLenum _glfwGetEGLPlatformX11(EGLint** attribs) @@ -3489,46 +3613,6 @@ VkResult _glfwCreateWindowSurfaceX11(VkInstance instance, } } -void _glfwPlatformResetPreeditText(_GLFWwindow* window) { - XIC ic = window->x11.ic; - - /* restore conversion state after resetting ic later */ - XIMPreeditState preedit_state = XIMPreeditUnKnown; - XVaNestedList preedit_attr; - char* result; - - if (window->ntext == 0) - return; - - preedit_attr = XVaCreateNestedList(0, XNPreeditState, &preedit_state, NULL); - XGetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); - XFree(preedit_attr); - - result = XmbResetIC(ic); - - preedit_attr = XVaCreateNestedList(0, XNPreeditState, preedit_state, NULL); - XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); - XFree(preedit_attr); - - window->ntext = 0; - window->nblocks = 0; - _glfwInputPreedit(window, 0); - - XFree (result); -} - -void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active) { - XIC ic = window->x11.ic; - if (active) { - XSetICFocus(ic); - } else { - XUnsetICFocus(ic); - } -} - -int _glfwPlatformGetIMEStatus(_GLFWwindow* window) { - return window->x11.imeFocus; -} ////////////////////////////////////////////////////////////////////////// ////// GLFW native API //////