1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/libs/confui/host_renderer.h"
18
19 #include "host/libs/config/cuttlefish_config.h"
20
21 namespace cuttlefish {
22 namespace confui {
alfaCombineChannel(std::uint32_t shift,double alfa,teeui::Color a,teeui::Color b)23 static teeui::Color alfaCombineChannel(std::uint32_t shift, double alfa,
24 teeui::Color a, teeui::Color b) {
25 a >>= shift;
26 a &= 0xff;
27 b >>= shift;
28 b &= 0xff;
29 double acc = alfa * a + (1 - alfa) * b;
30 if (acc <= 0) return 0;
31 std::uint32_t result = acc;
32 if (result > 255) return 255 << shift;
33 return result << shift;
34 }
35
GenerateRenderer(const std::uint32_t display,const std::string & confirmation_msg,const std::string & locale,const bool inverted,const bool magnified)36 std::unique_ptr<ConfUiRenderer> ConfUiRenderer::GenerateRenderer(
37 const std::uint32_t display, const std::string& confirmation_msg,
38 const std::string& locale, const bool inverted, const bool magnified) {
39 ConfUiRenderer* raw_ptr = new ConfUiRenderer(display, confirmation_msg,
40 locale, inverted, magnified);
41 if (raw_ptr && raw_ptr->IsSetUpSuccessful()) {
42 return std::unique_ptr<ConfUiRenderer>(raw_ptr);
43 }
44 return nullptr;
45 }
46
GetDpi(const int display_num=0)47 static int GetDpi(const int display_num = 0) {
48 auto config = CuttlefishConfig::Get();
49 CHECK(config) << "Config is Missing";
50 auto display_configs = config->display_configs();
51 CHECK_GT(display_configs.size(), display_num)
52 << "Invalid display number " << display_num;
53 return display_configs[display_num].dpi;
54 }
55
56 /**
57 * device configuration
58 *
59 * ctx_{# of pixels in 1 mm, # of pixels per 1 density independent pixels}
60 *
61 * The numbers are, however, to fit for the host webRTC local/remote clients
62 * in general, not necessarily the supposedly guest device (e.g. Auto, phone,
63 * etc)
64 *
65 * In general, for a normal PC, roughly ctx_(6.45211, 400.0/412.0) is a good
66 * combination for the default DPI, 320. If we want to see the impact
67 * of the change in the guest DPI, we could adjust the combination above
68 * proportionally
69 *
70 */
ConfUiRenderer(const std::uint32_t display,const std::string & confirmation_msg,const std::string & locale,const bool inverted,const bool magnified)71 ConfUiRenderer::ConfUiRenderer(const std::uint32_t display,
72 const std::string& confirmation_msg,
73 const std::string& locale, const bool inverted,
74 const bool magnified)
75 : display_num_{display},
76 lang_id_{locale},
77 prompt_text_{confirmation_msg},
78 current_height_{ScreenConnectorInfo::ScreenHeight(display_num_)},
79 current_width_{ScreenConnectorInfo::ScreenWidth(display_num_)},
80 is_inverted_(inverted),
81 is_magnified_(magnified),
82 ctx_(6.45211 * GetDpi() / 320.0, 400.0 / 412.0 * GetDpi() / 320.0),
83 is_setup_well_(false) {
84 SetDeviceContext(current_width_, current_height_, is_inverted_,
85 is_magnified_);
86 layout_ = teeui::instantiateLayout(teeui::ConfUILayout(), ctx_);
87
88 if (auto error = UpdateLocale()) {
89 ConfUiLog(ERROR) << "Update Translation Error: " << Enum2Base(error.code());
90 // is_setup_well_ = false;
91 return;
92 }
93 UpdateColorScheme(is_inverted_);
94 SetText<LabelConfMsg>(prompt_text_);
95 is_setup_well_ = true;
96 }
97
UpdateLocale()98 teeui::Error ConfUiRenderer::UpdateLocale() {
99 using teeui::Error;
100 teeui::localization::selectLangId(lang_id_.c_str());
101 if (auto error = UpdateTranslations()) {
102 return error;
103 }
104 return Error::OK;
105 }
106
107 template <typename Label>
UpdateString()108 teeui::Error ConfUiRenderer::UpdateString() {
109 using namespace teeui;
110 const char* str;
111 auto& label = std::get<Label>(layout_);
112 str = localization::lookup(TranslationId(label.textId()));
113 if (str == nullptr) {
114 ConfUiLog(ERROR) << "Given translation_id" << label.textId() << "not found";
115 return Error::Localization;
116 }
117 label.setText({str, str + strlen(str)});
118 return Error::OK;
119 }
120
UpdateTranslations()121 teeui::Error ConfUiRenderer::UpdateTranslations() {
122 using namespace teeui;
123 if (auto error = UpdateString<LabelOK>()) {
124 return error;
125 }
126 if (auto error = UpdateString<LabelCancel>()) {
127 return error;
128 }
129 if (auto error = UpdateString<LabelTitle>()) {
130 return error;
131 }
132 if (auto error = UpdateString<LabelHint>()) {
133 return error;
134 }
135 return Error::OK;
136 }
137
SetDeviceContext(const unsigned long long w,const unsigned long long h,const bool is_inverted,const bool is_magnified)138 void ConfUiRenderer::SetDeviceContext(const unsigned long long w,
139 const unsigned long long h,
140 const bool is_inverted,
141 const bool is_magnified) {
142 using namespace teeui;
143 const auto screen_width = operator""_px(w);
144 const auto screen_height = operator""_px(h);
145 ctx_.setParam<RightEdgeOfScreen>(pxs(screen_width));
146 ctx_.setParam<BottomOfScreen>(pxs(screen_height));
147 if (is_magnified) {
148 ctx_.setParam<DefaultFontSize>(18_dp);
149 ctx_.setParam<BodyFontSize>(20_dp);
150 } else {
151 ctx_.setParam<DefaultFontSize>(14_dp);
152 ctx_.setParam<BodyFontSize>(16_dp);
153 }
154 if (is_inverted) {
155 ctx_.setParam<ShieldColor>(kColorShieldInv);
156 ctx_.setParam<ColorText>(kColorTextInv);
157 ctx_.setParam<ColorBG>(kColorBackgroundInv);
158 ctx_.setParam<ColorButton>(kColorShieldInv);
159 } else {
160 ctx_.setParam<ShieldColor>(kColorShield);
161 ctx_.setParam<ColorText>(kColorText);
162 ctx_.setParam<ColorBG>(kColorBackground);
163 ctx_.setParam<ColorButton>(kColorShield);
164 }
165 }
166
UpdatePixels(TeeUiFrameWrapper & raw_frame,std::uint32_t x,std::uint32_t y,teeui::Color color)167 teeui::Error ConfUiRenderer::UpdatePixels(TeeUiFrameWrapper& raw_frame,
168 std::uint32_t x, std::uint32_t y,
169 teeui::Color color) {
170 auto buffer = raw_frame.data();
171 const auto height = raw_frame.Height();
172 const auto width = raw_frame.Width();
173 auto pos = width * y + x;
174 if (pos >= (height * width)) {
175 ConfUiLog(ERROR) << "Rendering Out of Bound";
176 return teeui::Error::OutOfBoundsDrawing;
177 }
178 const double alfa = ((color & 0xff000000) >> 24) / 255.0;
179 auto& pixel = *reinterpret_cast<teeui::Color*>(buffer + pos);
180 pixel = alfaCombineChannel(0, alfa, color, pixel) |
181 alfaCombineChannel(8, alfa, color, pixel) |
182 alfaCombineChannel(16, alfa, color, pixel);
183 return teeui::Error::OK;
184 }
185
UpdateColorScheme(const bool is_inverted)186 void ConfUiRenderer::UpdateColorScheme(const bool is_inverted) {
187 using namespace teeui;
188 color_text_ = is_inverted ? kColorDisabledInv : kColorDisabled;
189 shield_color_ = is_inverted ? kColorShieldInv : kColorShield;
190 color_bg_ = is_inverted ? kColorBackgroundInv : kColorBackground;
191
192 ctx_.setParam<ShieldColor>(shield_color_);
193 ctx_.setParam<ColorText>(color_text_);
194 ctx_.setParam<ColorBG>(color_bg_);
195 return;
196 }
197
RenderRawFrame()198 std::shared_ptr<TeeUiFrameWrapper> ConfUiRenderer::RenderRawFrame() {
199 /* we repaint only if one or more of the followng meet:
200 *
201 * 1. raw_frame_ is empty
202 * 2. the current_width_ and current_height_ is out of date
203 *
204 */
205 const int w = ScreenConnectorInfo::ScreenWidth(display_num_);
206 const int h = ScreenConnectorInfo::ScreenHeight(display_num_);
207 if (!IsFrameReady() || current_height_ != h || current_width_ != w) {
208 auto new_frame = RepaintRawFrame(w, h);
209 if (!new_frame) {
210 // must repaint but failed
211 raw_frame_ = nullptr;
212 return nullptr;
213 }
214 // repainting from the scratch successful in a new frame
215 raw_frame_ = std::move(new_frame);
216 current_width_ = w;
217 current_height_ = h;
218 }
219 return raw_frame_;
220 }
221
RepaintRawFrame(const int w,const int h)222 std::unique_ptr<TeeUiFrameWrapper> ConfUiRenderer::RepaintRawFrame(
223 const int w, const int h) {
224 std::get<teeui::LabelOK>(layout_).setTextColor(kColorEnabled);
225 std::get<teeui::LabelCancel>(layout_).setTextColor(kColorEnabled);
226
227 /**
228 * should be uint32_t for teeui APIs.
229 * It assumes that each raw frame buffer element is 4 bytes
230 */
231 const teeui::Color background_color =
232 is_inverted_ ? kColorBackgroundInv : kColorBackground;
233 auto new_raw_frame =
234 std::make_unique<TeeUiFrameWrapper>(w, h, background_color);
235 auto draw_pixel = teeui::makePixelDrawer(
236 [this, &new_raw_frame](std::uint32_t x, std::uint32_t y,
237 teeui::Color color) -> teeui::Error {
238 return this->UpdatePixels(*new_raw_frame, x, y, color);
239 });
240
241 // render all components
242 const auto error = drawElements(layout_, draw_pixel);
243 if (error) {
244 ConfUiLog(ERROR) << "Painting failed: " << error.code();
245 return nullptr;
246 }
247
248 return new_raw_frame;
249 }
250
251 } // end of namespace confui
252 } // end of namespace cuttlefish
253