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