initial support for unicode keyboard input for ghost & blenders WM.
authorCampbell Barton <ideasman42@gmail.com>
Thu, 20 Oct 2011 05:30:26 +0000 (05:30 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 20 Oct 2011 05:30:26 +0000 (05:30 +0000)
- currently X11 only, depends on Xinput (but should not break other os's).
- ghost stores utf8 buffer, copies to wmEvent's
- UI text input is currently the only area that uses this - not console or text editor.
- no rna access yet.

14 files changed:
intern/ghost/GHOST_Types.h
intern/ghost/intern/GHOST_EventKey.h
intern/ghost/intern/GHOST_SystemCarbon.cpp
intern/ghost/intern/GHOST_SystemCocoa.mm
intern/ghost/intern/GHOST_SystemSDL.cpp
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
intern/ghost/intern/GHOST_WindowX11.cpp
intern/ghost/intern/GHOST_WindowX11.h
source/blender/editors/interface/interface_handlers.c
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/gameengine/Ketsji/KX_PythonInit.cpp

index dd399a7aa955222c8b3ee89364da489c3c8dac41..78fc3f69c7af3332905a287cf689b5f795bb86a7 100644 (file)
@@ -468,6 +468,7 @@ typedef struct {
        GHOST_TKey              key;
        /** The ascii code for the key event ('\0' if none). */
        char                    ascii;
+       char                    utf8_buf[6];
 } GHOST_TEventKeyData;
 
 typedef struct {
index 3581cd86b200219cc10041bcaf1be244b2ee9fc2..a8fca1e8f2d98999b58a024c22cfc809a8386f5d 100644 (file)
@@ -55,6 +55,7 @@ public:
        {
                m_keyEventData.key = key;
                m_keyEventData.ascii = '\0';
+               m_keyEventData.utf8_buf[0]= '\0';
                m_data = &m_keyEventData;
        }
        
@@ -65,11 +66,13 @@ public:
         * @param key   The key code of the key.
         * @param ascii The ascii code for the key event.
         */
-       GHOST_EventKey(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow* window, GHOST_TKey key, char ascii)
+       GHOST_EventKey(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow* window, GHOST_TKey key, char ascii, const char utf8_buf[6])
                : GHOST_Event(msec, type, window)
        {
                m_keyEventData.key = key;
                m_keyEventData.ascii = ascii;
+               if (utf8_buf) memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf));
+               else                 m_keyEventData.utf8_buf[0]= '\0';
                m_data = &m_keyEventData;
        }
                
index 02ff5c0f5598ab26e36cce10e71cac5c2a6e32d8..762966c9b9e43db3b9ea29e7d430cbee25b0a84d 100644 (file)
@@ -931,7 +931,7 @@ OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
                                } else {
                                        type = GHOST_kEventKeyUp;
                                }
-                               pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
+                               pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii, NULL) );
 //                     }
                        break;
        
index 303c2b24497df406d08ee3aa47f8d0325af20a80..e8ec13f20df6511a11f1b6afe675eda0a2c1cc1b 100644 (file)
@@ -1656,6 +1656,8 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
        }
        
        switch ([event type]) {
+               char utf8_buf[6]= {'\0'}; /* TODO, unicode input */
+
                case NSKeyDown:
                case NSKeyUp:
                        charsIgnoringModifiers = [event charactersIgnoringModifiers];
@@ -1684,10 +1686,10 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
                                break; //Cmd-Q is directly handled by Cocoa
 
                        if ([event type] == NSKeyDown) {
-                               pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) );
+                               pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii, utf8_buf) );
                                //printf("\nKey down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
                        } else {
-                               pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) );
+                               pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii, utf8_buf) );
                                //printf("\nKey up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
                        }
                        break;
index f2cc45731fa0c5a7b2dbed0910bc9bbf71d07e8c..11bd562f7674537edfa35e93d6857c9ef98d4c78 100644 (file)
@@ -441,7 +441,7 @@ GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
                                }
                        }
 
-                       g_event= new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym);
+                       g_event= new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, NULL);
                }
                break;
        }
index 38f3985b13931a97e73d2308982f8bb93bb0971d..24b6474732e64b488f8efe7318b3aae65178a53a 100644 (file)
@@ -725,7 +725,8 @@ GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINP
                                                                        (LPSTR) &ascii, 1,
                                                                        NULL,NULL);
 
