Compare commits

...

13 Commits

Author SHA1 Message Date
Simon Glass
65ca0514ea vidconsole: Avoid kerning against an unrelated character
When the cursor position changes, kerning should not be used for the
next character, since it can make the first displayed character shuffle
left or right a bit.

Clear the kern character when setting the position.

Series-to: u-boot
Cover-letter:
video: Enhancements related to truetype and console
This series includes some precursor patches needed for forthcoming expo
enhancements.

- truetype support for multiple lines
- make white-on-black a runtime option
- support drawing a rectangle

This series was split out from schd2
END

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:43 +13:00
Simon Glass
1473930f94 video: Add a function to draw a rectangle
Provide a way to draw an unfilled box of a certain width. This is useful
for grouping menu items together.

Add a comment showing how to see the copy-framebuffer, for testing.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
b4a9314445 video: Allow console output to be silenced
When using expo we want to be able to control the information on the
display and avoid other messages (such as USB scanning) appearing.

Add a 'quiet' flag for the console, to help with this.

The test is a little messy since stdio is still using the original
vidconsole create on start-up. So take care to use the same.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
1a8d4e4f47 test: video: Export the video-checking functions
We want to check the display contents in expo tests, so move the two
needed functions to a new header file.

Rename them to have a video_ prefix.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
58d2e7d4c7 video: Add a way to write a partial string to the console
When writing multiple lines of text we need to be able to control which
text goes on each line. Add a new vidconsole_put_stringn() function to
help with this.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
d00779e5f5 video: truetype: Support a limit on the width of a line
Expo needs to be able to word-wrap lines so that they are displayed as
the user expects. Add a limit on the width of each line and support this
in the measurement algorithm.

Add a log category to truetype while we are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
68530f31bb video: truetype: Support newlines in the measured string
It is useful to be able to embed newline characters in the string and
have the text measured into multiple lines. Add support for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
05a0eb4954 video: truetype: Fill in the measured line
Create a measured line for the (single) line of text.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
48a36a5f4b video: Begin support for measuring multiple lines of text
Update the vidconsole API so that measure() can measure multiple lines
of text. This will make it easier to implement multi-line fields in
expo.

Tidy up the function comments while we are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
06a8673fa4 video: Add a test for font measurement
Add a simple test which measures a line of text using a Truetype font.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
a3c5771383 sandbox: Select white-on-black
Use white on black for the expo menu as it is easier on the eyes.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
3ae3b13f55 video: Make white-on-black a video-device property
The CONFIG_WHITE_ON_BLACK setting is hard-coded at build-time. It is
useful to be able to control this when showing menus.

Create a property to hold this information, using the CONFIG as the
initial value.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
Simon Glass
08c45aea8a console: Support a format string for stderr output
Add a console_printf_select_stderr() function so that it is not
necessary for the caller to process the format string.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-04-02 04:08:12 +13:00
12 changed files with 594 additions and 141 deletions

View File

@@ -130,6 +130,7 @@
font-size = <30>;
menu-inset = <3>;
menuitem-gap-y = <1>;
white-on-black;
};
cedit-theme {

View File

@@ -194,7 +194,7 @@ int expo_render(struct expo *exp)
u32 colour;
int ret;
back = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK) ? VID_BLACK : VID_WHITE;
back = vid_priv->white_on_black ? VID_BLACK : VID_WHITE;
colour = video_index_to_colour(vid_priv, back);
ret = video_fill(dev, colour);
if (ret)

View File

