checkpoint. now sending toggle modifier state when entering

a screen.  this allows the secondary screen to set it's
modifier state to match the primary screen's state.  this is
not strictly necessary since each keystroke should adjust the
modifier state as needed to get the right result.
This commit is contained in:
crs 2002-04-30 17:48:11 +00:00
parent 56877bcc7d
commit b279c80608
17 changed files with 224 additions and 32 deletions

View File

@ -341,13 +341,14 @@ void CClient::closeSecondaryScreen()
void CClient::onEnter()
{
SInt16 x, y;
UInt16 mask;
{
CLock lock(&m_mutex);
CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum);
CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum, &mask);
m_active = true;
}
log((CLOG_DEBUG1 "recv enter, %d,%d %d", x, y, m_seqNum));
m_screen->enter(x, y);
log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask));
m_screen->enter(x, y, static_cast<KeyModifierMask>(mask));
}
void CClient::onLeave()

View File

@ -109,11 +109,12 @@ void CMSWindowsSecondaryScreen::close()
m_client = NULL;
}
void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y)
void CMSWindowsSecondaryScreen::enter(
SInt32 x, SInt32 y, KeyModifierMask mask)
{
assert(m_window != NULL);
log((CLOG_INFO "entering screen at %d,%d", x, y));
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
// warp to requested location
SInt32 w, h;
@ -130,6 +131,17 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y)
// update our keyboard state to reflect the local state
updateKeys();
updateModifiers();
// toggle modifiers that don't match the desired state
if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) {
toggleKey(VK_CAPITAL, KeyModifierCapsLock);
}
if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) {
toggleKey(VK_NUMLOCK, KeyModifierNumLock);
}
if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) {
toggleKey(VK_SCROLL, KeyModifierScrollLock);
}
}
void CMSWindowsSecondaryScreen::leave()
@ -1184,3 +1196,16 @@ void CMSWindowsSecondaryScreen::updateModifiers()
if ((m_keys[VK_SCROLL] & 0x01) != 0)
m_mask |= KeyModifierScrollLock;
}
void CMSWindowsSecondaryScreen::toggleKey(
UINT virtualKey, KeyModifierMask mask)
{
// send key events to simulate a press and release
const UINT code = MapVirtualKey(virtualKey, 0);
keybd_event(virtualKey, code, 0, 0);
keybd_event(virtualKey, code, KEYEVENTF_KEYUP, 0);
// toggle shadow state
m_mask ^= mask;
m_keys[virtualKey] ^= 0x01;
}

View File

@ -16,7 +16,8 @@ public:
virtual void stop();
virtual void open(CClient*);
virtual void close();
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute);
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute,
KeyModifierMask mask);
virtual void leave();
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
@ -48,6 +49,7 @@ private:
void updateKeys();
void updateModifiers();
void toggleKey(UINT virtualKey, KeyModifierMask mask);
private:
CClient* m_client;

View File

