mx_remote/types/multiviewer.rs
1// Author: Lars Op den Kamp (lars@opdenkamp-it.nl)
2// Copyright (c) 2026 Op den Kamp IT Solutions
3
4//! The state a OneIP multiviewer reports.
5
6use crate::wire::{
7 DeviceUid, MultiviewerAspectRatio, MultiviewerBool, MultiviewerEdidTemplate,
8 MultiviewerHdcpMode, MultiviewerItcMode, MultiviewerOutputMode, MultiviewerPipPosition,
9 MultiviewerPipSize, MultiviewerSource, MultiviewerViewMode,
10};
11
12/// How many windows a multiviewer can show, and how many sources it maps.
13pub const MULTIVIEWER_INPUTS: usize = 4;
14
15/// A multiviewer's complete reported state.
16///
17/// Every enumerated field passes an unrecognised wire value through as it
18/// arrived, so a firmware that adds a mode reaches the caller as a value it
19/// does not know rather than as whichever mode happens to be zero.
20#[derive(Clone, Debug, Default, PartialEq, Eq)]
21pub struct MultiviewerStatus {
22 /// The multiviewer this report describes.
23 pub uid: DeviceUid,
24 /// The source device mapped to each of the four inputs, or
25 /// [`DeviceUid::ZERO`] where the input has none.
26 ///
27 /// A multiviewer running a module older than version 2026083100 leaves the
28 /// previous device here when a mapping is removed, so on those an
29 /// identifier is what was last mapped rather than what is mapped now, and
30 /// nothing on the wire separates the two.
31 pub mappings: [DeviceUid; MULTIVIEWER_INPUTS],
32 /// The MCU firmware version.
33 pub mcu_version: String,
34 /// The scaler firmware version.
35 pub scaler_version: String,
36 /// The layout the hardware reports, which the firmware maps to `view_mode`.
37 pub hw_view_mode: u8,
38 /// The window layout.
39 pub view_mode: MultiviewerViewMode,
40 /// Where the picture-in-picture window sits.
41 pub pip_position: MultiviewerPipPosition,
42 /// How large the picture-in-picture window is.
43 pub pip_size: MultiviewerPipSize,
44 /// The output resolution and refresh rate.
45 pub output_mode: MultiviewerOutputMode,
46 /// The HDCP version negotiated on the output.
47 pub hdcp_mode: MultiviewerHdcpMode,
48 /// The IT-content flag set on the output.
49 pub output_itc: MultiviewerItcMode,
50 /// The EDID template presented to the sources.
51 pub edid_template: MultiviewerEdidTemplate,
52 /// The aspect ratio the windows are scaled to.
53 pub aspect_ratio: MultiviewerAspectRatio,
54 /// Whether the multiviewer switches windows on its own.
55 pub auto_switch: MultiviewerBool,
56 /// The window whose audio is being output.
57 pub audio_source: MultiviewerSource,
58 /// Output volume as a percentage, or `None` when the device reported a
59 /// value outside 0..=100.
60 pub audio_volume: Option<u8>,
61 /// Whether the output is muted.
62 pub audio_muted: MultiviewerBool,
63 /// The source shown in each of the four windows.
64 pub video_sources: [MultiviewerSource; MULTIVIEWER_INPUTS],
65 /// The window receiving remote-control passthrough.
66 pub remote_control: MultiviewerSource,
67}
68
69/// How many windows each hardware layout shows, indexed by `hw_view_mode`.
70///
71/// Single is one window, picture-in-picture and the two-window layouts are
72/// two, and so on up to four. Slot zero is the layout a multiviewer reports
73/// before it has read one back from its scaler, which names no count.
74const WINDOWS_PER_HW_VIEW_MODE: [u8; 6] = [0, 1, 2, 2, 3, 4];
75
76impl MultiviewerStatus {
77 /// How many windows the multiviewer is showing, or `None` when it has
78 /// reported no layout.
79 ///
80 /// This reads `hw_view_mode` rather than `view_mode`: the second is
81 /// derived from the first plus a separate size read back from the scaler,
82 /// so a failed readback leaves `view_mode` naming nothing while the window
83 /// count is still known.
84 pub fn window_count(&self) -> Option<u8> {
85 WINDOWS_PER_HW_VIEW_MODE
86 .get(usize::from(self.hw_view_mode))
87 .copied()
88 .filter(|count| *count != 0)
89 }
90}