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
6a68ed0211
4 files changed
+40
-13
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
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_REQUIRED ON)
|
||||
|
||||
@@ -242,6 +242,11 @@ Delivered:
|
||||
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
|
||||
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
|
||||
used to abort the connection generically; `kAnnouncedEncodings` is now
|
||||
the single source of truth for what `SetEncodings` announces and what
|
||||
|
||||
+27
-12
@@ -28,6 +28,7 @@
|
||||
#include <QComboBox>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QPushButton>
|
||||
#include <QShowEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
@@ -799,6 +800,26 @@ void SessionTab::onBackendRemoteClipboardTextChanged(const QString& text)
|
||||
}
|
||||
|
||||
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()) {
|
||||
return;
|
||||
@@ -814,22 +835,16 @@ void SessionTab::onSystemClipboardChanged()
|
||||
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);
|
||||
appendEvent(QStringLiteral("Clipboard: sending %1 characters to remote.").arg(text.size()));
|
||||
}
|
||||
|
||||
void SessionTab::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
pushLocalClipboardToRemote();
|
||||
}
|
||||
|
||||
void SessionTab::setupUi()
|
||||
{
|
||||
auto* rootLayout = new QVBoxLayout(this);
|
||||
|
||||
@@ -24,6 +24,7 @@ class QComboBox;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QScrollArea;
|
||||
class QShowEvent;
|
||||
class KodoTerm;
|
||||
|
||||
struct SessionUiPreferences
|
||||
@@ -99,6 +100,11 @@ signals:
|
||||
void requestSetClipboardText(const QString& text);
|
||||
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:
|
||||
void onBackendStateChanged(SessionState state, const QString& message);
|
||||
void onBackendEventLogged(const QString& message);
|
||||
@@ -166,6 +172,7 @@ private:
|
||||
std::function<void(std::optional<QString>)> callback,
|
||||
bool maskInput = true);
|
||||
void hidePasswordPrompt();
|
||||
void pushLocalClipboardToRemote();
|
||||
bool validateProfileForConnect();
|
||||
void appendEvent(const QString& message);
|
||||
void setState(SessionState state, const QString& message);
|
||||
|
||||
Reference in new issue
Block a user