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;