From 26cc85e8784b09f84db5112a7356d525ecf12570 Mon Sep 17 00:00:00 2001 From: laz-001 <194351703+laz-001@users.noreply.github.com> Date: Wed, 11 Jun 2025 03:43:10 +0300 Subject: [PATCH] add protocol developer documentation --- doc/dev/CMakeLists.txt | 1 + doc/dev/contributing.md | 2 + doc/dev/mainpage.md | 52 +- doc/dev/protocol_reference.md | 532 ++++++++++ src/lib/deskflow/ProtocolTypes.cpp | 2 + src/lib/deskflow/ProtocolTypes.h | 1459 +++++++++++++++++++++++----- src/lib/server/Config.cpp | 2 + 7 files changed, 1811 insertions(+), 239 deletions(-) create mode 100644 doc/dev/protocol_reference.md diff --git a/doc/dev/CMakeLists.txt b/doc/dev/CMakeLists.txt index f34b440b5..1144e6eb0 100644 --- a/doc/dev/CMakeLists.txt +++ b/doc/dev/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources(dev-docs PRIVATE mainpage.md contributing.md build.md + protocol_reference.md ) # missing install target is intended generate a local copy diff --git a/doc/dev/contributing.md b/doc/dev/contributing.md index 26283af90..57d4967b5 100644 --- a/doc/dev/contributing.md +++ b/doc/dev/contributing.md @@ -1,3 +1,5 @@ +\page contributing_guide Contributing to Deskflow + # Contributing to Deskflow Thanks for your interest in contributing to Deskflow! We welcome all kinds of contributions — bug reports, feature suggestions, documentation improvements, and code. diff --git a/doc/dev/mainpage.md b/doc/dev/mainpage.md index 31978046b..530716128 100644 --- a/doc/dev/mainpage.md +++ b/doc/dev/mainpage.md @@ -2,12 +2,54 @@ Use the keyboard, mouse, or trackpad of one computer to control nearby computers, and work seamlessly between them. -Check out our [Building] guide -or our general [Contributing] section +Deskflow acts as a software KVM (without video) that allows you to: +- Share keyboard and mouse input across multiple computers +- Synchronize clipboard content between machines +- Work seamlessly across different operating systems (Windows, macOS, Linux, BSD) -## More info +Deskflow software consists of a **server** (primary computer) that shares its input devices and **clients** (secondary computers) that receive and execute the input commands over a TCP network connection. + +### Architecture Overview + +Deskflow is built with a modular, cross-platform architecture: + +``` +┌─────────────────┐ Network Protocol ┌─────────────────┐ +│ Server App │◄──────────────────────►│ Client App │ +│ │ (Port 24800) │ (Windows) │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ Screen │ │ │ │ Screen │ │ +│ │ Platform │ │ │ │ Platform │ │ +│ │ Layer │ │ │ │ Layer │ │ +│ └─────────────┘ │ │ └─────────────┘ │ +└─────────────────┘ └─────────────────┘ +┌───────┐ ┌───────┐ +│ Keyb. │ │ Mouse │ +└───────┘ └───────┘ + + ┌─────────────────┐ + │ Client App │ + │ (macOS) │ + │ ┌─────────────┐ │ + │ │ Screen │ │ + │ │ Platform │ │ + │ │ Layer │ │ + │ └─────────────┘ │ + └─────────────────┘ + + ┌─────────────────┐ + │ Client App │ + │ (Custom) │ + │ ┌─────────────┐ │ + │ │ Screen │ │ + │ │ Platform │ │ + │ │ Layer │ │ + │ └─────────────┘ │ + └─────────────────┘ +``` + +### More info For more info, see our [Wiki](https://github.com/deskflow/deskflow/wiki). -[Contributing]:contributing.md -[Building]:build.md +Check out our [Building guide](build.md) or our general @ref contributing_guide "Contributing section". We also have a detailed [Protocol Reference](protocol_reference.md). diff --git a/doc/dev/protocol_reference.md b/doc/dev/protocol_reference.md new file mode 100644 index 000000000..e72c72006 --- /dev/null +++ b/doc/dev/protocol_reference.md @@ -0,0 +1,532 @@ +# Protocol Reference + +This document provides a comprehensive reference for the Deskflow network protocol. It is the primary source of information for developers implementing Deskflow clients or extending the protocol. + +## Protocol Overview + +The Deskflow protocol enables keyboard and mouse sharing between multiple computers over a TCP network connection. The protocol uses two distinct sets of terminology to describe the roles of the computers involved: + +- **Network Role (Client/Server)**: This describes the connection architecture. + - **Server**: The machine that listens for incoming TCP connections. + - **Client**: The machine that initiates the TCP connection to the server. + +- **Input Control Role (Primary/Secondary)**: This describes the flow of keyboard and mouse events. + - **Primary**: The computer whose keyboard and mouse are currently being used to control other computers. + - **Secondary**: A computer that is being controlled by the Primary's keyboard and mouse. + +In a typical setup, the Primary computer (the one whose keyboard and mouse are shared) also acts as the Server. However, the protocol is flexible and allows these roles to be separate. For example, a Primary machine can act as a Client to connect to a Secondary machine that is configured as a Server. This can be useful for navigating restrictive network environments like firewalls. + +Throughout the documentation, message direction is often described using the Primary/Secondary roles to clarify the input control flow, while Client/Server roles are used when discussing the underlying network connection. + +### Key Implementation Files + +- **[ProtocolTypes.h](@ref ProtocolTypes.h)** – Complete protocol specification +- **[ProtocolUtil.h](@ref ProtocolUtil.h)** – Message formatting utilities +- **[ClientInfo](@ref ClientInfo)** – Screen information structure + +The protocol is designed to be: +- Lightweight and efficient +- Cross-platform compatible +- Extensible for new features +- Backward compatible with older versions + +## Protocol Architecture + + +``` +┌─────────────────┐ TCP/IP Network ┌─────────────────┐ +│ Primary │◄────────────────────►│ Secondary │ +│ (Server) │ Port 24800 │ (Client) │ +│ │ │ │ +│ • Shares input │ │ • Receives input│ +│ • Manages layout│ │ • Reports info │ +│ • Coordinates │ │ • Executes cmds │ +└─────────────────┘ └─────────────────┘ +``` + +The protocol operates over a standard TCP connection on port 24800. In protocol versions 1.4 and later, TLS encryption is supported for secure communications. + +## Protocol State Machine + +The client's connection lifecycle is defined by five primary states: + + +``` + ┌──────────────────┐ + │ START │ + └────────┬─────────┘ + │ + ▼ + ┌────────┴─────────┐ + │ DISCONNECTED │ + │ (Initial & │◄───────────────────┐ + │ Final State) │ │ + └────────┬─────────┘ │ + │ │ + ▼ │ + ┌──────────────────┐ │ + │ CONNECTING │ TCP Failure │ + │ (TCP handshake) ├───────────────────►┤ + └────────┬─────────┘ │ + │ │ + TCP Success │ │ + │ │ + ▼ │ + ┌──────────────────┐ │ + │ HANDSHAKE │ Version Mismatch │ + │ (Hello/HelloBk) ├───────────────────►┤ + └────────┬─────────┘ │ + │ │ + OK │ │ + │ │ + ▼ │ + ┌──────────────────┐ │ + │ CONNECTED │ CCLOSE (close) │ + ┌───►│ (Authenticated ├───────────────────►┤ + │ │ but inactive) │ │ + │ └────────┬─────────┘ │ + │ │ │ + COUT │ CINN │ │ + (Leave) │ (Enter)│ │ + │ ▼ │ + │ ┌──────────────────┐ │ + │ │ ACTIVE │ CCLOSE (close) │ + └────┤ (Receiving all ├───────────────────►┘ + │ input events) ├◄─────┐ + └────────┬─────────┘ │ + │ │ + ▼ │ + ┌──────────────────┐ │ + │ PROCESS EVENT ├──────┘ + └──────────────────┘ + + + +``` + +### State Descriptions + +1. **Disconnected**: Initial and final state. No connection to @ref Server. +2. **Connecting**: @ref TCPSocket connection attempt in progress. + - Initiating @ref TCPSocket connection. + - On successful TCP connection, moves to the `Handshake` state. + - If TCP connection fails (timeout, RST packet), returns to `Disconnected`. +3. **Handshake**: Protocol version negotiation and authentication. + - @ref Server sends @ref kMsgHello with protocol version information. + - @ref Client responds with @ref kMsgHelloBack including version and screen name. + - @ref Server validates the client's message. + - Success transitions to `Connected`, failure sends @ref kMsgEIncompatible error. +4. **Connected**: Authenticated but not receiving input events. + - @ref Client must respond to @ref kMsgCKeepAlive messages from the @ref Server. + - Receiving @ref kMsgCEnter message transitions to `Active`. +5. **Active**: Receiving and processing input events from @ref Server. + - Receiving @ref kMsgCLeave message transitions back to `Connected`. + - Receiving @ref kMsgCClose message transitions to `Disconnected`. + + +## Message Categories + +The protocol organizes messages into logical categories: + +| Category | Prefix | Purpose | Examples | +|----------|---------|----------|-----------| +| **[Handshake](@ref protocol_handshake)** | None | Connection setup | @ref kMsgHello, @ref kMsgHelloBack | +| **[Commands](@ref protocol_commands)** | `C` | Screen control | @ref kMsgCEnter, @ref kMsgCLeave, @ref kMsgCKeepAlive | +| **[Data](@ref protocol_data)** | `D` | Input events | @ref kMsgDKeyDown, @ref kMsgDMouseMove, @ref kMsgCClipboard, @ref kMsgDClipboard | +| **[Queries](@ref protocol_queries)** | `Q` | Information requests | @ref kMsgQInfo | +| **[Errors](@ref protocol_errors)** | `E` | Error notifications | @ref kMsgEIncompatible, @ref kMsgEBusy | + +## Message Reference Table + +This table lists all protocol messages in alphabetical order. For a typical sequence of messages, see the [Typical Control Flow](#typical-control-flow) section. + +| Message | Constant | Category | Direction | Purpose | Constraints | Protocol Version | +|---|---|---|---|---|---|---| +| [**CALV**](@ref kMsgCKeepAlive) | @ref kMsgCKeepAlive | Command | Both | Keep-alive | [MsgSize](#constraint-protocol-max-message-length), [KeepAlive](#constraint-keep-alive) | 1.3+ | +| [**CBYE**](@ref kMsgCClose) | @ref kMsgCClose | Command | Server→Client | Close connection | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**CCLP**](@ref kMsgCClipboard) | @ref kMsgCClipboard | Command | Both | Clipboard ownership notification | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**CIAK**](@ref kMsgCInfoAck) | @ref kMsgCInfoAck | Command | Server→Client | Acknowledge info message | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**CINN**](@ref kMsgCEnter) | @ref kMsgCEnter | Command | Server→Client | Enter screen | [MsgSize](#constraint-protocol-max-message-length), [ScreenEntrySync](#constraint-screen-entry-sync) | 1.0+ | +| [**CNOP**](@ref kMsgCNoop) | @ref kMsgCNoop | Command | Both | No operation | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**COUT**](@ref kMsgCLeave) | @ref kMsgCLeave | Command | Server→Client | Leave screen | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**CROP**](@ref kMsgCResetOptions) | @ref kMsgCResetOptions | Command | Server→Client | Reset options to defaults | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**CSEC**](@ref kMsgCScreenSaver) | @ref kMsgCScreenSaver | Command | Server→Client | Screen saver control | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**DCLP**](@ref kMsgDClipboard) | @ref kMsgDClipboard | Data | Both | Clipboard data | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**DDRG**](@ref kMsgDDragInfo) | @ref kMsgDDragInfo | Data | Server→Client | Drag file info | [MsgSize](#constraint-protocol-max-message-length), [ListSize](#constraint-max-list) | 1.5+ | +| [**DFTR**](@ref kMsgDFileTransfer) | @ref kMsgDFileTransfer | Data | Both | File transfer data | [MsgSize](#constraint-protocol-max-message-length) | 1.5+ | +| [**DINF**](@ref kMsgDInfo) | @ref kMsgDInfo | Data | Client→Server | Screen information | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**DKDL**](@ref kMsgDKeyDownLang) | @ref kMsgDKeyDownLang | Data | Server→Client | Key down with language | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.8+ | +| [**DKDN**](@ref kMsgDKeyDown) | @ref kMsgDKeyDown | Data | Server→Client | Key down | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.1+ | +| [**DKDN**](@ref kMsgDKeyDown1_0) | @ref kMsgDKeyDown1_0 | Data | Server→Client | Key down (legacy) | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.0 | +| [**DKRP**](@ref kMsgDKeyRepeat) | @ref kMsgDKeyRepeat | Data | Server→Client | Key repeat | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.1+ | +| [**DKRP**](@ref kMsgDKeyRepeat1_0) | @ref kMsgDKeyRepeat1_0 | Data | Server→Client | Key repeat (legacy) | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.0 | +| [**DKUP**](@ref kMsgDKeyUp) | @ref kMsgDKeyUp | Data | Server→Client | Key up | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.1+ | +| [**DKUP**](@ref kMsgDKeyUp1_0) | @ref kMsgDKeyUp1_0 | Data | Server→Client | Key up (legacy) | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.0 | +| [**DMDN**](@ref kMsgDMouseDown) | @ref kMsgDMouseDown | Data | Server→Client | Mouse down | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**DMMV**](@ref kMsgDMouseMove) | @ref kMsgDMouseMove | Data | Server→Client | Mouse move (absolute) | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**DMRM**](@ref kMsgDMouseRelMove) | @ref kMsgDMouseRelMove | Data | Server→Client | Mouse move (relative) | [MsgSize](#constraint-protocol-max-message-length) | 1.2+ | +| [**DMUP**](@ref kMsgDMouseUp) | @ref kMsgDMouseUp | Data | Server→Client | Mouse up | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**DMWM**](@ref kMsgDMouseWheel) | @ref kMsgDMouseWheel | Data | Server→Client | Mouse wheel | [MsgSize](#constraint-protocol-max-message-length) | 1.3+ | +| [**DMWM**](@ref kMsgDMouseWheel1_0) | @ref kMsgDMouseWheel1_0 | Data | Server→Client | Mouse wheel (legacy) | [MsgSize](#constraint-protocol-max-message-length) | 1.0-1.2 | +| [**DSOP**](@ref kMsgDSetOptions) | @ref kMsgDSetOptions | Data | Server→Client | Set options | [MsgSize](#constraint-protocol-max-message-length), [ListSize](#constraint-max-list) | 1.0+ | +| [**EBAD**](@ref kMsgEBad) | @ref kMsgEBad | Error | Server→Client | Protocol violation | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**EBSY**](@ref kMsgEBusy) | @ref kMsgEBusy | Error | Server→Client | Server busy | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**EICV**](@ref kMsgEIncompatible) | @ref kMsgEIncompatible | Error | Server→Client | Incompatible version | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**EUNK**](@ref kMsgEUnknown) | @ref kMsgEUnknown | Error | Server→Client | Unknown client | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**Hello**](@ref kMsgHello) | @ref kMsgHello | Handshake | Server→Client | Protocol identification | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**HelloArgs**](@ref kMsgHelloArgs) | @ref kMsgHelloArgs | Handshake | Internal | Hello message construction | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**HelloBack**](@ref kMsgHelloBack) | @ref kMsgHelloBack | Handshake | Client→Server | Client identification | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length), [HandshakeTimeout](#constraint-handshake-timeout) | 1.0+ | +| [**HelloBackArgs**](@ref kMsgHelloBackArgs) | @ref kMsgHelloBackArgs | Handshake | Internal | HelloBack message construction | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length), [HandshakeTimeout](#constraint-handshake-timeout) | 1.0+ | +| [**LSYN**](@ref kMsgDLanguageSynchronisation) | @ref kMsgDLanguageSynchronisation | Data | Server→Client | Language synchronization | [MsgSize](#constraint-protocol-max-message-length) | 1.8+ | +| [**QINF**](@ref kMsgQInfo) | @ref kMsgQInfo | Query | Server→Client | Request screen info | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ | +| [**SECN**](@ref kMsgDSecureInputNotification) | @ref kMsgDSecureInputNotification | Data | Server→Client | Secure input notification | [MsgSize](#constraint-protocol-max-message-length) | 1.7+ | + +## Typical Control Flow + + +A typical control flow is as follows: +1. **Handshake**: The server and client exchange `Hello` and `HelloBack` messages to agree on a protocol version. +2. **Information Exchange**: The server requests client information with `QINF`, and the client responds with `DINF`. +3. **Options**: The server sends `DSOP` to configure client options. +4. **Keep-Alive**: The server and client periodically exchange `CALV` messages to maintain the connection. +5. **Screen Entry**: The server sends `CINN` to grant control to the client. +6. **Input Events**: The server sends a stream of input event messages (e.g., `DMMV`, `DMDN`, `DKDN`). +7. **Screen Leave**: The server sends `COUT` to revoke control from the client. +8. **Connection Close**: The server sends `CCLOSE` to terminate the connection. + +## Protocol Constraints + +To ensure security, stability, and compatibility, the protocol enforces strict constraints: + + + +### Message and Data Size Limits + +**Maximum Message Size:** +**4,194,304 bytes (4 MB)** — @ref PROTOCOL_MAX_MESSAGE_LENGTH +Maximum total size of any single, length-prefixed data packet +Defined in Protocol Limits + +**Maximum List Size:** +**1,048,576 elements** — @ref PROTOCOL_MAX_LIST_LENGTH +Maximum number of items in a list/vector within a message +Defined in Protocol Limits + +**Maximum Hello Size:** +**1,024 bytes** — @ref kMaxHelloLength +Maximum size of the initial Connection Handshake message +Defined in Protocol Limits + + + +### TLS Handshake and Security (Protocol v1.4+) + +When encryption is enabled, the protocol follows this sequence: + +1. Standard TCP connection established +2. TLS handshake performed over TCP socket +3. Protocol handshake begins only after TLS session is established + +- **Implementation Details**: + - The client initiates a standard TCP connection, then the @ref SecureSocket::handleTCPConnected method is called, which begins the TLS handshake + +- **Certificate Validation**: + - Client implementations **must** validate the server's certificate + - The reference implementation checks that the public key is RSA or DSA and that the key length is at least 2048 bits + +### Key Code and Modifier Mapping + +A modifier (modifier mask) represents the state of modifier keys (like Shift, Control, Alt, and Command) on a keyboard. It is a binary code (like 0000 0110) where each bit corresponds to a specific modifier key. + +**Key-Up/Key-Down Strategy:** +- Client must use the @ref KeyButton (physical key) to track pressed keys, as the @ref KeyID (virtual key) can change based on modifier state +- This strategy is described in the documentation for @ref kMsgDKeyDown + +**Modifier Remapping:** +- The server can command clients to remap modifier keys via the @ref kMsgDSetOptions message +- The client processes the @ref kMsgDSetOptions message and updates the modifier translation table accordingly + +## Timing and Synchronization + + + +### Keep-Alive Mechanism (Protocol v1.3+) + +**Server-Side Behavior:** +- The server sends kMsgCKeepAlive messages every 3.0 seconds (defined by @ref kKeepAliveRate) +- This timer is implemented in @ref ClientProxy1_3::addHeartbeatTimer in the @ref ClientProxy1_3 class + +**Client-Side Behavior:** +- Upon receiving a kMsgCKeepAlive message, the client must immediately send a kMsgCKeepAlive message back +- The client maintains a timeout that is reset each time any message is received +- If no message is received for 9.0 seconds (3 × @ref kKeepAliveRate), client must disconnect +- This is handled by the @ref ServerProxy::handleKeepAliveAlarm method + + +### Synchronization on Screen Entry + +- The @ref kMsgCEnter (Enter Screen) message includes the current modifier state +- Client must synchronize their local modifier state with this mask + + +### Handshake Timeout + +- Server allows **30 seconds** for handshake completion +- If client fails to send valid @ref kMsgHelloBack within this time, connection is closed +- When a new client connects, the server creates a temporary @ref ClientProxyUnknown to handle the version handshake +- A one-shot timer is started for 30 seconds +- If the client fails to respond in time, the @ref protocol_errors function is triggered, the connection is logged as unresponsive, and the socket is closed + +## Version Compatibility + +| Version | Release Date | Project | Features | Compatibility | +|---------|----------|---------------|----------|---------------| +| **1.0** | May 2001 | Synergy | Basic keyboard/mouse sharing (@ref kMsgDKeyDown, @ref kMsgDMouseMove) | All versions | +| **1.1** | Apr 2002 | Synergy | Physical key codes (@ref KeyButton) | 1.1+ | +| **1.2** | Jan 2006 | Synergy | Relative mouse movement | 1.2+ | +| **1.3** | May 2006 | Synergy | Keep-alive (@ref kMsgCKeepAlive), horizontal scroll (@ref kMsgDMouseWheel) | 1.3+ | +| **1.4** | Nov 2012 | Synergy | Encryption support (@ref SecureSocket) | 1.4+ | +| **1.5** | Sep 2013 | Synergy | File transfer | 1.5+ | +| **1.6** | Jan 2014 | Synergy | Clipboard streaming | 1.6+ | +| **1.7** | Nov 2021 | Synergy | Secure input notifications | 1.7+ | +| **1.8** | Jun 2025 | Synergy | Language synchronization | 1.8+ | + +### Version Migration Guide + +When implementing a client that supports multiple protocol versions: + +1. **Version Negotiation**: During handshake, client should advertise highest supported version +2. **Feature Detection**: Check server's version in `Hello` message before using version-specific features +3. **Fallback Mechanism**: Be prepared to operate with only features available in the negotiated version +4. **Graceful Degradation**: If server supports a lower version than client's minimum, handle `EIncompatible` error gracefully + +## Implementation Examples + +### Connection Lifecycle + +```cpp +// 1. Connect to server +std::string server_ip = "192.168.1.100"; +connect(server_ip, 24800); + +// 2. Receive Hello from server +auto hello = receive_message(); +std::string server_version, server_name; +parse_hello(hello, &server_version, &server_name); + +// 3. Send HelloBack to server +std::string client_version = "1.8"; +std::string client_name = "MyClient"; +send_hello_back(client_version, client_name); + +// 4. Enter main message loop +bool connected = true; +while (connected) { + auto message = receive_message(); + handle_message(message); +} +``` + +### Message Handling + +```cpp +void handle_message(const Message& msg) { + switch (msg.type) { + case "CINN": // Enter screen + handle_enter(msg.x, msg.y, msg.sequence, msg.modifiers); + break; + case "DKDN": // Key down + handle_key_down(msg.key_id, msg.modifiers, msg.key_button); + break; + case "DMMV": // Mouse move + handle_mouse_move(msg.x, msg.y); + break; + // ... handle other message types + } +} +``` + +### Complete Message Exchange Sequence + +Below is a typical message exchange sequence for a client connecting to a server: + + + +``` +Client Server + | | + | | Server starts listening on port 24800 + | | + | TCP SYN | + | ───────────────────────────────────► | + | | + | ◄─────────────────────────────────── | + | TCP SYN+ACK | + | | + | TCP ACK | + | ───────────────────────────────────► | + | | TCP connection established + | | + | ◄─────────────────────────────────── | + | "Deskflow" + version (1.8) | Hello message + | | + | "Deskflow" + version + name | + | ───────────────────────────────────► | HelloBack message + | | + | ◄─────────────────────────────────── | + | "QINF" | Query screen info + | | + | "DINF" + screen dimensions | + | ───────────────────────────────────► | Report screen info + | | + | ◄─────────────────────────────────── | + | "DSOP" + options | Set options + | | + | ◄─────────────────────────────────── | + | "CALV" | Keep-alive + | | + | "CALV" | + | ───────────────────────────────────► | Keep-alive response + | | + | ◄─────────────────────────────────── | + | "CINN" + x + y + seq + mask | Enter screen + | | + | ◄─────────────────────────────────── | + | "DMMV" + x + y | Mouse move + | | + | ◄─────────────────────────────────── | + | "DMDN" + button | Mouse down + | | + | ◄─────────────────────────────────── | + | "DMUP" + button | Mouse up + | | + | ◄─────────────────────────────────── | + | "DKDN" + key + mask + button | Key down + | | + | ◄─────────────────────────────────── | + | "DKUP" + key + mask + button | Key up + | | + | ◄─────────────────────────────────── | + | "COUT" | Leave screen + | | + | ◄─────────────────────────────────── | + | "CCLOSE" | Close connection + | | + | TCP FIN | + | ◄─────────────────────────────────── | + | | + | TCP FIN+ACK | + | ───────────────────────────────────► | + | | Connection closed +``` + +**Legend:** + +- Hello message: @ref kMsgHello +- HelloBack message: @ref kMsgHelloBack +- Query screen info: @ref kMsgQInfo +- Report screen info: @ref kMsgDInfo +- Set options: @ref kMsgDSetOptions +- Keep-alive: @ref kMsgCKeepAlive +- Enter screen: @ref kMsgCEnter +- Mouse move: @ref kMsgDMouseMove +- Mouse down: @ref kMsgDMouseDown +- Mouse up: @ref kMsgDMouseUp +- Key down: @ref kMsgDKeyDown +- Key up: @ref kMsgDKeyUp +- Leave screen: @ref kMsgCLeave +- Close connection: @ref kMsgCClose + +## Debugging and Troubleshooting + +### Common Issues + +1. **Version Mismatch**: Check protocol version negotiation +2. **Message Format**: Validate message structure and parameters +3. **Byte Order**: Ensure network byte order for multi-byte integers +4. **Keep-Alive**: Implement proper keep-alive response +5. **Screen Info**: Send accurate screen dimensions and mouse position + +### Debug Tools + +- **Wireshark**: Capture and analyze network traffic +- **Protocol Logs**: Enable verbose logging in Deskflow +- **Message Validation**: Check message format against specification + +## Platform-Specific Implementations + +For platform-specific implementation details, refer to: +- @ref ProtocolTypes.h – Complete protocol specification +- @ref ProtocolUtil.h – Message formatting utilities + +## Implementation Checklist + +### Basic Client Implementation + +- [ ] **Connection Management** + - [ ] TCP connection to server port 24800 + - [ ] Protocol handshake (Hello/HelloBack) + - [ ] Version negotiation + - [ ] Keep-alive handling + +- [ ] **Message Processing** + - [ ] Message parsing and validation + - [ ] Command message handling (Enter/Leave) + - [ ] Input event processing (keyboard/mouse) + - [ ] Error handling and recovery + +- [ ] **Screen Management** + - [ ] Screen information reporting (DINF) + - [ ] Resolution change detection + - [ ] Mouse cursor positioning + +- [ ] **Input Synthesis** + - [ ] Keyboard event injection + - [ ] Mouse event injection + - [ ] Modifier key synchronization + +### Advanced Features + +- [ ] **Clipboard Synchronization** + - [ ] Clipboard grab notifications + - [ ] Data transfer (@ref kMsgDClipboard - text, images, HTML) + - [ ] Streaming for large data (v1.6+) + +- [ ] **File Transfer** (v1.5+) + - [ ] Drag-and-drop initiation + - [ ] Chunked file transfer + - [ ] Progress tracking + +- [ ] **Security Features** + - [ ] TLS/SSL encryption (v1.4+) + - [ ] Secure input notifications (v1.7+) + - [ ] Input validation and limits + +## Reference Implementation + +The @ref ServerProxy class provides a reference implementation demonstrating: + +- Basic protocol handling +- Message parsing and generation +- Connection management +- Input event processing + + +## Contributing + +When extending the protocol: + +1. **Maintain Compatibility**: New features should be backward compatible +2. **Update Documentation**: Document new messages and parameters +3. **Version Increment**: Bump minor version for new features +4. **Test Thoroughly**: Verify with existing clients and servers + +## Support and Resources + + +- @ref ProtocolTypes.h – Complete protocol specification +- @ref ProtocolUtil.h – Message formatting utilities + +--- + +*This documentation is generated from the source code and is always up-to-date with the latest protocol implementation.* \ No newline at end of file diff --git a/src/lib/deskflow/ProtocolTypes.cpp b/src/lib/deskflow/ProtocolTypes.cpp index 15e277e7f..c47cd1cf2 100644 --- a/src/lib/deskflow/ProtocolTypes.cpp +++ b/src/lib/deskflow/ProtocolTypes.cpp @@ -7,6 +7,8 @@ #include "deskflow/ProtocolTypes.h" +// Protocol names used in network handshake messages (capitalized, must be exactly 7 chars). +// Note that @ref kSynergyProtocolOption / @ref kBarrierProtocolOption use lowercase names. const char *const kSynergyProtocolName = "Synergy"; const char *const kBarrierProtocolName = "Barrier"; diff --git a/src/lib/deskflow/ProtocolTypes.h b/src/lib/deskflow/ProtocolTypes.h index 93f423b53..fca69018d 100644 --- a/src/lib/deskflow/ProtocolTypes.h +++ b/src/lib/deskflow/ProtocolTypes.h @@ -11,365 +11,1356 @@ #include -// protocol version number -// 1.0: initial protocol -// 1.1: adds KeyCode to key press, release, and repeat -// 1.2: adds mouse relative motion -// 1.3: adds keep alive and deprecates heartbeats, -// adds horizontal mouse scrolling -// 1.4: adds crypto support -// 1.5: adds file transfer and removes home brew crypto -// 1.6: adds clipboard streaming -// 1.7 adds security input notifications -// 1.8 adds language synchronization functionality -// NOTE: with new version, deskflow minor version should increment +/** + * @file ProtocolTypes.h + * @brief Deskflow Network Protocol Specification and Implementation + * + * @section protocol_overview Protocol Overview + * + * This file defines the data types, constants, and message structures for the + * Deskflow network protocol. For a high-level conceptual overview of the protocol, + * including the distinction between Client/Server and Primary/Secondary roles, + * please see the **Protocol Reference** document in `doc/dev/protocol_reference.md`. + */ + +/** + * @defgroup protocol_constants Protocol Constants + * @brief Core protocol version and configuration constants + * @{ + */ + +/** + * @brief Protocol major version number + * + * The major version indicates fundamental protocol compatibility. + * Clients and servers with different major versions cannot communicate. + * + * @since Protocol version 1.0 + */ static const int16_t kProtocolMajorVersion = 1; + +/** + * @brief Protocol minor version number + * + * The minor version indicates feature availability within the same major version. + * Higher minor versions are backward compatible with lower minor versions. + * + * @note When incrementing the minor version, the Deskflow application version should also increment + * @since Protocol version 1.0 + */ static const int16_t kProtocolMinorVersion = 8; -// default contact port number +/** + * @brief Default TCP port for Deskflow connections + * + * Clients connect to this port on the server by default. + * Can be overridden in configuration. + * + * @since Protocol version 1.0 + */ static const uint16_t kDefaultPort = 24800; -// maximum total length for greeting returned by client +/** + * @brief Maximum length for client greeting message + * + * Limits the size of the hello message sent by clients during handshake. + * Prevents memory exhaustion attacks during connection establishment. + * + * @since Protocol version 1.0 + */ static const uint32_t kMaxHelloLength = 1024; -// time between kMsgCKeepAlive (in seconds). a non-positive value disables -// keep alives. this is the default rate that can be overridden using an -// option. +/** + * @brief Keep-alive message interval in seconds + * + * Time between kMsgCKeepAlive messages sent by the server. + * A non-positive value disables keep-alives. + * This is the default rate that can be overridden using options. + * + * @see kMsgCKeepAlive + * @since Protocol version 1.3 + */ static const double kKeepAliveRate = 3.0; -// number of skipped kMsgCKeepAlive messages that indicates a problem +/** + * @brief Keep-alive timeout threshold + * + * Number of consecutive missed kMsgCKeepAlive messages that indicates + * a connection problem. After this many missed messages, the connection + * is considered dead and will be terminated. + * + * @see kMsgCKeepAlive + * @since Protocol version 1.3 + */ static const double kKeepAlivesUntilDeath = 3.0; -// obsolete heartbeat stuff +/** + * @brief Obsolete heartbeat rate (deprecated) + * + * @deprecated Since protocol version 1.3, replaced by keep-alive mechanism + * @see kKeepAliveRate + */ static const double kHeartRate = -1.0; + +/** + * @brief Obsolete heartbeat timeout (deprecated) + * + * @deprecated Since protocol version 1.3, replaced by keep-alive mechanism + * @see kKeepAlivesUntilDeath + */ static const double kHeartBeatsUntilDeath = 3.0; -// Messages of very large size indicate a likely protocol error. We don't parse such messages and -// drop connection instead. Note that e.g. the clipboard messages are already limited to 32kB. +/** + * @brief Maximum allowed message length + * + * Messages exceeding this size indicate a likely protocol error. + * Such messages are not parsed and cause connection termination. + * This prevents memory exhaustion attacks. + * + * @note Clipboard messages are separately limited to 32KB chunks + * @since Protocol version 1.0 + */ static constexpr uint32_t PROTOCOL_MAX_MESSAGE_LENGTH = 4 * 1024 * 1024; + +/** + * @brief Maximum allowed list length in protocol messages + * + * Limits the size of lists (arrays) in protocol messages to prevent + * memory exhaustion attacks. + * + * @since Protocol version 1.0 + */ static constexpr uint32_t PROTOCOL_MAX_LIST_LENGTH = 1024 * 1024; + +/** + * @brief Maximum allowed string length in protocol messages + * + * Limits the size of strings in protocol messages to prevent + * memory exhaustion attacks. + * + * @since Protocol version 1.0 + */ static constexpr uint32_t PROTOCOL_MAX_STRING_LENGTH = 1024 * 1024; -// direction constants +/** @} */ // end of protocol_constants group + +/** + * @defgroup protocol_enums Protocol Enumerations + * @brief Enumeration types used in protocol messages + * @{ + */ + +/** + * @brief Screen edge directions for mouse movement + * + * Used to specify which edge of a screen the mouse cursor crosses + * when moving between primary and secondary screens. + * + * @since Protocol version 1.0 + */ enum EDirection { - kNoDirection, - kLeft, - kRight, - kTop, - kBottom, - kFirstDirection = kLeft, - kLastDirection = kBottom, - kNumDirections = kLastDirection - kFirstDirection + 1 + kNoDirection, ///< No specific direction + kLeft, ///< Left edge of screen + kRight, ///< Right edge of screen + kTop, ///< Top edge of screen + kBottom, ///< Bottom edge of screen + kFirstDirection = kLeft, ///< First valid direction value + kLastDirection = kBottom, ///< Last valid direction value + kNumDirections = kLastDirection - kFirstDirection + 1 ///< Total number of directions }; + +/** + * @brief Bitmask values for screen edge directions + * + * Used to create bitmasks representing multiple screen edges. + * Useful for configuration and edge detection. + * + * @since Protocol version 1.0 + */ enum EDirectionMask { - kNoDirMask = 0, - kLeftMask = 1 << kLeft, - kRightMask = 1 << kRight, - kTopMask = 1 << kTop, - kBottomMask = 1 << kBottom + kNoDirMask = 0, ///< No direction mask + kLeftMask = 1 << kLeft, ///< Left edge mask + kRightMask = 1 << kRight, ///< Right edge mask + kTopMask = 1 << kTop, ///< Top edge mask + kBottomMask = 1 << kBottom ///< Bottom edge mask }; -// Data transfer constants +/** + * @brief File transfer data chunk types + * + * Used in kMsgDFileTransfer messages to indicate the type of data + * being transferred in each chunk. Used in clipboard operations. + * + * @since Protocol version 1.5 + */ enum EDataTransfer { - kDataStart = 1, - kDataChunk = 2, - kDataEnd = 3 + kDataStart = 1, ///< Start of transfer (contains file size) + kDataChunk = 2, ///< Data chunk (contains file content) + kDataEnd = 3 ///< End of transfer (transfer complete) }; -// Data received constants +/** + * @brief Data reception status codes + * + * Used internally to track the status of data reception + * during clipboard operations. + * + * @since Protocol version 1.5 + */ enum EDataReceived { - kStart, - kNotFinish, - kFinish, - kError + kStart, ///< Reception started + kNotFinish, ///< Reception in progress + kFinish, ///< Reception completed successfully + kError ///< Reception failed with error }; -// -// message codes (trailing NUL is not part of code). in comments, $n -// refers to the n'th argument (counting from one). message codes are -// always 4 bytes optionally followed by message specific parameters -// except those for the greeting handshake. -// +/** @} */ // end of protocol_enums group -// -// positions and sizes are signed 16 bit integers. -// +/** + * @defgroup protocol_messages Protocol Message Definitions + * @brief All protocol message types and their format specifications + * + * In parameter descriptions, `$n` refers to the n\'th argument (counting from one). + * + * Messages are categorized by their first letter: + * - **Greeting**: Connection handshake messages (no prefix) + * - **C**: Command messages (server → client control) + * - **D**: Data messages (input events, clipboard, files) + * - **Q**: Query messages (information requests) + * - **E**: Error messages (protocol violations, failures) + * + * @{ + */ -// -// greeting handshake messages -// +/** + * @defgroup protocol_handshake Handshake Messages + * @brief Connection establishment and version negotiation + * @{ + */ -// used to say hello back to the server as Synergy +/** + * @brief Protocol name for Synergy compatibility + * + * Used in handshake messages to identify the protocol as Synergy-compatible. + * Must be exactly 7 characters for backward compatibility. + * + * @since Protocol version 1.0 + */ extern const char *const kSynergyProtocolName; -// used to say hello back to the server as Barrier +/** + * @brief Protocol name for Barrier compatibility + * + * Used in handshake messages to identify the protocol as Barrier-compatible. + * Must be exactly 7 characters for backward compatibility. + * + * @since Protocol version 1.0 + */ extern const char *const kBarrierProtocolName; -// say hello to client; primary -> secondary -// $1 = protocol major version number supported by server. $2 = -// protocol minor version number supported by server. $3 = server -// keyboard layout list. +/** + * @brief Server hello message + * + * **Direction**: Primary → Secondary + * **Format**: `"%7s%2i%2i"` + * **Parameters**: + * - `$1`: Protocol name (7 bytes, fixed-size) - A fixed-size field for the protocol + * identifier, used for backward compatibility. The supported values are "Synergy" or + * "Barrier". "Barrier" is the default protocol name, while "Synergy" is used only + * for backwards compatibility. This is an exception to the standard length-prefixed string format. + * - `$2`: Server major version number (2 bytes) + * - `$3`: Server minor version number (2 bytes) + * + * **Example**: + * ``` + * "Barrier\x00\x01\x00\x08" // Barrier protocol, version 1.8 + * ``` + * + * This is the first message sent by the server after a client connects. + * The client uses this to determine protocol compatibility. + * + * @see kMsgHelloBack + * @since Protocol version 1.0 + */ extern const char *const kMsgHello; -// args part of kMsgHello. -// used as part of a dynamic hello message. -// this can be superseded by kMsgHello once `ProtocolUtil::writef` -// supports fixed length strings (e.g. %7s). +/** + * @brief Format string for server hello message arguments + * + * **Format**: `"%2i%2i"` + * **Parameters**: + * - `$1`: Server major version number (2 bytes) + * - `$2`: Server minor version number (2 bytes) + * + * Used as part of dynamic hello message construction. + * + * @see kMsgHello + * @since Protocol version 1.0 + */ extern const char *const kMsgHelloArgs; -// respond to hello from server; secondary -> primary -// $1 = protocol major version number supported by client. $2 = -// protocol minor version number supported by client. $3 = client -// name. +/** + * @brief Client hello response message + * + * **Direction**: Secondary → Primary + * **Format**: `"%7s%2i%2i%s"` + * **Parameters**: + * - `$1`: Protocol name (7 bytes, fixed-size) - A fixed-size field for the protocol + * identifier, which must match the server's. The supported values are "Synergy" or + * "Barrier". "Barrier" is the default protocol name, while "Synergy" is used only + * for backwards compatibility. This is an exception to the standard length-prefixed string format. + * - `$2`: Client major version number (2 bytes) + * - `$3`: Client minor version number (2 bytes) + * - `$4`: Client name (string) - A standard length-prefixed string. + * + * **Example**: + * ``` + * "Barrier\x00\x01\x00\x08\x00\x00\x00\x0Bworkstation" + * // Barrier protocol, version 1.8, client name "workstation" + * ``` + * + * Sent by the client in response to kMsgHello. After this exchange, + * both sides negotiate to use the minimum supported protocol version. + * + * @see kMsgHello + * @since Protocol version 1.0 + */ extern const char *const kMsgHelloBack; -// args part of kMsgHelloBack. -// used as part of a dynamic hello message. -// this can be superseded by kMsgHelloBack once `ProtocolUtil::writef` -// supports fixed length strings (e.g. %7s). +/** + * @brief Format string for client hello response arguments + * + * **Format**: `"%2i%2i%s"` + * **Parameters**: + * - `$1`: Client major version number (2 bytes) + * - `$2`: Client minor version number (2 bytes) + * - `$3`: Client name (string) + * + * Used as part of dynamic hello message construction. + * + * @see kMsgHelloBack + * @since Protocol version 1.0 + */ extern const char *const kMsgHelloBackArgs; -// -// command codes -// +/** @} */ // end of protocol_handshake group -// no operation; secondary -> primary +/** + * @defgroup protocol_commands Command Messages + * @brief Control messages for screen management and connection maintenance + * @{ + */ + +/** + * @brief No operation command + * + * **Message Code**: `"CNOP"` + * **Direction**: Secondary → Primary + * **Format**: No parameters + * + * A no-operation message that can be used for testing connectivity + * or as a placeholder. Has no effect on the receiving end. + * + * @since Protocol version 1.0 + */ extern const char *const kMsgCNoop; -// close connection; primary -> secondary +/** + * @brief Close connection command + * + * **Message Code**: `"CBYE"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Instructs the client to close the connection gracefully. + * The client should clean up resources and disconnect. + * + * @since Protocol version 1.0 + */ extern const char *const kMsgCClose; -// enter screen: primary -> secondary -// entering screen at screen position $1 = x, $2 = y. x,y are -// absolute screen coordinates. $3 = sequence number, which is -// used to order messages between screens. the secondary screen -// must return this number with some messages. $4 = modifier key -// mask. this will have bits set for each toggle modifier key -// that is activated on entry to the screen. the secondary screen -// should adjust its toggle modifiers to reflect that state. +/** + * @brief Enter screen command + * + * **Message Code**: `"CINN"` + * **Direction**: Primary → Secondary + * **Format**: `"CINN%2i%2i%4i%2i"` + * **Parameters**: + * - `$1`: Entry X coordinate (2 bytes, signed) + * - `$2`: Entry Y coordinate (2 bytes, signed) + * - `$3`: Sequence number (4 bytes, unsigned) + * - `$4`: Modifier key mask (2 bytes, unsigned) + * + * **Example**: + * ``` + * "CINN\x01\x90\x01\x2C\x00\x00\x00\x01\x00\x00" + * // Enter at (400, 300), sequence 1, no modifiers + * ``` + * + * Sent when the mouse cursor enters the secondary screen from the primary. + * The coordinates specify the exact entry point. The sequence number is used + * to order messages and must be returned in subsequent messages from the client. + * The modifier mask indicates which toggle keys (Caps Lock, Num Lock, etc.) + * are active and should be synchronized on the secondary screen. + * + * @see kMsgCLeave + * @since Protocol version 1.0 + */ extern const char *const kMsgCEnter; -// leave screen: primary -> secondary -// leaving screen. the secondary screen should send clipboard -// data in response to this message for those clipboards that -// it has grabbed (i.e. has sent a kMsgCClipboard for and has -// not received a kMsgCClipboard for with a greater sequence -// number) and that were grabbed or have changed since the -// last leave. +/** + * @brief Leave screen command + * + * **Message Code**: `"COUT"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Sent when the mouse cursor leaves the secondary screen and returns to the primary. + * Upon receiving this message, the secondary screen should: + * 1. Send clipboard data for any clipboards it has grabbed + * 2. Only send clipboards that have changed since the last leave + * 3. Use the sequence number from the most recent kMsgCEnter + * + * @see kMsgCEnter, kMsgCClipboard + * @since Protocol version 1.0 + */ extern const char *const kMsgCLeave; -// grab clipboard: primary <-> secondary -// sent by screen when some other app on that screen grabs a -// clipboard. $1 = the clipboard identifier, $2 = sequence number. -// secondary screens must use the sequence number passed in the -// most recent kMsgCEnter. the primary always sends 0. +/** + * @brief Clipboard grab notification + * + * **Message Code**: `"CCLP"` + * **Direction**: Primary ↔ Secondary + * **Format**: `"CCLP%1i%4i"` + * **Parameters**: + * - `$1`: Clipboard identifier (1 byte) + * - `$2`: Sequence number (4 bytes) + * + * **Example**: + * ``` + * "CCLP\x00\x00\x00\x00\x01" + * // Primary clipboard grabbed, sequence 1 + * ``` + * + * Sent when an application grabs a clipboard on either screen. + * This notifies the other screen that clipboard ownership has changed. + * Secondary screens must use the sequence number from the most recent + * kMsgCEnter. The primary always sends sequence number 0. + * + * **Clipboard Identifiers**: + * - `0`: Primary clipboard (Ctrl+C/Ctrl+V) + * - `1`: Selection clipboard (middle-click on X11) + * + * @see kMsgDClipboard + * @since Protocol version 1.0 + */ extern const char *const kMsgCClipboard; -// screensaver change: primary -> secondary -// screensaver on primary has started ($1 == 1) or closed ($1 == 0) +/** + * @brief Screensaver state change + * + * **Message Code**: `"CSEC"` + * **Direction**: Primary → Secondary + * **Format**: `"CSEC%1i"` + * **Parameters**: + * - `$1`: Screensaver state (1 byte): 1 = started, 0 = stopped + * + * **Example**: + * ``` + * "CSEC\x01" // Screensaver started + * "CSEC\x00" // Screensaver stopped + * ``` + * + * Notifies the secondary screen when the primary's screensaver + * starts or stops. The secondary can use this to synchronize + * its own screensaver state. + * + * @since Protocol version 1.0 + */ extern const char *const kMsgCScreenSaver; -// reset options: primary -> secondary -// client should reset all of its options to their defaults. +/** + * @brief Reset options command + * + * **Message Code**: `"CROP"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Instructs the client to reset all of its options to their default values. + * This is typically sent when the server configuration changes. + * + * @see kMsgDSetOptions + * @since Protocol version 1.0 + */ extern const char *const kMsgCResetOptions; -// resolution change acknowledgment: primary -> secondary -// sent by primary in response to a secondary screen's kMsgDInfo. -// this is sent for every kMsgDInfo, whether or not the primary -// had sent a kMsgQInfo. +/** + * @brief Screen information acknowledgment + * + * **Message Code**: `"CIAK"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Sent by the primary in response to a secondary screen's kMsgDInfo message. + * This acknowledgment is sent for every kMsgDInfo, whether or not the + * primary had previously sent a kMsgQInfo query. + * + * @see kMsgDInfo, kMsgQInfo + * @since Protocol version 1.0 + */ extern const char *const kMsgCInfoAck; -// keep connection alive: primary <-> secondary -// sent by the server periodically to verify that connections are still -// up and running. clients must reply in kind on receipt. if the server -// gets an error sending the message or does not receive a reply within -// a reasonable time then the server disconnects the client. if the -// client doesn't receive these (or any message) periodically then it -// should disconnect from the server. the appropriate interval is -// defined by an option. +/** + * @brief Keep-alive message + * + * **Message Code**: `"CALV"` + * **Direction**: Primary ↔ Secondary + * **Format**: No parameters + * + * Sent periodically by the server to verify that connections are still + * active. Clients must reply with the same message upon receipt. + * + * **Timing**: + * - Default interval: 3 seconds (configurable) + * - Timeout: 3 missed messages trigger disconnection + * + * **Behavior**: + * - Server sends keep-alive to client + * - Client immediately responds with keep-alive back to server + * - If server doesn't receive response within timeout, it disconnects client + * - If client doesn't receive keep-alives, it should disconnect from server + * + * @see kKeepAliveRate, kKeepAlivesUntilDeath + * @since Protocol version 1.3 + */ extern const char *const kMsgCKeepAlive; -// -// data codes -// -// The same as kMsgDKeyDown but with languageCode -// $4 = languageCode +/** @} */ // end of protocol_commands group + +/** + * @defgroup protocol_data Data Messages + * @brief Input events, clipboard data, and file transfer messages + * @{ + */ + +/** + * @defgroup protocol_keyboard Keyboard Messages + * @brief Keyboard input event messages + * @{ + */ + +/** + * @brief Key press with language code (v1.8+) + * + * **Message Code**: `"DKDL"` + * **Direction**: Primary → Secondary + * **Format**: `"DKDL%2i%2i%2i%s"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier, often called a "keysym" on Linux/X11. This is platform-dependent + * and corresponds to values like `XK_a` on X11/Linux, `'a'` on macOS, and `'A'` on Windows. + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * - `$3`: KeyButton (2 bytes) - Physical key code, often called a "keycode" or "scancode". This is the raw, + * platform-dependent scan code of the key pressed. + * - `$4`: Language code (string) - Keyboard language identifier + * + * **Example**: + * ``` + * "DKDL\x00\x61\x00\x00\x00\x1E\x00\x00\x00\x02en" + * // 'a' key (KeyID 0x61), no modifiers, physical key (KeyButton 0x1E), English + * ``` + * + * Enhanced version of kMsgDKeyDown that includes language information + * to help clients handle unknown language characters correctly. + * + * @see kMsgDKeyDown + * @since Protocol version 1.8 + */ extern const char *const kMsgDKeyDownLang; -// key pressed: primary -> secondary -// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton -// the KeyButton identifies the physical key on the primary used to -// generate this key. the secondary should note the KeyButton along -// with the physical key it uses to generate the key press. on -// release, the secondary can then use the primary's KeyButton to -// find its corresponding physical key and release it. this is -// necessary because the KeyID on release may not be the KeyID of -// the press. this can happen with combining (dead) keys or if -// the keyboard layouts are not identical and the user releases -// a modifier key before releasing the modified key. -// languageCode is parameter which helps client to react on unknwon -// language letters +/** + * @brief Key press event + * + * **Message Code**: `"DKDN"` + * **Direction**: Primary → Secondary + * **Format**: `"DKDN%2i%2i%2i"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier, often called a "keysym" on Linux/X11. This is platform-dependent + * and corresponds to values like `XK_a` on X11/Linux, `'a'` on macOS, and `'A'` on Windows. + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * - `$3`: KeyButton (2 bytes) - Physical key code, often called a "keycode" or "scancode". This is the raw, + * platform-dependent scan code of the key pressed. + * + * **Example**: + * ``` + * "DKDN\x00\x61\x00\x00\x00\x1E" + * // 'a' key (KeyID 0x61), no modifiers, physical key (KeyButton 0x1E) + * ``` + * + * **Key Mapping Strategy**: + * The KeyButton parameter is crucial for proper key release handling. + * The secondary screen should: + * 1. Map the KeyID to its local virtual key + * 2. Remember the association between KeyButton and the local physical key + * 3. Use KeyButton (not KeyID) to identify which key to release + * + * This is necessary because: + * - Dead keys can change the KeyID between press and release + * - Different keyboard layouts may produce different KeyIDs + * - Modifier keys released before the main key can alter KeyID + * + * @see kMsgDKeyUp, kMsgDKeyDownLang + * @since Protocol version 1.1 + */ extern const char *const kMsgDKeyDown; -// key pressed 1.0: same as above but without KeyButton +/** + * @brief Key press event (legacy v1.0) + * + * **Message Code**: `"DKDN"` + * **Direction**: Primary → Secondary + * **Format**: `"DKDN%2i%2i"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * + * Legacy version without KeyButton parameter. Used only when + * communicating with protocol version 1.0 clients. + * + * @deprecated Use kMsgDKeyDown for protocol version 1.1+ + * @see kMsgDKeyDown + * @since Protocol version 1.0 + */ extern const char *const kMsgDKeyDown1_0; -// key auto-repeat: primary -> secondary -// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton -// $5 =language code +/** + * @brief Key auto-repeat event + * + * **Message Code**: `"DKRP"` + * **Direction**: Primary → Secondary + * **Format**: `"DKRP%2i%2i%2i%2i%s"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier, often called a "keysym" on Linux/X11. This is platform-dependent + * and corresponds to values like `XK_a` on X11/Linux, `'a'` on macOS, and `'A'` on Windows. + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * - `$3`: Repeat count (2 bytes) - Number of repeats + * - `$4`: KeyButton (2 bytes) - Physical key code, often called a "keycode" or "scancode". This is the raw, + * platform-dependent scan code of the key pressed. + * - `$5`: Language code (string) - Keyboard language identifier + * + * **Example**: + * ``` + * "DKRP\x00\x61\x00\x00\x00\x03\x00\x1E\x00\x00\x00\x02en" + * // 'a' key repeating 3 times, English layout + * ``` + * + * Sent when a key is held down and auto-repeating. The repeat count + * indicates how many repeat events occurred since the last message. + * + * @see kMsgDKeyDown + * @since Protocol version 1.1 + */ extern const char *const kMsgDKeyRepeat; -// key auto-repeat 1.0: same as above but without KeyButton +/** + * @brief Key auto-repeat event (legacy v1.0) + * + * **Message Code**: `"DKRP"` + * **Direction**: Primary → Secondary + * **Format**: `"DKRP%2i%2i%2i"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * - `$3`: Repeat count (2 bytes) - Number of repeats + * + * Legacy version without KeyButton and language parameters. + * + * @deprecated Use kMsgDKeyRepeat for protocol version 1.1+ + * @see kMsgDKeyRepeat + * @since Protocol version 1.0 + */ extern const char *const kMsgDKeyRepeat1_0; -// key released: primary -> secondary -// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +/** + * @brief Key release event + * + * **Message Code**: `"DKUP"` + * **Direction**: Primary → Secondary + * **Format**: `"DKUP%2i%2i%2i"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier, often called a "keysym" on Linux/X11. This is platform-dependent + * and corresponds to values like `XK_a` on X11/Linux, `'a'` on macOS, and `'A'` on Windows. + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * - `$3`: KeyButton (2 bytes) - Physical key code, often called a "keycode" or "scancode". This is the raw, + * platform-dependent scan code of the key pressed. + * + * **Example**: + * ``` + * "DKUP\x00\x61\x00\x00\x00\x1E" + * // Release 'a' key, physical key 0x1E + * ``` + * + * **Important**: The secondary screen should use KeyButton (not KeyID) + * to determine which physical key to release. This ensures correct + * behavior with dead keys and layout differences. + * + * @see kMsgDKeyDown + * @since Protocol version 1.1 + */ extern const char *const kMsgDKeyUp; -// key released 1.0: same as above but without KeyButton +/** + * @brief Key release event (legacy v1.0) + * + * **Message Code**: `"DKUP"` + * **Direction**: Primary → Secondary + * **Format**: `"DKUP%2i%2i"` + * **Parameters**: + * - `$1`: KeyID (2 bytes) - Virtual key identifier + * - `$2`: KeyModifierMask (2 bytes) - Active modifier keys + * + * Legacy version without KeyButton parameter. + * + * @deprecated Use kMsgDKeyUp for protocol version 1.1+ + * @see kMsgDKeyUp + * @since Protocol version 1.0 + */ extern const char *const kMsgDKeyUp1_0; -// mouse button pressed: primary -> secondary -// $1 = ButtonID +/** @} */ // end of protocol_keyboard group + +/** + * @defgroup protocol_mouse Mouse Messages + * @brief Mouse input event messages + * @{ + */ + +/** + * @brief Mouse button press event + * + * **Message Code**: `"DMDN"` + * **Direction**: Primary → Secondary + * **Format**: `"DMDN%1i"` + * **Parameters**: + * - `$1`: ButtonID (1 byte) - Mouse button identifier + * + * **Example**: + * ``` + * "DMDN\x01" // Left mouse button pressed + * "DMDN\x02" // Right mouse button pressed + * "DMDN\x03" // Middle mouse button pressed + * ``` + * + * **Button IDs**: + * - `1`: Left button + * - `2`: Right button + * - `3`: Middle button + * - `4+`: Additional buttons (side buttons, etc.) + * + * @see kMsgDMouseUp + * @since Protocol version 1.0 + */ extern const char *const kMsgDMouseDown; -// mouse button released: primary -> secondary -// $1 = ButtonID +/** + * @brief Mouse button release event + * + * **Message Code**: `"DMUP"` + * **Direction**: Primary → Secondary + * **Format**: `"DMUP%1i"` + * **Parameters**: + * - `$1`: ButtonID (1 byte) - Mouse button identifier + * + * **Example**: + * ``` + * "DMUP\x01" // Left mouse button released + * ``` + * + * Button IDs are the same as for kMsgDMouseDown. + * + * @see kMsgDMouseDown + * @since Protocol version 1.0 + */ extern const char *const kMsgDMouseUp; -// mouse moved: primary -> secondary -// $1 = x, $2 = y. x,y are absolute screen coordinates. +/** + * @brief Absolute mouse movement + * + * **Message Code**: `"DMMV"` + * **Direction**: Primary → Secondary + * **Format**: `"DMMV%2i%2i"` + * **Parameters**: + * - `$1`: X coordinate (2 bytes, signed) - Absolute screen position + * - `$2`: Y coordinate (2 bytes, signed) - Absolute screen position + * + * **Example**: + * ``` + * "DMMV\x01\x90\x01\x2C" // Move to (400, 300) + * ``` + * + * Coordinates are absolute positions on the secondary screen. + * The origin (0,0) is typically the top-left corner. + * + * @see kMsgDMouseRelMove + * @since Protocol version 1.0 + */ extern const char *const kMsgDMouseMove; -// relative mouse move: primary -> secondary -// $1 = dx, $2 = dy. dx,dy are motion deltas. +/** + * @brief Relative mouse movement + * + * **Message Code**: `"DMRM"` + * **Direction**: Primary → Secondary + * **Format**: `"DMRM%2i%2i"` + * **Parameters**: + * - `$1`: X delta (2 bytes, signed) - Horizontal movement + * - `$2`: Y delta (2 bytes, signed) - Vertical movement + * + * **Example**: + * ``` + * "DMRM\x00\x0A\xFF\xF6" // Move right 10, up 10 pixels + * ``` + * + * Relative movement is useful for: + * - High-precision input devices + * - Gaming applications + * - When absolute positioning is not desired + * + * @see kMsgDMouseMove + * @since Protocol version 1.2 + */ extern const char *const kMsgDMouseRelMove; -// mouse scroll: primary -> secondary -// $1 = xDelta, $2 = yDelta. the delta should be +120 for one tick forward -// (away from the user) or right and -120 for one tick backward (toward -// the user) or left. +/** + * @brief Mouse wheel scroll event + * + * **Message Code**: `"DMWM"` + * **Direction**: Primary → Secondary + * **Format**: `"DMWM%2i%2i"` + * **Parameters**: + * - `$1`: X delta (2 bytes, signed) - Horizontal scroll + * - `$2`: Y delta (2 bytes, signed) - Vertical scroll + * + * **Example**: + * ``` + * "DMWM\x00\x00\x00\x78" // Scroll up one tick (+120) + * "DMWM\x00\x00\xFF\x88" // Scroll down one tick (-120) + * "DMWM\x00\x78\x00\x00" // Scroll right one tick (+120) + * ``` + * + * **Scroll Values**: + * - `+120`: One tick forward (away from user) or right + * - `-120`: One tick backward (toward user) or left + * - Values are typically multiples of 120 + * + * **Directions**: + * - **Vertical**: Positive = up/away, Negative = down/toward + * - **Horizontal**: Positive = right, Negative = left + * + * @see kMsgDMouseWheel1_0 + * @since Protocol version 1.3 + */ extern const char *const kMsgDMouseWheel; -// mouse vertical scroll: primary -> secondary -// like as kMsgDMouseWheel except only sends $1 = yDelta. +/** + * @brief Mouse wheel scroll event (legacy v1.0-1.2) + * + * **Message Code**: `"DMWM"` + * **Direction**: Primary → Secondary + * **Format**: `"DMWM%2i"` + * **Parameters**: + * - `$1`: Y delta (2 bytes, signed) - Vertical scroll only + * + * Legacy version that only supports vertical scrolling. + * Used when communicating with protocol versions 1.0-1.2. + * + * @deprecated Use kMsgDMouseWheel for protocol version 1.3+ + * @see kMsgDMouseWheel + * @since Protocol version 1.0 + */ extern const char *const kMsgDMouseWheel1_0; -// clipboard data: primary <-> secondary -// $2 = sequence number, $3 = mark $4 = clipboard data. the sequence number -// is 0 when sent by the primary. secondary screens should use the -// sequence number from the most recent kMsgCEnter. $1 = clipboard -// identifier. +/** @} */ // end of protocol_mouse group + +/** + * @defgroup protocol_clipboard Clipboard Messages + * @brief Clipboard data synchronization messages + * @{ + */ + +/** + * @brief Clipboard data transfer + * + * **Message Code**: `"DCLP"` + * **Direction**: Primary ↔ Secondary + * **Format**: `"DCLP%1i%4i%1i%s"` + * **Parameters**: + * - `$1`: Clipboard identifier (1 byte) + * - `$2`: Sequence number (4 bytes) + * - `$3`: Mark/flags (1 byte) - For streaming support (v1.6+) + * - `$4`: Clipboard data (string) + * + * **Example**: + * ``` + * "DCLP\x00\x00\x00\x00\x01\x00\x00\x00\x00\x0BHello World" + * // Primary clipboard, sequence 1, no flags, text "Hello World" + * ``` + * + * **Clipboard Identifiers**: + * - `0`: Primary clipboard (Ctrl+C/Ctrl+V) + * - `1`: Selection clipboard (middle-click on X11) + * + * **Sequence Numbers**: + * - Primary always sends sequence number 0 + * - Secondary uses sequence number from most recent kMsgCEnter + * + * **Streaming (v1.6+)**: + * For large clipboard data, the mark byte enables chunked transfer: + * - `0`: Single chunk (complete data) + * - `1`: First chunk of multi-chunk transfer + * - `2`: Middle chunk + * - `3`: Final chunk + * + * @see kMsgCClipboard + * @since Protocol version 1.0 + */ extern const char *const kMsgDClipboard; -// client data: secondary -> primary -// $1 = coordinate of leftmost pixel on secondary screen, -// $2 = coordinate of topmost pixel on secondary screen, -// $3 = width of secondary screen in pixels, -// $4 = height of secondary screen in pixels, -// $5 = size of warp zone, (obsolete) -// $6, $7 = the x,y position of the mouse on the secondary screen. -// -// the secondary screen must send this message in response to the -// kMsgQInfo message. it must also send this message when the -// screen's resolution changes. in this case, the secondary screen -// should ignore any kMsgDMouseMove messages until it receives a -// kMsgCInfoAck in order to prevent attempts to move the mouse off -// the new screen area. +/** @} */ // end of protocol_clipboard group + +/** + * @defgroup protocol_info Information Messages + * @brief Screen information and configuration messages + * @{ + */ + +/** + * @brief Client screen information + * + * **Message Code**: `"DINF"` + * **Direction**: Secondary → Primary + * **Format**: `"DINF%2i%2i%2i%2i%2i%2i%2i"` + * **Parameters**: + * - `$1`: Left edge coordinate (2 bytes, signed) + * - `$2`: Top edge coordinate (2 bytes, signed) + * - `$3`: Screen width in pixels (2 bytes, unsigned) + * - `$4`: Screen height in pixels (2 bytes, unsigned) + * - `$5`: Warp zone size (2 bytes, obsolete) + * - `$6`: Mouse X position (2 bytes, signed) + * - `$7`: Mouse Y position (2 bytes, signed) + * + * **Example**: + * ``` + * "DINF\x00\x00\x00\x00\x07\x80\x04\x38\x00\x00\x01\x90\x01\x2C" + * // Screen at (0,0), 1920x1080, mouse at (400,300) + * ``` + * + * **When to Send**: + * 1. In response to kMsgQInfo query + * 2. When screen resolution changes + * 3. During initial connection setup + * + * **Resolution Change Protocol**: + * When sending due to resolution change, the secondary should: + * 1. Send kMsgDInfo with new dimensions + * 2. Ignore kMsgDMouseMove until receiving kMsgCInfoAck + * 3. This prevents mouse movement outside the new screen area + * + * @see kMsgQInfo, kMsgCInfoAck + * @since Protocol version 1.0 + */ extern const char *const kMsgDInfo; -// set options: primary -> secondary -// client should set the given option/value pairs. $1 = option/value -// pairs. +/** + * @brief Set client options + * + * **Message Code**: `"DSOP"` + * **Direction**: Primary → Secondary + * **Format**: `"DSOP%4I"` + * **Parameters**: + * - `$1`: Option/value pairs (4-byte integer list) + * + * **Example**: + * ``` + * "DSOP\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00" + * // 2 pairs: option 1 = value 1, option 2 = value 0 + * ``` + * + * Sets configuration options on the client. Options are sent as + * alternating option ID and value pairs. The client should apply + * these settings to modify its behavior. + * + * @see kMsgCResetOptions + * @since Protocol version 1.0 + */ extern const char *const kMsgDSetOptions; -// file data: primary <-> secondary -// transfer file data. A mark is used in the first byte. -// 0 means the content followed is the file size. -// 1 means the content followed is the chunk data. -// 2 means the file transfer is finished. +/** @} */ // end of protocol_info group + +/** + * @defgroup protocol_files File Transfer Messages + * @brief File transfer and drag-and-drop messages (v1.5+) + * @{ + */ + +/** + * @brief File transfer data + * + * **Message Code**: `"DFTR"` + * **Direction**: Primary ↔ Secondary + * **Format**: `"DFTR%1i%s"` + * **Parameters**: + * - `$1`: Transfer mark (1 byte) - Transfer state + * - `$2`: Data (string) - Content depends on mark + * + * **Transfer Marks**: + * - `1` (kDataStart): Data contains file size (8 bytes) + * - `2` (kDataChunk): Data contains file content chunk + * - `3` (kDataEnd): Transfer complete (data may be empty) + * + * **Example Transfer Sequence**: + * ``` + * "DFTR\x01\x00\x00\x00\x00\x00\x00\x10\x00" // Start: 4096 bytes + * "DFTR\x02[1024 bytes of file data]" // Chunk: data + * "DFTR\x02[1024 bytes of file data]" // Chunk: data + * "DFTR\x02[1024 bytes of file data]" // Chunk: data + * "DFTR\x02[1024 bytes of file data]" // Chunk: data + * "DFTR\x03" // End: complete + * ``` + * + * **Protocol Flow**: + * 1. Sender initiates with kDataStart containing total file size + * 2. Sender sends multiple kDataChunk messages with file content + * 3. Sender concludes with kDataEnd to signal completion + * 4. Receiver can abort by closing connection + * + * @see kMsgDDragInfo, EDataTransfer + * @since Protocol version 1.5 + * @deprecated File drag and drop is no longer implemented. + */ extern const char *const kMsgDFileTransfer; -// drag infomation: primary <-> secondary -// transfer drag infomation. The first 2 bytes are used for storing -// the number of dragging objects. Then the following string consists -// of each object's directory. +/** + * @brief Drag and drop information + * + * **Message Code**: `"DDRG"` + * **Direction**: Primary ↔ Secondary + * **Format**: `"DDRG%2i%s"` + * **Parameters**: + * - `$1`: Number of files (2 bytes) + * - `$2`: File paths (string) - Null-separated file paths + * + * **Example**: + * ``` * "DDRG\x00\x02/path/to/file1.txt\x00/path/to/file2.txt\x00" + * // Dragging 2 files + * ``` + * + * Sent when a drag-and-drop operation begins. Contains the list + * of files being dragged. The actual file transfer follows using + * kMsgDFileTransfer messages. + * + * **File Path Format**: + * - Paths are null-terminated strings + * - Multiple paths are concatenated with null separators + * - Paths should use forward slashes for compatibility + * + * @see kMsgDFileTransfer + * @since Protocol version 1.5 + * @deprecated File drag and drop is no longer implemented. + */ extern const char *const kMsgDDragInfo; -// secure input notification: primary -> secondary -// $1 = app. app only obtainable on MacOS since that's the only -// platform facing secure input problems +/** @} */ // end of protocol_files group + +/** + * @defgroup protocol_system System Messages + * @brief System-level notifications and synchronization + * @{ + */ + +/** + * @brief Secure input notification (macOS) + * + * **Message Code**: `"SECN"` + * **Direction**: Primary → Secondary + * **Format**: `"SECN%s"` + * **Parameters**: + * - `$1`: Application name (string) - App requesting secure input + * + * **Example**: + * ``` + * "SECN\x00\x00\x00\x08Terminal" + * // Terminal app is requesting secure input + * ``` + * + * Notifies the secondary screen when an application on the primary + * requests secure input mode. This is primarily a macOS feature + * where certain applications (like password fields) can request + * exclusive keyboard access. + * + * The secondary can use this information to: + * - Display security warnings to the user + * - Temporarily disable input forwarding + * - Show which application is requesting secure input + * + * @since Protocol version 1.7 + */ extern const char *const kMsgDSecureInputNotification; -// language synchronization: primary -> secondary -// $1 = List of server languages +/** + * @brief Language synchronization + * + * **Message Code**: `"LSYN"` + * **Direction**: Primary → Secondary + * **Format**: `"LSYN%s"` + * **Parameters**: + * - `$1`: Language list (string) - Available server languages + * + * **Example**: + * ``` + * "LSYN\x00\x00\x00\x0Ben,fr,de,es" + * // Server supports English, French, German, Spanish + * ``` + * + * Synchronizes keyboard language/layout information between + * primary and secondary screens. Helps ensure proper character + * mapping when different keyboard layouts are used. + * + * **Language Format**: + * - Comma-separated list of language codes + * - Uses standard ISO 639-1 language codes + * - Primary language listed first + * + * @since Protocol version 1.8 + */ extern const char *const kMsgDLanguageSynchronisation; -// -// query codes -// +/** @} */ // end of protocol_system group -// query screen info: primary -> secondary -// client should reply with a kMsgDInfo. +/** @} */ // end of protocol_data group + +/** + * @defgroup protocol_queries Query Messages + * @brief Information request messages + * @{ + */ + +/** + * @brief Query screen information + * + * **Message Code**: `"QINF"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Requests the secondary screen to send its current screen information. + * The client should respond with a kMsgDInfo message containing: + * - Screen dimensions and position + * - Current mouse position + * - Other screen-related data + * + * This is typically sent: + * - During initial connection setup + * - When the server needs updated screen information + * - After configuration changes + * + * @see kMsgDInfo, kMsgCInfoAck + * @since Protocol version 1.0 + */ extern const char *const kMsgQInfo; -// -// error codes -// +/** @} */ // end of protocol_queries group -// incompatible versions: primary -> secondary -// $1 = major version of primary, $2 = minor version of primary. +/** + * @defgroup protocol_errors Error Messages + * @brief Protocol error and failure notifications + * @{ + */ + +/** + * @brief Incompatible protocol versions + * + * **Message Code**: `"EICV"` + * **Direction**: Primary → Secondary + * **Format**: `"EICV%2i%2i"` + * **Parameters**: + * - `$1`: Primary major version (2 bytes) + * - `$2`: Primary minor version (2 bytes) + * + * **Example**: + * ``` + * "EICV\x00\x01\x00\x08" // Server is version 1.8 + * ``` + * + * Sent when the client and server have incompatible protocol versions. + * This typically occurs when: + * - Major versions differ (fundamental incompatibility) + * - Server requires newer features than client supports + * + * After sending this message, the server will disconnect the client. + * The client should display an appropriate error message to the user. + * + * @since Protocol version 1.0 + */ extern const char *const kMsgEIncompatible; -// name provided when connecting is already in use: primary -> secondary +/** + * @brief Client name already in use + * + * **Message Code**: `"EBSY"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Sent when the client name provided during connection is already + * in use by another connected client. Client names must be unique + * within a Deskflow network. + * + * After receiving this message, the client should: + * 1. Disconnect from the server + * 2. Inform the user of the name conflict + * 3. Allow the user to choose a different name + * 4. Retry connection with the new name + * + * @since Protocol version 1.0 + */ extern const char *const kMsgEBusy; -// unknown client: primary -> secondary -// name provided when connecting is not in primary's screen -// configuration map. +/** + * @brief Unknown client name + * + * **Message Code**: `"EUNK"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Sent when the client name provided during connection is not + * found in the server's screen configuration map. This means + * the server doesn't know about this client. + * + * This can happen when: + * - Client name is misspelled + * - Server configuration doesn't include this client + * - Client is connecting to wrong server + * + * The client should inform the user and check: + * - Client name spelling + * - Server configuration + * - Network connectivity to correct server + * + * @since Protocol version 1.0 + */ extern const char *const kMsgEUnknown; -// protocol violation: primary -> secondary -// primary should disconnect after sending this message. +/** + * @brief Protocol violation + * + * **Message Code**: `"EBAD"` + * **Direction**: Primary → Secondary + * **Format**: No parameters + * + * Sent when the client violates the protocol in some way. + * This can include: + * - Sending malformed messages + * - Sending messages in wrong order + * - Sending unexpected message types + * - Exceeding protocol limits + * + * After sending this message, the server will immediately + * disconnect the client. This indicates a serious protocol + * error that cannot be recovered from. + * + * **Common Causes**: + * - Implementation bugs in client + * - Corrupted network data + * - Version mismatch not caught earlier + * - Malicious or broken client + * + * @since Protocol version 1.0 + */ extern const char *const kMsgEBad; -// -// structures -// +/** @} */ // end of protocol_errors group -//! Screen information -/*! -This class contains information about a screen. -*/ +/** @} */ // end of protocol_messages group + +/** + * @defgroup protocol_structures Protocol Data Structures + * @brief Data structures used in protocol messages + * @{ + */ + +/** + * @brief Client screen information structure + * + * Contains comprehensive information about a secondary screen, + * including dimensions, position, and current mouse location. + * This data is sent via kMsgDInfo messages. + * + * **Usage**: + * - Sent by client in response to kMsgQInfo + * - Sent when screen resolution changes + * - Used by server for screen layout calculations + * + * **Coordinate System**: + * - Origin (0,0) is typically top-left corner + * - Coordinates can be negative for multi-monitor setups + * - All values are in pixels + * + * @see kMsgDInfo, kMsgQInfo + * @since Protocol version 1.0 + */ class ClientInfo { public: - //! Screen position - /*! - The position of the upper-left corner of the screen. This is - typically 0,0. - */ - int32_t m_x; - int32_t m_y; + /** + * @brief Screen position coordinates + * + * The position of the upper-left corner of the screen in the + * virtual desktop coordinate system. This is typically (0,0) + * for single-monitor setups, but can be different in multi-monitor + * configurations. + * + * @since Protocol version 1.0 + */ + int32_t m_x; ///< Left edge X coordinate + int32_t m_y; ///< Top edge Y coordinate - //! Screen size - /*! - The size of the screen in pixels. - */ - int32_t m_w; - int32_t m_h; + /** + * @brief Screen dimensions + * + * The size of the screen in pixels. These values represent + * the usable screen area for mouse movement and window placement. + * + * @since Protocol version 1.0 + */ + int32_t m_w; ///< Screen width in pixels + int32_t m_h; ///< Screen height in pixels - //! Obsolete (jump zone size) + /** + * @brief Obsolete jump zone size + * + * @deprecated This field is no longer used and should be set to 0 + * @since Protocol version 1.0 + */ int32_t obsolete1; - //! Mouse position - /*! - The current location of the mouse cursor. - */ - int32_t m_mx; - int32_t m_my; + /** + * @brief Current mouse position + * + * The current location of the mouse cursor on this screen. + * Coordinates are relative to the screen's coordinate system + * (m_x, m_y represent the origin). + * + * **Usage**: + * - Updated when mouse moves on this screen + * - Used for cursor synchronization between screens + * - Helps with smooth transitions during screen switching + * + * @since Protocol version 1.0 + */ + int32_t m_mx; ///< Mouse X position + int32_t m_my; ///< Mouse Y position }; + +/** @} */ // end of protocol_structures group diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index cadbdefd7..31676f45e 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -23,6 +23,8 @@ using namespace deskflow::string; namespace deskflow::server { +// Protocol options used in configuration files (lowercase). +// Note that @ref kSynergyProtocolName / @ref kBarrierProtocolName use capitalized names. const auto kSynergyProtocolOption = "synergy"; const auto kBarrierProtocolOption = "barrier";