1use crate::schema::{
4 AssetKind, BackgroundRepeat, ColorScheme, DesignSystem, FileDelivery, FileDialogKind,
5 FileDialogStatus, FileOp, IoStatus, Key, MAX_MODIFIER_WORDS, MaterialRole, MessageDuration,
6 MessageHorizontal, MessagePlacement, MessageVertical, Modifier, MotionRole,
7 NotificationImportance, NotificationPermission, NotificationPresentation, Paint, PropertyKind,
8 SEMANTICS_STATE_MASK, Selection, SemanticsRole, ShapeRole, SpaceRole, TYPE_ROLE_COUNT, Theme,
9 WidgetKind, modifier_extra_words,
10};
11use core::fmt;
12
13const TAG_ENVELOPE: u16 = 0;
14const TAG_CREATE: u16 = 1;
15const TAG_SET_PROP: u16 = 2;
16const TAG_SET_MODIFIER: u16 = 3;
17const TAG_INSERT: u16 = 4;
18const TAG_MOVE: u16 = 5;
19const TAG_REMOVE: u16 = 6;
20const TAG_SET_TEXT: u16 = 7;
21const TAG_APPEND_TEXT: u16 = 8;
22const TAG_SET_THEME: u16 = 9;
23const TAG_REGISTER_ASSET: u16 = 10;
24const TAG_RELEASE_ASSET: u16 = 11;
25const TAG_SHOW_MESSAGE: u16 = 12;
26const TAG_SET_WINDOW: u16 = 13;
27const TAG_POST_NOTIFICATION: u16 = 14;
28const TAG_WITHDRAW_NOTIFICATION: u16 = 15;
29const TAG_REQUEST_NOTIFICATION_PERMISSION: u16 = 16;
30const TAG_EDIT_CODE: u16 = 17;
31const TAG_START_ANIMATION: u16 = 18;
32const TAG_CONTROL_ANIMATION: u16 = 19;
33const TAG_SET_SELECTION: u16 = 20;
35const TAG_REVEAL_RANGE: u16 = 21;
36const TAG_SET_FOLDED: u16 = 22;
37const TAG_SET_WINDOW_THEME: u16 = 23;
39const TAG_FOCUS_WINDOW: u16 = 24;
40const TAG_REQUEST_FOCUS: u16 = 25;
41const TAG_SHOW_FILE_DIALOG: u16 = 26;
43const SHOW_FILE_DIALOG_PAYLOAD_LEN: usize = 4 + 4 + 2 + 2 + 4 * 8;
46const TAG_SET_OVERLAY_TOKENS: u16 = 27;
48const TAG_EDIT_CODE_GROUP: u16 = 28;
50const EDIT_CODE_GROUP_LEN: usize = 24;
53const TAG_SET_SCROLL_OFFSET: u16 = 30;
54const OVERLAY_TOKENS_PAYLOAD_LEN: usize = 28;
56const TAG_SET_CLIPBOARD_TEXT: u16 = 29;
58const TAG_READ_CLIPBOARD: u16 = 31;
61const TAG_STORAGE_CALL: u16 = 32;
64const STORAGE_CALL_PAYLOAD_LEN: usize = 4 + 2 + 2 + 3 * 8;
67const TAG_FILE_CALL: u16 = 33;
71const FILE_CALL_PAYLOAD_LEN: usize = 4 + 2 + 2 + 4 + 8 + 8;
74const TAG_RELEASE_FILE_HANDLE: u16 = 34;
75const TAG_SCROLL_TO: u16 = 35;
79const TAG_SCROLL_INTO_VIEW: u16 = 36;
80const TAG_QUERY_POSITION_RECT: u16 = 39;
83const QUERY_POSITION_RECT_LEN: usize = 20;
86const TAG_SET_SELECTIONS: u16 = 41;
88const SET_SELECTIONS_LEN: usize = 20;
91const TAG_INSERT_SNIPPET: u16 = 40;
93const INSERT_SNIPPET_LEN: usize = 36;
96const SCROLL_TO_LEN: usize = 32;
98const SCROLL_INTO_VIEW_LEN: usize = 52;
100const ANIMATION_HEADER_LEN: usize = 52;
102const KEYFRAME_HEAD_LEN: usize = 24;
104const TRANSFORM_FUNCTION_LEN: usize = 28;
106const KEYFRAME_FROM_PRESENTED: u16 = 1;
107const ENVELOPE_LEN: usize = 12;
108pub const THEME_PAYLOAD_LEN: usize = 8 + 4 * TYPE_ROLE_COUNT + 8;
111const MIN_RECORD_LEN: usize = 4;
114
115const VALUE_NONE: u16 = 0;
116const VALUE_STRING: u16 = 1;
117const VALUE_BOOL: u16 = 2;
118const VALUE_I64: u16 = 3;
119const VALUE_F32: u16 = 4;
120const VALUE_BYTES: u16 = 5;
123
124#[derive(Clone, Debug, PartialEq)]
125pub enum PropertyValue<'a> {
126 None,
127 String(&'a str),
128 Bool(bool),
129 Integer(i64),
130 Float(f32),
131 Bytes(&'a [u8]),
132}
133
134#[derive(Clone, Debug, PartialEq)]
135pub enum Mutation<'a> {
136 Create {
137 node_id: u32,
138 widget: WidgetKind,
139 },
140 SetProp {
141 node_id: u32,
142 property: PropertyKind,
143 value: PropertyValue<'a>,
144 },
145 SetModifier {
146 node_id: u32,
147 index: u16,
148 modifier: Modifier,
149 },
150 Insert {
151 parent_id: u32,
152 node_id: u32,
153 index: u32,
154 },
155 Move {
156 parent_id: u32,
157 node_id: u32,
158 index: u32,
159 },
160 Remove {
161 node_id: u32,
162 },
163 SetText {
164 node_id: u32,
165 text: &'a str,
166 selection: Option<Selection>,
167 },
168 AppendText {
170 node_id: u32,
171 text: &'a str,
172 },
173 SetTheme(Theme),
175 SetWindow(crate::schema::Window),
183 RegisterAsset {
187 asset_id: u32,
188 kind: AssetKind,
189 bytes: &'a [u8],
190 },
191 ReleaseAsset {
193 asset_id: u32,
194 },
195 ShowMessage {
208 handler_id: u64,
209 text: &'a str,
210 action: &'a str,
211 duration: MessageDuration,
212 placement: Option<MessagePlacement>,
213 offset_dp: Option<f32>,
214 },
215 PostNotification {
223 key: &'a str,
224 title: &'a str,
225 body: &'a str,
226 channel: &'a str,
227 action_1: &'a str,
228 action_2: &'a str,
229 importance: NotificationImportance,
230 presentation: NotificationPresentation,
231 },
232 WithdrawNotification {
234 key: &'a str,
235 },
236 RequestNotificationPermission,
239 EditCode {
248 node_id: u32,
249 request_id: u32,
250 base_version: u32,
251 start_line: u32,
252 start_column: u32,
253 end_line: u32,
254 end_column: u32,
255 text: &'a str,
256 },
257 StartAnimation(crate::schema::Animation<'a>),
260 ControlAnimation {
264 node_id: u32,
265 animation_id: u32,
266 property: crate::schema::AnimatedProperty,
267 slot: u8,
268 op: crate::schema::AnimationControl,
269 at_time_nanos: u64,
271 },
272 SetSelection {
277 node_id: u32,
278 base_version: u32,
279 anchor_line: u32,
280 anchor_column: u32,
281 active_line: u32,
282 active_column: u32,
283 },
284 RevealRange {
287 node_id: u32,
288 base_version: u32,
289 start_line: u32,
290 start_column: u32,
291 end_line: u32,
292 end_column: u32,
293 align: crate::schema::RevealAlign,
294 },
295 SetFolded {
297 node_id: u32,
298 start_line: u32,
299 collapsed: bool,
300 },
301 SetWindowTheme {
304 node_id: u32,
305 theme: Theme,
306 },
307 FocusWindow {
310 node_id: u32,
311 },
312 RequestFocus {
314 node_id: u32,
315 },
316 ShowFileDialog {
324 request: u32,
325 window: u32,
326 kind: FileDialogKind,
327 default_filter: u16,
328 title: &'a str,
329 directory: &'a str,
330 name: &'a str,
331 filters: &'a str,
332 },
333 SetOverlayTokens(crate::schema::OverlayTokens),
335 SetClipboardText {
339 text: &'a str,
340 },
341 ReadClipboard {
344 request: u32,
345 },
346 SetScrollOffset {
350 node_id: u32,
351 offset_px: f64,
352 },
353 StorageCall {
360 request: u32,
361 op: crate::schema::StorageOp,
362 store: &'a str,
363 key: &'a str,
364 data: &'a [u8],
365 },
366 FileCall {
372 request: u32,
373 op: FileOp,
374 create: bool,
375 handle: u32,
376 name: &'a str,
377 data: &'a [u8],
378 },
379 ReleaseFileHandle {
382 handle: u32,
383 },
384 ScrollTo {
389 node_id: u32,
390 item: u32,
391 flags: u32,
392 x_px: f64,
393 y_px: f64,
394 },
395 ScrollIntoView {
399 node_id: u32,
400 target: u32,
401 target_kind: u16,
402 align_x: u16,
403 align_y: u16,
404 flags: u16,
405 rect: crate::schema::Rect,
406 },
407 QueryPositionRect {
411 node_id: u32,
412 request: u32,
413 line: u32,
414 column: u32,
415 },
416 EditCodeGroup {
425 node_id: u32,
426 request_id: u32,
427 base_version: u32,
428 edits: &'a [u8],
429 },
430 InsertSnippet {
437 node_id: u32,
438 base_version: u32,
439 start_line: u32,
440 start_column: u32,
441 end_line: u32,
442 end_column: u32,
443 snippet: &'a str,
444 },
445 SetSelections {
450 node_id: u32,
451 base_version: u32,
452 selections: &'a [u8],
453 },
454}
455
456#[derive(Clone, Debug, Eq, PartialEq)]
457pub enum ProtocolError {
458 Truncated,
459 InvalidEnvelope,
460 InvalidRecordLength,
461 InvalidTag(u16),
462 InvalidWidget(u16),
463 InvalidProperty(u16),
464 InvalidValueKind(u16),
465 InvalidModifier(u16),
466 InvalidTheme(u16),
467 InvalidAssetKind(u16),
468 InvalidMessageDuration(u16),
469 InvalidMessagePlacement(u16),
472 InvalidNotification(u16),
474 InvalidStringRange,
475 InvalidUtf8,
476 LengthOverflow,
477 MeasureRefused(i32),
480 InvalidAnimation,
485 MisplacedWindow,
489}
490
491#[derive(Clone, Debug, PartialEq)]
492pub struct HostEvent<'a> {
493 pub node_id: u32,
494 pub handler_id: u64,
495 pub payload: crate::schema::EventPayload<'a>,
496}
497
498const EVENT_CLICK: u16 = 1;
499const EVENT_TEXT_CHANGED: u16 = 2;
500const EVENT_TEXT_SUBMITTED: u16 = 3;
501const EVENT_FOCUS_LOST: u16 = 4;
502const EVENT_PROTOCOL_ERROR: u16 = 5;
503const EVENT_KEY_DOWN: u16 = 6;
504const EVENT_RANGE_REQUESTED: u16 = 7;
505const EVENT_POINTER: u16 = 8;
508const POINTER_RECORD_LEN: usize = 72;
509const EVENT_VALUE_CHANGED: u16 = 16;
510const EVENT_WINDOW_SIZE_CHANGED: u16 = 17;
511const EVENT_RESYNC: u16 = 18;
512const EVENT_LIFECYCLE_START: u16 = 19;
513const EVENT_LIFECYCLE_STOP: u16 = 20;
514const EVENT_DESIGN_SYSTEM_RESOLVED: u16 = 21;
515const EVENT_FILES_ENTERED: u16 = 22;
516const EVENT_FILES_DROPPED: u16 = 23;
517const EVENT_NOTIFICATION_ACTIVATED: u16 = 24;
518const EVENT_NOTIFICATION_PERMISSION_CHANGED: u16 = 25;
519const EVENT_CODE_CHANGED: u16 = 26;
520const EVENT_CODE_EDIT_REJECTED: u16 = 27;
521const EVENT_CODE_HOVERED: u16 = 28;
522const EVENT_CODE_SAVE_REQUESTED: u16 = 29;
523const EVENT_DECORATION_ACTIVATED: u16 = 30;
524const EVENT_ANIMATION: u16 = 31;
525const EVENT_REDUCED_MOTION_CHANGED: u16 = 32;
526const EVENT_ZOOM_CHANGED: u16 = 33;
528const EVENT_SELECTION_CHANGED: u16 = 34;
530const EVENT_VISIBLE_RANGE_CHANGED: u16 = 35;
531const EVENT_EDITOR_FOCUS_CHANGED: u16 = 36;
532const EVENT_FOLD_CHANGED: u16 = 37;
533const EVENT_COMPOSITION_CHANGED: u16 = 38;
534const EVENT_WINDOW_CLOSE_REQUESTED: u16 = 39;
536const EVENT_WINDOW_FOCUS_CHANGED: u16 = 40;
537const EVENT_WINDOW_MOVED: u16 = 41;
538const EVENT_WINDOW_DRAG: u16 = 42;
539const EVENT_WINDOW_DRAG_OVER: u16 = 43;
540const EVENT_WINDOW_UNAVAILABLE: u16 = 44;
541const EVENT_WINDOW_CLOSED: u16 = 45;
542const EVENT_WINDOW_REQUESTED: u16 = 46;
543const EVENT_APPLICATION_REOPEN: u16 = 47;
544const EVENT_FOCUS_GAINED: u16 = 48;
546const EVENT_KEY_UP: u16 = 49;
547const EVENT_FILE_DIALOG_RESULT: u16 = 50;
548const EVENT_CODE_EDIT_GROUP_RESULT: u16 = 51;
550const CODE_EDIT_GROUP_RESULT_LEN: usize = 36;
552const EVENT_COLOR_SCHEME_CHANGED: u16 = 52;
556const EVENT_CODE_METRICS: u16 = 54;
557const EVENT_CODE_SCROLLED: u16 = 55;
558const FILE_DIALOG_RESULT_LEN: usize = 36;
560const EVENT_CLIPBOARD_TEXT: u16 = 56;
562const CLIPBOARD_TEXT_LEN: usize = 36;
564const EVENT_IO_ANSWER: u16 = 57;
565const IO_ANSWER_LEN: usize = 32;
567const EVENT_FILE_HANDLES: u16 = 58;
568const FILE_HANDLES_LEN: usize = 52;
570const EVENT_STORAGE_LOCATION: u16 = 59;
571const EVENT_SCROLL_CHANGED: u16 = 60;
572const SCROLL_CHANGED_LEN: usize = 72;
574const EVENT_MOTION_FINISHED: u16 = 63;
576const MOTION_FINISHED_LEN: usize = 24;
578const EVENT_CARET_RECT: u16 = 64;
581const EVENT_POSITION_RECT: u16 = 65;
582const CODE_RECT_EVENT_LEN: usize = 40;
584const EVENT_CODE_POINTER: u16 = 66;
586const EVENT_SELECTIONS_CHANGED: u16 = 67;
588const SELECTIONS_CHANGED_LEN: usize = 28;
589const CODE_POINTER_LEN: usize = POINTER_RECORD_LEN + 8;
590const NO_CODE_POSITION: u32 = u32::MAX;
592
593const WINDOW_MODE_SHIFT: u32 = 8;
596const WINDOW_POLICY_SHIFT: u32 = 12;
597
598pub const WINDOW_DRAG_MOVE: u32 = 1;
600pub const WINDOW_DRAG_END: u32 = 2;
601pub const WINDOW_DRAG_CANCEL: u32 = 3;
602pub const WINDOW_DRAG_OVER_ENTER: u32 = 1;
604pub const WINDOW_DRAG_OVER_MOVE: u32 = 2;
605pub const WINDOW_DRAG_OVER_LEAVE: u32 = 3;
606pub const WINDOW_DRAG_OVER_DROP: u32 = 4;
607
608const MODIFIER_SHIFT: u8 = 1 << 0;
609const MODIFIER_CTRL: u8 = 1 << 1;
610const MODIFIER_ALT: u8 = 1 << 2;
611const MODIFIER_META: u8 = 1 << 3;
612
613fn pointer_tag<T: TryFrom<u16, Error = ()>>(
616 bytes: &[u8],
617 offset: usize,
618) -> Result<T, ProtocolError> {
619 let raw = read_u32(bytes, offset)?;
620 u16::try_from(raw)
621 .ok()
622 .and_then(|tag| T::try_from(tag).ok())
623 .ok_or(ProtocolError::InvalidValueKind(
624 raw.min(u32::from(u16::MAX)) as u16,
625 ))
626}
627
628fn decode_pointer(bytes: &[u8]) -> Result<crate::schema::EventPayload<'_>, ProtocolError> {
630 Ok(crate::schema::EventPayload::Pointer {
631 kind: pointer_tag(bytes, 16)?,
632 pointer_id: read_u32(bytes, 20)?,
633 pointer_type: pointer_tag(bytes, 24)?,
634 x: f32::from_bits(read_u32(bytes, 28)?),
635 y: f32::from_bits(read_u32(bytes, 32)?),
636 buttons: read_u32(bytes, 36)?,
637 button: read_u32(bytes, 40)?,
638 modifiers: read_u32(bytes, 44)?,
639 click_count: read_u32(bytes, 48)?,
640 delta_x: f32::from_bits(read_u32(bytes, 52)?),
641 delta_y: f32::from_bits(read_u32(bytes, 56)?),
642 delta_mode: pointer_tag(bytes, 60)?,
643 timestamp_nanos: read_u64(bytes, 64)?,
644 })
645}
646
647pub fn decode_event(bytes: &[u8]) -> Result<HostEvent<'_>, ProtocolError> {
649 if bytes.len() < 16 {
650 return Err(ProtocolError::Truncated);
651 }
652 let tag = read_u16(bytes, 0)?;
653 let record_len = usize::from(read_u16(bytes, 2)?);
654 if record_len < 16 || record_len % 4 != 0 || record_len > bytes.len() {
655 return Err(ProtocolError::InvalidRecordLength);
656 }
657 let node_id = read_u32(bytes, 4)?;
658 let handler_id = read_u64(bytes, 8)?;
659 let payload = match tag {
660 EVENT_CLICK if record_len == 16 => crate::schema::EventPayload::Clicked,
661 EVENT_TEXT_CHANGED if record_len == 24 => {
662 crate::schema::EventPayload::TextChanged(read_string(bytes, 16, record_len)?)
663 }
664 EVENT_TEXT_SUBMITTED if record_len == 24 => {
665 crate::schema::EventPayload::TextSubmitted(read_string(bytes, 16, record_len)?)
666 }
667 EVENT_FOCUS_LOST if record_len == 16 => crate::schema::EventPayload::FocusLost,
668 EVENT_FOCUS_GAINED if record_len == 16 => crate::schema::EventPayload::FocusGained,
669 EVENT_PROTOCOL_ERROR if record_len == 28 => crate::schema::EventPayload::ProtocolError {
670 code: read_u32(bytes, 16)?,
671 message: read_string(bytes, 20, record_len)?,
672 },
673 EVENT_KEY_DOWN if record_len == 32 => {
674 let raw_key = read_u16(bytes, 16)?;
675 let modifiers = *bytes.get(18).ok_or(ProtocolError::Truncated)?;
676 let repeat = *bytes.get(19).ok_or(ProtocolError::Truncated)? != 0;
677 crate::schema::EventPayload::KeyDown {
678 key: Key::try_from(raw_key)
679 .map_err(|()| ProtocolError::InvalidValueKind(raw_key))?,
680 shift_key: modifiers & MODIFIER_SHIFT != 0,
681 ctrl_key: modifiers & MODIFIER_CTRL != 0,
682 alt_key: modifiers & MODIFIER_ALT != 0,
683 meta_key: modifiers & MODIFIER_META != 0,
684 repeat,
685 code: read_u32(bytes, 20)?,
686 text: read_string(bytes, 24, record_len)?,
687 }
688 }
689 EVENT_KEY_UP if record_len == 32 => {
691 let raw_key = read_u16(bytes, 16)?;
692 let modifiers = *bytes.get(18).ok_or(ProtocolError::Truncated)?;
693 let repeat = *bytes.get(19).ok_or(ProtocolError::Truncated)? != 0;
694 crate::schema::EventPayload::KeyUp {
695 key: Key::try_from(raw_key)
696 .map_err(|()| ProtocolError::InvalidValueKind(raw_key))?,
697 shift_key: modifiers & MODIFIER_SHIFT != 0,
698 ctrl_key: modifiers & MODIFIER_CTRL != 0,
699 alt_key: modifiers & MODIFIER_ALT != 0,
700 meta_key: modifiers & MODIFIER_META != 0,
701 repeat,
702 code: read_u32(bytes, 20)?,
703 text: read_string(bytes, 24, record_len)?,
704 }
705 }
706 EVENT_RANGE_REQUESTED if record_len == 24 => crate::schema::EventPayload::RangeRequested {
707 start: read_u32(bytes, 16)?,
708 count: read_u32(bytes, 20)?,
709 },
710 EVENT_VALUE_CHANGED if record_len == 24 => {
711 crate::schema::EventPayload::ValueChanged(f64::from_bits(read_u64(bytes, 16)?))
712 }
713 EVENT_WINDOW_SIZE_CHANGED if record_len == 44 => {
714 let raw_class = read_u32(bytes, 24)?;
715 let class = u16::try_from(raw_class)
716 .ok()
717 .and_then(|value| crate::schema::WindowSizeClass::try_from(value).ok())
718 .ok_or(ProtocolError::InvalidValueKind(raw_class as u16))?;
719 let raw_height_class = read_u32(bytes, 28)?;
720 let height_class = u16::try_from(raw_height_class)
721 .ok()
722 .and_then(|value| crate::schema::WindowHeightClass::try_from(value).ok())
723 .ok_or(ProtocolError::InvalidValueKind(raw_height_class as u16))?;
724 crate::schema::EventPayload::WindowSizeChanged {
725 width_dp: f32::from_bits(read_u32(bytes, 16)?),
726 height_dp: f32::from_bits(read_u32(bytes, 20)?),
727 class,
728 height_class,
729 safe_area_top: f32::from_bits(read_u32(bytes, 32)?),
730 safe_area_leading: f32::from_bits(read_u32(bytes, 36)?),
731 safe_area_trailing: f32::from_bits(read_u32(bytes, 40)?),
732 }
733 }
734 EVENT_DESIGN_SYSTEM_RESOLVED if record_len == 20 => {
735 let raw = read_u32(bytes, 16)?;
736 let system = u16::try_from(raw)
737 .ok()
738 .and_then(|tag| crate::schema::DesignSystem::try_from(tag).ok())
739 .ok_or(ProtocolError::InvalidValueKind(raw as u16))?;
740 crate::schema::EventPayload::DesignSystemResolved(system)
741 }
742 EVENT_FILES_ENTERED if record_len == 16 => crate::schema::EventPayload::FilesEntered,
743 EVENT_FILES_DROPPED if record_len == 24 => {
744 crate::schema::EventPayload::FilesDropped(read_string(bytes, 16, record_len)?)
745 }
746 EVENT_NOTIFICATION_ACTIVATED if record_len == 28 => {
747 let action = read_u32(bytes, 16)?;
748 if action > 2 {
751 return Err(ProtocolError::InvalidValueKind(
752 action.min(u32::from(u16::MAX)) as u16,
753 ));
754 }
755 crate::schema::EventPayload::NotificationActivated {
756 action,
757 key: read_string(bytes, 20, record_len)?,
758 }
759 }
760 EVENT_NOTIFICATION_PERMISSION_CHANGED if record_len == 20 => {
761 let raw = read_u32(bytes, 16)?;
762 let state = u16::try_from(raw)
763 .ok()
764 .and_then(|tag| NotificationPermission::try_from(tag).ok())
765 .ok_or(ProtocolError::InvalidValueKind(
766 raw.min(u32::from(u16::MAX)) as u16,
767 ))?;
768 crate::schema::EventPayload::NotificationPermissionChanged(state)
769 }
770 EVENT_ANIMATION if record_len == 40 => {
771 let raw_kind = u16::from(read_u8(bytes, 20)?);
772 let raw_property = u16::from(read_u8(bytes, 21)?);
773 crate::schema::EventPayload::AnimationEvent {
774 animation_id: read_u32(bytes, 16)?,
775 kind: crate::schema::AnimationEventKind::try_from(raw_kind)
776 .map_err(|()| ProtocolError::InvalidValueKind(raw_kind))?,
777 property: crate::schema::AnimatedProperty::try_from(raw_property)
778 .map_err(|()| ProtocolError::InvalidValueKind(raw_property))?,
779 slot: read_u8(bytes, 22)?,
780 iteration: read_u32(bytes, 24)?,
781 elapsed_ms: f32::from_bits(read_u32(bytes, 28)?),
782 time_nanos: read_u64(bytes, 32)?,
783 }
784 }
785 EVENT_POINTER if record_len == POINTER_RECORD_LEN => decode_pointer(bytes)?,
786 EVENT_REDUCED_MOTION_CHANGED if record_len == 20 => {
787 let raw = read_u32(bytes, 16)?;
788 let state = u16::try_from(raw)
789 .ok()
790 .and_then(|tag| crate::schema::ReducedMotion::try_from(tag).ok())
791 .ok_or(ProtocolError::InvalidValueKind(
792 raw.min(u32::from(u16::MAX)) as u16,
793 ))?;
794 crate::schema::EventPayload::ReducedMotionChanged(state)
795 }
796 EVENT_COLOR_SCHEME_CHANGED if record_len == 20 => {
797 let raw = read_u32(bytes, 16)?;
798 let scheme = u16::try_from(raw)
799 .ok()
800 .and_then(|tag| crate::schema::SystemColorScheme::try_from(tag).ok())
801 .ok_or(ProtocolError::InvalidValueKind(
802 raw.min(u32::from(u16::MAX)) as u16,
803 ))?;
804 crate::schema::EventPayload::ColorSchemeChanged(scheme)
805 }
806 EVENT_CODE_CHANGED if record_len == 44 => crate::schema::EventPayload::CodeChanged {
807 version: read_u32(bytes, 16)?,
808 start_line: read_u32(bytes, 20)?,
809 start_column: read_u32(bytes, 24)?,
810 end_line: read_u32(bytes, 28)?,
811 end_column: read_u32(bytes, 32)?,
812 text: read_string(bytes, 36, record_len)?,
813 },
814 EVENT_CODE_EDIT_REJECTED if record_len == 44 => {
815 crate::schema::EventPayload::CodeEditRejected {
816 request_id: read_u32(bytes, 16)?,
817 base_version: read_u32(bytes, 20)?,
818 current_version: read_u32(bytes, 24)?,
819 start_line: read_u32(bytes, 28)?,
820 start_column: read_u32(bytes, 32)?,
821 end_line: read_u32(bytes, 36)?,
822 end_column: read_u32(bytes, 40)?,
823 }
824 }
825 EVENT_CODE_HOVERED if record_len == 36 => {
826 let raw_phase = read_u32(bytes, 32)?;
827 let phase = u16::try_from(raw_phase)
828 .ok()
829 .and_then(|tag| crate::schema::HoverPhase::try_from(tag).ok())
830 .ok_or(ProtocolError::InvalidValueKind(raw_phase as u16))?;
831 crate::schema::EventPayload::CodeHovered {
832 decoration: read_u64(bytes, 16)?,
833 line: read_u32(bytes, 24)?,
834 column: read_u32(bytes, 28)?,
835 phase,
836 }
837 }
838 EVENT_CODE_SAVE_REQUESTED if record_len == 24 => {
839 crate::schema::EventPayload::CodeSaveRequested {
840 version: read_u32(bytes, 16)?,
841 }
842 }
843 EVENT_DECORATION_ACTIVATED if record_len == 24 => {
844 crate::schema::EventPayload::DecorationActivated {
845 decoration: read_u64(bytes, 16)?,
846 }
847 }
848 EVENT_CODE_CHANGED..=EVENT_DECORATION_ACTIVATED => {
849 return Err(ProtocolError::InvalidRecordLength);
850 }
851 EVENT_SELECTION_CHANGED if record_len == 36 => {
852 crate::schema::EventPayload::SelectionChanged {
853 version: read_u32(bytes, 16)?,
854 anchor_line: read_u32(bytes, 20)?,
855 anchor_column: read_u32(bytes, 24)?,
856 active_line: read_u32(bytes, 28)?,
857 active_column: read_u32(bytes, 32)?,
858 }
859 }
860 EVENT_VISIBLE_RANGE_CHANGED if record_len == 28 => {
861 crate::schema::EventPayload::VisibleRangeChanged {
862 version: read_u32(bytes, 16)?,
863 first_line: read_u32(bytes, 20)?,
864 last_line: read_u32(bytes, 24)?,
865 }
866 }
867 EVENT_EDITOR_FOCUS_CHANGED if record_len == 24 => {
868 crate::schema::EventPayload::EditorFocusChanged {
869 focused: read_flag(bytes, 16)?,
870 }
871 }
872 EVENT_FOLD_CHANGED if record_len == 24 => crate::schema::EventPayload::FoldChanged {
873 start_line: read_u32(bytes, 16)?,
874 collapsed: read_flag(bytes, 20)?,
875 },
876 EVENT_COMPOSITION_CHANGED if record_len == 24 => {
877 crate::schema::EventPayload::CompositionChanged {
878 composing: read_flag(bytes, 16)?,
879 }
880 }
881 EVENT_SELECTION_CHANGED..=EVENT_COMPOSITION_CHANGED => {
882 return Err(ProtocolError::InvalidRecordLength);
883 }
884 EVENT_WINDOW_CLOSE_REQUESTED if record_len == 16 => {
885 crate::schema::EventPayload::WindowCloseRequested
886 }
887 EVENT_WINDOW_FOCUS_CHANGED if record_len == 20 => {
888 crate::schema::EventPayload::WindowFocusChanged {
889 focused: read_flag(bytes, 16)?,
890 }
891 }
892 EVENT_WINDOW_MOVED if record_len == 32 => crate::schema::EventPayload::WindowMoved {
893 x: f32::from_bits(read_u32(bytes, 16)?),
894 y: f32::from_bits(read_u32(bytes, 20)?),
895 width: f32::from_bits(read_u32(bytes, 24)?),
896 height: f32::from_bits(read_u32(bytes, 28)?),
897 },
898 EVENT_WINDOW_DRAG if record_len == 32 => {
899 let phase = read_u32(bytes, 16)?;
900 if !(WINDOW_DRAG_MOVE..=WINDOW_DRAG_CANCEL).contains(&phase) {
901 return Err(ProtocolError::InvalidValueKind(
902 phase.min(u32::from(u16::MAX)) as u16,
903 ));
904 }
905 crate::schema::EventPayload::WindowDrag {
906 phase,
907 screen_x: f32::from_bits(read_u32(bytes, 20)?),
908 screen_y: f32::from_bits(read_u32(bytes, 24)?),
909 over_window: read_u32(bytes, 28)?,
910 }
911 }
912 EVENT_WINDOW_DRAG_OVER if record_len == 32 => {
913 let kind = read_u32(bytes, 16)?;
914 if !(WINDOW_DRAG_OVER_ENTER..=WINDOW_DRAG_OVER_DROP).contains(&kind) {
915 return Err(ProtocolError::InvalidValueKind(
916 kind.min(u32::from(u16::MAX)) as u16,
917 ));
918 }
919 crate::schema::EventPayload::WindowDragOver {
920 kind,
921 window: read_u32(bytes, 20)?,
922 x: f32::from_bits(read_u32(bytes, 24)?),
923 y: f32::from_bits(read_u32(bytes, 28)?),
924 }
925 }
926 EVENT_WINDOW_UNAVAILABLE if record_len == 16 => {
927 crate::schema::EventPayload::WindowUnavailable
928 }
929 EVENT_WINDOW_CLOSED if record_len == 16 => crate::schema::EventPayload::WindowClosed,
930 EVENT_WINDOW_REQUESTED if record_len == 28 => {
931 crate::schema::EventPayload::WindowRequested {
932 request: read_u32(bytes, 16)?,
933 key: read_string(bytes, 20, record_len)?,
934 }
935 }
936 EVENT_APPLICATION_REOPEN if record_len == 16 => {
937 crate::schema::EventPayload::ApplicationReopen
938 }
939 EVENT_FILE_DIALOG_RESULT if record_len == FILE_DIALOG_RESULT_LEN => {
940 let raw_status = read_u32(bytes, 20)?;
941 let status = u16::try_from(raw_status)
942 .ok()
943 .and_then(|tag| FileDialogStatus::try_from(tag).ok())
944 .ok_or(ProtocolError::InvalidValueKind(
945 raw_status.min(u32::from(u16::MAX)) as u16,
946 ))?;
947 crate::schema::EventPayload::FileDialogResult {
948 request: read_u32(bytes, 16)?,
949 status,
950 filter: read_u32(bytes, 24)?,
951 paths: read_string(bytes, 28, record_len)?,
952 }
953 }
954 EVENT_FILE_DIALOG_RESULT => return Err(ProtocolError::InvalidRecordLength),
955 EVENT_CODE_EDIT_GROUP_RESULT if record_len == CODE_EDIT_GROUP_RESULT_LEN => {
956 let raw_status = read_u32(bytes, 20)?;
957 let status = u16::try_from(raw_status)
958 .ok()
959 .and_then(|tag| crate::schema::EditGroupStatus::try_from(tag).ok())
960 .ok_or(ProtocolError::InvalidValueKind(
961 raw_status.min(u32::from(u16::MAX)) as u16,
962 ))?;
963 crate::schema::EventPayload::CodeEditGroupResult {
964 request_id: read_u32(bytes, 16)?,
965 status,
966 base_version: read_u32(bytes, 24)?,
967 current_version: read_u32(bytes, 28)?,
968 index: read_u32(bytes, 32)?,
969 }
970 }
971 EVENT_CODE_EDIT_GROUP_RESULT => return Err(ProtocolError::InvalidRecordLength),
972 EVENT_CLIPBOARD_TEXT if record_len == CLIPBOARD_TEXT_LEN => {
973 crate::schema::EventPayload::ClipboardText {
974 request: read_u32(bytes, 16)?,
975 status: read_u32(bytes, 20)?,
976 len: read_u32(bytes, 24)?,
977 text: read_string(bytes, 28, record_len)?,
978 }
979 }
980 EVENT_CLIPBOARD_TEXT => return Err(ProtocolError::InvalidRecordLength),
981 EVENT_CODE_METRICS if record_len == 24 => crate::schema::EventPayload::CodeMetrics {
982 line_height_px: f64::from_bits(read_u64(bytes, 16)?),
983 },
984 EVENT_CODE_SCROLLED if record_len == 24 => crate::schema::EventPayload::CodeScrolled {
985 offset_px: f64::from_bits(read_u64(bytes, 16)?),
986 },
987 EVENT_CODE_METRICS | EVENT_CODE_SCROLLED => {
988 return Err(ProtocolError::InvalidRecordLength);
989 }
990 EVENT_IO_ANSWER if record_len == IO_ANSWER_LEN => {
991 let raw_status = read_u32(bytes, 20)?;
992 let status = u16::try_from(raw_status)
993 .ok()
994 .and_then(|tag| IoStatus::try_from(tag).ok())
995 .ok_or(ProtocolError::InvalidValueKind(
996 raw_status.min(u32::from(u16::MAX)) as u16,
997 ))?;
998 crate::schema::EventPayload::IoAnswer {
999 request: read_u32(bytes, 16)?,
1000 status,
1001 data: read_bytes(bytes, 24, record_len)?,
1002 }
1003 }
1004 EVENT_IO_ANSWER => return Err(ProtocolError::InvalidRecordLength),
1005 EVENT_FILE_HANDLES if record_len == FILE_HANDLES_LEN => {
1006 let raw_status = read_u32(bytes, 20)?;
1007 let status = u16::try_from(raw_status)
1008 .ok()
1009 .and_then(|tag| FileDialogStatus::try_from(tag).ok())
1010 .ok_or(ProtocolError::InvalidValueKind(
1011 raw_status.min(u32::from(u16::MAX)) as u16,
1012 ))?;
1013 let raw_delivery = read_u32(bytes, 28)?;
1014 let delivery = u16::try_from(raw_delivery)
1015 .ok()
1016 .and_then(|tag| FileDelivery::try_from(tag).ok())
1017 .ok_or(ProtocolError::InvalidValueKind(
1018 raw_delivery.min(u32::from(u16::MAX)) as u16,
1019 ))?;
1020 let names = read_string(bytes, 36, record_len)?;
1021 let kinds = read_string(bytes, 44, record_len)?;
1022 if let Some(unknown) = kinds.bytes().find(|kind| *kind != b'f' && *kind != b'd') {
1025 return Err(ProtocolError::InvalidValueKind(u16::from(unknown)));
1026 }
1027 let count = if names.is_empty() {
1028 0
1029 } else {
1030 names.split('\0').count()
1031 };
1032 if count != kinds.len() || names.split('\0').any(|name| count != 0 && name.is_empty()) {
1033 return Err(ProtocolError::InvalidStringRange);
1034 }
1035 crate::schema::EventPayload::FileHandles {
1036 request: read_u32(bytes, 16)?,
1037 status,
1038 filter: read_u32(bytes, 24)?,
1039 delivery,
1040 first_handle: read_u32(bytes, 32)?,
1041 names,
1042 kinds,
1043 }
1044 }
1045 EVENT_FILE_HANDLES => return Err(ProtocolError::InvalidRecordLength),
1046 EVENT_STORAGE_LOCATION if record_len == 24 => {
1047 crate::schema::EventPayload::StorageLocation(read_string(bytes, 16, record_len)?)
1048 }
1049 EVENT_STORAGE_LOCATION => return Err(ProtocolError::InvalidRecordLength),
1050 EVENT_SCROLL_CHANGED if record_len == SCROLL_CHANGED_LEN => {
1051 use crate::schema::scroll_changed_flags as flag;
1052 let flags = read_u32(bytes, 20)?;
1053 if flags & !flag::KNOWN != 0 {
1054 return Err(ProtocolError::InvalidValueKind(
1057 (flags & !flag::KNOWN).trailing_zeros() as u16,
1058 ));
1059 }
1060 crate::schema::EventPayload::ScrollChanged {
1061 item: read_u32(bytes, 16)?,
1062 settled: flags & flag::SETTLED != 0,
1063 can_scroll_backward: flags & flag::CAN_SCROLL_BACKWARD != 0,
1064 can_scroll_forward: flags & flag::CAN_SCROLL_FORWARD != 0,
1065 x_px: f64::from_bits(read_u64(bytes, 24)?),
1066 y_px: f64::from_bits(read_u64(bytes, 32)?),
1067 max_x_px: f64::from_bits(read_u64(bytes, 40)?),
1068 max_y_px: f64::from_bits(read_u64(bytes, 48)?),
1069 viewport_width_px: f64::from_bits(read_u64(bytes, 56)?),
1070 viewport_height_px: f64::from_bits(read_u64(bytes, 64)?),
1071 }
1072 }
1073 EVENT_SCROLL_CHANGED => return Err(ProtocolError::InvalidRecordLength),
1074 EVENT_MOTION_FINISHED if record_len == MOTION_FINISHED_LEN => {
1075 crate::schema::EventPayload::MotionFinished {
1076 width_dp: f32::from_bits(read_u32(bytes, 16)?),
1077 height_dp: f32::from_bits(read_u32(bytes, 20)?),
1078 }
1079 }
1080 EVENT_MOTION_FINISHED => return Err(ProtocolError::InvalidRecordLength),
1081 EVENT_CARET_RECT if record_len == CODE_RECT_EVENT_LEN => {
1082 crate::schema::EventPayload::CaretRect {
1083 line: read_u32(bytes, 16)?,
1084 column: read_u32(bytes, 20)?,
1085 rect: read_code_rect(bytes, 24)?,
1086 }
1087 }
1088 EVENT_POSITION_RECT if record_len == CODE_RECT_EVENT_LEN => {
1089 let found = read_u32(bytes, 20)?;
1090 let rect = read_code_rect(bytes, 24)?;
1091 crate::schema::EventPayload::PositionRect {
1092 request: read_u32(bytes, 16)?,
1093 rect: match found {
1094 0 => None,
1095 1 => Some(rect),
1096 other => {
1097 return Err(ProtocolError::InvalidValueKind(
1098 other.min(u32::from(u16::MAX)) as u16,
1099 ));
1100 }
1101 },
1102 }
1103 }
1104 EVENT_CARET_RECT | EVENT_POSITION_RECT => {
1105 return Err(ProtocolError::InvalidRecordLength);
1106 }
1107 EVENT_CODE_POINTER if record_len == CODE_POINTER_LEN => {
1108 let crate::schema::EventPayload::Pointer {
1109 kind,
1110 pointer_id,
1111 pointer_type,
1112 x,
1113 y,
1114 buttons,
1115 button,
1116 modifiers,
1117 click_count,
1118 delta_x,
1119 delta_y,
1120 delta_mode,
1121 timestamp_nanos,
1122 } = decode_pointer(bytes)?
1123 else {
1124 unreachable!("decode_pointer reads a pointer record");
1125 };
1126 let line = read_u32(bytes, POINTER_RECORD_LEN)?;
1127 let column = read_u32(bytes, POINTER_RECORD_LEN + 4)?;
1128 let position = match (line, column) {
1129 (NO_CODE_POSITION, NO_CODE_POSITION) => None,
1130 (NO_CODE_POSITION, _) | (_, NO_CODE_POSITION) => {
1132 return Err(ProtocolError::InvalidValueKind(u16::MAX));
1133 }
1134 (line, column) => Some(crate::code::Position::new(line, column)),
1135 };
1136 crate::schema::EventPayload::CodePointer {
1137 kind,
1138 pointer_id,
1139 pointer_type,
1140 x,
1141 y,
1142 buttons,
1143 button,
1144 modifiers,
1145 click_count,
1146 delta_x,
1147 delta_y,
1148 delta_mode,
1149 timestamp_nanos,
1150 position,
1151 }
1152 }
1153 EVENT_CODE_POINTER => return Err(ProtocolError::InvalidRecordLength),
1154 EVENT_SELECTIONS_CHANGED if record_len == SELECTIONS_CHANGED_LEN => {
1155 let selections = read_bytes(bytes, 20, record_len)?;
1156 if selections.len() % crate::code::SELECTION_LEN != 0 {
1157 return Err(ProtocolError::InvalidRecordLength);
1158 }
1159 crate::schema::EventPayload::SelectionsChanged {
1160 version: read_u32(bytes, 16)?,
1161 selections,
1162 }
1163 }
1164 EVENT_SELECTIONS_CHANGED => return Err(ProtocolError::InvalidRecordLength),
1165 EVENT_WINDOW_CLOSE_REQUESTED..=EVENT_APPLICATION_REOPEN => {
1166 return Err(ProtocolError::InvalidRecordLength);
1167 }
1168 EVENT_ZOOM_CHANGED if record_len == 28 => {
1169 let level = read_u32(bytes, 24)? as i32;
1170 if !(i32::from(crate::zoom::MIN_ZOOM_LEVEL)..=i32::from(crate::zoom::MAX_ZOOM_LEVEL))
1171 .contains(&level)
1172 {
1173 return Err(ProtocolError::InvalidValueKind(level as u16));
1174 }
1175 crate::schema::EventPayload::ZoomChanged {
1176 k: f32::from_bits(read_u32(bytes, 16)?),
1177 os: f32::from_bits(read_u32(bytes, 20)?),
1178 level,
1179 }
1180 }
1181 EVENT_RESYNC if record_len == 16 => crate::schema::EventPayload::Resync,
1182 EVENT_LIFECYCLE_START if record_len == 16 => crate::schema::EventPayload::LifecycleStart,
1183 EVENT_LIFECYCLE_STOP if record_len == 16 => crate::schema::EventPayload::LifecycleStop,
1184 EVENT_CLICK..=EVENT_RANGE_REQUESTED
1185 | EVENT_VALUE_CHANGED..=EVENT_NOTIFICATION_PERMISSION_CHANGED
1186 | EVENT_ANIMATION
1187 | EVENT_POINTER
1188 | EVENT_REDUCED_MOTION_CHANGED
1189 | EVENT_COLOR_SCHEME_CHANGED
1190 | EVENT_ZOOM_CHANGED
1191 | EVENT_FOCUS_GAINED
1192 | EVENT_KEY_UP => {
1193 return Err(ProtocolError::InvalidRecordLength);
1194 }
1195 other => return Err(ProtocolError::InvalidTag(other)),
1196 };
1197 Ok(HostEvent {
1198 node_id,
1199 handler_id,
1200 payload,
1201 })
1202}
1203
1204fn read_code_rect(bytes: &[u8], offset: usize) -> Result<crate::code::CodeRect, ProtocolError> {
1206 Ok(crate::code::CodeRect {
1207 x: f32::from_bits(read_u32(bytes, offset)?),
1208 y: f32::from_bits(read_u32(bytes, offset + 4)?),
1209 width: f32::from_bits(read_u32(bytes, offset + 8)?),
1210 height: f32::from_bits(read_u32(bytes, offset + 12)?),
1211 })
1212}
1213
1214fn put_code_rect(output: &mut Vec<u8>, rect: crate::code::CodeRect) {
1215 for value in [rect.x, rect.y, rect.width, rect.height] {
1216 output.extend_from_slice(&value.to_bits().to_le_bytes());
1217 }
1218}
1219
1220pub fn encode_event(event: &HostEvent<'_>, output: &mut Vec<u8>) -> Result<(), ProtocolError> {
1222 output.clear();
1223 if let crate::schema::EventPayload::KeyDown {
1224 key,
1225 shift_key,
1226 ctrl_key,
1227 alt_key,
1228 meta_key,
1229 repeat,
1230 code,
1231 text,
1232 } = event.payload
1233 {
1234 output.extend_from_slice(&EVENT_KEY_DOWN.to_le_bytes());
1235 output.extend_from_slice(&32_u16.to_le_bytes());
1236 output.extend_from_slice(&event.node_id.to_le_bytes());
1237 output.extend_from_slice(&event.handler_id.to_le_bytes());
1238 output.extend_from_slice(&(key as u16).to_le_bytes());
1239 let modifiers = (u8::from(shift_key) * MODIFIER_SHIFT)
1240 | (u8::from(ctrl_key) * MODIFIER_CTRL)
1241 | (u8::from(alt_key) * MODIFIER_ALT)
1242 | (u8::from(meta_key) * MODIFIER_META);
1243 output.push(modifiers);
1244 output.push(u8::from(repeat));
1245 output.extend_from_slice(&code.to_le_bytes());
1246 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1247 output.extend_from_slice(&32_u32.to_le_bytes());
1248 output.extend_from_slice(&len.to_le_bytes());
1249 output.extend_from_slice(text.as_bytes());
1250 return Ok(());
1251 }
1252 if let crate::schema::EventPayload::KeyUp {
1253 key,
1254 shift_key,
1255 ctrl_key,
1256 alt_key,
1257 meta_key,
1258 repeat,
1259 code,
1260 text,
1261 } = event.payload
1262 {
1263 output.extend_from_slice(&EVENT_KEY_UP.to_le_bytes());
1264 output.extend_from_slice(&32_u16.to_le_bytes());
1265 output.extend_from_slice(&event.node_id.to_le_bytes());
1266 output.extend_from_slice(&event.handler_id.to_le_bytes());
1267 output.extend_from_slice(&(key as u16).to_le_bytes());
1268 let modifiers = (u8::from(shift_key) * MODIFIER_SHIFT)
1269 | (u8::from(ctrl_key) * MODIFIER_CTRL)
1270 | (u8::from(alt_key) * MODIFIER_ALT)
1271 | (u8::from(meta_key) * MODIFIER_META);
1272 output.push(modifiers);
1273 output.push(u8::from(repeat));
1274 output.extend_from_slice(&code.to_le_bytes());
1275 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1276 output.extend_from_slice(&32_u32.to_le_bytes());
1277 output.extend_from_slice(&len.to_le_bytes());
1278 output.extend_from_slice(text.as_bytes());
1279 return Ok(());
1280 }
1281 if let crate::schema::EventPayload::ValueChanged(value) = event.payload {
1282 output.extend_from_slice(&EVENT_VALUE_CHANGED.to_le_bytes());
1283 output.extend_from_slice(&24_u16.to_le_bytes());
1284 output.extend_from_slice(&event.node_id.to_le_bytes());
1285 output.extend_from_slice(&event.handler_id.to_le_bytes());
1286 output.extend_from_slice(&value.to_bits().to_le_bytes());
1287 return Ok(());
1288 }
1289 if let crate::schema::EventPayload::WindowSizeChanged {
1290 width_dp,
1291 height_dp,
1292 class,
1293 height_class,
1294 safe_area_top,
1295 safe_area_leading,
1296 safe_area_trailing,
1297 } = event.payload
1298 {
1299 output.extend_from_slice(&EVENT_WINDOW_SIZE_CHANGED.to_le_bytes());
1300 output.extend_from_slice(&44_u16.to_le_bytes());
1301 output.extend_from_slice(&event.node_id.to_le_bytes());
1302 output.extend_from_slice(&event.handler_id.to_le_bytes());
1303 output.extend_from_slice(&width_dp.to_bits().to_le_bytes());
1304 output.extend_from_slice(&height_dp.to_bits().to_le_bytes());
1305 output.extend_from_slice(&u32::from(u16::from(class)).to_le_bytes());
1306 output.extend_from_slice(&u32::from(u16::from(height_class)).to_le_bytes());
1307 output.extend_from_slice(&safe_area_top.to_bits().to_le_bytes());
1308 output.extend_from_slice(&safe_area_leading.to_bits().to_le_bytes());
1309 output.extend_from_slice(&safe_area_trailing.to_bits().to_le_bytes());
1310 return Ok(());
1311 }
1312 if let crate::schema::EventPayload::DesignSystemResolved(system) = event.payload {
1313 output.extend_from_slice(&EVENT_DESIGN_SYSTEM_RESOLVED.to_le_bytes());
1314 output.extend_from_slice(&20_u16.to_le_bytes());
1315 output.extend_from_slice(&event.node_id.to_le_bytes());
1316 output.extend_from_slice(&event.handler_id.to_le_bytes());
1317 output.extend_from_slice(&u32::from(u16::from(system)).to_le_bytes());
1320 return Ok(());
1321 }
1322 if let crate::schema::EventPayload::NotificationPermissionChanged(state) = event.payload {
1323 output.extend_from_slice(&EVENT_NOTIFICATION_PERMISSION_CHANGED.to_le_bytes());
1324 output.extend_from_slice(&20_u16.to_le_bytes());
1325 output.extend_from_slice(&event.node_id.to_le_bytes());
1326 output.extend_from_slice(&event.handler_id.to_le_bytes());
1327 output.extend_from_slice(&u32::from(u16::from(state)).to_le_bytes());
1328 return Ok(());
1329 }
1330 if let crate::schema::EventPayload::AnimationEvent {
1331 animation_id,
1332 kind,
1333 property,
1334 slot,
1335 iteration,
1336 elapsed_ms,
1337 time_nanos,
1338 } = event.payload
1339 {
1340 output.extend_from_slice(&EVENT_ANIMATION.to_le_bytes());
1341 output.extend_from_slice(&40_u16.to_le_bytes());
1342 output.extend_from_slice(&event.node_id.to_le_bytes());
1343 output.extend_from_slice(&event.handler_id.to_le_bytes());
1344 output.extend_from_slice(&animation_id.to_le_bytes());
1345 output.push(kind as u8);
1346 output.push(property as u8);
1347 output.push(slot);
1348 output.push(0);
1349 output.extend_from_slice(&iteration.to_le_bytes());
1350 output.extend_from_slice(&elapsed_ms.to_bits().to_le_bytes());
1351 output.extend_from_slice(&time_nanos.to_le_bytes());
1352 return Ok(());
1353 }
1354 if let crate::schema::EventPayload::Pointer {
1355 kind,
1356 pointer_id,
1357 pointer_type,
1358 x,
1359 y,
1360 buttons,
1361 button,
1362 modifiers,
1363 click_count,
1364 delta_x,
1365 delta_y,
1366 delta_mode,
1367 timestamp_nanos,
1368 } = event.payload
1369 {
1370 output.extend_from_slice(&EVENT_POINTER.to_le_bytes());
1371 output.extend_from_slice(&(POINTER_RECORD_LEN as u16).to_le_bytes());
1372 output.extend_from_slice(&event.node_id.to_le_bytes());
1373 output.extend_from_slice(&event.handler_id.to_le_bytes());
1374 output.extend_from_slice(&u32::from(u16::from(kind)).to_le_bytes());
1375 output.extend_from_slice(&pointer_id.to_le_bytes());
1376 output.extend_from_slice(&u32::from(u16::from(pointer_type)).to_le_bytes());
1377 output.extend_from_slice(&x.to_bits().to_le_bytes());
1378 output.extend_from_slice(&y.to_bits().to_le_bytes());
1379 output.extend_from_slice(&buttons.to_le_bytes());
1380 output.extend_from_slice(&button.to_le_bytes());
1381 output.extend_from_slice(&modifiers.to_le_bytes());
1382 output.extend_from_slice(&click_count.to_le_bytes());
1383 output.extend_from_slice(&delta_x.to_bits().to_le_bytes());
1384 output.extend_from_slice(&delta_y.to_bits().to_le_bytes());
1385 output.extend_from_slice(&u32::from(u16::from(delta_mode)).to_le_bytes());
1386 output.extend_from_slice(×tamp_nanos.to_le_bytes());
1387 return Ok(());
1388 }
1389 if let crate::schema::EventPayload::CodePointer {
1390 kind,
1391 pointer_id,
1392 pointer_type,
1393 x,
1394 y,
1395 buttons,
1396 button,
1397 modifiers,
1398 click_count,
1399 delta_x,
1400 delta_y,
1401 delta_mode,
1402 timestamp_nanos,
1403 position,
1404 } = event.payload
1405 {
1406 encode_event(
1408 &HostEvent {
1409 node_id: event.node_id,
1410 handler_id: event.handler_id,
1411 payload: crate::schema::EventPayload::Pointer {
1412 kind,
1413 pointer_id,
1414 pointer_type,
1415 x,
1416 y,
1417 buttons,
1418 button,
1419 modifiers,
1420 click_count,
1421 delta_x,
1422 delta_y,
1423 delta_mode,
1424 timestamp_nanos,
1425 },
1426 },
1427 output,
1428 )?;
1429 output[0..2].copy_from_slice(&EVENT_CODE_POINTER.to_le_bytes());
1430 output[2..4].copy_from_slice(&(CODE_POINTER_LEN as u16).to_le_bytes());
1431 let (line, column) = position.map_or((NO_CODE_POSITION, NO_CODE_POSITION), |at| {
1432 (at.line, at.column)
1433 });
1434 output.extend_from_slice(&line.to_le_bytes());
1435 output.extend_from_slice(&column.to_le_bytes());
1436 return Ok(());
1437 }
1438 if let crate::schema::EventPayload::ReducedMotionChanged(state) = event.payload {
1439 output.extend_from_slice(&EVENT_REDUCED_MOTION_CHANGED.to_le_bytes());
1440 output.extend_from_slice(&20_u16.to_le_bytes());
1441 output.extend_from_slice(&event.node_id.to_le_bytes());
1442 output.extend_from_slice(&event.handler_id.to_le_bytes());
1443 output.extend_from_slice(&u32::from(u16::from(state)).to_le_bytes());
1444 return Ok(());
1445 }
1446 if let crate::schema::EventPayload::ColorSchemeChanged(scheme) = event.payload {
1447 output.extend_from_slice(&EVENT_COLOR_SCHEME_CHANGED.to_le_bytes());
1448 output.extend_from_slice(&20_u16.to_le_bytes());
1449 output.extend_from_slice(&event.node_id.to_le_bytes());
1450 output.extend_from_slice(&event.handler_id.to_le_bytes());
1451 output.extend_from_slice(&u32::from(u16::from(scheme)).to_le_bytes());
1452 return Ok(());
1453 }
1454 match event.payload {
1455 crate::schema::EventPayload::CodeChanged {
1456 version,
1457 start_line,
1458 start_column,
1459 end_line,
1460 end_column,
1461 text,
1462 } => {
1463 put_event_header(output, EVENT_CODE_CHANGED, 44, event);
1464 for value in [version, start_line, start_column, end_line, end_column] {
1465 output.extend_from_slice(&value.to_le_bytes());
1466 }
1467 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1468 output.extend_from_slice(&44_u32.to_le_bytes());
1469 output.extend_from_slice(&len.to_le_bytes());
1470 output.extend_from_slice(text.as_bytes());
1471 return Ok(());
1472 }
1473 crate::schema::EventPayload::CodeEditRejected {
1474 request_id,
1475 base_version,
1476 current_version,
1477 start_line,
1478 start_column,
1479 end_line,
1480 end_column,
1481 } => {
1482 put_event_header(output, EVENT_CODE_EDIT_REJECTED, 44, event);
1483 for value in [
1484 request_id,
1485 base_version,
1486 current_version,
1487 start_line,
1488 start_column,
1489 end_line,
1490 end_column,
1491 ] {
1492 output.extend_from_slice(&value.to_le_bytes());
1493 }
1494 return Ok(());
1495 }
1496 crate::schema::EventPayload::CodeHovered {
1497 decoration,
1498 line,
1499 column,
1500 phase,
1501 } => {
1502 put_event_header(output, EVENT_CODE_HOVERED, 36, event);
1503 output.extend_from_slice(&decoration.to_le_bytes());
1504 output.extend_from_slice(&line.to_le_bytes());
1505 output.extend_from_slice(&column.to_le_bytes());
1506 output.extend_from_slice(&u32::from(u16::from(phase)).to_le_bytes());
1507 return Ok(());
1508 }
1509 crate::schema::EventPayload::CodeSaveRequested { version } => {
1510 put_event_header(output, EVENT_CODE_SAVE_REQUESTED, 24, event);
1511 output.extend_from_slice(&version.to_le_bytes());
1512 output.extend_from_slice(&0_u32.to_le_bytes());
1514 return Ok(());
1515 }
1516 crate::schema::EventPayload::DecorationActivated { decoration } => {
1517 put_event_header(output, EVENT_DECORATION_ACTIVATED, 24, event);
1518 output.extend_from_slice(&decoration.to_le_bytes());
1519 return Ok(());
1520 }
1521 crate::schema::EventPayload::SelectionChanged {
1522 version,
1523 anchor_line,
1524 anchor_column,
1525 active_line,
1526 active_column,
1527 } => {
1528 put_event_header(output, EVENT_SELECTION_CHANGED, 36, event);
1529 for value in [
1530 version,
1531 anchor_line,
1532 anchor_column,
1533 active_line,
1534 active_column,
1535 ] {
1536 output.extend_from_slice(&value.to_le_bytes());
1537 }
1538 return Ok(());
1539 }
1540 crate::schema::EventPayload::VisibleRangeChanged {
1541 version,
1542 first_line,
1543 last_line,
1544 } => {
1545 put_event_header(output, EVENT_VISIBLE_RANGE_CHANGED, 28, event);
1546 for value in [version, first_line, last_line] {
1547 output.extend_from_slice(&value.to_le_bytes());
1548 }
1549 return Ok(());
1550 }
1551 crate::schema::EventPayload::EditorFocusChanged { focused } => {
1552 put_event_header(output, EVENT_EDITOR_FOCUS_CHANGED, 24, event);
1553 output.extend_from_slice(&u32::from(focused).to_le_bytes());
1554 output.extend_from_slice(&0_u32.to_le_bytes());
1555 return Ok(());
1556 }
1557 crate::schema::EventPayload::FoldChanged {
1558 start_line,
1559 collapsed,
1560 } => {
1561 put_event_header(output, EVENT_FOLD_CHANGED, 24, event);
1562 output.extend_from_slice(&start_line.to_le_bytes());
1563 output.extend_from_slice(&u32::from(collapsed).to_le_bytes());
1564 return Ok(());
1565 }
1566 crate::schema::EventPayload::CompositionChanged { composing } => {
1567 put_event_header(output, EVENT_COMPOSITION_CHANGED, 24, event);
1568 output.extend_from_slice(&u32::from(composing).to_le_bytes());
1569 output.extend_from_slice(&0_u32.to_le_bytes());
1570 return Ok(());
1571 }
1572 _ => {}
1573 }
1574 match event.payload {
1575 crate::schema::EventPayload::WindowFocusChanged { focused } => {
1576 put_event_header(output, EVENT_WINDOW_FOCUS_CHANGED, 20, event);
1577 output.extend_from_slice(&u32::from(focused).to_le_bytes());
1578 return Ok(());
1579 }
1580 crate::schema::EventPayload::WindowMoved {
1581 x,
1582 y,
1583 width,
1584 height,
1585 } => {
1586 put_event_header(output, EVENT_WINDOW_MOVED, 32, event);
1587 for value in [x, y, width, height] {
1588 output.extend_from_slice(&value.to_bits().to_le_bytes());
1589 }
1590 return Ok(());
1591 }
1592 crate::schema::EventPayload::WindowDrag {
1593 phase,
1594 screen_x,
1595 screen_y,
1596 over_window,
1597 } => {
1598 put_event_header(output, EVENT_WINDOW_DRAG, 32, event);
1599 output.extend_from_slice(&phase.to_le_bytes());
1600 output.extend_from_slice(&screen_x.to_bits().to_le_bytes());
1601 output.extend_from_slice(&screen_y.to_bits().to_le_bytes());
1602 output.extend_from_slice(&over_window.to_le_bytes());
1603 return Ok(());
1604 }
1605 crate::schema::EventPayload::WindowDragOver { kind, window, x, y } => {
1606 put_event_header(output, EVENT_WINDOW_DRAG_OVER, 32, event);
1607 output.extend_from_slice(&kind.to_le_bytes());
1608 output.extend_from_slice(&window.to_le_bytes());
1609 output.extend_from_slice(&x.to_bits().to_le_bytes());
1610 output.extend_from_slice(&y.to_bits().to_le_bytes());
1611 return Ok(());
1612 }
1613 _ => {}
1614 }
1615 if let crate::schema::EventPayload::CodeEditGroupResult {
1616 request_id,
1617 status,
1618 base_version,
1619 current_version,
1620 index,
1621 } = event.payload
1622 {
1623 put_event_header(
1624 output,
1625 EVENT_CODE_EDIT_GROUP_RESULT,
1626 CODE_EDIT_GROUP_RESULT_LEN as u16,
1627 event,
1628 );
1629 for value in [
1630 request_id,
1631 u32::from(status as u16),
1632 base_version,
1633 current_version,
1634 index,
1635 ] {
1636 output.extend_from_slice(&value.to_le_bytes());
1637 }
1638 return Ok(());
1639 }
1640 if let crate::schema::EventPayload::FileDialogResult {
1641 request,
1642 status,
1643 filter,
1644 paths,
1645 } = event.payload
1646 {
1647 put_event_header(
1648 output,
1649 EVENT_FILE_DIALOG_RESULT,
1650 FILE_DIALOG_RESULT_LEN as u16,
1651 event,
1652 );
1653 output.extend_from_slice(&request.to_le_bytes());
1654 output.extend_from_slice(&u32::from(status as u16).to_le_bytes());
1655 output.extend_from_slice(&filter.to_le_bytes());
1656 let len = u32::try_from(paths.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1657 output.extend_from_slice(&(FILE_DIALOG_RESULT_LEN as u32).to_le_bytes());
1658 output.extend_from_slice(&len.to_le_bytes());
1659 output.extend_from_slice(paths.as_bytes());
1660 return Ok(());
1661 }
1662 if let crate::schema::EventPayload::ClipboardText {
1663 request,
1664 status,
1665 len,
1666 text,
1667 } = event.payload
1668 {
1669 put_event_header(
1670 output,
1671 EVENT_CLIPBOARD_TEXT,
1672 CLIPBOARD_TEXT_LEN as u16,
1673 event,
1674 );
1675 output.extend_from_slice(&request.to_le_bytes());
1676 output.extend_from_slice(&status.to_le_bytes());
1677 output.extend_from_slice(&len.to_le_bytes());
1678 let text_len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1679 output.extend_from_slice(&(CLIPBOARD_TEXT_LEN as u32).to_le_bytes());
1680 output.extend_from_slice(&text_len.to_le_bytes());
1681 output.extend_from_slice(text.as_bytes());
1682 return Ok(());
1683 }
1684 match event.payload {
1685 crate::schema::EventPayload::CodeMetrics { line_height_px } => {
1686 put_event_header(output, EVENT_CODE_METRICS, 24, event);
1687 output.extend_from_slice(&line_height_px.to_bits().to_le_bytes());
1688 return Ok(());
1689 }
1690 crate::schema::EventPayload::CodeScrolled { offset_px } => {
1691 put_event_header(output, EVENT_CODE_SCROLLED, 24, event);
1692 output.extend_from_slice(&offset_px.to_bits().to_le_bytes());
1693 return Ok(());
1694 }
1695 _ => {}
1696 }
1697 if let crate::schema::EventPayload::SelectionsChanged {
1698 version,
1699 selections,
1700 } = event.payload
1701 {
1702 put_event_header(
1703 output,
1704 EVENT_SELECTIONS_CHANGED,
1705 SELECTIONS_CHANGED_LEN as u16,
1706 event,
1707 );
1708 output.extend_from_slice(&version.to_le_bytes());
1709 let len = u32::try_from(selections.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1710 output.extend_from_slice(&(SELECTIONS_CHANGED_LEN as u32).to_le_bytes());
1711 output.extend_from_slice(&len.to_le_bytes());
1712 output.extend_from_slice(selections);
1713 return Ok(());
1714 }
1715 if let crate::schema::EventPayload::IoAnswer {
1716 request,
1717 status,
1718 data,
1719 } = event.payload
1720 {
1721 put_event_header(output, EVENT_IO_ANSWER, IO_ANSWER_LEN as u16, event);
1722 output.extend_from_slice(&request.to_le_bytes());
1723 output.extend_from_slice(&u32::from(u16::from(status)).to_le_bytes());
1724 let data_len = u32::try_from(data.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1725 output.extend_from_slice(&(IO_ANSWER_LEN as u32).to_le_bytes());
1726 output.extend_from_slice(&data_len.to_le_bytes());
1727 output.extend_from_slice(data);
1728 return Ok(());
1729 }
1730 if let crate::schema::EventPayload::FileHandles {
1731 request,
1732 status,
1733 filter,
1734 delivery,
1735 first_handle,
1736 names,
1737 kinds,
1738 } = event.payload
1739 {
1740 put_event_header(output, EVENT_FILE_HANDLES, FILE_HANDLES_LEN as u16, event);
1741 output.extend_from_slice(&request.to_le_bytes());
1742 output.extend_from_slice(&u32::from(status as u16).to_le_bytes());
1743 output.extend_from_slice(&filter.to_le_bytes());
1744 output.extend_from_slice(&u32::from(delivery as u16).to_le_bytes());
1745 output.extend_from_slice(&first_handle.to_le_bytes());
1746 let names_len = u32::try_from(names.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1747 let kinds_len = u32::try_from(kinds.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1748 let kinds_at = (FILE_HANDLES_LEN as u32)
1749 .checked_add(names_len)
1750 .ok_or(ProtocolError::LengthOverflow)?;
1751 output.extend_from_slice(&(FILE_HANDLES_LEN as u32).to_le_bytes());
1752 output.extend_from_slice(&names_len.to_le_bytes());
1753 output.extend_from_slice(&kinds_at.to_le_bytes());
1754 output.extend_from_slice(&kinds_len.to_le_bytes());
1755 output.extend_from_slice(names.as_bytes());
1756 output.extend_from_slice(kinds.as_bytes());
1757 return Ok(());
1758 }
1759 if let crate::schema::EventPayload::ScrollChanged {
1760 item,
1761 settled,
1762 can_scroll_backward,
1763 can_scroll_forward,
1764 x_px,
1765 y_px,
1766 max_x_px,
1767 max_y_px,
1768 viewport_width_px,
1769 viewport_height_px,
1770 } = event.payload
1771 {
1772 use crate::schema::scroll_changed_flags as flag;
1773 put_event_header(
1774 output,
1775 EVENT_SCROLL_CHANGED,
1776 SCROLL_CHANGED_LEN as u16,
1777 event,
1778 );
1779 let mut flags = 0_u32;
1780 if settled {
1781 flags |= flag::SETTLED;
1782 }
1783 if can_scroll_backward {
1784 flags |= flag::CAN_SCROLL_BACKWARD;
1785 }
1786 if can_scroll_forward {
1787 flags |= flag::CAN_SCROLL_FORWARD;
1788 }
1789 output.extend_from_slice(&item.to_le_bytes());
1790 output.extend_from_slice(&flags.to_le_bytes());
1791 for value in [
1792 x_px,
1793 y_px,
1794 max_x_px,
1795 max_y_px,
1796 viewport_width_px,
1797 viewport_height_px,
1798 ] {
1799 output.extend_from_slice(&value.to_bits().to_le_bytes());
1800 }
1801 return Ok(());
1802 }
1803 if let crate::schema::EventPayload::MotionFinished {
1804 width_dp,
1805 height_dp,
1806 } = event.payload
1807 {
1808 put_event_header(
1809 output,
1810 EVENT_MOTION_FINISHED,
1811 MOTION_FINISHED_LEN as u16,
1812 event,
1813 );
1814 output.extend_from_slice(&width_dp.to_bits().to_le_bytes());
1815 output.extend_from_slice(&height_dp.to_bits().to_le_bytes());
1816 return Ok(());
1817 }
1818 if let crate::schema::EventPayload::CaretRect { line, column, rect } = event.payload {
1819 put_event_header(output, EVENT_CARET_RECT, CODE_RECT_EVENT_LEN as u16, event);
1820 output.extend_from_slice(&line.to_le_bytes());
1821 output.extend_from_slice(&column.to_le_bytes());
1822 put_code_rect(output, rect);
1823 return Ok(());
1824 }
1825 if let crate::schema::EventPayload::PositionRect { request, rect } = event.payload {
1826 put_event_header(
1827 output,
1828 EVENT_POSITION_RECT,
1829 CODE_RECT_EVENT_LEN as u16,
1830 event,
1831 );
1832 output.extend_from_slice(&request.to_le_bytes());
1833 output.extend_from_slice(&u32::from(rect.is_some()).to_le_bytes());
1834 put_code_rect(output, rect.unwrap_or_default());
1835 return Ok(());
1836 }
1837 if let crate::schema::EventPayload::ZoomChanged { k, os, level } = event.payload {
1838 output.extend_from_slice(&EVENT_ZOOM_CHANGED.to_le_bytes());
1839 output.extend_from_slice(&28_u16.to_le_bytes());
1840 output.extend_from_slice(&event.node_id.to_le_bytes());
1841 output.extend_from_slice(&event.handler_id.to_le_bytes());
1842 output.extend_from_slice(&k.to_bits().to_le_bytes());
1843 output.extend_from_slice(&os.to_bits().to_le_bytes());
1844 output.extend_from_slice(&level.to_le_bytes());
1845 return Ok(());
1846 }
1847 if let crate::schema::EventPayload::RangeRequested { start, count } = event.payload {
1848 output.extend_from_slice(&EVENT_RANGE_REQUESTED.to_le_bytes());
1849 output.extend_from_slice(&24_u16.to_le_bytes());
1850 output.extend_from_slice(&event.node_id.to_le_bytes());
1851 output.extend_from_slice(&event.handler_id.to_le_bytes());
1852 output.extend_from_slice(&start.to_le_bytes());
1853 output.extend_from_slice(&count.to_le_bytes());
1854 return Ok(());
1855 }
1856 let (tag, record_len, text, error_code) = match &event.payload {
1857 crate::schema::EventPayload::Clicked => (EVENT_CLICK, 16_u16, None, None),
1858 crate::schema::EventPayload::TextChanged(value) => {
1859 (EVENT_TEXT_CHANGED, 24, Some(*value), None)
1860 }
1861 crate::schema::EventPayload::TextSubmitted(value) => {
1862 (EVENT_TEXT_SUBMITTED, 24, Some(*value), None)
1863 }
1864 crate::schema::EventPayload::FocusLost => (EVENT_FOCUS_LOST, 16, None, None),
1865 crate::schema::EventPayload::FocusGained => (EVENT_FOCUS_GAINED, 16, None, None),
1866 crate::schema::EventPayload::FilesEntered => (EVENT_FILES_ENTERED, 16, None, None),
1867 crate::schema::EventPayload::FilesDropped(paths) => {
1868 (EVENT_FILES_DROPPED, 24, Some(*paths), None)
1869 }
1870 crate::schema::EventPayload::StorageLocation(path) => {
1871 (EVENT_STORAGE_LOCATION, 24, Some(*path), None)
1872 }
1873 crate::schema::EventPayload::Resync => (EVENT_RESYNC, 16, None, None),
1874 crate::schema::EventPayload::WindowCloseRequested => {
1875 (EVENT_WINDOW_CLOSE_REQUESTED, 16, None, None)
1876 }
1877 crate::schema::EventPayload::WindowUnavailable => {
1878 (EVENT_WINDOW_UNAVAILABLE, 16, None, None)
1879 }
1880 crate::schema::EventPayload::WindowClosed => (EVENT_WINDOW_CLOSED, 16, None, None),
1881 crate::schema::EventPayload::ApplicationReopen => {
1882 (EVENT_APPLICATION_REOPEN, 16, None, None)
1883 }
1884 crate::schema::EventPayload::WindowRequested { key, request } => {
1886 (EVENT_WINDOW_REQUESTED, 28, Some(*key), Some(*request))
1887 }
1888 crate::schema::EventPayload::LifecycleStart => (EVENT_LIFECYCLE_START, 16, None, None),
1889 crate::schema::EventPayload::LifecycleStop => (EVENT_LIFECYCLE_STOP, 16, None, None),
1890 crate::schema::EventPayload::ProtocolError { code, message } => {
1891 (EVENT_PROTOCOL_ERROR, 28, Some(*message), Some(*code))
1892 }
1893 crate::schema::EventPayload::NotificationActivated { action, key } => {
1895 (EVENT_NOTIFICATION_ACTIVATED, 28, Some(*key), Some(*action))
1896 }
1897 crate::schema::EventPayload::KeyDown { .. }
1898 | crate::schema::EventPayload::KeyUp { .. }
1899 | crate::schema::EventPayload::RangeRequested { .. }
1900 | crate::schema::EventPayload::ValueChanged(_)
1901 | crate::schema::EventPayload::WindowSizeChanged { .. }
1902 | crate::schema::EventPayload::DesignSystemResolved(_)
1903 | crate::schema::EventPayload::NotificationPermissionChanged(_)
1904 | crate::schema::EventPayload::CodeChanged { .. }
1905 | crate::schema::EventPayload::CodeEditRejected { .. }
1906 | crate::schema::EventPayload::CodeHovered { .. }
1907 | crate::schema::EventPayload::CodeSaveRequested { .. }
1908 | crate::schema::EventPayload::DecorationActivated { .. }
1909 | crate::schema::EventPayload::SelectionChanged { .. }
1910 | crate::schema::EventPayload::VisibleRangeChanged { .. }
1911 | crate::schema::EventPayload::EditorFocusChanged { .. }
1912 | crate::schema::EventPayload::FoldChanged { .. }
1913 | crate::schema::EventPayload::CompositionChanged { .. }
1914 | crate::schema::EventPayload::WindowFocusChanged { .. }
1915 | crate::schema::EventPayload::WindowMoved { .. }
1916 | crate::schema::EventPayload::WindowDrag { .. }
1917 | crate::schema::EventPayload::WindowDragOver { .. }
1918 | crate::schema::EventPayload::CodeMetrics { .. }
1919 | crate::schema::EventPayload::CodeScrolled { .. }
1920 | crate::schema::EventPayload::FileDialogResult { .. }
1921 | crate::schema::EventPayload::ClipboardText { .. }
1922 | crate::schema::EventPayload::IoAnswer { .. }
1923 | crate::schema::EventPayload::FileHandles { .. }
1924 | crate::schema::EventPayload::ScrollChanged { .. }
1925 | crate::schema::EventPayload::MotionFinished { .. }
1926 | crate::schema::EventPayload::CaretRect { .. }
1927 | crate::schema::EventPayload::PositionRect { .. }
1928 | crate::schema::EventPayload::CodePointer { .. }
1929 | crate::schema::EventPayload::CodeEditGroupResult { .. }
1930 | crate::schema::EventPayload::SelectionsChanged { .. } => unreachable!(),
1931 crate::schema::EventPayload::AnimationEvent { .. }
1932 | crate::schema::EventPayload::Pointer { .. }
1933 | crate::schema::EventPayload::ReducedMotionChanged(_)
1934 | crate::schema::EventPayload::ColorSchemeChanged(_)
1935 | crate::schema::EventPayload::ZoomChanged { .. } => unreachable!(),
1936 };
1937 output.extend_from_slice(&tag.to_le_bytes());
1938 output.extend_from_slice(&record_len.to_le_bytes());
1939 output.extend_from_slice(&event.node_id.to_le_bytes());
1940 output.extend_from_slice(&event.handler_id.to_le_bytes());
1941 if let Some(code) = error_code {
1942 output.extend_from_slice(&code.to_le_bytes());
1943 }
1944 if let Some(text) = text {
1945 let offset = u32::from(record_len);
1946 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1947 output.extend_from_slice(&offset.to_le_bytes());
1948 output.extend_from_slice(&len.to_le_bytes());
1949 output.extend_from_slice(text.as_bytes());
1950 }
1951 Ok(())
1952}
1953
1954fn put_event_header(output: &mut Vec<u8>, tag: u16, record_len: u16, event: &HostEvent<'_>) {
1957 output.extend_from_slice(&tag.to_le_bytes());
1958 output.extend_from_slice(&record_len.to_le_bytes());
1959 output.extend_from_slice(&event.node_id.to_le_bytes());
1960 output.extend_from_slice(&event.handler_id.to_le_bytes());
1961}
1962
1963impl fmt::Display for ProtocolError {
1964 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1965 write!(formatter, "protocol error: {self:?}")
1966 }
1967}
1968
1969impl std::error::Error for ProtocolError {}
1970
1971pub struct BatchEncoder {
1973 records: Vec<u8>,
1974 strings: Vec<u8>,
1975 string_fixups: Vec<usize>,
1976 arena: Vec<u8>,
1977 record_count: u32,
1978}
1979
1980impl Default for BatchEncoder {
1981 fn default() -> Self {
1982 Self::with_capacity(4096, 1024, 64)
1983 }
1984}
1985
1986impl BatchEncoder {
1987 pub fn with_capacity(record_bytes: usize, string_bytes: usize, string_count: usize) -> Self {
1988 let mut encoder = Self {
1989 records: Vec::with_capacity(record_bytes),
1990 strings: Vec::with_capacity(string_bytes),
1991 string_fixups: Vec::with_capacity(string_count),
1992 arena: Vec::with_capacity(record_bytes + string_bytes),
1993 record_count: 0,
1994 };
1995 encoder.clear();
1996 encoder
1997 }
1998
1999 pub fn clear(&mut self) {
2000 self.records.clear();
2001 self.strings.clear();
2002 self.string_fixups.clear();
2003 self.arena.clear();
2004 self.record_count = 0;
2005 self.begin_record(TAG_ENVELOPE, 8);
2006 self.put_u32(0);
2007 self.put_u32(0);
2008 }
2009
2010 pub fn encode(&mut self, mutation: &Mutation<'_>) -> Result<(), ProtocolError> {
2011 match mutation {
2012 Mutation::Create { node_id, widget } => {
2013 self.begin_record(TAG_CREATE, 8);
2014 self.put_u32(*node_id);
2015 self.put_u16(*widget as u16);
2016 self.put_u16(0);
2017 }
2018 Mutation::SetProp {
2019 node_id,
2020 property,
2021 value,
2022 } => {
2023 self.begin_record(TAG_SET_PROP, 20);
2024 self.put_u32(*node_id);
2025 self.put_u16(*property as u16);
2026 match value {
2027 PropertyValue::None => {
2028 self.put_u16(VALUE_NONE);
2029 self.put_u64(0);
2030 self.put_u32(0);
2031 }
2032 PropertyValue::String(value) => {
2033 self.put_u16(VALUE_STRING);
2034 self.put_string_ref(value)?;
2035 self.put_u32(0);
2036 }
2037 PropertyValue::Bool(value) => {
2038 self.put_u16(VALUE_BOOL);
2039 self.put_u64(u64::from(*value));
2040 self.put_u32(0);
2041 }
2042 PropertyValue::Integer(value) => {
2043 self.put_u16(VALUE_I64);
2044 self.put_u64(*value as u64);
2045 self.put_u32(0);
2046 }
2047 PropertyValue::Float(value) => {
2048 self.put_u16(VALUE_F32);
2049 self.put_u64(u64::from(value.to_bits()));
2050 self.put_u32(0);
2051 }
2052 PropertyValue::Bytes(value) => {
2053 self.put_u16(VALUE_BYTES);
2054 self.put_bytes_ref(value)?;
2055 self.put_u32(0);
2056 }
2057 }
2058 }
2059 Mutation::SetModifier {
2060 node_id,
2061 index,
2062 modifier,
2063 } => {
2064 let (tag, words) = modifier_fields(modifier);
2065 let extra = modifier_extra_words(tag).ok_or(ProtocolError::InvalidModifier(tag))?;
2069 self.begin_record(TAG_SET_MODIFIER, 24 + 8 * u16::from(extra));
2070 self.put_u32(*node_id);
2071 self.put_u16(*index);
2072 self.put_u16(tag);
2073 for word in &words[..2 + usize::from(extra)] {
2074 self.put_u64(*word);
2075 }
2076 }
2077 Mutation::Insert {
2078 parent_id,
2079 node_id,
2080 index,
2081 } => {
2082 self.begin_record(TAG_INSERT, 12);
2083 self.put_u32(*parent_id);
2084 self.put_u32(*node_id);
2085 self.put_u32(*index);
2086 }
2087 Mutation::Move {
2088 parent_id,
2089 node_id,
2090 index,
2091 } => {
2092 self.begin_record(TAG_MOVE, 12);
2093 self.put_u32(*parent_id);
2094 self.put_u32(*node_id);
2095 self.put_u32(*index);
2096 }
2097 Mutation::Remove { node_id } => {
2098 self.begin_record(TAG_REMOVE, 4);
2099 self.put_u32(*node_id);
2100 }
2101 Mutation::SetText {
2102 node_id,
2103 text,
2104 selection,
2105 } => {
2106 self.begin_record(TAG_SET_TEXT, 20);
2107 self.put_u32(*node_id);
2108 self.put_string_ref(text)?;
2109 let (start, end) =
2110 selection.map_or((u32::MAX, u32::MAX), |value| (value.start, value.end));
2111 self.put_u32(start);
2112 self.put_u32(end);
2113 }
2114 Mutation::AppendText { node_id, text } => {
2115 self.begin_record(TAG_APPEND_TEXT, 12);
2116 self.put_u32(*node_id);
2117 self.put_string_ref(text)?;
2118 }
2119 Mutation::RegisterAsset {
2120 asset_id,
2121 kind,
2122 bytes,
2123 } => {
2124 self.begin_record(TAG_REGISTER_ASSET, 16);
2125 self.put_u32(*asset_id);
2126 self.put_u16(*kind as u16);
2127 self.put_u16(0);
2128 self.put_bytes_ref(bytes)?;
2129 }
2130 Mutation::ReleaseAsset { asset_id } => {
2131 self.begin_record(TAG_RELEASE_ASSET, 4);
2132 self.put_u32(*asset_id);
2133 }
2134 Mutation::ShowMessage {
2135 handler_id,
2136 text,
2137 action,
2138 duration,
2139 placement,
2140 offset_dp,
2141 } => {
2142 self.begin_record(TAG_SHOW_MESSAGE, 36);
2143 self.put_u64(*handler_id);
2144 self.put_string_ref(text)?;
2145 self.put_string_ref(action)?;
2146 self.put_u16(*duration as u16);
2147 self.put_u16(placement.map_or(0, |placement| placement.vertical as u16));
2150 self.put_u16(placement.map_or(0, |placement| placement.horizontal as u16));
2151 self.put_u16(0);
2152 self.put_u32(offset_dp.unwrap_or(f32::NAN).to_bits());
2153 }
2154 Mutation::EditCode {
2155 node_id,
2156 request_id,
2157 base_version,
2158 start_line,
2159 start_column,
2160 end_line,
2161 end_column,
2162 text,
2163 } => {
2164 self.begin_record(TAG_EDIT_CODE, 36);
2165 self.put_u32(*node_id);
2166 self.put_u32(*request_id);
2167 self.put_u32(*base_version);
2168 self.put_u32(*start_line);
2169 self.put_u32(*start_column);
2170 self.put_u32(*end_line);
2171 self.put_u32(*end_column);
2172 self.put_string_ref(text)?;
2173 }
2174 Mutation::EditCodeGroup {
2175 node_id,
2176 request_id,
2177 base_version,
2178 edits,
2179 } => {
2180 self.begin_record(TAG_EDIT_CODE_GROUP, (EDIT_CODE_GROUP_LEN - 4) as u16);
2181 self.put_u32(*node_id);
2182 self.put_u32(*request_id);
2183 self.put_u32(*base_version);
2184 self.put_bytes_ref(edits)?;
2185 }
2186 Mutation::SetSelection {
2187 node_id,
2188 base_version,
2189 anchor_line,
2190 anchor_column,
2191 active_line,
2192 active_column,
2193 } => {
2194 self.begin_record(TAG_SET_SELECTION, 24);
2195 self.put_u32(*node_id);
2196 self.put_u32(*base_version);
2197 self.put_u32(*anchor_line);
2198 self.put_u32(*anchor_column);
2199 self.put_u32(*active_line);
2200 self.put_u32(*active_column);
2201 }
2202 Mutation::RevealRange {
2203 node_id,
2204 base_version,
2205 start_line,
2206 start_column,
2207 end_line,
2208 end_column,
2209 align,
2210 } => {
2211 self.begin_record(TAG_REVEAL_RANGE, 28);
2212 self.put_u32(*node_id);
2213 self.put_u32(*base_version);
2214 self.put_u32(*start_line);
2215 self.put_u32(*start_column);
2216 self.put_u32(*end_line);
2217 self.put_u32(*end_column);
2218 self.put_u32(u32::from(*align as u16));
2219 }
2220 Mutation::SetFolded {
2221 node_id,
2222 start_line,
2223 collapsed,
2224 } => {
2225 self.begin_record(TAG_SET_FOLDED, 12);
2226 self.put_u32(*node_id);
2227 self.put_u32(*start_line);
2228 self.put_u32(u32::from(*collapsed));
2229 }
2230 Mutation::SetTheme(theme) => {
2231 self.begin_record(TAG_SET_THEME, THEME_PAYLOAD_LEN as u16);
2232 self.put_theme(theme)?;
2233 }
2234 Mutation::SetWindowTheme { node_id, theme } => {
2235 self.begin_record(TAG_SET_WINDOW_THEME, 4 + THEME_PAYLOAD_LEN as u16);
2236 self.put_u32(*node_id);
2237 self.put_theme(theme)?;
2238 }
2239 Mutation::FocusWindow { node_id } => {
2240 self.begin_record(TAG_FOCUS_WINDOW, 4);
2241 self.put_u32(*node_id);
2242 }
2243 Mutation::RequestFocus { node_id } => {
2244 self.begin_record(TAG_REQUEST_FOCUS, 4);
2245 self.put_u32(*node_id);
2246 }
2247 Mutation::SetScrollOffset { node_id, offset_px } => {
2248 self.begin_record(TAG_SET_SCROLL_OFFSET, 12);
2249 self.put_u32(*node_id);
2250 self.put_u64(offset_px.to_bits());
2251 }
2252 Mutation::ShowFileDialog {
2253 request,
2254 window,
2255 kind,
2256 default_filter,
2257 title,
2258 directory,
2259 name,
2260 filters,
2261 } => {
2262 self.begin_record(TAG_SHOW_FILE_DIALOG, SHOW_FILE_DIALOG_PAYLOAD_LEN as u16);
2263 self.put_u32(*request);
2264 self.put_u32(*window);
2265 self.put_u16(*kind as u16);
2266 self.put_u16(*default_filter);
2267 self.put_string_ref(title)?;
2268 self.put_string_ref(directory)?;
2269 self.put_string_ref(name)?;
2270 self.put_string_ref(filters)?;
2271 }
2272 Mutation::SetOverlayTokens(overlay) => {
2273 self.begin_record(TAG_SET_OVERLAY_TOKENS, OVERLAY_TOKENS_PAYLOAD_LEN as u16);
2274 self.put_u16(overlay.sheet_corner_radius);
2275 self.put_u16(overlay.dialog_corner_radius);
2276 self.put_u16(overlay.popup_corner_radius);
2277 self.put_u16(overlay.sheet_elevation);
2278 self.put_u16(overlay.popup_elevation);
2279 self.put_u16(overlay.dialog_elevation);
2280 self.put_u16(overlay.sheet_padding);
2281 self.put_u16(overlay.dialog_padding);
2282 self.put_u16(overlay.popup_padding);
2283 self.put_u16(0);
2284 self.put_u32(overlay.scrim);
2285 self.put_u16(overlay.toast_elevation);
2286 self.put_u16(overlay.toast_corner_radius);
2287 }
2288 Mutation::SetClipboardText { text } => {
2289 self.begin_record(TAG_SET_CLIPBOARD_TEXT, 8);
2290 self.put_string_ref(text)?;
2291 }
2292 Mutation::ReadClipboard { request } => {
2293 self.begin_record(TAG_READ_CLIPBOARD, 4);
2294 self.put_u32(*request);
2295 }
2296 Mutation::StorageCall {
2297 request,
2298 op,
2299 store,
2300 key,
2301 data,
2302 } => {
2303 self.begin_record(TAG_STORAGE_CALL, STORAGE_CALL_PAYLOAD_LEN as u16);
2304 self.put_u32(*request);
2305 self.put_u16(u16::from(*op));
2306 self.put_u16(0);
2307 self.put_string_ref(store)?;
2308 self.put_string_ref(key)?;
2309 self.put_bytes_ref(data)?;
2310 }
2311 Mutation::FileCall {
2312 request,
2313 op,
2314 create,
2315 handle,
2316 name,
2317 data,
2318 } => {
2319 self.begin_record(TAG_FILE_CALL, FILE_CALL_PAYLOAD_LEN as u16);
2320 self.put_u32(*request);
2321 self.put_u16(*op as u16);
2322 self.put_u16(u16::from(*create));
2323 self.put_u32(*handle);
2324 self.put_string_ref(name)?;
2325 self.put_bytes_ref(data)?;
2326 }
2327 Mutation::ReleaseFileHandle { handle } => {
2328 self.begin_record(TAG_RELEASE_FILE_HANDLE, 4);
2329 self.put_u32(*handle);
2330 }
2331 Mutation::ScrollTo {
2332 node_id,
2333 item,
2334 flags,
2335 x_px,
2336 y_px,
2337 } => {
2338 self.begin_record(TAG_SCROLL_TO, (SCROLL_TO_LEN - 4) as u16);
2339 self.put_u32(*node_id);
2340 self.put_u32(*item);
2341 self.put_u32(*flags);
2342 self.put_u64(x_px.to_bits());
2343 self.put_u64(y_px.to_bits());
2344 }
2345 Mutation::SetSelections {
2346 node_id,
2347 base_version,
2348 selections,
2349 } => {
2350 self.begin_record(TAG_SET_SELECTIONS, (SET_SELECTIONS_LEN - 4) as u16);
2351 self.put_u32(*node_id);
2352 self.put_u32(*base_version);
2353 self.put_bytes_ref(selections)?;
2354 }
2355 Mutation::InsertSnippet {
2356 node_id,
2357 base_version,
2358 start_line,
2359 start_column,
2360 end_line,
2361 end_column,
2362 snippet,
2363 } => {
2364 self.begin_record(TAG_INSERT_SNIPPET, (INSERT_SNIPPET_LEN - 4) as u16);
2365 self.put_u32(*node_id);
2366 self.put_u32(*base_version);
2367 self.put_u32(*start_line);
2368 self.put_u32(*start_column);
2369 self.put_u32(*end_line);
2370 self.put_u32(*end_column);
2371 self.put_string_ref(snippet)?;
2372 }
2373 Mutation::QueryPositionRect {
2374 node_id,
2375 request,
2376 line,
2377 column,
2378 } => {
2379 self.begin_record(
2380 TAG_QUERY_POSITION_RECT,
2381 (QUERY_POSITION_RECT_LEN - 4) as u16,
2382 );
2383 self.put_u32(*node_id);
2384 self.put_u32(*request);
2385 self.put_u32(*line);
2386 self.put_u32(*column);
2387 }
2388 Mutation::ScrollIntoView {
2389 node_id,
2390 target,
2391 target_kind,
2392 align_x,
2393 align_y,
2394 flags,
2395 rect,
2396 } => {
2397 self.begin_record(TAG_SCROLL_INTO_VIEW, (SCROLL_INTO_VIEW_LEN - 4) as u16);
2398 self.put_u32(*node_id);
2399 self.put_u32(*target);
2400 self.put_u16(*target_kind);
2401 self.put_u16(*align_x);
2402 self.put_u16(*align_y);
2403 self.put_u16(*flags);
2404 self.put_u64(rect.x_px.to_bits());
2405 self.put_u64(rect.y_px.to_bits());
2406 self.put_u64(rect.width_px.to_bits());
2407 self.put_u64(rect.height_px.to_bits());
2408 }
2409 Mutation::SetWindow(window) => {
2410 self.begin_record(TAG_SET_WINDOW, 28);
2411 self.put_u16(window.chrome as u16);
2412 self.put_u16(window.title_bar as u16);
2413 self.put_u16(window.width);
2414 self.put_u16(window.height);
2415 self.put_u16(window.min_width);
2416 self.put_u16(window.min_height);
2417 self.put_u16(
2422 u16::from(window.resizable)
2423 | (u16::from(window.report_size) << 1)
2424 | ((window.mode as u16) << WINDOW_MODE_SHIFT)
2425 | ((window.last_window_closed as u16) << WINDOW_POLICY_SHIFT),
2426 );
2427 self.put_u16(match window.zoom_level {
2432 None => 0,
2433 Some(level) => 1 | (u16::from(crate::zoom::clamp_level(level) as u8) << 8),
2434 });
2435 self.put_string_ref(window.title)?;
2436 self.put_u32(window.icon);
2437 }
2438 Mutation::PostNotification {
2439 key,
2440 title,
2441 body,
2442 channel,
2443 action_1,
2444 action_2,
2445 importance,
2446 presentation,
2447 } => {
2448 self.begin_record(TAG_POST_NOTIFICATION, 52);
2449 self.put_string_ref(key)?;
2450 self.put_string_ref(title)?;
2451 self.put_string_ref(body)?;
2452 self.put_string_ref(channel)?;
2453 self.put_string_ref(action_1)?;
2454 self.put_string_ref(action_2)?;
2455 self.put_u16(*importance as u16);
2456 self.put_u16(*presentation as u16);
2457 }
2458 Mutation::WithdrawNotification { key } => {
2459 self.begin_record(TAG_WITHDRAW_NOTIFICATION, 8);
2460 self.put_string_ref(key)?;
2461 }
2462 Mutation::RequestNotificationPermission => {
2463 self.begin_record(TAG_REQUEST_NOTIFICATION_PERMISSION, 0);
2464 }
2465 Mutation::StartAnimation(animation) => self.put_animation(animation)?,
2466 Mutation::ControlAnimation {
2467 node_id,
2468 animation_id,
2469 property,
2470 slot,
2471 op,
2472 at_time_nanos,
2473 } => {
2474 self.begin_record(TAG_CONTROL_ANIMATION, 20);
2475 self.put_u32(*node_id);
2476 self.put_u32(*animation_id);
2477 self.records.push(*property as u8);
2478 self.records.push(*slot);
2479 self.records.push(*op as u8);
2480 self.records.push(0);
2481 self.put_u64(*at_time_nanos);
2482 }
2483 }
2484 self.record_count = self
2485 .record_count
2486 .checked_add(1)
2487 .ok_or(ProtocolError::LengthOverflow)?;
2488 Ok(())
2489 }
2490
2491 pub fn finish(&mut self) -> Result<&[u8], ProtocolError> {
2492 let records_len =
2493 u32::try_from(self.records.len()).map_err(|_| ProtocolError::LengthOverflow)?;
2494 self.records[4..8].copy_from_slice(&records_len.to_le_bytes());
2495 self.records[8..12].copy_from_slice(&self.record_count.to_le_bytes());
2496 let total = self
2497 .records
2498 .len()
2499 .checked_add(self.strings.len())
2500 .ok_or(ProtocolError::LengthOverflow)?;
2501 self.arena.clear();
2502 if self.arena.capacity() < total {
2503 self.arena.reserve(total);
2504 }
2505 self.arena.extend_from_slice(&self.records);
2506 self.arena.extend_from_slice(&self.strings);
2507 for &position in &self.string_fixups {
2508 let relative = read_u32(&self.arena, position)?;
2509 let absolute = records_len
2510 .checked_add(relative)
2511 .ok_or(ProtocolError::LengthOverflow)?;
2512 self.arena[position..position + 4].copy_from_slice(&absolute.to_le_bytes());
2513 }
2514 Ok(&self.arena)
2515 }
2516
2517 pub fn arena(&self) -> (*const u8, usize) {
2524 (self.arena.as_ptr(), self.arena.capacity())
2525 }
2526
2527 pub fn capacity(&self) -> usize {
2528 self.records.capacity() + self.strings.capacity() + self.arena.capacity()
2529 }
2530
2531 fn put_animation(
2537 &mut self,
2538 animation: &crate::schema::Animation<'_>,
2539 ) -> Result<(), ProtocolError> {
2540 use crate::schema::{KeyframeValue, Timing};
2541 let keyframes_len: usize = animation
2542 .keyframes
2543 .iter()
2544 .map(|keyframe| {
2545 KEYFRAME_HEAD_LEN
2546 + match &keyframe.value {
2547 KeyframeValue::Alpha(_) => 4,
2548 KeyframeValue::Paint(_) => 8,
2549 KeyframeValue::Transform(functions) => {
2550 4 + TRANSFORM_FUNCTION_LEN * functions.len()
2551 }
2552 }
2553 })
2554 .sum();
2555 let payload = u16::try_from(ANIMATION_HEADER_LEN - 4 + keyframes_len)
2556 .ok()
2557 .filter(|payload| *payload <= u16::MAX - 4)
2558 .ok_or(ProtocolError::LengthOverflow)?;
2559 let keyframe_count =
2560 u16::try_from(animation.keyframes.len()).map_err(|_| ProtocolError::LengthOverflow)?;
2561 self.begin_record(TAG_START_ANIMATION, payload);
2562 self.put_u32(animation.node_id);
2563 self.put_u32(animation.animation_id);
2564 self.records.push(animation.property as u8);
2565 self.records.push(animation.slot);
2566 self.records.push(animation.direction as u8);
2567 self.records.push(animation.fill as u8);
2568 self.records.push(animation.play_state as u8);
2569 self.records
2570 .push(animation.interpolation.map_or(0, |value| value as u8));
2571 self.put_u16(keyframe_count);
2572 self.put_u64(animation.start_time_nanos);
2573 self.put_u32(animation.delay_ms.to_bits());
2574 self.put_u32(animation.duration_ms.to_bits());
2575 self.put_u32(animation.iterations.to_bits());
2576 self.put_u32(animation.origin_x.to_bits());
2577 self.put_u32(animation.origin_y.to_bits());
2578 self.records.push(animation.events.0);
2579 self.records.extend_from_slice(&[0, 0, 0]);
2580 for keyframe in animation.keyframes.iter() {
2581 self.put_u32(keyframe.offset.to_bits());
2582 let (kind, position, numbers) = match keyframe.timing {
2583 Timing::Linear => (1_u8, 0_u8, [0_u32; 4]),
2584 Timing::CubicBezier { x1, y1, x2, y2 } => (
2585 2,
2586 0,
2587 [x1.to_bits(), y1.to_bits(), x2.to_bits(), y2.to_bits()],
2588 ),
2589 Timing::Steps { count, position } => (3, position as u8, [count, 0, 0, 0]),
2590 };
2591 self.records.push(kind);
2592 self.records.push(position);
2593 self.put_u16(if keyframe.from_presented {
2594 KEYFRAME_FROM_PRESENTED
2595 } else {
2596 0
2597 });
2598 for number in numbers {
2599 self.put_u32(number);
2600 }
2601 match &keyframe.value {
2602 KeyframeValue::Alpha(value) => self.put_u32(value.to_bits()),
2603 KeyframeValue::Paint(paint) => self.put_u64(paint.to_bits()),
2604 KeyframeValue::Transform(functions) => {
2605 let count = u16::try_from(functions.len())
2606 .map_err(|_| ProtocolError::LengthOverflow)?;
2607 self.put_u16(count);
2608 self.put_u16(0);
2609 for function in functions.iter() {
2610 self.put_u32(u32::from(u16::from(function.kind)));
2611 for value in function.values {
2612 self.put_u32(value.to_bits());
2613 }
2614 }
2615 }
2616 }
2617 }
2618 Ok(())
2619 }
2620
2621 fn put_theme(&mut self, theme: &Theme) -> Result<(), ProtocolError> {
2623 self.put_u16(theme.design_system as u16);
2624 self.put_u16(theme.fallback as u16);
2625 self.put_u16(theme.color_scheme as u16);
2626 self.put_u16(u16::from(theme.adaptive));
2627 for asset in theme.fonts {
2631 self.put_u32(asset);
2632 }
2633 self.put_palette_ref(theme.palette)
2638 }
2639
2640 fn begin_record(&mut self, tag: u16, payload_len: u16) {
2641 self.put_u16(tag);
2642 self.put_u16(payload_len + 4);
2643 }
2644
2645 fn put_palette_ref(
2648 &mut self,
2649 palette: Option<&crate::palette::Palette>,
2650 ) -> Result<(), ProtocolError> {
2651 let start = self.strings.len();
2652 let offset = u32::try_from(start).map_err(|_| ProtocolError::LengthOverflow)?;
2653 if let Some(palette) = palette {
2654 palette.write_into(&mut self.strings);
2655 }
2656 let len =
2657 u32::try_from(self.strings.len() - start).map_err(|_| ProtocolError::LengthOverflow)?;
2658 self.string_fixups.push(self.records.len());
2659 self.put_u32(offset);
2660 self.put_u32(len);
2661 Ok(())
2662 }
2663
2664 fn put_string_ref(&mut self, value: &str) -> Result<(), ProtocolError> {
2665 self.put_bytes_ref(value.as_bytes())
2666 }
2667
2668 fn put_bytes_ref(&mut self, value: &[u8]) -> Result<(), ProtocolError> {
2671 let offset =
2672 u32::try_from(self.strings.len()).map_err(|_| ProtocolError::LengthOverflow)?;
2673 let len = u32::try_from(value.len()).map_err(|_| ProtocolError::LengthOverflow)?;
2674 self.string_fixups.push(self.records.len());
2675 self.put_u32(offset);
2676 self.put_u32(len);
2677 self.strings.extend_from_slice(value);
2678 Ok(())
2679 }
2680
2681 fn put_u16(&mut self, value: u16) {
2682 self.records.extend_from_slice(&value.to_le_bytes());
2683 }
2684
2685 fn put_u32(&mut self, value: u32) {
2686 self.records.extend_from_slice(&value.to_le_bytes());
2687 }
2688
2689 fn put_u64(&mut self, value: u64) {
2690 self.records.extend_from_slice(&value.to_le_bytes());
2691 }
2692}
2693
2694fn decode_theme(bytes: &[u8], payload: usize, records_len: usize) -> Result<Theme, ProtocolError> {
2697 let design_system = read_u16(bytes, payload)?;
2698 let fallback = read_u16(bytes, payload + 2)?;
2699 let color_scheme = read_u16(bytes, payload + 4)?;
2700 let adaptive = read_u16(bytes, payload + 6)?;
2701 if adaptive > 1 {
2702 return Err(ProtocolError::InvalidTheme(adaptive));
2703 }
2704 let mut fonts = [0u32; TYPE_ROLE_COUNT];
2705 for (index, slot) in fonts.iter_mut().enumerate() {
2706 *slot = read_u32(bytes, payload + 8 + 4 * index)?;
2707 }
2708 let palette_bytes = read_bytes(bytes, payload + 8 + 4 * TYPE_ROLE_COUNT, records_len)?;
2713 let palette = if palette_bytes.is_empty() {
2714 None
2715 } else {
2716 let (palette, _problems) = crate::palette::decode_palette(palette_bytes);
2717 Some(&*Box::leak(Box::new(palette)))
2718 };
2719 Ok(Theme {
2720 design_system: DesignSystem::try_from(design_system)
2721 .map_err(|()| ProtocolError::InvalidTheme(design_system))?,
2722 fallback: DesignSystem::try_from(fallback)
2723 .map_err(|()| ProtocolError::InvalidTheme(fallback))?,
2724 color_scheme: ColorScheme::try_from(color_scheme)
2725 .map_err(|()| ProtocolError::InvalidTheme(color_scheme))?,
2726 adaptive: adaptive == 1,
2727 fonts,
2728 palette,
2729 overlay: None,
2730 })
2731}
2732
2733pub fn decode_batch(bytes: &[u8]) -> Result<Vec<Mutation<'_>>, ProtocolError> {
2734 if bytes.len() < ENVELOPE_LEN
2735 || read_u16(bytes, 0)? != TAG_ENVELOPE
2736 || read_u16(bytes, 2)? != 12
2737 {
2738 return Err(ProtocolError::InvalidEnvelope);
2739 }
2740 let records_len =
2741 usize::try_from(read_u32(bytes, 4)?).map_err(|_| ProtocolError::LengthOverflow)?;
2742 let count = usize::try_from(read_u32(bytes, 8)?).map_err(|_| ProtocolError::LengthOverflow)?;
2743 if records_len < ENVELOPE_LEN || records_len > bytes.len() || records_len % 4 != 0 {
2744 return Err(ProtocolError::InvalidEnvelope);
2745 }
2746 let capacity = count.min((records_len - ENVELOPE_LEN) / MIN_RECORD_LEN);
2751 let mut output = Vec::with_capacity(capacity);
2752 let mut position = ENVELOPE_LEN;
2753 while position < records_len {
2754 let tag = read_u16(bytes, position)?;
2755 let len = usize::from(read_u16(bytes, position + 2)?);
2756 if len < 4 || len % 4 != 0 || position + len > records_len {
2757 return Err(ProtocolError::InvalidRecordLength);
2758 }
2759 let payload = position + 4;
2760 let mutation = match tag {
2761 TAG_CREATE if len == 12 => Mutation::Create {
2762 node_id: read_u32(bytes, payload)?,
2763 widget: WidgetKind::try_from(read_u16(bytes, payload + 4)?).map_err(|()| {
2764 ProtocolError::InvalidWidget(read_u16(bytes, payload + 4).unwrap_or(0))
2765 })?,
2766 },
2767 TAG_SET_PROP if len == 24 => {
2768 let property_raw = read_u16(bytes, payload + 4)?;
2769 let value_kind = read_u16(bytes, payload + 6)?;
2770 let value = match value_kind {
2771 VALUE_NONE => PropertyValue::None,
2772 VALUE_STRING => {
2773 PropertyValue::String(read_string(bytes, payload + 8, records_len)?)
2774 }
2775 VALUE_BOOL => PropertyValue::Bool(read_u64(bytes, payload + 8)? != 0),
2776 VALUE_I64 => PropertyValue::Integer(read_u64(bytes, payload + 8)? as i64),
2777 VALUE_F32 => {
2778 PropertyValue::Float(f32::from_bits(read_u64(bytes, payload + 8)? as u32))
2779 }
2780 VALUE_BYTES => {
2781 PropertyValue::Bytes(read_bytes(bytes, payload + 8, records_len)?)
2782 }
2783 other => return Err(ProtocolError::InvalidValueKind(other)),
2784 };
2785 Mutation::SetProp {
2786 node_id: read_u32(bytes, payload)?,
2787 property: PropertyKind::try_from(property_raw)
2788 .map_err(|()| ProtocolError::InvalidProperty(property_raw))?,
2789 value,
2790 }
2791 }
2792 TAG_SET_MODIFIER if len >= 28 => {
2793 let modifier_tag = read_u16(bytes, payload + 6)?;
2797 let extra = usize::from(
2798 modifier_extra_words(modifier_tag)
2799 .ok_or(ProtocolError::InvalidModifier(modifier_tag))?,
2800 );
2801 if len != 28 + 8 * extra {
2802 return Err(ProtocolError::InvalidRecordLength);
2803 }
2804 let mut words = [0_u64; MAX_MODIFIER_WORDS];
2805 for (word_index, word) in words.iter_mut().take(2 + extra).enumerate() {
2806 *word = read_u64(bytes, payload + 8 + 8 * word_index)?;
2807 }
2808 Mutation::SetModifier {
2809 node_id: read_u32(bytes, payload)?,
2810 index: read_u16(bytes, payload + 4)?,
2811 modifier: decode_modifier(modifier_tag, &words)?,
2812 }
2813 }
2814 TAG_INSERT if len == 16 => Mutation::Insert {
2815 parent_id: read_u32(bytes, payload)?,
2816 node_id: read_u32(bytes, payload + 4)?,
2817 index: read_u32(bytes, payload + 8)?,
2818 },
2819 TAG_MOVE if len == 16 => Mutation::Move {
2820 parent_id: read_u32(bytes, payload)?,
2821 node_id: read_u32(bytes, payload + 4)?,
2822 index: read_u32(bytes, payload + 8)?,
2823 },
2824 TAG_REMOVE if len == 8 => Mutation::Remove {
2825 node_id: read_u32(bytes, payload)?,
2826 },
2827 TAG_SET_TEXT if len == 24 => {
2828 let start = read_u32(bytes, payload + 12)?;
2829 let end = read_u32(bytes, payload + 16)?;
2830 Mutation::SetText {
2831 node_id: read_u32(bytes, payload)?,
2832 text: read_string(bytes, payload + 4, records_len)?,
2833 selection: (start != u32::MAX || end != u32::MAX)
2834 .then_some(Selection { start, end }),
2835 }
2836 }
2837 TAG_APPEND_TEXT if len == 16 => Mutation::AppendText {
2838 node_id: read_u32(bytes, payload)?,
2839 text: read_string(bytes, payload + 4, records_len)?,
2840 },
2841 TAG_SET_THEME if len == 4 + THEME_PAYLOAD_LEN => {
2842 Mutation::SetTheme(decode_theme(bytes, payload, records_len)?)
2843 }
2844 TAG_SET_WINDOW_THEME if len == 8 + THEME_PAYLOAD_LEN => Mutation::SetWindowTheme {
2845 node_id: read_u32(bytes, payload)?,
2846 theme: decode_theme(bytes, payload + 4, records_len)?,
2847 },
2848 TAG_FOCUS_WINDOW if len == 8 => Mutation::FocusWindow {
2849 node_id: read_u32(bytes, payload)?,
2850 },
2851 TAG_REQUEST_FOCUS if len == 8 => Mutation::RequestFocus {
2852 node_id: read_u32(bytes, payload)?,
2853 },
2854 TAG_SHOW_FILE_DIALOG if len == 4 + SHOW_FILE_DIALOG_PAYLOAD_LEN => {
2855 let raw_kind = read_u16(bytes, payload + 8)?;
2856 Mutation::ShowFileDialog {
2857 request: read_u32(bytes, payload)?,
2858 window: read_u32(bytes, payload + 4)?,
2859 kind: FileDialogKind::try_from(raw_kind)
2860 .map_err(|()| ProtocolError::InvalidValueKind(raw_kind))?,
2861 default_filter: read_u16(bytes, payload + 10)?,
2862 title: read_string(bytes, payload + 12, records_len)?,
2863 directory: read_string(bytes, payload + 20, records_len)?,
2864 name: read_string(bytes, payload + 28, records_len)?,
2865 filters: read_string(bytes, payload + 36, records_len)?,
2866 }
2867 }
2868 TAG_SHOW_FILE_DIALOG => return Err(ProtocolError::InvalidRecordLength),
2869 TAG_SET_OVERLAY_TOKENS if len == 4 + OVERLAY_TOKENS_PAYLOAD_LEN => {
2870 let sheet_corner_radius = read_u16(bytes, payload)?;
2871 let dialog_corner_radius = read_u16(bytes, payload + 2)?;
2872 let popup_corner_radius = read_u16(bytes, payload + 4)?;
2873 let sheet_elevation = read_u16(bytes, payload + 6)?;
2874 let popup_elevation = read_u16(bytes, payload + 8)?;
2875 let dialog_elevation = read_u16(bytes, payload + 10)?;
2876 let sheet_padding = read_u16(bytes, payload + 12)?;
2877 let dialog_padding = read_u16(bytes, payload + 14)?;
2878 let popup_padding = read_u16(bytes, payload + 16)?;
2879 let scrim = read_u32(bytes, payload + 20)?;
2880 let toast_elevation = read_u16(bytes, payload + 24)?;
2881 let toast_corner_radius = read_u16(bytes, payload + 26)?;
2882 Mutation::SetOverlayTokens(crate::schema::OverlayTokens {
2883 sheet_corner_radius,
2884 dialog_corner_radius,
2885 popup_corner_radius,
2886 sheet_elevation,
2887 popup_elevation,
2888 dialog_elevation,
2889 sheet_padding,
2890 dialog_padding,
2891 popup_padding,
2892 scrim,
2893 toast_elevation,
2894 toast_corner_radius,
2895 })
2896 }
2897 TAG_SET_OVERLAY_TOKENS => return Err(ProtocolError::InvalidRecordLength),
2898 TAG_SET_CLIPBOARD_TEXT if len == 12 => Mutation::SetClipboardText {
2899 text: read_string(bytes, payload, records_len)?,
2900 },
2901 TAG_READ_CLIPBOARD if len == 8 => Mutation::ReadClipboard {
2902 request: read_u32(bytes, payload)?,
2903 },
2904 TAG_STORAGE_CALL if len == 4 + STORAGE_CALL_PAYLOAD_LEN => {
2905 let raw_op = read_u16(bytes, payload + 4)?;
2906 Mutation::StorageCall {
2907 request: read_u32(bytes, payload)?,
2908 op: crate::schema::StorageOp::try_from(raw_op)
2909 .map_err(|()| ProtocolError::InvalidValueKind(raw_op))?,
2910 store: read_string(bytes, payload + 8, records_len)?,
2911 key: read_string(bytes, payload + 16, records_len)?,
2912 data: read_bytes(bytes, payload + 24, records_len)?,
2913 }
2914 }
2915 TAG_FILE_CALL if len == 4 + FILE_CALL_PAYLOAD_LEN => {
2916 let raw_op = read_u16(bytes, payload + 4)?;
2917 let raw_create = read_u16(bytes, payload + 6)?;
2918 if raw_create > 1 {
2919 return Err(ProtocolError::InvalidValueKind(raw_create));
2920 }
2921 Mutation::FileCall {
2922 request: read_u32(bytes, payload)?,
2923 op: FileOp::try_from(raw_op)
2924 .map_err(|()| ProtocolError::InvalidValueKind(raw_op))?,
2925 create: raw_create == 1,
2926 handle: read_u32(bytes, payload + 8)?,
2927 name: read_string(bytes, payload + 12, records_len)?,
2928 data: read_bytes(bytes, payload + 20, records_len)?,
2929 }
2930 }
2931 TAG_RELEASE_FILE_HANDLE if len == 8 => Mutation::ReleaseFileHandle {
2932 handle: read_u32(bytes, payload)?,
2933 },
2934 TAG_SCROLL_TO if len == SCROLL_TO_LEN => Mutation::ScrollTo {
2935 node_id: read_u32(bytes, payload)?,
2936 item: read_u32(bytes, payload + 4)?,
2937 flags: read_u32(bytes, payload + 8)?,
2938 x_px: f64::from_bits(read_u64(bytes, payload + 12)?),
2939 y_px: f64::from_bits(read_u64(bytes, payload + 20)?),
2940 },
2941 TAG_SCROLL_INTO_VIEW if len == SCROLL_INTO_VIEW_LEN => Mutation::ScrollIntoView {
2942 node_id: read_u32(bytes, payload)?,
2943 target: read_u32(bytes, payload + 4)?,
2944 target_kind: read_u16(bytes, payload + 8)?,
2945 align_x: read_u16(bytes, payload + 10)?,
2946 align_y: read_u16(bytes, payload + 12)?,
2947 flags: read_u16(bytes, payload + 14)?,
2948 rect: crate::schema::Rect {
2949 x_px: f64::from_bits(read_u64(bytes, payload + 16)?),
2950 y_px: f64::from_bits(read_u64(bytes, payload + 24)?),
2951 width_px: f64::from_bits(read_u64(bytes, payload + 32)?),
2952 height_px: f64::from_bits(read_u64(bytes, payload + 40)?),
2953 },
2954 },
2955 TAG_SET_WINDOW if len == 32 => {
2956 let chrome = read_u16(bytes, payload)?;
2957 let title_bar = read_u16(bytes, payload + 2)?;
2958 let flags = read_u16(bytes, payload + 12)?;
2962 if flags & 0x00FC != 0 {
2963 return Err(ProtocolError::InvalidValueKind(flags));
2964 }
2965 let resizable = flags & 1;
2966 let report_size = flags & 0x0002 != 0;
2967 let mode_tag = (flags >> WINDOW_MODE_SHIFT) & 0xF;
2968 let policy_tag = flags >> WINDOW_POLICY_SHIFT;
2969 let zoom = read_u16(bytes, payload + 14)?;
2970 let zoom_level = match zoom & 0xFF {
2971 0 => None,
2972 1 => {
2973 let level = (zoom >> 8) as u8 as i8;
2974 if !(crate::zoom::MIN_ZOOM_LEVEL..=crate::zoom::MAX_ZOOM_LEVEL)
2975 .contains(&level)
2976 {
2977 return Err(ProtocolError::InvalidValueKind(zoom));
2978 }
2979 Some(level)
2980 }
2981 _ => return Err(ProtocolError::InvalidValueKind(zoom)),
2982 };
2983 Mutation::SetWindow(crate::schema::Window {
2984 chrome: crate::schema::Chrome::try_from(chrome)
2985 .map_err(|()| ProtocolError::InvalidValueKind(chrome))?,
2986 title_bar: crate::schema::TitleBar::try_from(title_bar)
2987 .map_err(|()| ProtocolError::InvalidValueKind(title_bar))?,
2988 width: read_u16(bytes, payload + 4)?,
2989 height: read_u16(bytes, payload + 6)?,
2990 min_width: read_u16(bytes, payload + 8)?,
2991 min_height: read_u16(bytes, payload + 10)?,
2992 resizable: resizable == 1,
2993 zoom_level,
2994 report_size,
2995 mode: crate::schema::WindowMode::try_from(mode_tag)
2996 .map_err(|()| ProtocolError::InvalidValueKind(flags))?,
2997 last_window_closed: crate::schema::LastWindowClosed::try_from(policy_tag)
2998 .map_err(|()| ProtocolError::InvalidValueKind(flags))?,
2999 icon: read_u32(bytes, payload + 24)?,
3003 title: Box::leak(
3004 read_string(bytes, payload + 16, records_len)?
3005 .to_owned()
3006 .into_boxed_str(),
3007 ),
3008 })
3009 }
3010 TAG_REGISTER_ASSET if len == 20 => {
3011 let raw_kind = read_u16(bytes, payload + 4)?;
3012 Mutation::RegisterAsset {
3013 asset_id: read_u32(bytes, payload)?,
3014 kind: AssetKind::try_from(raw_kind)
3015 .map_err(|()| ProtocolError::InvalidAssetKind(raw_kind))?,
3016 bytes: read_bytes(bytes, payload + 8, records_len)?,
3017 }
3018 }
3019 TAG_RELEASE_ASSET if len == 8 => Mutation::ReleaseAsset {
3020 asset_id: read_u32(bytes, payload)?,
3021 },
3022 TAG_SHOW_MESSAGE if len == 40 => {
3023 let raw_duration = read_u16(bytes, payload + 24)?;
3024 let raw_vertical = read_u16(bytes, payload + 26)?;
3025 let raw_horizontal = read_u16(bytes, payload + 28)?;
3026 let placement = match (raw_vertical, raw_horizontal) {
3027 (0, 0) => None,
3028 (0, raw) | (raw, 0) => {
3029 return Err(ProtocolError::InvalidMessagePlacement(raw));
3030 }
3031 (vertical, horizontal) => Some(MessagePlacement {
3032 vertical: MessageVertical::try_from(vertical)
3033 .map_err(|()| ProtocolError::InvalidMessagePlacement(vertical))?,
3034 horizontal: MessageHorizontal::try_from(horizontal)
3035 .map_err(|()| ProtocolError::InvalidMessagePlacement(horizontal))?,
3036 }),
3037 };
3038 let offset = f32::from_bits(read_u32(bytes, payload + 32)?);
3039 Mutation::ShowMessage {
3040 handler_id: read_u64(bytes, payload)?,
3041 text: read_string(bytes, payload + 8, records_len)?,
3042 action: read_string(bytes, payload + 16, records_len)?,
3043 duration: MessageDuration::try_from(raw_duration)
3044 .map_err(|()| ProtocolError::InvalidMessageDuration(raw_duration))?,
3045 placement,
3046 offset_dp: (!offset.is_nan()).then_some(offset),
3047 }
3048 }
3049 TAG_EDIT_CODE if len == 40 => Mutation::EditCode {
3050 node_id: read_u32(bytes, payload)?,
3051 request_id: read_u32(bytes, payload + 4)?,
3052 base_version: read_u32(bytes, payload + 8)?,
3053 start_line: read_u32(bytes, payload + 12)?,
3054 start_column: read_u32(bytes, payload + 16)?,
3055 end_line: read_u32(bytes, payload + 20)?,
3056 end_column: read_u32(bytes, payload + 24)?,
3057 text: read_string(bytes, payload + 28, records_len)?,
3058 },
3059 TAG_POST_NOTIFICATION if len == 56 => {
3060 let raw_importance = read_u16(bytes, payload + 48)?;
3061 let raw_presentation = read_u16(bytes, payload + 50)?;
3062 Mutation::PostNotification {
3063 key: read_string(bytes, payload, records_len)?,
3064 title: read_string(bytes, payload + 8, records_len)?,
3065 body: read_string(bytes, payload + 16, records_len)?,
3066 channel: read_string(bytes, payload + 24, records_len)?,
3067 action_1: read_string(bytes, payload + 32, records_len)?,
3068 action_2: read_string(bytes, payload + 40, records_len)?,
3069 importance: NotificationImportance::try_from(raw_importance)
3070 .map_err(|()| ProtocolError::InvalidNotification(raw_importance))?,
3071 presentation: NotificationPresentation::try_from(raw_presentation)
3072 .map_err(|()| ProtocolError::InvalidNotification(raw_presentation))?,
3073 }
3074 }
3075 TAG_WITHDRAW_NOTIFICATION if len == 12 => Mutation::WithdrawNotification {
3076 key: read_string(bytes, payload, records_len)?,
3077 },
3078 TAG_REQUEST_NOTIFICATION_PERMISSION if len == 4 => {
3079 Mutation::RequestNotificationPermission
3080 }
3081 TAG_START_ANIMATION if len >= ANIMATION_HEADER_LEN => {
3082 Mutation::StartAnimation(
3085 decode_animation(&bytes[position..position + len]).map_err(|error| {
3086 if error == ProtocolError::Truncated {
3087 ProtocolError::InvalidAnimation
3088 } else {
3089 error
3090 }
3091 })?,
3092 )
3093 }
3094 TAG_CONTROL_ANIMATION if len == 24 => {
3095 let raw_property = u16::from(read_u8(bytes, payload + 8)?);
3096 let raw_op = u16::from(read_u8(bytes, payload + 10)?);
3097 Mutation::ControlAnimation {
3098 node_id: read_u32(bytes, payload)?,
3099 animation_id: read_u32(bytes, payload + 4)?,
3100 property: crate::schema::AnimatedProperty::try_from(raw_property)
3101 .map_err(|()| ProtocolError::InvalidAnimation)?,
3102 slot: read_u8(bytes, payload + 9)?,
3103 op: crate::schema::AnimationControl::try_from(raw_op)
3104 .map_err(|()| ProtocolError::InvalidAnimation)?,
3105 at_time_nanos: read_u64(bytes, payload + 12)?,
3106 }
3107 }
3108 TAG_SET_SELECTION if len == 28 => Mutation::SetSelection {
3109 node_id: read_u32(bytes, payload)?,
3110 base_version: read_u32(bytes, payload + 4)?,
3111 anchor_line: read_u32(bytes, payload + 8)?,
3112 anchor_column: read_u32(bytes, payload + 12)?,
3113 active_line: read_u32(bytes, payload + 16)?,
3114 active_column: read_u32(bytes, payload + 20)?,
3115 },
3116 TAG_REVEAL_RANGE if len == 32 => {
3117 let raw_align = read_u32(bytes, payload + 24)?;
3118 Mutation::RevealRange {
3119 node_id: read_u32(bytes, payload)?,
3120 base_version: read_u32(bytes, payload + 4)?,
3121 start_line: read_u32(bytes, payload + 8)?,
3122 start_column: read_u32(bytes, payload + 12)?,
3123 end_line: read_u32(bytes, payload + 16)?,
3124 end_column: read_u32(bytes, payload + 20)?,
3125 align: u16::try_from(raw_align)
3126 .ok()
3127 .and_then(|tag| crate::schema::RevealAlign::try_from(tag).ok())
3128 .ok_or(ProtocolError::InvalidValueKind(
3129 raw_align.min(u32::from(u16::MAX)) as u16,
3130 ))?,
3131 }
3132 }
3133 TAG_SET_FOLDED if len == 16 => Mutation::SetFolded {
3134 node_id: read_u32(bytes, payload)?,
3135 start_line: read_u32(bytes, payload + 4)?,
3136 collapsed: read_flag(bytes, payload + 8)?,
3137 },
3138 TAG_SET_SCROLL_OFFSET if len == 16 => Mutation::SetScrollOffset {
3139 node_id: read_u32(bytes, payload)?,
3140 offset_px: f64::from_bits(read_u64(bytes, payload + 4)?),
3141 },
3142 TAG_SET_SCROLL_OFFSET => return Err(ProtocolError::InvalidRecordLength),
3143 TAG_QUERY_POSITION_RECT if len == QUERY_POSITION_RECT_LEN => {
3144 Mutation::QueryPositionRect {
3145 node_id: read_u32(bytes, payload)?,
3146 request: read_u32(bytes, payload + 4)?,
3147 line: read_u32(bytes, payload + 8)?,
3148 column: read_u32(bytes, payload + 12)?,
3149 }
3150 }
3151 TAG_QUERY_POSITION_RECT => return Err(ProtocolError::InvalidRecordLength),
3152 TAG_INSERT_SNIPPET if len == INSERT_SNIPPET_LEN => Mutation::InsertSnippet {
3153 node_id: read_u32(bytes, payload)?,
3154 base_version: read_u32(bytes, payload + 4)?,
3155 start_line: read_u32(bytes, payload + 8)?,
3156 start_column: read_u32(bytes, payload + 12)?,
3157 end_line: read_u32(bytes, payload + 16)?,
3158 end_column: read_u32(bytes, payload + 20)?,
3159 snippet: read_string(bytes, payload + 24, records_len)?,
3160 },
3161 TAG_INSERT_SNIPPET => return Err(ProtocolError::InvalidRecordLength),
3162 TAG_SET_SELECTIONS if len == SET_SELECTIONS_LEN => Mutation::SetSelections {
3163 node_id: read_u32(bytes, payload)?,
3164 base_version: read_u32(bytes, payload + 4)?,
3165 selections: read_bytes(bytes, payload + 8, records_len)?,
3166 },
3167 TAG_SET_SELECTIONS => return Err(ProtocolError::InvalidRecordLength),
3168 TAG_EDIT_CODE_GROUP if len == EDIT_CODE_GROUP_LEN => Mutation::EditCodeGroup {
3169 node_id: read_u32(bytes, payload)?,
3170 request_id: read_u32(bytes, payload + 4)?,
3171 base_version: read_u32(bytes, payload + 8)?,
3172 edits: read_bytes(bytes, payload + 12, records_len)?,
3173 },
3174 TAG_CREATE..=TAG_EDIT_CODE
3175 | TAG_START_ANIMATION
3176 | TAG_CONTROL_ANIMATION
3177 | TAG_SET_SELECTION..=TAG_SET_FOLDED
3178 | TAG_SET_WINDOW_THEME..=TAG_SET_CLIPBOARD_TEXT
3179 | TAG_READ_CLIPBOARD
3180 | TAG_STORAGE_CALL
3181 | TAG_FILE_CALL
3182 | TAG_RELEASE_FILE_HANDLE
3183 | TAG_SCROLL_TO
3184 | TAG_SCROLL_INTO_VIEW => {
3185 return Err(ProtocolError::InvalidRecordLength);
3186 }
3187 other => return Err(ProtocolError::InvalidTag(other)),
3188 };
3189 output.push(mutation);
3190 position += len;
3191 }
3192 if output.len() != count {
3193 return Err(ProtocolError::InvalidEnvelope);
3194 }
3195 Ok(output)
3196}
3197
3198fn decode_animation(record: &[u8]) -> Result<crate::schema::Animation<'static>, ProtocolError> {
3202 use crate::schema::{
3203 AnimatedProperty, Animation, AnimationEvents, ColorInterpolation, FillMode, Keyframe,
3204 KeyframeValue, PlayState, PlaybackDirection, StepPosition, Timing, TransformFunction,
3205 TransformFunctionKind,
3206 };
3207 let bad = ProtocolError::InvalidAnimation;
3208 fn byte_enum<T: TryFrom<u16, Error = ()>>(
3209 record: &[u8],
3210 at: usize,
3211 ) -> Result<T, ProtocolError> {
3212 T::try_from(u16::from(read_u8(record, at)?)).map_err(|()| ProtocolError::InvalidAnimation)
3213 }
3214 let float = |at: usize| read_u32(record, at).map(f32::from_bits);
3215 let property: AnimatedProperty = byte_enum(record, 12)?;
3216 let direction: PlaybackDirection = byte_enum(record, 14)?;
3217 let fill: FillMode = byte_enum(record, 15)?;
3218 let play_state: PlayState = byte_enum(record, 16)?;
3219 let colour = matches!(
3220 property,
3221 AnimatedProperty::Color | AnimatedProperty::Background | AnimatedProperty::BorderColor
3222 );
3223 let interpolation = match (colour, read_u8(record, 17)?) {
3224 (false, 0) => None,
3225 (true, raw) => Some(
3226 ColorInterpolation::try_from(u16::from(raw))
3227 .map_err(|()| ProtocolError::InvalidAnimation)?,
3228 ),
3229 (false, _) => return Err(bad),
3230 };
3231 let keyframe_count = usize::from(read_u16(record, 18)?);
3232 let delay_ms = float(28)?;
3233 let duration_ms = float(32)?;
3234 let iterations = float(36)?;
3235 let origin_x = float(40)?;
3236 let origin_y = float(44)?;
3237 let events = read_u8(record, 48)?;
3238 if keyframe_count < 2
3239 || !delay_ms.is_finite()
3240 || !(duration_ms.is_finite() && duration_ms >= 0.0)
3241 || iterations.is_nan()
3242 || iterations < 0.0
3243 || !(origin_x.is_finite() && origin_y.is_finite())
3244 || events & !AnimationEvents::ALL.0 != 0
3245 {
3246 return Err(bad);
3247 }
3248 let mut keyframes = Vec::with_capacity(keyframe_count);
3249 let mut at = ANIMATION_HEADER_LEN;
3250 for index in 0..keyframe_count {
3251 let offset = float(at)?;
3252 let raw_timing = read_u8(record, at + 4)?;
3253 let raw_position = read_u8(record, at + 5)?;
3254 let flags = read_u16(record, at + 6)?;
3255 let timing = match raw_timing {
3256 1 => Timing::Linear,
3257 2 => {
3258 let (x1, y1, x2, y2) = (
3259 float(at + 8)?,
3260 float(at + 12)?,
3261 float(at + 16)?,
3262 float(at + 20)?,
3263 );
3264 if ![x1, y1, x2, y2].iter().all(|value| value.is_finite())
3265 || !(0.0..=1.0).contains(&x1)
3266 || !(0.0..=1.0).contains(&x2)
3267 {
3268 return Err(bad);
3269 }
3270 Timing::CubicBezier { x1, y1, x2, y2 }
3271 }
3272 3 => {
3273 let count = read_u32(record, at + 8)?;
3274 let position =
3275 StepPosition::try_from(u16::from(raw_position)).map_err(|()| bad.clone())?;
3276 let least = if position == StepPosition::JumpNone {
3277 2
3278 } else {
3279 1
3280 };
3281 if count < least {
3282 return Err(bad);
3283 }
3284 Timing::Steps { count, position }
3285 }
3286 _ => return Err(bad),
3287 };
3288 if flags & !KEYFRAME_FROM_PRESENTED != 0
3289 || (index > 0 && flags & KEYFRAME_FROM_PRESENTED != 0)
3290 || !(0.0..=1.0).contains(&offset)
3291 {
3292 return Err(bad);
3293 }
3294 let value_at = at + KEYFRAME_HEAD_LEN;
3295 let (value, value_len) = match property {
3296 AnimatedProperty::Alpha => {
3297 let value = float(value_at)?;
3298 if !value.is_finite() {
3299 return Err(bad);
3300 }
3301 (KeyframeValue::Alpha(value), 4)
3302 }
3303 AnimatedProperty::Color
3304 | AnimatedProperty::Background
3305 | AnimatedProperty::BorderColor => {
3306 let paint = Paint::from_bits(read_u64(record, value_at)?)
3308 .filter(|paint| !matches!(paint, Paint::Asset(_)))
3309 .ok_or(bad.clone())?;
3310 (KeyframeValue::Paint(paint), 8)
3311 }
3312 AnimatedProperty::Transform => {
3313 let count = usize::from(read_u16(record, value_at)?);
3314 let mut functions = Vec::with_capacity(count);
3315 for function in 0..count {
3316 let function_at = value_at + 4 + TRANSFORM_FUNCTION_LEN * function;
3317 let raw_kind = read_u32(record, function_at)?;
3318 let kind = u16::try_from(raw_kind)
3319 .ok()
3320 .and_then(|kind| TransformFunctionKind::try_from(kind).ok())
3321 .ok_or(bad.clone())?;
3322 let mut values = [0.0_f32; 6];
3323 for (slot, value) in values.iter_mut().enumerate() {
3324 *value = float(function_at + 4 + 4 * slot)?;
3325 }
3326 if !values.iter().all(|value| value.is_finite()) {
3327 return Err(bad);
3328 }
3329 functions.push(TransformFunction { kind, values });
3330 }
3331 (
3332 KeyframeValue::Transform(std::borrow::Cow::Owned(functions)),
3333 4 + TRANSFORM_FUNCTION_LEN * count,
3334 )
3335 }
3336 };
3337 keyframes.push(Keyframe {
3338 offset,
3339 timing,
3340 from_presented: flags & KEYFRAME_FROM_PRESENTED != 0,
3341 value,
3342 });
3343 at = value_at + value_len;
3344 }
3345 if at != record.len() {
3346 return Err(bad);
3347 }
3348 let first = keyframes.first().map(|keyframe| keyframe.offset);
3352 let last = keyframes.last().map(|keyframe| keyframe.offset);
3353 if first != Some(0.0)
3354 || last != Some(1.0)
3355 || keyframes
3356 .windows(2)
3357 .any(|pair| pair[1].offset < pair[0].offset)
3358 {
3359 return Err(bad);
3360 }
3361 if let Some(KeyframeValue::Transform(shape)) = keyframes.first().map(|keyframe| &keyframe.value)
3362 {
3363 let kinds: Vec<_> = shape.iter().map(|function| function.kind).collect();
3364 let lined_up = keyframes.iter().all(|keyframe| match &keyframe.value {
3365 KeyframeValue::Transform(functions) => functions
3366 .iter()
3367 .map(|function| function.kind)
3368 .eq(kinds.iter().copied()),
3369 _ => false,
3370 });
3371 if !lined_up {
3372 return Err(bad);
3373 }
3374 }
3375 Ok(Animation {
3376 node_id: read_u32(record, 4)?,
3377 animation_id: read_u32(record, 8)?,
3378 property,
3379 slot: read_u8(record, 13)?,
3380 direction,
3381 fill,
3382 play_state,
3383 interpolation,
3384 start_time_nanos: read_u64(record, 20)?,
3385 delay_ms,
3386 duration_ms,
3387 iterations,
3388 origin_x,
3389 origin_y,
3390 events: AnimationEvents(events),
3391 keyframes: std::borrow::Cow::Owned(keyframes),
3392 })
3393}
3394
3395const fn pack_floats(low: f32, high: f32) -> u64 {
3397 (low.to_bits() as u64) | ((high.to_bits() as u64) << 32)
3398}
3399
3400const fn unpack_low(word: u64) -> f32 {
3401 f32::from_bits(word as u32)
3402}
3403
3404const fn unpack_high(word: u64) -> f32 {
3405 f32::from_bits((word >> 32) as u32)
3406}
3407
3408const fn two_words(first: u64, second: u64) -> [u64; MAX_MODIFIER_WORDS] {
3411 [first, second, 0, 0, 0, 0, 0, 0, 0, 0]
3412}
3413
3414fn float_word(value: f32) -> u64 {
3415 u64::from(value.to_bits())
3416}
3417
3418fn modifier_fields(modifier: &Modifier) -> (u16, [u64; MAX_MODIFIER_WORDS]) {
3421 match modifier {
3422 Modifier::Empty => (0, two_words(0, 0)),
3423 Modifier::Padding(value) => (1, two_words(float_word(*value), 0)),
3424 Modifier::FillMaxWidth => (2, two_words(0, 0)),
3425 Modifier::MatchParentSize => (31, two_words(0, 0)),
3426 Modifier::FillMaxHeight => (3, two_words(0, 0)),
3427 Modifier::Width(value) => (4, two_words(float_word(*value), 0)),
3428 Modifier::Height(value) => (5, two_words(float_word(*value), 0)),
3429 Modifier::Size { width, height } => (6, two_words(float_word(*width), float_word(*height))),
3430 Modifier::Background(paint) => (7, two_words(paint.to_bits(), 0)),
3431 Modifier::Clickable { handler_id } => (8, two_words(*handler_id, 0)),
3432 Modifier::PaddingRole(role) => (9, two_words(*role as u64, 0)),
3433 Modifier::PaddingEach {
3434 start,
3435 top,
3436 end,
3437 bottom,
3438 } => (
3439 10,
3440 two_words(pack_floats(*start, *top), pack_floats(*end, *bottom)),
3441 ),
3442 Modifier::Weight(value) => (11, two_words(float_word(*value), 0)),
3443 Modifier::Shape {
3444 top_start,
3445 top_end,
3446 bottom_end,
3447 bottom_start,
3448 } => (
3449 12,
3450 two_words(
3451 pack_floats(*top_start, *top_end),
3452 pack_floats(*bottom_end, *bottom_start),
3453 ),
3454 ),
3455 Modifier::ShapeRole(role) => (13, two_words(*role as u64, 0)),
3456 Modifier::Border { width, paint } => (14, two_words(float_word(*width), paint.to_bits())),
3457 Modifier::Elevation(dp) => (15, two_words(float_word(*dp), 0)),
3458 Modifier::ObserveSize { token, every_dp } => {
3459 (16, two_words(u64::from(*token), u64::from(*every_dp)))
3460 }
3461 Modifier::Motion(role) => (17, two_words(*role as u64, 0)),
3462 Modifier::Material(role) => (18, two_words(*role as u64, 0)),
3463 Modifier::Offset { x, y } => (19, two_words(float_word(*x), float_word(*y))),
3464 Modifier::RequiredSize { width, height } => {
3465 (20, two_words(float_word(*width), float_word(*height)))
3466 }
3467 Modifier::BorderEach {
3468 top,
3469 right,
3470 bottom,
3471 left,
3472 top_paint,
3473 right_paint,
3474 bottom_paint,
3475 left_paint,
3476 } => (
3477 21,
3478 [
3479 pack_floats(*top, *right),
3480 pack_floats(*bottom, *left),
3481 top_paint.to_bits(),
3482 right_paint.to_bits(),
3483 bottom_paint.to_bits(),
3484 left_paint.to_bits(),
3485 0,
3486 0,
3487 0,
3488 0,
3489 ],
3490 ),
3491 Modifier::CornerEach {
3492 top_left,
3493 top_right,
3494 bottom_right,
3495 bottom_left,
3496 } => (
3497 22,
3498 two_words(
3499 pack_floats(*top_left, *top_right),
3500 pack_floats(*bottom_right, *bottom_left),
3501 ),
3502 ),
3503 Modifier::Shadow {
3504 x,
3505 y,
3506 blur,
3507 spread,
3508 paint,
3509 } => (
3510 23,
3511 [
3512 pack_floats(*x, *y),
3513 pack_floats(*blur, *spread),
3514 paint.to_bits(),
3515 0,
3516 0,
3517 0,
3518 0,
3519 0,
3520 0,
3521 0,
3522 ],
3523 ),
3524 Modifier::WindowDrag(enabled) => (32, two_words(u64::from(*enabled), 0)),
3525 Modifier::Clip(enabled) => (24, two_words(u64::from(*enabled), 0)),
3526 Modifier::Alpha(value) => (25, two_words(float_word(*value), 0)),
3527 Modifier::PressFeedback { scale, tint_alpha } => {
3528 (33, two_words(pack_floats(*scale, *tint_alpha), 0))
3529 }
3530 Modifier::Semantics { role, state } => (34, two_words(*role as u64, u64::from(*state))),
3531 Modifier::CornerCurve { smoothing } => (35, two_words(float_word(*smoothing), 0)),
3532 Modifier::BackgroundBrush {
3533 x,
3534 y,
3535 width,
3536 height,
3537 paint,
3538 repeat,
3539 tile_width,
3540 tile_height,
3541 } => (
3542 28,
3543 [
3544 pack_floats(*x, *y),
3545 pack_floats(*width, *height),
3546 paint.to_bits(),
3547 u64::from(u16::from(*repeat)),
3548 pack_floats(*tile_width, *tile_height),
3549 0,
3550 0,
3551 0,
3552 0,
3553 0,
3554 ],
3555 ),
3556 Modifier::Transform {
3557 a,
3558 b,
3559 c,
3560 d,
3561 e,
3562 f,
3563 origin_x,
3564 origin_y,
3565 } => (
3566 26,
3567 [
3568 pack_floats(*a, *b),
3569 pack_floats(*c, *d),
3570 pack_floats(*e, *f),
3571 pack_floats(*origin_x, *origin_y),
3572 0,
3573 0,
3574 0,
3575 0,
3576 0,
3577 0,
3578 ],
3579 ),
3580 Modifier::Transform3D {
3581 m,
3582 origin_x,
3583 origin_y,
3584 backface_hidden,
3585 } => (
3586 27,
3587 [
3588 pack_floats(m[0], m[1]),
3589 pack_floats(m[2], m[3]),
3590 pack_floats(m[4], m[5]),
3591 pack_floats(m[6], m[7]),
3592 pack_floats(m[8], m[9]),
3593 pack_floats(m[10], m[11]),
3594 pack_floats(m[12], m[13]),
3595 pack_floats(m[14], m[15]),
3596 pack_floats(*origin_x, *origin_y),
3597 u64::from(*backface_hidden),
3598 ],
3599 ),
3600 }
3601}
3602
3603fn decode_role<T: TryFrom<u16, Error = ()>>(first: u64) -> Result<T, ProtocolError> {
3604 let tag = u16::try_from(first).map_err(|_| ProtocolError::InvalidModifier(0))?;
3605 T::try_from(tag).map_err(|()| ProtocolError::InvalidModifier(tag))
3606}
3607
3608fn decode_paint(bits: u64) -> Result<Paint, ProtocolError> {
3609 Paint::from_bits(bits).ok_or(ProtocolError::InvalidModifier(0))
3610}
3611
3612fn decode_modifier(tag: u16, words: &[u64; MAX_MODIFIER_WORDS]) -> Result<Modifier, ProtocolError> {
3613 let [
3614 first,
3615 second,
3616 third,
3617 fourth,
3618 fifth,
3619 sixth,
3620 seventh,
3621 eighth,
3622 ninth,
3623 tenth,
3624 ] = *words;
3625 match tag {
3626 0 => Ok(Modifier::Empty),
3627 1 => Ok(Modifier::Padding(f32::from_bits(first as u32))),
3628 2 => Ok(Modifier::FillMaxWidth),
3629 3 => Ok(Modifier::FillMaxHeight),
3630 4 => Ok(Modifier::Width(f32::from_bits(first as u32))),
3631 5 => Ok(Modifier::Height(f32::from_bits(first as u32))),
3632 6 => Ok(Modifier::Size {
3633 width: f32::from_bits(first as u32),
3634 height: f32::from_bits(second as u32),
3635 }),
3636 7 => Ok(Modifier::Background(decode_paint(first)?)),
3637 8 => Ok(Modifier::Clickable { handler_id: first }),
3638 9 => Ok(Modifier::PaddingRole(decode_role::<SpaceRole>(first)?)),
3639 10 => Ok(Modifier::PaddingEach {
3640 start: unpack_low(first),
3641 top: unpack_high(first),
3642 end: unpack_low(second),
3643 bottom: unpack_high(second),
3644 }),
3645 11 => Ok(Modifier::Weight(f32::from_bits(first as u32))),
3646 12 => Ok(Modifier::Shape {
3647 top_start: unpack_low(first),
3648 top_end: unpack_high(first),
3649 bottom_end: unpack_low(second),
3650 bottom_start: unpack_high(second),
3651 }),
3652 13 => Ok(Modifier::ShapeRole(decode_role::<ShapeRole>(first)?)),
3653 14 => Ok(Modifier::Border {
3654 width: f32::from_bits(first as u32),
3655 paint: decode_paint(second)?,
3656 }),
3657 15 => Ok(Modifier::Elevation(f32::from_bits(first as u32))),
3658 16 => Ok(Modifier::ObserveSize {
3659 token: first as u32,
3660 every_dp: match second {
3661 0 => false,
3662 1 => true,
3663 _ => return Err(ProtocolError::InvalidModifier(16)),
3664 },
3665 }),
3666 17 => Ok(Modifier::Motion(decode_role::<MotionRole>(first)?)),
3667 18 => Ok(Modifier::Material(decode_role::<MaterialRole>(first)?)),
3668 19 => Ok(Modifier::Offset {
3669 x: f32::from_bits(first as u32),
3670 y: f32::from_bits(second as u32),
3671 }),
3672 20 => Ok(Modifier::RequiredSize {
3673 width: f32::from_bits(first as u32),
3674 height: f32::from_bits(second as u32),
3675 }),
3676 21 => Ok(Modifier::BorderEach {
3677 top: unpack_low(first),
3678 right: unpack_high(first),
3679 bottom: unpack_low(second),
3680 left: unpack_high(second),
3681 top_paint: decode_paint(third)?,
3682 right_paint: decode_paint(fourth)?,
3683 bottom_paint: decode_paint(fifth)?,
3684 left_paint: decode_paint(sixth)?,
3685 }),
3686 22 => Ok(Modifier::CornerEach {
3687 top_left: unpack_low(first),
3688 top_right: unpack_high(first),
3689 bottom_right: unpack_low(second),
3690 bottom_left: unpack_high(second),
3691 }),
3692 23 => Ok(Modifier::Shadow {
3693 x: unpack_low(first),
3694 y: unpack_high(first),
3695 blur: unpack_low(second),
3696 spread: unpack_high(second),
3697 paint: decode_paint(third)?,
3698 }),
3699 24 => match first {
3702 0 => Ok(Modifier::Clip(false)),
3703 1 => Ok(Modifier::Clip(true)),
3704 _ => Err(ProtocolError::InvalidModifier(24)),
3705 },
3706 32 => match first {
3707 0 => Ok(Modifier::WindowDrag(false)),
3708 1 => Ok(Modifier::WindowDrag(true)),
3709 _ => Err(ProtocolError::InvalidModifier(32)),
3710 },
3711 25 => Ok(Modifier::Alpha(f32::from_bits(first as u32))),
3712 26 => {
3713 let values = [
3714 unpack_low(first),
3715 unpack_high(first),
3716 unpack_low(second),
3717 unpack_high(second),
3718 unpack_low(third),
3719 unpack_high(third),
3720 unpack_low(fourth),
3721 unpack_high(fourth),
3722 ];
3723 if values.iter().any(|value| !value.is_finite()) {
3727 return Err(ProtocolError::InvalidModifier(26));
3728 }
3729 let [a, b, c, d, e, f, origin_x, origin_y] = values;
3730 Ok(Modifier::Transform {
3731 a,
3732 b,
3733 c,
3734 d,
3735 e,
3736 f,
3737 origin_x,
3738 origin_y,
3739 })
3740 }
3741 27 => {
3742 let values = [
3743 unpack_low(first),
3744 unpack_high(first),
3745 unpack_low(second),
3746 unpack_high(second),
3747 unpack_low(third),
3748 unpack_high(third),
3749 unpack_low(fourth),
3750 unpack_high(fourth),
3751 unpack_low(fifth),
3752 unpack_high(fifth),
3753 unpack_low(sixth),
3754 unpack_high(sixth),
3755 unpack_low(seventh),
3756 unpack_high(seventh),
3757 unpack_low(eighth),
3758 unpack_high(eighth),
3759 unpack_low(ninth),
3760 unpack_high(ninth),
3761 ];
3762 if values.iter().any(|value| !value.is_finite()) {
3765 return Err(ProtocolError::InvalidModifier(27));
3766 }
3767 let backface_hidden = match tenth {
3768 0 => false,
3769 1 => true,
3770 _ => return Err(ProtocolError::InvalidModifier(27)),
3771 };
3772 let mut m = [0.0; 16];
3773 m.copy_from_slice(&values[..16]);
3774 Ok(Modifier::Transform3D {
3775 m,
3776 origin_x: values[16],
3777 origin_y: values[17],
3778 backface_hidden,
3779 })
3780 }
3781 31 => Ok(Modifier::MatchParentSize),
3782 33 => Ok(Modifier::PressFeedback {
3783 scale: unpack_low(first),
3784 tint_alpha: unpack_high(first),
3785 }),
3786 34 => {
3787 let state = u32::try_from(second)
3788 .ok()
3789 .filter(|state| state & !SEMANTICS_STATE_MASK == 0)
3790 .ok_or(ProtocolError::InvalidModifier(34))?;
3791 Ok(Modifier::Semantics {
3792 role: decode_role::<SemanticsRole>(first)?,
3793 state,
3794 })
3795 }
3796 28 => {
3797 let values = [
3798 unpack_low(first),
3799 unpack_high(first),
3800 unpack_low(second),
3801 unpack_high(second),
3802 unpack_low(fifth),
3803 unpack_high(fifth),
3804 ];
3805 if values.iter().any(|value| !value.is_finite()) || values[4] <= 0.0 || values[5] <= 0.0
3809 {
3810 return Err(ProtocolError::InvalidModifier(28));
3811 }
3812 let [x, y, width, height, tile_width, tile_height] = values;
3813 Ok(Modifier::BackgroundBrush {
3814 x,
3815 y,
3816 width,
3817 height,
3818 paint: decode_paint(third)?,
3819 repeat: decode_role::<BackgroundRepeat>(fourth)?,
3820 tile_width,
3821 tile_height,
3822 })
3823 }
3824 35 => Ok(Modifier::CornerCurve {
3827 smoothing: unpack_low(first),
3828 }),
3829 other => Err(ProtocolError::InvalidModifier(other)),
3830 }
3831}
3832
3833fn read_string(bytes: &[u8], position: usize, arena_start: usize) -> Result<&str, ProtocolError> {
3840 let value = read_bytes(bytes, position, arena_start)?;
3841 std::str::from_utf8(value).map_err(|_| ProtocolError::InvalidUtf8)
3842}
3843
3844fn read_bytes(bytes: &[u8], position: usize, arena_start: usize) -> Result<&[u8], ProtocolError> {
3846 let offset =
3847 usize::try_from(read_u32(bytes, position)?).map_err(|_| ProtocolError::LengthOverflow)?;
3848 let len = usize::try_from(read_u32(bytes, position + 4)?)
3849 .map_err(|_| ProtocolError::LengthOverflow)?;
3850 let end = offset
3851 .checked_add(len)
3852 .ok_or(ProtocolError::InvalidStringRange)?;
3853 if offset < arena_start {
3854 return Err(ProtocolError::InvalidStringRange);
3855 }
3856 bytes
3857 .get(offset..end)
3858 .ok_or(ProtocolError::InvalidStringRange)
3859}
3860
3861fn read_u8(bytes: &[u8], position: usize) -> Result<u8, ProtocolError> {
3862 bytes.get(position).copied().ok_or(ProtocolError::Truncated)
3863}
3864
3865fn read_u16(bytes: &[u8], position: usize) -> Result<u16, ProtocolError> {
3866 let value = bytes
3867 .get(position..position + 2)
3868 .ok_or(ProtocolError::Truncated)?;
3869 Ok(u16::from_le_bytes([value[0], value[1]]))
3870}
3871
3872fn read_u32(bytes: &[u8], position: usize) -> Result<u32, ProtocolError> {
3873 let value = bytes
3874 .get(position..position + 4)
3875 .ok_or(ProtocolError::Truncated)?;
3876 Ok(u32::from_le_bytes([value[0], value[1], value[2], value[3]]))
3877}
3878
3879fn read_flag(bytes: &[u8], position: usize) -> Result<bool, ProtocolError> {
3881 match read_u32(bytes, position)? {
3882 0 => Ok(false),
3883 1 => Ok(true),
3884 other => Err(ProtocolError::InvalidValueKind(
3885 other.min(u32::from(u16::MAX)) as u16,
3886 )),
3887 }
3888}
3889
3890fn read_u64(bytes: &[u8], position: usize) -> Result<u64, ProtocolError> {
3891 let value = bytes
3892 .get(position..position + 8)
3893 .ok_or(ProtocolError::Truncated)?;
3894 Ok(u64::from_le_bytes(
3895 value.try_into().map_err(|_| ProtocolError::Truncated)?,
3896 ))
3897}
3898
3899#[cfg(test)]
3900mod tests {
3901 use super::*;
3902 use crate::schema::TypeRole;
3903
3904 #[test]
3905 fn round_trips_all_m0_mutations() {
3906 let mutations = [
3907 Mutation::Create {
3908 node_id: 1,
3909 widget: WidgetKind::Column,
3910 },
3911 Mutation::SetProp {
3912 node_id: 2,
3913 property: PropertyKind::Text,
3914 value: PropertyValue::String("안녕"),
3915 },
3916 Mutation::SetProp {
3917 node_id: 3,
3918 property: PropertyKind::Enabled,
3919 value: PropertyValue::Bool(true),
3920 },
3921 Mutation::SetModifier {
3922 node_id: 1,
3923 index: 0,
3924 modifier: Modifier::Padding(16.0),
3925 },
3926 Mutation::Insert {
3927 parent_id: 1,
3928 node_id: 2,
3929 index: 0,
3930 },
3931 Mutation::Move {
3932 parent_id: 1,
3933 node_id: 2,
3934 index: 1,
3935 },
3936 Mutation::Remove { node_id: 3 },
3937 Mutation::SetText {
3938 node_id: 4,
3939 text: "compose",
3940 selection: Some(Selection { start: 1, end: 4 }),
3941 },
3942 Mutation::AppendText {
3943 node_id: 4,
3944 text: " token",
3945 },
3946 ];
3947 let mut encoder = BatchEncoder::default();
3948 for mutation in &mutations {
3949 encoder.encode(mutation).unwrap();
3950 }
3951 let decoded = decode_batch(encoder.finish().unwrap()).unwrap();
3952 assert_eq!(decoded, mutations);
3953 }
3954
3955 #[test]
3958 fn fr13_paint_role_and_literal_share_one_record_length() {
3959 let encode_one = |modifier| {
3960 let mut encoder = BatchEncoder::default();
3961 encoder
3962 .encode(&Mutation::SetModifier {
3963 node_id: 1,
3964 index: 0,
3965 modifier,
3966 })
3967 .unwrap();
3968 encoder.finish().unwrap().to_vec()
3969 };
3970 let role = encode_one(Modifier::Background(Paint::Role(
3971 crate::schema::ColorRole::Surface,
3972 )));
3973 let literal = encode_one(Modifier::Background(Paint::Literal(
3974 crate::schema::Color::rgb(0x1b1b1f),
3975 )));
3976 assert_eq!(role.len(), literal.len());
3977 assert_eq!(
3978 decode_batch(&role).unwrap(),
3979 [Mutation::SetModifier {
3980 node_id: 1,
3981 index: 0,
3982 modifier: Modifier::Background(Paint::Role(crate::schema::ColorRole::Surface)),
3983 }]
3984 );
3985 assert_eq!(
3986 decode_batch(&literal).unwrap(),
3987 [Mutation::SetModifier {
3988 node_id: 1,
3989 index: 0,
3990 modifier: Modifier::Background(Paint::Literal(crate::schema::Color::rgb(0x1b1b1f))),
3991 }]
3992 );
3993 }
3994
3995 #[test]
3996 fn fr68_semantics_round_trips_and_refuses_unknown_state_bits() {
3997 let expected = Modifier::Semantics {
3998 role: SemanticsRole::Switch,
3999 state: (2 << crate::schema::SEMANTICS_CHECKED_SHIFT)
4000 | (1 << crate::schema::SEMANTICS_SELECTED_SHIFT),
4001 };
4002 let mutation = Mutation::SetModifier {
4003 node_id: 1,
4004 index: 0,
4005 modifier: expected.clone(),
4006 };
4007 let mut encoder = BatchEncoder::default();
4008 encoder.encode(&mutation).unwrap();
4009 assert_eq!(decode_batch(encoder.finish().unwrap()).unwrap(), [mutation]);
4010
4011 let words = [SemanticsRole::Button as u64, 0x2000, 0, 0, 0, 0, 0, 0, 0, 0];
4012 assert_eq!(
4013 decode_modifier(34, &words),
4014 Err(ProtocolError::InvalidModifier(34))
4015 );
4016 }
4017
4018 #[test]
4019 fn fr71_every_new_role_round_trips_and_is_exposed() {
4020 use crate::schema::*;
4021 let roles = [
4022 (SemanticsRole::TabList, 9),
4023 (SemanticsRole::Tree, 10),
4024 (SemanticsRole::TreeItem, 11),
4025 (SemanticsRole::Menu, 12),
4026 (SemanticsRole::MenuItem, 13),
4027 (SemanticsRole::TextBox, 14),
4028 (SemanticsRole::Heading, 15),
4029 (SemanticsRole::Navigation, 16),
4030 (SemanticsRole::List, 17),
4031 (SemanticsRole::ListItem, 18),
4032 (SemanticsRole::Group, 19),
4033 ];
4034 let state = (2 << SEMANTICS_EXPANDED_SHIFT)
4035 | (2 << SEMANTICS_DISABLED_SHIFT)
4036 | (1 << SEMANTICS_FOCUSED_SHIFT)
4037 | SEMANTICS_HIDDEN_BIT
4038 | SEMANTICS_CLICK_ACTION_BIT
4039 | SEMANTICS_FOCUS_ACTION_BIT;
4040 assert_eq!(state & !SEMANTICS_STATE_MASK, 0);
4041 for (role, value) in roles {
4042 assert_eq!(role as u64, value, "{role:?} changed its wire value");
4043 let mutation = Mutation::SetModifier {
4044 node_id: 1,
4045 index: 0,
4046 modifier: Modifier::Semantics { role, state },
4047 };
4048 let mut encoder = BatchEncoder::default();
4049 encoder.encode(&mutation).unwrap();
4050 assert_eq!(decode_batch(encoder.finish().unwrap()).unwrap(), [mutation]);
4051 }
4052 assert!(
4053 ROLE_ENUM_SCHEMA
4054 .iter()
4055 .any(|e| e.name == "SemanticsRole" && e.variants.len() == 19),
4056 "the schema must list all 19 roles"
4057 );
4058 }
4059
4060 #[test]
4061 fn nfr7_unknown_semantics_bit_is_a_protocol_error() {
4062 let words = [SemanticsRole::Group as u64, 1 << 13, 0, 0, 0, 0, 0, 0, 0, 0];
4064 assert_eq!(
4065 decode_modifier(34, &words),
4066 Err(ProtocolError::InvalidModifier(34))
4067 );
4068 let words = [SemanticsRole::Group as u64, 1 << 12, 0, 0, 0, 0, 0, 0, 0, 0];
4070 assert!(decode_modifier(34, &words).is_ok());
4071 }
4072
4073 #[test]
4074 fn fr51_press_feedback_round_trips() {
4075 let expected = [Mutation::SetModifier {
4076 node_id: 1,
4077 index: 0,
4078 modifier: Modifier::PressFeedback {
4079 scale: 0.975,
4080 tint_alpha: 0.07,
4081 },
4082 }];
4083 let mut encoder = BatchEncoder::default();
4084 encoder.encode(&expected[0]).unwrap();
4085 assert_eq!(decode_batch(encoder.finish().unwrap()).unwrap(), expected);
4086 }
4087
4088 #[test]
4089 fn fr50_corner_curve_round_trips() {
4090 for smoothing in [0.0, 0.6, 1.0, -2.5, 7.0] {
4091 let expected = [Mutation::SetModifier {
4092 node_id: 1,
4093 index: 0,
4094 modifier: Modifier::CornerCurve { smoothing },
4095 }];
4096 let mut encoder = BatchEncoder::default();
4097 encoder.encode(&expected[0]).unwrap();
4098 let bytes = encoder.finish().unwrap().to_vec();
4099 assert_eq!(u16::from_le_bytes([bytes[22], bytes[23]]), 35);
4100 assert_eq!(decode_batch(&bytes).unwrap(), expected);
4101 }
4102 }
4103
4104 #[test]
4106 fn fr13_design_modifiers_round_trip_in_two_words() {
4107 let modifiers = [
4108 Modifier::PaddingRole(SpaceRole::Md),
4109 Modifier::PaddingEach {
4110 start: 1.0,
4111 top: 2.0,
4112 end: 3.0,
4113 bottom: 4.0,
4114 },
4115 Modifier::Weight(0.25),
4116 Modifier::Shape {
4117 top_start: 4.0,
4118 top_end: 8.0,
4119 bottom_end: 12.0,
4120 bottom_start: 16.0,
4121 },
4122 Modifier::ShapeRole(ShapeRole::Full),
4123 Modifier::Border {
4124 width: 2.0,
4125 paint: Paint::Role(crate::schema::ColorRole::Outline),
4126 },
4127 Modifier::Elevation(6.0),
4128 ];
4129 let mut encoder = BatchEncoder::default();
4130 let expected: Vec<_> = modifiers
4131 .iter()
4132 .enumerate()
4133 .map(|(index, modifier)| Mutation::SetModifier {
4134 node_id: 1,
4135 index: index as u16,
4136 modifier: modifier.clone(),
4137 })
4138 .collect();
4139 for mutation in &expected {
4140 encoder.encode(mutation).unwrap();
4141 }
4142 let bytes = encoder.finish().unwrap();
4143 assert_eq!(bytes.len(), ENVELOPE_LEN + expected.len() * 28);
4145 assert_eq!(decode_batch(bytes).unwrap(), expected);
4146 }
4147
4148 #[test]
4152 fn fr42_shadow_a_list_of_shadows_round_trips_in_order() {
4153 let shadows = [
4154 Modifier::Shadow {
4155 x: 0.0,
4156 y: 1.0,
4157 blur: 2.0,
4158 spread: 0.0,
4159 paint: Paint::Literal(crate::schema::Color::argb(0x1400_0000)),
4160 },
4161 Modifier::Shadow {
4162 x: 0.0,
4163 y: 4.0,
4164 blur: 12.0,
4165 spread: -2.0,
4166 paint: Paint::Literal(crate::schema::Color::argb(0x2900_0000)),
4167 },
4168 Modifier::Shadow {
4169 x: -3.5,
4170 y: 24.0,
4171 blur: 48.0,
4172 spread: 4.0,
4173 paint: Paint::Role(crate::schema::ColorRole::Outline),
4174 },
4175 ];
4176 let expected: Vec<_> = shadows
4177 .iter()
4178 .enumerate()
4179 .map(|(index, modifier)| Mutation::SetModifier {
4180 node_id: 7,
4181 index: 9 + index as u16,
4182 modifier: modifier.clone(),
4183 })
4184 .collect();
4185 let mut encoder = BatchEncoder::default();
4186 for mutation in &expected {
4187 encoder.encode(mutation).unwrap();
4188 }
4189 let bytes = encoder.finish().unwrap();
4190 assert_eq!(decode_batch(bytes).unwrap(), expected);
4191 }
4192
4193 #[test]
4199 fn fr23_set_theme_round_trips_with_a_slot_for_every_type_role() {
4200 let theme = Theme::adaptive(DesignSystem::Cupertino)
4201 .with_color_scheme(ColorScheme::Dark)
4202 .with_font(TypeRole::Display, 7);
4203 let length = 20 + 4 * TYPE_ROLE_COUNT as u16;
4204 let mut encoder = BatchEncoder::default();
4205 encoder.encode(&Mutation::SetTheme(theme)).unwrap();
4206 let bytes = encoder.finish().unwrap();
4207 assert_eq!(bytes.len(), ENVELOPE_LEN + length as usize);
4208 assert_eq!(read_u16(bytes, ENVELOPE_LEN).unwrap(), TAG_SET_THEME);
4209 assert_eq!(read_u16(bytes, ENVELOPE_LEN + 2).unwrap(), length);
4210 assert_eq!(decode_batch(bytes).unwrap(), [Mutation::SetTheme(theme)]);
4211 }
4212
4213 #[test]
4215 fn fr23_a_font_is_named_for_one_role_and_no_others() {
4216 let theme = Theme::unified(DesignSystem::Material3).with_font(TypeRole::Display, 3);
4217 assert_eq!(theme.font(TypeRole::Display), Some(3));
4218 for role in [
4219 TypeRole::Body,
4220 TypeRole::Title,
4221 TypeRole::Mono,
4222 TypeRole::Caption,
4223 ] {
4224 assert_eq!(theme.font(role), None);
4225 }
4226 }
4227
4228 #[test]
4231 fn fr52_a_default_font_names_every_role_but_mono() {
4232 let theme = Theme::unified(DesignSystem::Material3).with_default_font(5);
4233 for role in [
4234 TypeRole::Display,
4235 TypeRole::Headline,
4236 TypeRole::Title,
4237 TypeRole::Subtitle,
4238 TypeRole::Body,
4239 TypeRole::BodyStrong,
4240 TypeRole::Label,
4241 TypeRole::Caption,
4242 ] {
4243 assert_eq!(theme.font(role), Some(5), "{role:?}");
4244 }
4245 assert_eq!(theme.font(TypeRole::Mono), None);
4246 assert_eq!(theme.font(TypeRole::None), None);
4247
4248 let both = theme
4249 .with_font(TypeRole::Mono, 9)
4250 .with_font(TypeRole::Display, 3);
4251 assert_eq!(both.font(TypeRole::Mono), Some(9));
4252 assert_eq!(
4253 both.font(TypeRole::Display),
4254 Some(3),
4255 "the role named last wins"
4256 );
4257 assert_eq!(both.font(TypeRole::Body), Some(5));
4258
4259 let mut encoder = BatchEncoder::default();
4260 encoder.encode(&Mutation::SetTheme(theme)).unwrap();
4261 let bytes = encoder.finish().unwrap();
4262 assert_eq!(decode_batch(bytes).unwrap(), [Mutation::SetTheme(theme)]);
4263 }
4264
4265 #[test]
4267 fn fr14_rejects_unknown_theme_tags() {
4268 let mut encoder = BatchEncoder::default();
4269 encoder
4270 .encode(&Mutation::SetTheme(Theme::default()))
4271 .unwrap();
4272 let mut bytes = encoder.finish().unwrap().to_vec();
4273 bytes[ENVELOPE_LEN + 4..ENVELOPE_LEN + 6].copy_from_slice(&99_u16.to_le_bytes());
4274 assert_eq!(decode_batch(&bytes), Err(ProtocolError::InvalidTheme(99)));
4275 }
4276
4277 #[test]
4278 fn rejects_unknown_tags_without_panicking() {
4279 let mut bytes = vec![0, 0, 12, 0, 20, 0, 0, 0, 1, 0, 0, 0];
4280 bytes.extend_from_slice(&999_u16.to_le_bytes());
4281 bytes.extend_from_slice(&8_u16.to_le_bytes());
4282 bytes.extend_from_slice(&0_u32.to_le_bytes());
4283 assert_eq!(decode_batch(&bytes), Err(ProtocolError::InvalidTag(999)));
4284 }
4285
4286 #[test]
4287 fn pr4_renderer_events_round_trip_fixed_layout() {
4288 let events = [
4289 HostEvent {
4290 node_id: 7,
4291 handler_id: 11,
4292 payload: crate::EventPayload::Clicked,
4293 },
4294 HostEvent {
4295 node_id: 8,
4296 handler_id: 12,
4297 payload: crate::EventPayload::TextChanged("한글"),
4298 },
4299 HostEvent {
4300 node_id: 8,
4301 handler_id: 13,
4302 payload: crate::EventPayload::TextSubmitted("done"),
4303 },
4304 HostEvent {
4305 node_id: 8,
4306 handler_id: 14,
4307 payload: crate::EventPayload::FocusLost,
4308 },
4309 HostEvent {
4310 node_id: 9,
4311 handler_id: 16,
4312 payload: crate::EventPayload::RangeRequested {
4313 start: 100,
4314 count: 20,
4315 },
4316 },
4317 HostEvent {
4318 node_id: 8,
4319 handler_id: 15,
4320 payload: crate::EventPayload::KeyDown {
4321 key: Key::Enter,
4322 shift_key: true,
4323 ctrl_key: false,
4324 alt_key: true,
4325 meta_key: false,
4326 repeat: true,
4327 code: 0x28,
4328 text: "Enter",
4329 },
4330 },
4331 ];
4332 let mut bytes = Vec::new();
4333 for event in events {
4334 encode_event(&event, &mut bytes).unwrap();
4335 assert_eq!(decode_event(&bytes).unwrap(), event);
4336 }
4337 }
4338
4339 #[test]
4340 fn fr19_9_window_safe_area_round_trips() {
4341 let event = HostEvent {
4342 node_id: 0,
4343 handler_id: 0,
4344 payload: crate::EventPayload::WindowSizeChanged {
4345 width_dp: 841.5,
4346 height_dp: 600.25,
4347 class: crate::WindowSizeClass::Expanded,
4348 height_class: crate::WindowHeightClass::Medium,
4349 safe_area_top: 28.5,
4350 safe_area_leading: 72.0,
4351 safe_area_trailing: 13.0,
4352 },
4353 };
4354 let mut bytes = Vec::new();
4355 encode_event(&event, &mut bytes).unwrap();
4356 assert_eq!(bytes.len(), 44);
4357 assert_eq!(decode_event(&bytes).unwrap(), event);
4358 }
4359
4360 #[test]
4361 fn nfr7_malformed_key_down_text_is_a_protocol_error() {
4362 let event = HostEvent {
4363 node_id: 1,
4364 handler_id: 2,
4365 payload: crate::EventPayload::KeyDown {
4366 key: Key::Enter,
4367 shift_key: false,
4368 ctrl_key: false,
4369 alt_key: false,
4370 meta_key: false,
4371 repeat: false,
4372 code: 0x51,
4373 text: "ArrowDown",
4374 },
4375 };
4376 let mut bytes = Vec::new();
4377 encode_event(&event, &mut bytes).unwrap();
4378 assert_eq!(decode_event(&bytes).unwrap(), event);
4379 let mut past_end = bytes.clone();
4381 past_end[28..32].copy_from_slice(&200_u32.to_le_bytes());
4382 assert_eq!(
4383 decode_event(&past_end),
4384 Err(ProtocolError::InvalidStringRange)
4385 );
4386 let mut inside_header = bytes.clone();
4388 inside_header[24..28].copy_from_slice(&8_u32.to_le_bytes());
4389 assert_eq!(
4390 decode_event(&inside_header),
4391 Err(ProtocolError::InvalidStringRange)
4392 );
4393 let mut not_utf8 = bytes;
4395 not_utf8[32] = 0xff;
4396 assert_eq!(decode_event(¬_utf8), Err(ProtocolError::InvalidUtf8));
4397 }
4398
4399 #[test]
4400 fn fr19_9_short_window_size_record_is_a_protocol_error() {
4401 let mut bytes = vec![0; 32];
4402 bytes[0..2].copy_from_slice(&EVENT_WINDOW_SIZE_CHANGED.to_le_bytes());
4403 bytes[2..4].copy_from_slice(&32_u16.to_le_bytes());
4404 assert!(decode_event(&bytes).is_err());
4405 bytes[2..4].copy_from_slice(&44_u16.to_le_bytes());
4406 assert!(decode_event(&bytes).is_err());
4407 }
4408
4409 #[test]
4410 fn fr20_unknown_window_size_class_is_a_protocol_error() {
4411 let event = HostEvent {
4412 node_id: 0,
4413 handler_id: 0,
4414 payload: crate::EventPayload::WindowSizeChanged {
4415 width_dp: 320.0,
4416 height_dp: 640.0,
4417 class: crate::WindowSizeClass::Compact,
4418 height_class: crate::WindowHeightClass::Medium,
4419 safe_area_top: 0.0,
4420 safe_area_leading: 0.0,
4421 safe_area_trailing: 0.0,
4422 },
4423 };
4424 let mut bytes = Vec::new();
4425 encode_event(&event, &mut bytes).unwrap();
4426 bytes[24..28].copy_from_slice(&99_u32.to_le_bytes());
4427 assert_eq!(
4428 decode_event(&bytes),
4429 Err(ProtocolError::InvalidValueKind(99))
4430 );
4431 }
4432
4433 #[test]
4434 fn fr20_unknown_window_height_class_is_a_protocol_error() {
4435 let event = HostEvent {
4436 node_id: 0,
4437 handler_id: 0,
4438 payload: crate::EventPayload::WindowSizeChanged {
4439 width_dp: 320.0,
4440 height_dp: 640.0,
4441 class: crate::WindowSizeClass::Compact,
4442 height_class: crate::WindowHeightClass::Medium,
4443 safe_area_top: 0.0,
4444 safe_area_leading: 0.0,
4445 safe_area_trailing: 0.0,
4446 },
4447 };
4448 let mut bytes = Vec::new();
4449 encode_event(&event, &mut bytes).unwrap();
4450 bytes[28..32].copy_from_slice(&99_u32.to_le_bytes());
4451 assert_eq!(
4452 decode_event(&bytes),
4453 Err(ProtocolError::InvalidValueKind(99))
4454 );
4455 }
4456
4457 #[test]
4458 fn fr20_window_size_classes_follow_the_material_boundaries() {
4459 use crate::WindowSizeClass;
4460 assert_eq!(
4461 WindowSizeClass::from_width_dp(599.9),
4462 WindowSizeClass::Compact
4463 );
4464 assert_eq!(
4465 WindowSizeClass::from_width_dp(600.0),
4466 WindowSizeClass::Medium
4467 );
4468 assert_eq!(
4469 WindowSizeClass::from_width_dp(839.9),
4470 WindowSizeClass::Medium
4471 );
4472 assert_eq!(
4473 WindowSizeClass::from_width_dp(840.0),
4474 WindowSizeClass::Expanded
4475 );
4476 }
4477
4478 #[test]
4479 fn fr20_window_height_classes_follow_the_material_boundaries() {
4480 use crate::WindowHeightClass;
4481 assert_eq!(
4482 WindowHeightClass::from_height_dp(479.9),
4483 WindowHeightClass::Compact
4484 );
4485 assert_eq!(
4486 WindowHeightClass::from_height_dp(480.0),
4487 WindowHeightClass::Medium
4488 );
4489 assert_eq!(
4490 WindowHeightClass::from_height_dp(899.9),
4491 WindowHeightClass::Medium
4492 );
4493 assert_eq!(
4494 WindowHeightClass::from_height_dp(900.0),
4495 WindowHeightClass::Expanded
4496 );
4497 }
4498
4499 fn post_notification() -> Mutation<'static> {
4500 Mutation::PostNotification {
4501 key: "session/7",
4502 title: "세션이 끝났습니다",
4503 body: "refactor-parser: 테스트 214개 통과",
4504 channel: "세션",
4505 action_1: "열기",
4506 action_2: "",
4507 importance: NotificationImportance::Urgent,
4508 presentation: NotificationPresentation::WhenInactive,
4509 }
4510 }
4511
4512 #[test]
4515 fn fr36_notification_commands_round_trip_at_their_fixed_lengths() {
4516 let mutations = [
4517 post_notification(),
4518 Mutation::WithdrawNotification { key: "session/7" },
4519 Mutation::RequestNotificationPermission,
4520 ];
4521 let mut lengths = Vec::new();
4522 let mut encoder = BatchEncoder::default();
4523 for mutation in &mutations {
4524 encoder.encode(mutation).unwrap();
4525 }
4526 let bytes = encoder.finish().unwrap();
4527 let mut position = ENVELOPE_LEN;
4528 for _ in &mutations {
4529 let length = read_u16(bytes, position + 2).unwrap();
4530 lengths.push((read_u16(bytes, position).unwrap(), length));
4531 position += usize::from(length);
4532 }
4533 assert_eq!(
4534 lengths,
4535 [
4536 (TAG_POST_NOTIFICATION, 56),
4537 (TAG_WITHDRAW_NOTIFICATION, 12),
4538 (TAG_REQUEST_NOTIFICATION_PERMISSION, 4),
4539 ]
4540 );
4541 assert_eq!(decode_batch(bytes).unwrap(), mutations);
4542 }
4543
4544 #[test]
4548 fn fr21_message_placement_round_trips() {
4549 let placed = Mutation::ShowMessage {
4550 handler_id: 9,
4551 text: "Copied",
4552 action: "",
4553 duration: MessageDuration::Short,
4554 placement: Some(MessagePlacement {
4555 vertical: MessageVertical::Bottom,
4556 horizontal: MessageHorizontal::Center,
4557 }),
4558 offset_dp: Some(96.0),
4559 };
4560 let unplaced = Mutation::ShowMessage {
4561 handler_id: 0,
4562 text: "Saved",
4563 action: "",
4564 duration: MessageDuration::Long,
4565 placement: None,
4566 offset_dp: None,
4567 };
4568 let top_end = Mutation::ShowMessage {
4569 handler_id: 0,
4570 text: "Synced",
4571 action: "",
4572 duration: MessageDuration::Short,
4573 placement: Some(MessagePlacement {
4574 vertical: MessageVertical::Top,
4575 horizontal: MessageHorizontal::End,
4576 }),
4577 offset_dp: None,
4578 };
4579 let mutations = [placed, unplaced, top_end];
4580 let mut encoder = BatchEncoder::default();
4581 for mutation in &mutations {
4582 encoder.encode(mutation).unwrap();
4583 }
4584 let bytes = encoder.finish().unwrap();
4585 let record = ENVELOPE_LEN;
4586 assert_eq!(read_u16(bytes, record).unwrap(), TAG_SHOW_MESSAGE);
4587 assert_eq!(read_u16(bytes, record + 2).unwrap(), 40);
4588 assert_eq!(read_u64(bytes, record + 4).unwrap(), 9);
4590 assert_eq!(read_u16(bytes, record + 28).unwrap(), 1);
4591 assert_eq!(read_u16(bytes, record + 30).unwrap(), 2);
4593 assert_eq!(read_u16(bytes, record + 32).unwrap(), 2);
4594 assert_eq!(f32::from_bits(read_u32(bytes, record + 36).unwrap()), 96.0);
4595 let second = record + 40;
4597 assert_eq!(read_u16(bytes, second + 30).unwrap(), 0);
4598 assert_eq!(read_u16(bytes, second + 32).unwrap(), 0);
4599 assert!(f32::from_bits(read_u32(bytes, second + 36).unwrap()).is_nan());
4600 assert_eq!(decode_batch(bytes).unwrap(), mutations);
4601
4602 for (offset, raw) in [(30, 0_u16), (32, 0), (30, 7), (32, 9)] {
4605 let mut broken = bytes.to_vec();
4606 broken[record + offset..record + offset + 2].copy_from_slice(&raw.to_le_bytes());
4607 assert!(
4608 matches!(
4609 decode_batch(&broken),
4610 Err(ProtocolError::InvalidMessagePlacement(_))
4611 ),
4612 "offset {offset} set to {raw} must be refused"
4613 );
4614 }
4615 }
4616
4617 #[test]
4620 fn fr36_unknown_importance_and_presentation_are_protocol_errors() {
4621 for (offset, expected) in [(52, 9_u16), (54, 7_u16)] {
4622 let mut encoder = BatchEncoder::default();
4623 encoder.encode(&post_notification()).unwrap();
4624 let mut bytes = encoder.finish().unwrap().to_vec();
4625 bytes[ENVELOPE_LEN + offset..ENVELOPE_LEN + offset + 2]
4626 .copy_from_slice(&expected.to_le_bytes());
4627 assert_eq!(
4628 decode_batch(&bytes),
4629 Err(ProtocolError::InvalidNotification(expected))
4630 );
4631 }
4632 }
4633
4634 #[test]
4637 fn fr36_a_notification_record_of_the_wrong_length_is_rejected() {
4638 let mut bytes = vec![0, 0, 12, 0, 20, 0, 0, 0, 1, 0, 0, 0];
4639 bytes.extend_from_slice(&TAG_REQUEST_NOTIFICATION_PERMISSION.to_le_bytes());
4640 bytes.extend_from_slice(&8_u16.to_le_bytes());
4641 bytes.extend_from_slice(&0_u32.to_le_bytes());
4642 assert_eq!(
4643 decode_batch(&bytes),
4644 Err(ProtocolError::InvalidRecordLength)
4645 );
4646 }
4647
4648 #[test]
4651 fn fr36_notification_events_round_trip_fixed_layout() {
4652 let activated = HostEvent {
4653 node_id: 0,
4654 handler_id: 0,
4655 payload: crate::EventPayload::NotificationActivated {
4656 action: 2,
4657 key: "session/7",
4658 },
4659 };
4660 let mut bytes = Vec::new();
4661 encode_event(&activated, &mut bytes).unwrap();
4662 assert_eq!(read_u16(&bytes, 0).unwrap(), EVENT_NOTIFICATION_ACTIVATED);
4663 assert_eq!(read_u16(&bytes, 2).unwrap(), 28);
4664 assert_eq!(bytes.len(), 28 + "session/7".len());
4665 assert_eq!(decode_event(&bytes).unwrap(), activated);
4666
4667 for state in [
4668 NotificationPermission::NotDetermined,
4669 NotificationPermission::Granted,
4670 NotificationPermission::Denied,
4671 NotificationPermission::Unsupported,
4672 ] {
4673 let changed = HostEvent {
4674 node_id: 0,
4675 handler_id: 0,
4676 payload: crate::EventPayload::NotificationPermissionChanged(state),
4677 };
4678 encode_event(&changed, &mut bytes).unwrap();
4679 assert_eq!(bytes.len(), 20);
4680 assert_eq!(decode_event(&bytes).unwrap(), changed);
4681 }
4682 }
4683
4684 #[test]
4686 fn fr36_unknown_permission_state_is_a_protocol_error() {
4687 let mut bytes = Vec::new();
4688 encode_event(
4689 &HostEvent {
4690 node_id: 0,
4691 handler_id: 0,
4692 payload: crate::EventPayload::NotificationPermissionChanged(
4693 NotificationPermission::Granted,
4694 ),
4695 },
4696 &mut bytes,
4697 )
4698 .unwrap();
4699 bytes[16..20].copy_from_slice(&5_u32.to_le_bytes());
4700 assert_eq!(
4701 decode_event(&bytes),
4702 Err(ProtocolError::InvalidValueKind(5))
4703 );
4704 }
4705
4706 #[test]
4709 fn fr36_an_activation_naming_a_third_button_is_a_protocol_error() {
4710 let mut bytes = Vec::new();
4711 encode_event(
4712 &HostEvent {
4713 node_id: 0,
4714 handler_id: 0,
4715 payload: crate::EventPayload::NotificationActivated {
4716 action: 1,
4717 key: "k",
4718 },
4719 },
4720 &mut bytes,
4721 )
4722 .unwrap();
4723 bytes[16..20].copy_from_slice(&3_u32.to_le_bytes());
4724 assert_eq!(
4725 decode_event(&bytes),
4726 Err(ProtocolError::InvalidValueKind(3))
4727 );
4728 }
4729
4730 #[test]
4732 fn fr43_zoom_changed_round_trips_fixed_layout() {
4733 let event = HostEvent {
4734 node_id: 0,
4735 handler_id: 0,
4736 payload: crate::EventPayload::ZoomChanged {
4737 k: 1.25 * 1.44,
4738 os: 1.25,
4739 level: 2,
4740 },
4741 };
4742 let mut bytes = Vec::new();
4743 encode_event(&event, &mut bytes).unwrap();
4744 assert_eq!(read_u16(&bytes, 0).unwrap(), 33);
4745 assert_eq!(read_u16(&bytes, 2).unwrap(), 28);
4746 assert_eq!(bytes.len(), 28);
4747 assert_eq!(decode_event(&bytes).unwrap(), event);
4748
4749 let smaller = HostEvent {
4750 node_id: 0,
4751 handler_id: 0,
4752 payload: crate::EventPayload::ZoomChanged {
4753 k: 0.5,
4754 os: 1.0,
4755 level: -4,
4756 },
4757 };
4758 encode_event(&smaller, &mut bytes).unwrap();
4759 assert_eq!(decode_event(&bytes).unwrap(), smaller);
4760 }
4761
4762 #[test]
4764 fn fr43_a_zoom_level_out_of_range_is_rejected() {
4765 let event = HostEvent {
4766 node_id: 0,
4767 handler_id: 0,
4768 payload: crate::EventPayload::ZoomChanged {
4769 k: 1.0,
4770 os: 1.0,
4771 level: 9,
4772 },
4773 };
4774 let mut bytes = Vec::new();
4775 encode_event(&event, &mut bytes).unwrap();
4776 assert_eq!(
4777 decode_event(&bytes),
4778 Err(ProtocolError::InvalidValueKind(9))
4779 );
4780 bytes[2..4].copy_from_slice(&24_u16.to_le_bytes());
4781 assert_eq!(
4782 decode_event(&bytes),
4783 Err(ProtocolError::InvalidRecordLength)
4784 );
4785 }
4786
4787 #[test]
4790 fn fr43_the_window_record_carries_a_zoom_level() {
4791 for level in [None, Some(-8), Some(0), Some(2), Some(8)] {
4792 let mut window = crate::schema::Window::new().with_title("zoom");
4793 window.zoom_level = level;
4794 let mut encoder = BatchEncoder::default();
4795 encoder.encode(&Mutation::SetWindow(window)).unwrap();
4796 let bytes = encoder.finish().unwrap().to_vec();
4797 if level.is_none() {
4798 assert_eq!(read_u16(&bytes, ENVELOPE_LEN + 18).unwrap(), 0);
4799 }
4800 let decoded = decode_batch(&bytes).unwrap();
4801 assert_eq!(decoded, vec![Mutation::SetWindow(window)]);
4802 }
4803 assert_eq!(
4804 crate::schema::Window::new().with_zoom_level(20).zoom_level,
4805 Some(8)
4806 );
4807 }
4808
4809 #[test]
4812 fn fr20_8_set_window_report_size_bit_roundtrips() {
4813 for report in [false, true] {
4814 let window = crate::schema::Window::new()
4815 .with_title("size")
4816 .with_report_size(report);
4817 let mut encoder = BatchEncoder::default();
4818 encoder.encode(&Mutation::SetWindow(window)).unwrap();
4819 let bytes = encoder.finish().unwrap().to_vec();
4820 let flags = read_u16(&bytes, ENVELOPE_LEN + 16).unwrap();
4821 assert_eq!(flags, 1 | (u16::from(report) << 1));
4822 assert_eq!(
4823 decode_batch(&bytes).unwrap(),
4824 vec![Mutation::SetWindow(window)]
4825 );
4826 }
4827 let window = crate::schema::Window::new();
4829 let mut encoder = BatchEncoder::default();
4830 encoder.encode(&Mutation::SetWindow(window)).unwrap();
4831 let bytes = encoder.finish().unwrap().to_vec();
4832 let record = &bytes[ENVELOPE_LEN..];
4833 assert_eq!(record.len(), 32);
4834 assert_eq!(read_u16(record, 0).unwrap(), TAG_SET_WINDOW);
4835 assert_eq!(read_u16(record, 2).unwrap(), 32);
4836 assert_eq!(read_u16(record, 4).unwrap(), window.chrome as u16);
4837 assert_eq!(read_u16(record, 6).unwrap(), window.title_bar as u16);
4838 for at in [8, 10, 12, 14] {
4839 assert_eq!(read_u16(record, at).unwrap(), 0);
4840 }
4841 let expected_flags = 1
4843 | ((window.mode as u16) << WINDOW_MODE_SHIFT)
4844 | ((window.last_window_closed as u16) << WINDOW_POLICY_SHIFT);
4845 assert_eq!(read_u16(record, 16).unwrap(), expected_flags);
4846 assert_eq!(read_u16(record, 18).unwrap(), 0);
4847 assert_eq!(read_u32(record, 28).unwrap(), 0);
4848 }
4849
4850 #[test]
4853 fn fr20_8_set_window_rejects_unknown_flag_bits() {
4854 let mut encoder = BatchEncoder::default();
4855 encoder
4856 .encode(&Mutation::SetWindow(crate::schema::Window::new()))
4857 .unwrap();
4858 let bytes = encoder.finish().unwrap().to_vec();
4859 let flags = read_u16(&bytes, ENVELOPE_LEN + 16).unwrap();
4860 for bit in 2..8 {
4861 let bad = flags | (1 << bit);
4862 let mut copy = bytes.clone();
4863 copy[ENVELOPE_LEN + 16..ENVELOPE_LEN + 18].copy_from_slice(&bad.to_le_bytes());
4864 assert_eq!(
4865 decode_batch(©),
4866 Err(ProtocolError::InvalidValueKind(bad)),
4867 "bit {bit}"
4868 );
4869 }
4870 }
4871
4872 #[test]
4873 fn fr65_request_focus_round_trips_in_eight_bytes() {
4874 for node_id in [0_u32, 7, u32::MAX] {
4875 let mut encoder = BatchEncoder::default();
4876 encoder.encode(&Mutation::RequestFocus { node_id }).unwrap();
4877 let bytes = encoder.finish().unwrap().to_vec();
4878 assert_eq!(bytes.len(), ENVELOPE_LEN + 8);
4879 assert_eq!(read_u16(&bytes, ENVELOPE_LEN).unwrap(), TAG_REQUEST_FOCUS);
4880 assert_eq!(read_u16(&bytes, ENVELOPE_LEN + 2).unwrap(), 8);
4881 assert_eq!(read_u32(&bytes, ENVELOPE_LEN + 4).unwrap(), node_id);
4882 assert_eq!(
4883 decode_batch(&bytes).unwrap(),
4884 vec![Mutation::RequestFocus { node_id }]
4885 );
4886 }
4887 let mut encoder = BatchEncoder::default();
4888 encoder
4889 .encode(&Mutation::RequestFocus { node_id: 7 })
4890 .unwrap();
4891 let mut bad = encoder.finish().unwrap().to_vec();
4892 bad.extend_from_slice(&[0; 4]);
4893 let records_len = u32::try_from(bad.len()).unwrap();
4894 bad[4..8].copy_from_slice(&records_len.to_le_bytes());
4895 bad[ENVELOPE_LEN + 2..ENVELOPE_LEN + 4].copy_from_slice(&12_u16.to_le_bytes());
4896 assert_eq!(decode_batch(&bad), Err(ProtocolError::InvalidRecordLength));
4897 }
4898
4899 #[test]
4903 fn fr72_file_dialog_records_round_trip_and_unknown_enums_are_errors() {
4904 let filters = "이미지\0png,jpg\0Docs\0pdf,txt";
4905 for kind in [
4906 FileDialogKind::OpenFile,
4907 FileDialogKind::OpenFiles,
4908 FileDialogKind::OpenFolder,
4909 FileDialogKind::OpenFolders,
4910 FileDialogKind::Save,
4911 ] {
4912 let request = Mutation::ShowFileDialog {
4913 request: 7,
4914 window: 20,
4915 kind,
4916 default_filter: 1,
4917 title: "파일 고르기",
4918 directory: "/home/한글",
4919 name: "보고서.txt",
4920 filters,
4921 };
4922 let mut encoder = BatchEncoder::default();
4923 encoder.encode(&request).unwrap();
4924 let bytes = encoder.finish().unwrap().to_vec();
4925 assert_eq!(
4926 read_u16(&bytes, ENVELOPE_LEN).unwrap(),
4927 TAG_SHOW_FILE_DIALOG
4928 );
4929 assert_eq!(read_u16(&bytes, ENVELOPE_LEN + 2).unwrap(), 48);
4930 assert_eq!(decode_batch(&bytes).unwrap(), vec![request]);
4931 }
4932
4933 let empty = Mutation::ShowFileDialog {
4934 request: u32::MAX,
4935 window: 0,
4936 kind: FileDialogKind::Save,
4937 default_filter: 0,
4938 title: "",
4939 directory: "",
4940 name: "",
4941 filters: "",
4942 };
4943 let mut encoder = BatchEncoder::default();
4944 encoder.encode(&empty).unwrap();
4945 assert_eq!(
4946 decode_batch(encoder.finish().unwrap()).unwrap(),
4947 vec![empty]
4948 );
4949
4950 let full = Mutation::ShowFileDialog {
4951 request: 1,
4952 window: 2,
4953 kind: FileDialogKind::OpenFile,
4954 default_filter: 3,
4955 title: "t",
4956 directory: "/d",
4957 name: "n",
4958 filters,
4959 };
4960 let mut encoder = BatchEncoder::default();
4961 encoder.encode(&full).unwrap();
4962 let bytes = encoder.finish().unwrap().to_vec();
4963 for raw_kind in [0_u16, 6, 9, u16::MAX] {
4964 let mut bad = bytes.clone();
4965 bad[ENVELOPE_LEN + 12..ENVELOPE_LEN + 14].copy_from_slice(&raw_kind.to_le_bytes());
4966 assert_eq!(
4967 decode_batch(&bad),
4968 Err(ProtocolError::InvalidValueKind(raw_kind))
4969 );
4970 }
4971 let mut short = bytes.clone();
4972 short[ENVELOPE_LEN + 2..ENVELOPE_LEN + 4].copy_from_slice(&44_u16.to_le_bytes());
4973 assert_eq!(
4974 decode_batch(&short),
4975 Err(ProtocolError::InvalidRecordLength)
4976 );
4977 for cut in 0..bytes.len() {
4978 assert!(
4979 decode_batch(&bytes[..cut]).is_err(),
4980 "a batch cut at {cut} bytes decoded"
4981 );
4982 }
4983
4984 let paths = "/Users/한글/문서.txt\0/tmp/b c.png";
4985 for (status, filter) in [
4986 (FileDialogStatus::Chosen, 1_u32),
4987 (FileDialogStatus::Cancelled, u32::MAX),
4988 (FileDialogStatus::Unavailable, u32::MAX),
4989 ] {
4990 let result = HostEvent {
4991 node_id: 0,
4992 handler_id: 0,
4993 payload: crate::EventPayload::FileDialogResult {
4994 request: 41,
4995 status,
4996 filter,
4997 paths: if status == FileDialogStatus::Chosen {
4998 paths
4999 } else {
5000 ""
5001 },
5002 },
5003 };
5004 let mut bytes = Vec::new();
5005 encode_event(&result, &mut bytes).unwrap();
5006 assert_eq!(read_u16(&bytes, 0).unwrap(), EVENT_FILE_DIALOG_RESULT);
5007 assert_eq!(
5008 usize::from(read_u16(&bytes, 2).unwrap()),
5009 FILE_DIALOG_RESULT_LEN
5010 );
5011 assert_eq!(decode_event(&bytes).unwrap(), result);
5012 }
5013
5014 let result = HostEvent {
5015 node_id: 0,
5016 handler_id: 0,
5017 payload: crate::EventPayload::FileDialogResult {
5018 request: 3,
5019 status: FileDialogStatus::Chosen,
5020 filter: 0,
5021 paths,
5022 },
5023 };
5024 let mut bytes = Vec::new();
5025 encode_event(&result, &mut bytes).unwrap();
5026 for raw_status in [0_u32, 4, 0x1_0000, u32::MAX] {
5027 let mut bad = bytes.clone();
5028 bad[20..24].copy_from_slice(&raw_status.to_le_bytes());
5029 assert_eq!(
5030 decode_event(&bad),
5031 Err(ProtocolError::InvalidValueKind(
5032 raw_status.min(u32::from(u16::MAX)) as u16
5033 ))
5034 );
5035 }
5036 let mut wrong_length = bytes.clone();
5037 wrong_length[2..4].copy_from_slice(&32_u16.to_le_bytes());
5038 assert_eq!(
5039 decode_event(&wrong_length),
5040 Err(ProtocolError::InvalidRecordLength)
5041 );
5042 let mut not_utf8 = bytes.clone();
5043 let last = not_utf8.len() - 1;
5044 not_utf8[last] = 0xFF;
5045 assert_eq!(decode_event(¬_utf8), Err(ProtocolError::InvalidUtf8));
5046 for cut in 0..bytes.len() {
5047 assert!(
5048 decode_event(&bytes[..cut]).is_err(),
5049 "an event cut at {cut} bytes decoded"
5050 );
5051 }
5052 }
5053
5054 #[test]
5055 fn fr65_text_field_on_composition_change_delivers_bool() {
5056 use std::cell::RefCell;
5057 use std::rc::Rc;
5058 use std::sync::Arc;
5059 use std::sync::atomic::{AtomicUsize, Ordering};
5060
5061 let seen: Rc<RefCell<Vec<bool>>> = Rc::new(RefCell::new(Vec::new()));
5062 let calls = Arc::new(AtomicUsize::new(0));
5063 let calls_for_root = Arc::clone(&calls);
5064 let mut host = crate::ComposeHost::with_content(move || {
5065 let calls_inner = Arc::clone(&calls_for_root);
5066 crate::ui::TextField().on_composition_change(move |composing| {
5067 calls_inner.fetch_add(if composing { 10 } else { 1 }, Ordering::SeqCst);
5068 });
5069 });
5070 let init = host.rebuild().unwrap().to_vec();
5071 let mutations = decode_batch(&init).unwrap();
5072 let prop = mutations
5073 .iter()
5074 .find_map(|mutation| match mutation {
5075 Mutation::SetProp {
5076 node_id,
5077 property: PropertyKind::OnCompositionChange,
5078 value: PropertyValue::Integer(handler),
5079 } => Some((*node_id, *handler as u64)),
5080 _ => None,
5081 })
5082 .expect("TextField with on_composition_change must emit OnCompositionChange");
5083 let mut wire = Vec::new();
5084 encode_event(
5085 &HostEvent {
5086 node_id: prop.0,
5087 handler_id: prop.1,
5088 payload: crate::EventPayload::CompositionChanged { composing: true },
5089 },
5090 &mut wire,
5091 )
5092 .unwrap();
5093 host.dispatch_event(&wire).unwrap();
5094 encode_event(
5095 &HostEvent {
5096 node_id: prop.0,
5097 handler_id: prop.1,
5098 payload: crate::EventPayload::CompositionChanged { composing: false },
5099 },
5100 &mut wire,
5101 )
5102 .unwrap();
5103 host.dispatch_event(&wire).unwrap();
5104 assert_eq!(calls.load(Ordering::SeqCst), 11);
5105 drop(seen);
5106 }
5107
5108 #[test]
5109 fn fr45_focus_gained_and_lost_pair() {
5110 use std::sync::Arc;
5111 use std::sync::Mutex;
5112
5113 let trace: Arc<Mutex<Vec<&'static str>>> = Arc::new(Mutex::new(Vec::new()));
5114 let trace_for_root = Arc::clone(&trace);
5115 let mut host = crate::ComposeHost::with_content(move || {
5116 let gained_trace = Arc::clone(&trace_for_root);
5117 let lost_trace = Arc::clone(&trace_for_root);
5118 crate::ui::TextField()
5119 .on_focus_gained(move || {
5120 gained_trace.lock().unwrap().push("gained");
5121 })
5122 .on_focus_lost(move || {
5123 lost_trace.lock().unwrap().push("lost");
5124 });
5125 });
5126 let init = host.rebuild().unwrap().to_vec();
5127 let mutations = decode_batch(&init).unwrap();
5128 let gained_prop = mutations
5129 .iter()
5130 .find_map(|mutation| match mutation {
5131 Mutation::SetProp {
5132 node_id,
5133 property: PropertyKind::OnFocusGained,
5134 value: PropertyValue::Integer(handler),
5135 } => Some((*node_id, *handler as u64)),
5136 _ => None,
5137 })
5138 .expect("TextField with on_focus_gained must emit OnFocusGained");
5139 let lost_prop = mutations
5140 .iter()
5141 .find_map(|mutation| match mutation {
5142 Mutation::SetProp {
5143 node_id,
5144 property: PropertyKind::OnFocusLost,
5145 value: PropertyValue::Integer(handler),
5146 } => Some((*node_id, *handler as u64)),
5147 _ => None,
5148 })
5149 .expect("TextField with on_focus_lost must emit OnFocusLost");
5150 assert_eq!(gained_prop.0, lost_prop.0);
5151 assert_eq!(PropertyKind::OnFocusGained as u16, 117);
5152
5153 let gained_event = HostEvent {
5154 node_id: gained_prop.0,
5155 handler_id: gained_prop.1,
5156 payload: crate::EventPayload::FocusGained,
5157 };
5158 let mut wire = Vec::new();
5159 encode_event(&gained_event, &mut wire).unwrap();
5160 assert_eq!(wire.len(), 16);
5161 assert_eq!(read_u16(&wire, 0).unwrap(), EVENT_FOCUS_GAINED);
5162 assert_eq!(EVENT_FOCUS_GAINED, 48);
5163 assert_eq!(decode_event(&wire).unwrap(), gained_event);
5164 host.dispatch_event(&wire).unwrap();
5165
5166 let lost_event = HostEvent {
5167 node_id: lost_prop.0,
5168 handler_id: lost_prop.1,
5169 payload: crate::EventPayload::FocusLost,
5170 };
5171 encode_event(&lost_event, &mut wire).unwrap();
5172 assert_eq!(decode_event(&wire).unwrap(), lost_event);
5173 host.dispatch_event(&wire).unwrap();
5174
5175 assert_eq!(*trace.lock().unwrap(), vec!["gained", "lost"]);
5176 }
5177
5178 #[test]
5179 fn fr45_key_up_follows_key_down() {
5180 use std::sync::Arc;
5181 use std::sync::Mutex;
5182
5183 type KeyTrace = Vec<(&'static str, Key, bool, bool, bool, bool)>;
5185 let trace: Arc<Mutex<KeyTrace>> = Arc::new(Mutex::new(Vec::new()));
5186 let trace_for_root = Arc::clone(&trace);
5187 let mut host = crate::ComposeHost::with_content(move || {
5188 let down_trace = Arc::clone(&trace_for_root);
5189 let up_trace = Arc::clone(&trace_for_root);
5190 crate::ui::TextField()
5191 .on_key_down(move |event| {
5192 down_trace.lock().unwrap().push((
5193 "down",
5194 event.key(),
5195 event.shift_key(),
5196 event.ctrl_key(),
5197 event.alt_key(),
5198 event.meta_key(),
5199 ));
5200 })
5201 .on_key_up(move |event| {
5202 up_trace.lock().unwrap().push((
5203 "up",
5204 event.key(),
5205 event.shift_key(),
5206 event.ctrl_key(),
5207 event.alt_key(),
5208 event.meta_key(),
5209 ));
5210 });
5211 });
5212 let init = host.rebuild().unwrap().to_vec();
5213 let mutations = decode_batch(&init).unwrap();
5214 let down_prop = mutations
5215 .iter()
5216 .find_map(|mutation| match mutation {
5217 Mutation::SetProp {
5218 node_id,
5219 property: PropertyKind::OnKeyDown,
5220 value: PropertyValue::Integer(handler),
5221 } => Some((*node_id, *handler as u64)),
5222 _ => None,
5223 })
5224 .expect("TextField with on_key_down must emit OnKeyDown");
5225 let up_prop = mutations
5226 .iter()
5227 .find_map(|mutation| match mutation {
5228 Mutation::SetProp {
5229 node_id,
5230 property: PropertyKind::OnKeyUp,
5231 value: PropertyValue::Integer(handler),
5232 } => Some((*node_id, *handler as u64)),
5233 _ => None,
5234 })
5235 .expect("TextField with on_key_up must emit OnKeyUp");
5236 assert_eq!(down_prop.0, up_prop.0);
5237 assert_eq!(PropertyKind::OnKeyUp as u16, 118);
5238
5239 let down_event = HostEvent {
5240 node_id: down_prop.0,
5241 handler_id: down_prop.1,
5242 payload: crate::EventPayload::KeyDown {
5243 key: Key::Enter,
5244 shift_key: true,
5245 ctrl_key: false,
5246 alt_key: true,
5247 meta_key: false,
5248 repeat: false,
5249 code: 0x28,
5250 text: "Enter",
5251 },
5252 };
5253 let mut wire = Vec::new();
5254 encode_event(&down_event, &mut wire).unwrap();
5255 assert_eq!(decode_event(&wire).unwrap(), down_event);
5256 host.dispatch_event(&wire).unwrap();
5257
5258 let up_event = HostEvent {
5259 node_id: up_prop.0,
5260 handler_id: up_prop.1,
5261 payload: crate::EventPayload::KeyUp {
5262 key: Key::Enter,
5263 shift_key: true,
5264 ctrl_key: false,
5265 alt_key: true,
5266 meta_key: false,
5267 repeat: false,
5268 code: 0x28,
5269 text: "Enter",
5270 },
5271 };
5272 encode_event(&up_event, &mut wire).unwrap();
5273 assert_eq!(wire.len(), 37);
5274 assert_eq!(read_u16(&wire, 0).unwrap(), EVENT_KEY_UP);
5275 assert_eq!(EVENT_KEY_UP, 49);
5276 assert_eq!(decode_event(&wire).unwrap(), up_event);
5277 host.dispatch_event(&wire).unwrap();
5278
5279 assert_eq!(
5280 *trace.lock().unwrap(),
5281 vec![
5282 ("down", Key::Enter, true, false, true, false),
5283 ("up", Key::Enter, true, false, true, false),
5284 ]
5285 );
5286 }
5287
5288 #[test]
5289 fn fr45_unregistered_nodes_get_no_events() {
5290 let mut host = crate::ComposeHost::with_content(|| {
5291 crate::ui::TextField();
5292 crate::ui::Box().content(|| {});
5293 });
5294 let init = host.rebuild().unwrap().to_vec();
5295 let mutations = decode_batch(&init).unwrap();
5296 for mutation in &mutations {
5297 if let Mutation::SetProp { property, .. } = mutation {
5298 assert_ne!(
5299 *property,
5300 PropertyKind::OnFocusGained,
5301 "unregistered node must not emit OnFocusGained"
5302 );
5303 assert_ne!(
5304 *property,
5305 PropertyKind::OnKeyUp,
5306 "unregistered node must not emit OnKeyUp"
5307 );
5308 }
5309 }
5310 }
5311
5312 #[test]
5315 fn fr12_named_keys_are_appended_and_round_trip() {
5316 let named = [
5317 (Key::Enter, 1u16, 0x28u32, "Enter"),
5318 (Key::Other, 2, 0x04, "a"),
5319 (Key::Escape, 3, 0x29, "Escape"),
5320 (Key::Tab, 4, 0x2B, "Tab"),
5321 (Key::Backspace, 5, 0x2A, "Backspace"),
5322 (Key::Delete, 6, 0x4C, "Delete"),
5323 (Key::ArrowUp, 7, 0x52, "ArrowUp"),
5324 (Key::ArrowDown, 8, 0x51, "ArrowDown"),
5325 (Key::ArrowLeft, 9, 0x50, "ArrowLeft"),
5326 (Key::ArrowRight, 10, 0x4F, "ArrowRight"),
5327 (Key::Home, 11, 0x4A, "Home"),
5328 (Key::End, 12, 0x4D, "End"),
5329 (Key::PageUp, 13, 0x4B, "PageUp"),
5330 (Key::PageDown, 14, 0x4E, "PageDown"),
5331 ];
5332 let mut wire = Vec::new();
5333 for (key, tag, code, text) in named {
5334 assert_eq!(Key::try_from(tag), Ok(key), "tag {tag} names {text}");
5335 let event = HostEvent {
5336 node_id: 4,
5337 handler_id: 9,
5338 payload: crate::EventPayload::KeyDown {
5339 key,
5340 shift_key: false,
5341 ctrl_key: false,
5342 alt_key: false,
5343 meta_key: false,
5344 repeat: false,
5345 code,
5346 text,
5347 },
5348 };
5349 encode_event(&event, &mut wire).unwrap();
5350 assert_eq!(read_u16(&wire, 16).unwrap(), tag);
5351 assert_eq!(decode_event(&wire).unwrap(), event);
5352 }
5353 assert_eq!(Key::try_from(15), Ok(Key::F1));
5354 }
5355
5356 #[test]
5360 fn fr12_function_and_context_menu_keys() {
5361 let functions = [
5362 Key::F1,
5363 Key::F2,
5364 Key::F3,
5365 Key::F4,
5366 Key::F5,
5367 Key::F6,
5368 Key::F7,
5369 Key::F8,
5370 Key::F9,
5371 Key::F10,
5372 Key::F11,
5373 Key::F12,
5374 ];
5375 for (index, key) in functions.into_iter().enumerate() {
5376 assert_eq!(Key::try_from(15 + index as u16), Ok(key), "F{}", index + 1);
5377 }
5378 assert_eq!(Key::try_from(27), Ok(Key::ContextMenu));
5379 assert_eq!(Key::try_from(28), Err(()));
5380
5381 let presses = [
5382 (Key::F2, 16u16, false, 0x3Bu32, "F2"),
5383 (Key::F12, 26, false, 0x45, "F12"),
5384 (Key::F10, 24, true, 0x43, "F10"),
5385 (Key::ContextMenu, 27, false, 0x65, "ContextMenu"),
5386 ];
5387 let mut wire = Vec::new();
5388 for (key, tag, shift_key, code, text) in presses {
5389 let event = HostEvent {
5390 node_id: 4,
5391 handler_id: 9,
5392 payload: crate::EventPayload::KeyDown {
5393 key,
5394 shift_key,
5395 ctrl_key: false,
5396 alt_key: false,
5397 meta_key: false,
5398 repeat: false,
5399 code,
5400 text,
5401 },
5402 };
5403 encode_event(&event, &mut wire).unwrap();
5404 assert_eq!(read_u16(&wire, 16).unwrap(), tag, "{text}");
5405 assert_eq!(decode_event(&wire).unwrap(), event);
5406 }
5407 }
5408
5409 #[test]
5413 fn fr45_container_key_down_sees_escape_and_consumes_it() {
5414 use std::sync::{Arc, Mutex};
5415
5416 let seen: Arc<Mutex<Vec<(String, u32)>>> = Arc::new(Mutex::new(Vec::new()));
5417 let seen_for_root = Arc::clone(&seen);
5418 let mut host = crate::ComposeHost::with_content(move || {
5419 let seen = Arc::clone(&seen_for_root);
5420 crate::ui::Column()
5421 .on_key_down(move |event| {
5422 seen.lock()
5423 .unwrap()
5424 .push((event.text().to_owned(), event.code()));
5425 if event.text() == "Escape" {
5426 event.consume();
5427 }
5428 })
5429 .content(|| {
5430 crate::ui::Text("session list");
5431 });
5432 });
5433 let init = host.rebuild().unwrap().to_vec();
5434 let mutations = decode_batch(&init).unwrap();
5435 let (node_id, handler_id) = mutations
5436 .iter()
5437 .find_map(|mutation| match mutation {
5438 Mutation::SetProp {
5439 node_id,
5440 property: PropertyKind::OnKeyDown,
5441 value: PropertyValue::Integer(handler),
5442 } => Some((*node_id, *handler as u64)),
5443 _ => None,
5444 })
5445 .expect("a Column with on_key_down must emit OnKeyDown");
5446 let created_column = mutations.iter().any(|mutation| {
5447 matches!(
5448 mutation,
5449 Mutation::Create { node_id: id, widget: crate::WidgetKind::Column, .. } if *id == node_id
5450 )
5451 });
5452 assert!(
5453 created_column,
5454 "the key handler belongs to the Column itself"
5455 );
5456
5457 let mut wire = Vec::new();
5458 let mut press = |code: u32, text: &'static str| {
5459 encode_event(
5460 &HostEvent {
5461 node_id,
5462 handler_id,
5463 payload: crate::EventPayload::KeyDown {
5464 key: if code == 0x29 {
5465 Key::Escape
5466 } else {
5467 Key::ArrowDown
5468 },
5469 shift_key: false,
5470 ctrl_key: false,
5471 alt_key: false,
5472 meta_key: false,
5473 repeat: false,
5474 code,
5475 text,
5476 },
5477 },
5478 &mut wire,
5479 )
5480 .unwrap();
5481 host.dispatch_event(&wire).unwrap().1
5482 };
5483 assert_ne!(
5484 press(0x29, "Escape"),
5485 0,
5486 "Escape was consumed by the container"
5487 );
5488 assert_eq!(
5489 press(0x51, "ArrowDown"),
5490 0,
5491 "an unhandled key is left to the Renderer"
5492 );
5493
5494 assert_eq!(
5495 *seen.lock().unwrap(),
5496 vec![("Escape".to_owned(), 0x29), ("ArrowDown".to_owned(), 0x51)]
5497 );
5498 }
5499}