mx_remote/types/osd.rs
1// Author: Lars Op den Kamp (lars@opdenkamp-it.nl)
2// Copyright (c) 2026 Op den Kamp IT Solutions
3
4//! Overlay sync: a management unit having other units copy its overlay
5//! settings and logos.
6//!
7//! The source sends a sync naming what it copies and how many settings
8//! follow. Each unit asks the source for the settings it lacks, takes them,
9//! and reports how that went. Every frame of a sync carries the same `seq`,
10//! which the source numbers on its own.
11
12use std::net::Ipv4Addr;
13
14use crate::wire::{
15 DeviceUid, OsdCaptionAlign, OsdCaptionFormat, OsdMessageFlag, OsdMessagePriority, OsdSyncSize,
16 OsdSyncState, OsdSyncWhat,
17};
18
19/// The logo slots a sync can copy.
20pub const OSD_SYNC_SLOTS: usize = 4;
21
22/// One logo slot, as a sync describes it.
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub struct OsdSyncSlot {
25 /// The PNG's size in bytes; zero empties the slot.
26 pub bytes: u32,
27 /// The PNG's CRC-32.
28 pub crc32: u32,
29 /// The size it is shown at, in percent.
30 pub percent: u8,
31}
32
33/// A sync, as its source sent it.
34///
35/// A unit takes one only from a management unit: the mesh controller, or
36/// one that reports [`DeviceFeature::MANAGER`](crate::DeviceFeature::MANAGER).
37#[derive(Clone, Debug, PartialEq, Eq)]
38pub struct OsdSync {
39 /// The sync's number at its source.
40 pub seq: u16,
41 /// The unit it is for, or [`DeviceUid::ZERO`] for every unit.
42 pub target: DeviceUid,
43 /// What it copies.
44 pub what: OsdSyncWhat,
45 /// The source's HTTP port, zero meaning 80.
46 pub http_port: u16,
47 /// The source's address, unspecified meaning the sender's.
48 pub source_ip: Ipv4Addr,
49 /// How many settings follow.
50 pub records: u16,
51 /// The logo slots, when it copies them. A unit fetches a slot whose size
52 /// or CRC differs from its own from
53 /// `http://source_ip:http_port/osd-sync/slotN.png`, N being 1 to 4.
54 pub slots: Option<[OsdSyncSlot; OSD_SYNC_SLOTS]>,
55}
56
57/// A unit asking for a sync, or for settings of one it is taking, or
58/// management asking for a new sync for a unit.
59#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct OsdSyncRequest {
61 /// The unit the sync is for, which sent the request unless management
62 /// did.
63 pub target: DeviceUid,
64 /// The sync whose settings it asks for, or zero asking for a new sync.
65 pub seq: u16,
66 /// What it asks a new sync to copy.
67 pub what: OsdSyncWhat,
68 /// The settings it lacks, bit `i` standing for setting `i`.
69 pub missing: [u8; 16],
70 /// Who sent the sync it asks about. [`DeviceUid::ZERO`] when it asks for
71 /// a new sync.
72 pub source: DeviceUid,
73}
74
75impl OsdSyncRequest {
76 /// Whether the unit lacks setting `index`.
77 pub fn is_missing(&self, index: u16) -> bool {
78 self.missing
79 .get(usize::from(index / 8))
80 .is_some_and(|b| b & (1 << (index % 8)) != 0)
81 }
82}
83
84/// A caption, as a setting gives it.
85#[derive(Clone, Debug, PartialEq, Eq)]
86pub struct OsdCaption {
87 /// The font's name, empty for the layout's.
88 pub font: String,
89 /// The size, zero for the layout's.
90 pub size: u16,
91 /// The horizontal position, `i16::MIN` for the layout's.
92 pub x: i16,
93 /// The vertical position, `i16::MIN` for the layout's.
94 pub y: i16,
95 /// The wrap width in pixels: -1 automatic, 0 none, -2 the layout's.
96 pub wrap: i16,
97 /// The alignment.
98 pub align: OsdCaptionAlign,
99 /// How the text is read.
100 pub format: OsdCaptionFormat,
101 /// The colour as 0xRRGGBB, `u32::MAX` for the layout's.
102 pub colour: u32,
103 /// The text.
104 pub text: String,
105}
106
107/// A logo choice and place, as a setting gives it.
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub struct OsdLogo {
110 /// 0 the built-in logo, -1 none, 1 to 4 a slot.
111 pub logo: i8,
112 /// The horizontal position, `i16::MIN` for the layout's.
113 pub x: i16,
114 /// The vertical position, `i16::MIN` for the layout's.
115 pub y: i16,
116}
117
118/// What one setting of a sync sets.
119#[derive(Clone, Debug, PartialEq, Eq)]
120pub enum OsdSyncValue {
121 /// A caption.
122 Caption(OsdCaption),
123 /// A logo choice and place.
124 Logo(OsdLogo),
125 /// A background colour as 0xRRGGBB, `u32::MAX` for the layout's.
126 Background(u32),
127}
128
129/// One setting of a sync, which its source sends to one unit.
130#[derive(Clone, Debug, PartialEq, Eq)]
131pub struct OsdSyncSetting {
132 /// The sync it belongs to.
133 pub seq: u16,
134 /// The unit it is for.
135 pub target: DeviceUid,
136 /// Its place in the sync, below `records`.
137 pub index: u16,
138 /// How many settings the sync has.
139 pub records: u16,
140 /// The output size it applies at.
141 pub size: OsdSyncSize,
142 /// The layers it applies to, a bit per layer.
143 pub layers: u16,
144 /// The fields it gives: [`OsdCaptionField`](crate::OsdCaptionField)
145 /// bits for a caption, [`OsdLogoField`](crate::OsdLogoField) bits for a
146 /// logo, and bit 0 for a background's colour.
147 pub set: u16,
148 /// What it sets.
149 pub value: OsdSyncValue,
150}
151
152/// How a unit is getting on with a sync.
153///
154/// A unit reports taking, then done or failed. One in power save reports
155/// deferred, and reports again once it takes the sync on waking.
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub struct OsdSyncReport {
158 /// The sync it reports on, numbered by the source that sent it.
159 pub seq: u16,
160 /// How far it has got.
161 pub state: OsdSyncState,
162 /// The settings it wrote.
163 pub written: u16,
164 /// The settings it refused.
165 pub refused: u16,
166 /// Why it failed or refused any.
167 pub reason: String,
168}
169
170/// The most settings one sync carries.
171pub const OSD_SYNC_RECORDS_MAX: usize = 128;
172
173/// The longest caption font name, in bytes.
174pub const OSD_SYNC_FONT_MAX: usize = 31;
175
176/// The longest caption text a unit shows, in bytes. It cuts a longer one
177/// short.
178pub const OSD_SYNC_TEXT_MAX: usize = 360;
179
180/// The largest logo image a unit takes, in bytes.
181pub const OSD_SYNC_IMAGE_MAX: usize = 512 * 1024;
182
183/// The widest and tallest logo image a unit takes, in pixels.
184pub const OSD_SYNC_IMAGE_WIDTH_MAX: u32 = 1920;
185/// See [`OSD_SYNC_IMAGE_WIDTH_MAX`].
186pub const OSD_SYNC_IMAGE_HEIGHT_MAX: u32 = 1080;
187
188/// The largest size a logo is shown at, in percent.
189pub const OSD_SYNC_PERCENT_MAX: u8 = 100;
190
191/// One setting of a sync this client sends.
192#[derive(Clone, Debug, PartialEq, Eq)]
193pub struct OsdSyncEntry {
194 /// The output size it applies at.
195 pub size: OsdSyncSize,
196 /// The layers it applies to, a bit per layer. A unit leaves out the
197 /// layers it does not have, and skips the setting when none is left.
198 pub layers: u16,
199 /// The fields it gives, as in [`OsdSyncSetting::set`]. A field given its
200 /// default value goes back to the layout's.
201 pub set: u16,
202 /// What it sets.
203 pub value: OsdSyncValue,
204}
205
206/// The logo image for one slot.
207#[derive(Clone, Debug, Default, PartialEq, Eq)]
208pub struct OsdSyncImage {
209 /// The PNG. Empty empties the slot.
210 pub png: Vec<u8>,
211 /// The size it is shown at: 0 at its own pixels, 1 to 100 its longer
212 /// side as a percent of the large overlay.
213 pub percent: u8,
214}
215
216/// What this client has units copy.
217#[derive(Clone, Debug, Default, PartialEq, Eq)]
218pub struct OsdSyncContent {
219 /// The kinds of setting it copies.
220 /// [`OsdSyncWhat::LOGO_SLOTS`] follows from `images`.
221 pub what: OsdSyncWhat,
222 /// The settings, which units write in this order.
223 pub entries: Vec<OsdSyncEntry>,
224 /// The four logo slots, or `None` to leave every unit's own.
225 pub images: Option<[OsdSyncImage; OSD_SYNC_SLOTS]>,
226}
227
228/// The message on a unit's output, as the unit last reported it.
229///
230/// The unit holds it until it restarts.
231#[derive(Clone, Debug, PartialEq, Eq)]
232pub struct OsdMessage {
233 /// Whether a message is set and shown, whether the unit can show one, and
234 /// whether it refused the write it answers. Without
235 /// [`SET`](OsdMessageFlag::SET) the other fields are empty.
236 pub flags: OsdMessageFlag,
237 /// The logo shown with it: 0 the built-in logo, -1 none, 1 to 5 a slot.
238 pub logo: i8,
239 /// Where it stands against the unit's own messages.
240 pub priority: OsdMessagePriority,
241 /// Its markup.
242 pub text: String,
243}
244
245/// The longest message text a unit takes, in bytes.
246pub const OSD_MESSAGE_TEXT_MAX: usize = 255;