forked from bartvdbraak/blender
initial support for unicode keyboard input for ghost & blenders WM.
- 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.
This commit is contained in:
parent
8e58fceab1
commit
9bbec84e7e
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<GHOST_WindowX11 *> m_dirty_windows;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user