mirror of
https://github.com/debauchee/barrier.git
synced 2025-06-23 13:07:05 +02:00
Initial commit of the synergy trunk sources from sf.net
This commit is contained in:
commit
958fa80d1d
429 changed files with 96848 additions and 0 deletions
826
lib/platform/CXWindowsKeyState.cpp
Normal file
826
lib/platform/CXWindowsKeyState.cpp
Normal file
|
@ -0,0 +1,826 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2003 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CXWindowsKeyState.h"
|
||||
#include "CXWindowsUtil.h"
|
||||
#include "CLog.h"
|
||||
#include "CStringUtil.h"
|
||||
#include "stdmap.h"
|
||||
#if X_DISPLAY_MISSING
|
||||
# error X11 is required to build synergy
|
||||
#else
|
||||
# include <X11/X.h>
|
||||
# include <X11/Xutil.h>
|
||||
# define XK_MISCELLANY
|
||||
# define XK_XKB_KEYS
|
||||
# include <X11/keysymdef.h>
|
||||
#if HAVE_XKB_EXTENSION
|
||||
# include <X11/XKBlib.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) :
|
||||
m_display(display)
|
||||
{
|
||||
XGetKeyboardControl(m_display, &m_keyboardState);
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (useXKB) {
|
||||
m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
|
||||
XkbAllClientInfoMask, XkbUseCoreKbd);
|
||||
}
|
||||
else {
|
||||
m_xkb = NULL;
|
||||
}
|
||||
#endif
|
||||
setActiveGroup(kGroupPollAndSet);
|
||||
}
|
||||
|
||||
CXWindowsKeyState::~CXWindowsKeyState()
|
||||
{
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
XkbFreeKeyboard(m_xkb, 0, True);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::setActiveGroup(SInt32 group)
|
||||
{
|
||||
if (group == kGroupPollAndSet) {
|
||||
m_group = -1;
|
||||
m_group = pollActiveGroup();
|
||||
}
|
||||
else if (group == kGroupPoll) {
|
||||
m_group = -1;
|
||||
}
|
||||
else {
|
||||
assert(group >= 0);
|
||||
m_group = group;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state)
|
||||
{
|
||||
m_keyboardState = state;
|
||||
}
|
||||
|
||||
KeyModifierMask
|
||||
CXWindowsKeyState::mapModifiersFromX(unsigned int state) const
|
||||
{
|
||||
UInt32 offset = 8 * getGroupFromState(state);
|
||||
KeyModifierMask mask = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if ((state & (1u << i)) != 0) {
|
||||
mask |= m_modifierFromX[offset + i];
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsKeyState::mapModifiersToX(KeyModifierMask mask,
|
||||
unsigned int& modifiers) const
|
||||
{
|
||||
modifiers = 0;
|
||||
|
||||
for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
|
||||
KeyModifierMask bit = (1u << i);
|
||||
if ((mask & bit) != 0) {
|
||||
KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit);
|
||||
if (j == m_modifierToX.end()) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
modifiers |= j->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::mapKeyToKeycodes(KeyID key, CKeycodeList& keycodes) const
|
||||
{
|
||||
keycodes.clear();
|
||||
std::pair<KeyToKeyCodeMap::const_iterator,
|
||||
KeyToKeyCodeMap::const_iterator> range =
|
||||
m_keyCodeFromKey.equal_range(key);
|
||||
for (KeyToKeyCodeMap::const_iterator i = range.first;
|
||||
i != range.second; ++i) {
|
||||
keycodes.push_back(i->second);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsKeyState::fakeCtrlAltDel()
|
||||
{
|
||||
// pass keys through unchanged
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyModifierMask
|
||||
CXWindowsKeyState::pollActiveModifiers() const
|
||||
{
|
||||
Window root = DefaultRootWindow(m_display), window;
|
||||
int xRoot, yRoot, xWindow, yWindow;
|
||||
unsigned int state;
|
||||
if (!XQueryPointer(m_display, root, &root, &window,
|
||||
&xRoot, &yRoot, &xWindow, &yWindow, &state)) {
|
||||
state = 0;
|
||||
}
|
||||
return mapModifiersFromX(state);
|
||||
}
|
||||
|
||||
SInt32
|
||||
CXWindowsKeyState::pollActiveGroup() const
|
||||
{
|
||||
if (m_group != -1) {
|
||||
assert(m_group >= 0);
|
||||
return m_group;
|
||||
}
|
||||
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
XkbStateRec state;
|
||||
if (XkbGetState(m_display, XkbUseCoreKbd, &state)) {
|
||||
return state.group;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
|
||||
{
|
||||
char keys[32];
|
||||
XQueryKeymap(m_display, keys);
|
||||
for (UInt32 i = 0; i < 32; ++i) {
|
||||
for (UInt32 j = 0; j < 8; ++j) {
|
||||
if ((keys[i] & (1u << j)) != 0) {
|
||||
pressedKeys.insert(8 * i + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::getKeyMap(CKeyMap& keyMap)
|
||||
{
|
||||
// get autorepeat info. we must use the global_auto_repeat told to
|
||||
// us because it may have modified by synergy.
|
||||
int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat;
|
||||
XGetKeyboardControl(m_display, &m_keyboardState);
|
||||
m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat;
|
||||
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
XkbGetUpdatedMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
|
||||
XkbAllClientInfoMask, m_xkb);
|
||||
updateKeysymMapXKB(keyMap);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
updateKeysymMap(keyMap);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::fakeKey(const Keystroke& keystroke)
|
||||
{
|
||||
switch (keystroke.m_type) {
|
||||
case Keystroke::kButton:
|
||||
LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
|
||||
if (keystroke.m_data.m_button.m_repeat) {
|
||||
int c = keystroke.m_data.m_button.m_button;
|
||||
int i = (c >> 3);
|
||||
int b = 1 << (c & 7);
|
||||
if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff ||
|
||||
(m_keyboardState.auto_repeats[i] & b) == 0) {
|
||||
LOG((CLOG_DEBUG1 " discard autorepeat"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button,
|
||||
keystroke.m_data.m_button.m_press ? True : False,
|
||||
CurrentTime);
|
||||
break;
|
||||
|
||||
case Keystroke::kGroup:
|
||||
if (keystroke.m_data.m_group.m_absolute) {
|
||||
LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
XkbLockGroup(m_display, XkbUseCoreKbd,
|
||||
keystroke.m_data.m_group.m_group);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
LOG((CLOG_DEBUG1 " ignored"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
XkbLockGroup(m_display, XkbUseCoreKbd,
|
||||
getEffectiveGroup(pollActiveGroup(),
|
||||
keystroke.m_data.m_group.m_group));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
LOG((CLOG_DEBUG1 " ignored"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
XFlush(m_display);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap)
|
||||
{
|
||||
// there are up to 4 keysyms per keycode
|
||||
static const int maxKeysyms = 4;
|
||||
|
||||
LOG((CLOG_DEBUG1 "non-XKB mapping"));
|
||||
|
||||
// prepare map from X modifier to KeyModifierMask. certain bits
|
||||
// are predefined.
|
||||
m_modifierFromX.clear();
|
||||
m_modifierFromX.resize(8);
|
||||
m_modifierFromX[ShiftMapIndex] = KeyModifierShift;
|
||||
m_modifierFromX[LockMapIndex] = KeyModifierCapsLock;
|
||||
m_modifierFromX[ControlMapIndex] = KeyModifierControl;
|
||||
m_modifierToX.clear();
|
||||
m_modifierToX[KeyModifierShift] = ShiftMask;
|
||||
m_modifierToX[KeyModifierCapsLock] = LockMask;
|
||||
m_modifierToX[KeyModifierControl] = ControlMask;
|
||||
|
||||
// prepare map from KeyID to KeyCode
|
||||
m_keyCodeFromKey.clear();
|
||||
|
||||
// get the number of keycodes
|
||||
int minKeycode, maxKeycode;
|
||||
XDisplayKeycodes(m_display, &minKeycode, &maxKeycode);
|
||||
int numKeycodes = maxKeycode - minKeycode + 1;
|
||||
|
||||
// get the keyboard mapping for all keys
|
||||
int keysymsPerKeycode;
|
||||
KeySym* allKeysyms = XGetKeyboardMapping(m_display,
|
||||
minKeycode, numKeycodes,
|
||||
&keysymsPerKeycode);
|
||||
|
||||
// it's more convenient to always have maxKeysyms KeySyms per key
|
||||
{
|
||||
KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes];
|
||||
for (int i = 0; i < numKeycodes; ++i) {
|
||||
for (int j = 0; j < maxKeysyms; ++j) {
|
||||
if (j < keysymsPerKeycode) {
|
||||
tmpKeysyms[maxKeysyms * i + j] =
|
||||
allKeysyms[keysymsPerKeycode * i + j];
|
||||
}
|
||||
else {
|
||||
tmpKeysyms[maxKeysyms * i + j] = NoSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
XFree(allKeysyms);
|
||||
allKeysyms = tmpKeysyms;
|
||||
}
|
||||
|
||||
// get the buttons assigned to modifiers. X11 does not predefine
|
||||
// the meaning of any modifiers except shift, caps lock, and the
|
||||
// control key. the meaning of a modifier bit (other than those)
|
||||
// depends entirely on the KeySyms mapped to that bit. unfortunately
|
||||
// you cannot map a bit back to the KeySym used to produce it.
|
||||
// for example, let's say button 1 maps to Alt_L without shift and
|
||||
// Meta_L with shift. now if mod1 is mapped to button 1 that could
|
||||
// mean the user used Alt or Meta to turn on that modifier and there's
|
||||
// no way to know which. it's also possible for one button to be
|
||||
// mapped to multiple bits so both mod1 and mod2 could be generated
|
||||
// by button 1.
|
||||
//
|
||||
// we're going to ignore any modifier for a button except the first.
|
||||
// with the above example, that means we'll ignore the mod2 modifier
|
||||
// bit unless it's also mapped to some other button. we're also
|
||||
// going to ignore all KeySyms except the first modifier KeySym,
|
||||
// which means button 1 above won't map to Meta, just Alt.
|
||||
std::map<KeyCode, unsigned int> modifierButtons;
|
||||
XModifierKeymap* modifiers = XGetModifierMapping(m_display);
|
||||
for (unsigned int i = 0; i < 8; ++i) {
|
||||
const KeyCode* buttons =
|
||||
modifiers->modifiermap + i * modifiers->max_keypermod;
|
||||
for (int j = 0; j < modifiers->max_keypermod; ++j) {
|
||||
modifierButtons.insert(std::make_pair(buttons[j], i));
|
||||
}
|
||||
}
|
||||
XFreeModifiermap(modifiers);
|
||||
modifierButtons.erase(0);
|
||||
|
||||
// Hack to deal with VMware. When a VMware client grabs input the
|
||||
// player clears out the X modifier map for whatever reason. We're
|
||||
// notified of the change and arrive here to discover that there
|
||||
// are no modifiers at all. Since this prevents the modifiers from
|
||||
// working in the VMware client we'll use the last known good set
|
||||
// of modifiers when there are no modifiers. If there are modifiers
|
||||
// we update the last known good set.
|
||||
if (!modifierButtons.empty()) {
|
||||
m_lastGoodNonXKBModifiers = modifierButtons;
|
||||
}
|
||||
else {
|
||||
modifierButtons = m_lastGoodNonXKBModifiers;
|
||||
}
|
||||
|
||||
// add entries for each keycode
|
||||
CKeyMap::KeyItem item;
|
||||
for (int i = 0; i < numKeycodes; ++i) {
|
||||
KeySym* keysyms = allKeysyms + maxKeysyms * i;
|
||||
KeyCode keycode = static_cast<KeyCode>(i + minKeycode);
|
||||
item.m_button = static_cast<KeyButton>(keycode);
|
||||
item.m_client = 0;
|
||||
|
||||
// determine modifier sensitivity
|
||||
item.m_sensitive = 0;
|
||||
|
||||
// if the keysyms in levels 2 or 3 exist and differ from levels
|
||||
// 0 and 1 then the key is sensitive AltGr (Mode_switch)
|
||||
if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) ||
|
||||
(keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) {
|
||||
item.m_sensitive |= KeyModifierAltGr;
|
||||
}
|
||||
|
||||
// check if the key is caps-lock sensitive. some systems only
|
||||
// provide one keysym for keys sensitive to caps-lock. if we
|
||||
// find that then fill in the missing keysym.
|
||||
if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol &&
|
||||
keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) {
|
||||
KeySym lKeysym, uKeysym;
|
||||
XConvertCase(keysyms[0], &lKeysym, &uKeysym);
|
||||
if (lKeysym != uKeysym) {
|
||||
keysyms[0] = lKeysym;
|
||||
keysyms[1] = uKeysym;
|
||||
item.m_sensitive |= KeyModifierCapsLock;
|
||||
}
|
||||
}
|
||||
else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) {
|
||||
KeySym lKeysym, uKeysym;
|
||||
XConvertCase(keysyms[0], &lKeysym, &uKeysym);
|
||||
if (lKeysym != uKeysym &&
|
||||
lKeysym == keysyms[0] &&
|
||||
uKeysym == keysyms[1]) {
|
||||
item.m_sensitive |= KeyModifierCapsLock;
|
||||
}
|
||||
else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) {
|
||||
XConvertCase(keysyms[2], &lKeysym, &uKeysym);
|
||||
if (lKeysym != uKeysym &&
|
||||
lKeysym == keysyms[2] &&
|
||||
uKeysym == keysyms[3]) {
|
||||
item.m_sensitive |= KeyModifierCapsLock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// key is sensitive to shift if keysyms in levels 0 and 1 or
|
||||
// levels 2 and 3 don't match. it's also sensitive to shift
|
||||
// if it's sensitive to caps-lock.
|
||||
if ((item.m_sensitive & KeyModifierCapsLock) != 0) {
|
||||
item.m_sensitive |= KeyModifierShift;
|
||||
}
|
||||
else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol &&
|
||||
keysyms[0] != keysyms[1]) ||
|
||||
(keysyms[2] != NoSymbol && keysyms[3] != NoSymbol &&
|
||||
keysyms[2] != keysyms[3])) {
|
||||
item.m_sensitive |= KeyModifierShift;
|
||||
}
|
||||
|
||||
// key is sensitive to numlock if any keysym on it is
|
||||
if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) ||
|
||||
IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) ||
|
||||
IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) ||
|
||||
IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) {
|
||||
item.m_sensitive |= KeyModifierNumLock;
|
||||
}
|
||||
|
||||
// do each keysym (shift level)
|
||||
for (int j = 0; j < maxKeysyms; ++j) {
|
||||
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[j]);
|
||||
if (item.m_id == kKeyNone) {
|
||||
if (j != 0 && modifierButtons.count(keycode) > 0) {
|
||||
// pretend the modifier works in other shift levels
|
||||
// because it probably does.
|
||||
if (keysyms[1] == NoSymbol || j != 3) {
|
||||
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[0]);
|
||||
}
|
||||
else {
|
||||
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[1]);
|
||||
}
|
||||
}
|
||||
if (item.m_id == kKeyNone) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// group is 0 for levels 0 and 1 and 1 for levels 2 and 3
|
||||
item.m_group = (j >= 2) ? 1 : 0;
|
||||
|
||||
// compute required modifiers
|
||||
item.m_required = 0;
|
||||
if ((j & 1) != 0) {
|
||||
item.m_required |= KeyModifierShift;
|
||||
}
|
||||
if ((j & 2) != 0) {
|
||||
item.m_required |= KeyModifierAltGr;
|
||||
}
|
||||
|
||||
item.m_generates = 0;
|
||||
item.m_lock = false;
|
||||
if (modifierButtons.count(keycode) > 0) {
|
||||
// get flags for modifier keys
|
||||
CKeyMap::initModifierKey(item);
|
||||
|
||||
// add mapping from X (unless we already have)
|
||||
if (item.m_generates != 0) {
|
||||
unsigned int bit = modifierButtons[keycode];
|
||||
if (m_modifierFromX[bit] == 0) {
|
||||
m_modifierFromX[bit] = item.m_generates;
|
||||
m_modifierToX[item.m_generates] = (1u << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add key
|
||||
keyMap.addKeyEntry(item);
|
||||
m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
|
||||
|
||||
// add other ways to synthesize the key
|
||||
if ((j & 1) != 0) {
|
||||
// add capslock version of key is sensitive to capslock
|
||||
KeySym lKeysym, uKeysym;
|
||||
XConvertCase(keysyms[j], &lKeysym, &uKeysym);
|
||||
if (lKeysym != uKeysym &&
|
||||
lKeysym == keysyms[j - 1] &&
|
||||
uKeysym == keysyms[j]) {
|
||||
item.m_required &= ~KeyModifierShift;
|
||||
item.m_required |= KeyModifierCapsLock;
|
||||
keyMap.addKeyEntry(item);
|
||||
item.m_required |= KeyModifierShift;
|
||||
item.m_required &= ~KeyModifierCapsLock;
|
||||
}
|
||||
|
||||
// add numlock version of key if sensitive to numlock
|
||||
if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) {
|
||||
item.m_required &= ~KeyModifierShift;
|
||||
item.m_required |= KeyModifierNumLock;
|
||||
keyMap.addKeyEntry(item);
|
||||
item.m_required |= KeyModifierShift;
|
||||
item.m_required &= ~KeyModifierNumLock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] allKeysyms;
|
||||
}
|
||||
|
||||
#if HAVE_XKB_EXTENSION
|
||||
void
|
||||
CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap)
|
||||
{
|
||||
static const XkbKTMapEntryRec defMapEntry = {
|
||||
True, // active
|
||||
0, // level
|
||||
{
|
||||
0, // mods.mask
|
||||
0, // mods.real_mods
|
||||
0 // mods.vmods
|
||||
}
|
||||
};
|
||||
|
||||
LOG((CLOG_DEBUG1 "XKB mapping"));
|
||||
|
||||
// find the number of groups
|
||||
int maxNumGroups = 0;
|
||||
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
|
||||
int numGroups = XkbKeyNumGroups(m_xkb, static_cast<KeyCode>(i));
|
||||
if (numGroups > maxNumGroups) {
|
||||
maxNumGroups = numGroups;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare map from X modifier to KeyModifierMask
|
||||
std::vector<int> modifierLevel(maxNumGroups * 8, 4);
|
||||
m_modifierFromX.clear();
|
||||
m_modifierFromX.resize(maxNumGroups * 8);
|
||||
m_modifierToX.clear();
|
||||
|
||||
// prepare map from KeyID to KeyCode
|
||||
m_keyCodeFromKey.clear();
|
||||
|
||||
// Hack to deal with VMware. When a VMware client grabs input the
|
||||
// player clears out the X modifier map for whatever reason. We're
|
||||
// notified of the change and arrive here to discover that there
|
||||
// are no modifiers at all. Since this prevents the modifiers from
|
||||
// working in the VMware client we'll use the last known good set
|
||||
// of modifiers when there are no modifiers. If there are modifiers
|
||||
// we update the last known good set.
|
||||
bool useLastGoodModifiers = !hasModifiersXKB();
|
||||
if (!useLastGoodModifiers) {
|
||||
m_lastGoodXKBModifiers.clear();
|
||||
}
|
||||
|
||||
// check every button. on this pass we save all modifiers as native
|
||||
// X modifier masks.
|
||||
CKeyMap::KeyItem item;
|
||||
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
|
||||
KeyCode keycode = static_cast<KeyCode>(i);
|
||||
item.m_button = static_cast<KeyButton>(keycode);
|
||||
item.m_client = 0;
|
||||
|
||||
// skip keys with no groups (they generate no symbols)
|
||||
if (XkbKeyNumGroups(m_xkb, keycode) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// note half-duplex keys
|
||||
const XkbBehavior& b = m_xkb->server->behaviors[keycode];
|
||||
if ((b.type & XkbKB_OpMask) == XkbKB_Lock) {
|
||||
keyMap.addHalfDuplexButton(item.m_button);
|
||||
}
|
||||
|
||||
// iterate over all groups
|
||||
for (int group = 0; group < maxNumGroups; ++group) {
|
||||
item.m_group = group;
|
||||
int eGroup = getEffectiveGroup(keycode, group);
|
||||
|
||||
// get key info
|
||||
XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup);
|
||||
|
||||
// set modifiers the item is sensitive to
|
||||
item.m_sensitive = type->mods.mask;
|
||||
|
||||
// iterate over all shift levels for the button (including none)
|
||||
for (int j = -1; j < type->map_count; ++j) {
|
||||
const XkbKTMapEntryRec* mapEntry =
|
||||
((j == -1) ? &defMapEntry : type->map + j);
|
||||
if (!mapEntry->active) {
|
||||
continue;
|
||||
}
|
||||
int level = mapEntry->level;
|
||||
|
||||
// set required modifiers for this item
|
||||
item.m_required = mapEntry->mods.mask;
|
||||
if ((item.m_required & LockMask) != 0 &&
|
||||
j != -1 && type->preserve != NULL &&
|
||||
(type->preserve[j].mask & LockMask) != 0) {
|
||||
// sensitive caps lock and we preserve caps-lock.
|
||||
// preserving caps-lock means we Xlib functions would
|
||||
// yield the capitialized KeySym so we'll adjust the
|
||||
// level accordingly.
|
||||
if ((level ^ 1) < type->num_levels) {
|
||||
level ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// get the keysym for this item
|
||||
KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup);
|
||||
|
||||
// check for group change actions, locking modifiers, and
|
||||
// modifier masks.
|
||||
item.m_lock = false;
|
||||
bool isModifier = false;
|
||||
UInt32 modifierMask = m_xkb->map->modmap[keycode];
|
||||
if (XkbKeyHasActions(m_xkb, keycode)) {
|
||||
XkbAction* action =
|
||||
XkbKeyActionEntry(m_xkb, keycode, level, eGroup);
|
||||
if (action->type == XkbSA_SetMods ||
|
||||
action->type == XkbSA_LockMods) {
|
||||
isModifier = true;
|
||||
|
||||
// note toggles
|
||||
item.m_lock = (action->type == XkbSA_LockMods);
|
||||
|
||||
// maybe use action's mask
|
||||
if ((action->mods.flags & XkbSA_UseModMapMods) == 0) {
|
||||
modifierMask = action->mods.mask;
|
||||
}
|
||||
}
|
||||
else if (action->type == XkbSA_SetGroup ||
|
||||
action->type == XkbSA_LatchGroup ||
|
||||
action->type == XkbSA_LockGroup) {
|
||||
// ignore group change key
|
||||
continue;
|
||||
}
|
||||
}
|
||||
level = mapEntry->level;
|
||||
|
||||
// VMware modifier hack
|
||||
if (useLastGoodModifiers) {
|
||||
XKBModifierMap::const_iterator k =
|
||||
m_lastGoodXKBModifiers.find(eGroup * 256 + keycode);
|
||||
if (k != m_lastGoodXKBModifiers.end()) {
|
||||
// Use last known good modifier
|
||||
isModifier = true;
|
||||
level = k->second.m_level;
|
||||
modifierMask = k->second.m_mask;
|
||||
item.m_lock = k->second.m_lock;
|
||||
}
|
||||
}
|
||||
else if (isModifier) {
|
||||
// Save known good modifier
|
||||
XKBModifierInfo& info =
|
||||
m_lastGoodXKBModifiers[eGroup * 256 + keycode];
|
||||
info.m_level = level;
|
||||
info.m_mask = modifierMask;
|
||||
info.m_lock = item.m_lock;
|
||||
}
|
||||
|
||||
// record the modifier mask for this key. don't bother
|
||||
// for keys that change the group.
|
||||
item.m_generates = 0;
|
||||
UInt32 modifierBit =
|
||||
CXWindowsUtil::getModifierBitForKeySym(keysym);
|
||||
if (isModifier && modifierBit != kKeyModifierBitNone) {
|
||||
item.m_generates = (1u << modifierBit);
|
||||
for (SInt32 j = 0; j < 8; ++j) {
|
||||
// skip modifiers this key doesn't generate
|
||||
if ((modifierMask & (1u << j)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip keys that map to a modifier that we've
|
||||
// already seen using fewer modifiers. that is
|
||||
// if this key must combine with other modifiers
|
||||
// and we know of a key that combines with fewer
|
||||
// modifiers (or no modifiers) then prefer the
|
||||
// other key.
|
||||
if (level >= modifierLevel[8 * group + j]) {
|
||||
continue;
|
||||
}
|
||||
modifierLevel[8 * group + j] = level;
|
||||
|
||||
// save modifier
|
||||
m_modifierFromX[8 * group + j] |= (1u << modifierBit);
|
||||
m_modifierToX.insert(std::make_pair(
|
||||
1u << modifierBit, 1u << j));
|
||||
}
|
||||
}
|
||||
|
||||
// handle special cases of just one keysym for the keycode
|
||||
if (type->num_levels == 1) {
|
||||
// if there are upper- and lowercase versions of the
|
||||
// keysym then add both.
|
||||
KeySym lKeysym, uKeysym;
|
||||
XConvertCase(keysym, &lKeysym, &uKeysym);
|
||||
if (lKeysym != uKeysym) {
|
||||
if (j != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.m_sensitive |= ShiftMask | LockMask;
|
||||
|
||||
KeyID lKeyID = CXWindowsUtil::mapKeySymToKeyID(lKeysym);
|
||||
KeyID uKeyID = CXWindowsUtil::mapKeySymToKeyID(uKeysym);
|
||||
if (lKeyID == kKeyNone || uKeyID == kKeyNone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.m_id = lKeyID;
|
||||
item.m_required = 0;
|
||||
keyMap.addKeyEntry(item);
|
||||
|
||||
item.m_id = uKeyID;
|
||||
item.m_required = ShiftMask;
|
||||
keyMap.addKeyEntry(item);
|
||||
item.m_required = LockMask;
|
||||
keyMap.addKeyEntry(item);
|
||||
|
||||
if (group == 0) {
|
||||
m_keyCodeFromKey.insert(
|
||||
std::make_pair(lKeyID, keycode));
|
||||
m_keyCodeFromKey.insert(
|
||||
std::make_pair(uKeyID, keycode));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// add entry
|
||||
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysym);
|
||||
keyMap.addKeyEntry(item);
|
||||
if (group == 0) {
|
||||
m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change all modifier masks to synergy masks from X masks
|
||||
keyMap.foreachKey(&CXWindowsKeyState::remapKeyModifiers, this);
|
||||
|
||||
// allow composition across groups
|
||||
keyMap.allowGroupSwitchDuringCompose();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CXWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group,
|
||||
CKeyMap::KeyItem& item, void* vself)
|
||||
{
|
||||
CXWindowsKeyState* self = reinterpret_cast<CXWindowsKeyState*>(vself);
|
||||
item.m_required =
|
||||
self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group));
|
||||
item.m_sensitive =
|
||||
self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group));
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsKeyState::hasModifiersXKB() const
|
||||
{
|
||||
#if HAVE_XKB_EXTENSION
|
||||
// iterate over all keycodes
|
||||
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
|
||||
KeyCode keycode = static_cast<KeyCode>(i);
|
||||
if (XkbKeyHasActions(m_xkb, keycode)) {
|
||||
// iterate over all groups
|
||||
int numGroups = XkbKeyNumGroups(m_xkb, keycode);
|
||||
for (int group = 0; group < numGroups; ++group) {
|
||||
// iterate over all shift levels for the button (including none)
|
||||
XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group);
|
||||
for (int j = -1; j < type->map_count; ++j) {
|
||||
if (j != -1 && !type->map[j].active) {
|
||||
continue;
|
||||
}
|
||||
int level = ((j == -1) ? 0 : type->map[j].level);
|
||||
XkbAction* action =
|
||||
XkbKeyActionEntry(m_xkb, keycode, level, group);
|
||||
if (action->type == XkbSA_SetMods ||
|
||||
action->type == XkbSA_LockMods) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
CXWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const
|
||||
{
|
||||
(void)keycode;
|
||||
#if HAVE_XKB_EXTENSION
|
||||
// get effective group for key
|
||||
int numGroups = XkbKeyNumGroups(m_xkb, keycode);
|
||||
if (group >= numGroups) {
|
||||
unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode);
|
||||
switch (XkbOutOfRangeGroupAction(groupInfo)) {
|
||||
case XkbClampIntoRange:
|
||||
group = numGroups - 1;
|
||||
break;
|
||||
|
||||
case XkbRedirectIntoRange:
|
||||
group = XkbOutOfRangeGroupNumber(groupInfo);
|
||||
if (group >= numGroups) {
|
||||
group = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// wrap
|
||||
group %= numGroups;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return group;
|
||||
}
|
||||
|
||||
UInt32
|
||||
CXWindowsKeyState::getGroupFromState(unsigned int state) const
|
||||
{
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
return XkbGroupForCoreState(state);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue