"Send pointer (mouse) events to the server.", true);
static BoolParameter sendKeyEvents("SendKeyEvents",
"Send key presses (and releases) to the server.", true);
+static BoolParameter sendSysKeys("SendSysKeys",
+ "Send system keys (Alt combinations) to the server.", true);
static BoolParameter clientCutText("ClientCutText",
"Send clipboard changes to the server.", true);
CViewOptions::CViewOptions()
: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize),
autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen),
-shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents),
+shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents), sendSysKeys(::sendSysKeys),
preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText),
protocol3_3(::protocol3_3), acceptBell(::acceptBell), lowColourLevel(::lowColourLevel),
pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData()),
sendPtrEvents = atoi(value.buf);
} else if (stricmp(name.buf, "SendKeyEvents") == 0) {
sendKeyEvents = atoi(value.buf);
+ } else if (stricmp(name.buf, "SendSysKeys") == 0) {
+ sendSysKeys = atoi(value.buf);
} else if (stricmp(name.buf, "SendCutText") == 0) {
clientCutText = atoi(value.buf);
} else if (stricmp(name.buf, "AcceptCutText") == 0) {
fprintf(f, "Shared=%d\n", (int)shared);
fprintf(f, "SendPtrEvents=%d\n", (int)sendPtrEvents);
fprintf(f, "SendKeyEvents=%d\n", (int)sendKeyEvents);
+ fprintf(f, "SendSysKeys=%d\n", (int)sendSysKeys);
fprintf(f, "SendCutText=%d\n", (int)clientCutText);
fprintf(f, "AcceptCutText=%d\n", (int)serverCutText);
fprintf(f, "Emulate3=%d\n", (int)emulate3);
key.setBool(_T("Shared"), shared);
key.setBool(_T("SendPointerEvents"), sendPtrEvents);
key.setBool(_T("SendKeyEvents"), sendKeyEvents);
+ key.setBool(_T("SendSysKeys"), sendSysKeys);
key.setBool(_T("ClientCutText"), clientCutText);
key.setBool(_T("ServerCutText"), serverCutText);
key.setBool(_T("Protocol3.3"), protocol3_3);
shared = o.shared;
sendPtrEvents = o.sendPtrEvents;
sendKeyEvents = o.sendKeyEvents;
+ sendSysKeys = o.sendSysKeys;
clientCutText = o.clientCutText;
serverCutText = o.serverCutText;
emulate3 = o.emulate3;
bool shared;
bool sendPtrEvents;
bool sendKeyEvents;
+ bool sendSysKeys;
bool clientCutText;
bool serverCutText;
bool emulate3;
virtual void initDialog() {
setItemChecked(IDC_SEND_POINTER, dlg->options.sendPtrEvents);
setItemChecked(IDC_SEND_KEYS, dlg->options.sendKeyEvents);
+ setItemChecked(IDC_SEND_SYSKEYS, dlg->options.sendSysKeys);
setItemChecked(IDC_CLIENT_CUTTEXT, dlg->options.clientCutText);
setItemChecked(IDC_SERVER_CUTTEXT, dlg->options.serverCutText);
setItemChecked(IDC_EMULATE3, dlg->options.emulate3);
virtual bool onOk() {
dlg->options.sendPtrEvents = isItemChecked(IDC_SEND_POINTER);
dlg->options.sendKeyEvents = isItemChecked(IDC_SEND_KEYS);
+ dlg->options.sendSysKeys = isItemChecked(IDC_SEND_SYSKEYS);
dlg->options.clientCutText = isItemChecked(IDC_CLIENT_CUTTEXT);
dlg->options.serverCutText = isItemChecked(IDC_SERVER_CUTTEXT);
dlg->options.emulate3 = isItemChecked(IDC_EMULATE3);
const int TIMER_POINTER_INTERVAL = 2;
const int TIMER_POINTER_3BUTTON = 3;
+const int HOTKEY_ALTTAB = 0;
+
IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
"pixel data - a debugging feature", 0);
bumpScrollTimer.setHWND(getHandle());
bumpScrollTimer.setId(TIMER_BUMPSCROLL);
+ // Grab AltTab
+ setAltTabGrab(options.sendSysKeys);
+
// Hook the clipboard
clipboard.setNotifier(this);
// - Inputs
options.sendPtrEvents = opt.sendPtrEvents;
options.sendKeyEvents = opt.sendKeyEvents;
+ options.sendSysKeys = opt.sendSysKeys;
+ setAltTabGrab(options.sendSysKeys);
options.clientCutText = opt.clientCutText;
options.serverCutText = opt.serverCutText;
options.emulate3 = opt.emulate3;
case WM_SETFOCUS:
has_focus = true;
+ // Re-register AltTab hotkey
+ setAltTabGrab(options.sendSysKeys);
break;
case WM_KILLFOCUS:
has_focus = false;
cursorOutsideBuffer();
+ // Unregister AltTab hotkey
+ setAltTabGrab(false);
// Restore the remote keys to consistent states
try {
kbd.releaseAllKeys(writer());
// -=- Handle keyboard input
+ case WM_HOTKEY:
+ if (wParam == HOTKEY_ALTTAB) {
+ writeKeyEvent(VK_TAB, 0, true);
+ writeKeyEvent(VK_TAB, 0, false);
+ return 0;
+ }
+ break;
+
+ case WM_SYSKEYDOWN:
+ // Translate Alt-Space and Alt-F4 to WM_SYSCHAR and WM_CLOSE,
+ // since we are not using TranslateMessage().
+ if (!options.sendSysKeys) {
+ switch (wParam) {
+ case VK_SPACE:
+ writeKeyEvent(VK_MENU, 0, false);
+ SendMessage(getHandle(), WM_SYSCHAR, wParam, lParam);
+ return 0;
+ case VK_F4:
+ SendMessage(getHandle(), WM_CLOSE, wParam, lParam);
+ return 0;
+ }
+ }
+
+ case WM_SYSKEYUP:
+ // When we have registered for AltTab as a hotkey, SYSKEYUPs for
+ // Tabs are sent (but no SYSKEYDOWNs). AltTab is handled by
+ // WM_HOTKEY, though.
+ if (wParam == VK_TAB) return 0;
+
case WM_KEYUP:
case WM_KEYDOWN:
// Hook the MenuKey to pop-up the window menu
TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, getHandle(), 0);
}
- // Ignore the MenuKey keypress for both press & release events
- return 0;
+ // Ignore the MenuKey keypress for both press & release events
+ return 0;
}
}
- case WM_SYSKEYDOWN:
- case WM_SYSKEYUP:
+
writeKeyEvent(wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
return 0;
}
}
+void
+CView::setAltTabGrab(bool grab) {
+ BOOL hotKeyResult;
+ static bool grabstate = false;
+
+ // Do not call RegisterHotKey/UnregisterHotKey if not necessary
+ if (grabstate == grab)
+ return;
+
+ grabstate = grab;
+
+ // Only works for NT/2k/XP
+ if (grab) {
+ hotKeyResult = RegisterHotKey(hwnd, HOTKEY_ALTTAB, MOD_ALT, VK_TAB);
+ if (!hotKeyResult)
+ vlog.debug("RegisterHotkey failed with error %d", GetLastError());
+ } else {
+ hotKeyResult = UnregisterHotKey(hwnd, HOTKEY_ALTTAB);
+ }
+}
bool
CView::invalidateBufferRect(const Rect& crect) {
void hideSystemCursor();
void showSystemCursor();
+ // Grab AltTab?
+ void setAltTabGrab(bool grab);
+
// cursorOutsideBuffer() is called whenever we detect that the mouse has
// moved outside the desktop. It restores the system arrow cursor.
void cursorOutsideBuffer();
#define IDC_COMPRESSLEVEL 1056
#define IDC_ALLOW_JPEG 1057
#define IDC_QUALITYLEVEL 1058
+#define IDC_SEND_SYSKEYS 1059
#define ID_CLOSE 40002
#define ID_OPTIONS 40003
#define ID_NEW_CONNECTION 40004
BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
CONTROL "Send keyboard events to server",IDC_SEND_KEYS,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
- CONTROL "Send clipboard changes to server",IDC_CLIENT_CUTTEXT,
+ CONTROL "Send system keys (Alt combinations) to server",IDC_SEND_SYSKEYS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,172,15
+ CONTROL "Send clipboard changes to server",IDC_CLIENT_CUTTEXT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,172,15
CONTROL "Accept clipboard changes from server",
IDC_SERVER_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
- 7,55,172,15
+ 7,70,172,15
CONTROL "Enable 3-button mouse emulation",IDC_EMULATE3,"Button",
- BS_AUTOCHECKBOX | WS_TABSTOP,7,70,172,15
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,85,172,15
CONTROL "Rate-limit mouse move events",IDC_POINTER_INTERVAL,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,86,172,14
- LTEXT "Menu key",IDC_STATIC,7,100,98,15,SS_CENTERIMAGE
- COMBOBOX IDC_MENU_KEY,105,100,74,105,CBS_DROPDOWNLIST | CBS_SORT |
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,172,14
+ LTEXT "Menu key",IDC_STATIC,7,115,98,15,SS_CENTERIMAGE
+ COMBOBOX IDC_MENU_KEY,105,115,74,105,CBS_DROPDOWNLIST | CBS_SORT |
WS_VSCROLL | WS_TABSTOP
END