xim_parser/
lib.rs

1//! A parser for reading and writing the X Input Method protocol.
2//!
3//! This is intended to be used as a building block for higher level libraries. See the
4//! [`xim`] crate for an example.
5//!
6//! [`xim`]: https://crates.io/crates/xim
7
8#![allow(clippy::uninlined_format_args, clippy::needless_borrow)]
9#![forbid(unsafe_code, future_incompatible)]
10#![no_std]
11
12extern crate alloc;
13
14#[cfg(any(test, feature = "std"))]
15extern crate std;
16
17use alloc::vec::Vec;
18
19pub mod attrs;
20mod parser;
21
22pub use parser::*;
23
24pub fn write_extend_vec(f: impl XimWrite, out: &mut Vec<u8>) {
25    let from = out.len();
26    out.extend(core::iter::repeat_n(0, f.size()));
27    f.write(&mut Writer::new(&mut out[from..]));
28}
29
30pub fn write_to_vec(f: impl XimWrite) -> Vec<u8> {
31    let mut out: Vec<u8> = core::iter::repeat_n(0, f.size()).collect();
32    f.write(&mut Writer::new(&mut out));
33    out
34}
35
36#[cfg(test)]
37mod tests {
38    use crate::{parser::*, write_to_vec};
39    use alloc::vec;
40    use alloc::vec::Vec;
41    use pretty_assertions::assert_eq;
42
43    #[cfg(target_endian = "little")]
44    #[test]
45    fn read_connect_req() {
46        let req: Request = read(b"\x01\x00\x00\x00\x6c\x00\x00\x00\x00\x00\x00\x00").unwrap();
47
48        assert_eq!(
49            req,
50            Request::Connect {
51                endian: Endian::Native,
52                client_auth_protocol_names: vec![],
53                client_minor_protocol_version: 0,
54                client_major_protocol_version: 0,
55            }
56        );
57    }
58
59    #[test]
60    fn read_open() {
61        let req = read::<Request>(&[
62            30, 0, 2, 0, 5, 101, 110, 95, 85, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63        ])
64        .unwrap();
65        assert_eq!(
66            req,
67            Request::Open {
68                locale: "en_US".into(),
69            }
70        );
71    }
72
73    #[test]
74    fn read_query() {
75        let req = read::<Request>(&[
76            40, 0, 5, 0, 0, 0, 13, 0, 12, 88, 73, 77, 95, 69, 88, 84, 95, 77, 79, 86, 69, 0, 0, 0,
77        ])
78        .unwrap();
79        assert_eq!(
80            req,
81            Request::QueryExtension {
82                input_method_id: 0,
83                extensions: vec!["XIM_EXT_MOVE".into(),],
84            }
85        );
86    }
87
88    #[test]
89    fn read_input_styles() {
90        let styles: InputStyleList = read(&[1, 0, 0, 0, 4, 1, 0, 0]).unwrap();
91
92        assert_eq!(
93            styles,
94            InputStyleList {
95                styles: vec![InputStyle::PREEDIT_POSITION | InputStyle::STATUS_AREA]
96            }
97        );
98    }
99
100    #[test]
101    fn commit() {
102        let req = Request::Commit {
103            input_method_id: 1,
104            input_context_id: 1,
105            data: CommitData::Chars {
106                commited: xim_ctext::utf8_to_compound_text("맘"),
107                syncronous: false,
108            },
109        };
110
111        write_to_vec(req);
112    }
113
114    #[test]
115    fn set_event_mask() {
116        let req = Request::SetEventMask {
117            input_method_id: 2,
118            input_context_id: 1,
119            forward_event_mask: 3,
120            synchronous_event_mask: 4294967292,
121        };
122        let out = write_to_vec(&req);
123        assert_eq!(
124            out,
125            [37, 0, 3, 0, 2, 0, 1, 0, 3, 0, 0, 0, 252, 255, 255, 255]
126        );
127        assert_eq!(req, read::<Request>(&out).unwrap());
128    }
129
130    #[test]
131    fn attr_size() {
132        let list = InputStyleList {
133            styles: vec![InputStyle::PREEDIT_POSITION | InputStyle::STATUS_AREA],
134        };
135
136        assert_eq!(list.size(), 8);
137
138        let attr = Attribute {
139            id: 0,
140            value: write_to_vec(list),
141        };
142
143        assert_eq!(attr.size(), 12);
144    }
145
146    #[test]
147    fn im_reply() {
148        let req = Request::GetImValuesReply {
149            input_method_id: 3,
150            im_attributes: vec![Attribute {
151                id: 0,
152                value: write_to_vec(InputStyleList {
153                    styles: vec![InputStyle::PREEDIT_POSITION | InputStyle::STATUS_AREA],
154                }),
155            }],
156        };
157
158        let out = write_to_vec(&req);
159        assert_eq!(req.size(), out.len());
160
161        let new_req = read(&out).unwrap();
162
163        assert_eq!(req, new_req);
164    }
165
166    #[test]
167    fn spot_attr() {
168        let value = [4, 0, 4, 0, 0, 0, 0, 0];
169
170        let attr = read::<Attribute>(&value).unwrap();
171
172        assert_eq!(attr.id, 4);
173        assert_eq!(read::<Point>(&attr.value).unwrap(), Point { x: 0, y: 0 });
174    }
175
176    #[test]
177    fn read_error() {
178        let req: Request = read(&[
179            20, 0, 7, 0, 2, 0, 1, 0, 3, 0, 2, 0, 16, 0, 0, 0, 105, 110, 118, 97, 108, 105, 100, 32,
180            105, 109, 32, 115, 116, 121, 108, 101,
181        ])
182        .unwrap();
183
184        assert_eq!(
185            req,
186            Request::Error {
187                input_method_id: 2,
188                input_context_id: 1,
189                flag: ErrorFlag::INPUT_METHOD_ID_VALID | ErrorFlag::INPUT_CONTEXT_ID_VALID,
190                code: ErrorCode::BadStyle,
191                detail: "invalid im style".into(),
192            }
193        );
194    }
195
196    #[test]
197    fn write_get_im_values() {
198        let req = Request::GetImValues {
199            input_method_id: 1,
200            im_attributes: vec![0],
201        };
202
203        let out = write_to_vec(&req);
204
205        assert_eq!(out.len(), req.size());
206    }
207
208    #[test]
209    fn write_forward_event() {
210        let req = Request::ForwardEvent {
211            input_method_id: 0,
212            input_context_id: 0,
213            flag: ForwardEventFlag::empty(),
214            serial_number: 0,
215            xev: XEvent {
216                response_type: 0,
217                detail: 0,
218                sequence: 0,
219                time: 0,
220                root: 0,
221                event: 0,
222                child: 0,
223                root_x: 0,
224                root_y: 0,
225                event_x: 0,
226                event_y: 0,
227                state: 0,
228                same_screen: false,
229            },
230        };
231        assert_eq!(req.size(), 4 + 8 + 32);
232
233        let out = write_to_vec(&req);
234        assert!(out.starts_with(b"\x3c\x00\x0a\x00"));
235    }
236
237    #[test]
238    fn write_create_ic() {
239        let req = Request::CreateIc {
240            input_method_id: 2,
241            ic_attributes: Vec::new(),
242        };
243        let out = write_to_vec(&req);
244        assert_eq!(out, b"\x32\x00\x01\x00\x02\x00\x00\x00");
245    }
246
247    #[test]
248    fn write_connect_reply() {
249        let req = Request::ConnectReply {
250            server_minor_protocol_version: 0,
251            server_major_protocol_version: 1,
252        };
253        let out = write_to_vec(&req);
254        assert_eq!(out, b"\x02\x00\x01\x00\x01\x00\x00\x00");
255    }
256
257    const OPEN_REPLY: &[u8] = b"\x1f\x00\x59\x00\x01\x00\x18\x00\x00\x00\x0a\x00\x0f\x00\x71\x75\x65\x72\x79\x49\x6e\x70\x75\x74\x53\x74\x79\x6c\x65\x00\x00\x00\x44\x01\x00\x00\x01\x00\x03\x00\x0a\x00\x69\x6e\x70\x75\x74\x53\x74\x79\x6c\x65\x02\x00\x05\x00\x0c\x00\x63\x6c\x69\x65\x6e\x74\x57\x69\x6e\x64\x6f\x77\x00\x00\x03\x00\x05\x00\x0b\x00\x66\x6f\x63\x75\x73\x57\x69\x6e\x64\x6f\x77\x00\x00\x00\x04\x00\x03\x00\x0c\x00\x66\x69\x6c\x74\x65\x72\x45\x76\x65\x6e\x74\x73\x00\x00\x05\x00\xff\x7f\x11\x00\x70\x72\x65\x65\x64\x69\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x73\x00\x06\x00\xff\x7f\x10\x00\x73\x74\x61\x74\x75\x73\x41\x74\x74\x72\x69\x62\x75\x74\x65\x73\x00\x00\x07\x00\x0d\x00\x07\x00\x66\x6f\x6e\x74\x53\x65\x74\x00\x00\x00\x08\x00\x0b\x00\x04\x00\x61\x72\x65\x61\x00\x00\x09\x00\x0b\x00\x0a\x00\x61\x72\x65\x61\x4e\x65\x65\x64\x65\x64\x0a\x00\x03\x00\x08\x00\x63\x6f\x6c\x6f\x72\x4d\x61\x70\x00\x00\x0b\x00\x03\x00\x0b\x00\x73\x74\x64\x43\x6f\x6c\x6f\x72\x4d\x61\x70\x00\x00\x00\x0c\x00\x03\x00\x0a\x00\x66\x6f\x72\x65\x67\x72\x6f\x75\x6e\x64\x0d\x00\x03\x00\x0a\x00\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x0e\x00\x03\x00\x10\x00\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x50\x69\x78\x6d\x61\x70\x00\x00\x0f\x00\x0c\x00\x0c\x00\x73\x70\x6f\x74\x4c\x6f\x63\x61\x74\x69\x6f\x6e\x00\x00\x10\x00\x03\x00\x09\x00\x6c\x69\x6e\x65\x53\x70\x61\x63\x65\x00\x11\x00\x00\x00\x15\x00\x73\x65\x70\x61\x72\x61\x74\x6f\x72\x6f\x66\x4e\x65\x73\x74\x65\x64\x4c\x69\x73\x74\x00";
258
259    fn open_reply_value() -> Request {
260        Request::OpenReply {
261            input_method_id: 1,
262            im_attrs: vec![Attr {
263                id: 0,
264                ty: AttrType::Style,
265                name: AttributeName::QueryInputStyle,
266            }],
267            ic_attrs: vec![
268                Attr {
269                    id: 1,
270                    ty: AttrType::Long,
271                    name: AttributeName::InputStyle,
272                },
273                Attr {
274                    id: 2,
275                    ty: AttrType::Window,
276                    name: AttributeName::ClientWindow,
277                },
278                Attr {
279                    id: 3,
280                    ty: AttrType::Window,
281                    name: AttributeName::FocusWindow,
282                },
283                Attr {
284                    id: 4,
285                    ty: AttrType::Long,
286                    name: AttributeName::FilterEvents,
287                },
288                Attr {
289                    id: 5,
290                    ty: AttrType::NestedList,
291                    name: AttributeName::PreeditAttributes,
292                },
293                Attr {
294                    id: 6,
295                    ty: AttrType::NestedList,
296                    name: AttributeName::StatusAttributes,
297                },
298                Attr {
299                    id: 7,
300                    ty: AttrType::XFontSet,
301                    name: AttributeName::FontSet,
302                },
303                Attr {
304                    id: 8,
305                    ty: AttrType::XRectangle,
306                    name: AttributeName::Area,
307                },
308                Attr {
309                    id: 9,
310                    ty: AttrType::XRectangle,
311                    name: AttributeName::AreaNeeded,
312                },
313                Attr {
314                    id: 10,
315                    ty: AttrType::Long,
316                    name: AttributeName::ColorMap,
317                },
318                Attr {
319                    id: 11,
320                    ty: AttrType::Long,
321                    name: AttributeName::StdColorMap,
322                },
323                Attr {
324                    id: 12,
325                    ty: AttrType::Long,
326                    name: AttributeName::Foreground,
327                },
328                Attr {
329                    id: 13,
330                    ty: AttrType::Long,
331                    name: AttributeName::Background,
332                },
333                Attr {
334                    id: 14,
335                    ty: AttrType::Long,
336                    name: AttributeName::BackgroundPixmap,
337                },
338                Attr {
339                    id: 15,
340                    ty: AttrType::XPoint,
341                    name: AttributeName::SpotLocation,
342                },
343                Attr {
344                    id: 16,
345                    ty: AttrType::Long,
346                    name: AttributeName::LineSpace,
347                },
348                Attr {
349                    id: 17,
350                    ty: AttrType::Separator,
351                    name: AttributeName::SeparatorofNestedList,
352                },
353            ],
354        }
355    }
356
357    #[test]
358    fn read_open_reply() {
359        assert_eq!(read::<Request>(OPEN_REPLY).unwrap(), open_reply_value());
360    }
361
362    #[test]
363    fn size_open_reply() {
364        assert_eq!(open_reply_value().size(), OPEN_REPLY.len());
365    }
366
367    #[test]
368    fn write_open_reply() {
369        let value = open_reply_value();
370        let out = write_to_vec(&value);
371        assert_eq!(OPEN_REPLY.len(), out.len());
372        assert_eq!(OPEN_REPLY, out);
373        let new: Request = read(&out).unwrap();
374        assert_eq!(value, new);
375    }
376}