fix(client): add per-version server proxies for older servers
A client that downgrades to a 1.6 or 1.7 server parsed key repeat with the 1.8 format and swallowed the messages behind it. ServerProxy is now the 1.6 form with 1.7 and 1.8 subclasses picked by the negotiated version, mirroring the ClientProxy classes.
This commit is contained in:
parent
6638076b85
commit
49e6293a4d
9 changed files with 286 additions and 119 deletions
|
|
@ -8,6 +8,10 @@ add_library(client STATIC
|
|||
Client.h
|
||||
ServerProxy.cpp
|
||||
ServerProxy.h
|
||||
ServerProxy1_7.cpp
|
||||
ServerProxy1_7.h
|
||||
ServerProxy1_8.cpp
|
||||
ServerProxy1_8.h
|
||||
)
|
||||
|
||||
target_link_libraries(client PUBLIC common)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
#include "base/IEventQueue.h"
|
||||
#include "base/Log.h"
|
||||
#include "client/ServerProxy.h"
|
||||
#include "client/ServerProxy1_7.h"
|
||||
#include "client/ServerProxy1_8.h"
|
||||
#include "common/NetworkProtocol.h"
|
||||
#include "common/Settings.h"
|
||||
#include "deskflow/Clipboard.h"
|
||||
|
|
@ -465,18 +467,38 @@ void Client::setupConnection()
|
|||
});
|
||||
}
|
||||
|
||||
void Client::setupScreen()
|
||||
bool Client::setupScreen(int16_t protocolMinor)
|
||||
{
|
||||
assert(m_server == nullptr);
|
||||
|
||||
m_ready = false;
|
||||
|
||||
// only 1.6 and later have a proxy: the clipboard, mouse wheel and key message formats
|
||||
// differ below that, and nothing older (synergy 1.4 and earlier) still needs supporting.
|
||||
// a version with no case is refused by the hello handler.
|
||||
switch (protocolMinor) {
|
||||
case 6:
|
||||
m_server = new ServerProxy(this, m_stream, m_events);
|
||||
break;
|
||||
case 7:
|
||||
m_server = new ServerProxy1_7(this, m_stream, m_events);
|
||||
break;
|
||||
case 8:
|
||||
m_server = new ServerProxy1_8(this, m_stream, m_events);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_server != nullptr) {
|
||||
m_events->addHandler(EventTypes::ScreenShapeChanged, getEventTarget(), [this](const auto &) {
|
||||
handleShapeChanged();
|
||||
});
|
||||
m_events->addHandler(EventTypes::ClipboardGrabbed, getEventTarget(), [this](const auto &e) {
|
||||
handleClipboardGrabbed(e);
|
||||
});
|
||||
}
|
||||
return m_server != nullptr;
|
||||
}
|
||||
|
||||
void Client::setupTimer()
|
||||
|
|
@ -683,6 +705,17 @@ void Client::handleHello()
|
|||
);
|
||||
}
|
||||
|
||||
// no proxy speaks the negotiated version, so hang up as incompatible rather than
|
||||
// talk a version the client does not implement, the same rule the server applies
|
||||
// when it picks a client proxy
|
||||
if (!setupScreen(helloBackMinor)) {
|
||||
LOG_WARN("server protocol version not supported: %d.%d", serverMajor, serverMinor);
|
||||
sendConnectionFailedEvent(IncompatibleClientException(serverMajor, serverMinor).what());
|
||||
cleanupTimer();
|
||||
cleanupConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("saying hello back with version %s %d.%d", protocolName.c_str(), kProtocolMajorVersion, helloBackMinor);
|
||||
|
||||
// dynamically build write format for hello back since `ProtocolUtil::writef`
|
||||
|
|
@ -691,7 +724,6 @@ void Client::handleHello()
|
|||
ProtocolUtil::writef(m_stream, helloBackMessage.c_str(), kProtocolMajorVersion, helloBackMinor, &m_name);
|
||||
|
||||
// now connected but waiting to complete handshake
|
||||
setupScreen();
|
||||
cleanupTimer();
|
||||
|
||||
// make sure we process any remaining messages later. we won't
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ private:
|
|||
void sendConnectionFailedEvent(const char *msg);
|
||||
void setupConnecting();
|
||||
void setupConnection();
|
||||
void setupScreen();
|
||||
bool setupScreen(int16_t protocolMinor);
|
||||
void setupTimer();
|
||||
void cleanup();
|
||||
void cleanupConnecting();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd
|
||||
* SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Synergy App Ltd
|
||||
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
#include "deskflow/ProtocolTypes.h"
|
||||
#include "deskflow/ProtocolUtil.h"
|
||||
#include "deskflow/StreamChunker.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
#include "io/IStream.h"
|
||||
|
||||
#include <cstring>
|
||||
|
|
@ -140,12 +139,7 @@ ServerProxy::ConnectionResult ServerProxy::parseHandshakeMessage(const uint8_t *
|
|||
setOptions();
|
||||
|
||||
// handshake is complete
|
||||
m_parser = &ServerProxy::parseMessage;
|
||||
|
||||
if (const auto missedKeyboardLayouts = m_layoutManager.getMissedLayouts(); !missedKeyboardLayouts.empty()) {
|
||||
LOG_WARN("server layouts missing on this computer: %s", missedKeyboardLayouts.c_str());
|
||||
ipcSendToClient("missingKeyboardLayouts", QString::fromStdString(missedKeyboardLayouts));
|
||||
}
|
||||
m_parser = &ServerProxy::handleMessage;
|
||||
|
||||
m_client->handshakeComplete();
|
||||
}
|
||||
|
|
@ -196,8 +190,6 @@ ServerProxy::ConnectionResult ServerProxy::parseHandshakeMessage(const uint8_t *
|
|||
LOG_ERR("server disconnected due to a protocol error");
|
||||
requestRefuseConnection(ProtocolError, "server reported a protocol error");
|
||||
return Disconnect;
|
||||
} else if (memcmp(code, kMsgDLanguageSynchronisation, 4) == 0) {
|
||||
setServerLanguages();
|
||||
} else {
|
||||
return Unknown;
|
||||
}
|
||||
|
|
@ -205,6 +197,22 @@ ServerProxy::ConnectionResult ServerProxy::parseHandshakeMessage(const uint8_t *
|
|||
return Okay;
|
||||
}
|
||||
|
||||
ServerProxy::ConnectionResult ServerProxy::handleMessage(const uint8_t *code)
|
||||
{
|
||||
const auto result = parseMessage(code);
|
||||
if (result == ConnectionResult::Okay) {
|
||||
// send a reply. this is intended to work around a delay when
|
||||
// running a linux server and an OS X (any BSD?) client. the
|
||||
// client waits to send an ACK (if the system control flag
|
||||
// net.inet.tcp.delayed_ack is 1) in hopes of piggybacking it
|
||||
// on a data packet. we provide that packet here. i don't
|
||||
// know why a delayed ACK should cause the server to wait since
|
||||
// TCP_NODELAY is enabled.
|
||||
ProtocolUtil::writef(m_stream, kMsgCNoop);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ServerProxy::ConnectionResult ServerProxy::parseMessage(const uint8_t *code)
|
||||
{
|
||||
using enum ConnectionResult;
|
||||
|
|
@ -231,18 +239,6 @@ ServerProxy::ConnectionResult ServerProxy::parseMessage(const uint8_t *code)
|
|||
keyDown(id, mask, button, "");
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
||||
std::string lang;
|
||||
uint16_t id = 0;
|
||||
uint16_t mask = 0;
|
||||
uint16_t button = 0;
|
||||
|
||||
ProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button, &lang);
|
||||
LOG_VERBOSE("recv key down id=0x%08x, mask=0x%04x, button=0x%04x, lang=\"%s\"", id, mask, button, lang.c_str());
|
||||
|
||||
keyDown(id, mask, button, lang);
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
|
||||
keyUp();
|
||||
}
|
||||
|
|
@ -255,8 +251,15 @@ ServerProxy::ConnectionResult ServerProxy::parseMessage(const uint8_t *code)
|
|||
mouseUp();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
||||
keyRepeat();
|
||||
else if (memcmp(code, kMsgDKeyRepeat1_1, 4) == 0) {
|
||||
uint16_t id = 0;
|
||||
uint16_t mask = 0;
|
||||
uint16_t count = 0;
|
||||
uint16_t button = 0;
|
||||
ProtocolUtil::readf(m_stream, kMsgDKeyRepeat1_1 + 4, &id, &mask, &count, &button);
|
||||
LOG_VERBOSE("recv key repeat id=0x%08x, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button);
|
||||
|
||||
keyRepeat(id, mask, count, button, "");
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
|
||||
|
|
@ -305,10 +308,6 @@ ServerProxy::ConnectionResult ServerProxy::parseMessage(const uint8_t *code)
|
|||
setOptions();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDSecureInputNotification, 4) == 0) {
|
||||
secureInputNotification();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
||||
// server wants us to hangup
|
||||
LOG_VERBOSE("recv close");
|
||||
|
|
@ -322,15 +321,6 @@ ServerProxy::ConnectionResult ServerProxy::parseMessage(const uint8_t *code)
|
|||
return Unknown;
|
||||
}
|
||||
|
||||
// send a reply. this is intended to work around a delay when
|
||||
// running a linux server and an OS X (any BSD?) client. the
|
||||
// client waits to send an ACK (if the system control flag
|
||||
// net.inet.tcp.delayed_ack is 1) in hopes of piggybacking it
|
||||
// on a data packet. we provide that packet here. i don't
|
||||
// know why a delayed ACK should cause the server to wait since
|
||||
// TCP_NODELAY is enabled.
|
||||
ProtocolUtil::writef(m_stream, kMsgCNoop);
|
||||
|
||||
return Okay;
|
||||
}
|
||||
|
||||
|
|
@ -521,8 +511,6 @@ void ServerProxy::enter()
|
|||
m_dxMouse = 0;
|
||||
m_dyMouse = 0;
|
||||
m_seqNum = seqNum;
|
||||
m_serverLayout = "";
|
||||
m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
|
||||
// forward
|
||||
m_client->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
|
||||
|
|
@ -588,45 +576,25 @@ void ServerProxy::grabClipboard()
|
|||
|
||||
void ServerProxy::keyDown(uint16_t id, uint16_t mask, uint16_t button, const std::string &lang)
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
setActiveServerLanguage(lang);
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
KeyModifierMask mask2 = translateModifierMask(static_cast<KeyModifierMask>(mask));
|
||||
if (id2 != static_cast<KeyID>(id) || mask2 != static_cast<KeyModifierMask>(mask))
|
||||
LOG_VERBOSE("key down translated to id=0x%08x, mask=0x%04x", id2, mask2);
|
||||
|
||||
// forward
|
||||
m_client->keyDown(id2, mask2, button, lang);
|
||||
}
|
||||
|
||||
void ServerProxy::keyRepeat()
|
||||
void ServerProxy::keyRepeat(uint16_t id, uint16_t mask, uint16_t count, uint16_t button, const std::string &lang)
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
uint16_t id;
|
||||
uint16_t mask;
|
||||
uint16_t count;
|
||||
uint16_t button;
|
||||
std::string lang;
|
||||
ProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4, &id, &mask, &count, &button, &lang);
|
||||
LOG(
|
||||
(CLOG_VERBOSE "recv key repeat id=0x%08x, mask=0x%04x, count=%d, "
|
||||
"button=0x%04x, lang=\"%s\"",
|
||||
id, mask, count, button, lang.c_str())
|
||||
);
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
KeyModifierMask mask2 = translateModifierMask(static_cast<KeyModifierMask>(mask));
|
||||
if (id2 != static_cast<KeyID>(id) || mask2 != static_cast<KeyModifierMask>(mask))
|
||||
LOG_VERBOSE("key repeat translated to id=0x%08x, mask=0x%04x", id2, mask2);
|
||||
|
||||
// forward
|
||||
m_client->keyRepeat(id2, mask2, count, button, lang);
|
||||
}
|
||||
|
||||
|
|
@ -841,38 +809,3 @@ void ServerProxy::infoAcknowledgment()
|
|||
LOG_VERBOSE("recv info acknowledgment");
|
||||
m_ignoreMouse = false;
|
||||
}
|
||||
|
||||
void ServerProxy::secureInputNotification()
|
||||
{
|
||||
std::string app;
|
||||
ProtocolUtil::readf(m_stream, kMsgDSecureInputNotification + 4, &app);
|
||||
LOG_INFO("application \"%s\" is blocking the keyboard", app.c_str());
|
||||
}
|
||||
|
||||
void ServerProxy::setServerLanguages()
|
||||
{
|
||||
std::string serverLayout;
|
||||
ProtocolUtil::readf(m_stream, kMsgDLanguageSynchronisation + 4, &serverLayout);
|
||||
m_layoutManager.setRemoteLayouts(serverLayout);
|
||||
}
|
||||
|
||||
void ServerProxy::setActiveServerLanguage(const std::string_view &language)
|
||||
{
|
||||
if (!language.empty() && (language.size() > 0)) {
|
||||
if (m_serverLayout != language) {
|
||||
m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
m_serverLayout = language;
|
||||
}
|
||||
|
||||
if (!m_layoutManager.isLayoutInstalled(m_serverLayout)) {
|
||||
if (!m_isUserNotifiedAboutLayoutSyncError) {
|
||||
LOG_WARN("current server layout is not installed on client");
|
||||
m_isUserNotifiedAboutLayoutSyncError = true;
|
||||
}
|
||||
} else {
|
||||
m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
}
|
||||
} else {
|
||||
LOG_VERBOSE("active server layout is empty");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd
|
||||
* SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Synergy App Ltd
|
||||
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
|
@ -12,7 +12,6 @@
|
|||
#include "deskflow/ClipboardChunk.h"
|
||||
#include "deskflow/ClipboardTypes.h"
|
||||
#include "deskflow/KeyTypes.h"
|
||||
#include "deskflow/KeyboardLayoutManager.h"
|
||||
|
||||
class Client;
|
||||
class ClientInfo;
|
||||
|
|
@ -23,10 +22,12 @@ class IStream;
|
|||
}
|
||||
class IEventQueue;
|
||||
|
||||
//! Proxy for server
|
||||
//! Proxy for server implementing protocol version 1.6
|
||||
/*!
|
||||
This class acts a proxy for the server, converting calls into messages
|
||||
to the server and messages from the server to calls on the client.
|
||||
It implements 1.6, the oldest version the client supports; later
|
||||
versions subclass it and add their messages.
|
||||
*/
|
||||
class ServerProxy
|
||||
{
|
||||
|
|
@ -38,7 +39,7 @@ public:
|
|||
ServerProxy(Client *client, deskflow::IStream *stream, IEventQueue *events);
|
||||
ServerProxy(ServerProxy const &) = delete;
|
||||
ServerProxy(ServerProxy &&) = delete;
|
||||
~ServerProxy();
|
||||
virtual ~ServerProxy();
|
||||
|
||||
ServerProxy &operator=(ServerProxy const &) = delete;
|
||||
ServerProxy &operator=(ServerProxy &&) = delete;
|
||||
|
|
@ -59,8 +60,16 @@ protected:
|
|||
Unknown,
|
||||
Disconnect
|
||||
};
|
||||
ConnectionResult parseHandshakeMessage(const uint8_t *code);
|
||||
ConnectionResult parseMessage(const uint8_t *code);
|
||||
|
||||
virtual ConnectionResult parseHandshakeMessage(const uint8_t *code);
|
||||
virtual ConnectionResult parseMessage(const uint8_t *code);
|
||||
void keyDown(uint16_t id, uint16_t mask, uint16_t button, const std::string &lang);
|
||||
void keyRepeat(uint16_t id, uint16_t mask, uint16_t count, uint16_t button, const std::string &lang);
|
||||
|
||||
deskflow::IStream *getStream() const
|
||||
{
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
private:
|
||||
// if compressing mouse motion then send the last motion now
|
||||
|
|
@ -77,6 +86,7 @@ private:
|
|||
|
||||
// event handlers
|
||||
void handleData();
|
||||
ConnectionResult handleMessage(const uint8_t *code);
|
||||
void handleKeepAliveAlarm();
|
||||
void requestDisconnect(const char *message);
|
||||
void requestRefuseConnection(deskflow::core::ConnectionRefusal reason, const char *message);
|
||||
|
|
@ -86,8 +96,6 @@ private:
|
|||
void leave();
|
||||
void setClipboard();
|
||||
void grabClipboard();
|
||||
void keyDown(uint16_t id, uint16_t mask, uint16_t button, const std::string &lang);
|
||||
void keyRepeat();
|
||||
void keyUp();
|
||||
void mouseDown();
|
||||
void mouseUp();
|
||||
|
|
@ -99,9 +107,6 @@ private:
|
|||
void setOptions();
|
||||
void queryInfo();
|
||||
void infoAcknowledgment();
|
||||
void secureInputNotification();
|
||||
void setServerLanguages();
|
||||
void setActiveServerLanguage(const std::string_view &language);
|
||||
|
||||
private:
|
||||
using MessageParser = ConnectionResult (ServerProxy::*)(const uint8_t *);
|
||||
|
|
@ -127,9 +132,6 @@ private:
|
|||
|
||||
MessageParser m_parser = &ServerProxy::parseHandshakeMessage;
|
||||
IEventQueue *m_events = nullptr;
|
||||
std::string m_serverLayout = "";
|
||||
std::string m_clipboardDataCached;
|
||||
ClipboardChunkAssemblyState m_clipboardChunkState;
|
||||
bool m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
deskflow::KeyboardLayoutManager m_layoutManager;
|
||||
};
|
||||
|
|
|
|||
37
src/lib/client/ServerProxy1_7.cpp
Normal file
37
src/lib/client/ServerProxy1_7.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "client/ServerProxy1_7.h"
|
||||
|
||||
#include "base/Log.h"
|
||||
#include "deskflow/ProtocolTypes.h"
|
||||
#include "deskflow/ProtocolUtil.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
ServerProxy1_7::ServerProxy1_7(Client *client, deskflow::IStream *stream, IEventQueue *events)
|
||||
: ServerProxy(client, stream, events)
|
||||
{
|
||||
}
|
||||
|
||||
ServerProxy::ConnectionResult ServerProxy1_7::parseMessage(const uint8_t *code)
|
||||
{
|
||||
auto result = ConnectionResult::Okay;
|
||||
if (memcmp(code, kMsgDSecureInputNotification, 4) == 0) {
|
||||
secureInputNotification();
|
||||
} else {
|
||||
result = ServerProxy::parseMessage(code);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ServerProxy1_7::secureInputNotification() const
|
||||
{
|
||||
std::string app;
|
||||
ProtocolUtil::readf(getStream(), kMsgDSecureInputNotification + 4, &app);
|
||||
LOG_INFO("application \"%s\" is blocking the keyboard", app.c_str());
|
||||
}
|
||||
23
src/lib/client/ServerProxy1_7.h
Normal file
23
src/lib/client/ServerProxy1_7.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "client/ServerProxy.h"
|
||||
|
||||
//! Proxy for server implementing protocol version 1.7
|
||||
class ServerProxy1_7 : public ServerProxy
|
||||
{
|
||||
public:
|
||||
ServerProxy1_7(Client *client, deskflow::IStream *stream, IEventQueue *events);
|
||||
~ServerProxy1_7() override = default;
|
||||
|
||||
protected:
|
||||
ConnectionResult parseMessage(const uint8_t *code) override;
|
||||
|
||||
private:
|
||||
void secureInputNotification() const;
|
||||
};
|
||||
103
src/lib/client/ServerProxy1_8.cpp
Normal file
103
src/lib/client/ServerProxy1_8.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "client/ServerProxy1_8.h"
|
||||
|
||||
#include "base/Log.h"
|
||||
#include "deskflow/ProtocolTypes.h"
|
||||
#include "deskflow/ProtocolUtil.h"
|
||||
#include "deskflow/ipc/CoreIpc.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <QString>
|
||||
|
||||
ServerProxy1_8::ServerProxy1_8(Client *client, deskflow::IStream *stream, IEventQueue *events)
|
||||
: ServerProxy1_7(client, stream, events)
|
||||
{
|
||||
}
|
||||
|
||||
ServerProxy::ConnectionResult ServerProxy1_8::parseHandshakeMessage(const uint8_t *code)
|
||||
{
|
||||
auto result = ConnectionResult::Okay;
|
||||
if (memcmp(code, kMsgDLanguageSynchronisation, 4) == 0) {
|
||||
setServerLayouts();
|
||||
} else {
|
||||
result = ServerProxy1_7::parseHandshakeMessage(code);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ServerProxy::ConnectionResult ServerProxy1_8::parseMessage(const uint8_t *code)
|
||||
{
|
||||
auto result = ConnectionResult::Okay;
|
||||
if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
||||
uint16_t id = 0;
|
||||
uint16_t mask = 0;
|
||||
uint16_t button = 0;
|
||||
std::string lang;
|
||||
ProtocolUtil::readf(getStream(), kMsgDKeyDown + 4, &id, &mask, &button, &lang);
|
||||
LOG_VERBOSE("recv key down id=0x%08x, mask=0x%04x, button=0x%04x, lang=\"%s\"", id, mask, button, lang.c_str());
|
||||
|
||||
setActiveServerLayout(lang);
|
||||
keyDown(id, mask, button, lang);
|
||||
} else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
||||
uint16_t id = 0;
|
||||
uint16_t mask = 0;
|
||||
uint16_t count = 0;
|
||||
uint16_t button = 0;
|
||||
std::string lang;
|
||||
ProtocolUtil::readf(getStream(), kMsgDKeyRepeat + 4, &id, &mask, &count, &button, &lang);
|
||||
LOG_VERBOSE(
|
||||
"recv key repeat id=0x%08x, mask=0x%04x, count=%d, button=0x%04x, lang=\"%s\"", id, mask, count, button,
|
||||
lang.c_str()
|
||||
);
|
||||
|
||||
keyRepeat(id, mask, count, button, lang);
|
||||
} else if (memcmp(code, kMsgCEnter, 4) == 0) {
|
||||
m_serverLayout.clear();
|
||||
m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
result = ServerProxy1_7::parseMessage(code);
|
||||
} else {
|
||||
result = ServerProxy1_7::parseMessage(code);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ServerProxy1_8::setServerLayouts()
|
||||
{
|
||||
std::string serverLayouts;
|
||||
ProtocolUtil::readf(getStream(), kMsgDLanguageSynchronisation + 4, &serverLayouts);
|
||||
m_layoutManager.setRemoteLayouts(serverLayouts);
|
||||
|
||||
if (const auto missingLayouts = m_layoutManager.getMissedLayouts(); !missingLayouts.empty()) {
|
||||
LOG_WARN("server layouts missing on this computer: %s", missingLayouts.c_str());
|
||||
ipcSendToClient("missingKeyboardLayouts", QString::fromStdString(missingLayouts));
|
||||
}
|
||||
}
|
||||
|
||||
void ServerProxy1_8::setActiveServerLayout(const std::string_view &layout)
|
||||
{
|
||||
if (layout.empty()) {
|
||||
LOG_VERBOSE("active server layout is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_serverLayout != layout) {
|
||||
m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
m_serverLayout = layout;
|
||||
}
|
||||
|
||||
if (!m_layoutManager.isLayoutInstalled(m_serverLayout)) {
|
||||
if (!m_isUserNotifiedAboutLayoutSyncError) {
|
||||
LOG_WARN("current server layout is not installed on client");
|
||||
m_isUserNotifiedAboutLayoutSyncError = true;
|
||||
}
|
||||
} else {
|
||||
m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
}
|
||||
}
|
||||
33
src/lib/client/ServerProxy1_8.h
Normal file
33
src/lib/client/ServerProxy1_8.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "client/ServerProxy1_7.h"
|
||||
#include "deskflow/KeyboardLayoutManager.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
//! Proxy for server implementing protocol version 1.8
|
||||
class ServerProxy1_8 : public ServerProxy1_7
|
||||
{
|
||||
public:
|
||||
ServerProxy1_8(Client *client, deskflow::IStream *stream, IEventQueue *events);
|
||||
~ServerProxy1_8() override = default;
|
||||
|
||||
protected:
|
||||
ConnectionResult parseHandshakeMessage(const uint8_t *code) override;
|
||||
ConnectionResult parseMessage(const uint8_t *code) override;
|
||||
|
||||
private:
|
||||
void setServerLayouts();
|
||||
void setActiveServerLayout(const std::string_view &layout);
|
||||
|
||||
std::string m_serverLayout;
|
||||
bool m_isUserNotifiedAboutLayoutSyncError = false;
|
||||
deskflow::KeyboardLayoutManager m_layoutManager;
|
||||
};
|
||||
Loading…
Reference in a new issue