From b728885e25b2af6f69c7aa87716c6753c254aab4 Mon Sep 17 00:00:00 2001 From: crs23 Date: Mon, 10 Sep 2007 00:59:51 +0000 Subject: [PATCH] Applied patch 1547642, key broadcasting. Modified the patch to conform to style guidelines. Also enhanced it to allow binding hot keys to not only toggling broadcasting but also turning it on or off, resized the hot key dialog to accommodate the keyboard broadcasting option, changed the configuration string for the keyboard broadcasting option, and added documentation. This change does not include the VS8 fix included in the patch. --- cmd/launcher/CHotkeyOptions.cpp | 80 ++++++++++++++++++++++++++++-- cmd/launcher/CHotkeyOptions.h | 2 + cmd/launcher/launcher.rc | 25 ++++++---- cmd/launcher/resource.h | 11 +++-- doc/configuration.html | 26 ++++++++++ lib/server/CConfig.cpp | 30 +++++++++++ lib/server/CInputFilter.cpp | 71 +++++++++++++++++++++++++- lib/server/CInputFilter.h | 22 +++++++++ lib/server/CServer.cpp | 88 ++++++++++++++++++++++++++++++++- lib/server/CServer.h | 29 +++++++++++ lib/synergy/IKeyState.cpp | 85 +++++++++++++++++-------------- lib/synergy/IKeyState.h | 4 +- 12 files changed, 415 insertions(+), 58 deletions(-) diff --git a/cmd/launcher/CHotkeyOptions.cpp b/cmd/launcher/CHotkeyOptions.cpp index 5b71d4ae..5aa981e0 100644 --- a/cmd/launcher/CHotkeyOptions.cpp +++ b/cmd/launcher/CHotkeyOptions.cpp @@ -925,6 +925,8 @@ CInputFilter::CAction* CHotkeyOptions::CActionDialog::s_action = NULL; CInputFilter::CAction* CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL; +std::set + CHotkeyOptions::CActionDialog::s_screens; WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL; bool @@ -995,11 +997,21 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) // fill lock modes child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)getString(IDS_LOCK_MODE_OFF).c_str()); + (LPARAM)getString(IDS_MODE_OFF).c_str()); SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)getString(IDS_LOCK_MODE_ON).c_str()); + (LPARAM)getString(IDS_MODE_ON).c_str()); SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)getString(IDS_LOCK_MODE_TOGGLE).c_str()); + (LPARAM)getString(IDS_MODE_TOGGLE).c_str()); + SendMessage(child, CB_SETCURSEL, 0, 0); + + // fill keyboard broadcast modes + child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_MODE_OFF).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_MODE_ON).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_MODE_TOGGLE).c_str()); SendMessage(child, CB_SETCURSEL, 0, 0); // select when @@ -1011,6 +1023,9 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) } setItemChecked(child, true); + // no screens by default + s_screens.clear(); + // select mode child = NULL; CInputFilter::CKeystrokeAction* keyAction = @@ -1023,6 +1038,8 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) dynamic_cast(s_action); CInputFilter::CSwitchInDirectionAction* switchInAction = dynamic_cast(s_action); + CInputFilter::CKeyboardBroadcastAction* keyboardBroadcastAction= + dynamic_cast(s_action); if (keyAction != NULL) { if (dynamic_cast(s_action) != NULL) { child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP); @@ -1066,6 +1083,14 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) switchInAction->getDirection() - kLeft, 0); child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN); } + else if (keyboardBroadcastAction != NULL) { + // Save the screens we're broadcasting to + s_screens = keyboardBroadcastAction->getScreens(); + + child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST); + SendMessage(child, CB_SETCURSEL, keyboardBroadcastAction->getMode(), 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST); + } if (child != NULL) { setItemChecked(child, true); } @@ -1111,12 +1136,18 @@ CHotkeyOptions::CActionDialog::updateControls(HWND hwnd) else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) { mode = 4; } + else if (isItemChecked(getItem(hwnd, + IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST))) { + mode = 5; + } // enable/disable all mode specific controls enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1); enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2); enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3); enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4); + enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST, mode == 5); + enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, mode == 5); // can only set screens in key actions CInputFilter::CKeystrokeAction* keyAction = @@ -1306,6 +1337,18 @@ CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd) } } +void +CHotkeyOptions::CActionDialog::onKeyboardBroadcastAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST); + LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) { + delete s_action; + s_action = new CInputFilter::CKeyboardBroadcastAction( + (CInputFilter::CKeyboardBroadcastAction::Mode)index, s_screens); + } +} + KeyID CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam) { @@ -1509,6 +1552,11 @@ CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd, updateControls(hwnd); return TRUE; + case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST: + onKeyboardBroadcastAction(hwnd); + updateControls(hwnd); + return TRUE; + case IDC_HOTKEY_ACTION_LOCK_LIST: switch (HIWORD(wParam)) { case LBN_SELCHANGE: @@ -1533,11 +1581,37 @@ CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd, } break; + case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onKeyboardBroadcastAction(hwnd); + return TRUE; + } + break; + case IDC_HOTKEY_ACTION_SCREENS: CScreensDialog::doModal(hwnd, s_config, dynamic_cast(s_action)); fillHotkey(hwnd); return TRUE; + + case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS: { + // convert screens to form that CScreenDialog::doModal() wants + IPlatformScreen::CKeyInfo* tmpInfo = + IPlatformScreen::CKeyInfo::alloc(0, 0, 0, 1, s_screens); + CInputFilter::CKeystrokeAction tmpAction(tmpInfo, true); + + // get the screens + CScreensDialog::doModal(hwnd, s_config, &tmpAction); + + // convert screens back + IPlatformScreen::CKeyInfo::split( + tmpAction.getInfo()->m_screens, s_screens); + + // update + onKeyboardBroadcastAction(hwnd); + return TRUE; + } } break; diff --git a/cmd/launcher/CHotkeyOptions.h b/cmd/launcher/CHotkeyOptions.h index b63fcf2b..dec5367a 100644 --- a/cmd/launcher/CHotkeyOptions.h +++ b/cmd/launcher/CHotkeyOptions.h @@ -156,6 +156,7 @@ private: static void onLockAction(HWND hwnd); static void onSwitchToAction(HWND hwnd); static void onSwitchInAction(HWND hwnd); + static void onKeyboardBroadcastAction(HWND hwnd); static KeyID getChar(WPARAM wParam, LPARAM lParam); static KeyModifierMask @@ -176,6 +177,7 @@ private: s_action; static CInputFilter::CAction* s_lastGoodAction; + static std::set s_screens; static WNDPROC s_editWndProc; }; diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 6d2952e8..ff72d069 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -338,7 +338,7 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,126,37,50,14 END -IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 202 +IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 218 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Action" FONT 8, "MS Sans Serif" @@ -356,6 +356,9 @@ BEGIN "Button",BS_AUTORADIOBUTTON,7,101,77,10 CONTROL "Lock Cursor to Screen:",IDC_HOTKEY_ACTION_LOCK,"Button", BS_AUTORADIOBUTTON,7,117,89,10 + CONTROL "Keyboard broadcasting:", + IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST,"Button", + BS_AUTORADIOBUTTON,7,133,89,10 LTEXT "&Hot key or mouse button:",IDC_STATIC,7,55,80,8 EDITTEXT IDC_HOTKEY_ACTION_HOTKEY,7,67,152,12,ES_WANTRETURN PUSHBUTTON "...",IDC_HOTKEY_ACTION_SCREENS,162,67,14,12 @@ -365,13 +368,17 @@ BEGIN CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_HOTKEY_ACTION_LOCK_LIST,106,115,70,58, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Action takes place &when:",IDC_STATIC,7,137,81,8 + COMBOBOX IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST,106,131,53,58, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "...",IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, + 162,131,14,12 + LTEXT "Action takes place &when:",IDC_STATIC,7,153,81,8 CONTROL "Hot key is pressed",IDC_HOTKEY_ACTION_ON_ACTIVATE, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,149,74,10 + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,165,74,10 CONTROL "Hot key is released",IDC_HOTKEY_ACTION_ON_DEACTIVATE, - "Button",BS_AUTORADIOBUTTON,7,161,76,10 - DEFPUSHBUTTON "OK",IDOK,70,181,50,14 - PUSHBUTTON "Cancel",IDCANCEL,126,181,50,14 + "Button",BS_AUTORADIOBUTTON,7,177,76,10 + DEFPUSHBUTTON "OK",IDOK,70,197,50,14 + PUSHBUTTON "Cancel",IDCANCEL,126,197,50,14 END IDD_HOTKEY_SCREENS DIALOG DISCARDABLE 0, 0, 237, 79 @@ -586,9 +593,9 @@ BEGIN IDS_AUTOSTART_SAVE_FAILED "Failed to save autostart configuration: %{1}" IDS_LOAD_FAILED "Failed to load configuration." IDS_CONFIG_CHANGED "Configuration changed on disk. Reload?" - IDS_LOCK_MODE_OFF "off" - IDS_LOCK_MODE_ON "on" - IDS_LOCK_MODE_TOGGLE "toggle" + IDS_MODE_OFF "off" + IDS_MODE_ON "on" + IDS_MODE_TOGGLE "toggle" IDS_ALL_SCREENS "All Screens" IDS_ACTIVE_SCREEN "Active Screen" END diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index e4e7b487..f284fd62 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -59,9 +59,9 @@ #define IDS_AUTOSTART_SAVE_FAILED 54 #define IDS_LOAD_FAILED 55 #define IDS_CONFIG_CHANGED 56 -#define IDS_LOCK_MODE_OFF 57 -#define IDS_LOCK_MODE_ON 58 -#define IDS_LOCK_MODE_TOGGLE 59 +#define IDS_MODE_OFF 57 +#define IDS_MODE_ON 58 +#define IDS_MODE_TOGGLE 59 #define IDS_ALL_SCREENS 60 #define IDS_ACTIVE_SCREEN 61 #define IDD_MAIN 101 @@ -169,6 +169,9 @@ #define IDC_HOTKEY_SCREENS_DST 1096 #define IDC_HOTKEY_SCREENS_ADD 1097 #define IDC_HOTKEY_SCREENS_REMOVE 1098 +#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST 1099 +#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST 1100 +#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS 1101 // Next default values for new objects // @@ -177,7 +180,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 116 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1098 +#define _APS_NEXT_CONTROL_VALUE 1102 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/doc/configuration.html b/doc/configuration.html index 48b2cc2d..6c1c8baa 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -406,6 +406,8 @@ Allowed individual actions are: screens lists the screen or screens to direct the event to, regardless of the active screen. If not given then the event is directed to the active screen only. + (Due to a bug, keys cannot be directed to the server while on a + client screen.)

