diff --git a/src/gui/src/Action.cpp b/src/gui/src/Action.cpp index f34d1e8f..5184099c 100644 --- a/src/gui/src/Action.cpp +++ b/src/gui/src/Action.cpp @@ -26,7 +26,8 @@ const char* Action::m_ActionTypeNames[] = "keyDown", "keyUp", "keystroke", "switchToScreen", "toggleScreen", "switchInDirection", "lockCursorToScreen", - "mouseDown", "mouseUp", "mousebutton" + "userScript", + "mouseDown", "mouseUp", "mousebutton", }; const char* Action::m_SwitchDirectionNames[] = { "left", "right", "up", "down" }; @@ -37,6 +38,7 @@ Action::Action() : m_Type(keystroke), m_TypeScreenNames(), m_SwitchScreenName(), + m_UserScriptCommand(), m_SwitchDirection(switchLeft), m_LockCursorMode(lockCursorToggle), m_ActiveOnRelease(false), @@ -105,6 +107,11 @@ QString Action::text() const text += ")"; break; + case userScript: + text += "("; + text += userScriptCommand(); + text += ")"; + break; default: Q_ASSERT(0); break; @@ -133,6 +140,7 @@ void Action::loadSettings(QSettings& settings) setLockCursorMode(settings.value("lockCursorToScreen", lockCursorToggle).toInt()); setActiveOnRelease(settings.value("activeOnRelease", false).toBool()); setHaveScreens(settings.value("hasScreens", false).toBool()); + setUserScriptCommand(settings.value("userScriptCommand").toString()); } void Action::saveSettings(QSettings& settings) const @@ -153,6 +161,7 @@ void Action::saveSettings(QSettings& settings) const settings.setValue("lockCursorToScreen", lockCursorMode()); settings.setValue("activeOnRelease", activeOnRelease()); settings.setValue("hasScreens", haveScreens()); + settings.setValue("userScriptCommand", userScriptCommand()); } QTextStream& operator<<(QTextStream& outStream, const Action& action) diff --git a/src/gui/src/Action.h b/src/gui/src/Action.h index c260badc..64a85206 100644 --- a/src/gui/src/Action.h +++ b/src/gui/src/Action.h @@ -35,7 +35,8 @@ class Action public: enum ActionType { keyDown, keyUp, keystroke, switchToScreen, toggleScreen, switchInDirection, - lockCursorToScreen, mouseDown, mouseUp, mousebutton }; + lockCursorToScreen, userScript, + mouseDown, mouseUp, mousebutton }; enum SwitchDirection { switchLeft, switchRight, switchUp, switchDown }; enum LockCursorMode { lockCursorToggle, lockCursonOn, lockCursorOff }; @@ -72,11 +73,15 @@ class Action bool haveScreens() const { return m_HasScreens; } void setHaveScreens(bool b) { m_HasScreens = b; } + const QString& userScriptCommand() const { return m_UserScriptCommand; } + void setUserScriptCommand(const QString& n) { m_UserScriptCommand = n; } + private: KeySequence m_KeySequence; int m_Type; QStringList m_TypeScreenNames; QString m_SwitchScreenName; + QString m_UserScriptCommand; int m_SwitchDirection; int m_LockCursorMode; bool m_ActiveOnRelease; diff --git a/src/gui/src/ActionDialog.cpp b/src/gui/src/ActionDialog.cpp index 89a037e3..493a5aba 100644 --- a/src/gui/src/ActionDialog.cpp +++ b/src/gui/src/ActionDialog.cpp @@ -39,7 +39,7 @@ ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey // work around Qt Designer's lack of a QButtonGroup; we need it to get // at the button id of the checked radio button - QRadioButton* const typeButtons[] = { m_pRadioPress, m_pRadioRelease, m_pRadioPressAndRelease, m_pRadioSwitchToScreen, m_pRadioToggleScreen, m_pRadioSwitchInDirection, m_pRadioLockCursorToScreen }; + QRadioButton* const typeButtons[] = { m_pRadioPress, m_pRadioRelease, m_pRadioPressAndRelease, m_pRadioSwitchToScreen, m_pRadioToggleScreen, m_pRadioSwitchInDirection, m_pRadioLockCursorToScreen, m_pRadioUserScript }; for (unsigned int i = 0; i < sizeof(typeButtons) / sizeof(typeButtons[0]); i++) m_pButtonGroupType->addButton(typeButtons[i], i); @@ -49,6 +49,7 @@ ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey m_pButtonGroupType->button(m_Action.type())->setChecked(true); m_pComboSwitchInDirection->setCurrentIndex(m_Action.switchDirection()); m_pComboLockCursorToScreen->setCurrentIndex(m_Action.lockCursorMode()); + m_pUserScriptCommand->setText(m_Action.userScriptCommand()); if (m_Action.activeOnRelease()) m_pRadioHotkeyReleased->setChecked(true); @@ -93,6 +94,7 @@ void ActionDialog::accept() m_Action.setSwitchDirection(m_pComboSwitchInDirection->currentIndex()); m_Action.setLockCursorMode(m_pComboLockCursorToScreen->currentIndex()); m_Action.setActiveOnRelease(m_pRadioHotkeyReleased->isChecked()); + m_Action.setUserScriptCommand(m_pUserScriptCommand->text()); QDialog::accept(); } diff --git a/src/gui/src/ActionDialogBase.ui b/src/gui/src/ActionDialogBase.ui index 9c6ad0a0..1113b2bf 100644 --- a/src/gui/src/ActionDialogBase.ui +++ b/src/gui/src/ActionDialogBase.ui @@ -7,7 +7,7 @@ 0 0 372 - 484 + 508 @@ -250,6 +250,37 @@ + + + + + + User Script + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + script1 + + + + + @@ -588,5 +619,53 @@ + + m_pRadioUserScript + toggled(bool) + m_pUserScriptCommand + setEnabled(bool) + + + 63 + 385 + + + 291 + 385 + + + + + m_pRadioUserScript + toggled(bool) + m_pKeySequenceWidgetHotkey + setDisabled(bool) + + + 63 + 385 + + + 185 + 118 + + + + + m_pRadioUserScript + toggled(bool) + m_pGroupBoxScreens + setDisabled(bool) + + + 63 + 385 + + + 185 + 189 + + + diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 63943267..e320ce5f 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -61,7 +61,8 @@ AppConfig::AppConfig(QSettings* settings) : m_CryptoEnabled(false), m_AutoHide(false), m_AutoStart(false), - m_MinimizeToTray(false) + m_MinimizeToTray(false), + m_HeadlessMode(false) { Q_ASSERT(m_pSettings); @@ -161,6 +162,7 @@ void AppConfig::loadSettings() m_AutoHide = settings().value("autoHide", false).toBool(); m_AutoStart = settings().value("autoStart", false).toBool(); m_MinimizeToTray = settings().value("minimizeToTray", false).toBool(); + m_HeadlessMode = settings().value("headlessMode", false).toBool(); } void AppConfig::saveSettings() @@ -184,6 +186,7 @@ void AppConfig::saveSettings() settings().setValue("autoHide", m_AutoHide); settings().setValue("autoStart", m_AutoStart); settings().setValue("minimizeToTray", m_MinimizeToTray); + settings().setValue("headlessMode", m_HeadlessMode); settings().sync(); } @@ -236,3 +239,7 @@ bool AppConfig::getAutoStart() { return m_AutoStart; } void AppConfig::setMinimizeToTray(bool b) { m_MinimizeToTray = b; } bool AppConfig::getMinimizeToTray() { return m_MinimizeToTray; } + +void AppConfig::setHeadlessMode(bool b) { m_HeadlessMode = b; } + +bool AppConfig::getHeadlessMode() { return m_HeadlessMode; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 124ee85f..4561153e 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -100,6 +100,9 @@ class AppConfig: public QObject void setMinimizeToTray(bool b); bool getMinimizeToTray(); + void setHeadlessMode(bool b); + bool getHeadlessMode(); + void saveSettings(); protected: @@ -135,6 +138,7 @@ protected: bool m_AutoHide; bool m_AutoStart; bool m_MinimizeToTray; + bool m_HeadlessMode; static const char m_BarriersName[]; static const char m_BarriercName[]; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 40050782..b353d54c 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -520,6 +520,9 @@ void MainWindow::startBarrier() args << "--enable-crypto"; } + if (m_AppConfig->getHeadlessMode()) { + args << "--headless-mode"; + } #if defined(Q_OS_WIN) // on windows, the profile directory changes depending on the user that // launched the process (e.g. when launched with elevation). setting the diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 1caeae5d..0e37afbc 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -51,6 +51,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pCheckBoxAutoStart->setChecked(appConfig().getAutoStart()); m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray()); m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); + m_pCheckBoxHeadlessMode->setChecked(m_appConfig.getHeadlessMode()); #if defined(Q_OS_WIN) m_pComboElevate->setCurrentIndex(static_cast(appConfig().elevateMode())); @@ -75,6 +76,7 @@ void SettingsDialog::accept() m_appConfig.setAutoHide(m_pCheckBoxAutoHide->isChecked()); m_appConfig.setAutoStart(m_pCheckBoxAutoStart->isChecked()); m_appConfig.setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked()); + m_appConfig.setHeadlessMode(m_pCheckBoxHeadlessMode->isChecked()); m_appConfig.saveSettings(); QDialog::accept(); } diff --git a/src/gui/src/SettingsDialogBase.ui b/src/gui/src/SettingsDialogBase.ui index 719a84bc..d250d3cc 100644 --- a/src/gui/src/SettingsDialogBase.ui +++ b/src/gui/src/SettingsDialogBase.ui @@ -7,7 +7,7 @@ 0 0 368 - 428 + 454 @@ -133,6 +133,13 @@ + + + + Headless &mode + + + diff --git a/src/lib/barrier/App.cpp b/src/lib/barrier/App.cpp index f0aea6a5..97a20b9f 100644 --- a/src/lib/barrier/App.cpp +++ b/src/lib/barrier/App.cpp @@ -114,6 +114,9 @@ App::run(int argc, char** argv) // using the exit(int) function! result = e.getCode(); } + catch (std::runtime_error& re) { + LOG((CLOG_CRIT "A runtime error occurred: %s\n", re.what())); + } catch (std::exception& e) { LOG((CLOG_CRIT "An error occurred: %s\n", e.what())); } diff --git a/src/lib/barrier/App.h b/src/lib/barrier/App.h index 749ca85d..a4146189 100644 --- a/src/lib/barrier/App.h +++ b/src/lib/barrier/App.h @@ -168,7 +168,10 @@ private: " --enable-drag-drop enable file drag & drop.\n" \ " --enable-crypto enable the crypto (ssl) plugin.\n" \ " --profile-dir use named profile directory instead.\n" \ - " --drop-dir use named drop target directory instead.\n" + " --drop-dir use named drop target directory instead.\n" \ + " --headless-mode when any client connected, if server screen is\n" \ + " holding the input focus, server will actively\n" \ + " switch input focus to the client.\n" #define HELP_COMMON_INFO_2 \ " -h, --help display this help and exit.\n" \ diff --git a/src/lib/barrier/ArgParser.cpp b/src/lib/barrier/ArgParser.cpp index 1ac4baff..28d6c04d 100644 --- a/src/lib/barrier/ArgParser.cpp +++ b/src/lib/barrier/ArgParser.cpp @@ -290,6 +290,9 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { argsBase().m_pluginDirectory = argv[++i]; } + else if (isArg(i, argc, argv, NULL, "--headless-mode")) { + argsBase().m_headlessMode = true; + } else { // option not supported here return false; diff --git a/src/lib/barrier/ArgsBase.cpp b/src/lib/barrier/ArgsBase.cpp index dd983c6f..ee9c9356 100644 --- a/src/lib/barrier/ArgsBase.cpp +++ b/src/lib/barrier/ArgsBase.cpp @@ -44,7 +44,8 @@ m_shouldExit(false), m_barrierAddress(), m_enableCrypto(false), m_profileDirectory(""), -m_pluginDirectory("") +m_pluginDirectory(""), +m_headlessMode(false) { } diff --git a/src/lib/barrier/ArgsBase.h b/src/lib/barrier/ArgsBase.h index ad442dc9..4e32f481 100644 --- a/src/lib/barrier/ArgsBase.h +++ b/src/lib/barrier/ArgsBase.h @@ -52,4 +52,5 @@ public: bool m_enableCrypto; String m_profileDirectory; String m_pluginDirectory; + bool m_headlessMode; }; diff --git a/src/lib/barrier/ClientApp.cpp b/src/lib/barrier/ClientApp.cpp index ec687e1e..190f3312 100644 --- a/src/lib/barrier/ClientApp.cpp +++ b/src/lib/barrier/ClientApp.cpp @@ -164,7 +164,7 @@ ClientApp::createScreen() { #if WINAPI_MSWINDOWS return new barrier::Screen(new MSWindowsScreen( - false, args().m_noHooks, args().m_stopOnDeskSwitch, m_events), m_events); + false, args().m_noHooks, args().m_stopOnDeskSwitch, args().m_headlessMode, m_events), m_events); #elif WINAPI_XWINDOWS return new barrier::Screen(new XWindowsScreen( new XWindowsImpl(), diff --git a/src/lib/barrier/IKeyState.cpp b/src/lib/barrier/IKeyState.cpp index e89c0e9d..cd2aa44c 100644 --- a/src/lib/barrier/IKeyState.cpp +++ b/src/lib/barrier/IKeyState.cpp @@ -62,21 +62,21 @@ IKeyState::KeyInfo::alloc(KeyID id, info->m_button = button; info->m_count = count; info->m_screens = info->m_screensBuffer; - strcpy(info->m_screensBuffer, screens.c_str()); + strcpy(info->m_screensBuffer, screens.c_str()); // Compliant: String type is safe return info; } IKeyState::KeyInfo* IKeyState::KeyInfo::alloc(const KeyInfo& x) { - KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo) + - strlen(x.m_screensBuffer)); + auto bufferLen = strnlen(x.m_screensBuffer, SIZE_MAX); + auto info = (KeyInfo*)malloc(sizeof(KeyInfo) + bufferLen); 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); + memcpy(info->m_screensBuffer, x.m_screensBuffer, bufferLen + 1); return info; } diff --git a/src/lib/barrier/ProtocolUtil.cpp b/src/lib/barrier/ProtocolUtil.cpp index 6e67b1b6..4b43d604 100644 --- a/src/lib/barrier/ProtocolUtil.cpp +++ b/src/lib/barrier/ProtocolUtil.cpp @@ -62,6 +62,9 @@ ProtocolUtil::readf(barrier::IStream* stream, const char* fmt, ...) catch (XIO&) { result = false; } + catch (const std::bad_alloc&) { + result = false; + } va_end(args); return result; } @@ -79,18 +82,17 @@ ProtocolUtil::vwritef(barrier::IStream* stream, } // fill buffer - UInt8* buffer = new UInt8[size]; - writef_void(buffer, fmt, args); + std::vector buffer; + buffer.reserve(size); + writef_void(buffer.data(), fmt, args); try { // write buffer - stream->write(buffer, size); + stream->write(buffer.data(), size); LOG((CLOG_DEBUG2 "wrote %d bytes", size)); - - delete[] buffer; } - catch (XBase&) { - delete[] buffer; + catch (const XBase& exception) { + LOG((CLOG_DEBUG2 "Exception <%s> during wrote %d bytes into stream", exception.what(), size)); throw; } } diff --git a/src/lib/barrier/ServerApp.cpp b/src/lib/barrier/ServerApp.cpp index 599bde70..b3d43887 100644 --- a/src/lib/barrier/ServerApp.cpp +++ b/src/lib/barrier/ServerApp.cpp @@ -267,6 +267,18 @@ ServerApp::handleClientConnected(const Event&, void* vlistener) if (client != NULL) { m_server->adoptClient(client); updateStatus(); + if( args().m_headlessMode && m_server->isServerHoldFocus() ) { + std::string screen = args().m_config->getCanonicalName(client->getName()); + if (screen.empty()) { + screen = client->getName(); + } + if(screen.empty()) return; + // send event + Server::SwitchToScreenInfo* info = + Server::SwitchToScreenInfo::alloc(screen); + m_events->addEvent(Event(m_events->forServer().switchToScreen(), + args().m_config->getInputFilter(), info)); + } } } @@ -604,7 +616,7 @@ ServerApp::createScreen() { #if WINAPI_MSWINDOWS return new barrier::Screen(new MSWindowsScreen( - true, args().m_noHooks, args().m_stopOnDeskSwitch, m_events), m_events); + true, args().m_noHooks, args().m_stopOnDeskSwitch, args().m_headlessMode, m_events), m_events); #elif WINAPI_XWINDOWS return new barrier::Screen(new XWindowsScreen( new XWindowsImpl(), diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index b0dbbc37..18bd1a82 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -240,7 +240,8 @@ void Client::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) { m_active = true; - m_screen->mouseMove(xAbs, yAbs); + if (!m_args.m_headlessMode) + m_screen->mouseMove(xAbs, yAbs); m_screen->enter(mask); if (m_sendFileThread != NULL) { @@ -515,7 +516,7 @@ Client::setupTimer() { assert(m_timer == NULL); - m_timer = m_events->newOneShotTimer(15.0, NULL); + m_timer = m_events->newOneShotTimer(2.0, NULL); m_events->adoptHandler(Event::kTimer, m_timer, new TMethodEventJob(this, &Client::handleConnectTimeout)); diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 848ded78..81308cce 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -100,7 +100,7 @@ MSWindowsDesks::MSWindowsDesks( bool isPrimary, bool noHooks, const IScreenSaver* screensaver, IEventQueue* events, - IJob* updateKeys, bool stopOnDeskSwitch) : + IJob* updateKeys, bool stopOnDeskSwitch, bool headlessMode) : m_isPrimary(isPrimary), m_noHooks(noHooks), m_isOnScreen(m_isPrimary), @@ -117,7 +117,8 @@ MSWindowsDesks::MSWindowsDesks( m_deskReady(&m_mutex, false), m_updateKeys(updateKeys), m_events(events), - m_stopOnDeskSwitch(stopOnDeskSwitch) + m_stopOnDeskSwitch(stopOnDeskSwitch), + m_headlessMode(headlessMode) { m_cursor = createBlankCursor(); m_deskClass = createDeskWindowClass(m_isPrimary); @@ -596,9 +597,11 @@ MSWindowsDesks::deskLeave(Desk* desk, HKL keyLayout) // we aren't notified when the mouse leaves our window. SetCapture(desk->m_window); - // warp the mouse to the cursor center - LOG((CLOG_DEBUG2 "warping cursor to center: %+d,%+d", m_xCenter, m_yCenter)); - deskMouseMove(m_xCenter, m_yCenter); + if (!m_headlessMode) { + // warp the mouse to the cursor center + LOG((CLOG_DEBUG2 "warping cursor to center: %+d,%+d", m_xCenter, m_yCenter)); + deskMouseMove(m_xCenter, m_yCenter); + } } } diff --git a/src/lib/platform/MSWindowsDesks.h b/src/lib/platform/MSWindowsDesks.h index 65d93ef5..3c988048 100644 --- a/src/lib/platform/MSWindowsDesks.h +++ b/src/lib/platform/MSWindowsDesks.h @@ -68,7 +68,7 @@ public: MSWindowsDesks( bool isPrimary, bool noHooks, const IScreenSaver* screensaver, IEventQueue* events, - IJob* updateKeys, bool stopOnDeskSwitch); + IJob* updateKeys, bool stopOnDeskSwitch, bool headlessMode); ~MSWindowsDesks(); //! @name manipulators @@ -294,4 +294,6 @@ private: // true if program should stop on desk switch. bool m_stopOnDeskSwitch; + + bool m_headlessMode; }; diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 2449ba7c..d09e03e5 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -93,6 +93,7 @@ MSWindowsScreen::MSWindowsScreen( bool isPrimary, bool noHooks, bool stopOnDeskSwitch, + bool headlessMode, IEventQueue* events) : PlatformScreen(events), m_isPrimary(isPrimary), @@ -136,7 +137,8 @@ MSWindowsScreen::MSWindowsScreen( m_events, new TMethodJob( this, &MSWindowsScreen::updateKeysCB), - stopOnDeskSwitch); + stopOnDeskSwitch, + headlessMode); m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events); updateScreenShape(); @@ -311,6 +313,13 @@ MSWindowsScreen::enter() bool MSWindowsScreen::leave() { + POINT pos; + if (!getThisCursorPos(&pos)) + { + LOG((CLOG_DEBUG "Unable to leave screen as Windows security has disabled critical functions required to let barrier work")); + //unable to get position this means barrier will break if the cursor leaves the screen + return false; + } // get keyboard layout of foreground window. we'll use this // keyboard layout for translating keys sent to clients. HWND window = GetForegroundWindow(); @@ -521,6 +530,60 @@ MSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const m_desks->getCursorPos(x, y); } +/* + * getThisCursorPos and setThisCursorPos will attempt to negotiate with the system + * to try get the and set the mouse position, however on the logon screen due to + * hooks this process has it may unable to work around the problem. Although these + * functions did not fix the issue at hand (#5294) its worth keeping them here anyway. + */ +bool MSWindowsScreen::getThisCursorPos(LPPOINT pos) +{ + auto result = GetCursorPos(pos); + auto error = GetLastError(); + LOG((CLOG_DEBUG3 "%s Attempt: 1 , status %d, code: %d Pos {%d, %d}", __func__, result, error, pos->x, pos->y)); + if (!result) + { + result = GetCursorPos(pos); + error = GetLastError(); + LOG((CLOG_DEBUG3 "%s Attempt: 2, status %d, code: %d Pos {%d, %d}", __func__, result, error, pos->x, pos->y)); + updateDesktopThread(); + } + return result; +} + +bool MSWindowsScreen::setThisCursorPos(int x, int y) +{ + auto result = SetCursorPos(x, y); + auto error = GetLastError(); + LOG((CLOG_DEBUG3 "%s Attempt: 1, status %d, code: %d", __func__, result, error)); + if (!result) + { + result = SetCursorPos(x, y); + error = GetLastError(); + LOG((CLOG_DEBUG3 "%s Attempt: 2, status %d, code: %d", __func__, result, error)); + updateDesktopThread(); + } + + return result; +} + +void MSWindowsScreen::updateDesktopThread() +{ + + LOG((CLOG_DEBUG3 "Failed to set cursor Attempting to switch desktop")); + SetLastError(0); + HDESK cur_hdesk = OpenInputDesktop(0, true, GENERIC_ALL); + + auto error = GetLastError(); + LOG((CLOG_DEBUG3 "\tGetting desktop Handle: %p Status code: %d", cur_hdesk, error)); + + error = GetLastError(); + LOG((CLOG_DEBUG3 "\tSetting desktop return: %d Status code: %d", SetThreadDesktop(cur_hdesk), GetLastError())); + + CloseDesktop(cur_hdesk); + +} + void MSWindowsScreen::reconfigure(UInt32 activeSides) { @@ -1505,11 +1568,11 @@ MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) // warp mouse. hopefully this inserts a mouse motion event // between the previous message and the following message. - SetCursorPos(x, y); + setThisCursorPos(x, y); // check to see if the mouse pos was set correctly POINT cursorPos; - GetCursorPos(&cursorPos); + getThisCursorPos(&cursorPos); // there is a bug or round error in SetCursorPos and GetCursorPos on // a high DPI setting. The check here is for Vista/7 login screen. diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index ae53f1ad..ad134182 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -44,6 +44,7 @@ public: bool isPrimary, bool noHooks, bool stopOnDeskSwitch, + bool headlessMode, IEventQueue* events); virtual ~MSWindowsScreen(); @@ -76,6 +77,25 @@ public: SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + /** + * \brief Get the position of the cursor on the current machine + * \param pos the object that the function will use to store the position of the cursor + * \return true if the function was successful + */ + virtual bool getThisCursorPos(LPPOINT pos); + /** + * \brief Sets the cursor position on the current machine + * \param x The x coordinate of the cursor + * \param y The Y coordinate of the cursor + * \return True is successful + */ + virtual bool setThisCursorPos(int x, int y); + + /** + * \brief This function will attempt to switch to the current desktop the mouse is located on + */ + virtual void updateDesktopThread(); + // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index bcdb88cd..0c30bed8 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -1244,6 +1244,20 @@ void Config::parseAction(ConfigReadContext& s, const std::string& name, action = new InputFilter::KeyboardBroadcastAction(m_events, mode, screens); } + else if (name == "userScript") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: userScript(scriptCommand)"); + } + + std::string scriptCommand = args[0]; + + if (scriptCommand.empty()) { + throw XConfigRead(s, "bad script command in userScript"); + } + + action = new InputFilter::UserScriptAction(m_events, scriptCommand); + } + else { throw XConfigRead(s, "unknown action argument \"%{1}\"", name); } diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp index a0dce17c..c8dd8d9d 100644 --- a/src/lib/server/InputFilter.cpp +++ b/src/lib/server/InputFilter.cpp @@ -558,7 +558,7 @@ std::string InputFilter::KeystrokeAction::format() const return barrier::string::sprintf("%s(%s,%.*s)", type, barrier::KeyMap::formatKey(m_keyInfo->m_key, m_keyInfo->m_mask).c_str(), - strlen(m_keyInfo->m_screens + 1) - 1, + strnlen(m_keyInfo->m_screens + 1, SIZE_MAX) - 1, m_keyInfo->m_screens + 1); } } @@ -660,6 +660,53 @@ InputFilter::MouseButtonAction::formatName() const return (m_press ? "mouseDown" : "mouseUp"); } + +InputFilter::UserScriptAction::UserScriptAction(IEventQueue* events, + const std::string& scriptCommand) : + m_scriptCommand(scriptCommand), + m_events(events) +{ + // do nothing +} + +std::string InputFilter::UserScriptAction::getScriptCommand() const +{ + return m_scriptCommand; +} + +InputFilter::Action* +InputFilter::UserScriptAction::clone() const +{ + return new UserScriptAction(*this); +} + +std::string InputFilter::UserScriptAction::format() const +{ + return barrier::string::sprintf("userScript(%s)", m_scriptCommand.c_str()); +} + +void +InputFilter::UserScriptAction::perform(const Event& event) +{ + // pick screen name. if m_screen is empty then use the screen from + // event if it has one. + std::string scriptCommand = m_scriptCommand; + if (scriptCommand.empty()) { + LOG((CLOG_ERR "script ID empty")); + return; + } + + // exec script +#if !defined(_WIN32) + std::string s = barrier::string::sprintf(R"(sh -c '%s')", scriptCommand.c_str()); +#else + std::string s = barrier::string::sprintf(R"(cmd.exe /C "%s")", scriptCommand.c_str()); + +#endif + LOG((CLOG_DEBUG "%s",s.c_str())); + system(s.c_str()); +} + // // InputFilter::Rule // diff --git a/src/lib/server/InputFilter.h b/src/lib/server/InputFilter.h index 5e6ef9cf..5a730004 100644 --- a/src/lib/server/InputFilter.h +++ b/src/lib/server/InputFilter.h @@ -270,6 +270,23 @@ public: IEventQueue* m_events; }; + // UserScriptAction + class UserScriptAction : public Action { + public: + UserScriptAction(IEventQueue* events, const std::string& scriptCommand); + + std::string getScriptCommand() const; + + // Action overrides + virtual Action* clone() const; + virtual String format() const; + virtual void perform(const Event&); + + private: + std::string m_scriptCommand; + IEventQueue* m_events; + }; + class Rule { public: Rule(); diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 2eff0029..c4ad92dc 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -2338,7 +2338,7 @@ Server::SwitchToScreenInfo::alloc(const std::string& screen) SwitchToScreenInfo* info = (SwitchToScreenInfo*)malloc(sizeof(SwitchToScreenInfo) + screen.size()); - strcpy(info->m_screen, screen.c_str()); + strcpy(info->m_screen, screen.c_str()); // Compliant: we made sure the buffer is large enough return info; } @@ -2377,7 +2377,7 @@ Server::KeyboardBroadcastInfo::alloc(State state, const std::string& screens) (KeyboardBroadcastInfo*)malloc(sizeof(KeyboardBroadcastInfo) + screens.size()); info->m_state = state; - strcpy(info->m_screens, screens.c_str()); + strcpy(info->m_screens, screens.c_str()); // Compliant: we made sure that screens variable ended with null return info; } diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index cb71ec3d..bf5087f8 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -179,6 +179,10 @@ public: //! Return fake drag file list DragFileList getFakeDragFileList() { return m_fakeDragFileList; } + //! Return true if fucus in primary clinet + bool isServerHoldFocus() { return (m_active == + (BaseClientProxy*) m_primaryClient); } + //@} private: diff --git a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp index 79935d89..404bbb1c 100644 --- a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp @@ -53,7 +53,7 @@ protected: return new MSWindowsDesks( true, false, m_screensaver, eventQueue, new TMethodJob( - this, &MSWindowsKeyStateTests::updateKeysCB), false); + this, &MSWindowsKeyStateTests::updateKeysCB), false, false); } void* getEventTarget() const