changed processing of key events in X. secondary screen now
activates/deactivates modifiers as necessary to get a keycode interpreted as the expected keysym. still some work and testing to do on this.
This commit is contained in:
parent
b01fc4bd35
commit
419eadfaf9
|
@ -4,6 +4,9 @@
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <X11/X.h>
|
#include <X11/X.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#define XK_MISCELLANY
|
||||||
|
#include <X11/keysymdef.h>
|
||||||
#include <X11/extensions/XTest.h>
|
#include <X11/extensions/XTest.h>
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -35,6 +38,17 @@ void CXWindowsSecondaryScreen::run()
|
||||||
|
|
||||||
// handle event
|
// handle event
|
||||||
switch (xevent.type) {
|
switch (xevent.type) {
|
||||||
|
case MappingNotify: {
|
||||||
|
// keyboard mapping changed
|
||||||
|
CDisplayLock display(this);
|
||||||
|
XRefreshKeyboardMapping(&xevent.xmapping);
|
||||||
|
updateKeys(display);
|
||||||
|
updateKeycodeMap(display);
|
||||||
|
updateModifierMap(display);
|
||||||
|
updateModifiers(display);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case LeaveNotify: {
|
case LeaveNotify: {
|
||||||
// mouse moved out of hider window somehow. hide the window.
|
// mouse moved out of hider window somehow. hide the window.
|
||||||
assert(m_window != None);
|
assert(m_window != None);
|
||||||
|
@ -118,6 +132,12 @@ void CXWindowsSecondaryScreen::open(CClient* client)
|
||||||
if (!XQueryExtension(display, XTestExtensionName,
|
if (!XQueryExtension(display, XTestExtensionName,
|
||||||
&majorOpcode, &firstEvent, &firstError))
|
&majorOpcode, &firstEvent, &firstError))
|
||||||
throw int(6); // FIXME -- make exception for this
|
throw int(6); // FIXME -- make exception for this
|
||||||
|
|
||||||
|
// update key state
|
||||||
|
updateKeys(display);
|
||||||
|
updateKeycodeMap(display);
|
||||||
|
updateModifierMap(display);
|
||||||
|
updateModifiers(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsSecondaryScreen::close()
|
void CXWindowsSecondaryScreen::close()
|
||||||
|
@ -143,6 +163,10 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y)
|
||||||
|
|
||||||
// show cursor
|
// show cursor
|
||||||
XUnmapWindow(display, m_window);
|
XUnmapWindow(display, m_window);
|
||||||
|
|
||||||
|
// update our keyboard state to reflect the local state
|
||||||
|
updateKeys(display);
|
||||||
|
updateModifiers(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsSecondaryScreen::leave()
|
void CXWindowsSecondaryScreen::leave()
|
||||||
|
@ -154,8 +178,25 @@ void CXWindowsSecondaryScreen::leave()
|
||||||
void CXWindowsSecondaryScreen::keyDown(
|
void CXWindowsSecondaryScreen::keyDown(
|
||||||
KeyID key, KeyModifierMask mask)
|
KeyID key, KeyModifierMask mask)
|
||||||
{
|
{
|
||||||
|
Keystrokes keys;
|
||||||
|
KeyCode keycode;
|
||||||
|
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
XTestFakeKeyEvent(display, mapKey(key, mask), True, CurrentTime);
|
|
||||||
|
// get the sequence of keys to simulate key press and the final
|
||||||
|
// modifier state.
|
||||||
|
m_mask = mapKey(keys, keycode, key, mask, True);
|
||||||
|
if (keys.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// generate key events
|
||||||
|
for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k)
|
||||||
|
XTestFakeKeyEvent(display, k->first, k->second, CurrentTime);
|
||||||
|
|
||||||
|
// note that key is now down
|
||||||
|
m_keys[keycode] = true;
|
||||||
|
|
||||||
|
// update
|
||||||
XSync(display, False);
|
XSync(display, False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +210,25 @@ void CXWindowsSecondaryScreen::keyRepeat(
|
||||||
void CXWindowsSecondaryScreen::keyUp(
|
void CXWindowsSecondaryScreen::keyUp(
|
||||||
KeyID key, KeyModifierMask mask)
|
KeyID key, KeyModifierMask mask)
|
||||||
{
|
{
|
||||||
|
Keystrokes keys;
|
||||||
|
KeyCode keycode;
|
||||||
|
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
XTestFakeKeyEvent(display, mapKey(key, mask), False, CurrentTime);
|
|
||||||
|
// get the sequence of keys to simulate key release and the final
|
||||||
|
// modifier state.
|
||||||
|
m_mask = mapKey(keys, keycode, key, mask, False);
|
||||||
|
if (keys.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// generate key events
|
||||||
|
for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k)
|
||||||
|
XTestFakeKeyEvent(display, k->first, k->second, CurrentTime);
|
||||||
|
|
||||||
|
// note that key is now up
|
||||||
|
m_keys[keycode] = false;
|
||||||
|
|
||||||
|
// update
|
||||||
XSync(display, False);
|
XSync(display, False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,17 +352,408 @@ void CXWindowsSecondaryScreen::leaveNoLock(Display* display)
|
||||||
XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0);
|
XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyCode CXWindowsSecondaryScreen::mapKey(
|
|
||||||
KeyID id, KeyModifierMask /*mask*/) const
|
|
||||||
{
|
|
||||||
CDisplayLock display(this);
|
|
||||||
// FIXME -- use mask
|
|
||||||
return XKeysymToKeycode(display, static_cast<KeySym>(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CXWindowsSecondaryScreen::mapButton(
|
unsigned int CXWindowsSecondaryScreen::mapButton(
|
||||||
ButtonID id) const
|
ButtonID id) const
|
||||||
{
|
{
|
||||||
// FIXME -- should use button mapping?
|
// FIXME -- should use button mapping?
|
||||||
return static_cast<unsigned int>(id);
|
return static_cast<unsigned int>(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyModifierMask CXWindowsSecondaryScreen::mapKey(
|
||||||
|
Keystrokes& keys,
|
||||||
|
KeyCode& keycode,
|
||||||
|
KeyID id, KeyModifierMask mask,
|
||||||
|
Bool press) const
|
||||||
|
{
|
||||||
|
// note -- must have display locked on entry
|
||||||
|
|
||||||
|
// the system translates key events into characters depending
|
||||||
|
// on the modifier key state at the time of the event. to
|
||||||
|
// generate the right keysym we need to set the modifier key
|
||||||
|
// states appropriately.
|
||||||
|
//
|
||||||
|
// the mask passed by the caller is the desired mask. however,
|
||||||
|
// there may not be a keycode mapping to generate the desired
|
||||||
|
// keysym with that mask. we override the bits in the mask
|
||||||
|
// that cannot be accomodated.
|
||||||
|
|
||||||
|
// lookup the a keycode for this key id. also return the
|
||||||
|
// key modifier mask required.
|
||||||
|
unsigned int outMask;
|
||||||
|
if (!findKeyCode(keycode, outMask, id, maskToX(mask))) {
|
||||||
|
// we cannot generate the desired keysym because no key
|
||||||
|
// maps to that keysym. just return the current mask.
|
||||||
|
return m_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we cannot match the modifier mask then don't return any
|
||||||
|
// keys and just return the current mask.
|
||||||
|
if ((outMask & m_modifierMask) != outMask) {
|
||||||
|
return m_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note if the key is a modifier
|
||||||
|
ModifierMap::const_iterator index = m_keycodeToModifier.find(keycode);
|
||||||
|
const bool isModifier = (index != m_keycodeToModifier.end());
|
||||||
|
|
||||||
|
// add the key events required to get to the modifier state
|
||||||
|
// necessary to generate an event yielding id. also save the
|
||||||
|
// key events required to restore the state. if the key is
|
||||||
|
// a modifier key then skip this because modifiers should not
|
||||||
|
// modify modifiers.
|
||||||
|
Keystrokes undo;
|
||||||
|
if (outMask != m_mask && !isModifier) {
|
||||||
|
for (unsigned int i = 0; i < 8; ++i) {
|
||||||
|
unsigned int bit = (1 << i);
|
||||||
|
if ((outMask & bit) != (m_mask & bit)) {
|
||||||
|
// get list of keycodes for the modifier. there must
|
||||||
|
// be at least one.
|
||||||
|
const KeyCode* modifierKeys =
|
||||||
|
&m_modifierToKeycode[i * m_keysPerModifier];
|
||||||
|
assert(modifierKeys[0] != 0);
|
||||||
|
|
||||||
|
if ((outMask & bit) != 0) {
|
||||||
|
// modifier is not active but should be. if the
|
||||||
|
// modifier is a toggle then toggle it on with a
|
||||||
|
// press/release, otherwise activate it with a
|
||||||
|
// press. use the first keycode for the modifier.
|
||||||
|
const KeyCode modifierKey = modifierKeys[0];
|
||||||
|
keys.push_back(std::make_pair(modifierKey, True));
|
||||||
|
if ((bit & m_toggleModifierMask) != 0) {
|
||||||
|
keys.push_back(std::make_pair(modifierKey, False));
|
||||||
|
undo.push_back(std::make_pair(modifierKey, False));
|
||||||
|
undo.push_back(std::make_pair(modifierKey, True));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
undo.push_back(std::make_pair(modifierKey, False));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
// modifier is active but should not be. if the
|
||||||
|
// modifier is a toggle then toggle it off with a
|
||||||
|
// press/release, otherwise deactivate it with a
|
||||||
|
// release. we must check each keycode for the
|
||||||
|
// modifier if not a toggle.
|
||||||
|
if (bit & m_toggleModifierMask) {
|
||||||
|
const KeyCode modifierKey = modifierKeys[0];
|
||||||
|
keys.push_back(std::make_pair(modifierKey, True));
|
||||||
|
keys.push_back(std::make_pair(modifierKey, False));
|
||||||
|
undo.push_back(std::make_pair(modifierKey, False));
|
||||||
|
undo.push_back(std::make_pair(modifierKey, True));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
|
||||||
|
const KeyCode key = modifierKeys[j];
|
||||||
|
if (m_keys[key]) {
|
||||||
|
keys.push_back(std::make_pair(key, False));
|
||||||
|
undo.push_back(std::make_pair(key, True));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the key event
|
||||||
|
keys.push_back(std::make_pair(keycode, press));
|
||||||
|
|
||||||
|
// add key events to restore the modifier state. apply events in
|
||||||
|
// the reverse order that they're stored in undo.
|
||||||
|
while (!undo.empty()) {
|
||||||
|
keys.push_back(undo.back());
|
||||||
|
undo.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the key is a modifier key then compute the modifier map after
|
||||||
|
// this key is pressed.
|
||||||
|
mask = m_mask;
|
||||||
|
if (isModifier) {
|
||||||
|
// get modifier
|
||||||
|
const unsigned int modifierBit = (1 << index->second);
|
||||||
|
|
||||||
|
// toggle keys modify the state on press if toggling on and on
|
||||||
|
// release if toggling off. other keys set the bit on press
|
||||||
|
// and clear the bit on release.
|
||||||
|
if ((modifierBit & m_toggleModifierMask) != 0) {
|
||||||
|
if (((mask & modifierBit) == 0) == press)
|
||||||
|
mask ^= modifierBit;
|
||||||
|
}
|
||||||
|
else if (press) {
|
||||||
|
mask |= modifierBit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// can't reset bit until all keys that set it are released.
|
||||||
|
// scan those keys to see if any (except keycode) are pressed.
|
||||||
|
bool down = false;
|
||||||
|
const KeyCode* modifierKeys = &m_modifierToKeycode[
|
||||||
|
index->second * m_keysPerModifier];
|
||||||
|
for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) {
|
||||||
|
if (m_keys[modifierKeys[j]])
|
||||||
|
down = true;
|
||||||
|
}
|
||||||
|
if (!down)
|
||||||
|
mask &= ~modifierBit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CXWindowsSecondaryScreen::findKeyCode(
|
||||||
|
KeyCode& keycode,
|
||||||
|
unsigned int& maskOut,
|
||||||
|
KeyID id,
|
||||||
|
unsigned int maskIn) const
|
||||||
|
{
|
||||||
|
// find a keycode to generate id. XKeysymToKeycode() almost does
|
||||||
|
// what we need but won't tell us which index to use with the
|
||||||
|
// keycode. return false if there's no keycode to generate id.
|
||||||
|
KeyCodeMap::const_iterator index = m_keycodeMap.find(id);
|
||||||
|
if (index == m_keycodeMap.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// save the keycode
|
||||||
|
keycode = index->second.keycode;
|
||||||
|
|
||||||
|
// compute output mask. that's the set of modifiers that need to
|
||||||
|
// be enabled when the keycode event is encountered in order to
|
||||||
|
// generate the id keysym and match maskIn. it's possible that
|
||||||
|
// maskIn wants, say, a shift key to be down but that would make
|
||||||
|
// it impossible to generate the keysym. in that case we must
|
||||||
|
// override maskIn.
|
||||||
|
//
|
||||||
|
// this is complicated by caps/shift-lock and num-lock. for
|
||||||
|
// example, if id is a keypad keysym and maskIn indicates that
|
||||||
|
// shift is not active but keyMask indicates that shift is
|
||||||
|
// required then we can either activate shift and then send
|
||||||
|
// the keycode or we can activate num-lock and then send the
|
||||||
|
// keycode. the latter is better because applications may
|
||||||
|
// treat, say, shift+Home differently than Home.
|
||||||
|
maskOut = (maskIn & ~index->second.keyMaskMask);
|
||||||
|
if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) {
|
||||||
|
// compare shift state of maskIn and keyMask
|
||||||
|
const bool agree = ((maskIn & ShiftMask) ==
|
||||||
|
(index->second.keyMask & ShiftMask));
|
||||||
|
|
||||||
|
// get num-lock state
|
||||||
|
const bool numLockActive = ((m_mask & m_numLockMask) != 0);
|
||||||
|
|
||||||
|
// if num-lock is active and the shift states agree or if
|
||||||
|
// num-lock is not active and the shift states do not agree
|
||||||
|
// then we should toggle num-lock.
|
||||||
|
if (numLockActive == agree) {
|
||||||
|
maskOut |= (maskIn & ShiftMask) |
|
||||||
|
(index->second.keyMask & ~ShiftMask);
|
||||||
|
if (numLockActive)
|
||||||
|
maskOut &= ~m_numLockMask;
|
||||||
|
else
|
||||||
|
maskOut |= m_numLockMask;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
maskOut |= index->second.keyMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// compare shift state of maskIn and keyMask
|
||||||
|
const bool agree = ((maskIn & ShiftMask) ==
|
||||||
|
(index->second.keyMask & ShiftMask));
|
||||||
|
|
||||||
|
// get caps-lock state
|
||||||
|
const bool capsLockActive = ((m_mask & m_capsLockMask) != 0);
|
||||||
|
|
||||||
|
// if caps-lock is active and the shift states agree or if
|
||||||
|
// caps-lock is not active and the shift states do not agree
|
||||||
|
// then we should toggle caps-lock.
|
||||||
|
if (capsLockActive == agree) {
|
||||||
|
maskOut |= (maskIn & ShiftMask) |
|
||||||
|
(index->second.keyMask & ~ShiftMask);
|
||||||
|
if (capsLockActive)
|
||||||
|
maskOut &= ~m_capsLockMask;
|
||||||
|
else
|
||||||
|
maskOut |= m_capsLockMask;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
maskOut |= index->second.keyMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CXWindowsSecondaryScreen::maskToX(
|
||||||
|
KeyModifierMask inMask) const
|
||||||
|
{
|
||||||
|
// FIXME -- should be configurable. not using Mod3Mask.
|
||||||
|
unsigned int outMask = 0;
|
||||||
|
if (inMask & KeyModifierShift)
|
||||||
|
outMask |= ShiftMask;
|
||||||
|
if (inMask & KeyModifierControl)
|
||||||
|
outMask |= ControlMask;
|
||||||
|
if (inMask & KeyModifierAlt)
|
||||||
|
outMask |= Mod1Mask;
|
||||||
|
if (inMask & KeyModifierMeta)
|
||||||
|
outMask |= Mod4Mask;
|
||||||
|
if (inMask & KeyModifierCapsLock)
|
||||||
|
outMask |= LockMask;
|
||||||
|
if (inMask & KeyModifierNumLock)
|
||||||
|
outMask |= Mod2Mask;
|
||||||
|
if (inMask & KeyModifierScrollLock)
|
||||||
|
outMask |= Mod5Mask;
|
||||||
|
return outMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsSecondaryScreen::updateKeys(Display* display)
|
||||||
|
{
|
||||||
|
// ask server which keys are pressed
|
||||||
|
char keys[32];
|
||||||
|
XQueryKeymap(display, keys);
|
||||||
|
|
||||||
|
// transfer to our state
|
||||||
|
for (unsigned int i = 0, j = 0; i < 32; j += 8, ++i) {
|
||||||
|
m_keys[j + 0] = ((keys[i] & 0x01) != 0);
|
||||||
|
m_keys[j + 1] = ((keys[i] & 0x02) != 0);
|
||||||
|
m_keys[j + 2] = ((keys[i] & 0x04) != 0);
|
||||||
|
m_keys[j + 3] = ((keys[i] & 0x08) != 0);
|
||||||
|
m_keys[j + 4] = ((keys[i] & 0x10) != 0);
|
||||||
|
m_keys[j + 5] = ((keys[i] & 0x20) != 0);
|
||||||
|
m_keys[j + 6] = ((keys[i] & 0x40) != 0);
|
||||||
|
m_keys[j + 7] = ((keys[i] & 0x80) != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsSecondaryScreen::updateModifiers(
|
||||||
|
Display*)
|
||||||
|
{
|
||||||
|
// update active modifier mask
|
||||||
|
m_mask = 0;
|
||||||
|
for (unsigned int i = 0; i < 8; ++i) {
|
||||||
|
const unsigned int bit = (1 << i);
|
||||||
|
if ((bit & m_toggleModifierMask) == 0) {
|
||||||
|
for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
|
||||||
|
if (m_keys[m_modifierToKeycode[i * m_keysPerModifier + j]])
|
||||||
|
m_mask |= bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// FIXME -- not sure how to check current lock states
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsSecondaryScreen::updateKeycodeMap(
|
||||||
|
Display* display)
|
||||||
|
{
|
||||||
|
// get the number of keycodes
|
||||||
|
int minKeycode, maxKeycode;
|
||||||
|
XDisplayKeycodes(display, &minKeycode, &maxKeycode);
|
||||||
|
const int numKeycodes = maxKeycode - minKeycode + 1;
|
||||||
|
|
||||||
|
// get the keyboard mapping for all keys
|
||||||
|
int keysymsPerKeycode;
|
||||||
|
KeySym* keysyms = XGetKeyboardMapping(display,
|
||||||
|
minKeycode, numKeycodes,
|
||||||
|
&keysymsPerKeycode);
|
||||||
|
|
||||||
|
// restrict keysyms per keycode to 2 because, frankly, i have no
|
||||||
|
// idea how/what modifiers are used to access keysyms beyond the
|
||||||
|
// first 2.
|
||||||
|
int numKeysyms = 2; // keysymsPerKeycode
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
KeyCodeMask entry;
|
||||||
|
m_keycodeMap.clear();
|
||||||
|
|
||||||
|
// insert keys
|
||||||
|
for (int i = 0; i < numKeycodes; ++i) {
|
||||||
|
// how many keysyms for this keycode?
|
||||||
|
int n;
|
||||||
|
for (n = 0; n < numKeysyms; ++n) {
|
||||||
|
if (keysyms[i * keysymsPerKeycode + n] == NoSymbol) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to next keycode if there are no keysyms
|
||||||
|
if (n == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the mask of modifiers that this keycode uses
|
||||||
|
entry.keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask);
|
||||||
|
|
||||||
|
// add entries for this keycode
|
||||||
|
entry.keycode = static_cast<KeyCode>(minKeycode + i);
|
||||||
|
for (int j = 0; j < numKeysyms; ++j) {
|
||||||
|
entry.keyMask = (j == 0) ? 0 : ShiftMask;
|
||||||
|
m_keycodeMap.insert(std::make_pair(keysyms[i *
|
||||||
|
keysymsPerKeycode + j], entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
XFree(keysyms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsSecondaryScreen::updateModifierMap(
|
||||||
|
Display* display)
|
||||||
|
{
|
||||||
|
// get modifier map from server
|
||||||
|
XModifierKeymap* keymap = XGetModifierMapping(display);
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
m_modifierMask = 0;
|
||||||
|
m_toggleModifierMask = 0;
|
||||||
|
m_numLockMask = 0;
|
||||||
|
m_capsLockMask = 0;
|
||||||
|
m_keysPerModifier = keymap->max_keypermod;
|
||||||
|
m_modifierToKeycode.clear();
|
||||||
|
m_modifierToKeycode.resize(8 * m_keysPerModifier);
|
||||||
|
|
||||||
|
// set keycodes and masks
|
||||||
|
for (unsigned int i = 0; i < 8; ++i) {
|
||||||
|
const unsigned int bit = (1 << i);
|
||||||
|
for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
|
||||||
|
KeyCode keycode = keymap->modifiermap[i * m_keysPerModifier + j];
|
||||||
|
|
||||||
|
// save in modifier to keycode
|
||||||
|
m_modifierToKeycode[i * m_keysPerModifier + j] = keycode;
|
||||||
|
|
||||||
|
// save in keycode to modifier
|
||||||
|
m_keycodeToModifier.insert(std::make_pair(keycode, i));
|
||||||
|
|
||||||
|
// modifier is enabled if keycode isn't 0
|
||||||
|
if (keycode != 0)
|
||||||
|
m_modifierMask |= bit;
|
||||||
|
|
||||||
|
// modifier is a toggle if the keysym is a toggle modifier
|
||||||
|
const KeySym keysym = XKeycodeToKeysym(display, keycode, 0);
|
||||||
|
if (isToggleKeysym(keysym)) {
|
||||||
|
m_toggleModifierMask |= bit;
|
||||||
|
|
||||||
|
// note num/caps-lock
|
||||||
|
if (keysym == XK_Num_Lock)
|
||||||
|
m_numLockMask |= bit;
|
||||||
|
if (keysym == XK_Caps_Lock)
|
||||||
|
m_capsLockMask |= bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XFreeModifiermap(keymap);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CXWindowsSecondaryScreen::isToggleKeysym(KeySym key)
|
||||||
|
{
|
||||||
|
switch (key) {
|
||||||
|
case XK_Caps_Lock:
|
||||||
|
case XK_Shift_Lock:
|
||||||
|
case XK_Num_Lock:
|
||||||
|
case XK_Scroll_Lock:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "CXWindowsScreen.h"
|
#include "CXWindowsScreen.h"
|
||||||
#include "ISecondaryScreen.h"
|
#include "ISecondaryScreen.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen {
|
class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen {
|
||||||
public:
|
public:
|
||||||
|
@ -35,13 +36,63 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen
|
||||||
virtual void onCloseDisplay();
|
virtual void onCloseDisplay();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct KeyCodeMask {
|
||||||
|
public:
|
||||||
|
KeyCode keycode;
|
||||||
|
unsigned int keyMask;
|
||||||
|
unsigned int keyMaskMask;
|
||||||
|
};
|
||||||
|
typedef std::pair<KeyCode, Bool> Keystroke;
|
||||||
|
typedef std::vector<Keystroke> Keystrokes;
|
||||||
|
typedef std::vector<KeyCode> KeyCodes;
|
||||||
|
typedef std::map<KeyID, KeyCodeMask> KeyCodeMap;
|
||||||
|
typedef std::map<KeyCode, unsigned int> ModifierMap;
|
||||||
|
|
||||||
void leaveNoLock(Display*);
|
void leaveNoLock(Display*);
|
||||||
KeyCode mapKey(KeyID, KeyModifierMask) const;
|
|
||||||
unsigned int mapButton(ButtonID button) const;
|
unsigned int mapButton(ButtonID button) const;
|
||||||
|
|
||||||
|
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
|
||||||
|
KeyModifierMask, Bool press) const;
|
||||||
|
bool findKeyCode(KeyCode&, unsigned int&,
|
||||||
|
KeyID id, unsigned int) const;
|
||||||
|
unsigned int maskToX(KeyModifierMask) const;
|
||||||
|
|
||||||
|
void updateKeys(Display* display);
|
||||||
|
void updateKeycodeMap(Display* display);
|
||||||
|
void updateModifiers(Display* display);
|
||||||
|
void updateModifierMap(Display* display);
|
||||||
|
static bool isToggleKeysym(KeySym);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CClient* m_client;
|
CClient* m_client;
|
||||||
Window m_window;
|
Window m_window;
|
||||||
|
|
||||||
|
// set entries indicate keys that are pressed. indexed by keycode.
|
||||||
|
bool m_keys[256];
|
||||||
|
|
||||||
|
// current active modifiers (X key masks)
|
||||||
|
unsigned int m_mask;
|
||||||
|
|
||||||
|
// maps key IDs to X keycodes and the X modifier key mask needed
|
||||||
|
// to generate the right keysym
|
||||||
|
KeyCodeMap m_keycodeMap;
|
||||||
|
|
||||||
|
// the modifiers that have keys bound to them
|
||||||
|
unsigned int m_modifierMask;
|
||||||
|
|
||||||
|
// set bits indicate modifiers that toggle (e.g. caps-lock)
|
||||||
|
unsigned int m_toggleModifierMask;
|
||||||
|
|
||||||
|
// masks that indicate which modifier bits are num-lock and caps-lock
|
||||||
|
unsigned int m_numLockMask;
|
||||||
|
unsigned int m_capsLockMask;
|
||||||
|
|
||||||
|
// map X modifier key indices to the key codes bound to them
|
||||||
|
unsigned int m_keysPerModifier;
|
||||||
|
KeyCodes m_modifierToKeycode;
|
||||||
|
|
||||||
|
// maps keycodes to modifier indices
|
||||||
|
ModifierMap m_keycodeToModifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <X11/X.h>
|
#include <X11/X.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
//
|
//
|
||||||
// CXWindowsPrimaryScreen
|
// CXWindowsPrimaryScreen
|
||||||
|
@ -40,10 +41,17 @@ void CXWindowsPrimaryScreen::run()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MappingNotify: {
|
||||||
|
// keyboard mapping changed
|
||||||
|
CDisplayLock display(this);
|
||||||
|
XRefreshKeyboardMapping(&xevent.xmapping);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case KeyPress: {
|
case KeyPress: {
|
||||||
log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||||
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
||||||
const KeyID key = mapKey(xevent.xkey.keycode, mask);
|
const KeyID key = mapKey(&xevent.xkey);
|
||||||
if (key != kKeyNone) {
|
if (key != kKeyNone) {
|
||||||
m_server->onKeyDown(key, mask);
|
m_server->onKeyDown(key, mask);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +63,7 @@ void CXWindowsPrimaryScreen::run()
|
||||||
case KeyRelease: {
|
case KeyRelease: {
|
||||||
log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||||
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
||||||
const KeyID key = mapKey(xevent.xkey.keycode, mask);
|
const KeyID key = mapKey(&xevent.xkey);
|
||||||
if (key != kKeyNone) {
|
if (key != kKeyNone) {
|
||||||
m_server->onKeyUp(key, mask);
|
m_server->onKeyUp(key, mask);
|
||||||
}
|
}
|
||||||
|
@ -406,33 +414,31 @@ KeyModifierMask CXWindowsPrimaryScreen::mapModifier(
|
||||||
{
|
{
|
||||||
// FIXME -- should be configurable
|
// FIXME -- should be configurable
|
||||||
KeyModifierMask mask = 0;
|
KeyModifierMask mask = 0;
|
||||||
if (state & 1)
|
if (state & ShiftMask)
|
||||||
mask |= KeyModifierShift;
|
mask |= KeyModifierShift;
|
||||||
if (state & 2)
|
if (state & LockMask)
|
||||||
mask |= KeyModifierCapsLock;
|
mask |= KeyModifierCapsLock;
|
||||||
if (state & 4)
|
if (state & ControlMask)
|
||||||
mask |= KeyModifierControl;
|
mask |= KeyModifierControl;
|
||||||
if (state & 8)
|
if (state & Mod1Mask)
|
||||||
mask |= KeyModifierAlt;
|
mask |= KeyModifierAlt;
|
||||||
if (state & 16)
|
if (state & Mod2Mask)
|
||||||
mask |= KeyModifierNumLock;
|
mask |= KeyModifierNumLock;
|
||||||
if (state & 32)
|
if (state & Mod4Mask)
|
||||||
mask |= KeyModifierMeta;
|
mask |= KeyModifierMeta;
|
||||||
if (state & 128)
|
if (state & Mod5Mask)
|
||||||
mask |= KeyModifierScrollLock;
|
mask |= KeyModifierScrollLock;
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyID CXWindowsPrimaryScreen::mapKey(
|
KeyID CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const
|
||||||
KeyCode keycode, KeyModifierMask mask) const
|
|
||||||
{
|
{
|
||||||
int index;
|
KeySym keysym;
|
||||||
if (mask & KeyModifierShift)
|
char dummy[1];
|
||||||
index = 1;
|
|
||||||
else
|
|
||||||
index = 0;
|
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
return static_cast<KeyID>(XKeycodeToKeysym(display, keycode, index));
|
XLookupString(event, dummy, 0, &keysym, NULL);
|
||||||
|
return static_cast<KeyID>(keysym);
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonID CXWindowsPrimaryScreen::mapButton(
|
ButtonID CXWindowsPrimaryScreen::mapButton(
|
||||||
|
|
|
@ -36,7 +36,7 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen {
|
||||||
SInt32 xAbsolute, SInt32 yAbsolute);
|
SInt32 xAbsolute, SInt32 yAbsolute);
|
||||||
|
|
||||||
KeyModifierMask mapModifier(unsigned int state) const;
|
KeyModifierMask mapModifier(unsigned int state) const;
|
||||||
KeyID mapKey(KeyCode, KeyModifierMask) const;
|
KeyID mapKey(XKeyEvent*) const;
|
||||||
ButtonID mapButton(unsigned int button) const;
|
ButtonID mapButton(unsigned int button) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// type to hold a key identifier
|
// type to hold a key identifier
|
||||||
typedef UInt32 KeyID;
|
typedef UInt32 KeyID;
|
||||||
|
|
||||||
// type to hold bitmask of key modifiers (i.e. shift keys)
|
// type to hold bitmask of key modifiers (e.g. shift keys)
|
||||||
typedef UInt32 KeyModifierMask;
|
typedef UInt32 KeyModifierMask;
|
||||||
|
|
||||||
// key codes
|
// key codes
|
||||||
|
|
Loading…
Reference in New Issue