use std::ffi::c_char;
use std::net::Ipv4Addr;
use std::time::{Duration, UNIX_EPOCH};
use mx_remote::{
DeviceUid, EdidProfile, MultiviewerAspectRatio, MultiviewerEdidTemplate, MultiviewerHdcpMode,
MultiviewerItcMode, MultiviewerOutputMode, MultiviewerPipPosition, MultiviewerPipSize,
MultiviewerSource, MultiviewerViewMode, RcAction, RcKey, UnitNetConfig, V2ipAudioFormat,
V2ipColourSpace, V2ipDeviceSetting, V2ipDeviceSettings, V2ipOutputMode, V2ipPowerSaveSchedule,
V2ipPtpMode, V2ipRoute, V2ipRouteTarget, V2ipTestPattern, V2ipVlan, V2ipVlanFlag,
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};
use crate::subsystems::{
mxr_v2ip_device_settings_t, mxr_v2ip_power_save_t, mxr_v2ip_ptp_settings_t,
mxr_v2ip_test_sync_t, mxr_v2ip_test_tone_t, mxr_v2ip_vlan_t,
};
#[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_locked(
remote: *const mxr_remote_t,
device: mxr_uid_t,
endpoint: u8,
locked: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(
r.remote
.set_audio_endpoint_locked(device.into(), endpoint, locked),
)
})
}
#[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_ping(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.ping(device.into())))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_auto_assign_v2ip_source_addresses(
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.auto_assign_v2ip_source_addresses(device.into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_mesh_time_zone(
remote: *const mxr_remote_t,
zone: *const c_char,
rule: *const c_char,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
match unsafe { (req_str(zone), req_str(rule)) } {
(Ok(zone), Ok(rule)) => from_control(r.remote.set_mesh_time_zone(zone, rule)),
(Err(code), _) | (_, Err(code)) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_clear_mesh_time_zone(remote: *const mxr_remote_t) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| from_control(r.remote.clear_mesh_time_zone()))
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_mesh_time(remote: *const mxr_remote_t, utc: u64) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let time = UNIX_EPOCH.checked_add(Duration::from_secs(utc));
match time {
Some(time) => from_control(r.remote.set_mesh_time(time)),
None => fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the time does not fit the frame",
),
}
})
}
#[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()))
}
pub const MXR_V2IP_COLOUR_RGB: u8 = 0;
pub const MXR_V2IP_COLOUR_YCBCR444: u8 = 1;
pub const MXR_V2IP_COLOUR_YCBCR422: u8 = 2;
pub const MXR_V2IP_COLOUR_YCBCR420: u8 = 3;
pub const MXR_V2IP_SCALING_REFRESH_MIN: u16 = 24;
pub const MXR_V2IP_SCALING_REFRESH_MAX: u16 = 120;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct mxr_v2ip_output_mode_t {
pub svd: u8,
pub depth: u8,
pub colour: u8,
pub refresh: u16,
}
impl From<mxr_v2ip_output_mode_t> for V2ipOutputMode {
fn from(m: mxr_v2ip_output_mode_t) -> Self {
Self {
svd: m.svd,
depth: m.depth,
colour: V2ipColourSpace::from_wire(m.colour),
refresh: m.refresh,
}
}
}
unsafe fn output_mode(mode: *const mxr_v2ip_output_mode_t) -> Result<V2ipOutputMode, mxr_result_t> {
match unsafe { mode.as_ref() } {
Some(m) => Ok((*m).into()),
None => Err(fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the mode pointer is null",
)),
}
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_auto_scaling(
remote: *const mxr_remote_t,
device: mxr_uid_t,
enabled: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_auto_scaling(device.into(), enabled))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_device_setting(
remote: *const mxr_remote_t,
device: mxr_uid_t,
setting: u32,
enabled: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_device_setting(
device.into(),
V2ipDeviceSetting::from_bits(setting),
enabled,
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_ir_profile(
remote: *const mxr_remote_t,
device: mxr_uid_t,
profile: i8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_ir_profile(device.into(), profile))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_auto_power_save(
remote: *const mxr_remote_t,
device: mxr_uid_t,
minutes: u16,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_auto_power_save(device.into(), minutes))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_power_save_schedule(
remote: *const mxr_remote_t,
device: mxr_uid_t,
schedule: *const mxr_v2ip_power_save_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(schedule) = (unsafe { schedule.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the schedule pointer is null",
);
};
from_control(r.remote.set_v2ip_power_save_schedule(
device.into(),
V2ipPowerSaveSchedule {
start: schedule.start,
end: schedule.end,
},
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_all_v2ip_device_settings(
remote: *const mxr_remote_t,
settings: *const mxr_v2ip_device_settings_t,
power_save: *const mxr_v2ip_power_save_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 settings pointer is null",
);
};
let valid = V2ipDeviceSetting::from_bits(settings.valid);
if !(valid & V2ipDeviceSetting::PTP_SETTINGS).is_empty() {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"PTP settings go through mxr_set_all_v2ip_ptp_settings()",
);
}
let needs_power_save = valid.has(V2ipDeviceSetting::AUTO_POWER_SAVE)
|| valid.has(V2ipDeviceSetting::POWER_SAVE_SCHEDULE);
let power_save = match unsafe { power_save.as_ref() } {
Some(p) => *p,
None if needs_power_save => {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the power save pointer is null",
)
}
None => mxr_v2ip_power_save_t {
auto_minutes: 0,
start: [0; 7],
end: [0; 7],
},
};
from_control(r.remote.set_all_v2ip_device_settings(V2ipDeviceSettings {
valid,
flags: V2ipDeviceSetting::from_bits(settings.flags),
ir_profiles: 0,
ir_profile: settings.ir_profile,
ir_profile_sink: settings.ir_profile_sink,
auto_power_save: power_save.auto_minutes,
power_save: V2ipPowerSaveSchedule {
start: power_save.start,
end: power_save.end,
},
..V2ipDeviceSettings::default()
}))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_ptp_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_v2ip_ptp_mode(device.into(), V2ipPtpMode::from_wire(mode)),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_ptp_domain(
remote: *const mxr_remote_t,
device: mxr_uid_t,
domain: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_ptp_domain(device.into(), domain))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_ptp_priority1(
remote: *const mxr_remote_t,
device: mxr_uid_t,
priority1: u8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_ptp_priority1(device.into(), priority1))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_all_v2ip_ptp_settings(
remote: *const mxr_remote_t,
valid: u32,
ptp: *const mxr_v2ip_ptp_settings_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(ptp) = (unsafe { ptp.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the PTP settings pointer is null",
);
};
let valid = V2ipDeviceSetting::from_bits(valid);
if valid.is_empty() || !V2ipDeviceSetting::PTP_SETTINGS.has(valid) {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"only PTP settings are carried here",
);
}
from_control(r.remote.set_all_v2ip_device_settings(V2ipDeviceSettings {
valid,
ptp_mode: V2ipPtpMode::from_wire(ptp.mode),
ptp_domain: ptp.domain,
ptp_priority1: ptp.priority1,
..V2ipDeviceSettings::default()
}))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_clear_unit_status(
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.clear_unit_status(device.into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_upgrade_unit_fpga(
remote: *const mxr_remote_t,
device: mxr_uid_t,
force: bool,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.upgrade_unit_fpga(device.into(), force))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_unit_dhcp(
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.set_unit_dhcp(device.into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_unit_network(
remote: *const mxr_remote_t,
device: mxr_uid_t,
ip: *const c_char,
netmask: *const c_char,
gateway: *const c_char,
dns1: *const c_char,
dns2: *const c_char,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let read = |text: *const c_char, what: &str| -> Result<Ipv4Addr, mxr_result_t> {
let text = unsafe { opt_str(text) }?.unwrap_or_default();
if text.is_empty() {
return Ok(Ipv4Addr::UNSPECIFIED);
}
text.parse().map_err(|_| {
fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
&format!("{what} is not an IPv4 address: {text:?}"),
)
})
};
let network = (|| {
Ok(UnitNetConfig {
ip: read(ip, "ip")?,
netmask: read(netmask, "netmask")?,
gateway: read(gateway, "gateway")?,
dns1: read(dns1, "dns1")?,
dns2: read(dns2, "dns2")?,
})
})();
match network {
Ok(network) => from_control(r.remote.set_unit_network(device.into(), network)),
Err(code) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_confirm_unit_network(
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.confirm_unit_network(device.into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_vlan(
remote: *const mxr_remote_t,
device: mxr_uid_t,
vlan: *const mxr_v2ip_vlan_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(vlan) = (unsafe { vlan.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the VLAN pointer is null",
);
};
from_control(r.remote.set_v2ip_vlan(
device.into(),
V2ipVlan {
flags: V2ipVlanFlag::from_bits(vlan.flags),
device: vlan.device,
port: vlan.port,
uplink: vlan.uplink,
active_uplink: vlan.active_uplink,
revert_s: vlan.revert_s,
},
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_request_v2ip_testcard(
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.request_v2ip_testcard(device.into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_test_pattern(
remote: *const mxr_remote_t,
device: mxr_uid_t,
pattern: u8,
colour: u32,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_test_pattern(
device.into(),
V2ipTestPattern::from_wire(pattern),
colour,
))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_test_tone(
remote: *const mxr_remote_t,
device: mxr_uid_t,
tone: *const mxr_v2ip_test_tone_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(tone) = (unsafe { tone.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the tone pointer is null",
);
};
from_control(r.remote.set_v2ip_test_tone(device.into(), (*tone).into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_test_sync(
remote: *const mxr_remote_t,
device: mxr_uid_t,
sync: *const mxr_v2ip_test_sync_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
let Some(sync) = (unsafe { sync.as_ref() }) else {
return fail(
mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
"the lip-sync pointer is null",
);
};
from_control(r.remote.set_v2ip_test_sync(device.into(), (*sync).into()))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_sink_ir_profile(
remote: *const mxr_remote_t,
device: mxr_uid_t,
profile: i8,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
from_control(r.remote.set_v2ip_sink_ir_profile(device.into(), profile))
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_set_v2ip_output_mode(
remote: *const mxr_remote_t,
device: mxr_uid_t,
mode: *const mxr_v2ip_output_mode_t,
) -> mxr_result_t {
let handle = unsafe { remote.as_ref() };
with(handle, |r| {
match unsafe { output_mode(mode) } {
Ok(m) => from_control(r.remote.set_v2ip_output_mode(device.into(), m)),
Err(code) => code,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mxr_clear_v2ip_output_mode(
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.clear_v2ip_output_mode(device.into()))
})
}
#[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()))
})
}