You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
8.6 KiB
246 lines
8.6 KiB
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "host/libs/confui/host_renderer.h"
|
|
|
|
namespace cuttlefish {
|
|
namespace confui {
|
|
static teeui::Color alfaCombineChannel(std::uint32_t shift, double alfa,
|
|
teeui::Color a, teeui::Color b) {
|
|
a >>= shift;
|
|
a &= 0xff;
|
|
b >>= shift;
|
|
b &= 0xff;
|
|
double acc = alfa * a + (1 - alfa) * b;
|
|
if (acc <= 0) return 0;
|
|
std::uint32_t result = acc;
|
|
if (result > 255) return 255 << shift;
|
|
return result << shift;
|
|
}
|
|
|
|
ConfUiRenderer::ConfUiRenderer(const std::uint32_t display)
|
|
: display_num_(display),
|
|
lang_id_{"en"},
|
|
prompt_("Am I Yumi Meow?"),
|
|
current_height_(ScreenConnectorInfo::ScreenHeight(display_num_)),
|
|
current_width_(ScreenConnectorInfo::ScreenWidth(display_num_)),
|
|
color_bg_{kColorBackground},
|
|
color_text_{kColorDisabled},
|
|
shield_color_{kColorShield},
|
|
is_inverted_{false},
|
|
ctx_{GetDeviceContext()} {
|
|
auto opted_frame = RepaintRawFrame(prompt_, lang_id_);
|
|
if (opted_frame) {
|
|
raw_frame_ = std::move(opted_frame.value());
|
|
}
|
|
}
|
|
|
|
void ConfUiRenderer::SetConfUiMessage(const std::string& msg) {
|
|
prompt_ = msg;
|
|
SetText<LabelConfMsg>(msg);
|
|
}
|
|
|
|
teeui::Error ConfUiRenderer::SetLangId(const std::string& lang_id) {
|
|
using teeui::Error;
|
|
lang_id_ = lang_id;
|
|
teeui::localization::selectLangId(lang_id_.c_str());
|
|
if (auto error = UpdateTranslations()) {
|
|
return error;
|
|
}
|
|
return Error::OK;
|
|
}
|
|
|
|
teeui::Error ConfUiRenderer::UpdateTranslations() {
|
|
using namespace teeui;
|
|
if (auto error = UpdateString<LabelOK>()) {
|
|
return error;
|
|
}
|
|
if (auto error = UpdateString<LabelOK>()) {
|
|
return error;
|
|
}
|
|
if (auto error = UpdateString<LabelCancel>()) {
|
|
return error;
|
|
}
|
|
if (auto error = UpdateString<LabelTitle>()) {
|
|
return error;
|
|
}
|
|
if (auto error = UpdateString<LabelHint>()) {
|
|
return error;
|
|
}
|
|
return Error::OK;
|
|
}
|
|
|
|
teeui::context<teeui::ConUIParameters> ConfUiRenderer::GetDeviceContext() {
|
|
using namespace teeui;
|
|
const unsigned long long w = ScreenConnectorInfo::ScreenWidth(display_num_);
|
|
const unsigned long long h = ScreenConnectorInfo::ScreenHeight(display_num_);
|
|
const auto screen_width = operator""_px(w);
|
|
const auto screen_height = operator""_px(h);
|
|
context<teeui::ConUIParameters> ctx(6.45211, 400.0 / 412.0);
|
|
ctx.setParam<RightEdgeOfScreen>(screen_width);
|
|
ctx.setParam<BottomOfScreen>(screen_height);
|
|
ctx.setParam<PowerButtonTop>(20.26_mm);
|
|
ctx.setParam<PowerButtonBottom>(30.26_mm);
|
|
ctx.setParam<VolUpButtonTop>(40.26_mm);
|
|
ctx.setParam<VolUpButtonBottom>(50.26_mm);
|
|
ctx.setParam<DefaultFontSize>(14_dp);
|
|
ctx.setParam<BodyFontSize>(16_dp);
|
|
return ctx;
|
|
}
|
|
|
|
bool ConfUiRenderer::InitLayout(const std::string& lang_id) {
|
|
layout_ = teeui::instantiateLayout(teeui::ConfUILayout(), ctx_);
|
|
SetLangId(lang_id);
|
|
if (auto error = UpdateTranslations()) {
|
|
ConfUiLog(ERROR) << "Update Translation Error";
|
|
return false;
|
|
}
|
|
UpdateColorScheme(&ctx_);
|
|
return true;
|
|
}
|
|
|
|
teeui::Error ConfUiRenderer::UpdatePixels(TeeUiFrame& raw_frame,
|
|
std::uint32_t x, std::uint32_t y,
|
|
teeui::Color color) {
|
|
auto buffer = raw_frame.data();
|
|
const auto height = ScreenConnectorInfo::ScreenHeight(display_num_);
|
|
const auto width = ScreenConnectorInfo::ScreenWidth(display_num_);
|
|
auto pos = width * y + x;
|
|
if (pos >= (height * width)) {
|
|
ConfUiLog(ERROR) << "Rendering Out of Bound";
|
|
return teeui::Error::OutOfBoundsDrawing;
|
|
}
|
|
const double alfa = ((color & 0xff000000) >> 24) / 255.0;
|
|
auto& pixel = *reinterpret_cast<teeui::Color*>(buffer + pos);
|
|
pixel = alfaCombineChannel(0, alfa, color, pixel) |
|
|
alfaCombineChannel(8, alfa, color, pixel) |
|
|
alfaCombineChannel(16, alfa, color, pixel);
|
|
return teeui::Error::OK;
|
|
}
|
|
|
|
std::tuple<TeeUiFrame&, bool> ConfUiRenderer::RenderRawFrame(
|
|
const std::string& confirmation_msg, const std::string& lang_id) {
|
|
/* we repaint only if one or more of the followng meet:
|
|
*
|
|
* 1. raw_frame_ is empty
|
|
* 2. the current_width_ and current_height_ is out of date
|
|
* 3. lang_id is different (e.g. new locale)
|
|
*
|
|
* in the future, maybe you wanna inverted, new background, etc?
|
|
*/
|
|
if (lang_id != lang_id_ || !IsFrameReady() ||
|
|
current_height_ != ScreenConnectorInfo::ScreenHeight(display_num_) ||
|
|
current_width_ != ScreenConnectorInfo::ScreenWidth(display_num_)) {
|
|
auto opted_new_frame = RepaintRawFrame(confirmation_msg, lang_id_);
|
|
if (opted_new_frame) {
|
|
// repainting from the scratch successful in a new frame
|
|
raw_frame_ = std::move(opted_new_frame.value());
|
|
return {raw_frame_, true};
|
|
}
|
|
// repaint failed even if it was necessary, so returns invalid values
|
|
raw_frame_.clear();
|
|
return {raw_frame_, false};
|
|
}
|
|
// no need to repaint from the scratch. repaint the confirmation message only
|
|
// the frame is mostly already in raw_frame_
|
|
auto ret_code = RenderConfirmationMsgOnly(confirmation_msg);
|
|
return {raw_frame_, (ret_code == teeui::Error::OK)};
|
|
}
|
|
|
|
std::optional<TeeUiFrame> ConfUiRenderer::RepaintRawFrame(
|
|
const std::string& confirmation_msg, const std::string& lang_id) {
|
|
/*
|
|
* NOTE: DON'T use current_width_/height_ to create this frame
|
|
* it may fail, and then we must not mess up the current_width_, height_
|
|
*
|
|
*/
|
|
if (!InitLayout(lang_id)) {
|
|
return std::nullopt;
|
|
}
|
|
SetConfUiMessage(confirmation_msg);
|
|
auto color = kColorEnabled;
|
|
std::get<teeui::LabelOK>(layout_).setTextColor(color);
|
|
std::get<teeui::LabelCancel>(layout_).setTextColor(color);
|
|
|
|
/* in the future, if ever we need to register a handler for the
|
|
Label{OK,Cancel}. do this: std::get<teeui::LabelOK>(layout_)
|
|
.setCB(teeui::makeCallback<teeui::Error, teeui::Event>(
|
|
[](teeui::Event e, void* p) -> teeui::Error {
|
|
LOG(DEBUG) << "Calling callback for Confirm?";
|
|
return reinterpret_cast<decltype(owner)*>(p)->TapOk(e); },
|
|
owner));
|
|
*/
|
|
// we manually check if click happened, where if yes, and generate the label
|
|
// event manually. So we won't register the handler here.
|
|
/**
|
|
* should be uint32_t for teeui APIs.
|
|
* It assumes that each raw frame buffer element is 4 bytes
|
|
*/
|
|
TeeUiFrame new_raw_frame(
|
|
ScreenConnectorInfo::ScreenSizeInBytes(display_num_) / 4,
|
|
kColorBackground);
|
|
|
|
auto draw_pixel = teeui::makePixelDrawer(
|
|
[this, &new_raw_frame](std::uint32_t x, std::uint32_t y,
|
|
teeui::Color color) -> teeui::Error {
|
|
return this->UpdatePixels(new_raw_frame, x, y, color);
|
|
});
|
|
|
|
// render all components
|
|
const auto error = drawElements(layout_, draw_pixel);
|
|
if (error) {
|
|
ConfUiLog(ERROR) << "Painting failed: " << error.code();
|
|
return std::nullopt;
|
|
}
|
|
|
|
// set current frame's dimension as frame generation was successful
|
|
current_height_ = ScreenConnectorInfo::ScreenHeight(display_num_);
|
|
current_width_ = ScreenConnectorInfo::ScreenWidth(display_num_);
|
|
|
|
return {new_raw_frame};
|
|
}
|
|
|
|
teeui::Error ConfUiRenderer::RenderConfirmationMsgOnly(
|
|
const std::string& confirmation_msg) {
|
|
// repaint labelbody on the raw_frame__ only
|
|
auto callback_func = [this](std::uint32_t x, std::uint32_t y,
|
|
teeui::Color color) -> teeui::Error {
|
|
return UpdatePixels(raw_frame_, x, y, color);
|
|
};
|
|
auto draw_pixel = teeui::makePixelDrawer(callback_func);
|
|
LabelConfMsg& label = std::get<LabelConfMsg>(layout_);
|
|
auto b = GetBoundary(label);
|
|
for (std::uint32_t i = 0; i != b.w; i++) {
|
|
const auto col_index = i + b.x - 1;
|
|
for (std::uint32_t j = 0; j != b.y; j++) {
|
|
const auto row_index = (j + b.y - 1);
|
|
raw_frame_[current_width_ * row_index + col_index] = color_bg_;
|
|
}
|
|
}
|
|
|
|
SetConfUiMessage(confirmation_msg);
|
|
ConfUiLog(DEBUG) << "Repaint Confirmation Msg with :" << prompt_;
|
|
if (auto error = std::get<LabelConfMsg>(layout_).draw(draw_pixel)) {
|
|
ConfUiLog(ERROR) << "Repainting Confirmation Message Label failed:"
|
|
<< error.code();
|
|
return error;
|
|
}
|
|
return teeui::Error::OK;
|
|
}
|
|
|
|
} // end of namespace confui
|
|
} // end of namespace cuttlefish
|