@ -177,7 +177,8 @@ void CXWindowsSecondaryScreen::close()
m_client = NULL;
}
void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y)
void CXWindowsSecondaryScreen::enter(
SInt32 x, SInt32 y, KeyModifierMask mask)
{
assert(m_window != None);
@ -193,6 +194,19 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y)
// update our keyboard state to reflect the local state
updateKeys(display);
updateModifiers(display);
// toggle modifiers that don't match the desired state
unsigned int xMask = maskToX(mask);
if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) {
toggleKey(display, XK_Caps_Lock, m_capsLockMask);
}
if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) {
toggleKey(display, XK_Num_Lock, m_numLockMask);
}
if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) {
toggleKey(display, XK_Scroll_Lock, m_scrollLockMask);
}
XSync(display, False);
}
void CXWindowsSecondaryScreen::leave()
@ -461,6 +475,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
const KeyCode modifierKey = modifierKeys[0];
keys.push_back(std::make_pair(modifierKey, True));
if ((bit & m_toggleModifierMask) != 0) {
if (bit != m_capsLockMask || !m_capsLockHalfDuplex) {
keys.push_back(std::make_pair(modifierKey, False));
undo.push_back(std::make_pair(modifierKey, False));
undo.push_back(std::make_pair(modifierKey, True));
@ -469,6 +484,10 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
undo.push_back(std::make_pair(modifierKey, False));
}
}
else {
undo.push_back(std::make_pair(modifierKey, False));
}
}
else {
// modifier is active but should not be. if the
@ -476,13 +495,19 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// press/release, otherwise deactivate it with a
// release. we must check each keycode for the
// modifier if not a toggle.
if (bit & m_toggleModifierMask) {
if ((bit & m_toggleModifierMask) != 0) {
const KeyCode modifierKey = modifierKeys[0];
if (bit != m_capsLockMask || !m_capsLockHalfDuplex) {
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 {
keys.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];
@ -601,11 +626,11 @@ unsigned int CXWindowsSecondaryScreen::maskToX(
if (inMask & KeyModifierMeta)
outMask |= Mod4Mask;
if (inMask & KeyModifierCapsLock)
outMask |= LockMask;
outMask |= m_capsLockMask;
if (inMask & KeyModifierNumLock)
outMask |= Mod2Mask;
outMask |= m_numLockMask;
if (inMask & KeyModifierScrollLock)
outMask |= Mod5Mask;
outMask |= m_scrollLockMask;
return outMask;
}
@ -712,6 +737,7 @@ void CXWindowsSecondaryScreen::updateModifierMap(
m_toggleModifierMask = 0;
m_numLockMask = 0;
m_capsLockMask = 0;
m_scrollLockMask = 0;
m_keysPerModifier = keymap->max_keypermod;
m_modifierToKeycode.clear();
m_modifierToKeycode.resize(8 * m_keysPerModifier);
@ -738,17 +764,47 @@ void CXWindowsSecondaryScreen::updateModifierMap(
m_toggleModifierMask |= bit;
// note num/caps-lock
if (keysym == XK_Num_Lock)
if (keysym == XK_Num_Lock) {
m_numLockMask |= bit;
if (keysym == XK_Caps_Lock)
}
else if (keysym == XK_Caps_Lock) {
m_capsLockMask |= bit;
}
else if (keysym == XK_Scroll_Lock) {
m_scrollLockMask |= bit;
}
}
}
}
XFreeModifiermap(keymap);
}
void CXWindowsSecondaryScreen::toggleKey(
Display* display,
KeySym keysym, unsigned int mask)
{
// lookup the keycode
KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym);
if (index == m_keycodeMap.end())
return;
KeyCode keycode = index->second.keycode;
// toggle the key
if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) {
// "half-duplex" toggle
XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime);
}
else {
// normal toggle
XTestFakeKeyEvent(display, keycode, True, CurrentTime);
XTestFakeKeyEvent(display, keycode, False, CurrentTime);
}
// toggle shadow state
m_mask ^= mask;
}
bool CXWindowsSecondaryScreen::isToggleKeysym(KeySym key)
{
switch (key) {

View File

@ -15,7 +15,8 @@ public:
virtual void stop();
virtual void open(CClient*);
virtual void close();
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute);
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute,
KeyModifierMask mask);
virtual void leave();
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
@ -62,6 +63,7 @@ private:
void updateKeycodeMap(Display* display);
void updateModifiers(Display* display);
void updateModifierMap(Display* display);
void toggleKey(Display*, KeySym, unsigned int mask);
static bool isToggleKeysym(KeySym);
private:
@ -88,9 +90,10 @@ private:
// 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
// masks that indicate which modifier bits are for toggle keys
unsigned int m_numLockMask;
unsigned int m_capsLockMask;
unsigned int m_scrollLockMask;
// map X modifier key indices to the key codes bound to them
unsigned int m_keysPerModifier;

View File

@ -252,6 +252,18 @@ void CMSWindowsPrimaryScreen::getClipboard(
CClipboard::copy(dst, &src);
}
KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const
{
KeyModifierMask mask;
if ((m_keys[VK_CAPITAL] & 0x01) != 0)
mask |= KeyModifierCapsLock;
if ((m_keys[VK_NUMLOCK] & 0x01) != 0)
mask |= KeyModifierNumLock;
if ((m_keys[VK_SCROLL] & 0x01) != 0)
mask |= KeyModifierScrollLock;
return mask;
}
#include "resource.h" // FIXME
void CMSWindowsPrimaryScreen::onOpenDisplay()

View File

@ -26,6 +26,7 @@ public:
virtual void getSize(SInt32* width, SInt32* height) const;
virtual SInt32 getJumpZoneSize() const;
virtual void getClipboard(ClipboardID, IClipboard*) const;
virtual KeyModifierMask getToggleMask() const;
protected:
// CMSWindowsScreen overrides

View File

@ -537,7 +537,8 @@ void CServer::switchScreen(CScreenInfo* dst,
m_primary->enter(x, y);
}
else {
m_active->m_protocol->sendEnter(x, y, m_seqNum);
m_active->m_protocol->sendEnter(x, y, m_seqNum,
m_primary->getToggleMask());
}
// send the clipboard data to new active screen

View File

@ -31,7 +31,8 @@ public:
virtual void run() = 0;
virtual void queryInfo() = 0;
virtual void sendClose() = 0;
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0;
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask) = 0;
virtual void sendLeave() = 0;
virtual void sendClipboard(ClipboardID, const CString&) = 0;
virtual void sendGrabClipboard(ClipboardID) = 0;

View File

@ -91,10 +91,12 @@ void CServerProtocol1_0::sendClose()
}
void CServerProtocol1_0::sendEnter(
SInt32 xAbs, SInt32 yAbs, UInt32 seqNum)
SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask)
{
log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d", getClient().c_str(), xAbs, yAbs, seqNum));
CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum);
log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask));
CProtocolUtil::writef(getOutputStream(), kMsgCEnter,
xAbs, yAbs, seqNum, mask);
}
void CServerProtocol1_0::sendLeave()

View File

@ -16,7 +16,8 @@ public:
virtual void run();
virtual void queryInfo();
virtual void sendClose();
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum);
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask);
virtual void sendLeave();
virtual void sendClipboard(ClipboardID, const CString&);
virtual void sendGrabClipboard(ClipboardID);

