xcb: have a proper detection of key events originating from SendEvent

This is a more correct fix for QTBUG-48795. The original fix
was unnecessarily using non-XKB code path for updating state
for all incoming key events. This would result in losing some
valuable bits from xkb state.

Task-number: QTBUG-48795
Change-Id: Ic4fb28b2d834272f1db2cbf5888cafb209707847
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
bb10
Gatis Paeglis 2018-02-01 16:04:43 +01:00
parent 1219fc26a0
commit 7c01c759ae
2 changed files with 58 additions and 47 deletions

View File

@ -786,37 +786,32 @@ void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state)
}
#endif
void QXcbKeyboard::updateXKBStateFromState(struct xkb_state *kb_state, quint16 state)
static xkb_layout_index_t lockedGroup(quint16 state)
{
const quint32 modsDepressed = xkb_state_serialize_mods(kb_state, XKB_STATE_MODS_DEPRESSED);
const quint32 modsLatched = xkb_state_serialize_mods(kb_state, XKB_STATE_MODS_LATCHED);
const quint32 modsLocked = xkb_state_serialize_mods(kb_state, XKB_STATE_MODS_LOCKED);
const quint32 xkbMask = xkbModMask(state);
const quint32 latched = modsLatched & xkbMask;
const quint32 locked = modsLocked & xkbMask;
quint32 depressed = modsDepressed & xkbMask;
// set modifiers in depressed if they don't appear in any of the final masks
depressed |= ~(depressed | latched | locked) & xkbMask;
const xkb_state_component newState
= xkb_state_update_mask(kb_state,
depressed,
latched,
locked,
0,
0,
(state >> 13) & 3); // bits 13 and 14 report the state keyboard group
if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
//qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
}
return (state >> 13) & 3; // bits 13 and 14 report the state keyboard group
}
void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
{
if (m_config && !connection()->hasXKB()) {
updateXKBStateFromState(m_xkbState.get(), state);
struct xkb_state *xkbState = m_xkbState.get();
xkb_mod_mask_t modsDepressed = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_DEPRESSED);
xkb_mod_mask_t modsLatched = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED);
xkb_mod_mask_t modsLocked = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED);
xkb_mod_mask_t xkbMask = xkbModMask(state);
xkb_mod_mask_t latched = modsLatched & xkbMask;
xkb_mod_mask_t locked = modsLocked & xkbMask;
xkb_mod_mask_t depressed = modsDepressed & xkbMask;
// set modifiers in depressed if they don't appear in any of the final masks
depressed |= ~(depressed | latched | locked) & xkbMask;
xkb_state_component changedComponents = xkb_state_update_mask(
xkbState, depressed, latched, locked, 0, 0, lockedGroup(state));
if ((changedComponents & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
//qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
}
}
}
@ -841,9 +836,9 @@ void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
}
#endif
quint32 QXcbKeyboard::xkbModMask(quint16 state)
xkb_mod_mask_t QXcbKeyboard::xkbModMask(quint16 state)
{
quint32 xkb_mask = 0;
xkb_mod_mask_t xkb_mask = 0;
if ((state & XCB_MOD_MASK_SHIFT) && xkb_mods.shift != XKB_MOD_INVALID)
xkb_mask |= (1 << xkb_mods.shift);
@ -1503,7 +1498,7 @@ private:
};
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
quint16 state, xcb_timestamp_t time)
quint16 state, xcb_timestamp_t time, bool fromSendEvent)
{
if (!m_config)
return;
@ -1515,17 +1510,25 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time);
// Have a temporary keyboard state filled in from state
// this way we allow for synthetic events to have different state
// from the current state i.e. you can have Alt+Ctrl pressed
// and receive a synthetic key event that has neither Alt nor Ctrl pressed
ScopedXKBState xkbState(xkb_state_new(m_xkbKeymap.get()));
if (!xkbState)
return;
updateXKBStateFromState(xkbState.get(), state);
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState.get(), code);
QString string = lookupString(xkbState.get(), code);
ScopedXKBState sendEventState;
if (fromSendEvent) {
// Have a temporary keyboard state filled in from state
// this way we allow for synthetic events to have different state
// from the current state i.e. you can have Alt+Ctrl pressed
// and receive a synthetic key event that has neither Alt nor Ctrl pressed
sendEventState.reset(xkb_state_new(m_xkbKeymap.get()));
if (!sendEventState)
return;
xkb_mod_mask_t depressed = xkbModMask(state);
xkb_state_update_mask(sendEventState.get(), depressed, 0, 0, 0, 0, lockedGroup(state));
}
struct xkb_state *xkbState = fromSendEvent ? sendEventState.get() : m_xkbState.get();
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code);
QString string = lookupString(xkbState, code);
Qt::KeyboardModifiers modifiers = translateModifiers(state);
if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9)
@ -1548,7 +1551,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
}
int qtcode = keysymToQtKey(latinKeysym != XKB_KEY_NoSymbol ? latinKeysym : sym,
modifiers, xkbState.get(), code);
modifiers, xkbState, code);
bool isAutoRepeat = false;
if (type == QEvent::KeyPress) {
@ -1624,14 +1627,23 @@ QString QXcbKeyboard::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) c
return QString::fromUtf8(chars.constData(), size);
}
void QXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *event)
static bool fromSendEvent(const void *event)
{
handleKeyEvent(event->event, QEvent::KeyPress, event->detail, event->state, event->time);
// From X11 protocol: Every event contains an 8-bit type code. The most
// significant bit in this code is set if the event was generated from
// a SendEvent request.
const xcb_generic_event_t *e = reinterpret_cast<const xcb_generic_event_t *>(event);
return (e->response_type & 0x80) != 0;
}
void QXcbKeyboard::handleKeyReleaseEvent(const xcb_key_release_event_t *event)
void QXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *e)
{
handleKeyEvent(event->event, QEvent::KeyRelease, event->detail, event->state, event->time);
handleKeyEvent(e->event, QEvent::KeyPress, e->detail, e->state, e->time, fromSendEvent(e));
}
void QXcbKeyboard::handleKeyReleaseEvent(const xcb_key_release_event_t *e)
{
handleKeyEvent(e->event, QEvent::KeyRelease, e->detail, e->state, e->time, fromSendEvent(e));
}
void QXcbKeyboard::handleMappingNotifyEvent(const void *event)

View File

@ -72,7 +72,7 @@ public:
// when XKEYBOARD not present on the X server
void updateXKBMods();
quint32 xkbModMask(quint16 state);
xkb_mod_mask_t xkbModMask(quint16 state);
void updateXKBStateFromCore(quint16 state);
#if QT_CONFIG(xinput2)
void updateXKBStateFromXI(void *modInfo, void *groupInfo);
@ -84,7 +84,8 @@ public:
#endif
protected:
void handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time);
void handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
quint16 state, xcb_timestamp_t time, bool fromSendEvent);
void resolveMaskConflicts();
QString lookupString(struct xkb_state *state, xcb_keycode_t code) const;
@ -106,8 +107,6 @@ protected:
void checkForLatinLayout() const;
private:
void updateXKBStateFromState(struct xkb_state *kb_state, quint16 state);
bool m_config = false;
xcb_keycode_t m_autorepeat_code = 0;