now_proto_pdu/session/
window_rec_start.rs

1use bitflags::bitflags;
2use ironrdp_core::{cast_length, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor};
3
4use crate::{NowHeader, NowMessage, NowMessageClass, NowSessionMessage, NowSessionMessageKind};
5
6bitflags! {
7    /// Flags for window recording start message.
8    ///
9    /// NOW_PROTO: NOW_SESSION_WINDOW_REC_START_MSG msgFlags
10    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
11    pub struct WindowRecStartFlags: u16 {
12        /// Enable window title change tracking.
13        ///
14        /// NOW-PROTO: NOW_WINDOW_REC_FLAG_TRACK_TITLE_CHANGE
15        const TRACK_TITLE_CHANGE = 0x0001;
16    }
17}
18
19/// The NOW_SESSION_WINDOW_REC_START_MSG message is used to start window recording, which tracks
20/// active window changes and title updates.
21///
22/// NOW_PROTO: NOW_SESSION_WINDOW_REC_START_MSG
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct NowSessionWindowRecStartMsg {
25    /// Flags for window recording options
26    pub flags: WindowRecStartFlags,
27    /// Interval in milliseconds for polling window changes.
28    /// Set to 0 to use the host's default poll interval.
29    pub poll_interval: u32,
30}
31
32impl NowSessionWindowRecStartMsg {
33    const NAME: &'static str = "NOW_SESSION_WINDOW_REC_START_MSG";
34    const FIXED_PART_SIZE: usize = 4;
35
36    pub fn new(poll_interval: u32, flags: WindowRecStartFlags) -> Self {
37        Self { poll_interval, flags }
38    }
39}
40
41impl Encode for NowSessionWindowRecStartMsg {
42    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
43        let header = NowHeader {
44            size: cast_length!("size", Self::FIXED_PART_SIZE)?,
45            class: NowMessageClass::SESSION,
46            kind: NowSessionMessageKind::WINDOW_REC_START.0,
47            flags: self.flags.bits(),
48        };
49
50        header.encode(dst)?;
51        dst.write_u32(self.poll_interval);
52
53        Ok(())
54    }
55
56    fn name(&self) -> &'static str {
57        Self::NAME
58    }
59
60    fn size(&self) -> usize {
61        NowHeader::FIXED_PART_SIZE + Self::FIXED_PART_SIZE
62    }
63}
64
65impl Decode<'_> for NowSessionWindowRecStartMsg {
66    fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
67        let header = NowHeader::decode(src)?;
68
69        match (header.class, NowSessionMessageKind(header.kind)) {
70            (NowMessageClass::SESSION, NowSessionMessageKind::WINDOW_REC_START) => Self::decode_from_body(header, src),
71            _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)),
72        }
73    }
74}
75
76impl NowSessionWindowRecStartMsg {
77    pub(crate) fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
78        let flags = WindowRecStartFlags::from_bits_retain(header.flags);
79        let poll_interval = src.read_u32();
80
81        Ok(Self { poll_interval, flags })
82    }
83}
84
85impl From<NowSessionWindowRecStartMsg> for NowMessage<'_> {
86    fn from(value: NowSessionWindowRecStartMsg) -> Self {
87        Self::Session(NowSessionMessage::WindowRecStart(value))
88    }
89}