From 72b43022d4b8ca24d0ed2bcd9d9ad18f43ab1905 Mon Sep 17 00:00:00 2001 From: Daijiro Fukuda Date: Thu, 15 Dec 2022 12:01:14 +0900 Subject: [PATCH] Win32: Support IME This commit re-organizes 9d9af132610829f295c34ceb81b17af8b567b76f. * Use dynamic load for Imm32. * Generalize platform-specific features to _GLFWplatform. * Add caret-position info to preedit-callback. * Add cursorWidth to preeditCursor and related APIs. * Handle UTF16 data correctly. * Handle GCS_RESULTSTR so that committed texts are processed correctly. * Handle WM_IME_ENDCOMPOSITION to clear preedit. * Handle WM_IME_SETCONTEXT. * https://learn.microsoft.com/en-us/windows/win32/intl/wm-ime-setcontext#remarks * Refactor code shapes and variable names. Co-authored-by: Takuro Ashie --- include/GLFW/glfw3.h | 103 ++++++---- src/CMakeLists.txt | 1 - src/cocoa_init.m | 4 + src/cocoa_platform.h | 5 + src/cocoa_window.m | 17 ++ src/input.c | 62 ++++-- src/internal.h | 36 ++-- src/null_init.c | 4 + src/null_platform.h | 5 + src/null_window.c | 17 ++ src/win32_init.c | 28 +++ src/win32_platform.h | 39 ++++ src/win32_window.c | 437 ++++++++++++++++++++++++++++++------------- src/window.c | 13 +- src/wl_init.c | 4 + src/wl_platform.h | 5 + src/wl_window.c | 17 ++ src/x11_init.c | 4 + src/x11_platform.h | 5 + src/x11_window.c | 17 ++ 20 files changed, 619 insertions(+), 204 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index ce2853f8..648c491f 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1950,27 +1950,34 @@ typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); */ typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); -/*! @brief The function signature for preedit callbacks. +/*! @brief The function pointer type for preedit callbacks. * - * This is the function signature for preedit callback functions. + * This is the function pointer type for preedit callback functions. * * @param[in] window The window that received the event. - * @param[in] length Preedit string length. - * @param[in] string Preedit string. - * @param[in] count Attributed block count. - * @param[in] blocksizes List of attributed block size. - * @param[in] focusedblock Focused block index. + * @param[in] preedit_count Preedit string count. + * @param[in] preedit_string Preedit string. + * @param[in] block_count Attributed block count. + * @param[in] block_sizes List of attributed block size. + * @param[in] focused_block Focused block index. + * @param[in] caret Caret position. * * @sa @ref preedit * @sa glfwSetPreeditCallback * * @ingroup input */ -typedef void (* GLFWpreeditfun)(GLFWwindow*,int,unsigned int*,int,int*,int); +typedef void (* GLFWpreeditfun)(GLFWwindow* window, + int preedit_count, + unsigned int* preedit_string, + int block_count, + int* block_sizes, + int focused_block, + int caret); -/*! @brief The function signature for IME status change callbacks. +/*! @brief The function pointer type for IME status change callbacks. * - * This is the function signature for IME status change callback functions. + * This is the function pointer type for IME status change callback functions. * * @param[in] window The window that received the event. * @@ -1979,7 +1986,7 @@ typedef void (* GLFWpreeditfun)(GLFWwindow*,int,unsigned int*,int,int*,int); * * @ingroup monitor */ -typedef void (* GLFWimestatusfun)(GLFWwindow*); +typedef void (* GLFWimestatusfun)(GLFWwindow* window); /*! @brief The function pointer type for path drop callbacks. * @@ -4701,8 +4708,8 @@ GLFWAPI void glfwPostEmptyEvent(void); * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or - * `GLFW_RAW_MOUSE_MOTION`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS`, `GLFW_RAW_MOUSE_MOTION`, + * or `GLFW_IME`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. @@ -5204,14 +5211,16 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); */ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); -/*! @brief Retrieves the position of the text cursor relative to the client area of window. +/*! @brief Retrieves the area of the preedit text cursor. * - * This function returns position hint to decide the candidate window. + * This area is used to decide the position of the candidate window. + * The cursor position is relative to the window. * - * @param[in] window The window to set the text cursor for. - * @param[out] x The text cursor x position (relative position from window coordinates). - * @param[out] y The text cursor y position (relative position from window coordinates). - * @param[out] h The text cursor height. + * @param[in] window The window to set the preedit text cursor for. + * @param[out] x The preedit text cursor x position (relative position from window coordinates). + * @param[out] y The preedit text cursor y position (relative position from window coordinates). + * @param[out] w The preedit text cursor width. + * @param[out] h The preedit text cursor height. * * @par Thread Safety * This function may only be called from the main thread. @@ -5222,20 +5231,18 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * * @ingroup input */ -GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* window, int *x, int *y, int *h); +GLFWAPI void glfwGetPreeditCursorRectangle(GLFWwindow* window, int* x, int* y, int* w, int* h); -/*! @brief Notify the text cursor position to window system to decide the candidate window position. +/*! @brief Sets the area of the preedit text cursor. * - * This function teach position hint to decide the candidate window. The candidate window - * is a part of IME(Input Method Editor) and show several candidate strings. - * - * Windows sytems decide proper pisition from text cursor geometry. - * You should call this function in preedit callback. + * This area is used to decide the position of the candidate window. + * The cursor position is relative to the window. * * @param[in] window The window to set the text cursor for. - * @param[in] x The text cursor x position (relative position from window coordinates). - * @param[in] y The text cursor y position (relative position from window coordinates). - * @param[in] h The text cursor height. + * @param[in] x The preedit text cursor x position (relative position from window coordinates). + * @param[in] y The preedit text cursor y position (relative position from window coordinates). + * @param[in] w The preedit text cursor width. + * @param[in] h The preedit text cursor height. * * @par Thread Safety * This function may only be called from the main thread. @@ -5246,9 +5253,9 @@ GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* window, int *x, int *y, int *h) * * @ingroup input */ -GLFWAPI void glfwSetPreeditCursorPos(GLFWwindow* window, int x, int y, int h); +GLFWAPI void glfwSetPreeditCursorRectangle(GLFWwindow* window, int x, int y, int w, int h); -/*! @brief Reset IME input status. +/*! @brief Resets IME input status. * * This function resets IME's preedit text. * @@ -5402,11 +5409,11 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods /*! @brief Sets the preedit callback. * - * This function sets the preedit callback of the specified - * window, which is called when an IME is processing text before commited. + * This function sets the preedit callback of the specified + * window, which is called when an IME is processing text before committed. * * Callback receives relative position of input cursor inside preedit text and - * attributed text blocks. This callback is used for on-the-spot text editing + * attributed text blocks. This callback is used for on-the-spot text editing * with IME. * * @param[in] window The window whose callback to set. @@ -5415,6 +5422,19 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * @return The previously set callback, or `NULL` if no callback was set or an * error occurred. * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, + int preedit_count, + unsigned int* preedit_string, + int block_count, + int* block_sizes, + int focused_block, + int caret) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWpreeditfun). + * * @par Thread Safety * This function may only be called from the main thread. * @@ -5428,12 +5448,8 @@ GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun /*! @brief Sets the IME status change callback. * - * This function sets the preedit callback of the specified - * window, which is called when an IME is processing text before commited. - * - * Callback receives relative position of input cursor inside preedit text and - * attributed text blocks. This callback is used for on-the-spot text editing - * with IME. + * This function sets the IME status callback of the specified + * window, which is called when an IME is switched on and off. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -5441,6 +5457,13 @@ GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun * @return The previously set callback, or `NULL` if no callback was set or an * error occurred. * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWimestatusfun). + * * @par Thread Safety * This function may only be called from the main thread. * diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6aa38ad3..24f099fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,7 +140,6 @@ target_link_libraries(glfw PRIVATE Threads::Threads) if (GLFW_BUILD_WIN32) list(APPEND glfw_PKG_LIBS "-lgdi32") - list(APPEND glfw_LIBRARIES "imm32") endif() if (GLFW_BUILD_COCOA) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 90048bd8..efa51d94 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -474,6 +474,10 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) .getKeyScancode = _glfwGetKeyScancodeCocoa, .setClipboardString = _glfwSetClipboardStringCocoa, .getClipboardString = _glfwGetClipboardStringCocoa, + .updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleCocoa, + .resetPreeditText = _glfwResetPreeditTextCocoa, + .setIMEStatus = _glfwSetIMEStatusCocoa, + .getIMEStatus = _glfwGetIMEStatusCocoa, .initJoysticks = _glfwInitJoysticksCocoa, .terminateJoysticks = _glfwTerminateJoysticksCocoa, .pollJoystick = _glfwPollJoystickCocoa, diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index b4c9a6d7..1239a91e 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -268,6 +268,11 @@ void _glfwSetCursorCocoa(_GLFWwindow* window, _GLFWcursor* cursor); void _glfwSetClipboardStringCocoa(const char* string); const char* _glfwGetClipboardStringCocoa(void); +void _glfwUpdatePreeditCursorRectangleCocoa(_GLFWwindow* window); +void _glfwResetPreeditTextCocoa(_GLFWwindow* window); +void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active); +int _glfwGetIMEStatusCocoa(_GLFWwindow* window); + EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs); EGLNativeDisplayType _glfwGetEGLNativeDisplayCocoa(void); EGLNativeWindowType _glfwGetEGLNativeWindowCocoa(_GLFWwindow* window); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 8a09026b..1bb69871 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1909,6 +1909,23 @@ const char* _glfwGetClipboardStringCocoa(void) } // autoreleasepool } +void _glfwUpdatePreeditCursorRectangleCocoa(_GLFWwindow* window) +{ +} + +void _glfwResetPreeditTextCocoa(_GLFWwindow* window) +{ +} + +void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active) +{ +} + +int _glfwGetIMEStatusCocoa(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) diff --git a/src/input.c b/src/input.c index 8e89aa96..1e6ce01f 100644 --- a/src/input.c +++ b/src/input.c @@ -328,16 +328,29 @@ void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool } } -void _glfwInputPreedit(_GLFWwindow* window, int focusedBlock) +// Notifies shared code of a preedit event +// +void _glfwInputPreedit(_GLFWwindow* window) { - if (window->callbacks.preedit) { - window->callbacks.preedit((GLFWwindow*) window, window->ntext, window->preeditText, window->nblocks, window->preeditAttributeBlocks, focusedBlock); + if (window->callbacks.preedit) + { + _GLFWpreedit *preedit = &window->preedit; + window->callbacks.preedit((GLFWwindow*) window, + preedit->textCount, + preedit->text, + preedit->blockSizesCount, + preedit->blockSizes, + preedit->focusedBlockIndex, + preedit->caretIndex); } } +// Notifies shared code of a IME status event +// void _glfwInputIMEStatus(_GLFWwindow* window) { - if (window->callbacks.imestatus) { + if (window->callbacks.imestatus) + { window->callbacks.imestatus((GLFWwindow*) window); } } @@ -592,7 +605,7 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) case GLFW_UNLIMITED_MOUSE_BUTTONS: return window->disableMouseButtonLimit; case GLFW_IME: - return _glfwPlatformGetIMEStatus(window); + return _glfw.platform.getIMEStatus(window); } _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); @@ -709,7 +722,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) case GLFW_IME: { - _glfwPlatformSetIMEStatus(window, value ? GLFW_TRUE : GLFW_FALSE); + _glfw.platform.setIMEStatus(window, value ? GLFW_TRUE : GLFW_FALSE); return; } } @@ -971,28 +984,45 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) _glfw.platform.setCursor(window, cursor); } -GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* handle, int *x, int *y, int *h) +GLFWAPI void glfwGetPreeditCursorRectangle(GLFWwindow* handle, int* x, int* y, int* w, int* h) { _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFWpreedit* preedit = &window->preedit; if (x) - *x = window->preeditCursorPosX; + *x = preedit->cursorPosX; if (y) - *y = window->preeditCursorPosY; + *y = preedit->cursorPosY; + if (w) + *w = preedit->cursorWidth; if (h) - *h = window->preeditCursorHeight; + *h = preedit->cursorHeight; } -GLFWAPI void glfwSetPreeditCursorPos(GLFWwindow* handle, int x, int y, int h) +GLFWAPI void glfwSetPreeditCursorRectangle(GLFWwindow* handle, int x, int y, int w, int h) { _GLFWwindow* window = (_GLFWwindow*) handle; - window->preeditCursorPosX = x; - window->preeditCursorPosY = y; - window->preeditCursorHeight = h; + _GLFWpreedit* preedit = &window->preedit; + + if (x == preedit->cursorPosX && + y == preedit->cursorPosY && + w == preedit->cursorWidth && + h == preedit->cursorHeight) + { + return; + } + + preedit->cursorPosX = x; + preedit->cursorPosY = y; + preedit->cursorWidth = w; + preedit->cursorHeight = h; + + _glfw.platform.updatePreeditCursorRectangle(window); } -GLFWAPI void glfwResetPreeditText(GLFWwindow* handle) { +GLFWAPI void glfwResetPreeditText(GLFWwindow* handle) +{ _GLFWwindow* window = (_GLFWwindow*) handle; - _glfwPlatformResetPreeditText(window); + _glfw.platform.resetPreeditText(window); } GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) diff --git a/src/internal.h b/src/internal.h index 0702368a..5f78327b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -69,6 +69,7 @@ typedef struct _GLFWwndconfig _GLFWwndconfig; typedef struct _GLFWctxconfig _GLFWctxconfig; typedef struct _GLFWfbconfig _GLFWfbconfig; typedef struct _GLFWcontext _GLFWcontext; +typedef struct _GLFWpreedit _GLFWpreedit; typedef struct _GLFWwindow _GLFWwindow; typedef struct _GLFWplatform _GLFWplatform; typedef struct _GLFWlibrary _GLFWlibrary; @@ -526,6 +527,21 @@ struct _GLFWcontext GLFW_PLATFORM_CONTEXT_STATE }; +// Preedit structure for Input Method Editor/Engine +// +struct _GLFWpreedit +{ + unsigned int* text; + int textCount; + int textBufferCount; + int* blockSizes; + int blockSizesCount; + int blockSizesBufferCount; + int focusedBlockIndex; + int caretIndex; + int cursorPosX, cursorPosY, cursorWidth, cursorHeight; +}; + // Window and context structure // struct _GLFWwindow @@ -562,17 +578,10 @@ struct _GLFWwindow double virtualCursorPosX, virtualCursorPosY; GLFWbool rawMouseMotion; - // Preedit texts - unsigned int* preeditText; - int ntext; - int ctext; - int* preeditAttributeBlocks; - int nblocks; - int cblocks; - int preeditCursorPosX, preeditCursorPosY, preeditCursorHeight; - _GLFWcontext context; + _GLFWpreedit preedit; + struct { GLFWwindowposfun pos; GLFWwindowsizefun size; @@ -711,6 +720,10 @@ struct _GLFWplatform int (*getKeyScancode)(int); void (*setClipboardString)(const char*); const char* (*getClipboardString)(void); + void (*updatePreeditCursorRectangle)(_GLFWwindow*); + void (*resetPreeditText)(_GLFWwindow*); + void (*setIMEStatus)(_GLFWwindow*,int); + int (*getIMEStatus)(_GLFWwindow*); GLFWbool (*initJoysticks)(void); void (*terminateJoysticks)(void); GLFWbool (*pollJoystick)(_GLFWjoystick*,int); @@ -946,7 +959,7 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods); void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool plain); -void _glfwInputPreedit(_GLFWwindow* window, int focusedBlock); +void _glfwInputPreedit(_GLFWwindow* window); void _glfwInputIMEStatus(_GLFWwindow* window); void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); @@ -968,9 +981,6 @@ void _glfwInputError(int code, const char* format, ...) void _glfwInputError(int code, const char* format, ...); #endif -void _glfwPlatformResetPreeditText(_GLFWwindow* window); -void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active); -int _glfwPlatformGetIMEStatus(_GLFWwindow* window); ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// diff --git a/src/null_init.c b/src/null_init.c index f8234a31..f1065e93 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -55,6 +55,10 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) .getKeyScancode = _glfwGetKeyScancodeNull, .setClipboardString = _glfwSetClipboardStringNull, .getClipboardString = _glfwGetClipboardStringNull, + .updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleNull, + .resetPreeditText = _glfwResetPreeditTextNull, + .setIMEStatus = _glfwSetIMEStatusNull, + .getIMEStatus = _glfwGetIMEStatusNull, .initJoysticks = _glfwInitJoysticksNull, .terminateJoysticks = _glfwTerminateJoysticksNull, .pollJoystick = _glfwPollJoystickNull, diff --git a/src/null_platform.h b/src/null_platform.h index 548b7b49..68601417 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -270,6 +270,11 @@ const char* _glfwGetClipboardStringNull(void); const char* _glfwGetScancodeNameNull(int scancode); int _glfwGetKeyScancodeNull(int key); +void _glfwUpdatePreeditCursorRectangleNull(_GLFWwindow* window); +void _glfwResetPreeditTextNull(_GLFWwindow* window); +void _glfwSetIMEStatusNull(_GLFWwindow* window, int active); +int _glfwGetIMEStatusNull(_GLFWwindow* window); + EGLenum _glfwGetEGLPlatformNull(EGLint** attribs); EGLNativeDisplayType _glfwGetEGLNativeDisplayNull(void); EGLNativeWindowType _glfwGetEGLNativeWindowNull(_GLFWwindow* window); diff --git a/src/null_window.c b/src/null_window.c index 0a42d436..9e737adb 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -551,6 +551,23 @@ const char* _glfwGetClipboardStringNull(void) return _glfw.null.clipboardString; } +void _glfwUpdatePreeditCursorRectangleNull(_GLFWwindow* window) +{ +} + +void _glfwResetPreeditTextNull(_GLFWwindow* window) +{ +} + +void _glfwSetIMEStatusNull(_GLFWwindow* window, int active) +{ +} + +int _glfwGetIMEStatusNull(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + EGLenum _glfwGetEGLPlatformNull(EGLint** attribs) { if (_glfw.egl.EXT_platform_base && _glfw.egl.MESA_platform_surfaceless) diff --git a/src/win32_init.c b/src/win32_init.c index 5a24e256..edf002b6 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -163,6 +163,29 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } + _glfw.win32.imm32.instance = _glfwPlatformLoadModule("imm32.dll"); + if (_glfw.win32.imm32.instance) + { + _glfw.win32.imm32.ImmGetCompositionStringW_ = (PFN_ImmGetCompositionStringW) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetCompositionStringW"); + _glfw.win32.imm32.ImmGetContext_ = (PFN_ImmGetContext) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetContext"); + _glfw.win32.imm32.ImmGetConversionStatus_ = (PFN_ImmGetConversionStatus) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetConversionStatus"); + _glfw.win32.imm32.ImmGetDescriptionW_ = (PFN_ImmGetDescriptionW) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetDescriptionW"); + _glfw.win32.imm32.ImmGetOpenStatus_ = (PFN_ImmGetOpenStatus) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetOpenStatus"); + _glfw.win32.imm32.ImmNotifyIME_ = (PFN_ImmNotifyIME) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmNotifyIME"); + _glfw.win32.imm32.ImmReleaseContext_ = (PFN_ImmReleaseContext) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmReleaseContext"); + _glfw.win32.imm32.ImmSetCandidateWindow_ = (PFN_ImmSetCandidateWindow) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmSetCandidateWindow"); + _glfw.win32.imm32.ImmSetOpenStatus_ = (PFN_ImmSetOpenStatus) + _glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmSetOpenStatus"); + } + return GLFW_TRUE; } @@ -592,6 +615,10 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) .getKeyScancode = _glfwGetKeyScancodeWin32, .setClipboardString = _glfwSetClipboardStringWin32, .getClipboardString = _glfwGetClipboardStringWin32, + .updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleWin32, + .resetPreeditText = _glfwResetPreeditTextWin32, + .setIMEStatus = _glfwSetIMEStatusWin32, + .getIMEStatus = _glfwGetIMEStatusWin32, .initJoysticks = _glfwInitJoysticksWin32, .terminateJoysticks = _glfwTerminateJoysticksWin32, .pollJoystick = _glfwPollJoystickWin32, @@ -704,6 +731,7 @@ void _glfwTerminateWin32(void) _glfwPlatformFreeModule(_glfw.win32.dwmapi.instance); _glfwPlatformFreeModule(_glfw.win32.shcore.instance); _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); + _glfwPlatformFreeModule(_glfw.win32.imm32.instance); memset(&_glfw.win32, 0, sizeof(_glfw.win32)); } diff --git a/src/win32_platform.h b/src/win32_platform.h index d6500900..f67935ee 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -70,6 +70,7 @@ #include #include #include +#include // HACK: Define macros that some windows.h variants don't #ifndef WM_COPYGLOBALDATA @@ -258,6 +259,26 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*, typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); #define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ +// imm32 function pointer typedefs +typedef LONG (WINAPI * PFN_ImmGetCompositionStringW)(HIMC,DWORD,LPVOID,DWORD); +typedef HIMC (WINAPI * PFN_ImmGetContext)(HWND); +typedef BOOL (WINAPI * PFN_ImmGetConversionStatus)(HIMC,LPDWORD,LPDWORD); +typedef UINT (WINAPI * PFN_ImmGetDescriptionW)(HKL,LPWSTR,UINT); +typedef BOOL (WINAPI * PFN_ImmGetOpenStatus)(HIMC); +typedef BOOL (WINAPI * PFN_ImmNotifyIME)(HIMC,DWORD,DWORD,DWORD); +typedef BOOL (WINAPI * PFN_ImmReleaseContext)(HWND,HIMC); +typedef BOOL (WINAPI * PFN_ImmSetCandidateWindow)(HIMC,LPCANDIDATEFORM); +typedef BOOL (WINAPI * PFN_ImmSetOpenStatus)(HIMC,BOOL); +#define ImmGetCompositionStringW _glfw.win32.imm32.ImmGetCompositionStringW_ +#define ImmGetContext _glfw.win32.imm32.ImmGetContext_ +#define ImmGetConversionStatus _glfw.win32.imm32.ImmGetConversionStatus_ +#define ImmGetDescriptionW _glfw.win32.imm32.ImmGetDescriptionW_ +#define ImmGetOpenStatus _glfw.win32.imm32.ImmGetOpenStatus_ +#define ImmNotifyIME _glfw.win32.imm32.ImmNotifyIME_ +#define ImmReleaseContext _glfw.win32.imm32.ImmReleaseContext_ +#define ImmSetCandidateWindow _glfw.win32.imm32.ImmSetCandidateWindow_ +#define ImmSetOpenStatus _glfw.win32.imm32.ImmSetOpenStatus_ + // WGL extension pointer typedefs typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); @@ -442,6 +463,19 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; + + struct { + HINSTANCE instance; + PFN_ImmGetCompositionStringW ImmGetCompositionStringW_; + PFN_ImmGetContext ImmGetContext_; + PFN_ImmGetConversionStatus ImmGetConversionStatus_; + PFN_ImmGetDescriptionW ImmGetDescriptionW_; + PFN_ImmGetOpenStatus ImmGetOpenStatus_; + PFN_ImmNotifyIME ImmNotifyIME_; + PFN_ImmReleaseContext ImmReleaseContext_; + PFN_ImmSetCandidateWindow ImmSetCandidateWindow_; + PFN_ImmSetOpenStatus ImmSetOpenStatus_; + } imm32; } _GLFWlibraryWin32; // Win32-specific per-monitor data @@ -536,6 +570,11 @@ void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor); void _glfwSetClipboardStringWin32(const char* string); const char* _glfwGetClipboardStringWin32(void); +void _glfwUpdatePreeditCursorRectangleWin32(_GLFWwindow* window); +void _glfwResetPreeditTextWin32(_GLFWwindow* window); +void _glfwSetIMEStatusWin32(_GLFWwindow* window, int active); +int _glfwGetIMEStatusWin32(_GLFWwindow* window); + EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs); EGLNativeDisplayType _glfwGetEGLNativeDisplayWin32(void); EGLNativeWindowType _glfwGetEGLNativeWindowWin32(_GLFWwindow* window); diff --git a/src/win32_window.c b/src/win32_window.c index f0237174..25bd2ceb 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -35,7 +35,41 @@ #include #include #include -#include + +// Converts utf16 units to Unicode code points (UTF32). +// Returns GLFW_TRUE when the converting completes and the result is assigned to +// the argument `codepoint`. +// Returns GLFW_FALSE when the converting is not yet completed (for +// Surrogate-pair processing) and the unit is assigned to the argument +// `highsurrogate`. It will be used in the next unit's processing. +// +static GLFWbool convertToUTF32FromUTF16(WCHAR utf16_unit, + WCHAR* highsurrogate, + uint32_t* codepoint) +{ + *codepoint = 0; + + if (utf16_unit >= 0xd800 && utf16_unit <= 0xdbff) + { + *highsurrogate = (WCHAR) utf16_unit; + return GLFW_FALSE; + } + + if (utf16_unit >= 0xdc00 && utf16_unit <= 0xdfff) + { + if (*highsurrogate) + { + *codepoint += (*highsurrogate - 0xd800) << 10; + *codepoint += (WCHAR) utf16_unit - 0xdc00; + *codepoint += 0x10000; + } + } + else + *codepoint = (WCHAR) utf16_unit; + + *highsurrogate = 0; + return GLFW_TRUE; +} // Returns the window style for the specified window // @@ -527,13 +561,205 @@ static void maximizeWindowManually(_GLFWwindow* window) SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); } -// Set cursor position to decide candidate window -static void _win32ChangeCursorPosition(HIMC hIMC, _GLFWwindow* window) { - int x = window->preeditCursorPosX; - int y = window->preeditCursorPosY; - int h = window->preeditCursorHeight; - CANDIDATEFORM excludeRect = {0, CFS_EXCLUDE, {x, y}, {x, y, x, y+h}}; - ImmSetCandidateWindow(hIMC, &excludeRect); +// Get preedit texts of Imm32 and pass them to preedit-callback +// +static GLFWbool getImmPreedit(_GLFWwindow* window) +{ + _GLFWpreedit* preedit = &window->preedit; + HIMC hIMC = ImmGetContext(window->win32.handle); + // get preedit data sizes + LONG preeditBytes = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); + LONG attrBytes = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0); + LONG clauseBytes = ImmGetCompositionStringW(hIMC, GCS_COMPCLAUSE, NULL, 0); + LONG cursorPos = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0); + + if (preeditBytes > 0) + { + int textBufferCount = preedit->textBufferCount; + int blockBufferCount = preedit->blockSizesBufferCount; + int textLen = preeditBytes / sizeof(WCHAR); + LPWSTR buffer = _glfw_calloc(preeditBytes, 1); + LPSTR attributes = _glfw_calloc(attrBytes, 1); + DWORD* clauses = _glfw_calloc(clauseBytes, 1); + + if (!buffer || (attrBytes > 0 && !attributes) || (clauseBytes > 0 && !clauses)) + { + _glfw_free(buffer); + _glfw_free(attributes); + _glfw_free(clauses); + ImmReleaseContext(window->win32.handle, hIMC); + return GLFW_FALSE; + } + + // get preedit data + ImmGetCompositionStringW(hIMC, GCS_COMPSTR, buffer, preeditBytes); + if (attributes) + ImmGetCompositionStringW(hIMC, GCS_COMPATTR, attributes, attrBytes); + if (clauses) + ImmGetCompositionStringW(hIMC, GCS_COMPCLAUSE, clauses, clauseBytes); + + // realloc preedit text + while (textBufferCount < textLen + 1) + textBufferCount = (textBufferCount == 0) ? 1 : textBufferCount * 2; + if (textBufferCount != preedit->textBufferCount) + { + size_t bufsize = sizeof(unsigned int) * textBufferCount; + unsigned int* preeditText = _glfw_realloc(preedit->text, + bufsize); + + if (preeditText == NULL) + { + _glfw_free(buffer); + _glfw_free(attributes); + _glfw_free(clauses); + ImmReleaseContext(window->win32.handle, hIMC); + return GLFW_FALSE; + } + preedit->text = preeditText; + preedit->textBufferCount = textBufferCount; + } + + // realloc blocks + preedit->blockSizesCount = clauses ? clauseBytes / sizeof(DWORD) - 1 : 1; + while (blockBufferCount < preedit->blockSizesCount) + blockBufferCount = (blockBufferCount == 0) ? 1 : blockBufferCount * 2; + if (blockBufferCount != preedit->blockSizesBufferCount) + { + size_t bufsize = sizeof(int) * blockBufferCount; + int* blocks = _glfw_realloc(preedit->blockSizes, + bufsize); + + if (blocks == NULL) + { + _glfw_free(buffer); + _glfw_free(attributes); + _glfw_free(clauses); + ImmReleaseContext(window->win32.handle, hIMC); + return GLFW_FALSE; + } + preedit->blockSizes = blocks; + preedit->blockSizesBufferCount = blockBufferCount; + } + + // store preedit text & block sizes + { + // Win32 API handles text data in UTF16, so we have to convert them + // to UTF32. Not only the encoding, but also the number of characters, + // the position of each block and the cursor. + int i; + uint32_t codepoint; + WCHAR highSurrogate = 0; + int convertedLength = 0; + int blockIndex = 0; + int currentBlockLength = 0; + + // The last element of clauses is a block count, but + // text length is convenient. + if (clauses) + clauses[preedit->blockSizesCount] = textLen; + + for (i = 0; i < textLen; i++) + { + if (clauses && clauses[blockIndex + 1] <= (DWORD) i) + { + preedit->blockSizes[blockIndex++] = currentBlockLength; + currentBlockLength = 0; + } + + if (convertToUTF32FromUTF16(buffer[i], + &highSurrogate, + &codepoint)) + { + preedit->text[convertedLength++] = codepoint; + currentBlockLength++; + } + else if ((LONG) i < cursorPos) + { + // A high surrogate appears before cursorPos, so needs to + // fix cursorPos on UTF16 for UTF32 + cursorPos--; + } + } + preedit->blockSizes[blockIndex] = currentBlockLength; + preedit->textCount = convertedLength; + preedit->text[convertedLength] = 0; + preedit->caretIndex = cursorPos; + + preedit->focusedBlockIndex = 0; + if (attributes && clauses) + { + for (i = 0; i < preedit->blockSizesCount; i++) + { + if (attributes[clauses[i]] == ATTR_TARGET_CONVERTED || + attributes[clauses[i]] == ATTR_TARGET_NOTCONVERTED) + { + preedit->focusedBlockIndex = i; + break; + } + } + } + } + + _glfw_free(buffer); + _glfw_free(attributes); + _glfw_free(clauses); + + _glfwInputPreedit(window); + } + + ImmReleaseContext(window->win32.handle, hIMC); + return GLFW_TRUE; +} + +// Clear peedit data +// +static void clearImmPreedit(_GLFWwindow* window) +{ + window->preedit.blockSizesCount = 0; + window->preedit.textCount = 0; + window->preedit.focusedBlockIndex = 0; + window->preedit.caretIndex = 0; + _glfwInputPreedit(window); +} + +// Commit the result texts of Imm32 to character-callback +// +static GLFWbool commitImmResultStr(_GLFWwindow* window) +{ + HIMC hIMC; + LONG bytes; + uint32_t codepoint; + WCHAR highSurrogate = 0; + + if (!window->callbacks.character) + return GLFW_FALSE; + + hIMC = ImmGetContext(window->win32.handle); + // get preedit data sizes + bytes = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); + + if (bytes > 0) + { + int i; + int length = bytes / sizeof(WCHAR); + LPWSTR buffer = _glfw_calloc(bytes, 1); + + // get preedit data + ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buffer, bytes); + + for (i = 0; i < length; i++) + { + if (convertToUTF32FromUTF16(buffer[i], + &highSurrogate, + &codepoint)) + window->callbacks.character((GLFWwindow*) window, codepoint); + } + + _glfw_free(buffer); + } + + ImmReleaseContext(window->win32.handle, hIMC); + return GLFW_TRUE; } // Window procedure for user-created windows @@ -564,6 +790,14 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l switch (uMsg) { + case WM_IME_SETCONTEXT: + { + // To draw preedit text by an application side + if (lParam & ISC_SHOWUICOMPOSITIONWINDOW) + lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; + break; + } + case WM_MOUSEACTIVATE: { // HACK: Postpone cursor disabling when the window was activated by @@ -669,27 +903,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l case WM_CHAR: case WM_SYSCHAR: { - if (wParam >= 0xd800 && wParam <= 0xdbff) - window->win32.highSurrogate = (WCHAR) wParam; - else - { - uint32_t codepoint = 0; - - if (wParam >= 0xdc00 && wParam <= 0xdfff) - { - if (window->win32.highSurrogate) - { - codepoint += (window->win32.highSurrogate - 0xd800) << 10; - codepoint += (WCHAR) wParam - 0xdc00; - codepoint += 0x10000; - } - } - else - codepoint = (WCHAR) wParam; - - window->win32.highSurrogate = 0; + uint32_t codepoint; + if (convertToUTF32FromUTF16((WCHAR) wParam, + &window->win32.highSurrogate, + &codepoint)) _glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR); - } if (uMsg == WM_SYSCHAR && window->win32.keymenu) break; @@ -810,91 +1028,33 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l case WM_IME_COMPOSITION: { - if (lParam & GCS_RESULTSTR) { - window->nblocks = 0; - window->ntext = 0; - _glfwInputPreedit(window, 0); - return TRUE; - } - if (lParam & GCS_COMPSTR) { - HIMC hIMC = ImmGetContext(hWnd); - // get preedit data sizes - LONG preeditTextLength = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); - LONG attrLength = ImmGetCompositionString(hIMC, GCS_COMPATTR, NULL, 0); - LONG clauseLength = ImmGetCompositionString(hIMC, GCS_COMPCLAUSE, NULL, 0); - if (preeditTextLength > 0) { - // get preedit data - int length = preeditTextLength/sizeof(WCHAR); - LPWSTR buffer = (LPWSTR)_glfw_calloc(preeditTextLength, sizeof(WCHAR)); - LPSTR attributes = (LPSTR)_glfw_calloc(attrLength, 1); - DWORD *clauses = (DWORD*)_glfw_calloc(clauseLength, 1); - ImmGetCompositionStringW(hIMC, GCS_COMPSTR, buffer, preeditTextLength); - ImmGetCompositionString(hIMC, GCS_COMPATTR, attributes, attrLength); - ImmGetCompositionString(hIMC, GCS_COMPCLAUSE, clauses, clauseLength); - // store preedit text - int ctext = window->ctext; - while (ctext < length+1) { - ctext = (ctext == 0) ? 1 : ctext*2; - } - if (ctext != window->ctext) { - unsigned int* preeditText = _glfw_realloc(window->preeditText, sizeof(unsigned int)*ctext); - if (preeditText == NULL) { - return 0; - _glfw_free(buffer); - _glfw_free(attributes); - _glfw_free(clauses); - } - window->preeditText = preeditText; - window->ctext = ctext; - } - window->ntext = length; - window->preeditText[length] = 0; - int i; - for (i=0; i < length; i++) { - window->preeditText[i] = buffer[i]; - } - // store blocks - window->nblocks = clauseLength/sizeof(DWORD)-1; - // last element of clauses is a block count, but - // text length is convenient. - clauses[window->nblocks] = length; - int cblocks = window->cblocks; - while (cblocks < window->nblocks) { - cblocks = (cblocks == 0) ? 1 : cblocks*2; - } - if (cblocks != window->cblocks) { - int* blocks = _glfw_realloc(window->preeditAttributeBlocks, sizeof(int)*cblocks); - if (blocks == NULL) { - return 0; - _glfw_free(buffer); - _glfw_free(attributes); - _glfw_free(clauses); - } - window->preeditAttributeBlocks = blocks; - window->cblocks = cblocks; - } - int focusedBlock = 0; - for (i=0; i < window->nblocks; i++) { - window->preeditAttributeBlocks[i] = clauses[i+1]-clauses[i]; - if (attributes[clauses[i]] != ATTR_CONVERTED) { - focusedBlock = i; - } - } - _glfw_free(buffer); - _glfw_free(attributes); - _glfw_free(clauses); - _glfwInputPreedit(window, focusedBlock); - _win32ChangeCursorPosition(hIMC, window); - } - ImmReleaseContext(hWnd, hIMC); + if (lParam & (GCS_RESULTSTR | GCS_COMPSTR)) + { + if (lParam & GCS_RESULTSTR) + commitImmResultStr(window); + if (lParam & GCS_COMPSTR) + getImmPreedit(window); return TRUE; } break; } + + case WM_IME_ENDCOMPOSITION: + { + clearImmPreedit(window); + return TRUE; + } + case WM_IME_NOTIFY: + { if (wParam == IMN_SETOPENSTATUS) + { _glfwInputIMEStatus(window); + return TRUE; + } break; + } + case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: @@ -2548,6 +2708,48 @@ const char* _glfwGetClipboardStringWin32(void) return _glfw.win32.clipboardString; } +void _glfwUpdatePreeditCursorRectangleWin32(_GLFWwindow* window) +{ + _GLFWpreedit* preedit = &window->preedit; + HWND hWnd = window->win32.handle; + HIMC hIMC = ImmGetContext(hWnd); + + int x = preedit->cursorPosX; + int y = preedit->cursorPosY; + int w = preedit->cursorWidth; + int h = preedit->cursorHeight; + CANDIDATEFORM excludeRect = { 0, CFS_EXCLUDE, { x, y }, { x, y, x + w, y + h } }; + + ImmSetCandidateWindow(hIMC, &excludeRect); + + ImmReleaseContext(hWnd, hIMC); +} + +void _glfwResetPreeditTextWin32(_GLFWwindow* window) +{ + HWND hWnd = window->win32.handle; + HIMC hIMC = ImmGetContext(hWnd); + ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); + ImmReleaseContext(hWnd, hIMC); +} + +void _glfwSetIMEStatusWin32(_GLFWwindow* window, int active) +{ + HWND hWnd = window->win32.handle; + HIMC hIMC = ImmGetContext(hWnd); + ImmSetOpenStatus(hIMC, active ? TRUE : FALSE); + ImmReleaseContext(hWnd, hIMC); +} + +int _glfwGetIMEStatusWin32(_GLFWwindow* window) +{ + HWND hWnd = window->win32.handle; + HIMC hIMC = ImmGetContext(hWnd); + BOOL result = ImmGetOpenStatus(hIMC); + ImmReleaseContext(hWnd, hIMC); + return result ? GLFW_TRUE : GLFW_FALSE; +} + EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) @@ -2660,31 +2862,6 @@ VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, return err; } -void _glfwPlatformResetPreeditText(_GLFWwindow* window) -{ - HWND hWnd = window->win32.handle; - HIMC hIMC = ImmGetContext(hWnd); - ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); - ImmReleaseContext(hWnd, hIMC); -} - -void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active) -{ - HWND hWnd = window->win32.handle; - HIMC hIMC = ImmGetContext(hWnd); - ImmSetOpenStatus(hIMC, active ? TRUE : FALSE); - ImmReleaseContext(hWnd, hIMC); -} - -int _glfwPlatformGetIMEStatus(_GLFWwindow* window) -{ - HWND hWnd = window->win32.handle; - HIMC hIMC = ImmGetContext(hWnd); - BOOL result = ImmGetOpenStatus(hIMC); - ImmReleaseContext(hWnd, hIMC); - return result ? GLFW_TRUE : GLFW_FALSE; -} - GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); diff --git a/src/window.c b/src/window.c index 450a4ef4..34f544d5 100644 --- a/src/window.c +++ b/src/window.c @@ -243,6 +243,11 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->denom = GLFW_DONT_CARE; window->title = _glfw_strdup(title); + window->preedit.cursorPosX = 0; + window->preedit.cursorPosY = height; + window->preedit.cursorWidth = 0; + window->preedit.cursorHeight = 0; + if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { glfwDestroyWindow((GLFWwindow*) window); @@ -495,10 +500,10 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) } // Clear memory for preedit text - if (window->preeditText) - _glfw_free(window->preeditText); - if (window->preeditAttributeBlocks) - _glfw_free(window->preeditAttributeBlocks); + if (window->preedit.text) + _glfw_free(window->preedit.text); + if (window->preedit.blockSizes) + _glfw_free(window->preedit.blockSizes); _glfw_free(window->title); _glfw_free(window); } diff --git a/src/wl_init.c b/src/wl_init.c index 57ebfe1a..7a158901 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -442,6 +442,10 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) .getKeyScancode = _glfwGetKeyScancodeWayland, .setClipboardString = _glfwSetClipboardStringWayland, .getClipboardString = _glfwGetClipboardStringWayland, + .updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleWayland, + .resetPreeditText = _glfwResetPreeditTextWayland, + .setIMEStatus = _glfwSetIMEStatusWayland, + .getIMEStatus = _glfwGetIMEStatusWayland, #if defined(GLFW_BUILD_LINUX_JOYSTICK) .initJoysticks = _glfwInitJoysticksLinux, .terminateJoysticks = _glfwTerminateJoysticksLinux, diff --git a/src/wl_platform.h b/src/wl_platform.h index c6de53ba..95c35f3b 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -709,6 +709,11 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor); void _glfwSetClipboardStringWayland(const char* string); const char* _glfwGetClipboardStringWayland(void); +void _glfwUpdatePreeditCursorRectangleWayland(_GLFWwindow* window); +void _glfwResetPreeditTextWayland(_GLFWwindow* window); +void _glfwSetIMEStatusWayland(_GLFWwindow* window, int active); +int _glfwGetIMEStatusWayland(_GLFWwindow* window); + EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs); EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void); EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window); diff --git a/src/wl_window.c b/src/wl_window.c index 4914dc83..bb1804bc 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -3499,6 +3499,23 @@ const char* _glfwGetClipboardStringWayland(void) return _glfw.wl.clipboardString; } +void _glfwUpdatePreeditCursorRectangleWayland(_GLFWwindow* window) +{ +} + +void _glfwResetPreeditTextWayland(_GLFWwindow* window) +{ +} + +void _glfwSetIMEStatusWayland(_GLFWwindow* window, int active) +{ +} + +int _glfwGetIMEStatusWayland(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs) { if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland) diff --git a/src/x11_init.c b/src/x11_init.c index 1467c74d..21bcf5e4 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1183,6 +1183,10 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) .getKeyScancode = _glfwGetKeyScancodeX11, .setClipboardString = _glfwSetClipboardStringX11, .getClipboardString = _glfwGetClipboardStringX11, + .updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleX11, + .resetPreeditText = _glfwResetPreeditTextX11, + .setIMEStatus = _glfwSetIMEStatusX11, + .getIMEStatus = _glfwGetIMEStatusX11, #if defined(GLFW_BUILD_LINUX_JOYSTICK) .initJoysticks = _glfwInitJoysticksLinux, .terminateJoysticks = _glfwTerminateJoysticksLinux, diff --git a/src/x11_platform.h b/src/x11_platform.h index da3ff96b..5ae238a0 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -954,6 +954,11 @@ void _glfwSetCursorX11(_GLFWwindow* window, _GLFWcursor* cursor); void _glfwSetClipboardStringX11(const char* string); const char* _glfwGetClipboardStringX11(void); +void _glfwUpdatePreeditCursorRectangleX11(_GLFWwindow* window); +void _glfwResetPreeditTextX11(_GLFWwindow* window); +void _glfwSetIMEStatusX11(_GLFWwindow* window, int active); +int _glfwGetIMEStatusX11(_GLFWwindow* window); + EGLenum _glfwGetEGLPlatformX11(EGLint** attribs); EGLNativeDisplayType _glfwGetEGLNativeDisplayX11(void); EGLNativeWindowType _glfwGetEGLNativeWindowX11(_GLFWwindow* window); diff --git a/src/x11_window.c b/src/x11_window.c index 00746993..db784b82 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3110,6 +3110,23 @@ const char* _glfwGetClipboardStringX11(void) return getSelectionString(_glfw.x11.CLIPBOARD); } +void _glfwUpdatePreeditCursorRectangleX11(_GLFWwindow* window) +{ +} + +void _glfwResetPreeditTextX11(_GLFWwindow* window) +{ +} + +void _glfwSetIMEStatusX11(_GLFWwindow* window, int active) +{ +} + +int _glfwGetIMEStatusX11(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + EGLenum _glfwGetEGLPlatformX11(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle)