From e1d02fb9ff2ab58f018bbe27f83dc0c86ea61b36 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 19 Oct 2018 15:52:17 -0700 Subject: Recovery now expects public keys in zipfile This is in line with the build system change which copies the recovery ota install keys to a zipfile. And now recovery will parses and loads the public keys from /res/otacerts.zip. The legacy load_keys functions will be removed in later cls. Bug: 116655889 Test: sideload an ota package Change-Id: I95e91736ca9964df06d74aa292d672e2f9e442e8 --- install.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install.cpp b/install.cpp index e379ef307..42d264157 100644 --- a/install.cpp +++ b/install.cpp @@ -695,18 +695,18 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, } bool verify_package(const unsigned char* package_data, size_t package_size) { - static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys"; - std::vector loadedKeys; - if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) { + static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip"; + std::vector loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE); + if (loaded_keys.empty()) { LOG(ERROR) << "Failed to load keys"; return false; } - LOG(INFO) << loadedKeys.size() << " key(s) loaded from " << PUBLIC_KEYS_FILE; + LOG(INFO) << loaded_keys.size() << " key(s) loaded from " << CERTIFICATE_ZIP_FILE; // Verify package. ui->Print("Verifying update package...\n"); auto t0 = std::chrono::system_clock::now(); - int err = verify_file(package_data, package_size, loadedKeys, + int err = verify_file(package_data, package_size, loaded_keys, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); std::chrono::duration duration = std::chrono::system_clock::now() - t0; ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err); -- cgit v1.2.3 From b99e6069c1b0749a4811c4d222c5009cbad1edc8 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 16 Oct 2018 15:13:09 -0700 Subject: Add function to show localized rescue party menu Add a function in screenUI to display the pre-generated graphs for rescue party. If these graphs are not valid, falls back to display the old text strings. Right now we haven't generated the localized graphs yet, so the UI always shows the TextMenu. Bug: 116655889 Test: check rescue party under recovery Change-Id: I0558cb536b659cdc25c8b7946d3a39820935b003 --- recovery.cpp | 11 ++-- screen_ui.cpp | 125 ++++++++++++++++++++++++++++++------------ screen_ui.h | 56 +++++++++++++------ stub_ui.h | 6 ++ tests/unit/screen_ui_test.cpp | 37 +++++++++++++ ui.h | 7 +++ wear_ui.cpp | 15 ++--- wear_ui.h | 5 +- 8 files changed, 196 insertions(+), 66 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 3ea282fc0..d7bc6fd2f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -397,23 +397,22 @@ static bool wipe_data(Device* device) { static InstallResult prompt_and_wipe_data(Device* device) { // Use a single string and let ScreenRecoveryUI handles the wrapping. - std::vector headers{ + std::vector wipe_data_menu_headers{ "Can't load Android system. Your data may be corrupt. " "If you continue to get this message, you may need to " "perform a factory data reset and erase all user data " "stored on this device.", }; // clang-format off - std::vector items { + std::vector wipe_data_menu_items { "Try again", "Factory data reset", }; // clang-format on for (;;) { - size_t chosen_item = ui->ShowMenu( - headers, items, 0, true, + size_t chosen_item = ui->ShowPromptWipeDataMenu( + wipe_data_menu_headers, wipe_data_menu_items, std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); - // If ShowMenu() returned RecoveryUI::KeyError::INTERRUPTED, WaitKey() was interrupted. if (chosen_item == static_cast(RecoveryUI::KeyError::INTERRUPTED)) { return INSTALL_KEY_INTERRUPTED; @@ -421,6 +420,8 @@ static InstallResult prompt_and_wipe_data(Device* device) { if (chosen_item != 1) { return INSTALL_SUCCESS; // Just reboot, no wipe; not a failure, user asked for it } + + // TODO(xunchang) localize the confirmation texts also. if (ask_to_wipe_data(device)) { if (wipe_data(device)) { return INSTALL_SUCCESS; diff --git a/screen_ui.cpp b/screen_ui.cpp index 181d58ecf..c538815b5 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -197,12 +197,9 @@ int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const { return offset; } -GraphicMenu::GraphicMenu(size_t max_width, size_t max_height, GRSurface* graphic_headers, - const std::vector& graphic_items, size_t initial_selection, - const DrawInterface& draw_funcs) +GraphicMenu::GraphicMenu(GRSurface* graphic_headers, const std::vector& graphic_items, + size_t initial_selection, const DrawInterface& draw_funcs) : Menu(initial_selection, draw_funcs), - max_width_(max_width), - max_height_(max_height), graphic_headers_(graphic_headers), graphic_items_(graphic_items) {} @@ -223,6 +220,7 @@ int GraphicMenu::Select(int sel) { } int GraphicMenu::DrawHeader(int x, int y) const { + draw_funcs_.SetColor(UIElement::HEADER); draw_funcs_.DrawTextIcon(x, y, graphic_headers_); return graphic_headers_->height; } @@ -253,15 +251,16 @@ int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) cons return offset; } -bool GraphicMenu::Validate() const { +bool GraphicMenu::Validate(size_t max_width, size_t max_height, GRSurface* graphic_headers, + const std::vector& graphic_items) { int offset = 0; - if (!ValidateGraphicSurface(offset, graphic_headers_)) { + if (!ValidateGraphicSurface(max_width, max_height, offset, graphic_headers)) { return false; } - offset += graphic_headers_->height; + offset += graphic_headers->height; - for (const auto& item : graphic_items_) { - if (!ValidateGraphicSurface(offset, item)) { + for (const auto& item : graphic_items) { + if (!ValidateGraphicSurface(max_width, max_height, offset, item)) { return false; } offset += item->height; @@ -270,7 +269,8 @@ bool GraphicMenu::Validate() const { return true; } -bool GraphicMenu::ValidateGraphicSurface(int y, const GRSurface* surface) const { +bool GraphicMenu::ValidateGraphicSurface(size_t max_width, size_t max_height, int y, + const GRSurface* surface) { if (!surface) { fprintf(stderr, "Graphic surface can not be null"); return false; @@ -282,11 +282,11 @@ bool GraphicMenu::ValidateGraphicSurface(int y, const GRSurface* surface) const return false; } - if (surface->width > max_width_ || surface->height > max_height_ - y) { + if (surface->width > max_width || surface->height > max_height - y) { fprintf(stderr, "Graphic surface doesn't fit into the screen. width: %d, height: %d, max_width: %zu," " max_height: %zu, vertical offset: %d\n", - surface->width, surface->height, max_width_, max_height_, y); + surface->width, surface->height, max_width, max_height, y); return false; } @@ -697,7 +697,6 @@ void ScreenRecoveryUI::draw_menu_and_text_buffer_locked( const std::vector& help_message) { int y = margin_height_; if (menu_) { - static constexpr int kMenuIndent = 4; int x = margin_width_ + kMenuIndent; SetColor(UIElement::INFO); @@ -836,6 +835,16 @@ bool ScreenRecoveryUI::InitTextParams() { return true; } +// TODO(xunchang) load localized text icons for the menu. (Init for screenRecoveryUI but +// not wearRecoveryUI). +bool ScreenRecoveryUI::LoadWipeDataMenuText() { + wipe_data_menu_header_text_ = nullptr; + factory_data_reset_text_ = nullptr; + try_again_text_ = nullptr; + + return true; +} + bool ScreenRecoveryUI::Init(const std::string& locale) { RecoveryUI::Init(locale); @@ -876,6 +885,8 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { LoadLocalizedBitmap("no_command_text", &no_command_text); LoadLocalizedBitmap("error_text", &error_text); + LoadWipeDataMenuText(); + LoadAnimation(); // Keep the progress bar updated, even when the process is otherwise busy. @@ -1104,14 +1115,36 @@ void ScreenRecoveryUI::ShowFile(const std::string& filename) { text_row_ = old_text_row; } -void ScreenRecoveryUI::StartMenu(const std::vector& headers, - const std::vector& items, size_t initial_selection) { - std::lock_guard lg(updateMutex); +std::unique_ptr ScreenRecoveryUI::CreateMenu(GRSurface* graphic_header, + const std::vector& graphic_items, + const std::vector& text_headers, + const std::vector& text_items, + size_t initial_selection) const { + // horizontal unusable area: margin width + menu indent + size_t max_width = ScreenWidth() - margin_width_ - kMenuIndent; + // vertical unusable area: margin height + title lines + helper message + high light bar. + // It is safe to reserve more space. + size_t max_height = ScreenHeight() - margin_height_ - char_height_ * (title_lines_.size() + 3); + if (GraphicMenu::Validate(max_width, max_height, graphic_header, graphic_items)) { + return std::make_unique(graphic_header, graphic_items, initial_selection, *this); + } + + fprintf(stderr, "Failed to initialize graphic menu, falling back to use the text menu.\n"); + + return CreateMenu(text_headers, text_items, initial_selection); +} + +std::unique_ptr ScreenRecoveryUI::CreateMenu(const std::vector& text_headers, + const std::vector& text_items, + size_t initial_selection) const { if (text_rows_ > 0 && text_cols_ > 1) { - menu_ = std::make_unique(scrollable_menu_, text_rows_, text_cols_ - 1, headers, items, - initial_selection, char_height_, *this); - update_screen_locked(); + return std::make_unique(scrollable_menu_, text_rows_, text_cols_ - 1, text_headers, + text_items, initial_selection, char_height_, *this); } + + fprintf(stderr, "Failed to create text menu, text_rows %zu, text_cols %zu.\n", text_rows_, + text_cols_); + return nullptr; } int ScreenRecoveryUI::SelectMenu(int sel) { @@ -1127,17 +1160,7 @@ int ScreenRecoveryUI::SelectMenu(int sel) { return sel; } -void ScreenRecoveryUI::EndMenu() { - std::lock_guard lg(updateMutex); - if (menu_) { - menu_.reset(); - update_screen_locked(); - } -} - -size_t ScreenRecoveryUI::ShowMenu(const std::vector& headers, - const std::vector& items, size_t initial_selection, - bool menu_only, +size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr&& menu, bool menu_only, const std::function& key_handler) { // Throw away keys pressed previously, so user doesn't accidentally trigger menu items. FlushKeys(); @@ -1146,9 +1169,13 @@ size_t ScreenRecoveryUI::ShowMenu(const std::vector& headers, // menu. if (IsKeyInterrupted()) return static_cast(KeyError::INTERRUPTED); - StartMenu(headers, items, initial_selection); + CHECK(menu != nullptr); + + // Starts and displays the menu + menu_ = std::move(menu); + Redraw(); - int selected = initial_selection; + int selected = menu_->selection(); int chosen_item = -1; while (chosen_item < 0) { int key = WaitKey(); @@ -1160,7 +1187,8 @@ size_t ScreenRecoveryUI::ShowMenu(const std::vector& headers, continue; } else { LOG(INFO) << "Timed out waiting for key input; rebooting."; - EndMenu(); + menu_.reset(); + Redraw(); return static_cast(KeyError::TIMED_OUT); } } @@ -1186,10 +1214,37 @@ size_t ScreenRecoveryUI::ShowMenu(const std::vector& headers, } } - EndMenu(); + menu_.reset(); + Redraw(); + return chosen_item; } +size_t ScreenRecoveryUI::ShowMenu(const std::vector& headers, + const std::vector& items, size_t initial_selection, + bool menu_only, + const std::function& key_handler) { + auto menu = CreateMenu(headers, items, initial_selection); + if (menu == nullptr) { + return initial_selection; + } + + return ShowMenu(CreateMenu(headers, items, initial_selection), menu_only, key_handler); +} + +size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector& backup_headers, + const std::vector& backup_items, + const std::function& key_handler) { + auto wipe_data_menu = + CreateMenu(wipe_data_menu_header_text_, { try_again_text_, factory_data_reset_text_ }, + backup_headers, backup_items, 0); + if (wipe_data_menu == nullptr) { + return 0; + } + + return ShowMenu(std::move(wipe_data_menu), true, key_handler); +} + bool ScreenRecoveryUI::IsTextVisible() { std::lock_guard lg(updateMutex); int visible = show_text; diff --git a/screen_ui.h b/screen_ui.h index 915288793..46a882c78 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -167,25 +167,23 @@ class GraphicMenu : public Menu { public: // Constructs a Menu instance with the given |headers|, |items| and properties. Sets the initial // selection to |initial_selection|. - GraphicMenu(size_t max_width, size_t max_height, GRSurface* graphic_headers, - const std::vector& graphic_items, size_t initial_selection, - const DrawInterface& draw_funcs); + GraphicMenu(GRSurface* graphic_headers, const std::vector& graphic_items, + size_t initial_selection, const DrawInterface& draw_funcs); int Select(int sel) override; int DrawHeader(int x, int y) const override; int DrawItems(int x, int y, int screen_width, bool long_press) const override; // Checks if all the header and items are valid GRSurfaces; and that they can fit in the area - // defined by |max_width_| and |max_height_|. - bool Validate() const; + // defined by |max_width| and |max_height|. + static bool Validate(size_t max_width, size_t max_height, GRSurface* graphic_headers, + const std::vector& graphic_items); - private: // Returns true if |surface| fits on the screen with a vertical offset |y|. - bool ValidateGraphicSurface(int y, const GRSurface* surface) const; - - const size_t max_width_; - const size_t max_height_; + static bool ValidateGraphicSurface(size_t max_width, size_t max_height, int y, + const GRSurface* surface); + private: // Pointers to the menu headers and items in graphic icons. This class does not have the ownership // of the these objects. GRSurface* graphic_headers_; @@ -238,7 +236,13 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { // the on-device resource files and shows the localized text, for manual inspection. void CheckBackgroundTextImages(); + // Displays the localized wipe data menu. + size_t ShowPromptWipeDataMenu(const std::vector& backup_headers, + const std::vector& backup_items, + const std::function& key_handler) override; + protected: + static constexpr int kMenuIndent = 4; // The margin that we don't want to use for showing texts (e.g. round screen, or screen with // rounded corners). const int margin_width_; @@ -252,18 +256,31 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { virtual bool InitTextParams(); - // Displays some header text followed by a menu of items, which appears at the top of the screen - // (in place of any scrolling ui_print() output, if necessary). - virtual void StartMenu(const std::vector& headers, - const std::vector& items, size_t initial_selection); + virtual bool LoadWipeDataMenuText(); + + // Creates a GraphicMenu with |graphic_header| and |graphic_items|. If the GraphicMenu isn't + // valid or it doesn't fit on the screen; falls back to create a TextMenu instead. If succeeds, + // returns a unique pointer to the created menu; otherwise returns nullptr. + virtual std::unique_ptr CreateMenu(GRSurface* graphic_header, + const std::vector& graphic_items, + const std::vector& text_headers, + const std::vector& text_items, + size_t initial_selection) const; + + // Creates a TextMenu with |text_headers| and |text_items|; and sets the menu selection to + // |initial_selection|. + virtual std::unique_ptr CreateMenu(const std::vector& text_headers, + const std::vector& text_items, + size_t initial_selection) const; + + // Takes the ownership of |menu| and displays it. + virtual size_t ShowMenu(std::unique_ptr&& menu, bool menu_only, + const std::function& key_handler); // Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item // selected. virtual int SelectMenu(int sel); - // Ends menu mode, resetting the text overlay so that ui_print() statements will be displayed. - virtual void EndMenu(); - virtual void draw_background_locked(); virtual void draw_foreground_locked(); virtual void draw_screen_locked(); @@ -318,6 +335,11 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { GRSurface* installing_text; GRSurface* no_command_text; + // Graphs for the wipe data menu + GRSurface* wipe_data_menu_header_text_; + GRSurface* try_again_text_; + GRSurface* factory_data_reset_text_; + GRSurface** introFrames; GRSurface** loopFrames; diff --git a/stub_ui.h b/stub_ui.h index a3cf12b05..ca137dff3 100644 --- a/stub_ui.h +++ b/stub_ui.h @@ -68,6 +68,12 @@ class StubRecoveryUI : public RecoveryUI { return initial_selection; } + size_t ShowPromptWipeDataMenu(const std::vector& /* backup_headers */, + const std::vector& /* backup_items */, + const std::function& /* key_handle */) override { + return 0; + } + void SetTitle(const std::vector& /* lines */) override {} }; diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp index ec269503e..dc6e6a896 100644 --- a/tests/unit/screen_ui_test.cpp +++ b/tests/unit/screen_ui_test.cpp @@ -229,6 +229,43 @@ TEST_F(ScreenUITest, WearMenuSelectItemsOverflow) { ASSERT_EQ(3u, menu.MenuEnd()); } +TEST_F(ScreenUITest, GraphicMenuSelection) { + GRSurface fake_surface = GRSurface{ 50, 50, 50, 1, nullptr }; + std::vector items = { &fake_surface, &fake_surface, &fake_surface }; + GraphicMenu menu(&fake_surface, items, 0, draw_funcs_); + + ASSERT_EQ(0, menu.selection()); + + int sel = 0; + for (int i = 0; i < 3; i++) { + sel = menu.Select(++sel); + ASSERT_EQ((i + 1) % 3, sel); + ASSERT_EQ(sel, menu.selection()); + } + + sel = 0; + for (int i = 0; i < 3; i++) { + sel = menu.Select(--sel); + ASSERT_EQ(2 - i, sel); + ASSERT_EQ(sel, menu.selection()); + } +} + +TEST_F(ScreenUITest, GraphicMenuValidate) { + auto fake_surface = GRSurface{ 50, 50, 50, 1, nullptr }; + std::vector items = { &fake_surface, &fake_surface, &fake_surface }; + + ASSERT_TRUE(GraphicMenu::Validate(200, 200, &fake_surface, items)); + + // Menu exceeds the horizontal boundary. + auto wide_surface = GRSurface{ 300, 50, 300, 1, nullptr }; + ASSERT_FALSE(GraphicMenu::Validate(299, 200, &wide_surface, items)); + + // Menu exceeds the vertical boundary. + items.push_back(&fake_surface); + ASSERT_FALSE(GraphicMenu::Validate(200, 249, &fake_surface, items)); +} + static constexpr int kMagicAction = 101; enum class KeyCode : int { diff --git a/ui.h b/ui.h index e0fb13e40..1e6186a11 100644 --- a/ui.h +++ b/ui.h @@ -162,6 +162,13 @@ class RecoveryUI { const std::vector& items, size_t initial_selection, bool menu_only, const std::function& key_handler) = 0; + // Displays the localized wipe data menu with pre-generated graphs. If there's an issue + // with the graphs, falls back to use the backup string headers and items instead. The initial + // selection is the 0th item in the menu, which is expected to reboot the device without a wipe. + virtual size_t ShowPromptWipeDataMenu(const std::vector& backup_headers, + const std::vector& backup_items, + const std::function& key_handler) = 0; + // Resets the key interrupt status. void ResetKeyInterruptStatus() { key_interrupted_ = false; diff --git a/wear_ui.cpp b/wear_ui.cpp index 8f3bc7bbe..0611f94c9 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -95,13 +95,14 @@ void WearRecoveryUI::update_progress_locked() { void WearRecoveryUI::SetStage(int /* current */, int /* max */) {} -void WearRecoveryUI::StartMenu(const std::vector& headers, - const std::vector& items, size_t initial_selection) { - std::lock_guard lg(updateMutex); +std::unique_ptr WearRecoveryUI::CreateMenu(const std::vector& text_headers, + const std::vector& text_items, + size_t initial_selection) const { if (text_rows_ > 0 && text_cols_ > 0) { - menu_ = std::make_unique(scrollable_menu_, text_rows_ - menu_unusable_rows_ - 1, - text_cols_ - 1, headers, items, initial_selection, - char_height_, *this); - update_screen_locked(); + return std::make_unique(scrollable_menu_, text_rows_ - menu_unusable_rows_ - 1, + text_cols_ - 1, text_headers, text_items, initial_selection, + char_height_, *this); } + + return nullptr; } diff --git a/wear_ui.h b/wear_ui.h index b80cfd758..429af69d2 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -36,8 +36,9 @@ class WearRecoveryUI : public ScreenRecoveryUI { // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. const int menu_unusable_rows_; - void StartMenu(const std::vector& headers, const std::vector& items, - size_t initial_selection) override; + std::unique_ptr CreateMenu(const std::vector& text_headers, + const std::vector& text_items, + size_t initial_selection) const override; int GetProgressBaseline() const override; -- cgit v1.2.3 From 92bdb5a38964baf8326a7d6f53926c30e250922c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 21 Oct 2018 12:12:37 -0700 Subject: minui: Move GRSurface into a class. This CL adds GRSurface::Create() and dtor for managing the allocated memory in GRSurface class. It also adds GRSurface::data() that hides the underlying implementation, with both of const and non-const overloads. This allows `const GRSurface&` to be more useful - previously it only ensured a const member variable of `data`, instead of a read-only buffer it points to. It also marks the parameters in gr_texticon() and gr_blit() as const, as they're incoming source that shouldn't be altered. It corrects the type of gr_draw, which is the sink to be painted on (an earlier attempt was made in [1], but didn't get the full picture correctly). [1] https://android-review.googlesource.com/c/platform/bootable/recovery/+/704757/ Test: mmma -j bootable/recovery Test: recovery_unit_test on marlin Test: Run graphics test on marlin (fbdev). Test: Run graphics test on blueline (drm). Change-Id: I7904df084cd6c08fa04a9da97d01b4b1a6e3a20c --- minui/graphics.cpp | 59 +++++++++++++++++++------------------- minui/graphics_adf.cpp | 12 ++++---- minui/graphics_adf.h | 17 +++++++---- minui/graphics_drm.cpp | 13 ++++----- minui/graphics_drm.h | 15 ++++++---- minui/graphics_fbdev.cpp | 28 ++++++++---------- minui/graphics_fbdev.h | 23 +++++++++++---- minui/include/minui/minui.h | 34 ++++++++++++++++------ minui/resources.cpp | 70 ++++++++++++++++++++++++--------------------- screen_ui.h | 2 +- tests/unit/minui_test.cpp | 32 +++++++++++++++++++++ 11 files changed, 190 insertions(+), 115 deletions(-) create mode 100644 tests/unit/minui_test.cpp diff --git a/minui/graphics.cpp b/minui/graphics.cpp index e6367d950..4d1f9b2d2 100644 --- a/minui/graphics.cpp +++ b/minui/graphics.cpp @@ -40,7 +40,7 @@ static uint32_t gr_current = ~0; static constexpr uint32_t alpha_mask = 0xff000000; // gr_draw is owned by backends. -static const GRSurface* gr_draw = nullptr; +static GRSurface* gr_draw = nullptr; static GRRotation rotation = GRRotation::NONE; static PixelFormat pixel_format = PixelFormat::UNKNOWN; @@ -121,28 +121,29 @@ static void incr_y(uint32_t** p, int row_pixels) { } // Returns pixel pointer at given coordinates with rotation adjustment. -static uint32_t* pixel_at(const GRSurface* surf, int x, int y, int row_pixels) { +static uint32_t* PixelAt(GRSurface* surface, int x, int y, int row_pixels) { switch (rotation) { case GRRotation::NONE: - return reinterpret_cast(surf->data) + y * row_pixels + x; + return reinterpret_cast(surface->data()) + y * row_pixels + x; case GRRotation::RIGHT: - return reinterpret_cast(surf->data) + x * row_pixels + (surf->width - y); + return reinterpret_cast(surface->data()) + x * row_pixels + (surface->width - y); case GRRotation::DOWN: - return reinterpret_cast(surf->data) + (surf->height - 1 - y) * row_pixels + - (surf->width - 1 - x); + return reinterpret_cast(surface->data()) + (surface->height - 1 - y) * row_pixels + + (surface->width - 1 - x); case GRRotation::LEFT: - return reinterpret_cast(surf->data) + (surf->height - 1 - x) * row_pixels + y; + return reinterpret_cast(surface->data()) + (surface->height - 1 - x) * row_pixels + + y; default: printf("invalid rotation %d", static_cast(rotation)); } return nullptr; } -static void text_blend(uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, - int width, int height) { +static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, + int width, int height) { uint8_t alpha_current = static_cast((alpha_mask & gr_current) >> 24); for (int j = 0; j < height; ++j) { - uint8_t* sx = src_p; + const uint8_t* sx = src_p; uint32_t* px = dst_p; for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) { uint8_t a = *sx++; @@ -176,18 +177,18 @@ void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) { } int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint8_t* src_p = font->texture->data + ((ch - ' ') * font->char_width) + - (bold ? font->char_height * font->texture->row_bytes : 0); - uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels); + const uint8_t* src_p = font->texture->data() + ((ch - ' ') * font->char_width) + + (bold ? font->char_height * font->texture->row_bytes : 0); + uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); - text_blend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, - font->char_height); + TextBlend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, + font->char_height); x += font->char_width; } } -void gr_texticon(int x, int y, GRSurface* icon) { +void gr_texticon(int x, int y, const GRSurface* icon) { if (icon == nullptr) return; if (icon->pixel_bytes != 1) { @@ -201,10 +202,9 @@ void gr_texticon(int x, int y, GRSurface* icon) { if (outside(x, y) || outside(x + icon->width - 1, y + icon->height - 1)) return; int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint8_t* src_p = icon->data; - uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels); - - text_blend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height); + const uint8_t* src_p = icon->data(); + uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); + TextBlend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height); } void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { @@ -221,9 +221,9 @@ void gr_clear() { (gr_current & 0xff) == ((gr_current >> 16) & 0xff) && (gr_current & 0xff) == ((gr_current >> 24) & 0xff) && gr_draw->row_bytes == gr_draw->width * gr_draw->pixel_bytes) { - memset(gr_draw->data, gr_current & 0xff, gr_draw->height * gr_draw->row_bytes); + memset(gr_draw->data(), gr_current & 0xff, gr_draw->height * gr_draw->row_bytes); } else { - uint32_t* px = reinterpret_cast(gr_draw->data); + uint32_t* px = reinterpret_cast(gr_draw->data()); int row_diff = gr_draw->row_bytes / gr_draw->pixel_bytes - gr_draw->width; for (int y = 0; y < gr_draw->height; ++y) { for (int x = 0; x < gr_draw->width; ++x) { @@ -244,7 +244,7 @@ void gr_fill(int x1, int y1, int x2, int y2) { if (outside(x1, y1) || outside(x2 - 1, y2 - 1)) return; int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint32_t* p = pixel_at(gr_draw, x1, y1, row_pixels); + uint32_t* p = PixelAt(gr_draw, x1, y1, row_pixels); uint8_t alpha = static_cast(((gr_current & alpha_mask) >> 24)); if (alpha > 0) { for (int y = y1; y < y2; ++y) { @@ -258,7 +258,7 @@ void gr_fill(int x1, int y1, int x2, int y2) { } } -void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { +void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { if (source == nullptr) return; if (gr_draw->pixel_bytes != source->pixel_bytes) { @@ -274,11 +274,12 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { if (rotation != GRRotation::NONE) { int src_row_pixels = source->row_bytes / source->pixel_bytes; int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint32_t* src_py = reinterpret_cast(source->data) + sy * source->row_bytes / 4 + sx; - uint32_t* dst_py = pixel_at(gr_draw, dx, dy, row_pixels); + const uint32_t* src_py = + reinterpret_cast(source->data()) + sy * source->row_bytes / 4 + sx; + uint32_t* dst_py = PixelAt(gr_draw, dx, dy, row_pixels); for (int y = 0; y < h; y += 1) { - uint32_t* src_px = src_py; + const uint32_t* src_px = src_py; uint32_t* dst_px = dst_py; for (int x = 0; x < w; x += 1) { *dst_px = *src_px++; @@ -288,8 +289,8 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { incr_y(&dst_py, row_pixels); } } else { - unsigned char* src_p = source->data + sy * source->row_bytes + sx * source->pixel_bytes; - unsigned char* dst_p = gr_draw->data + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes; + const uint8_t* src_p = source->data() + sy * source->row_bytes + sx * source->pixel_bytes; + uint8_t* dst_p = gr_draw->data() + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes; for (int i = 0; i < h; ++i) { memcpy(dst_p, src_p, w * source->pixel_bytes); diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp index 7439df9ac..6fc193f74 100644 --- a/minui/graphics_adf.cpp +++ b/minui/graphics_adf.cpp @@ -45,14 +45,14 @@ int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* su surf->row_bytes = surf->pitch; surf->pixel_bytes = (format == DRM_FORMAT_RGB565) ? 2 : 4; - surf->data = static_cast( - mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset)); - if (surf->data == MAP_FAILED) { + auto mmapped = + mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset); + if (mmapped == MAP_FAILED) { int saved_errno = errno; close(surf->fd); return -saved_errno; } - + surf->mmapped_buffer_ = static_cast(mmapped); return 0; } @@ -185,7 +185,9 @@ void MinuiBackendAdf::Blank(bool blank) { } void MinuiBackendAdf::SurfaceDestroy(GRSurfaceAdf* surf) { - munmap(surf->data, surf->pitch * surf->height); + if (surf->mmapped_buffer_) { + munmap(surf->mmapped_buffer_, surf->pitch * surf->height); + } close(surf->fence_fd); close(surf->fd); } diff --git a/minui/graphics_adf.h b/minui/graphics_adf.h index 2f019ed0b..099d32962 100644 --- a/minui/graphics_adf.h +++ b/minui/graphics_adf.h @@ -14,21 +14,30 @@ * limitations under the License. */ -#ifndef _GRAPHICS_ADF_H_ -#define _GRAPHICS_ADF_H_ +#pragma once + +#include #include #include "graphics.h" +#include "minui/minui.h" class GRSurfaceAdf : public GRSurface { + public: + uint8_t* data() override { + return mmapped_buffer_; + } + private: + friend class MinuiBackendAdf; + int fence_fd; int fd; __u32 offset; __u32 pitch; - friend class MinuiBackendAdf; + uint8_t* mmapped_buffer_{ nullptr }; }; class MinuiBackendAdf : public MinuiBackend { @@ -54,5 +63,3 @@ class MinuiBackendAdf : public MinuiBackend { unsigned int n_surfaces; GRSurfaceAdf surfaces[2]; }; - -#endif // _GRAPHICS_ADF_H_ diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 630b80180..81b49fd95 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -70,8 +70,8 @@ void MinuiBackendDrm::Blank(bool blank) { void MinuiBackendDrm::DrmDestroySurface(GRSurfaceDrm* surface) { if (!surface) return; - if (surface->data) { - munmap(surface->data, surface->row_bytes * surface->height); + if (surface->mmapped_buffer_) { + munmap(surface->mmapped_buffer_, surface->row_bytes * surface->height); } if (surface->fb_id) { @@ -172,15 +172,14 @@ GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) { surface->width = width; surface->row_bytes = create_dumb.pitch; surface->pixel_bytes = create_dumb.bpp / 8; - surface->data = static_cast(mmap(nullptr, surface->height * surface->row_bytes, - PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, - map_dumb.offset)); - if (surface->data == MAP_FAILED) { + auto mmapped = mmap(nullptr, surface->height * surface->row_bytes, PROT_READ | PROT_WRITE, + MAP_SHARED, drm_fd, map_dumb.offset); + if (mmapped == MAP_FAILED) { perror("mmap() failed"); DrmDestroySurface(surface); return nullptr; } - + surface->mmapped_buffer_ = static_cast(mmapped); return surface; } diff --git a/minui/graphics_drm.h b/minui/graphics_drm.h index 756625b03..f3aad6bfc 100644 --- a/minui/graphics_drm.h +++ b/minui/graphics_drm.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _GRAPHICS_DRM_H_ -#define _GRAPHICS_DRM_H_ +#pragma once #include @@ -25,11 +24,17 @@ #include "minui/minui.h" class GRSurfaceDrm : public GRSurface { + public: + uint8_t* data() override { + return mmapped_buffer_; + } + private: + friend class MinuiBackendDrm; + uint32_t fb_id; uint32_t handle; - - friend class MinuiBackendDrm; + uint8_t* mmapped_buffer_{ nullptr }; }; class MinuiBackendDrm : public MinuiBackend { @@ -54,5 +59,3 @@ class MinuiBackendDrm : public MinuiBackend { drmModeConnector* main_monitor_connector; int drm_fd; }; - -#endif // _GRAPHICS_DRM_H_ diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp index 746f42aaa..f958d62d4 100644 --- a/minui/graphics_fbdev.cpp +++ b/minui/graphics_fbdev.cpp @@ -100,16 +100,16 @@ GRSurface* MinuiBackendFbdev::Init() { gr_framebuffer[0].height = vi.yres; gr_framebuffer[0].row_bytes = fi.line_length; gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; - gr_framebuffer[0].data = static_cast(bits); - memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + gr_framebuffer[0].buffer_ = static_cast(bits); + memset(gr_framebuffer[0].buffer_, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); /* check if we can use double buffering */ if (vi.yres * fi.line_length * 2 <= fi.smem_len) { double_buffered = true; - memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(GRSurface)); - gr_framebuffer[1].data = - gr_framebuffer[0].data + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; + gr_framebuffer[1] = gr_framebuffer[0]; + gr_framebuffer[1].buffer_ = + gr_framebuffer[0].buffer_ + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; gr_draw = gr_framebuffer + 1; @@ -120,16 +120,12 @@ GRSurface* MinuiBackendFbdev::Init() { // draw in, and then "flipping" the buffer consists of a // memcpy from the buffer we allocated to the framebuffer. - gr_draw = static_cast(malloc(sizeof(GRSurface))); - memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); - gr_draw->data = static_cast(malloc(gr_draw->height * gr_draw->row_bytes)); - if (!gr_draw->data) { - perror("failed to allocate in-memory surface"); - return nullptr; - } + gr_draw = new GRSurfaceFbdev; + *gr_draw = gr_framebuffer[0]; + gr_draw->buffer_ = new uint8_t[gr_draw->height * gr_draw->row_bytes]; } - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + memset(gr_draw->buffer_, 0, gr_draw->height * gr_draw->row_bytes); fb_fd = fd; SetDisplayedFramebuffer(0); @@ -150,7 +146,7 @@ GRSurface* MinuiBackendFbdev::Flip() { SetDisplayedFramebuffer(1 - displayed_buffer); } else { // Copy from the in-memory surface to the framebuffer. - memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes); + memcpy(gr_framebuffer[0].buffer_, gr_draw->buffer_, gr_draw->height * gr_draw->row_bytes); } return gr_draw; } @@ -160,8 +156,8 @@ MinuiBackendFbdev::~MinuiBackendFbdev() { fb_fd = -1; if (!double_buffered && gr_draw) { - free(gr_draw->data); - free(gr_draw); + delete[] gr_draw->buffer_; + delete gr_draw; } gr_draw = nullptr; } diff --git a/minui/graphics_fbdev.h b/minui/graphics_fbdev.h index 107e19567..be813dccb 100644 --- a/minui/graphics_fbdev.h +++ b/minui/graphics_fbdev.h @@ -14,14 +14,27 @@ * limitations under the License. */ -#ifndef _GRAPHICS_FBDEV_H_ -#define _GRAPHICS_FBDEV_H_ +#pragma once #include +#include #include "graphics.h" #include "minui/minui.h" +class GRSurfaceFbdev : public GRSurface { + public: + uint8_t* data() override { + return buffer_; + } + + private: + friend class MinuiBackendFbdev; + + // Points to the start of the buffer: either the mmap'd framebuffer or one allocated in-memory. + uint8_t* buffer_; +}; + class MinuiBackendFbdev : public MinuiBackend { public: GRSurface* Init() override; @@ -33,12 +46,10 @@ class MinuiBackendFbdev : public MinuiBackend { private: void SetDisplayedFramebuffer(unsigned n); - GRSurface gr_framebuffer[2]; + GRSurfaceFbdev gr_framebuffer[2]; bool double_buffered; - GRSurface* gr_draw; + GRSurfaceFbdev* gr_draw; int displayed_buffer; fb_var_screeninfo vi; int fb_fd; }; - -#endif // _GRAPHICS_FBDEV_H_ diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index fa13ecdff..e9bd1c4f1 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -14,12 +14,13 @@ * limitations under the License. */ -#ifndef _MINUI_H_ -#define _MINUI_H_ +#pragma once +#include #include #include +#include #include #include @@ -27,12 +28,31 @@ // Graphics. // -struct GRSurface { +class GRSurface { + public: + GRSurface() = default; + virtual ~GRSurface(); + + // Creates and returns a GRSurface instance for the given data_size. The starting address of the + // surface data is aligned to SURFACE_DATA_ALIGNMENT. Returns the created GRSurface instance (in + // std::unique_ptr), or nullptr on error. + static std::unique_ptr Create(size_t data_size); + + virtual uint8_t* data() { + return data_; + } + + const uint8_t* data() const { + return const_cast(const_cast(this)->data()); + } + int width; int height; int row_bytes; int pixel_bytes; - unsigned char* data; + + private: + uint8_t* data_{ nullptr }; }; struct GRFont { @@ -75,7 +95,7 @@ void gr_clear(); void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); void gr_fill(int x1, int y1, int x2, int y2); -void gr_texticon(int x, int y, GRSurface* icon); +void gr_texticon(int x, int y, const GRSurface* icon); const GRFont* gr_sys_font(); int gr_init_font(const char* name, GRFont** dest); @@ -85,7 +105,7 @@ int gr_measure(const GRFont* font, const char* s); // Returns -1 if font is nullptr. int gr_font_size(const GRFont* font, int* x, int* y); -void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy); +void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy); unsigned int gr_get_width(const GRSurface* surface); unsigned int gr_get_height(const GRSurface* surface); @@ -165,5 +185,3 @@ std::vector get_locales_in_png(const std::string& png_name); // Free a surface allocated by any of the res_create_*_surface() // functions. void res_free_surface(GRSurface* surface); - -#endif diff --git a/minui/resources.cpp b/minui/resources.cpp index 477fbe2a2..c01c1868e 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -37,18 +37,23 @@ #include "minui/minui.h" -#define SURFACE_DATA_ALIGNMENT 8 - static std::string g_resource_dir{ "/res/images" }; -static GRSurface* malloc_surface(size_t data_size) { - size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT; - unsigned char* temp = static_cast(malloc(size)); - if (temp == NULL) return NULL; - GRSurface* surface = reinterpret_cast(temp); - surface->data = temp + sizeof(GRSurface) + - (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT)); - return surface; +std::unique_ptr GRSurface::Create(size_t data_size) { + static constexpr size_t kSurfaceDataAlignment = 8; + std::unique_ptr result = std::make_unique(); + size_t aligned_size = + (data_size + kSurfaceDataAlignment - 1) / kSurfaceDataAlignment * kSurfaceDataAlignment; + result->data_ = static_cast(aligned_alloc(kSurfaceDataAlignment, aligned_size)); + if (result->data_ == nullptr) return nullptr; + return result; +} + +GRSurface::~GRSurface() { + if (data_ != nullptr) { + free(data_); + data_ = nullptr; + } } PngHandler::PngHandler(const std::string& name) { @@ -133,18 +138,18 @@ PngHandler::~PngHandler() { // framebuffer pixel format; they need to be modified if the // framebuffer format changes (but nothing else should). -// Allocate and return a GRSurface* sufficient for storing an image of -// the indicated size in the framebuffer pixel format. -static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) { - GRSurface* surface = malloc_surface(width * height * 4); - if (surface == NULL) return NULL; +// Allocates and returns a GRSurface* sufficient for storing an image of the indicated size in the +// framebuffer pixel format. +static std::unique_ptr init_display_surface(png_uint_32 width, png_uint_32 height) { + std::unique_ptr surface = GRSurface::Create(width * height * 4); + if (!surface) return nullptr; - surface->width = width; - surface->height = height; - surface->row_bytes = width * 4; - surface->pixel_bytes = 4; + surface->width = width; + surface->height = height; + surface->row_bytes = width * 4; + surface->pixel_bytes = 4; - return surface; + return surface; } // Copy 'input_row' to 'output_row', transforming it to the @@ -202,7 +207,7 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) { png_uint_32 width = png_handler.width(); png_uint_32 height = png_handler.height(); - GRSurface* surface = init_display_surface(width, height); + std::unique_ptr surface = init_display_surface(width, height); if (!surface) { return -8; } @@ -215,11 +220,11 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) { for (png_uint_32 y = 0; y < height; ++y) { std::vector p_row(width * 4); png_read_row(png_ptr, p_row.data(), nullptr); - transform_rgb_to_draw(p_row.data(), surface->data + y * surface->row_bytes, + transform_rgb_to_draw(p_row.data(), surface->data() + y * surface->row_bytes, png_handler.channels(), width); } - *pSurface = surface; + *pSurface = surface.release(); return 0; } @@ -272,11 +277,12 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps, goto exit; } for (int i = 0; i < *frames; ++i) { - surface[i] = init_display_surface(width, height / *frames); - if (!surface[i]) { + auto created_surface = init_display_surface(width, height / *frames); + if (!created_surface) { result = -8; goto exit; } + surface[i] = created_surface.release(); } if (gr_pixel_format() == PixelFormat::ABGR || gr_pixel_format() == PixelFormat::BGRA) { @@ -287,7 +293,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps, std::vector p_row(width * 4); png_read_row(png_ptr, p_row.data(), nullptr); int frame = y % *frames; - unsigned char* out_row = surface[frame]->data + (y / *frames) * surface[frame]->row_bytes; + unsigned char* out_row = surface[frame]->data() + (y / *frames) * surface[frame]->row_bytes; transform_rgb_to_draw(p_row.data(), out_row, png_handler.channels(), width); } @@ -319,7 +325,7 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { png_uint_32 width = png_handler.width(); png_uint_32 height = png_handler.height(); - GRSurface* surface = malloc_surface(width * height); + std::unique_ptr surface = GRSurface::Create(width * height); if (!surface) { return -8; } @@ -334,11 +340,11 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { } for (png_uint_32 y = 0; y < height; ++y) { - unsigned char* p_row = surface->data + y * surface->row_bytes; + unsigned char* p_row = surface->data() + y * surface->row_bytes; png_read_row(png_ptr, p_row, nullptr); } - *pSurface = surface; + *pSurface = surface.release(); return 0; } @@ -429,7 +435,7 @@ int res_create_localized_alpha_surface(const char* name, if (y + 1 + h >= height || matches_locale(loc, locale)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); - GRSurface* surface = malloc_surface(w * h); + std::unique_ptr surface = GRSurface::Create(w * h); if (!surface) { return -8; } @@ -440,10 +446,10 @@ int res_create_localized_alpha_surface(const char* name, for (int i = 0; i < h; ++i, ++y) { png_read_row(png_ptr, row.data(), nullptr); - memcpy(surface->data + i * w, row.data(), w); + memcpy(surface->data() + i * w, row.data(), w); } - *pSurface = surface; + *pSurface = surface.release(); break; } diff --git a/screen_ui.h b/screen_ui.h index 915288793..861b3605a 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -29,7 +29,7 @@ #include "ui.h" // From minui/minui.h. -struct GRSurface; +class GRSurface; enum class UIElement { HEADER, diff --git a/tests/unit/minui_test.cpp b/tests/unit/minui_test.cpp new file mode 100644 index 000000000..cad6a3d79 --- /dev/null +++ b/tests/unit/minui_test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 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 + +#include + +#include + +#include "minui/minui.h" + +TEST(GRSurfaceTest, Create_aligned) { + static constexpr size_t kSurfaceDataAlignment = 8; + for (size_t data_size = 100; data_size < 128; data_size++) { + std::unique_ptr surface(GRSurface::Create(data_size)); + ASSERT_TRUE(surface); + ASSERT_EQ(0, reinterpret_cast(surface->data()) % kSurfaceDataAlignment); + } +} -- cgit v1.2.3 From 1e10cc4297e98a934f548aa7e7767ee6d469ff0f Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 23 Oct 2018 12:10:46 -0700 Subject: Add a function to construct the GRSurface in test This fixes the build error as the initializer list no longer work without the proper constructor for c++ class. Bug: 74397117 Test: unit tests pass Change-Id: If3ff508a1a01ad5326413dab8e05bacae8a946c8 --- tests/unit/screen_ui_test.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp index dc6e6a896..06a98c742 100644 --- a/tests/unit/screen_ui_test.cpp +++ b/tests/unit/screen_ui_test.cpp @@ -69,6 +69,17 @@ class ScreenUITest : public testing::Test { MockDrawFunctions draw_funcs_; }; +// TODO(xunchang) Create a constructor. +static GRSurface CreateFakeGRSurface(int width, int height, int row_bytes, int pixel_bytes) { + GRSurface fake_surface; + fake_surface.width = width; + fake_surface.height = height; + fake_surface.row_bytes = row_bytes; + fake_surface.pixel_bytes = pixel_bytes; + + return fake_surface; +} + TEST_F(ScreenUITest, StartPhoneMenuSmoke) { TextMenu menu(false, 10, 20, HEADERS, ITEMS, 0, 20, draw_funcs_); ASSERT_FALSE(menu.scrollable()); @@ -230,7 +241,7 @@ TEST_F(ScreenUITest, WearMenuSelectItemsOverflow) { } TEST_F(ScreenUITest, GraphicMenuSelection) { - GRSurface fake_surface = GRSurface{ 50, 50, 50, 1, nullptr }; + auto fake_surface = CreateFakeGRSurface(50, 50, 50, 1); std::vector items = { &fake_surface, &fake_surface, &fake_surface }; GraphicMenu menu(&fake_surface, items, 0, draw_funcs_); @@ -252,13 +263,13 @@ TEST_F(ScreenUITest, GraphicMenuSelection) { } TEST_F(ScreenUITest, GraphicMenuValidate) { - auto fake_surface = GRSurface{ 50, 50, 50, 1, nullptr }; + auto fake_surface = CreateFakeGRSurface(50, 50, 50, 1); std::vector items = { &fake_surface, &fake_surface, &fake_surface }; ASSERT_TRUE(GraphicMenu::Validate(200, 200, &fake_surface, items)); // Menu exceeds the horizontal boundary. - auto wide_surface = GRSurface{ 300, 50, 300, 1, nullptr }; + auto wide_surface = CreateFakeGRSurface(300, 50, 300, 1); ASSERT_FALSE(GraphicMenu::Validate(299, 200, &wide_surface, items)); // Menu exceeds the vertical boundary. -- cgit v1.2.3