-               event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
+               /* TODO, last arg is utf8, need to pass utf8 arg */
+               event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii, NULL);
                
 #ifdef GHOST_DEBUG
                std::cout << ascii << std::endl;
index 27a61cf57fcee2e2d35441880b221ee4af952109..209ffe501f4f29a2f4fcb0c7d6a8f526217473ba 100644 (file)
@@ -89,6 +89,11 @@ 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);
+#endif
+
        m_delete_window_atom 
          = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
 
@@ -141,6 +146,10 @@ GHOST_SystemX11(
 GHOST_SystemX11::
 ~GHOST_SystemX11()
 {
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+       XCloseIM(m_xim);
+#endif
+
        XCloseDisplay(m_display);
 }
 
@@ -500,9 +509,9 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                case KeyRelease:
                {
                        XKeyEvent *xke = &(xe->xkey);
-               
                        KeySym key_sym = XLookupKeysym(xke,0);
                        char ascii;
+                       char utf8_buf[6]; /* 6 is enough for a utf8 char */
                        
                        GHOST_TKey gkey = convertXKey(key_sym);
                        GHOST_TEventType type = (xke->type == KeyPress) ? 
@@ -512,13 +521,55 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                ascii = '\0';
                        }
                        
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+                       /* getting unicode on key-up events gives XLookupNone status */
+                       if (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))) {
+                                       utf8_buf[0]= '\0';
+                               }
+
+                               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 */
+                                       }
+                                       else {
+                                               utf8_buf[0]= '\0';
+                                       }
+                               }
+                               else if (status == XLookupKeySym) {
+                                       /* this key doesn't have a text representation, it is a command
+                                          key of some sort */;
+                               }
+                               else {
+                                       printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
+                                                         (unsigned int) key_sym,
+                                                         (status == XBufferOverflow ? "BufferOverflow" :
+                                                          status == XLookupNone ? "XLookupNone" :
+                                                          status == XLookupKeySym ? "XLookupKeySym" :
+                                                          "Unknown status"));
+
+                                       printf("'%.*s' %p %p\n", len, utf8_buf, window->getX11_XIC(), m_xim);
+                               }
+                       }
+                       else {
+                               utf8_buf[0]= '\0';
+                       }
+#else
+                       utf8_buf[0]= '\0';
+#endif
+
                        g_event = new
                        GHOST_EventKey(
                                getMilliSeconds(),
                                type,
                                window,
                                gkey,
-                               ascii
+                               ascii,
+                           utf8_buf
                        );
                        
                break;
index b43d955d1568f28f8f9ef4e0615152aabb4fdef1..af45d68450fa26acfc07440b0042d1b234da688c 100644 (file)
 #include "GHOST_System.h"
 #include "../GHOST_Types.h"
 
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+#  define GHOST_X11_RES_NAME  "Blender" /* res_name */
+#  define GHOST_X11_RES_CLASS "Blender" /* res_class */
+#endif
+
+
 class GHOST_WindowX11;
 
 /**
@@ -203,6 +209,14 @@ public:
                return m_display;
        }       
 
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+               XIM
+       getX11_XIM(
+       ) {
+               return m_xim;
+       }
+#endif
+
        /* Helped function for get data from the clipboard. */
        void getClipboard_xcout(XEvent evt, Atom sel, Atom target,
                         unsigned char **txt, unsigned long *len,
@@ -258,6 +272,9 @@ public:
 private :
 
        Display * m_display;
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+       XIM m_xim;
+#endif
 
        /// The vector of windows that need to be updated.
        std::vector<GHOST_WindowX11 *> m_dirty_windows;
index 160980b633168939788cf36567e4962ed80d9ae2..d26224d956ab5884691d91b85d94ab8f106231e5 100644 (file)
@@ -392,6 +392,13 @@ 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);
+#endif
+
        // Set the window icon
        XWMHints *xwmhints = XAllocWMHints();
        XImage *x_image, *mask_image;
@@ -1304,6 +1311,13 @@ GHOST_WindowX11::
                XSetSelectionOwner(m_display, Clipboard_atom, None, CurrentTime);
        }
        
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+       if (m_xic) {
+               XDestroyIC(m_xic);
+       }
+#endif
+
+
        XDestroyWindow(m_display, m_window);
        XFree(m_visual);
 }
