use crate::prelude::*;
use crate::proto::Protocol;
use crate::{unsafe_guid, CStr16, Char16, Completion, Result, Status};
use core::fmt;
#[repr(C)]
#[unsafe_guid("387477c2-69c7-11d2-8e39-00a0c969723b")]
#[derive(Protocol)]
pub struct Output<'boot> {
reset: extern "efiapi" fn(this: &Output, extended: bool) -> Status,
output_string: unsafe extern "efiapi" fn(this: &Output, string: *const Char16) -> Status,
test_string: unsafe extern "efiapi" fn(this: &Output, string: *const Char16) -> Status,
query_mode: extern "efiapi" fn(
this: &Output,
mode: usize,
columns: &mut usize,
rows: &mut usize,
) -> Status,
set_mode: extern "efiapi" fn(this: &mut Output, mode: usize) -> Status,
set_attribute: extern "efiapi" fn(this: &mut Output, attribute: usize) -> Status,
clear_screen: extern "efiapi" fn(this: &mut Output) -> Status,
set_cursor_position: extern "efiapi" fn(this: &mut Output, column: usize, row: usize) -> Status,
enable_cursor: extern "efiapi" fn(this: &mut Output, visible: bool) -> Status,
data: &'boot OutputData,
}
impl<'boot> Output<'boot> {
pub fn reset(&mut self, extended: bool) -> Result {
(self.reset)(self, extended).into()
}
pub fn clear(&mut self) -> Result {
(self.clear_screen)(self).into()
}
pub fn output_string(&mut self, string: &CStr16) -> Result {
unsafe { (self.output_string)(self, string.as_ptr()) }.into()
}
pub fn test_string(&mut self, string: &CStr16) -> Result<bool> {
match unsafe { (self.test_string)(self, string.as_ptr()) } {
Status::UNSUPPORTED => Ok(false.into()),
other => other.into_with_val(|| true),
}
}
pub fn modes<'out>(&'out mut self) -> OutputModeIter<'out, 'boot> {
let max = self.data.max_mode as usize;
OutputModeIter {
output: self,
current: 0,
max,
}
}
fn query_mode(&self, index: usize) -> Result<(usize, usize)> {
let (mut columns, mut rows) = (0, 0);
(self.query_mode)(self, index, &mut columns, &mut rows).into_with_val(|| (columns, rows))
}
pub fn current_mode(&self) -> Result<Option<OutputMode>> {
match self.data.mode {
-1 => Ok(None.into()),
n if n > 0 => {
let index = n as usize;
self.query_mode(index)
.map_inner(|dims| Some(OutputMode { index, dims }))
}
_ => unreachable!(),
}
}
pub fn set_mode(&mut self, mode: OutputMode) -> Result {
(self.set_mode)(self, mode.index).into()
}
pub fn cursor_visible(&self) -> bool {
self.data.cursor_visible
}
pub fn enable_cursor(&mut self, visible: bool) -> Result {
(self.enable_cursor)(self, visible).into()
}
pub fn cursor_position(&self) -> (usize, usize) {
let column = self.data.cursor_column;
let row = self.data.cursor_row;
(column as usize, row as usize)
}
pub fn set_cursor_position(&mut self, column: usize, row: usize) -> Result {
(self.set_cursor_position)(self, column, row).into()
}
pub fn set_color(&mut self, foreground: Color, background: Color) -> Result {
let fgc = foreground as usize;
let bgc = background as usize;
assert!(bgc < 8, "An invalid background color was requested");
let attr = ((bgc & 0x7) << 4) | (fgc & 0xF);
(self.set_attribute)(self, attr).into()
}
}
impl<'boot> fmt::Write for Output<'boot> {
fn write_str(&mut self, s: &str) -> fmt::Result {
const BUF_SIZE: usize = 128;
let mut buf = [0u16; BUF_SIZE + 1];
let mut i = 0;
let mut flush_buffer = |buf: &mut [u16], i: &mut usize| {
buf[*i] = 0;
let codes = &buf[..=*i];
*i = 0;
let text = CStr16::from_u16_with_nul(codes).map_err(|_| fmt::Error)?;
self.output_string(text)
.warning_as_error()
.map_err(|_| fmt::Error)
};
let mut add_char = |ch| {
buf[i] = ch;
i += 1;
if i == BUF_SIZE {
flush_buffer(&mut buf, &mut i).map_err(|_| ucs2::Error::BufferOverflow)
} else {
Ok(())
}
};
let add_ch = |ch| {
if ch == '\n' as u16 {
add_char('\r' as u16)?;
}
add_char(ch)
};
ucs2::encode_with(s, add_ch).map_err(|_| fmt::Error)?;
flush_buffer(&mut buf, &mut i)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct OutputMode {
index: usize,
dims: (usize, usize),
}
impl OutputMode {
#[inline]
pub fn index(&self) -> usize {
self.index
}
#[inline]
pub fn columns(&self) -> usize {
self.dims.0
}
#[inline]
pub fn rows(&self) -> usize {
self.dims.1
}
}
pub struct OutputModeIter<'out, 'boot: 'out> {
output: &'out mut Output<'boot>,
current: usize,
max: usize,
}
impl<'out, 'boot> Iterator for OutputModeIter<'out, 'boot> {
type Item = Completion<OutputMode>;
fn next(&mut self) -> Option<Self::Item> {
let index = self.current;
if index < self.max {
self.current += 1;
if let Ok(dims_completion) = self.output.query_mode(index) {
Some(dims_completion.map(|dims| OutputMode { index, dims }))
} else {
self.next()
}
} else {
None
}
}
}
#[derive(Debug)]
#[repr(C)]
struct OutputData {
max_mode: i32,
mode: i32,
attribute: i32,
cursor_column: i32,
cursor_row: i32,
cursor_visible: bool,
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub enum Color {
Black = 0,
Blue,
Green,
Cyan,
Red,
Magenta,
Brown,
LightGray,
DarkGray,
LightBlue,
LightGreen,
LightCyan,
LightRed,
LightMagenta,
Yellow,
White,
}