diff options
Diffstat (limited to 'intern/ghost/intern/GHOST_SystemX11.cpp')
-rw-r--r-- | intern/ghost/intern/GHOST_SystemX11.cpp | 189 |
1 files changed, 123 insertions, 66 deletions
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 69aa3a09977..fcda5d8b72d 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -101,7 +101,12 @@ * See T47228 and D1746 */ #define USE_NON_LATIN_KB_WORKAROUND -static GHOST_TKey convertXKey(KeySym key); +static GHOST_TKey ghost_key_from_keysym( + const KeySym key); +static GHOST_TKey ghost_key_from_keycode( + const XkbDescPtr xkb_descr, const KeyCode keycode); +static GHOST_TKey ghost_key_from_keysym_or_keycode( + const KeySym key, const XkbDescPtr xkb_descr, const KeyCode keycode); /* these are for copy and select copy */ static char *txt_cut_buffer = NULL; @@ -117,11 +122,12 @@ GHOST_SystemX11:: GHOST_SystemX11( ) : GHOST_System(), + m_xkb_descr(NULL), m_start_time(0) { XInitThreads(); m_display = XOpenDisplay(NULL); - + if (!m_display) { std::cerr << "Unable to open a display" << std::endl; abort(); /* was return before, but this would just mean it will crash later */ @@ -179,19 +185,24 @@ GHOST_SystemX11( if (gettimeofday(&tv, NULL) == -1) { GHOST_ASSERT(false, "Could not instantiate timer!"); } - + /* Taking care not to overflow the tv.tv_sec * 1000 */ m_start_time = GHOST_TUns64(tv.tv_sec) * 1000 + tv.tv_usec / 1000; - - + + /* use detectable autorepeate, mac and windows also do this */ int use_xkb; int xkb_opcode, xkb_event, xkb_error; int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion; - + use_xkb = XkbQueryExtension(m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor); if (use_xkb) { XkbSetDetectableAutoRepeat(m_display, true, NULL); + + m_xkb_descr = XkbGetMap(m_display, 0, XkbUseCoreKbd); + if (m_xkb_descr) { + XkbGetNames(m_display, XkbKeyNamesMask, m_xkb_descr); + } } #ifdef WITH_XWAYLAND_HACK @@ -244,11 +255,15 @@ GHOST_SystemX11:: /* close tablet devices */ if (m_xtablet.StylusDevice) XCloseDevice(m_display, m_xtablet.StylusDevice); - + if (m_xtablet.EraserDevice) XCloseDevice(m_display, m_xtablet.EraserDevice); #endif /* WITH_X11_XINPUT */ + if (m_xkb_descr) { + XkbFreeNames(m_xkb_descr, XkbKeyNamesMask, false); + } + XCloseDisplay(m_display); } @@ -285,7 +300,7 @@ getMilliSeconds() const /* Taking care not to overflow the tv.tv_sec * 1000 */ return GHOST_TUns64(tv.tv_sec) * 1000 + tv.tv_usec / 1000 - m_start_time; } - + GHOST_TUns8 GHOST_SystemX11:: getNumDisplays() const @@ -358,9 +373,9 @@ createWindow(const STR_String& title, const GHOST_TEmbedderWindowID parentWindow) { GHOST_WindowX11 *window = NULL; - + if (!m_display) return 0; - + window = new GHOST_WindowX11(this, m_display, title, left, top, width, height, state, parentWindow, type, @@ -386,7 +401,7 @@ createWindow(const STR_String& title, return window; } -bool GHOST_SystemX11::supportsNativeDialogs(void) +bool GHOST_SystemX11::supportsNativeDialogs(void) { return false; } @@ -516,7 +531,7 @@ GHOST_SystemX11:: findGhostWindow( Window xwind) const { - + if (xwind == 0) return NULL; /* It is not entirely safe to do this as the backptr may point @@ -528,7 +543,7 @@ findGhostWindow( vector<GHOST_IWindow *>::iterator win_it = win_vec.begin(); vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end(); - + for (; win_it != win_end; ++win_it) { GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it); if (window->getXWindow() == xwind) { @@ -536,14 +551,14 @@ findGhostWindow( } } return NULL; - + } static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) { int fd = ConnectionNumber(display); fd_set fds; - + FD_ZERO(&fds); FD_SET(fd, &fds); @@ -555,7 +570,7 @@ static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) tv.tv_sec = maxSleep / 1000; tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000; - + select(fd + 1, &fds, NULL, NULL, &tv); } } @@ -618,15 +633,15 @@ processEvents( { /* Get all the current events -- translate them into * ghost events and call base class pushEvent() method. */ - + bool anyProcessed = false; - + do { GHOST_TimerManager *timerMgr = getTimerManager(); - + if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) { GHOST_TUns64 next = timerMgr->nextFireTime(); - + if (next == GHOST_kFireTimeNever) { SleepTillEvent(m_display, -1); } @@ -637,11 +652,11 @@ processEvents( SleepTillEvent(m_display, next - getMilliSeconds()); } } - + if (timerMgr->fireTimers(getMilliSeconds())) { anyProcessed = true; } - + while (XPending(m_display)) { XEvent xevent; XNextEvent(m_display, &xevent); @@ -669,10 +684,8 @@ processEvents( } /* 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. */ + if ((XFilterEvent(&xevent, (Window)NULL) == True)) { + /* do nothing now, the event is consumed by XIM. */ continue; } #endif @@ -726,7 +739,7 @@ processEvents( getMilliSeconds(), GHOST_kEventKeyDown, window, - convertXKey(modifiers[i]), + ghost_key_from_keysym(modifiers[i]), '\0', NULL)); } @@ -738,7 +751,7 @@ processEvents( #endif /* USE_UNITY_WORKAROUND */ } - + if (generateWindowExposeEvents()) { anyProcessed = true; } @@ -748,9 +761,9 @@ processEvents( anyProcessed = true; } #endif - + } while (waitForEvent && !anyProcessed); - + return anyProcessed; } @@ -956,7 +969,6 @@ GHOST_SystemX11::processEvent(XEvent *xe) { XKeyEvent *xke = &(xe->xkey); KeySym key_sym; - KeySym key_sym_str; char ascii; #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* utf8_array[] is initial buffer used for Xutf8LookupString(). @@ -971,7 +983,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) #else char *utf8_buf = NULL; #endif - + GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; GHOST_TKey gkey; @@ -983,7 +995,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) * is unmodified (or anyone swapping the keys with xmodmap). * * - XLookupKeysym seems to always use first defined keymap (see T47228), which generates - * keycodes unusable by convertXKey for non-latin-compatible keymaps. + * keycodes unusable by ghost_key_from_keysym for non-latin-compatible keymaps. * * To address this, we: * @@ -1001,6 +1013,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) * * [1] http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/KeyBind.c */ + KeySym key_sym_str; /* Mode_switch 'modifier' is AltGr - when this one or Shift are enabled, we do not want to apply * that 'forced number' hack. */ const unsigned int mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch); @@ -1021,7 +1034,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) /* Only allow a limited set of keys from XLookupKeysym, all others we take from XLookupString, * unless it gives unknown key... */ - gkey = convertXKey(key_sym); + gkey = ghost_key_from_keysym_or_keycode(key_sym, m_xkb_descr, xke->keycode); switch (gkey) { case GHOST_kKeyRightAlt: case GHOST_kKeyLeftAlt: @@ -1058,10 +1071,12 @@ GHOST_SystemX11::processEvent(XEvent *xe) case GHOST_kKeyNumpadSlash: break; default: - GHOST_TKey gkey_str = convertXKey(key_sym_str); + { + GHOST_TKey gkey_str = ghost_key_from_keysym(key_sym_str); if (gkey_str != GHOST_kKeyUnknown) { gkey = gkey_str; } + } } #else /* In keyboards like latin ones, @@ -1083,8 +1098,8 @@ GHOST_SystemX11::processEvent(XEvent *xe) key_sym = XLookupKeysym(xke, 0); } - gkey = convertXKey(key_sym); - + gkey = ghost_key_from_keysym_or_keycode(key_sym, m_xkb_descr, xke->keycode); + if (!XLookupString(xke, &ascii, 1, NULL, NULL)) { ascii = '\0'; } @@ -1178,7 +1193,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) if (utf8_buf != utf8_array) free(utf8_buf); #endif - + break; } @@ -1187,7 +1202,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) { XButtonEvent & xbe = xe->xbutton; GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft; - GHOST_TEventType type = (xbe.type == ButtonPress) ? + GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp; /* process wheel mouse events and break, only pass on press events */ @@ -1201,7 +1216,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1); break; } - + /* process rest of normal mouse buttons */ if (xbe.button == Button1) gbmask = GHOST_kButtonMaskLeft; @@ -1233,13 +1248,13 @@ GHOST_SystemX11::processEvent(XEvent *xe) ); break; } - + /* change of size, border, layer etc. */ case ConfigureNotify: { /* XConfigureEvent & xce = xe->xconfigure; */ - g_event = new + g_event = new GHOST_Event( getMilliSeconds(), GHOST_kEventWindowSize, @@ -1255,10 +1270,10 @@ GHOST_SystemX11::processEvent(XEvent *xe) /* TODO: make sure this is the correct place for activate/deactivate */ // printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window); - + /* May have to look at the type of event and filter some out. */ - GHOST_TEventType gtype = (xfe.type == FocusIn) ? + GHOST_TEventType gtype = (xfe.type == FocusIn) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate; #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) @@ -1271,7 +1286,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) } #endif - g_event = new + g_event = new GHOST_Event( getMilliSeconds(), gtype, @@ -1285,7 +1300,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) XClientMessageEvent & xcme = xe->xclient; if (((Atom)xcme.data.l[0]) == m_atom.WM_DELETE_WINDOW) { - g_event = new + g_event = new GHOST_Event( getMilliSeconds(), GHOST_kEventWindowClose, @@ -1329,14 +1344,14 @@ GHOST_SystemX11::processEvent(XEvent *xe) break; } - + case DestroyNotify: ::exit(-1); /* We're not interested in the following things.(yet...) */ case NoExpose: case GraphicsExpose: break; - + case EnterNotify: case LeaveNotify: { @@ -1349,7 +1364,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) */ XCrossingEvent &xce = xe->xcrossing; if (xce.mode == NotifyNormal) { - g_event = new + g_event = new GHOST_EventCursor( getMilliSeconds(), GHOST_kEventCursorMove, @@ -1397,18 +1412,18 @@ GHOST_SystemX11::processEvent(XEvent *xe) XEvent nxe; Atom target, utf8_string, string, compound_text, c_string; XSelectionRequestEvent *xse = &xe->xselectionrequest; - + target = XInternAtom(m_display, "TARGETS", False); utf8_string = XInternAtom(m_display, "UTF8_STRING", False); string = XInternAtom(m_display, "STRING", False); compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False); c_string = XInternAtom(m_display, "C_STRING", False); - + /* support obsolete clients */ if (xse->property == None) { xse->property = xse->target; } - + nxe.xselection.type = SelectionNotify; nxe.xselection.requestor = xse->requestor; nxe.xselection.property = xse->property; @@ -1416,7 +1431,7 @@ GHOST_SystemX11::processEvent(XEvent *xe) nxe.xselection.selection = xse->selection; nxe.xselection.target = xse->target; nxe.xselection.time = xse->time; - + /* Check to see if the requestor is asking for String */ if (xse->target == utf8_string || xse->target == string || @@ -1447,13 +1462,13 @@ GHOST_SystemX11::processEvent(XEvent *xe) /* Change property to None because we do not support anything but STRING */ nxe.xselection.property = None; } - + /* Send the event to the client 0 0 == False, SelectionNotify */ XSendEvent(m_display, xse->requestor, 0, 0, &nxe); XFlush(m_display); break; } - + default: { #ifdef WITH_X11_XINPUT @@ -1584,7 +1599,7 @@ getButtons( } else { return GHOST_kFailure; - } + } return GHOST_kSuccess; } @@ -1688,7 +1703,7 @@ setCursorPosition( #endif XSync(m_display, 0); /* Sync to process all requests */ - + return GHOST_kSuccess; } @@ -1699,7 +1714,7 @@ addDirtyWindow( GHOST_WindowX11 *bad_wind) { GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)"); - + m_dirty_windows.push_back(bad_wind); } @@ -1711,7 +1726,7 @@ generateWindowExposeEvents() vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin(); vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end(); bool anyProcessed = false; - + for (; w_start != w_end; ++w_start) { GHOST_Event *g_event = new GHOST_Event( @@ -1721,7 +1736,7 @@ generateWindowExposeEvents() ); (*w_start)->validate(); - + if (g_event) { pushEvent(g_event); anyProcessed = true; @@ -1732,10 +1747,22 @@ generateWindowExposeEvents() return anyProcessed; } +static GHOST_TKey +ghost_key_from_keysym_or_keycode(const KeySym keysym, XkbDescPtr xkb_descr, const KeyCode keycode) +{ + GHOST_TKey type = ghost_key_from_keysym(keysym); + if (type == GHOST_kKeyUnknown) { + if (xkb_descr) { + type = ghost_key_from_keycode(xkb_descr, keycode); + } + } + return type; +} + #define GXMAP(k, x, y) case x: k = y; break static GHOST_TKey -convertXKey(KeySym key) +ghost_key_from_keysym(const KeySym key) { GHOST_TKey type; @@ -1844,6 +1871,9 @@ convertXKey(KeySym key) #endif #endif default: +#ifdef GHOST_DEBUG + printf("%s: unknown key: %lu / 0x%lx\n", __func__, key, key); +#endif type = GHOST_kKeyUnknown; break; } @@ -1854,6 +1884,33 @@ convertXKey(KeySym key) #undef GXMAP +#define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) + +static GHOST_TKey +ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCode keycode) +{ + GHOST_ASSERT(XkbKeyNameLength == 4, "Name length is invalid!"); + if (keycode >= xkb_descr->min_key_code && keycode <= xkb_descr->max_key_code) { + const char *id_str = xkb_descr->names->keys[keycode].name; + const uint32_t id = MAKE_ID(id_str[0], id_str[1], id_str[2], id_str[3]); + switch (id) { + case MAKE_ID('T', 'L', 'D', 'E'): + return GHOST_kKeyAccentGrave; +#ifdef GHOST_DEBUG + default: + printf("%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str); + break; +#endif + } + } + else { + GHOST_ASSERT(false, "KeyCode out of range!"); + } + return GHOST_kKeyUnknown; +} + +#undef MAKE_ID + /* from xclip.c xcout() v0.11 */ #define XCLIB_XCOUT_NONE 0 /* no context */ @@ -2110,12 +2167,12 @@ GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const unsigned char *tmp_data = (unsigned char *) malloc(sel_len + 1); memcpy((char *)tmp_data, (char *)sel_buf, sel_len); tmp_data[sel_len] = '\0'; - + if (sseln == m_atom.STRING) XFree(sel_buf); else free(sel_buf); - + return tmp_data; } return(NULL); @@ -2156,7 +2213,7 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const } #ifdef WITH_XDND -GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, +GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_IWindow *window, int mouseX, int mouseY, @@ -2322,7 +2379,7 @@ void GHOST_SystemX11::refreshXInputDevices() for (int i = 0; i < device_count; ++i) { char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) : NULL; - + // printf("Tablet type:'%s', name:'%s', index:%d\n", device_type, device_info[i].name, i); @@ -2355,7 +2412,7 @@ void GHOST_SystemX11::refreshXInputDevices() break; } - + ici = (XAnyClassPtr)(((char *)ici) + ici->length); } } |