Revision: 48827
          
http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=48827
Author:   campbellbarton
Date:     2012-07-11 08:31:54 +0000 (Wed, 11 Jul 2012)
Log Message:
-----------
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.

Modified Paths:
--------------
    trunk/blender/intern/ghost/intern/GHOST_SystemX11.cpp
    trunk/blender/intern/ghost/intern/GHOST_SystemX11.h
    trunk/blender/intern/ghost/intern/GHOST_WindowX11.cpp
    trunk/blender/intern/ghost/intern/GHOST_WindowX11.h

Modified: trunk/blender/intern/ghost/intern/GHOST_SystemX11.cpp
===================================================================
--- trunk/blender/intern/ghost/intern/GHOST_SystemX11.cpp       2012-07-11 
07:49:08 UTC (rev 48826)
+++ trunk/blender/intern/ghost/intern/GHOST_SystemX11.cpp       2012-07-11 
08:31:54 UTC (rev 48827)
@@ -93,9 +93,12 @@
                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 @@
        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 @@
                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 @@
                        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 @@
                        
 #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 @@
                                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 @@
                            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_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(),

Modified: trunk/blender/intern/ghost/intern/GHOST_SystemX11.h
===================================================================
--- trunk/blender/intern/ghost/intern/GHOST_SystemX11.h 2012-07-11 07:49:08 UTC 
(rev 48826)
+++ trunk/blender/intern/ghost/intern/GHOST_SystemX11.h 2012-07-11 08:31:54 UTC 
(rev 48827)
@@ -309,6 +309,10 @@
         * X11 window xwind
         */
 
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+       bool openX11_IM();
+#endif
+
        GHOST_WindowX11 *
        findGhostWindow(
            Window xwind

Modified: trunk/blender/intern/ghost/intern/GHOST_WindowX11.cpp
===================================================================
--- trunk/blender/intern/ghost/intern/GHOST_WindowX11.cpp       2012-07-11 
07:49:08 UTC (rev 48826)
+++ trunk/blender/intern/ghost/intern/GHOST_WindowX11.cpp       2012-07-11 
08:31:54 UTC (rev 48827)
@@ -401,10 +401,7 @@
        }
 
 #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 @@
        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

Modified: trunk/blender/intern/ghost/intern/GHOST_WindowX11.h
===================================================================
--- trunk/blender/intern/ghost/intern/GHOST_WindowX11.h 2012-07-11 07:49:08 UTC 
(rev 48826)
+++ trunk/blender/intern/ghost/intern/GHOST_WindowX11.h 2012-07-11 08:31:54 UTC 
(rev 48827)
@@ -234,6 +234,8 @@
        XIC getX11_XIC() {
                return m_xic;
        }
+
+       bool createX11_XIC();
 #endif
 
 #ifdef WITH_XDND

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to