@@ -298,7 +298,7 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
}
ret = vidconsole_measure(scn->expo->cons, txt->font_name,
txt->font_size, str, &bbox);
txt->font_size, str, -1, &bbox, NULL);
if (ret)
return log_msg_ret("mea", ret);
if (widthp)
@@ -330,8 +330,9 @@ static void scene_render_background(struct scene_obj *obj, bool box_only)
enum colour_idx fore, back;
uint inset = theme->menu_inset;
vid_priv = dev_get_uclass_priv(dev);
/* draw a background for the object */
if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
if (vid_priv->white_on_black) {
fore = VID_DARK_GREY;
back = VID_WHITE;
} else {
@@ -344,7 +345,6 @@ static void scene_render_background(struct scene_obj *obj, bool box_only)
return;
vidconsole_push_colour(cons, fore, back, &old);
vid_priv = dev_get_uclass_priv(dev);
video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
label_bbox.x1 + inset, label_bbox.y1 + inset,
vid_priv->colour_fg);
@@ -408,7 +408,8 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode)
struct vidconsole_colour old;
enum colour_idx fore, back;
if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
vid_priv = dev_get_uclass_priv(dev);
if (vid_priv->white_on_black) {
fore = VID_BLACK;
back = VID_WHITE;
} else {
@@ -416,7 +417,6 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode)
back = VID_BLACK;
}
vid_priv = dev_get_uclass_priv(dev);
if (obj->flags & SCENEOF_POINT) {
vidconsole_push_colour(cons, fore, back, &old);
video_fill_part(dev, x - theme->menu_inset, y,

View File

@@ -359,6 +359,24 @@ void console_puts_select_stderr(bool serial_only, const char *s)
console_puts_select(stderr, serial_only, s);
}
int console_printf_select_stderr(bool serial_only, const char *fmt, ...)
{
char buf[CONFIG_SYS_PBSIZE];
va_list args;
int ret;
va_start(args, fmt);
/* For this to work, buf must be larger than anything we ever want to
* print.
*/
ret = vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
console_puts_select_stderr(serial_only, buf);
return ret;
}
static void console_puts(int file, const char *s)
{
int i;

View File

@@ -3,6 +3,8 @@
* Copyright (c) 2016 Google, Inc
*/
#define LOG_CATEGORY UCLASS_VIDEO
#include <abuf.h>
#include <dm.h>
#include <log.h>
@@ -488,10 +490,12 @@ static int console_truetype_backspace(struct udevice *dev)
static int console_truetype_entry_start(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_tt_priv *priv = dev_get_priv(dev);
/* A new input line has start, so clear our history */
priv->pos_ptr = 0;
vc_priv->last_ch = 0;
return 0;
}
@@ -733,14 +737,18 @@ static int truetype_select_font(struct udevice *dev, const char *name,
}
static int truetype_measure(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox)
const char *text, int pixel_limit,
struct vidconsole_bbox *bbox, struct alist *lines)
{
struct console_tt_metrics *met;
struct vidconsole_mline mline;
const char *s, *last_space;
int width, last_width;
stbtt_fontinfo *font;
int lsb, advance;
const char *s;
int width;
int last;
int start;
int limit;
int lastch;
int ret;
ret = get_metrics(dev, name, size, &met);
@@ -751,27 +759,85 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
if (!*text)
return 0;
limit = -1;
if (pixel_limit != -1)
limit = tt_ceil((double)pixel_limit / met->scale);
font = &met->font;
width = 0;
for (last = 0, s = text; *s; s++) {
bbox->y1 = 0;
bbox->x1 = 0;
start = 0;
last_space = NULL;
last_width = 0;
for (lastch = 0, s = text; *s; s++) {
int neww;
int ch = *s;
/* Used kerning to fine-tune the position of this character */
if (last)
width += stbtt_GetCodepointKernAdvance(font, last, ch);
if (ch == ' ') {
/*
* store the position and width so we can use it again
* if we need to word-wrap
*/
last_space = s;
last_width = width;
}
/* First get some basic metrics about this character */
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
neww = width + advance;
width += advance;
last = ch;
/* Use kerning to fine-tune the position of this character */
if (lastch)
neww += stbtt_GetCodepointKernAdvance(font, lastch, ch);
lastch = ch;
/* see if we need to start a new line */
if (ch == '\n' || (limit != -1 && neww >= limit)) {
if (ch != '\n' && last_space) {
s = last_space;
width = last_width;
}
last_space = NULL;
mline.bbox.x0 = 0;
mline.bbox.y0 = bbox->y1;
mline.bbox.x1 = tt_ceil((double)width * met->scale);
bbox->x1 = max(bbox->x1, mline.bbox.x1);
bbox->y1 += met->font_size;
mline.bbox.y1 = bbox->y1;
mline.bbox.valid = true;
mline.start = start;
mline.len = (s - text) - start;
if (lines && !alist_add(lines, mline))
return log_msg_ret("ttm", -ENOMEM);
log_debug("line x1 %d y0 %d y1 %d start %d len %d text '%.*s'\n",
mline.bbox.x1, mline.bbox.y0, mline.bbox.y1,
mline.start, mline.len, mline.len, text + mline.start);
start = s - text;
start++;
lastch = 0;
neww = 0;
}
width = neww;
}
/* add the final line */
mline.bbox.x0 = 0;
mline.bbox.y0 = bbox->y1;
mline.bbox.x1 = tt_ceil((double)width * met->scale);
bbox->y1 += met->font_size;
mline.bbox.y1 = bbox->y1;
mline.start = start;
mline.len = (s - text) - start;
if (lines && !alist_add(lines, mline))
return log_msg_ret("ttM", -ENOMEM);
bbox->valid = true;
bbox->x0 = 0;
bbox->y0 = 0;
bbox->x1 = tt_ceil((double)width * met->scale);
bbox->y1 = met->font_size;
bbox->x1 = max(bbox->x1, mline.bbox.x1);
return 0;
}

View File

@@ -127,6 +127,9 @@ void vidconsole_set_cursor_pos(struct udevice *dev, int x, int y)
priv->xcur_frac = VID_TO_POS(x);
priv->xstart_frac = priv->xcur_frac;
priv->ycur = y;
/* make sure not to kern against the previous character */
priv->last_ch = 0;
vidconsole_entry_start(dev);
}
@@ -508,12 +511,14 @@ int vidconsole_put_char(struct udevice *dev, char ch)
return 0;
}
int vidconsole_put_string(struct udevice *dev, const char *str)
int vidconsole_put_stringn(struct udevice *dev, const char *str, int maxlen)
{
const char *s;
const char *s, *end = NULL;
int ret;
for (s = str; *s; s++) {
if (maxlen != -1)
end = str + maxlen;
for (s = str; *s && (maxlen == -1 || s < end); s++) {
ret = vidconsole_put_char(dev, *s);
if (ret)
return ret;
@@ -522,11 +527,19 @@ int vidconsole_put_string(struct udevice *dev, const char *str)
return 0;
}
int vidconsole_put_string(struct udevice *dev, const char *str)
{
return vidconsole_put_stringn(dev, str, -1);
}
static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
{
struct udevice *dev = sdev->priv;
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
int ret;
if (priv->quiet)
return;
ret = vidconsole_put_char(dev, ch);
if (ret) {
#ifdef DEBUG
@@ -544,8 +557,11 @@ static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
{
struct udevice *dev = sdev->priv;
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
int ret;
if (priv->quiet)
return;
ret = vidconsole_put_string(dev, s);
if (ret) {
#ifdef DEBUG
@@ -608,14 +624,17 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size)
}
int vidconsole_measure(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox)
const char *text, int limit,
struct vidconsole_bbox *bbox, struct alist *lines)
{
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
struct vidconsole_ops *ops = vidconsole_get_ops(dev);
int ret;
if (ops->measure) {
ret = ops->measure(dev, name, size, text, bbox);
if (lines)
alist_empty(lines);
ret = ops->measure(dev, name, size, text, limit, bbox, lines);
if (ret != -ENOSYS)
return ret;
}
@@ -784,3 +803,10 @@ void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
y = min_t(short, row * priv->y_charsize, vid_priv->ysize - 1);
vidconsole_set_cursor_pos(dev, x, y);
}
void vidconsole_set_quiet(struct udevice *dev, bool quiet)
{
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
priv->quiet = quiet;
}

View File

@@ -26,6 +26,7 @@
#ifdef CONFIG_SANDBOX
#include <asm/sdl.h>
#endif
#include "vidconsole_internal.h"
/*
* Theory of operation:
@@ -216,6 +217,40 @@ int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
return 0;
}
int video_draw_box(struct udevice *dev, int x0, int y0, int x1, int y1,
int width, u32 colour)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
int pbytes = VNBYTES(priv->bpix);
void *start, *line;
int pixels = x1 - x0;
int row;
start = priv->fb + y0 * priv->line_length;
start += x0 * pbytes;
line = start;
for (row = y0; row < y1; row++) {
void *ptr = line;
int i;
for (i = 0; i < width; i++)
fill_pixel_and_goto_next(&ptr, colour, pbytes, pbytes);
if (row < y0 + width || row >= y1 - width) {
for (i = 0; i < pixels - width * 2; i++)
fill_pixel_and_goto_next(&ptr, colour, pbytes,
pbytes);
} else {
ptr += (pixels - width * 2) * pbytes;
}
for (i = 0; i < width; i++)
fill_pixel_and_goto_next(&ptr, colour, pbytes, pbytes);
line += priv->line_length;
}
video_damage(dev, x0, y0, x1 - x0, y1 - y0);
return 0;
}
int video_reserve_from_bloblist(struct video_handoff *ho)
{
if (!ho->fb || ho->size == 0)
@@ -343,7 +378,7 @@ void video_set_default_colors(struct udevice *dev, bool invert)
struct video_priv *priv = dev_get_uclass_priv(dev);
int fore, back;
if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
if (priv->white_on_black) {
/* White is used when switching to bold, use light gray here */
fore = VID_LIGHT_GRAY;
back = VID_BLACK;
@@ -479,6 +514,7 @@ int video_sync(struct udevice *vid, bool force)
video_flush_dcache(vid, true);
#if defined(CONFIG_VIDEO_SANDBOX_SDL)
/* to see the copy framebuffer, use priv->copy_fb */
sandbox_sdl_sync(priv->fb);
#endif
priv->last_sync = get_timer(0);
@@ -580,6 +616,18 @@ static void video_idle(struct cyclic_info *cyc)
video_sync_all();
}
void video_set_white_on_black(struct udevice *dev, bool white_on_black)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
if (priv->white_on_black != white_on_black) {
priv->white_on_black = white_on_black;
video_set_default_colors(dev, false);
video_clear(dev);
}
}
/* Set up the display ready for use */
static int video_post_probe(struct udevice *dev)
{
@@ -622,6 +670,8 @@ static int video_post_probe(struct udevice *dev)
if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->copy_base)
priv->copy_fb = map_sysmem(plat->copy_base, plat->size);
priv->white_on_black = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK);
/* Set up colors */
video_set_default_colors(dev, false);

