123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933 | // Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qxcbkeyboard.h"#include"qxcbwindow.h"#include"qxcbscreen.h"#include"qxcbcursor.h"#include <qpa/qwindowsysteminterface.h>#include <qpa/qplatforminputcontext.h>#include <qpa/qplatformintegration.h>#include <qpa/qplatformcursor.h>#include <QtCore/QMetaEnum>#include <private/qguiapplication_p.h>#include <xcb/xinput.h> QT_BEGIN_NAMESPACE Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s)const{Qt::KeyboardModifiers ret =Qt::NoModifier;if(s & XCB_MOD_MASK_SHIFT) ret |=Qt::ShiftModifier;if(s & XCB_MOD_MASK_CONTROL) ret |=Qt::ControlModifier;if(s & rmod_masks.alt) ret |=Qt::AltModifier;if(s & rmod_masks.meta) ret |=Qt::MetaModifier;if(s & rmod_masks.altgr) ret |=Qt::GroupSwitchModifier;return ret;}/* Look at a pair of unshifted and shifted key symbols. * If the 'unshifted' symbol is uppercase and there is no shifted symbol, * return the matching lowercase symbol; otherwise return 0. * The caller can then use the previously 'unshifted' symbol as the new * 'shifted' (uppercase) symbol and the symbol returned by the function * as the new 'unshifted' (lowercase) symbol.) */static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifted){if(shifted != XKB_KEY_NoSymbol)// Has a shifted symbolreturn0; xcb_keysym_t xlower; xcb_keysym_t xupper;QXkbCommon::xkbcommon_XConvertCase(unshifted, &xlower, &xupper);if(xlower != xupper // Check if symbol is cased&& unshifted == xupper) {// Unshifted must be upper casereturn xlower;}return0;}static QByteArray symbolsGroupString(const xcb_keysym_t *symbols,int count){// Don't output trailing NoSymbolswhile(count >0&& symbols[count -1] == XKB_KEY_NoSymbol) count--; QByteArray groupString;for(int symIndex =0; symIndex < count; symIndex++) { xcb_keysym_t sym = symbols[symIndex];char symString[64];if(sym == XKB_KEY_NoSymbol)strcpy(symString,"NoSymbol");elsexkb_keysym_get_name(sym, symString,sizeof(symString));if(!groupString.isEmpty()) groupString +=", "; groupString += symString;}return groupString;}struct xkb_keymap *QXcbKeyboard::keymapFromCore(const KeysymModifierMap &keysymMods){/* Construct an XKB keymap string from information queried from * the X server */ QByteArray keymap; keymap +="xkb_keymap {\n";const xcb_keycode_t minKeycode =connection()->setup()->min_keycode;const xcb_keycode_t maxKeycode =connection()->setup()->max_keycode;// Generate symbolic names from keycodes{ keymap +="xkb_keycodes\"core\"{\n""\tminimum = "+QByteArray::number(minKeycode) +";\n""\tmaximum = "+QByteArray::number(maxKeycode) +";\n";for(int code = minKeycode; code <= maxKeycode; code++) {auto codeStr =QByteArray::number(code); keymap +="<K"+ codeStr +"> = "+ codeStr +";\n";}/* TODO: indicators? */ keymap +="};\n";// xkb_keycodes}/* Set up default types (xkbcommon automatically assigns these to * symbols, but doesn't have shift info) */ keymap +="xkb_types\"core\"{\n""virtual_modifiers NumLock,Alt,LevelThree;\n""type\"ONE_LEVEL\"{\n""modifiers= none;\n""level_name[Level1] =\"Any\";\n""};\n""type\"TWO_LEVEL\"{\n""modifiers= Shift;\n""map[Shift]= Level2;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Shift\";\n""};\n""type\"ALPHABETIC\"{\n""modifiers= Shift+Lock;\n""map[Shift]= Level2;\n""map[Lock]= Level2;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Caps\";\n""};\n""type\"KEYPAD\"{\n""modifiers= Shift+NumLock;\n""map[Shift]= Level2;\n""map[NumLock]= Level2;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Number\";\n""};\n""type\"FOUR_LEVEL\"{\n""modifiers= Shift+LevelThree;\n""map[Shift]= Level2;\n""map[LevelThree]= Level3;\n""map[Shift+LevelThree]= Level4;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Shift\";\n""level_name[Level3] =\"Alt Base\";\n""level_name[Level4] =\"Shift Alt\";\n""};\n""type\"FOUR_LEVEL_ALPHABETIC\"{\n""modifiers= Shift+Lock+LevelThree;\n""map[Shift]= Level2;\n""map[Lock]= Level2;\n""map[LevelThree]= Level3;\n""map[Shift+LevelThree]= Level4;\n""map[Lock+LevelThree]= Level4;\n""map[Shift+Lock+LevelThree]= Level3;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Shift\";\n""level_name[Level3] =\"Alt Base\";\n""level_name[Level4] =\"Shift Alt\";\n""};\n""type\"FOUR_LEVEL_SEMIALPHABETIC\"{\n""modifiers= Shift+Lock+LevelThree;\n""map[Shift]= Level2;\n""map[Lock]= Level2;\n""map[LevelThree]= Level3;\n""map[Shift+LevelThree]= Level4;\n""map[Lock+LevelThree]= Level3;\n""preserve[Lock+LevelThree]= Lock;\n""map[Shift+Lock+LevelThree]= Level4;\n""preserve[Shift+Lock+LevelThree]= Lock;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Shift\";\n""level_name[Level3] =\"Alt Base\";\n""level_name[Level4] =\"Shift Alt\";\n""};\n""type\"FOUR_LEVEL_KEYPAD\"{\n""modifiers= Shift+NumLock+LevelThree;\n""map[Shift]= Level2;\n""map[NumLock]= Level2;\n""map[LevelThree]= Level3;\n""map[Shift+LevelThree]= Level4;\n""map[NumLock+LevelThree]= Level4;\n""map[Shift+NumLock+LevelThree]= Level3;\n""level_name[Level1] =\"Base\";\n""level_name[Level2] =\"Number\";\n""level_name[Level3] =\"Alt Base\";\n""level_name[Level4] =\"Alt Number\";\n""};\n""};\n";// xkb_types// Generate mapping between symbolic names and keysyms{ QList<xcb_keysym_t> xkeymap;int keysymsPerKeycode =0;{int keycodeCount = maxKeycode - minKeycode +1;if(auto keymapReply =Q_XCB_REPLY(xcb_get_keyboard_mapping,xcb_connection(), minKeycode, keycodeCount)) { keysymsPerKeycode = keymapReply->keysyms_per_keycode;int numSyms = keycodeCount * keysymsPerKeycode;auto keymapPtr =xcb_get_keyboard_mapping_keysyms(keymapReply.get()); xkeymap.resize(numSyms);for(int i =0; i < numSyms; i++) xkeymap[i] = keymapPtr[i];}}if(xkeymap.isEmpty())returnnullptr;static const char*const builtinModifiers[] ={"Shift","Lock","Control","Mod1","Mod2","Mod3","Mod4","Mod5"};/* Level 3 symbols (e.g. AltGr+something) seem to come in two flavors: * - as a proper level 3 in group 1, at least on recent X.org versions * - 'disguised' as group 2, on 'legacy' X servers * In the 2nd case, remap group 2 to level 3, that seems to work better * in practice */bool mapGroup2ToLevel3 = keysymsPerKeycode <5; keymap +="xkb_symbols\"core\"{\n";for(int code = minKeycode; code <= maxKeycode; code++) {auto codeMap = xkeymap.constData() + (code - minKeycode) * keysymsPerKeycode;const int maxGroup1 =4;// We only support 4 shift states anywayconst int maxGroup2 =2;// Only 3rd and 4th keysym are group 2 xcb_keysym_t symbolsGroup1[maxGroup1]; xcb_keysym_t symbolsGroup2[maxGroup2] = { XKB_KEY_NoSymbol, XKB_KEY_NoSymbol };for(int i =0; i < maxGroup1 + maxGroup2; i++) { xcb_keysym_t sym = i < keysymsPerKeycode ? codeMap[i] : XKB_KEY_NoSymbol;if(mapGroup2ToLevel3) {// Merge into single groupif(i < maxGroup1) symbolsGroup1[i] = sym;}else{// Preserve groupsif(i <2) symbolsGroup1[i] = sym;else if(i <4) symbolsGroup2[i -2] = sym;else symbolsGroup1[i -2] = sym;}}/* Fix symbols so the unshifted and shifted symbols have * lower resp. upper case */if(auto lowered =getUnshiftedXKey(symbolsGroup1[0], symbolsGroup1[1])) { symbolsGroup1[1] = symbolsGroup1[0]; symbolsGroup1[0] = lowered;}if(auto lowered =getUnshiftedXKey(symbolsGroup2[0], symbolsGroup2[1])) { symbolsGroup2[1] = symbolsGroup2[0]; symbolsGroup2[0] = lowered;} QByteArray groupStr1 =symbolsGroupString(symbolsGroup1, maxGroup1);if(groupStr1.isEmpty())continue; keymap +="key <K"+QByteArray::number(code) +"> { "; keymap +="symbols[Group1] = [ "+ groupStr1 +" ]"; QByteArray groupStr2 =symbolsGroupString(symbolsGroup2, maxGroup2);if(!groupStr2.isEmpty()) keymap +=", symbols[Group2] = [ "+ groupStr2 +" ]";// See if this key code is for a modifier xcb_keysym_t modifierSym = XKB_KEY_NoSymbol;for(int symIndex =0; symIndex < keysymsPerKeycode; symIndex++) { xcb_keysym_t sym = codeMap[symIndex];if(sym == XKB_KEY_Alt_L || sym == XKB_KEY_Meta_L || sym == XKB_KEY_Mode_switch || sym == XKB_KEY_Super_L || sym == XKB_KEY_Super_R || sym == XKB_KEY_Hyper_L || sym == XKB_KEY_Hyper_R) { modifierSym = sym;break;}}// AltGrif(modifierSym == XKB_KEY_Mode_switch) keymap +=", virtualMods=LevelThree"; keymap +=" };\n";// key// Generate modifier mappingsint modNum = keysymMods.value(modifierSym, -1);if(modNum != -1) {// Here modNum is always < 8 (see keysymsToModifiers()) keymap +=QByteArray("modifier_map ") + builtinModifiers[modNum]+" { <K"+QByteArray::number(code) +"> };\n";}}// TODO: indicators? keymap +="};\n";// xkb_symbols}// We need an "Alt" modifier, provide via the xkb_compatibility section keymap +="xkb_compatibility\"core\"{\n""virtual_modifiers NumLock,Alt,LevelThree;\n""interpret Alt_L+AnyOf(all) {\n""virtualModifier= Alt;\n""action= SetMods(modifiers=modMapMods,clearLocks);\n""};\n""interpret Alt_R+AnyOf(all) {\n""virtualModifier= Alt;\n""action= SetMods(modifiers=modMapMods,clearLocks);\n""};\n""};\n";/* TODO: There is an issue with modifier state not being handled * correctly if using Xming with XKEYBOARD disabled. */ keymap +="};\n";// xkb_keymapreturnxkb_keymap_new_from_buffer(m_xkbContext.get(), keymap.constData(), keymap.size(), XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);}voidQXcbKeyboard::updateKeymap(xcb_mapping_notify_event_t *event){if(connection()->hasXKB() || event->request == XCB_MAPPING_POINTER)return;xcb_refresh_keyboard_mapping(m_key_symbols, event);updateKeymap();}voidQXcbKeyboard::updateKeymap(){ KeysymModifierMap keysymMods;if(!connection()->hasXKB()) keysymMods =keysymsToModifiers();updateModifiers(keysymMods); m_config =true;if(!m_xkbContext) { m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES));if(!m_xkbContext) {qCWarning(lcQpaKeyboard,"failed to create XKB context"); m_config =false;return;} xkb_log_level logLevel =lcQpaKeyboard().isDebugEnabled() ? XKB_LOG_LEVEL_DEBUG : XKB_LOG_LEVEL_CRITICAL;xkb_context_set_log_level(m_xkbContext.get(), logLevel);}if(connection()->hasXKB()) { m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(),xcb_connection(), core_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS));if(m_xkbKeymap) m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(),xcb_connection(), core_device_id));}else{ m_xkbKeymap.reset(keymapFromCore(keysymMods));if(m_xkbKeymap) m_xkbState.reset(xkb_state_new(m_xkbKeymap.get()));}if(!m_xkbKeymap) {qCWarning(lcQpaKeyboard,"failed to compile a keymap"); m_config =false;return;}if(!m_xkbState) {qCWarning(lcQpaKeyboard,"failed to create XKB state"); m_config =false;return;}updateXKBMods();QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get());} QList<QKeyCombination>QXcbKeyboard::possibleKeyCombinations(const QKeyEvent *event)const{returnQXkbCommon::possibleKeyCombinations( m_xkbState.get(), event, m_superAsMeta, m_hyperAsMeta);}Qt::KeyboardModifiers QXcbKeyboard::queryKeyboardModifiers()const{// FIXME: Should we base this on m_xkbState?int stateMask =0;QXcbCursor::queryPointer(connection(),nullptr,nullptr, &stateMask);returntranslateModifiers(stateMask);}voidQXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state){if(m_config &&connection()->hasXKB()) {const xkb_state_component changedComponents =xkb_state_update_mask(m_xkbState.get(), state->baseMods, state->latchedMods, state->lockedMods, state->baseGroup, state->latchedGroup, state->lockedGroup);handleStateChanges(changedComponents);}}static xkb_layout_index_t lockedGroup(quint16 state){return(state >>13) &3;// bits 13 and 14 report the state keyboard group}voidQXcbKeyboard::updateXKBStateFromCore(quint16 state){if(m_config) {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));handleStateChanges(changedComponents);}}voidQXcbKeyboard::updateXKBStateFromXI(void*modInfo,void*groupInfo){if(m_config) {auto*mods =static_cast<xcb_input_modifier_info_t *>(modInfo);auto*group =static_cast<xcb_input_group_info_t *>(groupInfo);const xkb_state_component changedComponents =xkb_state_update_mask(m_xkbState.get(), mods->base, mods->latched, mods->locked, group->base, group->latched, group->locked);handleStateChanges(changedComponents);}}voidQXcbKeyboard::handleStateChanges(xkb_state_component changedComponents){// Note: Ubuntu (with Unity) always creates a new keymap when layout is changed// via system settings, which means that the layout change would not be detected// by this code. That can be solved by emitting KeyboardLayoutChange also from updateKeymap().if((changedComponents & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE)qCDebug(lcQpaKeyboard,"TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");} xkb_mod_mask_t QXcbKeyboard::xkbModMask(quint16 state){ 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);if((state & XCB_MOD_MASK_LOCK) && xkb_mods.lock != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.lock);if((state & XCB_MOD_MASK_CONTROL) && xkb_mods.control != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.control);if((state & XCB_MOD_MASK_1) && xkb_mods.mod1 != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.mod1);if((state & XCB_MOD_MASK_2) && xkb_mods.mod2 != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.mod2);if((state & XCB_MOD_MASK_3) && xkb_mods.mod3 != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.mod3);if((state & XCB_MOD_MASK_4) && xkb_mods.mod4 != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.mod4);if((state & XCB_MOD_MASK_5) && xkb_mods.mod5 != XKB_MOD_INVALID) xkb_mask |= (1<< xkb_mods.mod5);return xkb_mask;}voidQXcbKeyboard::updateXKBMods(){ xkb_mods.shift =xkb_keymap_mod_get_index(m_xkbKeymap.get(), XKB_MOD_NAME_SHIFT); xkb_mods.lock =xkb_keymap_mod_get_index(m_xkbKeymap.get(), XKB_MOD_NAME_CAPS); xkb_mods.control =xkb_keymap_mod_get_index(m_xkbKeymap.get(), XKB_MOD_NAME_CTRL); xkb_mods.mod1 =xkb_keymap_mod_get_index(m_xkbKeymap.get(),"Mod1"); xkb_mods.mod2 =xkb_keymap_mod_get_index(m_xkbKeymap.get(),"Mod2"); xkb_mods.mod3 =xkb_keymap_mod_get_index(m_xkbKeymap.get(),"Mod3"); xkb_mods.mod4 =xkb_keymap_mod_get_index(m_xkbKeymap.get(),"Mod4"); xkb_mods.mod5 =xkb_keymap_mod_get_index(m_xkbKeymap.get(),"Mod5");}QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection):QXcbObject(connection){ core_device_id =0;if(connection->hasXKB()) {selectEvents(); core_device_id =xkb_x11_get_core_keyboard_device_id(xcb_connection());if(core_device_id == -1) {qCWarning(lcQpaXcb,"failed to get core keyboard device info");return;}}else{ m_key_symbols =xcb_key_symbols_alloc(xcb_connection());}updateKeymap();}QXcbKeyboard::~QXcbKeyboard(){if(m_key_symbols)xcb_key_symbols_free(m_key_symbols);}voidQXcbKeyboard::initialize(){auto inputContext =QGuiApplicationPrivate::platformIntegration()->inputContext();QXkbCommon::setXkbContext(inputContext, m_xkbContext.get());}voidQXcbKeyboard::selectEvents(){const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS | XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_KEY_BEHAVIORS | XCB_XKB_MAP_PART_VIRTUAL_MODS | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP);const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY);// XKB events are reported to all interested clients without regard// to the current keyboard input focus or grab state xcb_void_cookie_t select =xcb_xkb_select_events_checked(xcb_connection(), XCB_XKB_ID_USE_CORE_KBD, required_events,0, required_events, required_map_parts, required_map_parts,nullptr); xcb_generic_error_t *error =xcb_request_check(xcb_connection(), select);if(error) {free(error);qCWarning(lcQpaXcb,"failed to select notify events from XKB");}}voidQXcbKeyboard::updateVModMapping(){ xcb_xkb_get_names_value_list_t names_list;memset(&vmod_masks,0,sizeof(vmod_masks));auto name_reply =Q_XCB_REPLY(xcb_xkb_get_names,xcb_connection(), XCB_XKB_ID_USE_CORE_KBD, XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);if(!name_reply) {qWarning("Qt: failed to retrieve the virtual modifier names from XKB");return;}const void*buffer =xcb_xkb_get_names_value_list(name_reply.get());xcb_xkb_get_names_value_list_unpack(buffer, name_reply->nTypes, name_reply->indicators, name_reply->virtualMods, name_reply->groupNames, name_reply->nKeys, name_reply->nKeyAliases, name_reply->nRadioGroups, name_reply->which,&names_list);int count =0; uint vmod_mask, bit;char*vmod_name; vmod_mask = name_reply->virtualMods;// find the virtual modifiers for which names are defined.for(bit =1; vmod_mask; bit <<=1) { vmod_name =nullptr;if(!(vmod_mask & bit))continue; vmod_mask &= ~bit;// virtualModNames - the list of virtual modifier atoms beginning with the lowest-numbered// virtual modifier for which a name is defined and proceeding to the highest. QByteArray atomName =connection()->atomName(names_list.virtualModNames[count]); vmod_name = atomName.data(); count++;if(!vmod_name)continue;// similarly we could retrieve NumLock, Super, Hyper modifiers if needed.if(qstrcmp(vmod_name,"Alt") ==0) vmod_masks.alt = bit;else if(qstrcmp(vmod_name,"Meta") ==0) vmod_masks.meta = bit;else if(qstrcmp(vmod_name,"AltGr") ==0) vmod_masks.altgr = bit;else if(qstrcmp(vmod_name,"Super") ==0) vmod_masks.super = bit;else if(qstrcmp(vmod_name,"Hyper") ==0) vmod_masks.hyper = bit;}}voidQXcbKeyboard::updateVModToRModMapping(){ xcb_xkb_get_map_map_t map;memset(&rmod_masks,0,sizeof(rmod_masks));auto map_reply =Q_XCB_REPLY(xcb_xkb_get_map,xcb_connection(), XCB_XKB_ID_USE_CORE_KBD, XCB_XKB_MAP_PART_VIRTUAL_MODS,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);if(!map_reply) {qWarning("Qt: failed to retrieve the virtual modifier map from XKB");return;}const void*buffer =xcb_xkb_get_map_map(map_reply.get());xcb_xkb_get_map_map_unpack(buffer, map_reply->nTypes, map_reply->nKeySyms, map_reply->nKeyActions, map_reply->totalActions, map_reply->totalKeyBehaviors, map_reply->nVModMapKeys, map_reply->totalKeyExplicit, map_reply->totalModMapKeys, map_reply->totalVModMapKeys, map_reply->present,&map); uint vmod_mask, bit;// the virtual modifiers mask for which a set of corresponding// real modifiers is to be returned vmod_mask = map_reply->virtualMods;int count =0;for(bit =1; vmod_mask; bit <<=1) { uint modmap;if(!(vmod_mask & bit))continue; vmod_mask &= ~bit;// real modifier bindings for the specified virtual modifiers modmap = map.vmods_rtrn[count]; count++;if(vmod_masks.alt == bit) rmod_masks.alt = modmap;else if(vmod_masks.meta == bit) rmod_masks.meta = modmap;else if(vmod_masks.altgr == bit) rmod_masks.altgr = modmap;else if(vmod_masks.super == bit) rmod_masks.super = modmap;else if(vmod_masks.hyper == bit) rmod_masks.hyper = modmap;}}// Small helper: set modifier bit, if modifier position is validstaticinlinevoidapplyModifier(uint *mask,int modifierBit){if(modifierBit >=0&& modifierBit <8)*mask |=1<< modifierBit;}voidQXcbKeyboard::updateModifiers(const KeysymModifierMap &keysymMods){if(connection()->hasXKB()) {updateVModMapping();updateVModToRModMapping();}else{memset(&rmod_masks,0,sizeof(rmod_masks));// Compute X modifier bits for Qt modifiersapplyModifier(&rmod_masks.alt, keysymMods.value(XKB_KEY_Alt_L, -1));applyModifier(&rmod_masks.alt, keysymMods.value(XKB_KEY_Alt_R, -1));applyModifier(&rmod_masks.meta, keysymMods.value(XKB_KEY_Meta_L, -1));applyModifier(&rmod_masks.meta, keysymMods.value(XKB_KEY_Meta_R, -1));applyModifier(&rmod_masks.altgr, keysymMods.value(XKB_KEY_Mode_switch, -1));applyModifier(&rmod_masks.super, keysymMods.value(XKB_KEY_Super_L, -1));applyModifier(&rmod_masks.super, keysymMods.value(XKB_KEY_Super_R, -1));applyModifier(&rmod_masks.hyper, keysymMods.value(XKB_KEY_Hyper_L, -1));applyModifier(&rmod_masks.hyper, keysymMods.value(XKB_KEY_Hyper_R, -1));}resolveMaskConflicts();}// Small helper: check if an array of xcb_keycode_t contains a certain codestaticinlineboolkeycodes_contains(xcb_keycode_t *codes, xcb_keycode_t which){while(*codes != XCB_NO_SYMBOL) {if(*codes == which)return true; codes++;}return false;}QXcbKeyboard::KeysymModifierMap QXcbKeyboard::keysymsToModifiers(){// The core protocol does not provide a convenient way to determine the mapping// of modifier bits. Clients must retrieve and search the modifier map to determine// the keycodes bound to each modifier, and then retrieve and search the keyboard// mapping to determine the keysyms bound to the keycodes. They must repeat this// process for all modifiers whenever any part of the modifier mapping is changed. KeysymModifierMap map;auto modMapReply =Q_XCB_REPLY(xcb_get_modifier_mapping,xcb_connection());if(!modMapReply) {qWarning("Qt: failed to get modifier mapping");return map;}// for Alt and Meta L and R are the samestatic const xcb_keysym_t symbols[] = { XKB_KEY_Alt_L, XKB_KEY_Meta_L, XKB_KEY_Mode_switch, XKB_KEY_Super_L, XKB_KEY_Super_R, XKB_KEY_Hyper_L, XKB_KEY_Hyper_R };static const size_t numSymbols =sizeof symbols /sizeof*symbols;// Figure out the modifier mapping, ICCCM 6.6 xcb_keycode_t* modKeyCodes[numSymbols];for(size_t i =0; i < numSymbols; ++i) modKeyCodes[i] =xcb_key_symbols_get_keycode(m_key_symbols, symbols[i]); xcb_keycode_t *modMap =xcb_get_modifier_mapping_keycodes(modMapReply.get());const int modMapLength =xcb_get_modifier_mapping_keycodes_length(modMapReply.get());/* For each modifier of "Shift, Lock, Control, Mod1, Mod2, Mod3, * Mod4, and Mod5" the modifier map contains keycodes_per_modifier * key codes that are associated with a modifier. * * As an example, take this 'xmodmap' output: * xmodmap: up to 4 keys per modifier, (keycodes in parentheses): * * shift Shift_L (0x32), Shift_R (0x3e) * lock Caps_Lock (0x42) * control Control_L (0x25), Control_R (0x69) * mod1 Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd) * mod2 Num_Lock (0x4d) * mod3 * mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf) * mod5 ISO_Level3_Shift (0x5c), Mode_switch (0xcb) * * The corresponding raw modifier map would contain keycodes for: * Shift_L (0x32), Shift_R (0x3e), 0, 0, * Caps_Lock (0x42), 0, 0, 0, * Control_L (0x25), Control_R (0x69), 0, 0, * Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd), 0, * Num_Lock (0x4d), 0, 0, 0, * 0,0,0,0, * Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf), * ISO_Level3_Shift (0x5c), Mode_switch (0xcb), 0, 0 *//* Create a map between a modifier keysym (as per the symbols array) * and the modifier bit it's associated with (if any). * As modMap contains key codes, search modKeyCodes for a match; * if one is found we can look up the associated keysym. * Together with the modifier index this will be used * to compute a mapping between X modifier bits and Qt's * modifiers (Alt, Ctrl etc). */for(int i =0; i < modMapLength; i++) {if(modMap[i] == XCB_NO_SYMBOL)continue;// Get key symbol for key codefor(size_t k =0; k < numSymbols; k++) {if(modKeyCodes[k] &&keycodes_contains(modKeyCodes[k], modMap[i])) {// Key code is for modifier. Record mapping xcb_keysym_t sym = symbols[k];/* As per modMap layout explanation above, dividing * by keycodes_per_modifier gives the 'row' in the * modifier map, which in turn is the modifier bit. */ map[sym] = i / modMapReply->keycodes_per_modifier;break;}}}for(size_t i =0; i < numSymbols; ++i)free(modKeyCodes[i]);return map;}voidQXcbKeyboard::resolveMaskConflicts(){// if we don't have a meta key (or it's hidden behind alt), use super or hyper to generate// Qt::Key_Meta and Qt::MetaModifier, since most newer XFree86/Xorg installations map the Windows// key to Superif(rmod_masks.alt == rmod_masks.meta) rmod_masks.meta =0;if(rmod_masks.meta ==0) {// no meta keys... s/meta/super, rmod_masks.meta = rmod_masks.super;if(rmod_masks.meta ==0) {// no super keys either? guess we'll use hyper then rmod_masks.meta = rmod_masks.hyper;}}// translate Super/Hyper keys to Meta if we're using them as the MetaModifierif(rmod_masks.meta && rmod_masks.meta == rmod_masks.super) m_superAsMeta =true;if(rmod_masks.meta && rmod_masks.meta == rmod_masks.hyper) m_hyperAsMeta =true;}voidQXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow,QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time,bool fromSendEvent){if(!m_config)return; QXcbWindow *source =connection()->platformWindowFromId(sourceWindow); QXcbWindow *targetWindow =connection()->focusWindow() ?connection()->focusWindow() : source;if(!targetWindow || !source)return;if(type ==QEvent::KeyPress) targetWindow->updateNetWmUserTime(time);QXkbCommon::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 text =QXkbCommon::lookupString(xkbState, code);Qt::KeyboardModifiers modifiers =translateModifiers(state);if(QXkbCommon::isKeypad(sym)) modifiers |=Qt::KeypadModifier;int qtcode =QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code, m_superAsMeta, m_hyperAsMeta);if(type ==QEvent::KeyPress) {if(m_isAutoRepeat && m_autoRepeatCode != code)// Some other key was pressed while we are auto-repeating on a different key. m_isAutoRepeat =false;}else{ m_isAutoRepeat =false;// Look at the next event in the queue to see if we are auto-repeating.connection()->eventQueue()->peek(QXcbEventQueue::PeekRetainMatch,[this, time, code](xcb_generic_event_t *event,int type) {if(type == XCB_KEY_PRESS) {auto keyPress =reinterpret_cast<xcb_key_press_event_t *>(event); m_isAutoRepeat = keyPress->time == time && keyPress->detail == code;if(m_isAutoRepeat) m_autoRepeatCode = code;}return true;});}bool filtered =false;if(auto inputContext =QGuiApplicationPrivate::platformIntegration()->inputContext()) { QKeyEvent event(type, qtcode, modifiers, code, sym, state, text, m_isAutoRepeat, text.size()); event.setTimestamp(time); filtered = inputContext->filterEvent(&event);}if(!filtered) { QWindow *window = targetWindow->window();#ifndef QT_NO_CONTEXTMENUif(type ==QEvent::KeyPress && qtcode ==Qt::Key_Menu) {const QPoint globalPos = window->screen()->handle()->cursor()->pos();const QPoint pos = window->mapFromGlobal(globalPos);QWindowSystemInterface::handleContextMenuEvent(window,false, pos, globalPos, modifiers);}#endifQWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, code, sym, state, text, m_isAutoRepeat);}}static boolfromSendEvent(const void*event){// 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;}voidQXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *e){handleKeyEvent(e->event,QEvent::KeyPress, e->detail, e->state, e->time,fromSendEvent(e));}voidQXcbKeyboard::handleKeyReleaseEvent(const xcb_key_release_event_t *e){handleKeyEvent(e->event,QEvent::KeyRelease, e->detail, e->state, e->time,fromSendEvent(e));} QT_END_NAMESPACE
|