index 2cb269ea91976cf58a99ce2b2c394f430b35ca01..ee662945eaab4ef532a3d4d502a30c23e5b5c9dd 100644 (file)
@@ -221,6 +221,10 @@ public:
        { return NULL; }
 #endif // WITH_X11_XINPUT
 
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+       XIC getX11_XIC() { return m_xic; }
+#endif
+
        /*
         * Need this in case that we want start the window
         * in FullScree or Maximized state.
@@ -363,6 +367,10 @@ private :
        XTablet m_xtablet;
 #endif
 
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+       XIC m_xic;
+#endif
+
        void icccmSetState(int state);
        int icccmGetState() const;
 
index 4df9d7f12e20d465fb3dd1e3922201c15ac52976..85103b4224448790d1305d05fe37ce554d98d5da 100644 (file)
@@ -1428,6 +1428,36 @@ static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data,
        ui_check_but(but);
 }
 
+/* note: utf8 & ascii funcs should be merged */
+static int ui_textedit_type_utf8(uiBut *but, uiHandleButtonData *data, const char utf8_buf[6])
+{
+       char *str;
+       int len, x, changed= 0;
+       size_t step= BLI_strnlen(utf8_buf, sizeof(utf8_buf));
+
+       str= data->str;
+       len= strlen(str);
+
+       if(len-(but->selend - but->selsta)+1 <= data->maxlen) {
+               /* type over the current selection */
+               if ((but->selend - but->selsta) > 0)
+                       changed= ui_textedit_delete_selection(but, data);
+
+               len= strlen(str);
+               if(len+step < data->maxlen) {
+                       for(x= data->maxlen; x>but->pos; x--)
+                               str[x]= str[x-step];
+                       memcpy(&str[but->pos], utf8_buf, step * sizeof(char));
+                       str[len+step]= '\0';
+
+                       but->pos += step;
+                       changed= 1;
+               }
+       }
+
+       return changed;
+}
+
 static int ui_textedit_type_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
 {
        char *str;
@@ -1939,7 +1969,15 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
                                if(event->type == PADPERIOD && ascii == ',')
                                        ascii = '.';
 
-                       changed= ui_textedit_type_ascii(but, data, ascii);
+                       if(event->utf8_buf[0]) {
+                               /* keep this printf until utf8 is well tested */
+                               printf("%s: utf8 char '%s'\n", __func__, event->utf8_buf);
+                               changed= ui_textedit_type_utf8(but, data, event->utf8_buf);
+                       }
+                       else {
+                               changed= ui_textedit_type_ascii(but, data, ascii);
+                       }
+
                        retval= WM_UI_HANDLER_BREAK;
                        
                }
index fec59e971947780eafe3aa5a04de4ac53d43a9d7..f28fb08ac6e073f48b8e30af6d8036e74fa7ce46 100644 (file)
@@ -341,8 +341,8 @@ typedef struct wmEvent {
        short val;                      /* press, release, scrollvalue */
        int x, y;                       /* mouse pointer position, screen coord */
        int mval[2];            /* region mouse position, name convention pre 2.5 :) */
-       short unicode;          /* future, ghost? */
-       char ascii;                     /* from ghost */
+       char utf8_buf[6];       /* from, ghost if utf8 is enabled for the platform */
+       char ascii;                     /* from ghost, fallback if utf8 isnt set */
        char pad;
 
        /* previous state */
index 33e98007fedc6ea87aae472a1b5553d3a31e773b..7dcb4cf091f84403388c2fdd8200f32dac7dc215 100644 (file)
@@ -2578,6 +2578,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        GHOST_TEventKeyData *kd= customdata;
                        event.type= convert_key(kd->key);
                        event.ascii= kd->ascii;
+                       strcpy(event.utf8_buf, kd->utf8_buf);
                        event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
                        
                        /* exclude arrow keys, esc, etc from text input */
index 40917a67c2f34d92cfebeb16308f2ecefbdec9d7..e86831b9323ea947304f99b066c571ff201230a4 100644 (file)
@@ -1816,7 +1816,7 @@ PyObject* initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur
        /* Yet another gotcha in the py api
         * Cant run PySys_SetArgv more then once because this adds the
         * binary dir to the sys.path each time.
-        * Id have thaught python being totally restarted would make this ok but
+        * Id have thought python being totally restarted would make this ok but
         * somehow it remembers the sys.path - Campbell
         */
        static bool first_time = true;