Files
jsix_import/src/boot/status.cpp
Justin C. Miller 421fe33dc0 [boot] Scroll serial output before exit_boot_services
The last line of the boot output was always getting cut off by anything
else getting printed to the serial port. Adding two newlines to clear
the cursor of the previous output.
2022-01-15 09:08:46 -08:00

277 lines
6.6 KiB
C++

#include <uefi/types.h>
#include <uefi/graphics.h>
#include <uefi/protos/simple_text_output.h>
#include "console.h"
#include "error.h"
#include "status.h"
#include "video.h"
constexpr int num_boxes = 30;
namespace boot {
static constexpr int level_ok = 0;
static constexpr int level_warn = 1;
static constexpr int level_fail = 2;
static const wchar_t *level_tags[] = {
L" ok ",
L" warn ",
L"failed"
};
static const uefi::attribute level_colors[] = {
uefi::attribute::green,
uefi::attribute::brown,
uefi::attribute::light_red
};
status *status::s_current = nullptr;
unsigned status::s_current_type = 0;
unsigned status_bar::s_count = 0;
status_line::status_line(const wchar_t *message, const wchar_t *context, bool fails_clean) :
status(fails_clean),
m_level(level_ok),
m_depth(0),
m_outer(nullptr)
{
if (status::s_current_type == status_line::type) {
m_outer = static_cast<status_line*>(s_current);
m_depth = (m_outer ? 1 + m_outer->m_depth : 0);
}
s_current = this;
s_current_type = status_line::type;
auto out = console::get().m_out;
m_line = out->mode->cursor_row;
int indent = 2 * m_depth;
out->set_cursor_position(indent, m_line);
out->set_attribute(uefi::attribute::light_gray);
out->output_string(message);
if (context) {
out->output_string(L": ");
out->output_string(context);
}
out->output_string(L"\r\n");
print_status_tag();
}
status_line::~status_line()
{
if (s_current != this)
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
if (m_outer && m_level > m_outer->m_level) {
m_outer->m_level = m_level;
m_outer->print_status_tag();
}
s_current = m_outer;
}
void
status_line::print_status_tag()
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
int col = out->mode->cursor_column;
uefi::attribute color = level_colors[m_level];
const wchar_t *tag = level_tags[m_level];
out->set_cursor_position(50, m_line);
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"[");
out->set_attribute(color);
out->output_string(tag);
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"]\r\n");
out->set_cursor_position(col, row);
}
void
status_line::do_warn(const wchar_t *message, uefi::status status)
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
if (m_level < level_warn) {
m_level = level_warn;
print_status_tag();
}
int indent = 2 + 2 * m_depth;
out->set_cursor_position(indent, row);
out->set_attribute(uefi::attribute::yellow);
out->output_string(message);
const wchar_t *error = error::message(status);
if (error) {
out->output_string(L": ");
out->output_string(error);
}
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"\r\n");
}
void
status_line::do_fail(const wchar_t *message, uefi::status status)
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
if (m_level < level_fail) {
m_level = level_fail;
print_status_tag();
}
int indent = 2 + 2 * m_depth;
out->set_cursor_position(indent, row);
out->set_attribute(uefi::attribute::red);
out->output_string(message);
const wchar_t *error = error::message(status);
if (error) {
out->output_string(L": ");
out->output_string(error);
}
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"\r\n");
}
void
status_line::do_blank()
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
out->set_cursor_position(0, row);
out->output_string(L"\r\n\r\n");
}
status_bar::status_bar(video::screen *screen) :
status(),
m_outer(nullptr)
{
m_size = (screen->mode.vertical / num_boxes) - 1;
m_top = screen->mode.vertical - m_size;
m_horiz = screen->mode.horizontal;
m_fb = reinterpret_cast<uint32_t*>(screen->framebuffer.pointer);
m_type = static_cast<uint16_t>(screen->mode.layout);
next();
if (status::s_current_type == status_bar::type)
m_outer = static_cast<status_bar*>(s_current);
s_current = this;
s_current_type = status_bar::type;
}
status_bar::~status_bar()
{
if (s_current != this)
error::raise(uefi::status::unsupported, L"Destroying non-current status_bar");
draw_box();
s_current = m_outer;
}
void
status_bar::do_warn(const wchar_t *message, uefi::status status)
{
m_status = status;
if (m_level < level_warn) {
m_level = level_warn;
draw_box();
}
}
void
status_bar::do_fail(const wchar_t *message, uefi::status status)
{
m_status = status;
if (m_level < level_fail) {
m_level = level_fail;
draw_box();
}
}
static uint32_t
make_color(uint8_t r, uint8_t g, uint8_t b, uint16_t type)
{
switch (static_cast<video::layout>(type)) {
case video::layout::bgr8:
return
(static_cast<uint32_t>(b) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(r) << 16);
case video::layout::rgb8:
return
(static_cast<uint32_t>(r) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(b) << 16);
default:
return 0;
}
}
void
status_bar::draw_box()
{
static const uint32_t colors[] = {0x909090, 0xf0f0f0};
constexpr unsigned ncolors = sizeof(colors) / sizeof(uint32_t);
if (m_fb == nullptr)
return;
unsigned x0 = m_current * m_size;
unsigned x1 = x0 + m_size - 3;
unsigned y0 = m_top;
unsigned y1 = m_top + m_size - 3;
uint32_t color = 0;
switch (m_level) {
case level_ok:
color = colors[m_current % ncolors];
break;
case level_warn:
color = make_color(0xff, 0xb2, 0x34, m_type);
break;
case level_fail:
color = make_color(0xfb, 0x0a, 0x1e, m_type);
break;
default:
color = 0;
}
for (unsigned y = y0; y < y1; ++y)
for (unsigned x = x0; x < x1; ++x)
m_fb[y * m_horiz + x] = color;
if (m_level > level_ok) {
unsigned nbars = static_cast<uint64_t>(m_status) & 0xffff;
constexpr unsigned bar_height = 4;
for (unsigned i = 1; i <= nbars; ++i) {
y0 = m_top - 2 * i * bar_height;
y1 = y0 + bar_height;
for (unsigned y = y0; y < y1; ++y)
for (unsigned x = x0; x < x1; ++x)
m_fb[y * m_horiz + x] = color;
}
}
}
} // namespace boot