diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-08-09 02:04:39 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-09 02:04:39 +0300 |
| commit | 1777e9bba32b953ed1ad203b4a16d01105539000 (patch) | |
| tree | 22096973812c9baba822453d8470bdcfe64fc2a6 /src/protocol | |
| parent | 36a8d36a7258e08b40c01bf521b1f4006525f143 (diff) | |
feat(graphics): add direct pane frame streaming (#2523)
* feat(graphics): add direct pane frame streaming
* feat(graphics): expose pane visibility
* test(graphics): preserve legacy terminal image output
* fix(graphics): harden streaming lifecycle
* fix(input): preserve mouse releases outside panes
Diffstat (limited to 'src/protocol')
| -rw-r--r-- | src/protocol/wire.rs | 81 |
1 files changed, 79 insertions, 2 deletions
diff --git a/src/protocol/wire.rs b/src/protocol/wire.rs index 4af2c794..4a445361 100644 --- a/src/protocol/wire.rs +++ b/src/protocol/wire.rs @@ -57,6 +57,8 @@ pub enum ClientKeybindings { pub enum ClientLaunchMode { /// Full app client. App, + /// Full app client eligible for audited local direct graphics. + AppDirectGraphics, /// Direct terminal attach client. TerminalAttach, } @@ -428,6 +430,25 @@ pub enum ClientMessage { /// Replace an existing writable controller for this terminal. takeover: bool, }, + + /// Result of the one armed Herdr-owned direct Kitty transmission. + GraphicsTransmissionResult { + transfer_id: u64, + image_id: u32, + success: bool, + }, + + /// One confirmed SGR 1016 mouse report with read-time host geometry. + InputPixels { + data: Vec<u8>, + cols: u16, + rows: u16, + width_px: u32, + height_px: u32, + }, + + /// The direct command was written and flushed; terminal response timing starts now. + GraphicsTransmissionStarted { transfer_id: u64, image_id: u32 }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] @@ -696,6 +717,8 @@ pub enum ServerMessage { MouseCapture { /// True when Herdr mouse UI is enabled or the focused pane app requests mouse reporting. enabled: bool, + /// True only while the focused pane requests DEC SGR pixel mode 1016. + sgr_pixels: bool, }, /// Whether the focused terminal requests Kitty report-all keyboard input. @@ -717,6 +740,19 @@ pub enum ServerMessage { /// Number of BEL characters parsed from one PTY read. count: u16, }, + + /// One validated Herdr-owned Kitty regular-file RGBA transmission. + GraphicsFile { + path: String, + expected_len: u64, + image_id: u32, + transfer_id: u64, + leading: Vec<u8>, + control: String, + }, + + /// Suppress a direct command that expired before terminal delivery. + GraphicsTransmissionRetired { transfer_id: u64, image_id: u32 }, } // --------------------------------------------------------------------------- @@ -1148,7 +1184,7 @@ mod tests { ], }; let encoded = bincode::serde::encode_to_vec(&msg, bincode::config::standard()).unwrap(); - // Freeze the protocol 19 input envelope before it is published. + // Freeze the protocol 20 input envelope before it is published. assert_eq!( encoded, vec![ @@ -1534,7 +1570,10 @@ mod tests { #[test] fn server_mouse_capture_roundtrip() { - let msg = ServerMessage::MouseCapture { enabled: true }; + let msg = ServerMessage::MouseCapture { + enabled: true, + sgr_pixels: true, + }; let encoded = bincode::serde::encode_to_vec(&msg, bincode::config::standard()).unwrap(); let (decoded, _): (ServerMessage, _) = bincode::serde::decode_from_slice(&encoded, bincode::config::standard()).unwrap(); @@ -1551,6 +1590,44 @@ mod tests { } #[test] + fn direct_graphics_messages_roundtrip() { + let client = ClientMessage::GraphicsTransmissionResult { + transfer_id: 7, + image_id: 42, + success: false, + }; + let encoded = bincode::serde::encode_to_vec(&client, bincode::config::standard()).unwrap(); + let (decoded, _): (ClientMessage, _) = + bincode::serde::decode_from_slice(&encoded, bincode::config::standard()).unwrap(); + assert_eq!(client, decoded); + + let server = ServerMessage::GraphicsFile { + path: "/run/user/1000/herdr/source/frame".into(), + expected_len: 4, + image_id: 42, + transfer_id: 7, + leading: b"\x1b[2;3H".to_vec(), + control: "a=T,f=32,i=42,q=0".into(), + }; + let encoded = bincode::serde::encode_to_vec(&server, bincode::config::standard()).unwrap(); + let (decoded, _): (ServerMessage, _) = + bincode::serde::decode_from_slice(&encoded, bincode::config::standard()).unwrap(); + assert_eq!(server, decoded); + + let pixels = ClientMessage::InputPixels { + data: b"\x1b[<35;321;241M".to_vec(), + cols: 80, + rows: 24, + width_px: 800, + height_px: 480, + }; + let encoded = bincode::serde::encode_to_vec(&pixels, bincode::config::standard()).unwrap(); + let (decoded, _): (ClientMessage, _) = + bincode::serde::decode_from_slice(&encoded, bincode::config::standard()).unwrap(); + assert_eq!(pixels, decoded); + } + + #[test] fn server_prefix_input_source_roundtrip() { for active in [true, false] { let msg = ServerMessage::PrefixInputSource { active }; |
