diff --git a/doc/user/configuration.md b/doc/user/configuration.md index 0310a1c7a..adab7035a 100644 --- a/doc/user/configuration.md +++ b/doc/user/configuration.md @@ -432,6 +432,9 @@ Actions are two lists of individual actions separated by commas. The two lists a * `switchInDirection(dir)` : Switch to the screen in the direction ''dir'', which may be one of ''left'', ''right'', ''up'' or ''down''. +* `switchToNextScreen()` +: Cycle to the next screen in the configuration order. If at the last screen, cycles back to the first screen. + ##### Keynames Valid key names are: diff --git a/src/lib/gui/Action.h b/src/lib/gui/Action.h index 073d3c451..ae90cd862 100644 --- a/src/lib/gui/Action.h +++ b/src/lib/gui/Action.h @@ -1,5 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd. * SPDX-FileCopyrightText: (C) 2008 Volker Lanz * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception @@ -42,6 +43,7 @@ public: keystroke, switchToScreen, switchInDirection, + switchToNextScreen, lockCursorToScreen, restartAllConnections, mouseDown, @@ -162,11 +164,17 @@ private: inline static const QString m_commandTemplate = QStringLiteral("(%1)"); inline static const QStringList m_actionTypeNames{ - QStringLiteral("keyDown"), QStringLiteral("keyUp"), - QStringLiteral("keystroke"), QStringLiteral("switchToScreen"), - QStringLiteral("switchInDirection"), QStringLiteral("lockCursorToScreen"), - QStringLiteral("restartServer"), QStringLiteral("mouseDown"), - QStringLiteral("mouseUp"), QStringLiteral("mousebutton") + QStringLiteral("keyDown"), + QStringLiteral("keyUp"), + QStringLiteral("keystroke"), + QStringLiteral("switchToScreen"), + QStringLiteral("switchInDirection"), + QStringLiteral("switchToNextScreen"), + QStringLiteral("lockCursorToScreen"), + QStringLiteral("restartServer"), + QStringLiteral("mouseDown"), + QStringLiteral("mouseUp"), + QStringLiteral("mousebutton") }; inline static const QStringList m_switchDirectionNames{ diff --git a/src/lib/gui/dialogs/ActionDialog.h b/src/lib/gui/dialogs/ActionDialog.h index f50c7b261..29f3438e1 100644 --- a/src/lib/gui/dialogs/ActionDialog.h +++ b/src/lib/gui/dialogs/ActionDialog.h @@ -1,6 +1,7 @@ /* * Deskflow -- mouse and keyboard sharing utility * SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd. * SPDX-FileCopyrightText: (C) 2008 Volker Lanz * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception @@ -31,8 +32,9 @@ public: inline static const auto ToggleKey = 2; inline static const auto SwitchTo = 3; inline static const auto SwitchInDirection = 4; - inline static const auto ModifyCursorLock = 5; - inline static const auto RestartServer = 6; + inline static const auto SwitchToNextScreen = 5; + inline static const auto ModifyCursorLock = 6; + inline static const auto RestartServer = 7; }; ActionDialog(QWidget *parent, const ServerConfig &config, Hotkey &hotkey, Action &action); diff --git a/src/lib/gui/dialogs/ActionDialog.ui b/src/lib/gui/dialogs/ActionDialog.ui index 27dab866e..59ebbd529 100644 --- a/src/lib/gui/dialogs/ActionDialog.ui +++ b/src/lib/gui/dialogs/ActionDialog.ui @@ -63,6 +63,11 @@ Switch in a direction + + + Switch to next computer + + Modify the cursor lock diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index d979bee72..530b89c25 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -1,5 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd. * SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception @@ -1061,6 +1062,14 @@ void Config::parseAction( action = new InputFilter::SwitchInDirectionAction(m_events, direction); } + else if (name == "switchToNextScreen") { + if (args.size() != 0) { + throw ServerConfigReadException(s, "syntax for action: switchToNextScreen"); + } + + action = new InputFilter::SwitchToNextScreenAction(m_events); + } + else if (name == "lockCursorToScreen") { if (args.size() > 1) { throw ServerConfigReadException(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])"); diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp index a8166fcbd..18aa38b19 100644 --- a/src/lib/server/InputFilter.cpp +++ b/src/lib/server/InputFilter.cpp @@ -336,6 +336,28 @@ void InputFilter::SwitchInDirectionAction::perform(const Event &event) ); } +InputFilter::SwitchToNextScreenAction::SwitchToNextScreenAction(IEventQueue *events) : m_events(events) +{ + // do nothing +} + +InputFilter::Action *InputFilter::SwitchToNextScreenAction::clone() const +{ + return new SwitchToNextScreenAction(*this); +} + +std::string InputFilter::SwitchToNextScreenAction::format() const +{ + return "switchToNextScreen()"; +} + +void InputFilter::SwitchToNextScreenAction::perform(const Event &event) +{ + m_events->addEvent( + Event(EventTypes::ServerToggleScreen, event.getTarget(), nullptr, Event::EventFlags::DeliverImmediately) + ); +} + InputFilter::KeyboardBroadcastAction::KeyboardBroadcastAction(IEventQueue *events, Mode mode) : m_mode(mode), m_events(events) diff --git a/src/lib/server/InputFilter.h b/src/lib/server/InputFilter.h index b0839b635..2cd87b1e4 100644 --- a/src/lib/server/InputFilter.h +++ b/src/lib/server/InputFilter.h @@ -211,6 +211,21 @@ public: IEventQueue *m_events; }; + // SwitchToNextScreenAction + class SwitchToNextScreenAction : public Action + { + public: + explicit SwitchToNextScreenAction(IEventQueue *events); + + // Action overrides + Action *clone() const override; + std::string format() const override; + void perform(const Event &) override; + + private: + IEventQueue *m_events; + }; + // KeyboardBroadcastAction class KeyboardBroadcastAction : public Action { diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index df15a5186..2fc8d9b03 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -28,6 +28,7 @@ #include "server/PrimaryClient.h" #ifdef _WIN32 +#include #include #endif #include @@ -110,6 +111,9 @@ Server::Server( m_events->addHandler(EventTypes::ServerSwitchInDirection, m_inputFilter, [this](const auto &e) { handleSwitchInDirectionEvent(e); }); + m_events->addHandler(EventTypes::ServerToggleScreen, m_inputFilter, [this](const auto &e) { + handleToggleScreenEvent(e); + }); m_events->addHandler(EventTypes::ServerKeyboardBroadcast, m_inputFilter, [this](const auto &e) { handleKeyboardBroadcastEvent(e); }); @@ -1330,6 +1334,41 @@ void Server::handleSwitchInDirectionEvent(const Event &event) } } +void Server::handleToggleScreenEvent(const Event &event) +{ + // Get the list of connected screens in config order + std::vector screens; + getClients(screens); + + if (screens.size() < 2) { + LOG_ERR("not enough screens to toggle"); + return; + } + + // Find the current active screen + std::string currentScreen = getName(m_active); + auto it = std::ranges::find(screens, currentScreen); + if (it == screens.end()) { + LOG_ERR("current screen not found in list"); + return; + } + + // Find the next screen + auto nextIt = it + 1; + if (nextIt == screens.end()) { + nextIt = screens.begin(); + } + + // Find the client for the next screen + ClientList::const_iterator clientIt = m_clients.find(*nextIt); + if (clientIt == m_clients.end()) { + LOG_ERR("next screen not active"); + return; + } + + jumpToScreen(clientIt->second); +} + void Server::handleKeyboardBroadcastEvent(const Event &event) { const auto *info = (KeyboardBroadcastInfo *)event.getData(); diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 3e74fecbf..fb9c12a72 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -306,6 +306,7 @@ private: void handleClientCloseTimeout(BaseClientProxy *client); void handleSwitchToScreenEvent(const Event &event); void handleSwitchInDirectionEvent(const Event &event); + void handleToggleScreenEvent(const Event &event); void handleKeyboardBroadcastEvent(const Event &event); void handleLockCursorToScreenEvent(const Event &event);