use crate::{Result, brlapi_call};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum Parameter {
ServerVersion = 0,
ClientPriority = 1,
DriverName = 2,
DriverCode = 3,
DriverVersion = 4,
DeviceModel = 5,
DeviceCellSize = 31,
DisplaySize = 6,
DeviceIdentifier = 7,
DeviceSpeed = 8,
DeviceOnline = 9,
RetainDots = 10,
ComputerBrailleCellSize = 11,
LiteraryBraille = 12,
CursorDots = 13,
CursorBlinkPeriod = 14,
CursorBlinkPercentage = 15,
RenderedCells = 16,
SkipIdenticalLines = 17,
AudibleAlerts = 18,
ClipboardContent = 19,
BoundCommandKeycodes = 20,
CommandKeycodeName = 21,
CommandKeycodeSummary = 22,
DefinedDriverKeycodes = 23,
DriverKeycodeName = 24,
DriverKeycodeSummary = 25,
ComputerBrailleRowsMask = 26,
ComputerBrailleRowCells = 27,
ComputerBrailleTable = 28,
LiteraryBrailleTable = 29,
MessageLocale = 30,
DriverPropertyValue = 32,
}
impl From<Parameter> for u32 {
fn from(param: Parameter) -> Self {
param as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParameterFlags(u32);
impl ParameterFlags {
pub const LOCAL: Self = Self(0x00);
pub const GLOBAL: Self = Self(0x01);
pub const SELF: Self = Self(0x02);
pub const fn from_raw(flags: u32) -> Self {
Self(flags)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn with(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
impl From<ParameterFlags> for brlapi_sys::brlapi_param_flags_t {
fn from(flags: ParameterFlags) -> Self {
flags.0
}
}
pub trait ParameterValue: Sized {
fn from_parameter_data(data: &[u8]) -> Result<Self>;
fn to_parameter_data(&self) -> Result<Vec<u8>>;
}
pub type ParameterBool = bool;
impl ParameterValue for bool {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
if data.is_empty() {
return Ok(false);
}
Ok(data[0] != 0)
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
Ok(vec![if *self { 1 } else { 0 }])
}
}
impl ParameterValue for u8 {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
if data.is_empty() {
return Err(crate::BrlApiError::invalid_parameter_data(
"u8 requires 1 byte",
));
}
Ok(data[0])
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
Ok(vec![*self])
}
}
impl ParameterValue for u32 {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
if data.len() < 4 {
return Err(crate::BrlApiError::invalid_parameter_data(
"u32 requires 4 bytes",
));
}
Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]]))
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
Ok(self.to_le_bytes().to_vec())
}
}
impl ParameterValue for u64 {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
if data.len() < 8 {
return Err(crate::BrlApiError::invalid_parameter_data(
"u64 requires 8 bytes",
));
}
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&data[0..8]);
Ok(u64::from_le_bytes(bytes))
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
Ok(self.to_le_bytes().to_vec())
}
}
impl ParameterValue for String {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
let len = data.iter().position(|&b| b == 0).unwrap_or(data.len());
std::str::from_utf8(&data[0..len])
.map(|s| s.to_owned())
.map_err(|_| crate::BrlApiError::invalid_parameter_data("Invalid UTF-8 string"))
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
let mut data = self.as_bytes().to_vec();
data.push(0); Ok(data)
}
}
impl ParameterValue for (u32, u32) {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
if data.len() < 8 {
return Err(crate::BrlApiError::invalid_parameter_data(
"Display size requires 8 bytes",
));
}
let width = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
let height = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
Ok((width, height))
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
let mut data = Vec::with_capacity(8);
data.extend_from_slice(&self.0.to_le_bytes());
data.extend_from_slice(&self.1.to_le_bytes());
Ok(data)
}
}
impl ParameterValue for Vec<u8> {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
Ok(data.to_vec())
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
Ok(self.clone())
}
}
impl ParameterValue for Vec<u64> {
fn from_parameter_data(data: &[u8]) -> Result<Self> {
if data.len() % 8 != 0 {
return Err(crate::BrlApiError::invalid_parameter_data(
"Vec<u64> requires multiple of 8 bytes",
));
}
let mut result = Vec::with_capacity(data.len() / 8);
for chunk in data.chunks_exact(8) {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(chunk);
result.push(u64::from_le_bytes(bytes));
}
Ok(result)
}
fn to_parameter_data(&self) -> Result<Vec<u8>> {
let mut data = Vec::with_capacity(self.len() * 8);
for &value in self {
data.extend_from_slice(&value.to_le_bytes());
}
Ok(data)
}
}
pub mod constants {
pub const CLIENT_PRIORITY_DEFAULT: u32 = 50;
pub const CLIENT_PRIORITY_DISABLE: u32 = 0;
}
impl crate::Connection {
pub fn get_parameter<T: ParameterValue>(
&self,
parameter: Parameter,
subparam: u64,
flags: ParameterFlags,
) -> Result<T> {
let size = brlapi_call!(unsafe {
brlapi_sys::brlapi__getParameter(
self.handle_ptr(),
parameter.into(),
subparam,
flags.into(),
std::ptr::null_mut(),
0,
)
})?;
if size < 0 {
return Err(crate::BrlApiError::from_brlapi_error());
}
let size = size as usize;
let mut buffer = vec![0u8; size];
let actual_size = brlapi_call!(unsafe {
brlapi_sys::brlapi__getParameter(
self.handle_ptr(),
parameter.into(),
subparam,
flags.into(),
buffer.as_mut_ptr() as *mut std::ffi::c_void,
size,
)
})?;
if actual_size < 0 {
return Err(crate::BrlApiError::from_brlapi_error());
}
buffer.truncate(actual_size as usize);
T::from_parameter_data(&buffer)
}
pub fn set_parameter<T: ParameterValue>(
&self,
parameter: Parameter,
subparam: u64,
flags: ParameterFlags,
value: &T,
) -> Result<()> {
let data = value.to_parameter_data()?;
brlapi_call!(unsafe {
brlapi_sys::brlapi__setParameter(
self.handle_ptr(),
parameter.into(),
subparam,
flags.into(),
data.as_ptr() as *const std::ffi::c_void,
data.len(),
)
})?;
Ok(())
}
pub fn get_parameter_alloc<T: ParameterValue>(
&self,
parameter: Parameter,
subparam: u64,
flags: ParameterFlags,
) -> Result<T> {
let mut len: usize = 0;
let ptr = unsafe {
brlapi_sys::brlapi__getParameterAlloc(
self.handle_ptr(),
parameter.into(),
subparam,
flags.into(),
&mut len,
)
};
if ptr.is_null() {
return Err(crate::BrlApiError::from_brlapi_error());
}
let data = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
let result = T::from_parameter_data(data);
unsafe {
libc::free(ptr);
}
result
}
}
impl crate::BrlApiError {
pub fn invalid_parameter_data(msg: &str) -> Self {
crate::BrlApiError::Custom {
message: format!("Invalid parameter data: {}", msg),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parameter_value_bool() {
assert!(
bool::from_parameter_data(&[1])
.expect("Bool parameter with value 1 should parse as true")
);
assert!(
!bool::from_parameter_data(&[0])
.expect("Bool parameter with value 0 should parse as false")
);
assert!(
!bool::from_parameter_data(&[]).expect("Empty bool parameter should parse as false")
);
assert_eq!(
true.to_parameter_data()
.expect("True should convert to parameter data [1]"),
vec![1]
);
assert_eq!(
false
.to_parameter_data()
.expect("False should convert to parameter data [0]"),
vec![0]
);
}
#[test]
fn test_parameter_value_u32() {
let value = 0x12345678u32;
let data = value
.to_parameter_data()
.expect("u32 value should convert to parameter data");
assert_eq!(
u32::from_parameter_data(&data)
.expect("Parameter data should convert back to original u32 value"),
value
);
}
#[test]
fn test_parameter_value_string() {
let s = "Hello, BrlAPI!";
let data = s
.to_string()
.to_parameter_data()
.expect("String should convert to parameter data");
assert_eq!(
String::from_parameter_data(&data)
.expect("Parameter data should convert back to original string"),
s
);
assert_eq!(data.last(), Some(&0));
}
#[test]
fn test_parameter_value_display_size() {
let size = (80u32, 25u32);
let data = size
.to_parameter_data()
.expect("Display size tuple should convert to parameter data");
assert_eq!(
<(u32, u32)>::from_parameter_data(&data)
.expect("Parameter data should convert back to original display size"),
size
);
}
#[test]
fn test_parameter_flags() {
let flags = ParameterFlags::LOCAL.with(ParameterFlags::SELF);
assert_eq!(flags.raw(), 0x02);
let global = ParameterFlags::GLOBAL;
assert_eq!(global.raw(), 0x01);
}
}
pub mod polling {
use super::*;
use crate::Connection;
use std::time::{Duration, Instant};
pub struct ParameterMonitor<T> {
connection: Connection,
parameter: Parameter,
subparam: u64,
flags: ParameterFlags,
last_value: Option<T>,
last_check: Instant,
check_interval: Duration,
}
impl<T> ParameterMonitor<T>
where
T: ParameterValue + Clone + PartialEq,
{
pub fn new(
connection: Connection,
parameter: Parameter,
subparam: u64,
flags: ParameterFlags,
check_interval: Duration,
) -> Self {
Self {
connection,
parameter,
subparam,
flags,
last_value: None,
last_check: Instant::now(),
check_interval,
}
}
pub fn check_for_change(&mut self) -> Option<T> {
if self.last_check.elapsed() < self.check_interval {
return None;
}
self.last_check = Instant::now();
match self
.connection
.get_parameter(self.parameter, self.subparam, self.flags)
{
Ok(new_value) => {
if let Some(ref last) = self.last_value {
if *last == new_value {
return None; }
}
self.last_value = Some(new_value.clone());
Some(new_value)
}
Err(_) => None, }
}
pub fn current_value(&self) -> Option<&T> {
self.last_value.as_ref()
}
pub fn force_check(&mut self) -> Option<T> {
self.last_check = Instant::now() - self.check_interval;
self.check_for_change()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScreenReaderHint {
Inactive,
Possible,
Unknown,
}
pub fn detect_screen_reader_activity(connection: &Connection) -> ScreenReaderHint {
match connection.display_driver() {
Ok(_) => {
ScreenReaderHint::Inactive
}
Err(_) => {
ScreenReaderHint::Unknown
}
}
}
pub fn monitor_display_size(connection: Connection) -> ParameterMonitor<(u32, u32)> {
ParameterMonitor::new(
connection,
Parameter::DisplaySize,
0,
ParameterFlags::LOCAL,
Duration::from_secs(1), )
}
}