View File

@@ -169,6 +169,21 @@ int console_announce_r(void);
*/
void console_puts_select_stderr(bool serial_only, const char *s);
/**
* console_printf_select_stderr() - Output a formatted string to selected devs
*
* This writes to stderr only. It is useful for outputting errors. Note that it
* uses its own buffer, separate from the print buffer, to allow printing
* messages within console/stdio code
*
* @serial_only: true to output only to serial, false to output to everything
* else
* @fmt: Printf format string, followed by format arguments
* Return: number of characters written
*/
int console_printf_select_stderr(bool serial_only, const char *fmt, ...)
__attribute__ ((format (__printf__, 2, 3)));
/**
* console_clear() - Clear the console
*

45
include/test/video.h Normal file
View File

@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2013 Google, Inc.
*/
#ifndef __TEST_VIDEO_H
#define __TEST_VIDEO_H
#include <stdbool.h>
struct udevice;
struct unit_test_state;
/**
* video_compress_fb() - Compress the frame buffer and return its size
*
* We want to write tests which perform operations on the video console and
* check that the frame buffer ends up with the correct contents. But it is
* painful to store 'known good' images for comparison with the frame
* buffer. As an alternative, we can compress the frame buffer and check the
* size of the compressed data. This provides a pretty good level of
* certainty and the resulting tests need only check a single value.
*
* @uts: Test state
* @dev: Video device
* @use_copy: Use copy frame buffer if available
* Return: compressed size of the frame buffer, or -ve on error
*/
int video_compress_fb(struct unit_test_state *uts, struct udevice *dev,
bool use_copy);
/**
* check_copy_frame_buffer() - Compare main frame buffer to copy
*
* If the copy frame buffer is enabled, this compares it to the main
* frame buffer. Normally they should have the same contents after a
* sync.
*
* @uts: Test state
* @dev: Video device
* Return: 0, or -ve on error
*/
int video_check_copy_fb(struct unit_test_state *uts, struct udevice *dev);
#endif

