X11: Support IME

This commit re-organizes 6e7f93916b96c643ca7abe45d09f72d841ff15ed.

* Load missing XIM related function symbols.
* Generalize platform-specific features to _GLFWplatform.
* Change the defalut input style to over-the-spot style.
* Rename `decodeUTF8()` to `_glfwDecodeUTF8()` to make it as internal API.
    * It will be also needed to implment input method for Wayland.
* Refactor code shapes and variable names.

About over-the-spot style and on-the-spot style on X11:

* In over-the-spot mode, almost all APIs are disabled since applications only
  need to specify the preedit candidate window position by `glfwSetPreeditCursorPos()`.
* We can change the style by enabling `GLFW_X11_ONTHESPOT` init hint, but it
  has the following problems.
    * Status APIs don't work because status callbacks don't work.
      (at least in my ibus environment).
    * Can't specify the candidate window position.

Known problems:

* Some keys (arrow, Enter, BackSpace, ...) are passed to applications during preediting.
    * This will be fixed in PR #1972 : https://github.com/glfw/glfw/pull/1972

Co-authored-by: Takuro Ashie <ashie@clear-code.com>
This commit is contained in:
Daijiro Fukuda 2022-12-06 10:33:58 +09:00 committed by Takuro Ashie
parent 6981f7ae83
commit b0506b7912
6 changed files with 362 additions and 211 deletions

View File

