Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-07-11 12:31:54 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-07-11 12:31:54 +0400
commit35b6f41f4651992bc6a1726fc3d123f7bc54d32d (patch)
tree2cb3cd97ce3489af60aabf86982fd4fbba698ffc
parent4e213765ecd2d05251ead548b01a55b9f00d0ec0 (diff)
patch [#30274] XIM improvement (non-latin support + connection recovery)
from Shinsuke Irie (irie) (from the tracker submission) - allow us to input non-latin languages such as Japanese/Chinese - recover XIM connection and its input contexts when XIM server restarted Currently it supports only "root window" style input, while most people (and I) want "over the spot" or "on the spot" style one. Probably the implementation of "over the spot" or "on the spot" style becomes much complicated, because XIM server requires the coordinates of current cursor location relative to the screen in order to show the candidate window in appropriate position.
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp146
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h4
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp46
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.h2
4 files changed, 183 insertions, 15 deletions
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 2d8f5e7890c..03a952fafb3 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -93,9 +93,12 @@ GHOST_SystemX11(
abort(); //was return before, but this would just mean it will crash later
}
- /* Open a connection to the X input manager */
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
- m_xim = XOpenIM(m_display, NULL, (char *)GHOST_X11_RES_NAME, (char *)GHOST_X11_RES_CLASS);
+ /* note -- don't open connection to XIM server here, because the locale
+ * has to be set before opening the connection but setlocale() has not
+ * been called yet. the connection will be opened after entering
+ * the event loop. */
+ m_xim = NULL;
#endif
m_delete_window_atom
@@ -273,6 +276,35 @@ createWindow(
return window;
}
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+static void destroyIMCallback(XIM xim, XPointer ptr, XPointer data)
+{
+ GHOST_PRINT("XIM server died\n");
+
+ if (ptr)
+ *(XIM *)ptr = NULL;
+}
+
+bool GHOST_SystemX11::openX11_IM()
+{
+ if (!m_display)
+ return false;
+
+ /* set locale modifiers such as "@im=ibus" specified by XMODIFIERS */
+ XSetLocaleModifiers("");
+
+ m_xim = XOpenIM(m_display, NULL, (char *)GHOST_X11_RES_NAME, (char *)GHOST_X11_RES_CLASS);
+ if (!m_xim)
+ return false;
+
+ XIMCallback destroy;
+ destroy.callback = (XIMProc)destroyIMCallback;
+ destroy.client_data = (XPointer)&m_xim;
+ XSetIMValues(m_xim, XNDestroyCallback, &destroy, NULL);
+ return true;
+}
+#endif
+
GHOST_WindowX11 *
GHOST_SystemX11::
findGhostWindow(
@@ -408,6 +440,38 @@ processEvents(
while (XPending(m_display)) {
XEvent xevent;
XNextEvent(m_display, &xevent);
+
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+ /* open connection to XIM server and create input context (XIC)
+ * when receiving the first FocusIn or KeyPress event after startup,
+ * or recover XIM and XIC when the XIM server has been restarted */
+ if (xevent.type == FocusIn || xevent.type == KeyPress) {
+ if (!m_xim && openX11_IM()) {
+ GHOST_PRINT("Connected to XIM server\n");
+ }
+
+ if (m_xim) {
+ GHOST_WindowX11 * window = findGhostWindow(xevent.xany.window);
+ if (window && !window->getX11_XIC() && window->createX11_XIC()) {
+ GHOST_PRINT("XIM input context created\n");
+ if (xevent.type == KeyPress)
+ /* we can assume the window has input focus
+ * here, because key events are received only
+ * when the window is focused. */
+ XSetICFocus(window->getX11_XIC());
+ }
+ }
+ }
+
+ /* dispatch event to XIM server */
+ if ((XFilterEvent(&xevent, (Window)NULL) == True) && (xevent.type != KeyRelease)) {
+ /* do nothing now, the event is consumed by XIM.
+ * however, KeyRelease event should be processed
+ * here, otherwise modifiers remain activated. */
+ continue;
+ }
+#endif
+
processEvent(&xevent);
anyProcessed = true;
}
@@ -535,7 +599,19 @@ GHOST_SystemX11::processEvent(XEvent *xe)
XKeyEvent *xke = &(xe->xkey);
KeySym key_sym = XLookupKeysym(xke, 0);
char ascii;
- char utf8_buf[6]; /* 6 is enough for a utf8 char */
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+ /* utf8_array[] is initial buffer used for Xutf8LookupString().
+ * if the length of the utf8 string exceeds this array, allocate
+ * another memory area and call Xutf8LookupString() again.
+ * the last 5 bytes are used to avoid segfault that might happen
+ * at the end of this buffer when the constructor of GHOST_EventKey
+ * reads 6 bytes regardless of the effective data length. */
+ char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */
+ char *utf8_buf = utf8_array;
+ int len = 1; /* at least one null character will be stored */
+#else
+ char *utf8_buf = NULL;
+#endif
GHOST_TKey gkey = convertXKey(key_sym);
GHOST_TEventType type = (xke->type == KeyPress) ?
@@ -547,15 +623,20 @@ GHOST_SystemX11::processEvent(XEvent *xe)
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
/* getting unicode on key-up events gives XLookupNone status */
- if (xke->type == KeyPress) {
+ XIC xic = window->getX11_XIC();
+ if (xic && xke->type == KeyPress) {
Status status;
- int len;
/* use utf8 because its not locale depentant, from xorg docs */
- if (!(len = Xutf8LookupString(window->getX11_XIC(), xke, utf8_buf, sizeof(utf8_buf), &key_sym, &status))) {
+ if (!(len = Xutf8LookupString(xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) {
utf8_buf[0] = '\0';
}
+ if (status == XBufferOverflow) {
+ utf8_buf = (char *) malloc(len + 5);
+ len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status);
+ }
+
if ((status == XLookupChars || status == XLookupBoth)) {
if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */
/* do nothing for now, this is valid utf8 */
@@ -571,19 +652,16 @@ GHOST_SystemX11::processEvent(XEvent *xe)
else {
printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
(unsigned int) key_sym,
- (status == XBufferOverflow ? "BufferOverflow" :
- status == XLookupNone ? "XLookupNone" :
+ (status == XLookupNone ? "XLookupNone" :
status == XLookupKeySym ? "XLookupKeySym" :
"Unknown status"));
- printf("'%.*s' %p %p\n", len, utf8_buf, window->getX11_XIC(), m_xim);
+ printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim);
}
}
else {
utf8_buf[0] = '\0';
}
-#else
- utf8_buf[0] = '\0';
#endif
g_event = new
@@ -595,6 +673,42 @@ GHOST_SystemX11::processEvent(XEvent *xe)
ascii,
utf8_buf
);
+
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+ /* when using IM for some languages such as Japanese,
+ * one event inserts multiple utf8 characters */
+ if (xic && xke->type == KeyPress) {
+ unsigned char c;
+ int i = 0;
+ while (1) {
+ /* search character boundary */
+ if ((unsigned char)utf8_buf[i++] > 0x7f) {
+ for (; i < len; ++i) {
+ c = utf8_buf[i];
+ if (c < 0x80 || c > 0xbf) break;
+ }
+ }
+
+ if (i >= len) break;
+
+ /* enqueue previous character */
+ pushEvent(g_event);
+
+ g_event = new
+ GHOST_EventKey(
+ getMilliSeconds(),
+ type,
+ window,
+ gkey,
+ '\0',
+ &utf8_buf[i]
+ );
+ }
+ }
+
+ if (utf8_buf != utf8_array)
+ free(utf8_buf);
+#endif
break;
}
@@ -675,6 +789,16 @@ GHOST_SystemX11::processEvent(XEvent *xe)
GHOST_TEventType gtype = (xfe.type == FocusIn) ?
GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+ XIC xic = window->getX11_XIC();
+ if (xic) {
+ if (xe->type == FocusIn)
+ XSetICFocus(xic);
+ else
+ XUnsetICFocus(xic);
+ }
+#endif
+
g_event = new
GHOST_Event(
getMilliSeconds(),
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index fd5a33c02d7..d86790e262f 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -309,6 +309,10 @@ private:
* X11 window xwind
*/
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+ bool openX11_IM();
+#endif
+
GHOST_WindowX11 *
findGhostWindow(
Window xwind
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index 285fbe96638..80d5b5a4652 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -401,10 +401,7 @@ GHOST_WindowX11(
}
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
- m_xic = XCreateIC(m_system->getX11_XIM(), XNClientWindow, m_window, XNFocusWindow, m_window,
- XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
- XNResourceName, GHOST_X11_RES_NAME, XNResourceClass,
- GHOST_X11_RES_CLASS, NULL);
+ m_xic = NULL;
#endif
// Set the window icon
@@ -478,6 +475,47 @@ GHOST_WindowX11(
XFlush(m_display);
}
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+static void destroyICCallback(XIC xic, XPointer ptr, XPointer data)
+{
+ GHOST_PRINT("XIM input context destroyed\n");
+
+ if (ptr) {
+ *(XIC *)ptr = NULL;
+ }
+}
+
+bool GHOST_WindowX11::createX11_XIC()
+{
+ XIM xim = m_system->getX11_XIM();
+ if (!xim)
+ return false;
+
+ XICCallback destroy;
+ destroy.callback = (XICProc)destroyICCallback;
+ destroy.client_data = (XPointer)&m_xic;
+ m_xic = XCreateIC(xim, XNClientWindow, m_window, XNFocusWindow, m_window,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNResourceName, GHOST_X11_RES_NAME,
+ XNResourceClass, GHOST_X11_RES_CLASS,
+ XNDestroyCallback, &destroy,
+ NULL);
+ if (!m_xic)
+ return false;
+
+ unsigned long fevent;
+ XGetICValues(m_xic, XNFilterEvents, &fevent, NULL);
+ XSelectInput(m_display, m_window,
+ ExposureMask | StructureNotifyMask |
+ KeyPressMask | KeyReleaseMask |
+ EnterWindowMask | LeaveWindowMask |
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | FocusChangeMask |
+ PropertyChangeMask | fevent);
+ return true;
+}
+#endif
+
#ifdef WITH_X11_XINPUT
/*
* Dummy function to get around IO Handler exiting if device invalid
diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h
index a04a43bc33a..753d8960a18 100644
--- a/intern/ghost/intern/GHOST_WindowX11.h
+++ b/intern/ghost/intern/GHOST_WindowX11.h
@@ -234,6 +234,8 @@ public:
XIC getX11_XIC() {
return m_xic;
}
+
+ bool createX11_XIC();
#endif
#ifdef WITH_XDND