View File

@@ -100,6 +100,7 @@ enum video_format {
* @fg_col_idx: Foreground color code (bit 3 = bold, bit 0-2 = color)
* @bg_col_idx: Background color code (bit 3 = bold, bit 0-2 = color)
* @last_sync: Monotonic time of last video sync
* @white_on_black: Use a black background
*/
struct video_priv {
/* Things set up by the driver: */
@@ -131,6 +132,7 @@ struct video_priv {
u8 fg_col_idx;
u8 bg_col_idx;
ulong last_sync;
bool white_on_black;
};
/**
@@ -247,7 +249,7 @@ int video_fill(struct udevice *dev, u32 colour);
/**
* video_fill_part() - Erase a region
*
* Erase a rectangle of the display within the given bounds.
* Erase a rectangle on the display within the given bounds
*
* @dev: Device to update
* @xstart: X start position in pixels from the left
@@ -260,6 +262,23 @@ int video_fill(struct udevice *dev, u32 colour);
int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
int yend, u32 colour);
/**
* video_draw_box() - Draw a box
*
* Draw a rectangle on the display within the given bounds
*
* @dev: Device to update
* @x0: X start position in pixels from the left
* @y0: Y start position in pixels from the top
* @x1: X end position in pixels from the left
* @y1: Y end position in pixels from the top
* @width: width in pixels
* @colour: Value to write
* Return: 0 if OK, -ENOSYS if the display depth is not supported
*/
int video_draw_box(struct udevice *dev, int x0, int y0, int x1, int y1,
int width, u32 colour);
/**
* video_sync() - Sync a device's frame buffer with its hardware
*
@@ -346,6 +365,16 @@ void video_set_flush_dcache(struct udevice *dev, bool flush);
*/
void video_set_default_colors(struct udevice *dev, bool invert);
/**
* video_set_white_on_black() - Change the setting for white-on-black
*
* This does nothing if the setting is already the same.
*
* @dev: video device
* @white_on_black: true to use white-on-black, false for black-on-white
*/
void video_set_white_on_black(struct udevice *dev, bool white_on_black);
/**
* video_default_font_height() - Get the default font height
*

View File

@@ -6,6 +6,7 @@
#ifndef __video_console_h
#define __video_console_h
#include <alist.h>
#include <video.h>
struct abuf;
@@ -52,6 +53,7 @@ enum {
* @row_saved: Saved Y position in pixels (0=top)
* @escape_buf: Buffer to accumulate escape sequence
* @utf8_buf: Buffer to accumulate UTF-8 byte sequence
* @quiet: Suppress all output from stdio
*/
struct vidconsole_priv {
struct stdio_dev sdev;
@@ -76,6 +78,7 @@ struct vidconsole_priv {
int col_saved;
char escape_buf[32];
char utf8_buf[5];
bool quiet;
};
/**
@@ -119,6 +122,19 @@ struct vidconsole_bbox {
int y1;
};
/**
* vidconsole_mline - Holds information about a line of measured text
*
* @bbox: Bounding box of the line, assuming it starts at 0,0
* @start: String index of the first character in the line
* @len: Number of characters in the line
*/
struct vidconsole_mline {
struct vidconsole_bbox bbox;
int start;
int len;
};
/**
* struct vidconsole_ops - Video console operations
*
@@ -228,18 +244,26 @@ struct vidconsole_ops {
int (*select_font)(struct udevice *dev, const char *name, uint size);
/**
* measure() - Measure the bounds of some text
* measure() - Measure the bounding box of some text
*
* @dev: Device to adjust
* The text can include newlines
*
* @dev: Console device to use
* @name: Font name to use (NULL to use default)
* @size: Font size to use (0 to use default)
* @text: Text to measure
* @limit: Width limit for each line, or -1 if none
* @bbox: Returns bounding box of text, assuming it is positioned
* at 0,0
* @lines: If non-NULL, this must be an alist of
* struct vidconsole_mline inited by caller. A separate
* record is added for each line of text
*
* Returns: 0 on success, -ENOENT if no such font
*/
int (*measure)(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox);
const char *text, int limit,
struct vidconsole_bbox *bbox, struct alist *lines);
/**
* nominal() - Measure the expected width of a line of text
@@ -320,19 +344,27 @@ int vidconsole_get_font(struct udevice *dev, int seq,
*/
int vidconsole_select_font(struct udevice *dev, const char *name, uint size);
/*
* vidconsole_measure() - Measuring the bounding box of some text
/**
* vidconsole_measure() - Measure the bounding box of some text
*
* @dev: Console device to use
* @name: Font name, NULL for default
* @size: Font size, ignored if @name is NULL
* @text: Text to measure
* @bbox: Returns nounding box of text
* Returns: 0 if OK, -ve on error
* The text can include newlines
*
* @dev: Device to adjust
* @name: Font name to use (NULL to use default)
* @size: Font size to use (0 to use default)
* @text: Text to measure
* @limit: Width limit for each line, or -1 if none
* @bbox: Returns bounding box of text, assuming it is positioned
* at 0,0
* @lines: If non-NULL, this must be an alist of
* struct vidconsole_mline inited by caller. The list is emptied
* and then a separate record is added for each line of text
*
* Returns: 0 on success, -ENOENT if no such font
*/
int vidconsole_measure(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox);
const char *text, int limit,
struct vidconsole_bbox *bbox, struct alist *lines);
/**
* vidconsole_nominal() - Measure the expected width of a line of text
*
@@ -469,6 +501,23 @@ int vidconsole_entry_start(struct udevice *dev);
*/
int vidconsole_put_char(struct udevice *dev, char ch);
/**
* vidconsole_put_stringn() - Output part of a string to the current console pos
*
* Outputs part of a string to the console and advances the cursor. This
* function handles wrapping to new lines and scrolling the console. Special
* characters are handled also: \n, \r, \b and \t.
*
* The device always starts with the cursor at position 0,0 (top left). It
* can be adjusted manually using vidconsole_position_cursor().
*
* @dev: Device to adjust
* @str: String to write
* @maxlen: Maximum chars to output, or -1 for all
* Return: 0 if OK, -ve on error
*/
int vidconsole_put_stringn(struct udevice *dev, const char *str, int maxlen);
/**
* vidconsole_put_string() - Output a string to the current console position
*
@@ -537,4 +586,12 @@ void vidconsole_list_fonts(struct udevice *dev);
*/
int vidconsole_get_font_size(struct udevice *dev, const char **name, uint *sizep);
/**
* vidconsole_set_quiet() - Select whether the console should output stdio
*
* @dev: vidconsole device
* @quiet: true to suppress stdout/stderr output, false to enable it
*/
void vidconsole_set_quiet(struct udevice *dev, bool quiet);
#endif

View File

@@ -17,8 +17,10 @@
#include <asm/sdl.h>
#include <dm/test.h>
#include <dm/uclass-internal.h>
#include <test/lib.h>
#include <test/test.h>
#include <test/ut.h>
#include <test/video.h>
/*
* These tests use the standard sandbox frame buffer, the resolution of which
@@ -44,24 +46,8 @@ static int dm_test_video_base(struct unit_test_state *uts)
}
DM_TEST(dm_test_video_base, UTF_SCAN_PDATA | UTF_SCAN_FDT);
/**
* compress_frame_buffer() - Compress the frame buffer and return its size
*
* We want to write tests which perform operations on the video console and
* check that the frame buffer ends up with the correct contents. But it is
* painful to store 'known good' images for comparison with the frame
* buffer. As an alternative, we can compress the frame buffer and check the
* size of the compressed data. This provides a pretty good level of
* certainty and the resulting tests need only check a single value.
*
* @uts: Test state
* @dev: Video device
* @use_copy: Use copy frame buffer if available
* Return: compressed size of the frame buffer, or -ve on error
*/
static int compress_frame_buffer(struct unit_test_state *uts,
struct udevice *dev,
bool use_copy)
int video_compress_fb(struct unit_test_state *uts, struct udevice *dev,
bool use_copy)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
uint destlen;
@@ -86,19 +72,7 @@ static int compress_frame_buffer(struct unit_test_state *uts,
return destlen;
}
/**
* check_copy_frame_buffer() - Compare main frame buffer to copy
*
* If the copy frame buffer is enabled, this compares it to the main
* frame buffer. Normally they should have the same contents after a
* sync.
*
* @uts: Test state
* @dev: Video device
* Return: 0, or -ve on error
*/
static int check_copy_frame_buffer(struct unit_test_state *uts,
struct udevice *dev)
int video_check_copy_fb(struct unit_test_state *uts, struct udevice *dev)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
@@ -174,31 +148,31 @@ static int dm_test_video_text(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
ut_assertok(vidconsole_select_font(con, "8x16", 0));
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_putc_xy(con, 0, 0, 'a');
ut_asserteq(79, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(79, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
vidconsole_putc_xy(con, 0, 0, ' ');
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
for (i = 0; i < 20; i++)
vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
ut_asserteq(273, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(273, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
vidconsole_set_row(con, 0, WHITE);
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
for (i = 0; i < 20; i++)
vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
ut_asserteq(273, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(273, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -216,31 +190,31 @@ static int dm_test_video_text_12x22(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
ut_assertok(vidconsole_select_font(con, "12x22", 0));
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_putc_xy(con, 0, 0, 'a');
ut_asserteq(89, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(89, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
vidconsole_putc_xy(con, 0, 0, ' ');
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
for (i = 0; i < 20; i++)
vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
ut_asserteq(363, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(363, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
vidconsole_set_row(con, 0, WHITE);
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
for (i = 0; i < 20; i++)
vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
ut_asserteq(363, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(363, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -257,8 +231,8 @@ static int dm_test_video_chars(struct unit_test_state *uts)
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
ut_assertok(vidconsole_select_font(con, "8x16", 0));
vidconsole_put_string(con, test_string);
ut_asserteq(466, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(466, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -279,24 +253,24 @@ static int dm_test_video_ansi(struct unit_test_state *uts)
/* reference clear: */
video_clear(con->parent);
video_sync(con->parent, false);
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/* test clear escape sequence: [2J */
vidconsole_put_string(con, "A\tB\tC"ANSI_ESC"[2J");
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/* test set-cursor: [%d;%df */
vidconsole_put_string(con, "abc"ANSI_ESC"[2;2fab"ANSI_ESC"[4;4fcd");
ut_asserteq(143, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(143, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/* test colors (30-37 fg color, 40-47 bg color) */
vidconsole_put_string(con, ANSI_ESC"[30;41mfoo"); /* black on red */
vidconsole_put_string(con, ANSI_ESC"[33;44mbar"); /* yellow on blue */
ut_asserteq(272, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(272, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -328,28 +302,28 @@ static int check_vidconsole_output(struct unit_test_state *uts, int rot,
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
ut_assertok(vidconsole_select_font(con, "8x16", 0));
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/* Check display wrap */
for (i = 0; i < 120; i++)
vidconsole_put_char(con, 'A' + i % 50);
ut_asserteq(wrap_size, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(wrap_size, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/* Check display scrolling */
for (i = 0; i < SCROLL_LINES; i++) {
vidconsole_put_char(con, 'A' + i % 50);
vidconsole_put_char(con, '\n');
}
ut_asserteq(scroll_size, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(scroll_size, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/* If we scroll enough, the screen becomes blank again */
for (i = 0; i < SCROLL_LINES; i++)
vidconsole_put_char(con, '\n');
ut_asserteq(46, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(46, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -423,8 +397,8 @@ static int dm_test_video_bmp(struct unit_test_state *uts)
ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
ut_asserteq(1368, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(1368, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -443,8 +417,8 @@ static int dm_test_video_bmp8(struct unit_test_state *uts)
ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
ut_asserteq(1247, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(1247, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -467,8 +441,8 @@ static int dm_test_video_bmp16(struct unit_test_state *uts)
&src_len));
ut_assertok(video_bmp_display(dev, dst, 0, 0, false));
ut_asserteq(3700, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(3700, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -491,8 +465,8 @@ static int dm_test_video_bmp24(struct unit_test_state *uts)
&src_len));
ut_assertok(video_bmp_display(dev, dst, 0, 0, false));
ut_asserteq(3656, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(3656, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -515,8 +489,8 @@ static int dm_test_video_bmp24_32(struct unit_test_state *uts)
&src_len));
ut_assertok(video_bmp_display(dev, dst, 0, 0, false));
ut_asserteq(6827, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(6827, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -534,8 +508,8 @@ static int dm_test_video_bmp32(struct unit_test_state *uts)
ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
ut_asserteq(2024, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(2024, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -551,8 +525,8 @@ static int dm_test_video_bmp_comp(struct unit_test_state *uts)
ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr));
ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
ut_asserteq(1368, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(1368, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -571,8 +545,8 @@ static int dm_test_video_comp_bmp32(struct unit_test_state *uts)
ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
ut_asserteq(2024, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(2024, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -591,8 +565,8 @@ static int dm_test_video_comp_bmp8(struct unit_test_state *uts)
ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
ut_asserteq(1247, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(1247, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -607,8 +581,9 @@ static int dm_test_video_truetype(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_put_string(con, test_string);
ut_asserteq(12174, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
vidconsole_put_stringn(con, test_string, 30);
ut_asserteq(13184, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -629,8 +604,8 @@ static int dm_test_video_truetype_scroll(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_put_string(con, test_string);
ut_asserteq(34287, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(34287, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -651,8 +626,8 @@ static int dm_test_video_truetype_bs(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_put_string(con, test_string);
ut_asserteq(29471, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(29471, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
@@ -690,8 +665,8 @@ static int dm_test_video_copy(struct unit_test_state *uts)
vidconsole_put_string(con, test_string);
vidconsole_put_string(con, test_string);
ut_asserteq(6678, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(6678, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
/*
* Secretly clear the hardware frame buffer, but in a different
@@ -715,8 +690,8 @@ static int dm_test_video_copy(struct unit_test_state *uts)
vidconsole_put_string(con, test_string);
vidconsole_put_string(con, test_string);
video_sync(dev, true);
ut_asserteq(7589, compress_frame_buffer(uts, dev, false));
ut_asserteq(7704, compress_frame_buffer(uts, dev, true));
ut_asserteq(7589, video_compress_fb(uts, dev, false));
ut_asserteq(7704, video_compress_fb(uts, dev, true));
return 0;
}
@@ -771,9 +746,180 @@ static int dm_test_video_damage(struct unit_test_state *uts)
ut_asserteq(0, priv->damage.xend);
ut_asserteq(0, priv->damage.yend);
ut_asserteq(7339, compress_frame_buffer(uts, dev, false));
ut_assertok(check_copy_frame_buffer(uts, dev));
ut_asserteq(7339, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
DM_TEST(dm_test_video_damage, UTF_SCAN_PDATA | UTF_SCAN_FDT);
/* Test font measurement */
static int dm_test_font_measure(struct unit_test_state *uts)
{
const char *test_string = "There is always much\nto be said for not "
"attempting more than you can do and for making a certainty of "
"what you try. But this principle, like others in life and "
"war, has its exceptions.";
const struct vidconsole_mline *line;
struct vidconsole_bbox bbox;
struct video_priv *priv;
struct udevice *dev, *con;
const int limit = 0x320;
struct alist lines;
int nl;
ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
priv = dev_get_uclass_priv(dev);
ut_asserteq(1366, priv->xsize);
ut_asserteq(768, priv->ysize);
/* this is using the Nimbus font with size of 18 pixels */
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_position_cursor(con, 0, 0);
alist_init_struct(&lines, struct vidconsole_mline);
ut_assertok(vidconsole_measure(con, NULL, 0, test_string, -1, &bbox,
&lines));
ut_asserteq(0, bbox.x0);
ut_asserteq(0, bbox.y0);
ut_asserteq(0x3ea, bbox.x1);
ut_asserteq(0x24, bbox.y1);
ut_asserteq(2, lines.count);
nl = strchr(test_string, '\n') - test_string;
line = alist_get(&lines, 0, struct vidconsole_mline);
ut_assertnonnull(line);
ut_asserteq(0, line->bbox.x0);
ut_asserteq(0, line->bbox.y0);
ut_asserteq(0x8c, line->bbox.x1);
ut_asserteq(0x12, line->bbox.y1);
ut_asserteq(0, line->start);
ut_asserteq(20, line->len);
ut_asserteq(nl, line->len);
line++;
ut_asserteq(0x0, line->bbox.x0);
ut_asserteq(0x12, line->bbox.y0);
ut_asserteq(0x3ea, line->bbox.x1);
ut_asserteq(0x24, line->bbox.y1);
ut_asserteq(21, line->start);
ut_asserteq(nl + 1, line->start);
ut_asserteq(163, line->len);
ut_asserteq(strlen(test_string + nl + 1), line->len);
/* now use a limit on the width */
ut_assertok(vidconsole_measure(con, NULL, 0, test_string, limit, &bbox,
&lines));
ut_asserteq(0, bbox.x0);
ut_asserteq(0, bbox.y0);
ut_asserteq(0x31e, bbox.x1);
ut_asserteq(0x36, bbox.y1);
ut_asserteq(3, lines.count);
nl = strchr(test_string, '\n') - test_string;
line = alist_get(&lines, 0, struct vidconsole_mline);
ut_assertnonnull(line);
ut_asserteq(0, line->bbox.x0);
ut_asserteq(0, line->bbox.y0);
ut_asserteq(0x8c, line->bbox.x1);
ut_asserteq(0x12, line->bbox.y1);
ut_asserteq(0, line->start);
ut_asserteq(20, line->len);
ut_asserteq(nl, line->len);
printf("line0 '%.*s'\n", line->len, test_string + line->start);
ut_asserteq_strn("There is always much",
test_string + line->start);
line++;
ut_asserteq(0x0, line->bbox.x0);
ut_asserteq(0x12, line->bbox.y0);
ut_asserteq(0x31e, line->bbox.x1);
ut_asserteq(0x24, line->bbox.y1);
ut_asserteq(21, line->start);
ut_asserteq(nl + 1, line->start);
ut_asserteq(129, line->len);
printf("line1 '%.*s'\n", line->len, test_string + line->start);
ut_asserteq_strn("to be said for not attempting more than you can do "
"and for making a certainty of what you try. But this "
"principle, like others in",
test_string + line->start);
line++;
ut_asserteq(0x0, line->bbox.x0);
ut_asserteq(0x24, line->bbox.y0);
ut_asserteq(0xc8, line->bbox.x1);
ut_asserteq(0x36, line->bbox.y1);
ut_asserteq(21 + 130, line->start);
ut_asserteq(33, line->len);
printf("line2 '%.*s'\n", line->len, test_string + line->start);
ut_asserteq_strn("life and war, has its exceptions.",
test_string + line->start);
/*
* all characters should be accounted for, except the newline and the
* space which is consumed in the wordwrap
*/
ut_asserteq(strlen(test_string) - 2,
line[-2].len + line[-1].len + line->len);
return 0;
}
DM_TEST(dm_test_font_measure, UTF_SCAN_FDT);
/* Test silencing the video console */
static int dm_test_video_silence(struct unit_test_state *uts)
{
struct udevice *dev, *con;
struct stdio_dev *sdev;
ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
/*
* use the old console device from before when dm_test_pre_run() was
* called, since that is what is in stdio / console
*/
sdev = stdio_get_by_name("vidconsole");
ut_assertnonnull(sdev);
con = sdev->priv;
ut_assertok(vidconsole_clear_and_reset(con));
ut_unsilence_console(uts);
printf("message 1: console\n");
vidconsole_put_string(con, "message 1: video\n");
vidconsole_set_quiet(con, true);
printf("second message: console\n");
vidconsole_put_string(con, "second message: video\n");
vidconsole_set_quiet(con, false);
printf("final message: console\n");
vidconsole_put_string(con, "final message: video\n");
ut_asserteq(3892, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
DM_TEST(dm_test_video_silence, UTF_SCAN_FDT);
/* test drawing a box */
static int dm_test_video_box(struct unit_test_state *uts)
{
struct video_priv *priv;
struct udevice *dev;
ut_assertok(video_get_nologo(uts, &dev));
priv = dev_get_uclass_priv(dev);
video_draw_box(dev, 100, 100, 200, 200, 3,
video_index_to_colour(priv, VID_LIGHT_BLUE));
video_draw_box(dev, 300, 100, 400, 200, 1,
video_index_to_colour(priv, VID_MAGENTA));
video_draw_box(dev, 500, 100, 600, 200, 20,
video_index_to_colour(priv, VID_LIGHT_RED));
ut_asserteq(133, video_compress_fb(uts, dev, false));
ut_assertok(video_check_copy_fb(uts, dev));
return 0;
}
DM_TEST(dm_test_video_box, UTF_SCAN_FDT);