Changed windows server to release ctrl and alt keys when it's
sending a key that requires AltGr. That's because AltGr *is* ctrl and alt but AltGr should be seen on clients as mode switch without the ctrl and alt. I can't think of a better way to do this other than to not send modifier keystrokes to the clients at all.
This commit is contained in:
parent
c325b923ea
commit
f27fd7b021
|
@ -485,10 +485,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
|
||||||
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
||||||
|
|
||||||
// process as if it were a key up
|
// process as if it were a key up
|
||||||
|
bool altgr;
|
||||||
KeyModifierMask mask;
|
KeyModifierMask mask;
|
||||||
KeyButton button = static_cast<KeyButton>(
|
KeyButton button = static_cast<KeyButton>(
|
||||||
(lParam & 0x00ff0000u) >> 16);
|
(lParam & 0x00ff0000u) >> 16);
|
||||||
const KeyID key = mapKey(wParam, lParam, &mask);
|
const KeyID key = mapKey(wParam, lParam, &mask, &altgr);
|
||||||
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
||||||
m_receiver->onKeyUp(key, mask, button);
|
m_receiver->onKeyUp(key, mask, button);
|
||||||
updateKey(wParam, false);
|
updateKey(wParam, false);
|
||||||
|
@ -502,10 +503,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
|
||||||
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
||||||
|
|
||||||
// process as if it were a key up
|
// process as if it were a key up
|
||||||
|
bool altgr;
|
||||||
KeyModifierMask mask;
|
KeyModifierMask mask;
|
||||||
KeyButton button = static_cast<KeyButton>(
|
KeyButton button = static_cast<KeyButton>(
|
||||||
(lParam & 0x00ff0000u) >> 16);
|
(lParam & 0x00ff0000u) >> 16);
|
||||||
const KeyID key = mapKey(wParam, lParam, &mask);
|
const KeyID key = mapKey(wParam, lParam, &mask, &altgr);
|
||||||
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
||||||
m_receiver->onKeyUp(key, mask, button);
|
m_receiver->onKeyUp(key, mask, button);
|
||||||
updateKey(wParam, false);
|
updateKey(wParam, false);
|
||||||
|
@ -535,22 +537,111 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// process key normally
|
// process key normally
|
||||||
|
bool altgr;
|
||||||
KeyModifierMask mask;
|
KeyModifierMask mask;
|
||||||
const KeyID key = mapKey(wParam, lParam, &mask);
|
const KeyID key = mapKey(wParam, lParam, &mask, &altgr);
|
||||||
KeyButton button = static_cast<KeyButton>(
|
KeyButton button = static_cast<KeyButton>(
|
||||||
(lParam & 0x00ff0000u) >> 16);
|
(lParam & 0x00ff0000u) >> 16);
|
||||||
if (key != kKeyNone && key != kKeyMultiKey) {
|
if (key != kKeyNone && key != kKeyMultiKey) {
|
||||||
if ((lParam & 0x80000000) == 0) {
|
if ((lParam & 0x80000000) == 0) {
|
||||||
// key press
|
// key press
|
||||||
|
|
||||||
|
// if AltGr required for this key then make sure
|
||||||
|
// the ctrl and alt keys are *not* down on the
|
||||||
|
// client. windows simulates AltGr with ctrl and
|
||||||
|
// alt for some inexplicable reason and clients
|
||||||
|
// will get confused if they see mode switch and
|
||||||
|
// ctrl and alt. we'll also need to put ctrl and
|
||||||
|
// alt back the way there were after we simulate
|
||||||
|
// the key.
|
||||||
|
bool ctrlL = ((m_keys[VK_LCONTROL] & 0x80) != 0);
|
||||||
|
bool ctrlR = ((m_keys[VK_RCONTROL] & 0x80) != 0);
|
||||||
|
bool altL = ((m_keys[VK_LMENU] & 0x80) != 0);
|
||||||
|
bool altR = ((m_keys[VK_RMENU] & 0x80) != 0);
|
||||||
|
if (altgr) {
|
||||||
|
KeyID key;
|
||||||
|
KeyButton button;
|
||||||
|
KeyModifierMask mask2 = (mask &
|
||||||
|
~(KeyModifierControl |
|
||||||
|
KeyModifierAlt |
|
||||||
|
KeyModifierModeSwitch));
|
||||||
|
if (ctrlL) {
|
||||||
|
key = kKeyControl_L;
|
||||||
|
button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyUp(key, mask2, button);
|
||||||
|
}
|
||||||
|
if (ctrlR) {
|
||||||
|
key = kKeyControl_R;
|
||||||
|
button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyUp(key, mask2, button);
|
||||||
|
}
|
||||||
|
if (altL) {
|
||||||
|
key = kKeyAlt_L;
|
||||||
|
button = mapKeyToScanCode(VK_LMENU, VK_MENU);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyUp(key, mask2, button);
|
||||||
|
}
|
||||||
|
if (altR) {
|
||||||
|
key = kKeyAlt_R;
|
||||||
|
button = mapKeyToScanCode(VK_RMENU, VK_MENU);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyUp(key, mask2, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send key
|
||||||
const bool wasDown = ((lParam & 0x40000000) != 0);
|
const bool wasDown = ((lParam & 0x40000000) != 0);
|
||||||
const SInt32 repeat = (SInt32)(lParam & 0xffff);
|
SInt32 repeat = (SInt32)(lParam & 0xffff);
|
||||||
if (repeat >= 2 || wasDown) {
|
if (!wasDown) {
|
||||||
|
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
||||||
|
m_receiver->onKeyDown(key, mask, button);
|
||||||
|
if (repeat > 0) {
|
||||||
|
--repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (repeat >= 1) {
|
||||||
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button));
|
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button));
|
||||||
m_receiver->onKeyRepeat(key, mask, repeat, button);
|
m_receiver->onKeyRepeat(key, mask, repeat, button);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
// restore ctrl and alt state
|
||||||
m_receiver->onKeyDown(key, mask, button);
|
if (altgr) {
|
||||||
|
KeyID key;
|
||||||
|
KeyButton button;
|
||||||
|
KeyModifierMask mask2 = (mask &
|
||||||
|
~(KeyModifierControl |
|
||||||
|
KeyModifierAlt |
|
||||||
|
KeyModifierModeSwitch));
|
||||||
|
if (ctrlL) {
|
||||||
|
key = kKeyControl_L;
|
||||||
|
button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyDown(key, mask2, button);
|
||||||
|
mask2 |= KeyModifierControl;
|
||||||
|
}
|
||||||
|
if (ctrlR) {
|
||||||
|
key = kKeyControl_R;
|
||||||
|
button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyDown(key, mask2, button);
|
||||||
|
mask2 |= KeyModifierControl;
|
||||||
|
}
|
||||||
|
if (altL) {
|
||||||
|
key = kKeyAlt_L;
|
||||||
|
button = mapKeyToScanCode(VK_LMENU, VK_MENU);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyDown(key, mask2, button);
|
||||||
|
mask2 |= KeyModifierAlt;
|
||||||
|
}
|
||||||
|
if (altR) {
|
||||||
|
key = kKeyAlt_R;
|
||||||
|
button = mapKeyToScanCode(VK_RMENU, VK_MENU);
|
||||||
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
||||||
|
m_receiver->onKeyDown(key, mask2, button);
|
||||||
|
mask2 |= KeyModifierAlt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1283,7 +1374,8 @@ KeyID
|
||||||
CMSWindowsPrimaryScreen::mapKey(
|
CMSWindowsPrimaryScreen::mapKey(
|
||||||
WPARAM vkCode,
|
WPARAM vkCode,
|
||||||
LPARAM info,
|
LPARAM info,
|
||||||
KeyModifierMask* maskOut)
|
KeyModifierMask* maskOut,
|
||||||
|
bool* altgr)
|
||||||
{
|
{
|
||||||
// note: known microsoft bugs
|
// note: known microsoft bugs
|
||||||
// Q72583 -- MapVirtualKey() maps keypad keys incorrectly
|
// Q72583 -- MapVirtualKey() maps keypad keys incorrectly
|
||||||
|
@ -1292,66 +1384,18 @@ CMSWindowsPrimaryScreen::mapKey(
|
||||||
// SEPARATOR, MULTIPLY, SUBTRACT, ADD
|
// SEPARATOR, MULTIPLY, SUBTRACT, ADD
|
||||||
|
|
||||||
assert(maskOut != NULL);
|
assert(maskOut != NULL);
|
||||||
|
assert(altgr != NULL);
|
||||||
// map modifier key
|
|
||||||
KeyModifierMask mask = 0;
|
|
||||||
if (((m_keys[VK_LSHIFT] |
|
|
||||||
m_keys[VK_RSHIFT] |
|
|
||||||
m_keys[VK_SHIFT]) & 0x80) != 0) {
|
|
||||||
mask |= KeyModifierShift;
|
|
||||||
}
|
|
||||||
if (((m_keys[VK_LCONTROL] |
|
|
||||||
m_keys[VK_RCONTROL] |
|
|
||||||
m_keys[VK_CONTROL]) & 0x80) != 0) {
|
|
||||||
mask |= KeyModifierControl;
|
|
||||||
}
|
|
||||||
if ((m_keys[VK_RMENU] & 0x80) != 0) {
|
|
||||||
// right alt => AltGr on windows
|
|
||||||
mask |= KeyModifierModeSwitch;
|
|
||||||
}
|
|
||||||
else if (((m_keys[VK_LMENU] |
|
|
||||||
m_keys[VK_MENU]) & 0x80) != 0) {
|
|
||||||
mask |= KeyModifierAlt;
|
|
||||||
}
|
|
||||||
if (((m_keys[VK_LWIN] |
|
|
||||||
m_keys[VK_RWIN]) & 0x80) != 0) {
|
|
||||||
mask |= KeyModifierSuper;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
// ctrl+alt => AltGr on windows
|
|
||||||
/* don't convert ctrl+alt to mode switch. if we do that then we can
|
|
||||||
* never send ctrl+alt+[key] from windows to some platform that
|
|
||||||
* doesn't treat ctrl+alt as mode switch (i.e. all other platforms).
|
|
||||||
* instead, let windows clients automatically treat ctrl+alt as
|
|
||||||
* AltGr and let other clients use ctrl+alt as is. the right alt
|
|
||||||
* key serves as a mode switch key.
|
|
||||||
if ((mask & (KeyModifierControl | KeyModifierAlt)) ==
|
|
||||||
(KeyModifierControl | KeyModifierAlt)) {
|
|
||||||
mask |= KeyModifierModeSwitch;
|
|
||||||
mask &= ~(KeyModifierControl | KeyModifierAlt);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
*maskOut = mask;
|
|
||||||
LOG((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask));
|
|
||||||
|
|
||||||
// get the scan code and the extended keyboard flag
|
// get the scan code and the extended keyboard flag
|
||||||
UINT scanCode = static_cast<UINT>((info & 0x00ff0000u) >> 16);
|
UINT scanCode = static_cast<UINT>((info & 0x00ff0000u) >> 16);
|
||||||
int extended = ((info & 0x01000000) == 0) ? 0 : 1;
|
int extended = ((info & 0x01000000) == 0) ? 0 : 1;
|
||||||
LOG((CLOG_DEBUG1 "key vk=%d ext=%d scan=%d", vkCode, extended, scanCode));
|
LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode));
|
||||||
|
|
||||||
// handle some keys via table lookup
|
// handle some keys via table lookup
|
||||||
|
char c = 0;
|
||||||
KeyID id = g_virtualKey[vkCode][extended];
|
KeyID id = g_virtualKey[vkCode][extended];
|
||||||
if (id != kKeyNone) {
|
if (id == kKeyNone) {
|
||||||
return id;
|
// not in table
|
||||||
}
|
|
||||||
|
|
||||||
// save the control state then clear it. ToAscii() maps ctrl+letter
|
// save the control state then clear it. ToAscii() maps ctrl+letter
|
||||||
// to the corresponding control code and ctrl+backspace to delete.
|
// to the corresponding control code and ctrl+backspace to delete.
|
||||||
|
@ -1363,7 +1407,7 @@ CMSWindowsPrimaryScreen::mapKey(
|
||||||
BYTE control = m_keys[VK_CONTROL];
|
BYTE control = m_keys[VK_CONTROL];
|
||||||
BYTE lMenu = m_keys[VK_LMENU];
|
BYTE lMenu = m_keys[VK_LMENU];
|
||||||
BYTE menu = m_keys[VK_MENU];
|
BYTE menu = m_keys[VK_MENU];
|
||||||
if ((mask & KeyModifierModeSwitch) == 0) {
|
if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
|
||||||
m_keys[VK_LCONTROL] = 0;
|
m_keys[VK_LCONTROL] = 0;
|
||||||
m_keys[VK_RCONTROL] = 0;
|
m_keys[VK_RCONTROL] = 0;
|
||||||
m_keys[VK_CONTROL] = 0;
|
m_keys[VK_CONTROL] = 0;
|
||||||
|
@ -1389,24 +1433,30 @@ CMSWindowsPrimaryScreen::mapKey(
|
||||||
// if result is less than zero then it was a dead key. leave it
|
// if result is less than zero then it was a dead key. leave it
|
||||||
// there.
|
// there.
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
return kKeyMultiKey;
|
id = kKeyMultiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if result is 1 then the key was succesfully converted
|
// if result is 1 then the key was succesfully converted
|
||||||
else if (result == 1) {
|
else if (result == 1) {
|
||||||
|
c = static_cast<char>(ascii & 0xff);
|
||||||
if (ascii >= 0x80) {
|
if (ascii >= 0x80) {
|
||||||
// character is not really ASCII. instead it's some
|
// character is not really ASCII. instead it's some
|
||||||
// character in the current ANSI code page. try to
|
// character in the current ANSI code page. try to
|
||||||
// convert that to a Unicode character. if we fail
|
// convert that to a Unicode character. if we fail
|
||||||
// then use the single byte character as is.
|
// then use the single byte character as is.
|
||||||
char src = static_cast<char>(ascii & 0xff);
|
char src = c;
|
||||||
wchar_t unicode;
|
wchar_t unicode;
|
||||||
if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
|
if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
|
||||||
&src, 1, &unicode, 1) > 0) {
|
&src, 1, &unicode, 1) > 0) {
|
||||||
return static_cast<KeyID>(unicode);
|
id = static_cast<KeyID>(unicode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
id = static_cast<KeyID>(ascii & 0x00ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return static_cast<KeyID>(ascii & 0x00ff);
|
else {
|
||||||
|
id = static_cast<KeyID>(ascii & 0x00ff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if result is 2 then a previous dead key could not be composed.
|
// if result is 2 then a previous dead key could not be composed.
|
||||||
|
@ -1447,11 +1497,64 @@ CMSWindowsPrimaryScreen::mapKey(
|
||||||
|
|
||||||
// put it back
|
// put it back
|
||||||
ToAscii(vkCode, scanCode, keys, &ascii, 0);
|
ToAscii(vkCode, scanCode, keys, &ascii, 0);
|
||||||
return kKeyMultiKey;
|
id = kKeyMultiKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cannot convert key
|
// set mask
|
||||||
return kKeyNone;
|
*altgr = false;
|
||||||
|
if (id != kKeyNone && id != kKeyMultiKey && c != 0) {
|
||||||
|
// note if key requires AltGr
|
||||||
|
SHORT virtualKeyAndModifierState = VkKeyScan(c);
|
||||||
|
BYTE modifierState = HIBYTE(virtualKeyAndModifierState);
|
||||||
|
if ((modifierState & 6) == 6) {
|
||||||
|
// key requires ctrl and alt == AltGr
|
||||||
|
*altgr = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// map modifier key
|
||||||
|
KeyModifierMask mask = 0;
|
||||||
|
if (((m_keys[VK_LSHIFT] |
|
||||||
|
m_keys[VK_RSHIFT] |
|
||||||
|
m_keys[VK_SHIFT]) & 0x80) != 0) {
|
||||||
|
mask |= KeyModifierShift;
|
||||||
|
}
|
||||||
|
if (*altgr) {
|
||||||
|
mask |= KeyModifierModeSwitch;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (((m_keys[VK_LCONTROL] |
|
||||||
|
m_keys[VK_RCONTROL] |
|
||||||
|
m_keys[VK_CONTROL]) & 0x80) != 0) {
|
||||||
|
mask |= KeyModifierControl;
|
||||||
|
}
|
||||||
|
if (((m_keys[VK_LMENU] |
|
||||||
|
m_keys[VK_RMENU] |
|
||||||
|
m_keys[VK_MENU]) & 0x80) != 0) {
|
||||||
|
mask |= KeyModifierAlt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (((m_keys[VK_LWIN] |
|
||||||
|
m_keys[VK_RWIN]) & 0x80) != 0) {
|
||||||
|
mask |= KeyModifierSuper;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
*maskOut = mask;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// don't care
|
||||||
|
*maskOut = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonID
|
ButtonID
|
||||||
|
@ -1679,3 +1782,13 @@ CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyButton
|
||||||
|
CMSWindowsPrimaryScreen::mapKeyToScanCode(UINT vk1, UINT vk2) const
|
||||||
|
{
|
||||||
|
KeyButton button = static_cast<KeyButton>(MapVirtualKey(vk1, 0));
|
||||||
|
if (button == 0) {
|
||||||
|
button = static_cast<KeyButton>(MapVirtualKey(vk2, 0));
|
||||||
|
}
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
|
@ -87,10 +87,11 @@ private:
|
||||||
|
|
||||||
// key and button queries
|
// key and button queries
|
||||||
KeyID mapKey(WPARAM keycode, LPARAM info,
|
KeyID mapKey(WPARAM keycode, LPARAM info,
|
||||||
KeyModifierMask* maskOut);
|
KeyModifierMask* maskOut, bool* altgr);
|
||||||
ButtonID mapButton(WPARAM msg, LPARAM button) const;
|
ButtonID mapButton(WPARAM msg, LPARAM button) const;
|
||||||
void updateKey(UINT vkCode, bool press);
|
void updateKey(UINT vkCode, bool press);
|
||||||
bool isModifier(UINT vkCode) const;
|
bool isModifier(UINT vkCode) const;
|
||||||
|
KeyButton mapKeyToScanCode(UINT vk1, UINT vk2) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IPrimaryScreenReceiver* m_receiver;
|
IPrimaryScreenReceiver* m_receiver;
|
||||||
|
|
Loading…
Reference in New Issue