uefi/proto/console/text/
output.rs1use crate::proto::unsafe_protocol;
4use crate::{CStr16, Result, ResultExt, Status, StatusExt};
5use core::fmt;
6use uefi_raw::protocol::console::{SimpleTextOutputMode, SimpleTextOutputProtocol};
7
8#[derive(Debug)]
27#[repr(transparent)]
28#[unsafe_protocol(SimpleTextOutputProtocol::GUID)]
29pub struct Output(SimpleTextOutputProtocol);
30
31impl Output {
32 pub fn reset(&mut self, extended: bool) -> Result {
34 unsafe { (self.0.reset)(&mut self.0, extended.into()) }.to_result()
35 }
36
37 pub fn clear(&mut self) -> Result {
42 unsafe { (self.0.clear_screen)(&mut self.0) }.to_result()
43 }
44
45 pub fn output_string(&mut self, string: &CStr16) -> Result {
47 unsafe { (self.0.output_string)(&mut self.0, string.as_ptr().cast()) }.to_result()
48 }
49
50 pub fn output_string_lossy(&mut self, string: &CStr16) -> Result {
54 self.output_string(string).handle_warning(|err| {
55 if err.status() == Status::WARN_UNKNOWN_GLYPH {
56 Ok(())
57 } else {
58 Err(err)
59 }
60 })
61 }
62
63 pub fn test_string(&mut self, string: &CStr16) -> Result<bool> {
68 match unsafe { (self.0.test_string)(&mut self.0, string.as_ptr().cast()) } {
69 Status::UNSUPPORTED => Ok(false),
70 other => other.to_result_with_val(|| true),
71 }
72 }
73
74 pub const fn modes(&mut self) -> OutputModeIter<'_> {
76 let max = self.data().max_mode as usize;
77 OutputModeIter {
78 output: self,
79 current: 0,
80 max,
81 }
82 }
83
84 fn query_mode(&self, index: usize) -> Result<(usize, usize)> {
95 let (mut columns, mut rows) = (0, 0);
96 let this: *const _ = &self.0;
97 unsafe { (self.0.query_mode)(this.cast_mut(), index, &mut columns, &mut rows) }
98 .to_result_with_val(|| (columns, rows))
99 }
100
101 pub fn current_mode(&self) -> Result<Option<OutputMode>> {
103 match self.data().mode {
104 -1 => Ok(None),
105 n if n >= 0 => {
106 let index = n as usize;
107 self.query_mode(index)
108 .map(|dims| Some(OutputMode { index, dims }))
109 }
110 _ => unreachable!(),
111 }
112 }
113
114 pub fn set_mode(&mut self, mode: OutputMode) -> Result {
116 unsafe { (self.0.set_mode)(&mut self.0, mode.index) }.to_result()
117 }
118
119 #[must_use]
121 pub fn cursor_visible(&self) -> bool {
122 self.data().cursor_visible.into()
123 }
124
125 pub fn enable_cursor(&mut self, visible: bool) -> Result {
130 unsafe { (self.0.enable_cursor)(&mut self.0, visible.into()) }.to_result()
131 }
132
133 #[must_use]
135 pub const fn cursor_position(&self) -> (usize, usize) {
136 let column = self.data().cursor_column;
137 let row = self.data().cursor_row;
138 (column as usize, row as usize)
139 }
140
141 pub fn set_cursor_position(&mut self, column: usize, row: usize) -> Result {
145 unsafe { (self.0.set_cursor_position)(&mut self.0, column, row) }.to_result()
146 }
147
148 pub fn set_color(&mut self, foreground: Color, background: Color) -> Result {
153 let fgc = foreground as usize;
154 let bgc = background as usize;
155
156 assert!(bgc < 8, "An invalid background color was requested");
157
158 let attr = ((bgc & 0x7) << 4) | (fgc & 0xF);
159 unsafe { (self.0.set_attribute)(&mut self.0, attr) }.to_result()
160 }
161
162 const fn data(&self) -> &SimpleTextOutputMode {
165 let mode = self.0.mode.cast_const();
167 unsafe { &*mode }
168 }
169}
170
171impl fmt::Write for Output {
172 fn write_str(&mut self, s: &str) -> fmt::Result {
173 const BUF_SIZE: usize = 128;
175 let mut buf = [0u16; BUF_SIZE + 1];
177
178 let mut i = 0;
179
180 let mut flush_buffer = |buf: &mut [u16], i: &mut usize| {
182 buf[*i] = 0;
183 let codes = &buf[..=*i];
184 *i = 0;
185
186 let text = CStr16::from_u16_with_nul(codes).map_err(|_| fmt::Error)?;
187
188 self.output_string(text).map_err(|_| fmt::Error)
189 };
190
191 let mut add_char = |ch| {
194 buf[i] = ch;
197 i += 1;
198
199 if i == BUF_SIZE {
200 flush_buffer(&mut buf, &mut i).map_err(|_| ucs2::Error::BufferOverflow)
201 } else {
202 Ok(())
203 }
204 };
205
206 let add_ch = |ch| {
208 if ch == '\n' as u16 {
209 add_char('\r' as u16)?;
210 }
211 add_char(ch)
212 };
213
214 ucs2::encode_with(s, add_ch).map_err(|_| fmt::Error)?;
216
217 flush_buffer(&mut buf, &mut i)
219 }
220}
221
222#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
224pub struct OutputMode {
225 index: usize,
226 dims: (usize, usize),
227}
228
229impl OutputMode {
230 #[inline]
232 #[must_use]
233 pub const fn index(&self) -> usize {
234 self.index
235 }
236
237 #[inline]
239 #[must_use]
240 pub const fn columns(&self) -> usize {
241 self.dims.0
242 }
243
244 #[inline]
246 #[must_use]
247 pub const fn rows(&self) -> usize {
248 self.dims.1
249 }
250}
251
252#[derive(Debug)]
254pub struct OutputModeIter<'out> {
255 output: &'out mut Output,
256 current: usize,
257 max: usize,
258}
259
260impl Iterator for OutputModeIter<'_> {
261 type Item = OutputMode;
262
263 fn next(&mut self) -> Option<Self::Item> {
264 let index = self.current;
265 if index < self.max {
266 self.current += 1;
267
268 if let Ok(dims) = self.output.query_mode(index) {
269 Some(OutputMode { index, dims })
270 } else {
271 self.next()
272 }
273 } else {
274 None
275 }
276 }
277}
278
279#[allow(missing_docs)]
284#[derive(Debug, Copy, Clone)]
285pub enum Color {
286 Black = 0,
287 Blue,
288 Green,
289 Cyan,
290 Red,
291 Magenta,
292 Brown,
293 LightGray,
294 DarkGray,
295 LightBlue,
296 LightGreen,
297 LightCyan,
298 LightRed,
299 LightMagenta,
300 Yellow,
301 White,
302}