use std::ffi::c_char;
use mx_remote::{
DeviceUid, EdidProfile, MultiviewerAspectRatio, MultiviewerEdidTemplate, MultiviewerHdcpMode,
MultiviewerItcMode, MultiviewerOutputMode, MultiviewerPipPosition, MultiviewerPipSize,
MultiviewerSource, MultiviewerViewMode, RcAction, RcKey, V2ipAudioFormat, V2ipRoute,
V2ipRouteTarget, VideoWallWindow,
};
use crate::abi::{
fail, from_control, mxr_bay_uid_t, mxr_result_t, mxr_tribool_t, mxr_uid_t, opt_str, req_str,
};
use crate::info::mxr_amp_zone_settings_t;
use crate::remote::{mxr_remote_t, with};
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_audio_format_t {
pub sample_rate: u32,
pub channels: u8,
}
impl From<mxr_audio_format_t> for V2ipAudioFormat {
fn from(f: mxr_audio_format_t) -> Self {
Self {
sample_rate: f.sample_rate,
channels: f.channels,
}
}
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_video_source(
remote: *const mxr_remote_t,
sink: mxr_bay_uid_t,
source_port: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.select_video_source(sink.into(), source_port))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_audio_source(
remote: *const mxr_remote_t,
sink: mxr_bay_uid_t,
source_port: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.select_audio_source(sink.into(), source_port))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_video_source_by_name(
remote: *const mxr_remote_t,
sink: mxr_bay_uid_t,
name: *const c_char,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
match unsafe { req_str(name) } {
Ok(name) => from_control(r.remote.select_video_source_by_name(sink.into(), name)),
Err(code) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_audio_source_by_name(
remote: *const mxr_remote_t,
sink: mxr_bay_uid_t,
name: *const c_char,
format: *const mxr_audio_format_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let name = match unsafe { req_str(name) } {
Ok(name) => name,
Err(code) => return code,
};
let format = unsafe { format.as_ref() }.map(|f| (*f).into());
from_control(
r.remote
.select_audio_source_by_name(sink.into(), name, format),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_audio_source_addr(
remote: *const mxr_remote_t,
sink: mxr_bay_uid_t,
audio_ip: *const c_char,
audio_port: u16,
format: *const mxr_audio_format_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let text = match unsafe { req_str(audio_ip) } {
Ok(text) => text,
Err(code) => return code,
};
let Ok(ip) = text.parse() else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
&format!("audio_ip is not an IPv4 address: {text:?}"),
);
};
let format = unsafe { format.as_ref() }.map(|f| (*f).into());
from_control(r.remote.select_audio_source_addr(
sink.into(),
ip,
(audio_port != 0).then_some(audio_port),
format,
))
})
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_stream_addr_t {
pub ip: *const c_char,
pub port: u16,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_route_t {
pub video: mxr_stream_addr_t,
pub audio: mxr_stream_addr_t,
pub anc: mxr_stream_addr_t,
}
unsafe fn to_target(slot: mxr_stream_addr_t, what: &str) -> Result<V2ipRouteTarget, mxr_result_t> {
let text = unsafe { opt_str(slot.ip) }?.unwrap_or_default();
if text.is_empty() {
return Ok(V2ipRouteTarget::default());
}
match text.parse() {
Ok(ip) => Ok(V2ipRouteTarget {
ip,
port: slot.port,
}),
Err(_) => Err(fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
&format!("{what} is not an IPv4 address: {text:?}"),
)),
}
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_source_addr(
remote: *const mxr_remote_t,
sink: mxr_bay_uid_t,
route: *const mxr_v2ip_route_t,
format: *const mxr_audio_format_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(route) = (unsafe { route.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the route pointer is null",
);
};
let route = unsafe {
match (
to_target(route.video, "video ip"),
to_target(route.audio, "audio ip"),
to_target(route.anc, "anc ip"),
) {
(Ok(video), Ok(audio), Ok(anc)) => V2ipRoute { video, audio, anc },
(Err(code), _, _) | (_, Err(code), _) | (_, _, Err(code)) => return code,
}
};
let format = unsafe { format.as_ref() }.map(|f| (*f).into());
from_control(r.remote.select_source_addr(sink.into(), route, format))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_bay_name(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
name: *const c_char,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
match unsafe { req_str(name) } {
Ok(name) => from_control(r.remote.set_bay_name(bay.into(), name)),
Err(code) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_bay_hidden(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
hidden: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_bay_hidden(bay.into(), hidden))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_edid_profile(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
profile: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.select_edid_profile(bay.into(), EdidProfile::from_wire(profile)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_send_action(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
action: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.send_action(bay.into(), RcAction::from_wire(action)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_send_key(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
key: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.send_key(bay.into(), RcKey::from_wire(key)))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_power_on(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.power_on(bay.into())))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_power_off(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.power_off(bay.into())))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_volume(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
volume: u8,
muted: mxr_tribool_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_volume(
bay.into(),
volume,
match muted {
mxr_tribool_t::MXR_UNKNOWN => None,
mxr_tribool_t::MXR_FALSE => Some(false),
mxr_tribool_t::MXR_TRUE => Some(true),
},
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_volume_up(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.volume_up(bay.into())))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_volume_down(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.volume_down(bay.into())))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_muted(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
muted: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_muted(bay.into(), muted))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_amp_zone_settings(
remote: *const mxr_remote_t,
bay: mxr_bay_uid_t,
settings: *const mxr_amp_zone_settings_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(settings) = (unsafe { settings.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the amp zone settings pointer is null",
);
};
from_control(
r.remote
.set_amp_zone_settings(bay.into(), (*settings).into()),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_audio_endpoint_muted(
remote: *const mxr_remote_t,
device: mxr_uid_t,
endpoint: u16,
muted: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_audio_endpoint_muted(device.into(), endpoint, muted),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_audio_endpoint_trigger(
remote: *const mxr_remote_t,
device: mxr_uid_t,
endpoint: u16,
active: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_audio_endpoint_trigger(device.into(), endpoint, active),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_audio_endpoint_volume(
remote: *const mxr_remote_t,
device: mxr_uid_t,
endpoint: u16,
volume: u32,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_audio_endpoint_volume(device.into(), endpoint, volume),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_select_audio_endpoint_input(
remote: *const mxr_remote_t,
sink: mxr_uid_t,
sink_endpoint: u16,
source: mxr_uid_t,
source_endpoint: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.select_audio_endpoint_input(
sink.into(),
sink_endpoint,
source.into(),
source_endpoint,
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_request_edid(
remote: *const mxr_remote_t,
device: mxr_uid_t,
output: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.request_edid(device.into(), output))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_request_signal_status(
remote: *const mxr_remote_t,
device: mxr_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let uid = DeviceUid::from(device);
let target = (uid != DeviceUid::ZERO).then_some(uid);
from_control(r.remote.request_signal_status(target))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_subscribe_v2ip_stats(
remote: *const mxr_remote_t,
device: mxr_uid_t,
subscribe: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.subscribe_v2ip_stats(device.into(), subscribe))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_reboot(
remote: *const mxr_remote_t,
device: mxr_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.reboot(device.into())))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_send_monitoring_pulse(remote: *const mxr_remote_t) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.send_monitoring_pulse()))
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_video_wall_window_t {
pub pos_x: u16,
pub pos_y: u16,
pub width: u16,
pub height: u16,
pub raster_w: u16,
pub raster_h: u16,
}
pub const MXR_VIDEO_WALL_POS_ALIGN: u16 = 64;
pub const MXR_VIDEO_WALL_WIDTH_ALIGN: u16 = 4;
pub const MXR_VIDEO_WALL_MIN_SIZE: u16 = 64;
impl From<mxr_video_wall_window_t> for VideoWallWindow {
fn from(w: mxr_video_wall_window_t) -> Self {
Self {
pos_x: w.pos_x,
pos_y: w.pos_y,
width: w.width,
height: w.height,
raster_w: w.raster_w,
raster_h: w.raster_h,
}
}
}
unsafe fn wall_window(
window: *const mxr_video_wall_window_t,
) -> Result<VideoWallWindow, mxr_result_t> {
match unsafe { window.as_ref() } {
Some(w) => Ok((*w).into()),
None => Err(fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the window pointer is null",
)),
}
}
#[no_mangle]
pub unsafe extern "C" fn mxr_preview_video_wall(
remote: *const mxr_remote_t,
sink: mxr_uid_t,
window: *const mxr_video_wall_window_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
match unsafe { wall_window(window) } {
Ok(w) => from_control(r.remote.preview_video_wall(sink.into(), w)),
Err(code) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_store_video_wall(
remote: *const mxr_remote_t,
sink: mxr_uid_t,
window: *const mxr_video_wall_window_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
match unsafe { wall_window(window) } {
Ok(w) => from_control(r.remote.store_video_wall(sink.into(), w)),
Err(code) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_revert_video_wall(
remote: *const mxr_remote_t,
sink: mxr_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.revert_video_wall(sink.into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_view_mode(
remote: *const mxr_remote_t,
device: mxr_uid_t,
mode: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_view_mode(device.into(), MultiviewerViewMode::from_wire(mode)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_video_source(
remote: *const mxr_remote_t,
device: mxr_uid_t,
screen: u8,
source: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_multiviewer_video_source(
device.into(),
screen,
MultiviewerSource::from_wire(source),
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_audio_source(
remote: *const mxr_remote_t,
device: mxr_uid_t,
source: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_audio_source(device.into(), MultiviewerSource::from_wire(source)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_audio_volume(
remote: *const mxr_remote_t,
device: mxr_uid_t,
volume: u8,
muted: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_audio_volume(device.into(), volume, muted),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_edid_template(
remote: *const mxr_remote_t,
device: mxr_uid_t,
template: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_multiviewer_edid_template(
device.into(),
MultiviewerEdidTemplate::from_wire(template),
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_remote_control(
remote: *const mxr_remote_t,
device: mxr_uid_t,
source: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote.set_multiviewer_remote_control(
device.into(),
MultiviewerSource::from_wire(source),
),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_pip_size(
remote: *const mxr_remote_t,
device: mxr_uid_t,
size: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_pip_size(device.into(), MultiviewerPipSize::from_wire(size)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_pip_position(
remote: *const mxr_remote_t,
device: mxr_uid_t,
position: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_multiviewer_pip_position(
device.into(),
MultiviewerPipPosition::from_wire(position),
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_aspect_ratio(
remote: *const mxr_remote_t,
device: mxr_uid_t,
aspect: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote.set_multiviewer_aspect_ratio(
device.into(),
MultiviewerAspectRatio::from_wire(aspect),
),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_auto_switch(
remote: *const mxr_remote_t,
device: mxr_uid_t,
enable: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_multiviewer_auto_switch(device.into(), enable))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_output_mode(
remote: *const mxr_remote_t,
device: mxr_uid_t,
mode: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_output_mode(device.into(), MultiviewerOutputMode::from_wire(mode)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_output_itc(
remote: *const mxr_remote_t,
device: mxr_uid_t,
mode: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_output_itc(device.into(), MultiviewerItcMode::from_wire(mode)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_hdcp_mode(
remote: *const mxr_remote_t,
device: mxr_uid_t,
mode: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_hdcp_mode(device.into(), MultiviewerHdcpMode::from_wire(mode)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_multiviewer_input_source(
remote: *const mxr_remote_t,
device: mxr_uid_t,
input: u8,
source: mxr_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_multiviewer_input_source(device.into(), input, source.into()),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_multiviewer_auto_route(
remote: *const mxr_remote_t,
device: mxr_uid_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.multiviewer_auto_route(device.into()))
})
}