feat(display): DrawText with built-in Spleen 8x16 font (BSD-2)
This commit is contained in:
@@ -37,3 +37,16 @@ pub fn get_dimensions(ep: CapSlot) -> (Dimension, Dimension) {
|
||||
(Dimension::new(0), Dimension::new(0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws `s` (up to `TEXT_BYTES` per call, more in several) from `(x, y)`.
|
||||
pub fn draw_text(ep: CapSlot, x: ScreenCoord, y: ScreenCoord, fg: Color, bg: Color, s: &[u8]) {
|
||||
use types::display::text::{TEXT_BYTES, Text};
|
||||
for (i, chunk) in s.chunks(TEXT_BYTES).enumerate() {
|
||||
let x = ScreenCoord::new(x.raw().saturating_add((i * TEXT_BYTES * CELL_WIDTH) as u16));
|
||||
let _ = syscall::request(ep, Text::new(x, y, fg, bg, chunk).words());
|
||||
}
|
||||
}
|
||||
|
||||
/// The display server's character cell, in pixels.
|
||||
pub const CELL_WIDTH: usize = 8;
|
||||
pub const CELL_HEIGHT: usize = 16;
|
||||
|
||||
24
crates/servers/display-server/src/font/LICENSE-spleen
Normal file
24
crates/servers/display-server/src/font/LICENSE-spleen
Normal file
@@ -0,0 +1,24 @@
|
||||
Copyright (c) 2018-2024, Frederic Cambus
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
114
crates/servers/display-server/src/font/mod.rs
Normal file
114
crates/servers/display-server/src/font/mod.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
//! The display server's one font: Spleen 8x16 (BSD-2-Clause, Frederic
|
||||
//! Cambus; see LICENSE-spleen), printable ASCII only. Generated from
|
||||
//! spleen-8x16.bdf; each glyph is 16 rows, bit 7 the leftmost pixel.
|
||||
|
||||
pub const WIDTH: u16 = 8;
|
||||
pub const HEIGHT: u16 = 16;
|
||||
|
||||
const FIRST: u8 = b' ';
|
||||
const LAST: u8 = b'~';
|
||||
|
||||
/// The glyph for `c`; anything unprintable shows as `?`.
|
||||
pub fn glyph(c: u8) -> &'static [u8; 16] {
|
||||
let c = if (FIRST..=LAST).contains(&c) { c } else { b'?' };
|
||||
&GLYPHS[usize::from(c - FIRST)]
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
static GLYPHS: [[u8; 16]; 95] = [
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // ' '
|
||||
[0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00], // '!'
|
||||
[0x00, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '"'
|
||||
[0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00], // '#'
|
||||
[0x00, 0x10, 0x7e, 0xd0, 0xd0, 0xd0, 0x7c, 0x16, 0x16, 0x16, 0x16, 0xfc, 0x10, 0x00, 0x00, 0x00], // '$'
|
||||
[0x00, 0x00, 0x06, 0x66, 0x6c, 0x0c, 0x18, 0x18, 0x30, 0x36, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00], // '%'
|
||||
[0x00, 0x00, 0x38, 0x6c, 0x6c, 0x6c, 0x38, 0x70, 0xda, 0xcc, 0xcc, 0x7a, 0x00, 0x00, 0x00, 0x00], // '&'
|
||||
[0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // "'"
|
||||
[0x00, 0x0e, 0x18, 0x30, 0x30, 0x60, 0x60, 0x60, 0x60, 0x30, 0x30, 0x18, 0x0e, 0x00, 0x00, 0x00], // '('
|
||||
[0x00, 0x70, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x70, 0x00, 0x00, 0x00], // ')'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0x18, 0xff, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00], // '*'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '+'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00], // ','
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '-'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00], // '.'
|
||||
[0x00, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00], // '/'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // '0'
|
||||
[0x00, 0x00, 0x18, 0x38, 0x78, 0x58, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00], // '1'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00], // '2'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // '3'
|
||||
[0x00, 0x00, 0xc0, 0xc0, 0xcc, 0xcc, 0xcc, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00], // '4'
|
||||
[0x00, 0x00, 0xfe, 0xc6, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // '5'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // '6'
|
||||
[0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00], // '7'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // '8'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // '9'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00], // ':'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00], // ';'
|
||||
[0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00], // '<'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '='
|
||||
[0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00], // '>'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00], // '?'
|
||||
[0x00, 0x00, 0x00, 0x7c, 0xc2, 0xda, 0xda, 0xda, 0xda, 0xde, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00], // '@'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'A'
|
||||
[0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00], // 'B'
|
||||
[0x00, 0x00, 0x7e, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'C'
|
||||
[0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00], // 'D'
|
||||
[0x00, 0x00, 0x7e, 0xc0, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'E'
|
||||
[0x00, 0x00, 0x7e, 0xc0, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00], // 'F'
|
||||
[0x00, 0x00, 0x7e, 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'G'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'H'
|
||||
[0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'I'
|
||||
[0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf0, 0x00, 0x00, 0x00, 0x00], // 'J'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'K'
|
||||
[0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'L'
|
||||
[0x00, 0x00, 0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'M'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xd6, 0xd6, 0xce, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'N'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // 'O'
|
||||
[0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00], // 'P'
|
||||
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0x7c, 0x18, 0x0c, 0x00, 0x00], // 'Q'
|
||||
[0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'R'
|
||||
[0x00, 0x00, 0x7e, 0xc0, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0x06, 0x06, 0xfc, 0x00, 0x00, 0x00, 0x00], // 'S'
|
||||
[0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00], // 'T'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'U'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00], // 'V'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'W'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'X'
|
||||
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x06, 0xfc, 0x00, 0x00, 0x00, 0x00], // 'Y'
|
||||
[0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00], // 'Z'
|
||||
[0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00, 0x00, 0x00], // '['
|
||||
[0x00, 0xc0, 0xc0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00], // '\\'
|
||||
[0x00, 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00, 0x00, 0x00], // ']'
|
||||
[0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '^'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00], // '_'
|
||||
[0x00, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '`'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'a'
|
||||
[0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00], // 'b'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'c'
|
||||
[0x00, 0x00, 0x06, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'd'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'e'
|
||||
[0x00, 0x00, 0x1e, 0x30, 0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00], // 'f'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06, 0x06, 0xfc, 0x00], // 'g'
|
||||
[0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'h'
|
||||
[0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1c, 0x00, 0x00, 0x00, 0x00], // 'i'
|
||||
[0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00], // 'j'
|
||||
[0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'k'
|
||||
[0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00], // 'l'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'm'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'n'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], // 'o'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00], // 'p'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x00], // 'q'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00], // 'r'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xfc, 0x00, 0x00, 0x00, 0x00], // 's'
|
||||
[0x00, 0x00, 0x30, 0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00], // 't'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00], // 'u'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00], // 'v'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0x6e, 0x00, 0x00, 0x00, 0x00], // 'w'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], // 'x'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0xfc, 0x00], // 'y'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xfe, 0x00, 0x00, 0x00, 0x00], // 'z'
|
||||
[0x00, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00], // '{'
|
||||
[0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00], // '|'
|
||||
[0x00, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00], // '}'
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x7e, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // '~'
|
||||
];
|
||||
@@ -1,5 +1,6 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod font;
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use server_framework::{Decode, Encode};
|
||||
use types::display::text::Text;
|
||||
use types::display::{Color, Dimension, ScreenCoord};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -7,6 +8,7 @@ pub enum DisplayRequest {
|
||||
Clear { color: Color },
|
||||
Flush,
|
||||
GetDimensions,
|
||||
DrawText(Text),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -27,6 +29,7 @@ impl Encode for DisplayRequest {
|
||||
DisplayRequest::Clear { color } => [1, color.raw() as usize, 0, 0],
|
||||
DisplayRequest::Flush => [2, 0, 0, 0],
|
||||
DisplayRequest::GetDimensions => [3, 0, 0, 0],
|
||||
DisplayRequest::DrawText(t) => t.words(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +48,7 @@ impl Decode for DisplayRequest {
|
||||
1 => Some(DisplayRequest::Clear { color: Color::from_raw(words[1] as u32) }),
|
||||
2 => Some(DisplayRequest::Flush),
|
||||
3 => Some(DisplayRequest::GetDimensions),
|
||||
_ => None,
|
||||
_ => Text::from_words(words).map(DisplayRequest::DrawText),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ use server_framework::Server;
|
||||
|
||||
use crate::protocol::{DisplayRequest, DisplayResponse};
|
||||
use port_framebuffer::Framebuffer;
|
||||
use types::display::Dimension;
|
||||
use types::display::text::Text;
|
||||
use types::display::{Dimension, ScreenCoord};
|
||||
|
||||
use crate::font;
|
||||
|
||||
pub struct DisplayServer<F: Framebuffer> {
|
||||
fb: F,
|
||||
@@ -16,6 +19,28 @@ impl<F: Framebuffer> DisplayServer<F> {
|
||||
pub fn framebuffer(&self) -> &F {
|
||||
&self.fb
|
||||
}
|
||||
|
||||
/// Draws each cell whole, background too, so text overwrites what was
|
||||
/// there; cells past the screen edge are skipped.
|
||||
fn draw_text(&mut self, t: &Text) {
|
||||
let (fw, fh) = (self.fb.width().raw(), self.fb.height().raw());
|
||||
let (x0, y0) = (t.x.raw(), t.y.raw());
|
||||
if y0.saturating_add(font::HEIGHT) > fh {
|
||||
return;
|
||||
}
|
||||
for (i, c) in t.chars().iter().enumerate() {
|
||||
let Some(cx) = x0.checked_add(i as u16 * font::WIDTH).filter(|cx| cx + font::WIDTH <= fw) else {
|
||||
return;
|
||||
};
|
||||
for (row, bits) in font::glyph(*c).iter().enumerate() {
|
||||
for col in 0..font::WIDTH {
|
||||
let on = bits & (0x80 >> col) != 0;
|
||||
let (x, y) = (ScreenCoord::new(cx + col), ScreenCoord::new(y0 + row as u16));
|
||||
self.fb.write_pixel(x, y, if on { t.fg } else { t.bg });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Framebuffer> Server for DisplayServer<F> {
|
||||
@@ -44,6 +69,10 @@ impl<F: Framebuffer> Server for DisplayServer<F> {
|
||||
self.fb.flush();
|
||||
DisplayResponse::Ok
|
||||
}
|
||||
DisplayRequest::DrawText(t) => {
|
||||
self.draw_text(&t);
|
||||
DisplayResponse::Ok
|
||||
}
|
||||
DisplayRequest::GetDimensions => DisplayResponse::Dimensions {
|
||||
width: self.fb.width(),
|
||||
height: self.fb.height(),
|
||||
|
||||
@@ -138,3 +138,32 @@ fn an_off_screen_rect_draws_nothing() {
|
||||
}
|
||||
assert_eq!(server.framebuffer().pixel_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_draws_whole_glyph_cells() {
|
||||
use types::display::text::Text;
|
||||
let mut server = test_server();
|
||||
let (fg, bg) = (Color::rgb(255, 255, 255), Color::rgb(0, 0, 0));
|
||||
let t = Text::new(ScreenCoord::new(8), ScreenCoord::new(16), fg, bg, b"A");
|
||||
assert_eq!(server.handle(DisplayRequest::DrawText(t)), DisplayResponse::Ok);
|
||||
// One 8x16 cell, written row by row.
|
||||
assert_eq!(server.framebuffer().pixel_count(), 128);
|
||||
// 'A' row 2 is 0x7c: column 0 off, column 1 on.
|
||||
let at = |i: usize| server.framebuffer().pixel_at(i);
|
||||
assert_eq!(at(16), (ScreenCoord::new(8), ScreenCoord::new(18), bg));
|
||||
assert_eq!(at(17), (ScreenCoord::new(9), ScreenCoord::new(18), fg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_past_the_edge_is_cut_not_wrapped() {
|
||||
use types::display::text::Text;
|
||||
let mut server = test_server();
|
||||
let c = Color::rgb(1, 1, 1);
|
||||
// 320 wide: from x=304 only two cells fit.
|
||||
let t = Text::new(ScreenCoord::new(304), ScreenCoord::new(0), c, c, b"abc");
|
||||
server.handle(DisplayRequest::DrawText(t));
|
||||
assert_eq!(server.framebuffer().pixel_count(), 256);
|
||||
let low = Text::new(ScreenCoord::new(0), ScreenCoord::new(230), c, c, b"a");
|
||||
server.handle(DisplayRequest::DrawText(low));
|
||||
assert_eq!(server.framebuffer().pixel_count(), 256);
|
||||
}
|
||||
|
||||
@@ -48,3 +48,78 @@ impl Dimension {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// `DrawText`, shared by the display server and `libkos::display`: up to
|
||||
/// `TEXT_BYTES` characters at a pixel position, each cell `fg` on `bg`.
|
||||
/// Words: `[TEXT | len << 8 | fg << 16, x | y << 16 | bg << 32, bytes 0..8, 8..16]`.
|
||||
pub mod text {
|
||||
use super::{Color, ScreenCoord};
|
||||
|
||||
pub const TEXT: usize = 4;
|
||||
pub const TEXT_BYTES: usize = 16;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Text {
|
||||
pub x: ScreenCoord,
|
||||
pub y: ScreenCoord,
|
||||
pub fg: Color,
|
||||
pub bg: Color,
|
||||
pub bytes: [u8; TEXT_BYTES],
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
/// `s` past `TEXT_BYTES` is cut off.
|
||||
pub fn new(x: ScreenCoord, y: ScreenCoord, fg: Color, bg: Color, s: &[u8]) -> Self {
|
||||
let len = s.len().min(TEXT_BYTES);
|
||||
let mut bytes = [0u8; TEXT_BYTES];
|
||||
bytes[..len].copy_from_slice(&s[..len]);
|
||||
Self { x, y, fg, bg, bytes, len }
|
||||
}
|
||||
|
||||
pub fn chars(&self) -> &[u8] {
|
||||
&self.bytes[..self.len.min(TEXT_BYTES)]
|
||||
}
|
||||
|
||||
pub fn words(&self) -> [usize; 4] {
|
||||
let (lo, hi) = self.bytes.as_chunks::<8>().0.split_at(1);
|
||||
[
|
||||
TEXT | self.len << 8 | (self.fg.raw() as usize) << 16,
|
||||
self.x.raw() as usize | (self.y.raw() as usize) << 16 | (self.bg.raw() as usize) << 32,
|
||||
u64::from_le_bytes(lo[0]) as usize,
|
||||
u64::from_le_bytes(hi[0]) as usize,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn from_words(w: [usize; 4]) -> Option<Self> {
|
||||
let len = (w[0] >> 8) & 0xFF;
|
||||
if w[0] & 0xFF != TEXT || len > TEXT_BYTES {
|
||||
return None;
|
||||
}
|
||||
let mut bytes = [0u8; TEXT_BYTES];
|
||||
for (b, word) in bytes.as_chunks_mut::<8>().0.iter_mut().zip(&w[2..]) {
|
||||
*b = (*word as u64).to_le_bytes();
|
||||
}
|
||||
Some(Self {
|
||||
x: ScreenCoord::new((w[1] & 0xFFFF) as u16),
|
||||
y: ScreenCoord::new(((w[1] >> 16) & 0xFFFF) as u16),
|
||||
fg: Color::from_raw((w[0] >> 16) as u32),
|
||||
bg: Color::from_raw((w[1] >> 32) as u32),
|
||||
bytes,
|
||||
len,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn text_round_trips() {
|
||||
let t = Text::new(ScreenCoord::new(1272), ScreenCoord::new(784), Color::rgb(1, 2, 3), Color::rgb(4, 5, 6), b"$ ls -la /home/x");
|
||||
assert_eq!(Text::from_words(t.words()), Some(t));
|
||||
assert_eq!(Text::new(t.x, t.y, t.fg, t.bg, &[b'a'; 40]).len, TEXT_BYTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user