use crate::{Result, connection::Connection, error::BrlApiError};
use brlapi_sys::*;
use libc::c_char;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Display {
width: u32,
height: u32,
driver_name: String,
model_identifier: String,
}
impl Display {
pub fn from_connection(connection: &Connection) -> Result<Self> {
let (width, height) = Self::query_size(connection)?;
let driver_name = Self::query_driver_name(connection)?;
let model_identifier = Self::query_model_identifier(connection)?;
Ok(Display {
width,
height,
driver_name,
model_identifier,
})
}
pub fn total_cells(&self) -> u32 {
self.width * self.height
}
pub fn is_single_line(&self) -> bool {
self.height == 1
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn driver_name(&self) -> &str {
&self.driver_name
}
pub fn model_identifier(&self) -> &str {
&self.model_identifier
}
fn query_string_from_brlapi<F>(connection: &Connection, query_fn: F) -> Result<String>
where
F: FnOnce(&Connection, *mut c_char, usize) -> i32,
{
let mut buffer = vec![0u8; BRLAPI_MAXNAMELENGTH as usize + 1];
crate::brlapi_call!(query_fn(
connection,
buffer.as_mut_ptr() as *mut c_char,
buffer.len()
))?;
let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len());
String::from_utf8(buffer[..end].to_vec()).map_err(BrlApiError::from)
}
fn query_size(connection: &Connection) -> Result<(u32, u32)> {
let mut width: u32 = 0;
let mut height: u32 = 0;
crate::brlapi_call!(unsafe {
brlapi__getDisplaySize(connection.handle_ptr(), &mut width, &mut height)
})?;
Ok((width, height))
}
fn query_driver_name(connection: &Connection) -> Result<String> {
Self::query_string_from_brlapi(connection, |conn, buffer, len|
unsafe {
brlapi__getDriverName(conn.handle_ptr(), buffer, len)
})
}
fn query_model_identifier(connection: &Connection) -> Result<String> {
Self::query_string_from_brlapi(connection, |conn, buffer, len|
unsafe {
brlapi__getModelIdentifier(conn.handle_ptr(), buffer, len)
})
}
}
impl Connection {
pub fn display_info(&self) -> Result<Display> {
Display::from_connection(self)
}
pub fn display_size(&self) -> Result<(u32, u32)> {
Display::query_size(self)
}
pub fn display_driver(&self) -> Result<String> {
Display::query_driver_name(self)
}
pub fn display_model(&self) -> Result<String> {
Display::query_model_identifier(self)
}
pub fn user_contraction_table(&self) -> Result<String> {
use crate::parameters::{Parameter, ParameterFlags};
let table_result = self
.get_parameter::<String>(Parameter::LiteraryBrailleTable, 0, ParameterFlags::GLOBAL)
.or_else(|_| {
self.get_parameter::<String>(Parameter::LiteraryBrailleTable, 0, ParameterFlags::LOCAL)
});
match table_result {
Ok(table) if !table.trim().is_empty() => {
let table_name = table.trim();
let resolved_table = match table_name {
"en_US" | "en-US" => "en-us-g2.ctb",
"en_GB" | "en-GB" => "en-gb-g2.ctb",
"fr_FR" | "fr-FR" => "fr-bfu-g2.ctb",
"de_DE" | "de-DE" => "de-g2.ctb",
name if name.ends_with(".ctb") => name, _ => {
use liblouis::TableResolver;
match TableResolver::resolve_locale(table_name) {
Ok(resolved) => return Ok(resolved),
Err(_) => "en-us-g2.ctb", }
}
};
Ok(resolved_table.to_string())
},
_ => {
Ok("en-us-g2.ctb".to_string())
}
}
}
pub fn debug_preferences(&self) {
use crate::parameters::{Parameter, ParameterFlags};
println!("BRLTTY Parameter Debug Information:");
println!("===================================");
let show_parameter = |name: &str, param: Parameter, subparam: u64| {
println!("{}:", name);
match self.get_parameter::<String>(param, subparam, ParameterFlags::GLOBAL) {
Ok(value) => println!(" GLOBAL: '{}'", value.trim()),
Err(e) => println!(" GLOBAL: (unable to read - {})", e),
}
match self.get_parameter::<String>(param, subparam, ParameterFlags::LOCAL) {
Ok(value) => println!(" LOCAL: '{}'", value.trim()),
Err(e) => println!(" LOCAL: (unable to read - {})", e),
}
};
show_parameter("Literary braille table", Parameter::LiteraryBrailleTable, 0);
show_parameter("Computer braille table", Parameter::ComputerBrailleTable, 0);
show_parameter("Message locale", Parameter::MessageLocale, 0);
println!("Literary braille mode:");
match self.get_parameter::<bool>(Parameter::LiteraryBraille, 0, ParameterFlags::GLOBAL) {
Ok(enabled) => println!(" GLOBAL: {}", if enabled { "enabled" } else { "disabled" }),
Err(e) => println!(" GLOBAL: (unable to read - {})", e),
}
match self.get_parameter::<bool>(Parameter::LiteraryBraille, 0, ParameterFlags::LOCAL) {
Ok(enabled) => println!(" LOCAL: {}", if enabled { "enabled" } else { "disabled" }),
Err(e) => println!(" LOCAL: (unable to read - {})", e),
}
println!("Client priority:");
match self.get_parameter::<u32>(Parameter::ClientPriority, 0, ParameterFlags::GLOBAL) {
Ok(priority) => println!(" GLOBAL: {}", priority),
Err(e) => println!(" GLOBAL: (unable to read - {})", e),
}
match self.get_parameter::<u32>(Parameter::ClientPriority, 0, ParameterFlags::LOCAL) {
Ok(priority) => println!(" LOCAL: {}", priority),
Err(e) => println!(" LOCAL: (unable to read - {})", e),
}
println!();
match self.user_contraction_table() {
Ok(table) => println!("user_contraction_table() returns: '{}'", table),
Err(e) => println!("user_contraction_table() error: {}", e),
}
println!("===================================");
}
}
impl fmt::Display for Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"A {} {} display, with {} rows and {} columns.",
self.driver_name, self.model_identifier, self.height, self.width
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_display_methods() {
let display = Display {
width: 40,
height: 1,
driver_name: "dummy".to_string(),
model_identifier: "test-model".to_string(),
};
assert_eq!(display.total_cells(), 40);
assert!(display.is_single_line());
assert_eq!(display.dimensions(), (40, 1));
}
#[test]
fn test_display_multi_line() {
let display = Display {
width: 20,
height: 4,
driver_name: "test".to_string(),
model_identifier: "multi-line".to_string(),
};
assert_eq!(display.total_cells(), 80);
assert!(!display.is_single_line());
assert_eq!(display.dimensions(), (20, 4));
}
#[test]
fn test_display_creation() {
use crate::Connection;
let connection_result = Connection::open();
if let Ok(connection) = connection_result {
let _display_result = Display::from_connection(&connection);
}
}
}