Rust · 7121 bytes Raw Blame History
1 //! Network protocol definitions for HyprKVM
2 //!
3 //! All messages exchanged between HyprKVM daemons are defined here.
4
5 use serde::{Deserialize, Serialize};
6
7 use crate::{Direction, ModifierState};
8
9 /// Protocol version - increment on breaking changes
10 pub const PROTOCOL_VERSION: u32 = 1;
11
12 /// All possible network messages
13 #[derive(Debug, Clone, Serialize, Deserialize)]
14 #[serde(tag = "type", rename_all = "snake_case")]
15 pub enum Message {
16 // Connection lifecycle
17 Hello(HelloPayload),
18 HelloAck(HelloAckPayload),
19 Goodbye,
20
21 // Topology
22 TopologyUpdate(TopologyPayload),
23 TopologyAck,
24
25 // Control transfer
26 Enter(EnterPayload),
27 EnterAck(EnterAckPayload),
28 Leave(LeavePayload),
29 LeaveAck,
30
31 // Input events
32 InputEvent(InputEventPayload),
33 InputBatch(Vec<InputEventPayload>),
34
35 // Clipboard
36 ClipboardOffer(ClipboardOfferPayload),
37 ClipboardRequest(ClipboardRequestPayload),
38 ClipboardData(ClipboardDataPayload),
39
40 // Health
41 Ping { timestamp: u64 },
42 Pong { timestamp: u64 },
43 }
44
45 // ============================================================================
46 // Connection Messages
47 // ============================================================================
48
49 #[derive(Debug, Clone, Serialize, Deserialize)]
50 pub struct HelloPayload {
51 /// Protocol version
52 pub protocol_version: u32,
53 /// Machine name
54 pub machine_name: String,
55 /// Supported capabilities
56 pub capabilities: Vec<String>,
57 }
58
59 #[derive(Debug, Clone, Serialize, Deserialize)]
60 pub struct HelloAckPayload {
61 /// Whether handshake was accepted
62 pub accepted: bool,
63 /// Protocol version (for negotiation)
64 pub protocol_version: u32,
65 /// Machine name
66 pub machine_name: String,
67 /// Error message if not accepted
68 pub error: Option<String>,
69 }
70
71 // ============================================================================
72 // Topology Messages
73 // ============================================================================
74
75 #[derive(Debug, Clone, Serialize, Deserialize)]
76 pub struct TopologyPayload {
77 /// This machine's name
78 pub machine_name: String,
79 /// Known neighbors
80 pub neighbors: Vec<NeighborInfo>,
81 }
82
83 #[derive(Debug, Clone, Serialize, Deserialize)]
84 pub struct NeighborInfo {
85 pub name: String,
86 pub direction: Direction,
87 }
88
89 // ============================================================================
90 // Control Transfer Messages
91 // ============================================================================
92
93 #[derive(Debug, Clone, Serialize, Deserialize)]
94 pub struct EnterPayload {
95 /// Direction we're entering from (from sender's perspective)
96 pub from_direction: Direction,
97 /// Cursor entry position
98 pub cursor_pos: CursorEntryPos,
99 /// Current modifier state
100 pub modifiers: ModifierState,
101 /// Unique transfer ID
102 pub transfer_id: u64,
103 }
104
105 #[derive(Debug, Clone, Serialize, Deserialize)]
106 #[serde(rename_all = "snake_case")]
107 pub enum CursorEntryPos {
108 /// Position along the edge (0.0 = top/left, 1.0 = bottom/right)
109 EdgeRelative(f64),
110 /// Absolute coordinates
111 Absolute { x: i32, y: i32 },
112 }
113
114 #[derive(Debug, Clone, Serialize, Deserialize)]
115 pub struct EnterAckPayload {
116 /// Whether entry was accepted
117 pub success: bool,
118 /// Transfer ID for correlation
119 pub transfer_id: u64,
120 /// Actual cursor position after entry
121 pub actual_cursor_pos: Option<(i32, i32)>,
122 /// Error message if not success
123 pub error: Option<String>,
124 }
125
126 #[derive(Debug, Clone, Serialize, Deserialize)]
127 pub struct LeavePayload {
128 /// Direction we're leaving towards
129 pub to_direction: Direction,
130 /// Cursor position for target machine
131 pub cursor_pos: CursorEntryPos,
132 /// Current modifier state
133 pub modifiers: ModifierState,
134 /// Transfer ID for correlation
135 pub transfer_id: u64,
136 }
137
138 // ============================================================================
139 // Input Messages
140 // ============================================================================
141
142 #[derive(Debug, Clone, Serialize, Deserialize)]
143 pub struct InputEventPayload {
144 /// Sequence number for ordering
145 pub sequence: u64,
146 /// Timestamp in microseconds
147 pub timestamp_us: u64,
148 /// The actual event
149 pub event: InputEventType,
150 }
151
152 #[derive(Debug, Clone, Serialize, Deserialize)]
153 #[serde(tag = "type", rename_all = "snake_case")]
154 pub enum InputEventType {
155 KeyDown { keycode: u32 },
156 KeyUp { keycode: u32 },
157 ModifierState {
158 shift: bool,
159 ctrl: bool,
160 alt: bool,
161 super_key: bool,
162 },
163 PointerMotion { dx: f64, dy: f64 },
164 PointerButton { button: u32, pressed: bool },
165 Scroll { horizontal: f64, vertical: f64 },
166 }
167
168 // ============================================================================
169 // Clipboard Messages
170 // ============================================================================
171
172 #[derive(Debug, Clone, Serialize, Deserialize)]
173 pub struct ClipboardOfferPayload {
174 /// Unique ID for this clipboard state
175 pub clipboard_id: u64,
176 /// Available MIME types
177 pub mime_types: Vec<String>,
178 /// Size hint in bytes (if known)
179 pub size_hint: Option<u64>,
180 /// Content hash for deduplication
181 pub content_hash: Option<String>,
182 }
183
184 #[derive(Debug, Clone, Serialize, Deserialize)]
185 pub struct ClipboardRequestPayload {
186 /// Which clipboard to fetch
187 pub clipboard_id: u64,
188 /// Preferred MIME type
189 pub mime_type: String,
190 }
191
192 #[derive(Debug, Clone, Serialize, Deserialize)]
193 pub struct ClipboardDataPayload {
194 /// Clipboard ID
195 pub clipboard_id: u64,
196 /// MIME type of data
197 pub mime_type: String,
198 /// Base64-encoded data
199 pub data: String,
200 /// Chunk index (if chunked)
201 pub chunk_index: Option<u32>,
202 /// Total chunks (if chunked)
203 pub total_chunks: Option<u32>,
204 }
205
206 // ============================================================================
207 // IPC Messages (CLI <-> Daemon)
208 // ============================================================================
209
210 /// IPC request from CLI to daemon
211 #[derive(Debug, Clone, Serialize, Deserialize)]
212 #[serde(tag = "type", rename_all = "snake_case")]
213 pub enum IpcRequest {
214 /// Request to move focus in a direction (keyboard navigation)
215 Move { direction: Direction },
216 /// Get daemon status
217 Status,
218 /// List connected peers
219 ListPeers,
220 }
221
222 /// IPC response from daemon to CLI
223 #[derive(Debug, Clone, Serialize, Deserialize)]
224 #[serde(tag = "type", rename_all = "snake_case")]
225 pub enum IpcResponse {
226 /// Move was handled - transferred to another machine
227 Transferred { to_machine: String },
228 /// Move should be handled locally (no edge crossing)
229 DoLocalMove,
230 /// Status response
231 Status {
232 state: String,
233 connected_peers: Vec<String>,
234 },
235 /// Peer list
236 Peers { peers: Vec<PeerInfo> },
237 /// Error occurred
238 Error { message: String },
239 }
240
241 /// Info about a connected peer
242 #[derive(Debug, Clone, Serialize, Deserialize)]
243 pub struct PeerInfo {
244 pub name: String,
245 pub direction: Direction,
246 pub connected: bool,
247 }
248