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 <ashie@clear-code.com>
This commit is contained in:
Daijiro Fukuda 2022-12-15 12:01:14 +09:00 committed by Takuro Ashie
parent 2e30ad17fe
commit 9802d73c96
20 changed files with 621 additions and 204 deletions

View File

@ -1946,27 +1946,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.
*
@ -1975,7 +1982,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.
*
@ -4689,8 +4696,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.
@ -5192,14 +5199,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.
@ -5210,20 +5219,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.
@ -5234,9 +5241,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.
*
@ -5390,11 +5397,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.
@ -5403,6 +5410,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.
*
@ -5416,12 +5436,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
@ -5429,6 +5445,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.
*

View File

@ -146,7 +146,6 @@ endif()
if (GLFW_BUILD_WIN32)
list(APPEND glfw_PKG_LIBS "-lgdi32")
list(APPEND glfw_LIBRARIES "imm32")
endif()
if (GLFW_BUILD_COCOA)

View File

@ -509,6 +509,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,

View File

@ -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);

View File

@ -1881,6 +1881,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)

View File

@ -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);
}
}
@ -595,7 +608,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);
@ -712,7 +725,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;
}
}
@ -975,28 +988,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)

View File

@ -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;
@ -525,6 +526,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
@ -561,17 +577,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;
@ -710,6 +719,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);
@ -945,7 +958,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);
@ -967,9 +980,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 //////

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -167,6 +167,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;
}
@ -191,6 +214,9 @@ static void freeLibraries(void)
if (_glfw.win32.ntdll.instance)
_glfwPlatformFreeModule(_glfw.win32.ntdll.instance);
if (_glfw.win32.imm32.instance)
_glfwPlatformFreeModule(_glfw.win32.imm32.instance);
}
// Create key code translation tables
@ -618,6 +644,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,

View File

@ -69,6 +69,7 @@
#include <dinput.h>
#include <xinput.h>
#include <dbt.h>
#include <imm.h>
// HACK: Define macros that some windows.h variants don't
#ifndef WM_MOUSEHWHEEL
@ -316,6 +317,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*);
@ -502,6 +523,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
@ -596,6 +630,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);

View File

@ -35,7 +35,41 @@
#include <assert.h>
#include <windowsx.h>
#include <shellapi.h>
#include <imm.h>
// 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
//
@ -530,13 +564,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
@ -567,6 +793,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
@ -672,27 +906,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;
@ -812,91 +1030,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:
@ -2560,6 +2720,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)
@ -2672,31 +2874,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);

View File

@ -244,6 +244,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);
@ -496,10 +501,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);
}

View File

@ -452,6 +452,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,

View File

@ -664,6 +664,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);

View File

@ -3195,6 +3195,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)

View File

@ -1182,6 +1182,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,

View File

@ -955,6 +955,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);

View File

@ -3084,6 +3084,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)