now_proto_pdu/session/
set_kbd_layout.rs1use alloc::borrow::Cow;
2
3use bitflags::bitflags;
4use ironrdp_core::{
5 cast_length, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, IntoOwned, ReadCursor, WriteCursor,
6};
7
8use crate::{NowHeader, NowMessage, NowMessageClass, NowSessionMessage, NowSessionMessageKind, NowVarStr};
9
10bitflags! {
11 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12 pub struct NowSessionSetKbdLayoutFlags: u16 {
13 const NEXT_LAYOUT = 0x0001;
18
19 const PREV_LAYOUT = 0x0002;
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum SetKbdLayoutOption<'a> {
29 Next,
30 Prev,
31 Specific(&'a str),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct NowSessionSetKbdLayoutMsg<'a> {
41 flags: NowSessionSetKbdLayoutFlags,
42 layout: NowVarStr<'a>,
43}
44
45impl_pdu_borrowing!(NowSessionSetKbdLayoutMsg<'_>, OwnedNowSessionSetKbdLayoutMsg);
46
47impl IntoOwned for NowSessionSetKbdLayoutMsg<'_> {
48 type Owned = NowSessionSetKbdLayoutMsg<'static>;
49
50 fn into_owned(self) -> Self::Owned {
51 NowSessionSetKbdLayoutMsg {
52 flags: self.flags,
53 layout: self.layout.into_owned(),
54 }
55 }
56}
57
58impl<'a> NowSessionSetKbdLayoutMsg<'a> {
59 const NAME: &'static str = "NOW_SESSION_SET_KBD_LAYOUT_MSG";
60
61 pub fn new_next() -> Self {
62 Self {
63 flags: NowSessionSetKbdLayoutFlags::NEXT_LAYOUT,
64 layout: NowVarStr::default(),
65 }
66 }
67
68 pub fn new_prev() -> Self {
69 Self {
70 flags: NowSessionSetKbdLayoutFlags::PREV_LAYOUT,
71 layout: NowVarStr::default(),
72 }
73 }
74
75 pub fn new_specific(layout: impl Into<Cow<'a, str>>) -> EncodeResult<Self> {
76 let layout = NowVarStr::new(layout.into())?;
77
78 ensure_now_message_size!(layout.size());
79
80 Ok(Self {
81 flags: NowSessionSetKbdLayoutFlags::empty(),
82 layout,
83 })
84 }
85
86 pub fn layout(&'a self) -> SetKbdLayoutOption<'a> {
87 if self.flags.contains(NowSessionSetKbdLayoutFlags::NEXT_LAYOUT) {
88 return SetKbdLayoutOption::Next;
89 }
90
91 if self.flags.contains(NowSessionSetKbdLayoutFlags::PREV_LAYOUT) {
92 return SetKbdLayoutOption::Prev;
93 }
94
95 SetKbdLayoutOption::Specific(&self.layout)
96 }
97
98 fn body_size(&self) -> usize {
99 self.layout.size()
100 }
101
102 pub(super) fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'a>) -> DecodeResult<Self> {
103 let flags = NowSessionSetKbdLayoutFlags::from_bits_retain(header.flags);
104 let layout = NowVarStr::decode(src)?;
105
106 if flags.contains(NowSessionSetKbdLayoutFlags::NEXT_LAYOUT)
107 && flags.contains(NowSessionSetKbdLayoutFlags::PREV_LAYOUT)
108 {
109 return Err(invalid_field_err!("flags", "both NEXT and PREV flags are set"));
110 }
111
112 Ok(Self { flags, layout })
113 }
114}
115
116impl Encode for NowSessionSetKbdLayoutMsg<'_> {
117 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
118 let header = NowHeader {
119 size: cast_length!("size", self.body_size())?,
120 class: NowMessageClass::SESSION,
121 kind: NowSessionMessageKind::SET_KBD_LAYOUT.0,
122 flags: self.flags.bits(),
123 };
124
125 header.encode(dst)?;
126 self.layout.encode(dst)?;
127
128 Ok(())
129 }
130
131 fn name(&self) -> &'static str {
132 Self::NAME
133 }
134
135 fn size(&self) -> usize {
136 NowHeader::FIXED_PART_SIZE + self.body_size()
137 }
138}
139
140impl<'de> Decode<'de> for NowSessionSetKbdLayoutMsg<'de> {
141 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
142 let header = NowHeader::decode(src)?;
143
144 match (header.class, NowSessionMessageKind(header.kind)) {
145 (NowMessageClass::SESSION, NowSessionMessageKind::SET_KBD_LAYOUT) => Self::decode_from_body(header, src),
146 _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)),
147 }
148 }
149}
150
151impl<'a> From<NowSessionSetKbdLayoutMsg<'a>> for NowMessage<'a> {
152 fn from(msg: NowSessionSetKbdLayoutMsg<'a>) -> Self {
153 NowMessage::Session(NowSessionMessage::SetKbdLayout(msg))
154 }
155}