diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index dd399a7aa95..78fc3f69c7a 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -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 { diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 3581cd86b20..a8fca1e8f2d 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -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; } diff --git a/intern/ghost/intern/GHOST_SystemCarbon.cpp b/intern/ghost/intern/GHOST_SystemCarbon.cpp index 02ff5c0f559..762966c9b9e 100644 --- a/intern/ghost/intern/GHOST_SystemCarbon.cpp +++ b/intern/ghost/intern/GHOST_SystemCarbon.cpp @@ -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; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 303c2b24497..e8ec13f20df 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -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; diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index f2cc45731fa..11bd562f767 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -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; } diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 38f3985b139..24b6474732e 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -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; diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 27a61cf57fc..209ffe501f4 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -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; diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index b43d955d156..af45d68450f 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -40,6 +40,12 @@ #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 m_dirty_windows; diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 160980b6331..d26224d956a 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -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); } diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h index 2cb269ea919..ee662945eaa 100644 --- a/intern/ghost/intern/GHOST_WindowX11.h +++ b/intern/ghost/intern/GHOST_WindowX11.h @@ -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; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 4df9d7f12e2..85103b42244 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -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; } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index fec59e97194..f28fb08ac6e 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -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 */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 33e98007fed..7dcb4cf091f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -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 */ diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp index 40917a67c2f..e86831b9323 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.cpp +++ b/source/gameengine/Ketsji/KX_PythonInit.cpp @@ -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;