Index: blender/intern/ghost/intern/GHOST_SystemX11.h =================================================================== --- blender/intern/ghost/intern/GHOST_SystemX11.h (revision 44609) +++ blender/intern/ghost/intern/GHOST_SystemX11.h (working copy) @@ -307,6 +307,10 @@ * X11 window xwind */ +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + bool openIM(); +#endif + GHOST_WindowX11 * findGhostWindow( Window xwind Index: blender/intern/ghost/intern/GHOST_WindowX11.h =================================================================== --- blender/intern/ghost/intern/GHOST_WindowX11.h (revision 44609) +++ blender/intern/ghost/intern/GHOST_WindowX11.h (working copy) @@ -226,6 +226,8 @@ #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) XIC getX11_XIC() { return m_xic; } + + bool createIC(); #endif #ifdef WITH_XDND Index: blender/intern/ghost/intern/GHOST_WindowX11.cpp =================================================================== --- blender/intern/ghost/intern/GHOST_WindowX11.cpp (revision 44609) +++ blender/intern/ghost/intern/GHOST_WindowX11.cpp (working copy) @@ -403,10 +403,8 @@ } #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); + if (!createIC()) + printf("Failed to create XIM input context\n"); #endif // Set the window icon @@ -475,6 +473,46 @@ XFlush(m_display); } +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) +static void destroyICCallback(XIC xic, XPointer ptr, XPointer data) +{ + printf("XIM input context destroyed\n"); + + if (ptr) + *(XIC *)ptr = NULL; +} + +bool GHOST_WindowX11::createIC() +{ + 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 Index: blender/intern/ghost/intern/GHOST_SystemX11.cpp =================================================================== --- blender/intern/ghost/intern/GHOST_SystemX11.cpp (revision 44609) +++ blender/intern/ghost/intern/GHOST_SystemX11.cpp (working copy) @@ -93,9 +93,15 @@ abort(); //was return before, but this would just mean it will crash later } +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + /* set locale to system's one */ + setlocale(LC_ALL, ""); + /* set locale modifiers such as "@im=ibus" */ + XSetLocaleModifiers(""); + /* 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); + if (!openIM()) + printf("Could not connect to XIM server\n"); #endif m_delete_window_atom @@ -273,6 +279,32 @@ return window; } +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) +static void destroyIMCallback(XIM xim, XPointer ptr, XPointer data) +{ + printf("XIM server died\n"); + + if (ptr) + *(XIM *)ptr = NULL; +} + +bool GHOST_SystemX11::openIM() +{ + if (!m_display) + return false; + + 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,29 @@ while (XPending(m_display)) { XEvent xevent; XNextEvent(m_display, &xevent); + +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + /* recover XIM and XIC if XIM server has been restarted */ + if (xevent.type == KeyPress) { + if (!m_xim && openIM()) + printf("Connected to XIM server again\n"); + + if (m_xim) { + GHOST_WindowX11 * window = findGhostWindow(xevent.xany.window); + if (window && !window->getX11_XIC()) + window->createIC(); + } + } + + /* 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; } @@ -529,7 +584,16 @@ 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. */ + char utf8_array[16 * 6]; /* 16 utf8 characters */ + char *utf8_buf = utf8_array; + int len = 1; /* at least one null character will be stored */ +#else + char utf8_buf[6]; +#endif GHOST_TKey gkey = convertXKey(key_sym); GHOST_TEventType type = (xke->type == KeyPress) ? @@ -541,15 +605,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), &key_sym, &status))) { utf8_buf[0]= '\0'; } + if (status == XBufferOverflow) { + utf8_buf = (char *) malloc(len); + 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 */ @@ -565,12 +634,11 @@ 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 { @@ -589,6 +657,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; } @@ -669,6 +773,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(), Index: blender/source/blender/editors/space_text/text_ops.c =================================================================== --- blender/source/blender/editors/space_text/text_ops.c (revision 44609) +++ blender/source/blender/editors/space_text/text_ops.c (working copy) @@ -2837,8 +2837,11 @@ // if(!RNA_struct_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */ if(!RNA_string_length(op->ptr, "text")) { - /* if alt/ctrl/super are pressed pass through */ - if(event->ctrl || event->oskey) { + /* if alt/ctrl/super are pressed pass through except for utf8 character event + (when input method are used for utf8 inputs, the user may assign key event + including alt/ctrl/super like ctrl+m to commit utf8 string. in such case, + the modifiers in the utf8 character event make no sense.) */ + if((event->ctrl || event->oskey) && !event->utf8_buf[0]) { return OPERATOR_PASS_THROUGH; } else {