View File

@ -47,6 +47,7 @@ void CXWindowsPrimaryScreen::run()
// keyboard mapping changed
CDisplayLock display(this);
XRefreshKeyboardMapping(&xevent.xmapping);
updateModifierMap(display);
break;
}
@ -219,6 +220,12 @@ void CXWindowsPrimaryScreen::open(CServer* server)
// FIXME -- may have to get these from some database
m_capsLockHalfDuplex = false;
// m_capsLockHalfDuplex = true;
// update key state
{
CDisplayLock display(this);
updateModifierMap(display);
}
}
void CXWindowsPrimaryScreen::close()
@ -365,6 +372,31 @@ void CXWindowsPrimaryScreen::getClipboard(
getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window));
}
KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const
{
CDisplayLock display(this);
// query the pointer to get the keyboard state
// FIXME -- is there a better way to do this?
Window root, window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state;
if (!XQueryPointer(display, m_window, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state))
return 0;
// convert to KeyModifierMask
KeyModifierMask mask;
if (state & m_numLockMask)
mask |= KeyModifierNumLock;
if (state & m_capsLockMask)
mask |= KeyModifierCapsLock;
if (state & m_scrollLockMask)
mask |= KeyModifierScrollLock;
return mask;
}
void CXWindowsPrimaryScreen::onOpenDisplay()
{
assert(m_window == None);
@ -483,3 +515,38 @@ ButtonID CXWindowsPrimaryScreen::mapButton(
else
return kButtonNone;
}
void CXWindowsPrimaryScreen::updateModifierMap(
Display* display)
{
// get modifier map from server
XModifierKeymap* keymap = XGetModifierMapping(display);
// initialize
m_numLockMask = 0;
m_capsLockMask = 0;
m_scrollLockMask = 0;
// set keycodes and masks
for (unsigned int i = 0; i < 8; ++i) {
const unsigned int bit = (1 << i);
for (int j = 0; j < keymap->max_keypermod; ++j) {
KeyCode keycode = keymap->modifiermap[i *
keymap->max_keypermod + j];
// note toggle modifier bits
const KeySym keysym = XKeycodeToKeysym(display, keycode, 0);
if (keysym == XK_Num_Lock) {
m_numLockMask |= bit;
}
else if (keysym == XK_Caps_Lock) {
m_capsLockMask |= bit;
}
else if (keysym == XK_Scroll_Lock) {
m_scrollLockMask |= bit;
}
}
}
XFreeModifiermap(keymap);
}

View File

@ -24,6 +24,7 @@ public:
virtual void getSize(SInt32* width, SInt32* height) const;
virtual SInt32 getJumpZoneSize() const;
virtual void getClipboard(ClipboardID, IClipboard*) const;
virtual KeyModifierMask getToggleMask() const;
protected:
// CXWindowsScreen overrides
@ -40,12 +41,21 @@ private:
KeyID mapKey(XKeyEvent*) const;
ButtonID mapButton(unsigned int button) const;
void updateModifierMap(Display* display);
private:
CServer* m_server;
bool m_active;
Window m_window;
// note if caps lock key toggles on up/down (false) or on
// transition (true)
bool m_capsLockHalfDuplex;
// masks that indicate which modifier bits are for toggle keys
unsigned int m_numLockMask;
unsigned int m_capsLockMask;
unsigned int m_scrollLockMask;
};
#endif

View File

@ -79,6 +79,11 @@ public:
// and should avoid setting the clipboard object if the screen's
// clipboard hasn't changed.
virtual void getClipboard(ClipboardID, IClipboard*) const = 0;
// get the primary screen's current toggle modifier key state.
// the returned mask should have the corresponding bit set for
// each toggle key that is active.
virtual KeyModifierMask getToggleMask() const = 0;
};
#endif

View File

@ -34,7 +34,8 @@ public:
// called when the user navigates to the secondary screen. warp
// the cursor to the given coordinates and unhide it. prepare to
// simulate input events.
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute,
KeyModifierMask mask) = 0;
// called when the user navigates off the secondary screen. clean
// up input event simulation and hide the cursor.

View File

@ -24,7 +24,8 @@ public:
// send various messages to client
virtual void sendClose() = 0;
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0;
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask) = 0;
virtual void sendLeave() = 0;
virtual void sendClipboard(ClipboardID, const CString&) = 0;
virtual void sendGrabClipboard(ClipboardID) = 0;

View File

@ -24,8 +24,11 @@ static const char kMsgCClose[] = "CBYE";
// entering screen at screen position $1 = x, $2 = y. x,y are
// absolute screen coordinates. $3 = sequence number, which is
// used to order messages between screens. the secondary screen
// must return this number with some messages.
static const char kMsgCEnter[] = "CINN%2i%2i%4i";
// must return this number with some messages. $4 = modifier key
// mask. this will have bits set for each toggle modifier key
// that is activated on entry to the screen. the secondary screen
// should adjust its toggle modifiers to reflect that state.
static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i";
// leave screen: primary -> secondary
// leaving screen. the secondary screen should send clipboard