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;