Rust · 9057 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 /// Target for switch command - either direction or machine name
211 #[derive(Debug, Clone, Serialize, Deserialize)]
212 #[serde(untagged)]
213 pub enum SwitchTarget {
214 Direction(Direction),
215 MachineName(String),
216 }
217
218 /// IPC request from CLI to daemon
219 #[derive(Debug, Clone, Serialize, Deserialize)]
220 #[serde(tag = "type", rename_all = "snake_case")]
221 pub enum IpcRequest {
222 /// Request to move focus in a direction (keyboard navigation)
223 Move { direction: Direction },
224 /// Get daemon status
225 Status,
226 /// List connected peers
227 ListPeers,
228 /// Ping a specific peer by name
229 PingPeer { peer_name: String },
230
231 // Control transfer
232 /// Transfer control to another machine (by direction or name)
233 Switch { target: SwitchTarget },
234 /// Return control to this machine
235 Return,
236
237 // Input management
238 /// Force release input capture
239 Release,
240 /// Enable/disable edge barrier (prevent cursor from leaving)
241 SetBarrier { enabled: bool },
242
243 // Connection management
244 /// Disconnect from a peer
245 Disconnect { peer_name: String },
246 /// Force reconnection to a peer
247 Reconnect { peer_name: String },
248
249 // Configuration
250 /// Get current configuration
251 GetConfig,
252 /// Reload configuration from file
253 Reload,
254
255 // Daemon control
256 /// Graceful shutdown
257 Shutdown,
258 /// Get daemon logs
259 GetLogs {
260 /// Number of lines to retrieve (default: 50)
261 lines: Option<u32>,
262 /// Stream new lines (not yet implemented)
263 follow: bool,
264 },
265 }
266
267 /// IPC response from daemon to CLI
268 #[derive(Debug, Clone, Serialize, Deserialize)]
269 #[serde(tag = "type", rename_all = "snake_case")]
270 pub enum IpcResponse {
271 /// Move was handled - transferred to another machine
272 Transferred { to_machine: String },
273 /// Move should be handled locally (no edge crossing)
274 DoLocalMove,
275 /// Status response
276 Status {
277 state: String,
278 connected_peers: Vec<String>,
279 /// Daemon uptime in seconds
280 uptime_secs: u64,
281 /// This machine's name
282 machine_name: String,
283 },
284 /// Peer list
285 Peers { peers: Vec<PeerInfo> },
286 /// Ping result
287 PingResult {
288 peer_name: String,
289 /// Round-trip time in milliseconds (None if peer not connected)
290 latency_ms: Option<u64>,
291 /// Error message if ping failed
292 error: Option<String>,
293 },
294 /// Error occurred
295 Error { message: String },
296
297 // New responses for CLI expansion
298 /// Generic success response
299 Ok { message: String },
300 /// Configuration dump
301 Config { toml: String },
302 /// Log lines
303 Logs { lines: Vec<String> },
304 }
305
306 /// Info about a connected peer
307 #[derive(Debug, Clone, Serialize, Deserialize)]
308 pub struct PeerInfo {
309 pub name: String,
310 pub direction: Direction,
311 pub connected: bool,
312 /// Configured address for this peer
313 pub address: String,
314 /// Connection status: "connected", "disconnected", "connecting"
315 pub status: String,
316 }
317