Apply shibukawa's fix of GLFW for Windows

This fix is based on shibukawa's fix:
  https://github.com/glfw/glfw/pull/658
  d36a164423

Some minor coding style changes are made, but not yet follow glfw's one,
and some comments doesn't follow recent changes. So further work is
needed.
This commit is contained in:
daipom 2022-03-10 17:55:08 +09:00 committed by Daijiro Fukuda
parent 62e175ef9f
commit fad44068e5
6 changed files with 361 additions and 7 deletions

View File

@ -1124,6 +1124,7 @@ extern "C" {
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
#define GLFW_LOCK_KEY_MODS 0x00033004
#define GLFW_RAW_MOUSE_MOTION 0x00033005
#define GLFW_IME 0x00033006
#define GLFW_CURSOR_NORMAL 0x00034001
#define GLFW_CURSOR_HIDDEN 0x00034002
@ -1877,6 +1878,37 @@ 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.
*
* This is the function signature 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.
*
* @sa @ref preedit
* @sa glfwSetPreeditCallback
*
* @ingroup input
*/
typedef void (* GLFWpreeditfun)(GLFWwindow*,int,unsigned int*,int,int*,int);
/*! @brief The function signature for IME status change callbacks.
*
* This is the function signature for IME status change callback functions.
*
* @param[in] window The window that received the event.
*
* @sa @ref preedit
* @sa glfwSetIMEStatusCallback
*
* @ingroup monitor
*/
typedef void (* GLFWimestatusfun)(GLFWwindow*);
/*! @brief The function pointer type for path drop callbacks.
*
* This is the function pointer type for path drop callbacks. A path drop
@ -4520,8 +4552,8 @@ GLFWAPI void glfwPostEmptyEvent(void);
*
* This function returns the value of an input option for the specified window.
* The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
* @ref GLFW_RAW_MOUSE_MOTION.
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS,
* @ref GLFW_RAW_MOUSE_MOTION or @ref GLFW_IME.
*
* @param[in] window The window to query.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
@ -4545,8 +4577,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
*
* This function sets an input mode option for the specified window. The mode
* must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
* @ref GLFW_RAW_MOUSE_MOTION.
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS,
* @ref GLFW_RAW_MOUSE_MOTION or @ref GLFW_IME.
*
* If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
* modes:
@ -4584,10 +4616,13 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
* attempting to set this will emit @ref GLFW_FEATURE_UNAVAILABLE. Call @ref
* glfwRawMouseMotionSupported to check for support.
*
* If the mode is `GLFW_IME`, the value must be either `GLFW_TRUE` to turn on
* IME, or `GLFW_FALSE` to turn off it.
*
* @param[in] window The window whose input mode to set.
* @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`.
* @param[in] value The new value of the specified input mode.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
@ -5012,6 +5047,67 @@ 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.
*
* This function returns position hint to decide the candidate 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.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
* @sa @ref input_char
*
* @since Added in GLFW 3.X.
*
* @ingroup input
*/
GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* window, int *x, int *y, int *h);
/*! @brief Notify the text cursor position to window system to decide the candidate window position.
*
* 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.
*
* @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.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
* @sa @ref input_char
*
* @since Added in GLFW 3.X.
*
* @ingroup input
*/
GLFWAPI void glfwSetPreeditCursorPos(GLFWwindow* window, int x, int y, int h);
/*! @brief Reset IME input status.
*
* This function resets IME's preedit text.
*
* @param[in] window The window.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
* @sa @ref preedit
*
* @since Added in GLFW 3.X.
*
* @ingroup input
*/
GLFWAPI void glfwResetPreeditText(GLFWwindow* window);
/*! @brief Sets the key callback.
*
* This function sets the key callback of the specified window, which is called
@ -5147,6 +5243,58 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback
*/
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback);
/*! @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.
*
* 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.
*
* @param[in] window The window whose callback to set.
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
* callback.
* @return The previously set callback, or `NULL` if no callback was set or an
* error occurred.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
* @sa @ref input_char
*
* @since Added in GLFW 3.X
*
* @ingroup input
*/
GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun cbfun);
/*! @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.
*
* @param[in] window The window whose callback to set.
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
* callback.
* @return The previously set callback, or `NULL` if no callback was set or an
* error occurred.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
* @sa @ref input_char
*
* @since Added in GLFW 3.X
*
* @ingroup input
*/
GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* window, GLFWimestatusfun cbfun);
/*! @brief Sets the mouse button callback.
*
* This function sets the mouse button callback of the specified window, which

View File

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

View File

@ -313,6 +313,20 @@ void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool
}
}
void _glfwInputPreedit(_GLFWwindow* window, int focusedBlock)
{
if (window->callbacks.preedit) {
window->callbacks.preedit((GLFWwindow*) window, window->ntext, window->preeditText, window->nblocks, window->preeditAttributeBlocks, focusedBlock);
}
}
void _glfwInputIMEStatus(_GLFWwindow* window)
{
if (window->callbacks.imestatus) {
window->callbacks.imestatus((GLFWwindow*) window);
}
}
// Notifies shared code of a scroll event
//
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
@ -509,6 +523,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
return window->lockKeyMods;
case GLFW_RAW_MOUSE_MOTION:
return window->rawMouseMotion;
case GLFW_IME:
return _glfwPlatformGetIMEStatus(window);
}
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
@ -615,6 +631,12 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
_glfw.platform.setRawMouseMotion(window, value);
return;
}
case GLFW_IME:
{
_glfwPlatformSetIMEStatus(window, value ? GLFW_TRUE : GLFW_FALSE);
return;
}
}
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
@ -869,6 +891,30 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
_glfw.platform.setCursor(window, cursor);
}
GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* handle, int *x, int *y, int *h)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
if (x)
*x = window->preeditCursorPosX;
if (y)
*y = window->preeditCursorPosY;
if (h)
*h = window->preeditCursorHeight;
}
GLFWAPI void glfwSetPreeditCursorPos(GLFWwindow* handle, int x, int y, int h)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
window->preeditCursorPosX = x;
window->preeditCursorPosY = y;
window->preeditCursorHeight = h;
}
GLFWAPI void glfwResetPreeditText(GLFWwindow* handle) {
_GLFWwindow* window = (_GLFWwindow*) handle;
_glfwPlatformResetPreeditText(window);
}
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
@ -899,6 +945,22 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmods
return cbfun;
}
GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* handle, GLFWpreeditfun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP(GLFWpreeditfun, window->callbacks.preedit, cbfun);
return cbfun;
}
GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* handle, GLFWimestatusfun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP(GLFWimestatusfun, window->callbacks.imestatus, cbfun);
return cbfun;
}
GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,
GLFWmousebuttonfun cbfun)
{

View File

@ -551,6 +551,15 @@ 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;
struct {
@ -570,6 +579,8 @@ struct _GLFWwindow
GLFWkeyfun key;
GLFWcharfun character;
GLFWcharmodsfun charmods;
GLFWpreeditfun preedit;
GLFWimestatusfun imestatus;
GLFWdropfun drop;
} callbacks;
@ -920,6 +931,8 @@ 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 _glfwInputIMEStatus(_GLFWwindow* window);
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
@ -940,6 +953,9 @@ 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

@ -34,6 +34,7 @@
#include <string.h>
#include <windowsx.h>
#include <shellapi.h>
#include <imm.h>
// Returns the window style for the specified window
//
@ -533,6 +534,15 @@ 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);
}
// Window callback function (handles window messages)
//
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
@ -732,7 +742,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
}
_glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE);
return 0;
return TRUE;
}
case WM_KEYDOWN:
@ -827,6 +837,93 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
break;
}
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);
return TRUE;
}
break;
}
case WM_IME_NOTIFY:
if (wParam == IMN_SETOPENSTATUS)
_glfwInputIMEStatus(window);
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
@ -2480,6 +2577,31 @@ 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)
{
_GLFWwindow* window = (_GLFWwindow*) handle;

View File

@ -452,6 +452,11 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
*prev = window->next;
}
// Clear memory for preedit text
if (window->preeditText)
_glfw_free(window->preeditText);
if (window->preeditAttributeBlocks)
_glfw_free(window->preeditAttributeBlocks);
_glfw_free(window);
}