use std::io::Write; use crate::proto::{self, read_exact, read_i32_be, read_modified_utf8, read_u8, read_u16_be}; // --------------------------------------------------------------------------- // Client-to-server message writers // --------------------------------------------------------------------------- /// Msg type 2: SetEncodings — aw.a(int[], int), line 597. /// Encoding IDs are i32 (negative values are pseudo-encodings). pub fn write_set_encodings(w: &mut impl Write, encodings: &[i32]) -> proto::Result<()> { let count = encodings.len() as u16; let mut buf = vec![0u8; 4 + 4 * encodings.len()]; buf[0] = 2; // msg type // buf[1] = 0 (pad) buf[2] = (count >> 8) as u8; buf[3] = count as u8; for (i, &enc) in encodings.iter().enumerate() { let bytes = enc.to_be_bytes(); buf[4 + i * 4..4 + i * 4 + 4].copy_from_slice(&bytes); } w.write_all(&buf)?; w.flush()?; Ok(()) } /// Msg type 3: FramebufferUpdateRequest — aw.a(...), line 562. pub fn write_fb_update_request( w: &mut impl Write, x: u16, y: u16, width: u16, height: u16, incremental: bool, ) -> proto::Result<()> { let buf: [u8; 10] = [ 3, // msg type if incremental { 1 } else { 0 }, (x >> 8) as u8, x as u8, (y >> 8) as u8, y as u8, (width >> 8) as u8, width as u8, (height >> 8) as u8, height as u8, ]; w.write_all(&buf)?; w.flush()?; Ok(()) } /// Msg type 4: KeyEvent — aw.a(byte), line 655. /// Single scancode byte. pub fn write_key_event(w: &mut impl Write, scancode: u8) -> proto::Result<()> { w.write_all(&[4, scancode])?; w.flush()?; Ok(()) } /// Msg type 5: PointerEvent — aw.a(boolean, int, int, int, int), line 612. /// 8 bytes: [5, mask, x_u16, y_u16, extra_u16]. pub fn write_pointer_event( w: &mut impl Write, x: u16, y: u16, button_mask: u8, ) -> proto::Result<()> { let buf: [u8; 8] = [ 5, // msg type (absolute mode) button_mask, (x >> 8) as u8, x as u8, (y >> 8) as u8, y as u8, 0, 0, // extra_u16 = 0 in absolute mode ]; w.write_all(&buf)?; w.flush()?; Ok(()) } /// Msg type 149: PingResponse — aw.if(int), line 636. /// 8 bytes: [149(-107 signed), 0, 0, 0, n_i32]. pub fn write_ping_response(w: &mut impl Write, payload: i32) -> proto::Result<()> { let mut buf = [0u8; 8]; buf[0] = 149u8; // -107 as u8 // buf[1..4] = 0 (pad) buf[4..8].copy_from_slice(&payload.to_be_bytes()); w.write_all(&buf)?; w.flush()?; Ok(()) } /// Msg type 151: bandwidth measurement bookend — aw.for(byte), line 649. /// 2 bytes: [151, phase]. Phase 1 = start, 2 = done. pub fn write_bandwidth_marker(w: &mut impl Write, phase: u8) -> proto::Result<()> { w.write_all(&[151u8, phase])?; w.flush()?; Ok(()) } // --------------------------------------------------------------------------- // Server-to-client message types // --------------------------------------------------------------------------- /// Server message type tag, read as the first byte of each server message. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ServerMsg { FramebufferUpdate, // 0 SetColourMapEntries, // 1 Bell, // 2 ServerCutText, // 3 ServerNameUpdate, // 7 PixelFormatChange, // 8 LayoutLocale, // 9 DesktopResize, // 16 Ack, // 17 ModeChange, // 128 DebugString, // 131 RfbCommand, // 132 Ping, // 148 BandwidthProbe, // 150 RdpEvent, // 161 Unknown(u8), } impl From for ServerMsg { fn from(b: u8) -> Self { match b { 0 => Self::FramebufferUpdate, 1 => Self::SetColourMapEntries, 2 => Self::Bell, 3 => Self::ServerCutText, 7 => Self::ServerNameUpdate, 8 => Self::PixelFormatChange, 9 => Self::LayoutLocale, 16 => Self::DesktopResize, 17 => Self::Ack, 128 => Self::ModeChange, 131 => Self::DebugString, 132 => Self::RfbCommand, 148 => Self::Ping, 150 => Self::BandwidthProbe, 161 => Self::RdpEvent, other => Self::Unknown(other), } } } // --------------------------------------------------------------------------- // Server message readers (for dispatch loop) // --------------------------------------------------------------------------- /// Read ping payload: 3 pad bytes + i32 — aw.b(), line 629. pub fn read_ping(r: &mut impl std::io::Read) -> proto::Result { let _pad = read_exact(r, 3)?; read_i32_be(r) } /// Read and discard bandwidth probe: 1 pad + u16 len + data — aw.do(), line 642. pub fn read_bandwidth_probe(r: &mut impl std::io::Read) -> proto::Result<()> { let _pad = read_u8(r)?; let len = read_u16_be(r)? as usize; let _data = read_exact(r, len)?; Ok(()) } /// Read 2-byte ack (no-op) — aw.for(), line 553. pub fn read_ack(r: &mut impl std::io::Read) -> proto::Result<()> { let _b1 = read_u8(r)?; let _b2 = read_u8(r)?; Ok(()) } /// Read debug string — aw.d(), line 498. /// 3 pad bytes + i32 length + string bytes. pub fn read_debug_string(r: &mut impl std::io::Read) -> proto::Result { let _pad = read_exact(r, 3)?; let len = read_i32_be(r)? as usize; let data = read_exact(r, len)?; Ok(String::from_utf8_lossy(&data).into_owned()) } /// Read RFB command — aw.long(), line 507. /// 1 pad + u16 key_len + u16 val_len + key bytes + val bytes. pub fn read_rfb_command(r: &mut impl std::io::Read) -> proto::Result<(String, String)> { let _pad = read_u8(r)?; let key_len = read_u16_be(r)? as usize; let val_len = read_u16_be(r)? as usize; let key_bytes = read_exact(r, key_len)?; let val_bytes = read_exact(r, val_len)?; Ok(( String::from_utf8_lossy(&key_bytes).into_owned(), String::from_utf8_lossy(&val_bytes).into_owned(), )) } /// Read server cut text — aw.goto(), line 464 (reads i32 error code, reused /// for ServerCutText which reads the text via standard RFB: 3 pad + u32 len + text). pub fn read_server_cut_text(r: &mut impl std::io::Read) -> proto::Result { let _pad = read_exact(r, 3)?; let len = read_i32_be(r)? as usize; let data = read_exact(r, len)?; Ok(String::from_utf8_lossy(&data).into_owned()) } /// Read server name update — aw.l(), line 413. /// 1 pad + modified-UTF-8 string. pub fn read_server_name_update(r: &mut impl std::io::Read) -> proto::Result { let _pad = read_u8(r)?; read_modified_utf8(r) } /// Read layout/locale string — aw.else(), line 529. /// 1 pad + u16 len + string bytes. pub fn read_layout_locale(r: &mut impl std::io::Read) -> proto::Result { let _pad = read_u8(r)?; let len = read_u16_be(r)? as usize; let data = read_exact(r, len)?; Ok(String::from_utf8_lossy(&data).into_owned()) } /// Read RDP event type byte — aw.case(), line 558. pub fn read_rdp_event(r: &mut impl std::io::Read) -> proto::Result { proto::read_i8(r) } /// Read framebuffer update header — aw.null(), line 459. /// 1 pad byte + u16 num_rects. pub fn read_fb_update_header(r: &mut impl std::io::Read) -> proto::Result { let _pad = read_u8(r)?; read_u16_be(r) }