Internal
Public Access
Sync local clipboard to a tab when it becomes visible
Only the visible tab syncs the clipboard, but a tab in the background missed local clipboard changes and kept a stale remote clipboard when switched to, so with two RDP/VNC sessions only the one last copied in worked. A tab now pushes the current local clipboard to its remote on show. Bump version to v2026.9.26.2. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0f821feeed
commit
67ea7ea6b3
4 files changed
+40
-13
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
|
||||||
project(OrbitHub VERSION 2026.9.26 LANGUAGES CXX)
|
project(OrbitHub VERSION 2026.9.26.2 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|||||||
@@ -242,6 +242,11 @@ Delivered:
|
|||||||
and not-yet-ready-channel copies are logged to the session event log
|
and not-yet-ready-channel copies are logged to the session event log
|
||||||
(with a line per copy sent/received) instead of silently dropped. Not
|
(with a line per copy sent/received) instead of silently dropped. Not
|
||||||
reproduced locally; needs confirmation from real use
|
reproduced locally; needs confirmation from real use
|
||||||
|
- Follow-up to the clipboard fix above: restricting sync to the visible
|
||||||
|
tab left a tab that had been in the background with a stale remote
|
||||||
|
clipboard when switched to (with two RDP sessions only the one last
|
||||||
|
copied in worked). A tab now pushes the current local clipboard to its
|
||||||
|
remote whenever it becomes visible
|
||||||
- Robustness fix: an unrecognized `FramebufferUpdate` rectangle encoding
|
- Robustness fix: an unrecognized `FramebufferUpdate` rectangle encoding
|
||||||
used to abort the connection generically; `kAnnouncedEncodings` is now
|
used to abort the connection generically; `kAnnouncedEncodings` is now
|
||||||
the single source of truth for what `SetEncodings` announces and what
|
the single source of truth for what `SetEncodings` announces and what
|
||||||
|
|||||||
+27
-12
@@ -28,6 +28,7 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QShowEvent>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@@ -799,6 +800,26 @@ void SessionTab::onBackendRemoteClipboardTextChanged(const QString& text)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SessionTab::onSystemClipboardChanged()
|
void SessionTab::onSystemClipboardChanged()
|
||||||
|
{
|
||||||
|
const QClipboard* clipboard = QApplication::clipboard();
|
||||||
|
const QString text = clipboard->mimeData()->hasText() ? clipboard->text() : QString();
|
||||||
|
|
||||||
|
// Suppress only the change we caused ourselves, and only briefly. The
|
||||||
|
// old "same as last synced text" check had no expiry: once a send was
|
||||||
|
// lost (channel not ready, remote clipboard changed to something we
|
||||||
|
// never saw) copying that same text again did nothing, indefinitely.
|
||||||
|
constexpr qint64 kEchoWindowMs = 1000;
|
||||||
|
const bool isEcho = !m_pendingClipboardEcho.isEmpty() && text == m_pendingClipboardEcho
|
||||||
|
&& QDateTime::currentMSecsSinceEpoch() - m_pendingClipboardEchoMs < kEchoWindowMs;
|
||||||
|
m_pendingClipboardEcho.clear();
|
||||||
|
if (isEcho) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushLocalClipboardToRemote();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::pushLocalClipboardToRemote()
|
||||||
{
|
{
|
||||||
if (!m_clipboardSyncSupported || m_state != SessionState::Connected || !isVisible()) {
|
if (!m_clipboardSyncSupported || m_state != SessionState::Connected || !isVisible()) {
|
||||||
return;
|
return;
|
||||||
@@ -814,22 +835,16 @@ void SessionTab::onSystemClipboardChanged()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suppress only the change we caused ourselves, and only briefly. The
|
|
||||||
// old "same as last synced text" check had no expiry: once a send was
|
|
||||||
// lost (channel not ready, remote clipboard changed to something we
|
|
||||||
// never saw) copying that same text again did nothing, indefinitely.
|
|
||||||
constexpr qint64 kEchoWindowMs = 1000;
|
|
||||||
const bool isEcho = !m_pendingClipboardEcho.isEmpty() && text == m_pendingClipboardEcho
|
|
||||||
&& QDateTime::currentMSecsSinceEpoch() - m_pendingClipboardEchoMs < kEchoWindowMs;
|
|
||||||
m_pendingClipboardEcho.clear();
|
|
||||||
if (isEcho) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit requestSetClipboardText(text);
|
emit requestSetClipboardText(text);
|
||||||
appendEvent(QStringLiteral("Clipboard: sending %1 characters to remote.").arg(text.size()));
|
appendEvent(QStringLiteral("Clipboard: sending %1 characters to remote.").arg(text.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionTab::showEvent(QShowEvent* event)
|
||||||
|
{
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
pushLocalClipboardToRemote();
|
||||||
|
}
|
||||||
|
|
||||||
void SessionTab::setupUi()
|
void SessionTab::setupUi()
|
||||||
{
|
{
|
||||||
auto* rootLayout = new QVBoxLayout(this);
|
auto* rootLayout = new QVBoxLayout(this);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class QComboBox;
|
|||||||
class QLabel;
|
class QLabel;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QScrollArea;
|
class QScrollArea;
|
||||||
|
class QShowEvent;
|
||||||
class KodoTerm;
|
class KodoTerm;
|
||||||
|
|
||||||
struct SessionUiPreferences
|
struct SessionUiPreferences
|
||||||
@@ -99,6 +100,11 @@ signals:
|
|||||||
void requestSetClipboardText(const QString& text);
|
void requestSetClipboardText(const QString& text);
|
||||||
void requestProvideUsername(const QString& username);
|
void requestProvideUsername(const QString& username);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// A tab that wasn't in front while the local clipboard changed missed
|
||||||
|
// that change (only the visible tab syncs), so catch it up on show.
|
||||||
|
void showEvent(QShowEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onBackendStateChanged(SessionState state, const QString& message);
|
void onBackendStateChanged(SessionState state, const QString& message);
|
||||||
void onBackendEventLogged(const QString& message);
|
void onBackendEventLogged(const QString& message);
|
||||||
@@ -166,6 +172,7 @@ private:
|
|||||||
std::function<void(std::optional<QString>)> callback,
|
std::function<void(std::optional<QString>)> callback,
|
||||||
bool maskInput = true);
|
bool maskInput = true);
|
||||||
void hidePasswordPrompt();
|
void hidePasswordPrompt();
|
||||||
|
void pushLocalClipboardToRemote();
|
||||||
bool validateProfileForConnect();
|
bool validateProfileForConnect();
|
||||||
void appendEvent(const QString& message);
|
void appendEvent(const QString& message);
|
||||||
void setState(SessionState state, const QString& message);
|
void setState(SessionState state, const QString& message);
|
||||||
|
|||||||
Reference in new issue
Block a user