@ -1324,6 +1324,11 @@ extern "C" {
* X11 specific [init hint](@ref GLFW_X11_XCB_VULKAN_SURFACE_hint).
*/
#define GLFW_X11_XCB_VULKAN_SURFACE 0x00052001
/*! @brief X11 specific init hint.
*
* X11 specific [init hint](@ref GLFW_X11_ONTHESPOT_hint).
*/
#define GLFW_X11_ONTHESPOT 0x00052002
/*! @brief Wayland specific init hint.
*
* Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint).
@ -5249,6 +5254,9 @@ GLFWAPI void glfwSetPreeditCursorRectangle(GLFWwindow* window, int x, int y, int
*
* @param[in] window The window.
*
* @remark @x11 Since over-the-spot style is used by default, you don't need
* to use this function.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
@ -5423,6 +5431,9 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods
* For more information about the callback parameters, see the
* [function pointer type](@ref GLFWpreeditfun).
*
* @remark @x11 Since over-the-spot style is used by default, you don't need
* to use this function.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
@ -5452,6 +5463,8 @@ GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun
* For more information about the callback parameters, see the
* [function pointer type](@ref GLFWimestatusfun).
*
* @remark @x11 Don't support this function. The callback is not called.
*
* @par Thread Safety
* This function may only be called from the main thread.
*

View File

@ -61,6 +61,7 @@ static _GLFWinitconfig _glfwInitHints =
.x11 =
{
.xcbVulkanSurface = GLFW_TRUE,
.onTheSpotIMStyle = GLFW_FALSE
},
.wl =
{
@ -175,6 +176,29 @@ size_t _glfwEncodeUTF8(char* s, uint32_t codepoint)
return count;
}
// Decode a Unicode code point from a UTF-8 stream
// Based on cutef8 by Jeff Bezanson (Public Domain)
//
uint32_t _glfwDecodeUTF8(const char** s)
{
uint32_t codepoint = 0, count = 0;
static const uint32_t offsets[] =
{
0x00000000u, 0x00003080u, 0x000e2080u,
0x03c82080u, 0xfa082080u, 0x82082080u
};
do
{
codepoint = (codepoint << 6) + (unsigned char) **s;
(*s)++;
count++;
} while ((**s & 0xc0) == 0x80);
assert(count <= 6);
return codepoint - offsets[count - 1];
}
// Splits and translates a text/uri-list into separate file paths
// NOTE: This function destroys the provided string
//
@ -459,6 +483,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
case GLFW_X11_XCB_VULKAN_SURFACE:
_glfwInitHints.x11.xcbVulkanSurface = value;
return;
case GLFW_X11_ONTHESPOT:
_glfwInitHints.x11.onTheSpotIMStyle = value;
return;
case GLFW_WAYLAND_LIBDECOR:
_glfwInitHints.wl.libdecorMode = value;
return;

View File

@ -385,6 +385,7 @@ struct _GLFWinitconfig
} ns;
struct {
GLFWbool xcbVulkanSurface;
GLFWbool onTheSpotIMStyle;
} x11;
struct {
int libdecorMode;
@ -1036,6 +1037,7 @@ void _glfwTerminateVulkan(void);
const char* _glfwGetVulkanResultString(VkResult result);
size_t _glfwEncodeUTF8(char* s, uint32_t codepoint);
uint32_t _glfwDecodeUTF8(const char** s);
char** _glfwParseUriList(char* text, int* count);
char* _glfw_strdup(const char* source);

View File

@ -445,9 +445,14 @@ static GLFWbool hasUsableInputMethodStyle(void)
if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL)
return GLFW_FALSE;
if (_glfw.hints.init.x11.onTheSpotIMStyle)
_glfw.x11.imStyle = STYLE_ONTHESPOT;
else
_glfw.x11.imStyle = STYLE_OVERTHESPOT;
for (unsigned int i = 0; i < styles->count_styles; i++)
{
if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing))
if (styles->supported_styles[i] == _glfw.x11.imStyle)
{
found = GLFW_TRUE;
break;
@ -1455,6 +1460,8 @@ int _glfwInitX11(void)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetErrorHandler");
_glfw.x11.xlib.SetICFocus = (PFN_XSetICFocus)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetICFocus");
_glfw.x11.xlib.SetICValues = (PFN_XSetICValues)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetICValues");
_glfw.x11.xlib.SetIMValues = (PFN_XSetIMValues)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetIMValues");
_glfw.x11.xlib.SetInputFocus = (PFN_XSetInputFocus)
@ -1485,6 +1492,8 @@ int _glfwInitX11(void)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnmapWindow");
_glfw.x11.xlib.UnsetICFocus = (PFN_XUnsetICFocus)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnsetICFocus");
_glfw.x11.xlib.VaCreateNestedList = (PFN_XVaCreateNestedList)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XVaCreateNestedList");
_glfw.x11.xlib.VisualIDFromVisual = (PFN_XVisualIDFromVisual)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XVisualIDFromVisual");
_glfw.x11.xlib.WarpPointer = (PFN_XWarpPointer)
@ -1517,6 +1526,8 @@ int _glfwInitX11(void)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XrmUniqueQuark");
_glfw.x11.xlib.UnregisterIMInstantiateCallback = (PFN_XUnregisterIMInstantiateCallback)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnregisterIMInstantiateCallback");
_glfw.x11.xlib.mbResetIC = (PFN_XmbResetIC)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XmbResetIC");
_glfw.x11.xlib.utf8LookupString = (PFN_Xutf8LookupString)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "Xutf8LookupString");
_glfw.x11.xlib.utf8SetWMProperties = (PFN_Xutf8SetWMProperties)

View File

@ -91,6 +91,9 @@
#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3
#define STYLE_OVERTHESPOT (XIMPreeditNothing | XIMStatusNothing)
#define STYLE_ONTHESPOT (XIMPreeditCallbacks | XIMStatusCallbacks)
typedef XID GLXWindow;
typedef XID GLXDrawable;
typedef struct __GLXFBConfig* GLXFBConfig;
@ -165,6 +168,7 @@ typedef Status (* PFN_XSendEvent)(Display*,Window,Bool,long,XEvent*);
typedef int (* PFN_XSetClassHint)(Display*,Window,XClassHint*);
typedef XErrorHandler (* PFN_XSetErrorHandler)(XErrorHandler);
typedef void (* PFN_XSetICFocus)(XIC);
typedef char* (* PFN_XSetICValues)(XIC,...);
typedef char* (* PFN_XSetIMValues)(XIM,...);
typedef int (* PFN_XSetInputFocus)(Display*,Window,int,Time);
typedef char* (* PFN_XSetLocaleModifiers)(const char*);
@ -180,6 +184,7 @@ typedef int (* PFN_XUndefineCursor)(Display*,Window);
typedef int (* PFN_XUngrabPointer)(Display*,Time);
typedef int (* PFN_XUnmapWindow)(Display*,Window);
typedef void (* PFN_XUnsetICFocus)(XIC);
typedef XVaNestedList (* PFN_XVaCreateNestedList)(int,...);
typedef VisualID (* PFN_XVisualIDFromVisual)(Visual*);
typedef int (* PFN_XWarpPointer)(Display*,Window,Window,int,int,unsigned int,unsigned int,int,int);
typedef void (* PFN_XkbFreeKeyboard)(XkbDescPtr,unsigned int,Bool);
@ -191,6 +196,7 @@ typedef KeySym (* PFN_XkbKeycodeToKeysym)(Display*,KeyCode,int,int);
typedef Bool (* PFN_XkbQueryExtension)(Display*,int*,int*,int*,int*,int*);
typedef Bool (* PFN_XkbSelectEventDetails)(Display*,unsigned int,unsigned int,unsigned long,unsigned long);
typedef Bool (* PFN_XkbSetDetectableAutoRepeat)(Display*,Bool,Bool*);
typedef char* (* PFN_XmbResetIC)(XIC);
typedef void (* PFN_XrmDestroyDatabase)(XrmDatabase);
typedef Bool (* PFN_XrmGetResource)(XrmDatabase,const char*,const char*,char**,XrmValue*);
typedef XrmDatabase (* PFN_XrmGetStringDatabase)(const char*);
@ -265,6 +271,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char
#define XSetClassHint _glfw.x11.xlib.SetClassHint
#define XSetErrorHandler _glfw.x11.xlib.SetErrorHandler
#define XSetICFocus _glfw.x11.xlib.SetICFocus
#define XSetICValues _glfw.x11.xlib.SetICValues
#define XSetIMValues _glfw.x11.xlib.SetIMValues
#define XSetInputFocus _glfw.x11.xlib.SetInputFocus
#define XSetLocaleModifiers _glfw.x11.xlib.SetLocaleModifiers
@ -280,6 +287,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char
#define XUngrabPointer _glfw.x11.xlib.UngrabPointer
#define XUnmapWindow _glfw.x11.xlib.UnmapWindow
#define XUnsetICFocus _glfw.x11.xlib.UnsetICFocus
#define XVaCreateNestedList _glfw.x11.xlib.VaCreateNestedList
#define XVisualIDFromVisual _glfw.x11.xlib.VisualIDFromVisual
#define XWarpPointer _glfw.x11.xlib.WarpPointer
#define XkbFreeKeyboard _glfw.x11.xkb.FreeKeyboard
@ -291,6 +299,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char
#define XkbQueryExtension _glfw.x11.xkb.QueryExtension
#define XkbSelectEventDetails _glfw.x11.xkb.SelectEventDetails
#define XkbSetDetectableAutoRepeat _glfw.x11.xkb.SetDetectableAutoRepeat
#define XmbResetIC _glfw.x11.xlib.mbResetIC
#define XrmDestroyDatabase _glfw.x11.xrm.DestroyDatabase
#define XrmGetResource _glfw.x11.xrm.GetResource
#define XrmGetStringDatabase _glfw.x11.xrm.GetStringDatabase
@ -577,6 +586,8 @@ typedef struct _GLFWlibraryX11
XContext context;
// XIM input method
XIM im;
// XIM input method style
XIMStyle imStyle;
// The previous X error handler, to be restored later
XErrorHandler errorHandler;
// Most recent error code received by X error handler
@ -722,6 +733,7 @@ typedef struct _GLFWlibraryX11
PFN_XSetClassHint SetClassHint;
PFN_XSetErrorHandler SetErrorHandler;
PFN_XSetICFocus SetICFocus;
PFN_XSetICValues SetICValues;
PFN_XSetIMValues SetIMValues;
PFN_XSetInputFocus SetInputFocus;
PFN_XSetLocaleModifiers SetLocaleModifiers;
@ -737,9 +749,11 @@ typedef struct _GLFWlibraryX11
PFN_XUngrabPointer UngrabPointer;
PFN_XUnmapWindow UnmapWindow;
PFN_XUnsetICFocus UnsetICFocus;
PFN_XVaCreateNestedList VaCreateNestedList;
PFN_XVisualIDFromVisual VisualIDFromVisual;
PFN_XWarpPointer WarpPointer;
PFN_XUnregisterIMInstantiateCallback UnregisterIMInstantiateCallback;
PFN_XmbResetIC mbResetIC;
PFN_Xutf8LookupString utf8LookupString;
PFN_Xutf8SetWMProperties utf8SetWMProperties;
} xlib;

View File

@ -410,29 +410,6 @@ static void updateWindowMode(_GLFWwindow* window)
}
}
// Decode a Unicode code point from a UTF-8 stream
// Based on cutef8 by Jeff Bezanson (Public Domain)
//
static uint32_t decodeUTF8(const char** s)
{
uint32_t codepoint = 0, count = 0;
static const uint32_t offsets[] =
{
0x00000000u, 0x00003080u, 0x000e2080u,
0x03c82080u, 0xfa082080u, 0x82082080u
};
do
{
codepoint = (codepoint << 6) + (unsigned char) **s;
(*s)++;
count++;
} while ((**s & 0xc0) == 0x80);
assert(count <= 6);
return codepoint - offsets[count - 1];
}
// Convert the specified Latin-1 string to UTF-8
//
static char* convertLatin1toUTF8(const char* source)
@ -553,197 +530,241 @@ static void enableCursor(_GLFWwindow* window)
updateCursorImage(window);
}
// TODO This callback is replaced by _createXIMPreeditCallbacks. Is there a possibility that this clearing process is necessary?
// Clear its handle when the input context has been destroyed
// static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData)
// {
// _GLFWwindow* window = (_GLFWwindow*) clientData;
// window->x11.ic = NULL;
// }
// Update cursor position to decide candidate window
static void _ximChangeCursorPosition(XIC xic, _GLFWwindow* window)
//
static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData)
{
XVaNestedList preedit_attr;
XPoint spot;
spot.x = window->preeditCursorPosX;
spot.y = window->preeditCursorPosY + window->preeditCursorHeight;
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
_GLFWwindow* window = (_GLFWwindow*) clientData;
window->x11.ic = NULL;
}
// IME Start callback (do nothing)
//
static void _ximPreeditStartCallback(XIC xic, XPointer clientData, XPointer callData)
{
}
// IME Done callback (do nothing)
//
static void _ximPreeditDoneCallback(XIC xic, XPointer clientData, XPointer callData)
{
}
// IME Draw callback
static void _ximPreeditDrawCallback(XIC xic, XPointer clientData, XIMPreeditDrawCallbackStruct *callData)
// When using the dafault style: STYLE_OVERTHESPOT, this is not used since applications
// don't need to display preedit texts.
//
static void _ximPreeditDrawCallback(XIC xic, XPointer clientData, XIMPreeditDrawCallbackStruct* callData)
{
int i, j, length, ctext, rstart, rend;
XIMText* text;
const char* src;
unsigned int codePoint;
unsigned int* preeditText;
XIMFeedback f;
_GLFWwindow* window = (_GLFWwindow*)clientData;
_GLFWwindow* window = (_GLFWwindow*) clientData;
_GLFWpreedit* preedit = &window->preedit;
// keep cursor position to reduce API call
int cursorX = window->preeditCursorPosX;
int cursorY = window->preeditCursorPosY;
int cursorHeight = window->preeditCursorHeight;
if (!callData->text) {
if (!callData->text)
{
// preedit text is empty
window->ntext = 0;
window->nblocks = 0;
_glfwInputPreedit(window, 0);
preedit->textCount = 0;
preedit->blockSizesCount = 0;
preedit->focusedBlockIndex = 0;
preedit->caretIndex = 0;
_glfwInputPreedit(window);
return;
} else {
text = callData->text;
length = callData->chg_length;
if (text->encoding_is_wchar) {
// wchar is not supported
return;
}
ctext = window->ctext;
while (ctext < length+1) {
ctext = (ctext == 0) ? 1 : ctext * 2;
}
if (ctext != window->ctext) {
preeditText = _glfw_realloc(window->preeditText, sizeof(unsigned int)*ctext);
if (preeditText == NULL) {
}
else if (callData->text->encoding_is_wchar)
{
// wchar is not supported
return;
}
else
{
XIMText* text = callData->text;
int textLen = preedit->textCount + text->length - callData->chg_length;
int textBufferCount = preedit->textBufferCount;
int i, j, rstart, rend;
const char* src;
// realloc preedit text
while (textBufferCount < textLen + 1)
textBufferCount = (textBufferCount == 0) ? 1 : textBufferCount * 2;
if (textBufferCount != preedit->textBufferCount)
{
unsigned int* preeditText = _glfw_realloc(preedit->text,
sizeof(unsigned int) * textBufferCount);
if (preeditText == NULL)
return;
}
window->preeditText = preeditText;
window->ctext = ctext;
preedit->text = preeditText;
preedit->textBufferCount = textBufferCount;
}
window->ntext = length;
window->preeditText[length] = 0;
if (window->cblocks == 0) {
window->preeditAttributeBlocks = _glfw_calloc(4, sizeof(int));
window->cblocks = 4;
preedit->textCount = textLen;
preedit->text[textLen] = 0;
// realloc block sizes
if (preedit->blockSizesBufferCount == 0)
{
preedit->blockSizes = _glfw_calloc(4, sizeof(int));
preedit->blockSizesBufferCount = 4;
}
// store preedit text
src = text->string.multi_byte;
rend = 0;
rstart = length;
for (i = 0, j = 0; i < text->length; i++) {
codePoint = decodeUTF8(&src);
if (i < callData->chg_first || callData->chg_first+length < i) {
rstart = textLen;
for (i = 0, j = callData->chg_first; i < text->length; i++)
{
XIMFeedback f;
if (i < callData->chg_first || callData->chg_first + textLen < i)
continue;
}
window->preeditText[j++] = codePoint;
preedit->text[j++] = _glfwDecodeUTF8(&src);
f = text->feedback[i];
if ((f & XIMReverse) || (f & XIMHighlight)) {
if ((f & XIMReverse) || (f & XIMHighlight))
{
rend = i;
if (i < rstart) {
if (i < rstart)
rstart = i;
}
}
}
if (rstart == length) {
window->nblocks = 1;
window->preeditAttributeBlocks[0] = length;
window->preeditAttributeBlocks[1] = 0;
_glfwInputPreedit(window, 0);
} else if (rstart == 0) {
if (rend == length -1) {
window->nblocks = 1;
window->preeditAttributeBlocks[0] = length;
window->preeditAttributeBlocks[1] = 0;
_glfwInputPreedit(window, 0);
} else {
window->nblocks = 2;
window->preeditAttributeBlocks[0] = rend + 1;
window->preeditAttributeBlocks[1] = length - rend - 1;
window->preeditAttributeBlocks[2] = 0;
_glfwInputPreedit(window, 0);
}
} else if (rend == length -1) {
window->nblocks = 2;
window->preeditAttributeBlocks[0] = rstart;
window->preeditAttributeBlocks[1] = length - rstart;
window->preeditAttributeBlocks[2] = 0;
_glfwInputPreedit(window, 1);
} else {
window->nblocks = 3;
window->preeditAttributeBlocks[0] = rstart;
window->preeditAttributeBlocks[1] = rend - rstart + 1;
window->preeditAttributeBlocks[2] = length - rend - 1;
window->preeditAttributeBlocks[3] = 0;
_glfwInputPreedit(window, 1);
// store block sizes
// TODO: It doesn't care callData->chg_first != 0 case although it's quite rare.
if (rstart == textLen)
{
preedit->blockSizesCount = 1;
preedit->blockSizes[0] = textLen;
preedit->blockSizes[1] = 0;
preedit->focusedBlockIndex = 0;
preedit->caretIndex = callData->caret;
_glfwInputPreedit(window);
}
if ((cursorX != window->preeditCursorPosX)
|| (cursorY != window->preeditCursorPosY)
|| (cursorHeight != window->preeditCursorHeight)) {
_ximChangeCursorPosition(xic, window);
else if (rstart == 0)
{
if (rend == textLen -1)
{
preedit->blockSizesCount = 1;
preedit->blockSizes[0] = textLen;
preedit->blockSizes[1] = 0;
preedit->focusedBlockIndex = 0;
preedit->caretIndex = callData->caret;
_glfwInputPreedit(window);
}
else
{
preedit->blockSizesCount = 2;
preedit->blockSizes[0] = rend + 1;
preedit->blockSizes[1] = textLen - rend - 1;
preedit->blockSizes[2] = 0;
preedit->focusedBlockIndex = 0;
preedit->caretIndex = callData->caret;
_glfwInputPreedit(window);
}
}
else if (rend == textLen - 1)
{
preedit->blockSizesCount = 2;
preedit->blockSizes[0] = rstart;
preedit->blockSizes[1] = textLen - rstart;
preedit->blockSizes[2] = 0;
preedit->focusedBlockIndex = 1;
preedit->caretIndex = callData->caret;
_glfwInputPreedit(window);
}
else
{
preedit->blockSizesCount = 3;
preedit->blockSizes[0] = rstart;
preedit->blockSizes[1] = rend - rstart + 1;
preedit->blockSizes[2] = textLen - rend - 1;
preedit->blockSizes[3] = 0;
preedit->focusedBlockIndex = 1;
preedit->caretIndex = callData->caret;
_glfwInputPreedit(window);
}
}
}
// IME Caret callback (do nothing)
//
static void _ximPreeditCaretCallback(XIC xic, XPointer clientData, XPointer callData)
{
}
// IME Status Start callback
// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status
// can not be taken.
//
static void _ximStatusStartCallback(XIC xic, XPointer clientData, XPointer callData)
{
_GLFWwindow* window = (_GLFWwindow*)clientData;
_GLFWwindow* window = (_GLFWwindow*) clientData;
window->x11.imeFocus = GLFW_TRUE;
}
// IME Status Done callback
// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status
// can not be taken.
//
static void _ximStatusDoneCallback(XIC xic, XPointer clientData, XPointer callData)
{
_GLFWwindow* window = (_GLFWwindow*)clientData;
_GLFWwindow* window = (_GLFWwindow*) clientData;
window->x11.imeFocus = GLFW_FALSE;
}
// IME Status Draw callback
// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status
// can not be taken.
//
static void _ximStatusDrawCallback(XIC xic, XPointer clientData, XIMStatusDrawCallbackStruct* callData)
{
_GLFWwindow* window = (_GLFWwindow*)clientData;
_GLFWwindow* window = (_GLFWwindow*) clientData;
_glfwInputIMEStatus(window);
}
// Create XIM Preedit callback
// When using the dafault style: STYLE_OVERTHESPOT, this is not used since applications
// don't need to display preedit texts.
//
static XVaNestedList _createXIMPreeditCallbacks(_GLFWwindow* window)
{
window->x11.preeditStartCallback.client_data = (XPointer)window;
window->x11.preeditStartCallback.callback = (XIMProc)_ximPreeditStartCallback;
window->x11.preeditDoneCallback.client_data = (XPointer)window;
window->x11.preeditDoneCallback.callback = (XIMProc)_ximPreeditDoneCallback;
window->x11.preeditDrawCallback.client_data = (XPointer)window;
window->x11.preeditDrawCallback.callback = (XIMProc)_ximPreeditDrawCallback;
window->x11.preeditCaretCallback.client_data = (XPointer)window;
window->x11.preeditCaretCallback.callback = (XIMProc)_ximPreeditCaretCallback;
return XVaCreateNestedList (0,
XNPreeditStartCallback, &window->x11.preeditStartCallback.client_data,
XNPreeditDoneCallback, &window->x11.preeditDoneCallback.client_data,
XNPreeditDrawCallback, &window->x11.preeditDrawCallback.client_data,
XNPreeditCaretCallback, &window->x11.preeditCaretCallback.client_data,
NULL);
window->x11.preeditStartCallback.client_data = (XPointer) window;
window->x11.preeditStartCallback.callback = (XIMProc) _ximPreeditStartCallback;
window->x11.preeditDoneCallback.client_data = (XPointer) window;
window->x11.preeditDoneCallback.callback = (XIMProc) _ximPreeditDoneCallback;
window->x11.preeditDrawCallback.client_data = (XPointer) window;
window->x11.preeditDrawCallback.callback = (XIMProc) _ximPreeditDrawCallback;
window->x11.preeditCaretCallback.client_data = (XPointer) window;
window->x11.preeditCaretCallback.callback = (XIMProc) _ximPreeditCaretCallback;
return XVaCreateNestedList(0,
XNPreeditStartCallback,
&window->x11.preeditStartCallback.client_data,
XNPreeditDoneCallback,
&window->x11.preeditDoneCallback.client_data,
XNPreeditDrawCallback,
&window->x11.preeditDrawCallback.client_data,
XNPreeditCaretCallback,
&window->x11.preeditCaretCallback.client_data,
NULL);
}
// Create XIM status callback
// When using the dafault style: STYLE_OVERTHESPOT, this is not used and the IME status
// can not be taken.
//
static XVaNestedList _createXIMStatusCallbacks(_GLFWwindow* window)
{
window->x11.statusStartCallback.client_data = (XPointer)window;
window->x11.statusStartCallback.callback = (XIMProc)_ximStatusStartCallback;
window->x11.statusDoneCallback.client_data = (XPointer)window;
window->x11.statusDoneCallback.callback = (XIMProc)_ximStatusDoneCallback;
window->x11.statusDrawCallback.client_data = (XPointer)window;
window->x11.statusDrawCallback.callback = (XIMProc)_ximStatusDrawCallback;
return XVaCreateNestedList (0,
XNStatusStartCallback, &window->x11.statusStartCallback.client_data,
XNStatusDoneCallback, &window->x11.statusDoneCallback.client_data,
XNStatusDrawCallback, &window->x11.statusDrawCallback.client_data,
NULL);
window->x11.statusStartCallback.client_data = (XPointer) window;
window->x11.statusStartCallback.callback = (XIMProc) _ximStatusStartCallback;
window->x11.statusDoneCallback.client_data = (XPointer) window;
window->x11.statusDoneCallback.callback = (XIMProc) _ximStatusDoneCallback;
window->x11.statusDrawCallback.client_data = (XPointer) window;
window->x11.statusDrawCallback.callback = (XIMProc) _ximStatusDrawCallback;
return XVaCreateNestedList(0,
XNStatusStartCallback,
&window->x11.statusStartCallback.client_data,
XNStatusDoneCallback,
&window->x11.statusDoneCallback.client_data,
XNStatusDrawCallback,
&window->x11.statusDrawCallback.client_data,
NULL);
}
// Create the X11 window (and its colormap)
@ -1475,7 +1496,7 @@ static void processEvent(XEvent *event)
const char* c = chars;
chars[count] = '\0';
while (c - chars < count)
_glfwInputChar(window, decodeUTF8(&c), mods, plain);
_glfwInputChar(window, _glfwDecodeUTF8(&c), mods, plain);
}
if (chars != buffer)
@ -2106,26 +2127,60 @@ void _glfwPushSelectionToManagerX11(void)
void _glfwCreateInputContextX11(_GLFWwindow* window)
{
XVaNestedList preeditList = _createXIMPreeditCallbacks(window);
XVaNestedList statusList = _createXIMStatusCallbacks(window);
XIMCallback callback;
callback.callback = (XIMProc) inputContextDestroyCallback;
callback.client_data = (XPointer) window;
window->x11.ic = XCreateIC(_glfw.x11.im,
XNInputStyle,
XIMPreeditCallbacks | XIMStatusCallbacks,
XNClientWindow,
window->x11.handle,
XNFocusWindow,
window->x11.handle,
XNPreeditAttributes,
preeditList,
XNStatusAttributes,
statusList,
NULL);
XFree(preeditList);
XFree(statusList);
window->x11.imeFocus = GLFW_FALSE;
if (_glfw.x11.imStyle == STYLE_ONTHESPOT)
{
// On X11, on-the-spot style is unstable.
// Status callbacks are not called and the preedit cursor position
// can not be changed.
XVaNestedList preeditList = _createXIMPreeditCallbacks(window);
XVaNestedList statusList = _createXIMStatusCallbacks(window);
window->x11.ic = XCreateIC(_glfw.x11.im,
XNInputStyle,
_glfw.x11.imStyle,
XNClientWindow,
window->x11.handle,
XNFocusWindow,
window->x11.handle,
XNPreeditAttributes,
preeditList,
XNStatusAttributes,
statusList,
XNDestroyCallback,
&callback,
NULL);
XFree(preeditList);
XFree(statusList);
}
else if (_glfw.x11.imStyle == STYLE_OVERTHESPOT)
{
window->x11.ic = XCreateIC(_glfw.x11.im,
XNInputStyle,
_glfw.x11.imStyle,
XNClientWindow,
window->x11.handle,
XNFocusWindow,
window->x11.handle,
XNDestroyCallback,
&callback,
NULL);
}
else
{
// (XIMPreeditNothing | XIMStatusNothing) is considered as STYLE_OVERTHESPOT.
// So this branch should not be used now.
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Failed to create input context.");
return;
}
if (window->x11.ic)
{
XWindowAttributes attribs;
@ -3274,21 +3329,90 @@ const char* _glfwGetClipboardStringX11(void)
return getSelectionString(_glfw.x11.CLIPBOARD);
}
// When using STYLE_ONTHESPOT, this doesn't work and the cursor position can't be updated
//
void _glfwUpdatePreeditCursorRectangleX11(_GLFWwindow* window)
{
XVaNestedList preedit_attr;
XPoint spot;
_GLFWpreedit* preedit = &window->preedit;
if (!window->x11.ic)
return;
spot.x = preedit->cursorPosX + preedit->cursorWidth;
spot.y = preedit->cursorPosY + preedit->cursorHeight;
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
XSetICValues(window->x11.ic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
}
void _glfwResetPreeditTextX11(_GLFWwindow* window)
{
XIC ic = window->x11.ic;
_GLFWpreedit* preedit = &window->preedit;
/* restore conversion state after resetting ic later */
XIMPreeditState preedit_state = XIMPreeditUnKnown;
XVaNestedList preedit_attr;
char* result;
if (!ic)
return;
// Can not manage IME in the case of over-the-spot.
if (_glfw.x11.imStyle == STYLE_OVERTHESPOT)
return;
if (preedit->textCount == 0)
return;
preedit_attr = XVaCreateNestedList(0, XNPreeditState, &preedit_state, NULL);
XGetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
result = XmbResetIC(ic);
preedit_attr = XVaCreateNestedList(0, XNPreeditState, preedit_state, NULL);
XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
preedit->textCount = 0;
preedit->blockSizesCount = 0;
preedit->focusedBlockIndex = 0;
preedit->caretIndex = 0;
_glfwInputPreedit(window);
XFree (result);
}
void _glfwSetIMEStatusX11(_GLFWwindow* window, int active)
{
XIC ic = window->x11.ic;
if (!ic)
return;
// Can not manage IME in the case of over-the-spot.
if (_glfw.x11.imStyle == STYLE_OVERTHESPOT)
return;
if (active)
XSetICFocus(ic);
else
XUnsetICFocus(ic);
}
int _glfwGetIMEStatusX11(_GLFWwindow* window)
{
return GLFW_FALSE;
if (!window->x11.ic)
return GLFW_FALSE;
// Can not manage IME in the case of over-the-spot.
if (_glfw.x11.imStyle == STYLE_OVERTHESPOT)
return GLFW_FALSE;
return window->x11.imeFocus;
}
EGLenum _glfwGetEGLPlatformX11(EGLint** attribs)
@ -3489,46 +3613,6 @@ VkResult _glfwCreateWindowSurfaceX11(VkInstance instance,
}
}
void _glfwPlatformResetPreeditText(_GLFWwindow* window) {
XIC ic = window->x11.ic;
/* restore conversion state after resetting ic later */
XIMPreeditState preedit_state = XIMPreeditUnKnown;
XVaNestedList preedit_attr;
char* result;
if (window->ntext == 0)
return;
preedit_attr = XVaCreateNestedList(0, XNPreeditState, &preedit_state, NULL);
XGetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
result = XmbResetIC(ic);
preedit_attr = XVaCreateNestedList(0, XNPreeditState, preedit_state, NULL);
XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
window->ntext = 0;
window->nblocks = 0;
_glfwInputPreedit(window, 0);
XFree (result);
}
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active) {
XIC ic = window->x11.ic;
if (active) {
XSetICFocus(ic);
} else {
XUnsetICFocus(ic);
}
}
int _glfwPlatformGetIMEStatus(_GLFWwindow* window) {
return window->x11.imeFocus;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////