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() void CClient::onEnter()
{ {
SInt16 x, y; SInt16 x, y;
UInt16 mask;
{ {
CLock lock(&m_mutex); 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; m_active = true;
} }
log((CLOG_DEBUG1 "recv enter, %d,%d %d", x, y, m_seqNum)); log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask));
m_screen->enter(x, y); m_screen->enter(x, y, static_cast<KeyModifierMask>(mask));
} }
void CClient::onLeave() void CClient::onLeave()

View File

@ -109,11 +109,12 @@ void CMSWindowsSecondaryScreen::close()
m_client = NULL; m_client = NULL;
} }
void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) void CMSWindowsSecondaryScreen::enter(
SInt32 x, SInt32 y, KeyModifierMask mask)
{ {
assert(m_window != NULL); 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 // warp to requested location
SInt32 w, h; SInt32 w, h;
@ -130,6 +131,17 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y)
// update our keyboard state to reflect the local state // update our keyboard state to reflect the local state
updateKeys(); updateKeys();
updateModifiers(); 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() void CMSWindowsSecondaryScreen::leave()
@ -1184,3 +1196,16 @@ void CMSWindowsSecondaryScreen::updateModifiers()
if ((m_keys[VK_SCROLL] & 0x01) != 0) if ((m_keys[VK_SCROLL] & 0x01) != 0)
m_mask |= KeyModifierScrollLock; 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 stop();
virtual void open(CClient*); virtual void open(CClient*);
virtual void close(); virtual void close();
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute,
KeyModifierMask mask);
virtual void leave(); virtual void leave();
virtual void keyDown(KeyID, KeyModifierMask); virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
@ -48,6 +49,7 @@ private:
void updateKeys(); void updateKeys();
void updateModifiers(); void updateModifiers();
void toggleKey(UINT virtualKey, KeyModifierMask mask);
private: private:
CClient* m_client; CClient* m_client;

View File

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

View File

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

View File

@ -252,6 +252,18 @@ void CMSWindowsPrimaryScreen::getClipboard(
CClipboard::copy(dst, &src); 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 #include "resource.h" // FIXME
void CMSWindowsPrimaryScreen::onOpenDisplay() void CMSWindowsPrimaryScreen::onOpenDisplay()

View File

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

View File

@ -537,7 +537,8 @@ void CServer::switchScreen(CScreenInfo* dst,
m_primary->enter(x, y); m_primary->enter(x, y);
} }
else { 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 // send the clipboard data to new active screen

View File

@ -31,7 +31,8 @@ public:
virtual void run() = 0; virtual void run() = 0;
virtual void queryInfo() = 0; virtual void queryInfo() = 0;
virtual void sendClose() = 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 sendLeave() = 0;
virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0;
virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendGrabClipboard(ClipboardID) = 0;

View File

@ -91,10 +91,12 @@ void CServerProtocol1_0::sendClose()
} }
void CServerProtocol1_0::sendEnter( 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)); 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); CProtocolUtil::writef(getOutputStream(), kMsgCEnter,
xAbs, yAbs, seqNum, mask);
} }
void CServerProtocol1_0::sendLeave() void CServerProtocol1_0::sendLeave()

View File

@ -16,7 +16,8 @@ public:
virtual void run(); virtual void run();
virtual void queryInfo(); virtual void queryInfo();
virtual void sendClose(); 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 sendLeave();
virtual void sendClipboard(ClipboardID, const CString&); virtual void sendClipboard(ClipboardID, const CString&);
virtual void sendGrabClipboard(ClipboardID); virtual void sendGrabClipboard(ClipboardID);

View File

@ -47,6 +47,7 @@ void CXWindowsPrimaryScreen::run()
// keyboard mapping changed // keyboard mapping changed
CDisplayLock display(this); CDisplayLock display(this);
XRefreshKeyboardMapping(&xevent.xmapping); XRefreshKeyboardMapping(&xevent.xmapping);
updateModifierMap(display);
break; break;
} }
@ -219,6 +220,12 @@ void CXWindowsPrimaryScreen::open(CServer* server)
// FIXME -- may have to get these from some database // FIXME -- may have to get these from some database
m_capsLockHalfDuplex = false; m_capsLockHalfDuplex = false;
// m_capsLockHalfDuplex = true; // m_capsLockHalfDuplex = true;
// update key state
{
CDisplayLock display(this);
updateModifierMap(display);
}
} }
void CXWindowsPrimaryScreen::close() void CXWindowsPrimaryScreen::close()
@ -365,6 +372,31 @@ void CXWindowsPrimaryScreen::getClipboard(
getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); 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() void CXWindowsPrimaryScreen::onOpenDisplay()
{ {
assert(m_window == None); assert(m_window == None);
@ -483,3 +515,38 @@ ButtonID CXWindowsPrimaryScreen::mapButton(
else else
return kButtonNone; 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 void getSize(SInt32* width, SInt32* height) const;
virtual SInt32 getJumpZoneSize() const; virtual SInt32 getJumpZoneSize() const;
virtual void getClipboard(ClipboardID, IClipboard*) const; virtual void getClipboard(ClipboardID, IClipboard*) const;
virtual KeyModifierMask getToggleMask() const;
protected: protected:
// CXWindowsScreen overrides // CXWindowsScreen overrides
@ -40,12 +41,21 @@ private:
KeyID mapKey(XKeyEvent*) const; KeyID mapKey(XKeyEvent*) const;
ButtonID mapButton(unsigned int button) const; ButtonID mapButton(unsigned int button) const;
void updateModifierMap(Display* display);
private: private:
CServer* m_server; CServer* m_server;
bool m_active; bool m_active;
Window m_window; Window m_window;
// note if caps lock key toggles on up/down (false) or on
// transition (true)
bool m_capsLockHalfDuplex; 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 #endif

View File

@ -79,6 +79,11 @@ public:
// and should avoid setting the clipboard object if the screen's // and should avoid setting the clipboard object if the screen's
// clipboard hasn't changed. // clipboard hasn't changed.
virtual void getClipboard(ClipboardID, IClipboard*) const = 0; 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 #endif

View File

@ -34,7 +34,8 @@ public:
// called when the user navigates to the secondary screen. warp // called when the user navigates to the secondary screen. warp
// the cursor to the given coordinates and unhide it. prepare to // the cursor to the given coordinates and unhide it. prepare to
// simulate input events. // 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 // called when the user navigates off the secondary screen. clean
// up input event simulation and hide the cursor. // up input event simulation and hide the cursor.

View File

@ -24,7 +24,8 @@ public:
// send various messages to client // send various messages to client
virtual void sendClose() = 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 sendLeave() = 0;
virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0;
virtual void sendGrabClipboard(ClipboardID) = 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 // entering screen at screen position $1 = x, $2 = y. x,y are
// absolute screen coordinates. $3 = sequence number, which is // absolute screen coordinates. $3 = sequence number, which is
// used to order messages between screens. the secondary screen // used to order messages between screens. the secondary screen
// must return this number with some messages. // must return this number with some messages. $4 = modifier key
static const char kMsgCEnter[] = "CINN%2i%2i%4i"; // 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 // leave screen: primary -> secondary
// leaving screen. the secondary screen should send clipboard // leaving screen. the secondary screen should send clipboard