Skip to main content

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;