keyDown synthesizes a key press and keyUp synthesizes a key release. @@ -458,6 +460,30 @@ Allowed individual actions are: right, up or down.

+

  • keyboardBroadcast(mode[,screens]) +

    + Turns broadcasting of keystrokes to multiple screens on and off. When + turned on all key presses and releases are sent to all of the screens + listed in screens. If not given, empty or + * then keystrokes are broadcast to all screens. + (However, due to a bug, keys cannot be sent to the server while on a + client screen.) +

    + mode can be off + to turn broadcasting off, on to turn it + on, or toggle to toggle the current + state. The default is toggle. +

    + screens is either * + to indicate all screens or a colon (:) separated list of screen + names. (Note that the screen name must have already been encountered + in the configuration file so you'll probably want to put actions at + the bottom of the file.) +

    + Multiple keyboardBroadcast actions may be + configured with different screens. The most + recently performed action defines the screens to broadcast to. +

    Examples: diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index a502fe78..92054f42 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -1203,6 +1203,36 @@ CConfig::parseAction(CConfigReadContext& s, action = new CInputFilter::CLockCursorToScreenAction(mode); } + else if (name == "keyboardBroadcast") { + if (args.size() > 2) { + throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])"); + } + + CInputFilter::CKeyboardBroadcastAction::Mode mode = + CInputFilter::CKeyboardBroadcastAction::kToggle; + if (args.size() >= 1) { + if (args[0] == "off") { + mode = CInputFilter::CKeyboardBroadcastAction::kOff; + } + else if (args[0] == "on") { + mode = CInputFilter::CKeyboardBroadcastAction::kOn; + } + else if (args[0] == "toggle") { + mode = CInputFilter::CKeyboardBroadcastAction::kToggle; + } + else { + throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])"); + } + } + + std::set screens; + if (args.size() >= 2) { + parseScreens(s, args[1], screens); + } + + action = new CInputFilter::CKeyboardBroadcastAction(mode, screens); + } + else { throw XConfigRead(s, "unknown action argument \"%{1}\"", name); } diff --git a/lib/server/CInputFilter.cpp b/lib/server/CInputFilter.cpp index 2a6c0e3a..d5d7fc20 100644 --- a/lib/server/CInputFilter.cpp +++ b/lib/server/CInputFilter.cpp @@ -268,13 +268,12 @@ CInputFilter::CAction::~CAction() // do nothing } -CInputFilter::CLockCursorToScreenAction::CLockCursorToScreenAction(Mode mode): +CInputFilter::CLockCursorToScreenAction::CLockCursorToScreenAction(Mode mode) : m_mode(mode) { // do nothing } - CInputFilter::CLockCursorToScreenAction::Mode CInputFilter::CLockCursorToScreenAction::getMode() const { @@ -400,6 +399,74 @@ CInputFilter::CSwitchInDirectionAction::perform(const CEvent& event) CEvent::kDeliverImmediately)); } +CInputFilter::CKeyboardBroadcastAction::CKeyboardBroadcastAction(Mode mode) : + m_mode(mode) +{ + // do nothing +} + +CInputFilter::CKeyboardBroadcastAction::CKeyboardBroadcastAction( + Mode mode, + const std::set& screens) : + m_mode(mode), + m_screens(IKeyState::CKeyInfo::join(screens)) +{ + // do nothing +} + +CInputFilter::CKeyboardBroadcastAction::Mode +CInputFilter::CKeyboardBroadcastAction::getMode() const +{ + return m_mode; +} + +std::set +CInputFilter::CKeyboardBroadcastAction::getScreens() const +{ + std::set screens; + IKeyState::CKeyInfo::split(m_screens.c_str(), screens); + return screens; +} + +CInputFilter::CAction* +CInputFilter::CKeyboardBroadcastAction::clone() const +{ + return new CKeyboardBroadcastAction(*this); +} + +CString +CInputFilter::CKeyboardBroadcastAction::format() const +{ + static const char* s_mode[] = { "off", "on", "toggle" }; + static const char* s_name = "keyboardBroadcast"; + + if (m_screens.empty() || m_screens[0] == '*') { + return CStringUtil::print("%s(%s)", s_name, s_mode[m_mode]); + } + else { + return CStringUtil::print("%s(%s,%.*s)", s_name, s_mode[m_mode], + m_screens.size() - 2, + m_screens.c_str() + 1); + } +} + +void +CInputFilter::CKeyboardBroadcastAction::perform(const CEvent& event) +{ + static const CServer::CKeyboardBroadcastInfo::State s_state[] = { + CServer::CKeyboardBroadcastInfo::kOff, + CServer::CKeyboardBroadcastInfo::kOn, + CServer::CKeyboardBroadcastInfo::kToggle + }; + + // send event + CServer::CKeyboardBroadcastInfo* info = + CServer::CKeyboardBroadcastInfo::alloc(s_state[m_mode], m_screens); + EVENTQUEUE->addEvent(CEvent(CServer::getKeyboardBroadcastEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + CInputFilter::CKeystrokeAction::CKeystrokeAction( IPlatformScreen::CKeyInfo* info, bool press) : m_keyInfo(info), diff --git a/lib/server/CInputFilter.h b/lib/server/CInputFilter.h index 1c64636c..571ec82b 100644 --- a/lib/server/CInputFilter.h +++ b/lib/server/CInputFilter.h @@ -21,6 +21,7 @@ #include "IPlatformScreen.h" #include "CString.h" #include "stdmap.h" +#include "stdset.h" class CPrimaryClient; class CEvent; @@ -173,6 +174,27 @@ public: EDirection m_direction; }; + // CKeyboardBroadcastAction + class CKeyboardBroadcastAction : public CAction { + public: + enum Mode { kOff, kOn, kToggle }; + + CKeyboardBroadcastAction(Mode = kToggle); + CKeyboardBroadcastAction(Mode, const std::set& screens); + + Mode getMode() const; + std::set getScreens() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + Mode m_mode; + CString m_screens; + }; + // CKeystrokeAction class CKeystrokeAction : public CAction { public: diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index ab7a7ade..3d4d32c2 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -39,6 +39,7 @@ CEvent::Type CServer::s_connectedEvent = CEvent::kUnknown; CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; CEvent::Type CServer::s_switchToScreen = CEvent::kUnknown; CEvent::Type CServer::s_switchInDirection = CEvent::kUnknown; +CEvent::Type CServer::s_keyboardBroadcast = CEvent::kUnknown; CEvent::Type CServer::s_lockCursorToScreen = CEvent::kUnknown; CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : @@ -61,6 +62,7 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_switchTwoTapArmed(false), m_switchTwoTapZone(3), m_relativeMoves(false), + m_keyboardBroadcasting(false), m_lockedToScreen(false) { // must have a primary client and it must have a canonical name @@ -133,6 +135,10 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_inputFilter, new TMethodEventJob(this, &CServer::handleSwitchInDirectionEvent)); + EVENTQUEUE->adoptHandler(getKeyboardBroadcastEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleKeyboardBroadcastEvent)); EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(), m_inputFilter, new TMethodEventJob(this, @@ -355,6 +361,13 @@ CServer::getSwitchInDirectionEvent() "CServer::switchInDirection"); } +CEvent::Type +CServer::getKeyboardBroadcastEvent() +{ + return CEvent::registerTypeOnce(s_keyboardBroadcast, + "CServer:keyboardBroadcast"); +} + CEvent::Type CServer::getLockCursorToScreenEvent() { @@ -1364,6 +1377,37 @@ CServer::handleSwitchInDirectionEvent(const CEvent& event, void*) } } +void +CServer::handleKeyboardBroadcastEvent(const CEvent& event, void*) +{ + CKeyboardBroadcastInfo* info = (CKeyboardBroadcastInfo*)event.getData(); + + // choose new state + bool newState; + switch (info->m_state) { + case CKeyboardBroadcastInfo::kOff: + newState = false; + break; + + default: + case CKeyboardBroadcastInfo::kOn: + newState = true; + break; + + case CKeyboardBroadcastInfo::kToggle: + newState = !m_keyboardBroadcasting; + break; + } + + // enter new state + if (newState != m_keyboardBroadcasting || + info->m_screens != m_keyboardBroadcastingScreens) { + m_keyboardBroadcasting = newState; + m_keyboardBroadcastingScreens = info->m_screens; + LOG((CLOG_DEBUG "keyboard broadcasting %s: %s", m_keyboardBroadcasting ? "on" : "off", m_keyboardBroadcastingScreens.c_str())); + } +} + void CServer::handleLockCursorToScreenEvent(const CEvent& event, void*) { @@ -1513,10 +1557,17 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, assert(m_active != NULL); // relay - if (IKeyState::CKeyInfo::isDefault(screens)) { + if (!m_keyboardBroadcasting || + (screens && IKeyState::CKeyInfo::isDefault(screens))) { m_active->keyDown(id, mask, button); } else { + if (!screens && m_keyboardBroadcasting) { + screens = m_keyboardBroadcastingScreens.c_str(); + if (IKeyState::CKeyInfo::isDefault(screens)) { + screens = "*"; + } + } for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { if (IKeyState::CKeyInfo::contains(screens, index->first)) { @@ -1534,10 +1585,17 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button, assert(m_active != NULL); // relay - if (IKeyState::CKeyInfo::isDefault(screens)) { + if (!m_keyboardBroadcasting || + (screens && IKeyState::CKeyInfo::isDefault(screens))) { m_active->keyUp(id, mask, button); } else { + if (!screens && m_keyboardBroadcasting) { + screens = m_keyboardBroadcastingScreens.c_str(); + if (IKeyState::CKeyInfo::isDefault(screens)) { + screens = "*"; + } + } for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { if (IKeyState::CKeyInfo::contains(screens, index->first)) { @@ -2090,3 +2148,29 @@ CServer::CScreenConnectedInfo::alloc(const CString& screen) strcpy(info->m_screen, screen.c_str()); return info; } + + +// +// CServer::CKeyboardBroadcastInfo +// + +CServer::CKeyboardBroadcastInfo* +CServer::CKeyboardBroadcastInfo::alloc(State state) +{ + CKeyboardBroadcastInfo* info = + (CKeyboardBroadcastInfo*)malloc(sizeof(CKeyboardBroadcastInfo)); + info->m_state = state; + info->m_screens[0] = '\0'; + return info; +} + +CServer::CKeyboardBroadcastInfo* +CServer::CKeyboardBroadcastInfo::alloc(State state, const CString& screens) +{ + CKeyboardBroadcastInfo* info = + (CKeyboardBroadcastInfo*)malloc(sizeof(CKeyboardBroadcastInfo) + + screens.size()); + info->m_state = state; + strcpy(info->m_screens, screens.c_str()); + return info; +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h index c05d138d..36cf5e83 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -77,6 +77,20 @@ public: char m_screen[1]; }; + //! Keyboard broadcast data + class CKeyboardBroadcastInfo { + public: + enum State { kOff, kOn, kToggle }; + + static CKeyboardBroadcastInfo* alloc(State state = kToggle); + static CKeyboardBroadcastInfo* alloc(State state, + const CString& screens); + + public: + State m_state; + char m_screens[1]; + }; + /*! Start the server with the configuration \p config and the primary client (local screen) \p primaryClient. The client retains @@ -166,6 +180,14 @@ public: */ static CEvent::Type getSwitchInDirectionEvent(); + //! Get keyboard broadcast event type + /*! + Returns the keyboard broadcast event type. The server responds + to this by turning on keyboard broadcasting or turning it off. The + event data is a \c CKeyboardBroadcastInfo*. + */ + static CEvent::Type getKeyboardBroadcastEvent(); + //! Get lock cursor event type /*! Returns the lock cursor event type. The server responds to this @@ -304,6 +326,7 @@ private: void handleClientCloseTimeout(const CEvent&, void*); void handleSwitchToScreenEvent(const CEvent&, void*); void handleSwitchInDirectionEvent(const CEvent&, void*); + void handleKeyboardBroadcastEvent(const CEvent&,void*); void handleLockCursorToScreenEvent(const CEvent&, void*); void handleFakeInputBeginEvent(const CEvent&, void*); void handleFakeInputEndEvent(const CEvent&, void*); @@ -421,6 +444,11 @@ private: // relative mouse move option bool m_relativeMoves; + // flag whether or not we have broadcasting enabled and the screens to + // which we should send broadcasted keys. + bool m_keyboardBroadcasting; + CString m_keyboardBroadcastingScreens; + // screen locking (former scroll lock) bool m_lockedToScreen; @@ -429,6 +457,7 @@ private: static CEvent::Type s_disconnectedEvent; static CEvent::Type s_switchToScreen; static CEvent::Type s_switchInDirection; + static CEvent::Type s_keyboardBroadcast; static CEvent::Type s_lockCursorToScreen; }; diff --git a/lib/synergy/IKeyState.cpp b/lib/synergy/IKeyState.cpp index 28cd3313..d44e17d3 100644 --- a/lib/synergy/IKeyState.cpp +++ b/lib/synergy/IKeyState.cpp @@ -53,12 +53,13 @@ IKeyState::CKeyInfo* IKeyState::CKeyInfo::alloc(KeyID id, KeyModifierMask mask, KeyButton button, SInt32 count) { - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); - info->m_key = id; - info->m_mask = mask; - info->m_button = button; - info->m_count = count; - info->m_screens[0] = '\0'; + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + info->m_screens = NULL; + info->m_screensBuffer[0] = '\0'; return info; } @@ -67,44 +68,30 @@ IKeyState::CKeyInfo::alloc(KeyID id, KeyModifierMask mask, KeyButton button, SInt32 count, const std::set& destinations) { - // collect destinations into a string. names are surrounded by ':' - // which makes searching easy later. the string is empty if there - // are no destinations and "*" means all destinations. - CString screens; - for (std::set::const_iterator i = destinations.begin(); - i != destinations.end(); ++i) { - if (*i == "*") { - screens = "*"; - break; - } - else { - if (screens.empty()) { - screens = ":"; - } - screens += *i; - screens += ":"; - } - } + CString screens = join(destinations); // build structure - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + screens.size()); - info->m_key = id; - info->m_mask = mask; - info->m_button = button; - info->m_count = count; - strcpy(info->m_screens, screens.c_str()); + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + screens.size()); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + info->m_screens = info->m_screensBuffer; + strcpy(info->m_screensBuffer, screens.c_str()); return info; } IKeyState::CKeyInfo* IKeyState::CKeyInfo::alloc(const CKeyInfo& x) { - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + strlen(x.m_screens)); - info->m_key = x.m_key; - info->m_mask = x.m_mask; - info->m_button = x.m_button; - info->m_count = x.m_count; - strcpy(info->m_screens, x.m_screens); + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + + strlen(x.m_screensBuffer)); + info->m_key = x.m_key; + info->m_mask = x.m_mask; + info->m_button = x.m_button; + info->m_count = x.m_count; + info->m_screens = x.m_screens ? info->m_screensBuffer : NULL; + strcpy(info->m_screensBuffer, x.m_screensBuffer); return info; } @@ -141,7 +128,31 @@ IKeyState::CKeyInfo::equal(const CKeyInfo* a, const CKeyInfo* b) a->m_mask == b->m_mask && a->m_button == b->m_button && a->m_count == b->m_count && - strcmp(a->m_screens, b->m_screens) == 0); + strcmp(a->m_screensBuffer, b->m_screensBuffer) == 0); +} + +CString +IKeyState::CKeyInfo::join(const std::set& destinations) +{ + // collect destinations into a string. names are surrounded by ':' + // which makes searching easy. the string is empty if there are no + // destinations and "*" means all destinations. + CString screens; + for (std::set::const_iterator i = destinations.begin(); + i != destinations.end(); ++i) { + if (*i == "*") { + screens = "*"; + break; + } + else { + if (screens.empty()) { + screens = ":"; + } + screens += *i; + screens += ":"; + } + } + return screens; } void diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index 1985a630..2b282487 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -43,6 +43,7 @@ public: static bool isDefault(const char* screens); static bool contains(const char* screens, const CString& name); static bool equal(const CKeyInfo*, const CKeyInfo*); + static CString join(const std::set& destinations); static void split(const char* screens, std::set&); public: @@ -50,7 +51,8 @@ public: KeyModifierMask m_mask; KeyButton m_button; SInt32 m_count; - char m_screens[1]; + char* m_screens; + char m_screensBuffer[1]; }; typedef std::set KeyButtonSet;