diff --git a/po/de.po b/po/de.po index 9e6c16ba..77f9e503 100644 --- a/po/de.po +++ b/po/de.po @@ -538,6 +538,22 @@ msgstr "Paketverlustverhältnis:" msgid "Lossy Encodings Settings" msgstr "Einstellungen für verlustbehaftete VNC-Kodierungen" +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input\tCtrl-M" +msgstr "Multi-Sync Eingabe\tCtrl-M" + +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input disabled." +msgstr "Multi-Sync Eingabe deaktiviert." + +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input enabled: input is replicated to all connections." +msgstr "Multi-Sync Eingabe aktiviert: Eingaben werden an alle Verbindungen gesendet." + +#: ../src/gui/MyFrameMain.cpp +msgid "Replicate mouse and keyboard input to all connections." +msgstr "Maus- und Tastatureingaben an alle Verbindungen senden." + #: ../src/gui/FrameMain.cpp:24 msgid "MultiVNC" msgstr "MultiVNC" diff --git a/po/es.po b/po/es.po index 8f01b868..8fc5565b 100644 --- a/po/es.po +++ b/po/es.po @@ -533,6 +533,22 @@ msgstr "Ratio de pérdidas:" msgid "Lossy Encodings Settings" msgstr "Ajustes de codificación con pérdidas" +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input\tCtrl-M" +msgstr "" + +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input disabled." +msgstr "" + +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input enabled: input is replicated to all connections." +msgstr "" + +#: ../src/gui/MyFrameMain.cpp +msgid "Replicate mouse and keyboard input to all connections." +msgstr "" + #: ../src/gui/FrameMain.cpp:24 msgid "MultiVNC" msgstr "MultiVNC" diff --git a/po/sv.po b/po/sv.po index a26aca3a..6354250e 100644 --- a/po/sv.po +++ b/po/sv.po @@ -530,6 +530,22 @@ msgstr "Förlustkvot:" msgid "Lossy Encodings Settings" msgstr "Inställningar för förstörande kodningar" +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input\tCtrl-M" +msgstr "" + +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input disabled." +msgstr "" + +#: ../src/gui/MyFrameMain.cpp +msgid "Multi-Sync Input enabled: input is replicated to all connections." +msgstr "" + +#: ../src/gui/MyFrameMain.cpp +msgid "Replicate mouse and keyboard input to all connections." +msgstr "" + #: ../src/gui/FrameMain.cpp:24 msgid "MultiVNC" msgstr "MultiVNC" diff --git a/src/gui/MyFrameMain.cpp b/src/gui/MyFrameMain.cpp index 9f9509d0..89fdd9b0 100644 --- a/src/gui/MyFrameMain.cpp +++ b/src/gui/MyFrameMain.cpp @@ -118,6 +118,16 @@ MyFrameMain::MyFrameMain(wxWindow* parent, int id, const wxString& title, // record/replay frame_main_menubar->Enable(ID_INPUT_RECORD, false); frame_main_menubar->Enable(ID_INPUT_REPLAY, false); + // multi-sync input + multi_sync_enabled = false; + { + wxMenu* machineMenu = frame_main_menubar->GetMenu(0); + machineMenu->InsertCheckItem(9, ID_MULTISYNC, + _("Multi-Sync Input\tCtrl-M"), + _("Replicate mouse and keyboard input to all connections.")); + Bind(wxEVT_MENU, &MyFrameMain::machine_multisync, this, ID_MULTISYNC); + frame_main_menubar->Enable(ID_MULTISYNC, false); + } // bookmarks frame_main_menubar->Enable(wxID_ADD, false); // window sharing @@ -1274,6 +1284,12 @@ void MyFrameMain::conn_setup(VNCConn *c) { GetToolBar()->EnableTool(ID_INPUT_RECORD, true); } + // multi-sync: enable when 2+ connections, update targets if already active + if (connections.size() >= 2) + frame_main_menubar->Enable(ID_MULTISYNC, true); + if (multi_sync_enabled) + updateMultiSyncTargets(); + } @@ -1340,6 +1356,15 @@ void MyFrameMain::conn_terminate(int which) // erase the ConnBlob connections.erase(connections.begin() + which); + // multi-sync: auto-disable if fewer than 2 connections remain + if (multi_sync_enabled && connections.size() < 2) { + multi_sync_enabled = false; + frame_main_menubar->Check(ID_MULTISYNC, false); + } + updateMultiSyncTargets(); + if (connections.size() < 2) + frame_main_menubar->Enable(ID_MULTISYNC, false); + if(connections.size() == 0) // nothing to end { // "end connection" @@ -1951,6 +1976,61 @@ void MyFrameMain::machine_input_replay(wxCommandEvent &event) } +void MyFrameMain::machine_multisync(wxCommandEvent &event) +{ + multi_sync_enabled = event.IsChecked(); + updateMultiSyncTargets(); + + if (multi_sync_enabled) + wxLogStatus(_("Multi-Sync Input enabled: input is replicated to all connections.")); + else + wxLogStatus(_("Multi-Sync Input disabled.")); +} + + +void MyFrameMain::updateMultiSyncTargets() +{ + const wxString syncPrefix = "[Sync] "; + + if (!multi_sync_enabled || connections.size() < 2) { + // clear all sync targets and restore tab titles + for (size_t i = 0; i < connections.size(); ++i) { + connections[i].viewerwindow->setSyncTargets({}); + wxString label = notebook_connections->GetPageText(i); + if (label.StartsWith(syncPrefix)) + notebook_connections->SetPageText(i, label.Mid(syncPrefix.length())); + } + return; + } + + int sel = notebook_connections->GetSelection(); + if (sel == wxNOT_FOUND) + return; + + // build target list: all connections except the active one + std::vector targets; + for (size_t i = 0; i < connections.size(); ++i) { + if ((int)i != sel) + targets.push_back(connections[i].conn); + } + + // only the active tab's ViewerWindow gets sync targets + for (size_t i = 0; i < connections.size(); ++i) { + wxString label = notebook_connections->GetPageText(i); + if ((int)i == sel) { + connections[i].viewerwindow->setSyncTargets(targets); + // master tab: strip prefix if present + if (label.StartsWith(syncPrefix)) + notebook_connections->SetPageText(i, label.Mid(syncPrefix.length())); + } else { + connections[i].viewerwindow->setSyncTargets({}); + // slave tab: add prefix if not present + if (!label.StartsWith(syncPrefix)) + notebook_connections->SetPageText(i, syncPrefix + label); + } + } +} + void MyFrameMain::machine_exit(wxCommandEvent &event) { @@ -2673,6 +2753,10 @@ void MyFrameMain::notebook_connections_pagechanged(wxNotebookEvent &event) } else // no object, i.e. disabled frame_main_menubar->Check(ID_SEAMLESS_DISABLED, true); + + // update multi-sync targets when active tab changes + if (multi_sync_enabled) + updateMultiSyncTargets(); } diff --git a/src/gui/MyFrameMain.h b/src/gui/MyFrameMain.h index 70f9a27d..ca80854d 100644 --- a/src/gui/MyFrameMain.h +++ b/src/gui/MyFrameMain.h @@ -110,7 +110,11 @@ class MyFrameMain: public FrameMain bool saveStats(VNCConn* c, int conn_index, const wxArrayString& stats, wxString desc, bool autosave); - + // multi-sync input + bool multi_sync_enabled; + void updateMultiSyncTargets(); + + protected: DECLARE_EVENT_TABLE(); @@ -141,6 +145,7 @@ class MyFrameMain: public FrameMain void machine_save_stats(wxCommandEvent &event); void machine_input_record(wxCommandEvent &event); void machine_input_replay(wxCommandEvent &event); + void machine_multisync(wxCommandEvent &event); void machine_exit(wxCommandEvent &event); void view_toggletoolbar(wxCommandEvent &event); diff --git a/src/gui/ViewerWindow.cpp b/src/gui/ViewerWindow.cpp index 71e671d7..0863acc3 100644 --- a/src/gui/ViewerWindow.cpp +++ b/src/gui/ViewerWindow.cpp @@ -1,5 +1,6 @@ +#include #include #include #include @@ -63,6 +64,7 @@ class VNCCanvas: public wxPanel wxRegion updated_area; double scale_factor = 1.0; bool do_keyboard_grab; + std::vector sync_targets; }; @@ -309,24 +311,49 @@ void VNCCanvas::onMouseAction(wxMouseEvent &event) event.m_y = std::round(event.m_y / scale_factor); conn->sendPointerEvent(event); + + // multi-sync: replicate to other connections + for (VNCConn* target : sync_targets) { + int tw = target->getFrameBufferWidth(); + int th = target->getFrameBufferHeight(); + if (tw <= 0 || th <= 0) + continue; + wxMouseEvent sync_event(event); + int sw = conn->getFrameBufferWidth(); + int sh = conn->getFrameBufferHeight(); + if (sw > 0 && sh > 0) { + sync_event.m_x = std::round(event.m_x * (double)tw / sw); + sync_event.m_y = std::round(event.m_y * (double)th / sh); + } + target->sendPointerEvent(sync_event); + } } void VNCCanvas::onKeyDown(wxKeyEvent &event) { conn->sendKeyEvent(event, true, false); + + for (VNCConn* target : sync_targets) + target->sendKeyEvent(event, true, false); } void VNCCanvas::onKeyUp(wxKeyEvent &event) { conn->sendKeyEvent(event, false, false); + + for (VNCConn* target : sync_targets) + target->sendKeyEvent(event, false, false); } void VNCCanvas::onChar(wxKeyEvent &event) { conn->sendKeyEvent(event, true, true); + + for (VNCConn* target : sync_targets) + target->sendKeyEvent(event, true, true); } @@ -339,7 +366,7 @@ void VNCCanvas::onFocusGain(wxFocusEvent &event) void VNCCanvas::onFocusLoss(wxFocusEvent &event) { wxLogDebug(wxT("VNCCanvas %p: lost focus, upping key modifiers"), this); - + wxKeyEvent key_event; key_event.m_keyCode = WXK_SHIFT; @@ -348,6 +375,16 @@ void VNCCanvas::onFocusLoss(wxFocusEvent &event) conn->sendKeyEvent(key_event, false, false); key_event.m_keyCode = WXK_CONTROL; conn->sendKeyEvent(key_event, false, false); + + for (VNCConn* target : sync_targets) { + wxKeyEvent sync_key; + sync_key.m_keyCode = WXK_SHIFT; + target->sendKeyEvent(sync_key, false, false); + sync_key.m_keyCode = WXK_ALT; + target->sendKeyEvent(sync_key, false, false); + sync_key.m_keyCode = WXK_CONTROL; + target->sendKeyEvent(sync_key, false, false); + } } @@ -663,3 +700,14 @@ void ViewerWindow::grabKeyboard(bool grabit) // grab later on enter/leave canvas->do_keyboard_grab = grabit; } + +void ViewerWindow::setSyncTargets(const std::vector& targets) +{ + if(canvas) + canvas->sync_targets = targets; +} + +VNCConn* ViewerWindow::getConn() const +{ + return canvas ? canvas->conn : nullptr; +} diff --git a/src/gui/ViewerWindow.h b/src/gui/ViewerWindow.h index 3a7cfdb6..b77e8721 100644 --- a/src/gui/ViewerWindow.h +++ b/src/gui/ViewerWindow.h @@ -3,6 +3,7 @@ #ifndef VIEWERWINDOW_H #define VIEWERWINDOW_H +#include #include #include #include @@ -27,6 +28,8 @@ class ViewerWindow: public wxPanel void showOneToOne(bool show_1to1); void grabKeyboard(bool yesno); + void setSyncTargets(const std::vector& targets); + VNCConn* getConn() const; protected: wxStaticText* label_updrawbytes; diff --git a/src/gui/evtids.h b/src/gui/evtids.h index 3c7b2a49..c58e8652 100644 --- a/src/gui/evtids.h +++ b/src/gui/evtids.h @@ -23,6 +23,7 @@ enum ID_INPUT_RECORD, ID_INPUT_REPLAY, ID_ISSUE_LIST, + ID_MULTISYNC, };