From 02ec6b88ed4e6cf40cc257572b07c7277b7b6341 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 22 Aug 2012 17:26:40 -0700 Subject: [PATCH] add simple text to recovery UI - recovery takes a --locale argument, which will be passed by the main system - the locale is saved in cache, in case the --locale argument is missing (eg, when recovery is started from fastboot) - we include images that have prerendered text for many locales - we split the background states into four (installing update, erasing, no command, error) so that appropriate text can be shown. Change-Id: I731b8108e83d5ccc09a4aacfc1dbf7e86b397aaf --- install.cpp | 2 +- install.h | 2 +- minui/graphics.c | 16 ++++ minui/minui.h | 2 + minui/resources.c | 150 +++++++++++++++++++++++++++++++++ recovery.cpp | 63 +++++++++++--- res/images/erasing_text.png | Bin 0 -> 4511 bytes res/images/error_text.png | Bin 0 -> 3119 bytes res/images/installing_text.png | Bin 0 -> 6433 bytes res/images/no_command_text.png | Bin 0 -> 2528 bytes screen_ui.cpp | 61 ++++++++++---- screen_ui.h | 6 +- ui.h | 2 +- 13 files changed, 274 insertions(+), 30 deletions(-) create mode 100644 res/images/erasing_text.png create mode 100644 res/images/error_text.png create mode 100644 res/images/installing_text.png create mode 100644 res/images/no_command_text.png diff --git a/install.cpp b/install.cpp index 819650e1..b8f47813 100644 --- a/install.cpp +++ b/install.cpp @@ -277,7 +277,7 @@ exit: static int really_install_package(const char *path, int* wipe_cache) { - ui->SetBackground(RecoveryUI::INSTALLING); + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); ui->SetProgressType(RecoveryUI::INDETERMINATE); LOGI("Update location: %s\n", path); diff --git a/install.h b/install.h index 1943f02d..2ada529e 100644 --- a/install.h +++ b/install.h @@ -23,7 +23,7 @@ extern "C" { #endif -enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; +enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE }; // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. diff --git a/minui/graphics.c b/minui/graphics.c index 81f13ad2..88572a87 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -244,6 +244,22 @@ int gr_text(int x, int y, const char *s) return x; } +void gr_texticon(int x, int y, gr_surface icon) { + GGLContext* gl = gr_context; + + gl->bindTexture(gl, (GGLSurface*) icon); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->enable(gl, GGL_TEXTURE_2D); + + int w = gr_get_width(icon); + int h = gr_get_height(icon); + + gl->texCoord2i(gl, -x, -y); + gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon)); +} + void gr_fill(int x, int y, int w, int h) { GGLContext *gl = gr_context; diff --git a/minui/minui.h b/minui/minui.h index 74da4e9c..767ffcb5 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -38,6 +38,7 @@ void gr_fb_blank(bool blank); void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); void gr_fill(int x, int y, int w, int h); int gr_text(int x, int y, const char *s); + void gr_texticon(int x, int y, gr_surface icon); int gr_measure(const char *s); void gr_font_size(int *x, int *y); @@ -71,6 +72,7 @@ void ev_dispatch(void); // Returns 0 if no error, else negative. int res_create_surface(const char* name, gr_surface* pSurface); +int res_create_localized_surface(const char* name, gr_surface* pSurface); void res_free_surface(gr_surface surface); #ifdef __cplusplus diff --git a/minui/resources.c b/minui/resources.c index b437a87c..af8720a5 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -33,6 +33,8 @@ #include "minui.h" +extern char* locale; + // libpng gives "undefined reference to 'pow'" errors, and I have no // idea how to convince the build system to link with -lm. We don't // need this functionality (it's used for gamma adjustment) so provide @@ -173,6 +175,154 @@ exit: return result; } +static int matches_locale(const char* loc) { + if (locale == NULL) return 0; + + printf("matching loc=[%s] vs locale=[%s]\n", loc, locale); + + if (strcmp(loc, locale) == 0) return 1; + + // if loc does *not* have an underscore, and it matches the start + // of locale, and the next character in locale *is* an underscore, + // that's a match. For instance, loc == "en" matches locale == + // "en_US". + + int i; + for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i); + if (loc[i] == '_') return 0; + printf(" partial match possible; i = %d\n", i); + + return (strncmp(locale, loc, i) == 0 && locale[i] == '_'); +} + +int res_create_localized_surface(const char* name, gr_surface* pSurface) { + char resPath[256]; + GGLSurface* surface = NULL; + int result = 0; + unsigned char header[8]; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + *pSurface = NULL; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); + resPath[sizeof(resPath)-1] = '\0'; + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + + size_t bytesRead = fread(header, 1, sizeof(header), fp); + if (bytesRead != sizeof(header)) { + result = -2; + goto exit; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; + goto exit; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + result = -4; + goto exit; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + result = -5; + goto exit; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + result = -6; + goto exit; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sizeof(header)); + png_read_info(png_ptr, info_ptr); + + size_t width = info_ptr->width; + size_t height = info_ptr->height; + size_t stride = 4 * width; + + printf("image size is %d x %d\n", width, height); + + int color_type = info_ptr->color_type; + int bit_depth = info_ptr->bit_depth; + int channels = info_ptr->channels; + printf("color_type %d bit_depth %d channels %d\n", + color_type, bit_depth, channels); + + if (!(bit_depth == 8 && + (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) { + return -7; + printf("exiting -7\n"); + goto exit; + } + + unsigned char* row = malloc(width); + int y; + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, row, NULL); + int w = (row[1] << 8) | row[0]; + int h = (row[3] << 8) | row[2]; + int len = row[4]; + char* loc = row+5; + + printf("row %d: %s %d x %d\n", y, loc, w, h); + + if (y+1+h >= height || matches_locale(loc)) { + printf(" taking this one\n"); + + surface = malloc(sizeof(GGLSurface)); + if (surface == NULL) { + result = -8; + goto exit; + } + unsigned char* pData = malloc(w*h); + + surface->version = sizeof(GGLSurface); + surface->width = w; + surface->height = h; + surface->stride = w; /* Yes, pixels, not bytes */ + surface->data = pData; + surface->format = GGL_PIXEL_FORMAT_A_8; + + int i; + for (i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row, NULL); + memcpy(pData + i*w, row, w); + } + + *pSurface = (gr_surface) surface; + break; + } else { + printf(" skipping\n"); + int i; + for (i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row, NULL); + } + } + } + +exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (fp != NULL) { + fclose(fp); + } + if (result < 0) { + if (surface) { + free(surface); + } + } + return result; +} + void res_free_surface(gr_surface surface) { GGLSurface* pSurface = (GGLSurface*) surface; if (pSurface) { diff --git a/recovery.cpp b/recovery.cpp index ce4358a5..e374c7d5 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -54,6 +54,7 @@ static const struct option OPTIONS[] = { { "wipe_cache", no_argument, NULL, 'c' }, { "show_text", no_argument, NULL, 't' }, { "just_exit", no_argument, NULL, 'x' }, + { "locale", required_argument, NULL, 'l' }, { NULL, 0, NULL, 0 }, }; @@ -62,6 +63,7 @@ static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install"; +static const char *LOCALE_FILE = "/cache/recovery/locale"; static const char *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; @@ -69,6 +71,7 @@ static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; +char* locale = NULL; /* * The recovery tool communicates with the main system through /cache files. @@ -283,6 +286,15 @@ finish_recovery(const char *send_intent) { chmod(LAST_LOG_FILE, 0640); chmod(LAST_INSTALL_FILE, 0644); + // Save the locale to cache, so if recovery is next started up + // without a --locale argument (eg, directly from the bootloader) + // it will use the last-known locale. + if (locale != NULL) { + FILE* fp = fopen(LOCALE_FILE, "w"); + fwrite(locale, 1, strlen(locale), fp); + fclose(fp); + } + // Reset to normal system boot so recovery won't cycle indefinitely. struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); @@ -300,7 +312,7 @@ finish_recovery(const char *send_intent) { static int erase_volume(const char *volume) { - ui->SetBackground(RecoveryUI::INSTALLING); + ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); ui->Print("Formatting %s...\n", volume); @@ -658,6 +670,7 @@ prompt_and_wait(Device* device) { for (;;) { finish_recovery(NULL); + ui->SetBackground(RecoveryUI::NO_COMMAND); ui->SetProgressType(RecoveryUI::EMPTY); int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device); @@ -679,6 +692,7 @@ prompt_and_wait(Device* device) { break; case Device::WIPE_CACHE: + ui->ShowText(false); ui->Print("\n-- Wiping cache...\n"); erase_volume("/cache"); ui->Print("Cache wipe complete.\n"); @@ -757,6 +771,24 @@ print_property(const char *key, const char *name, void *cookie) { printf("%s=%s\n", key, name); } +static void +load_locale_from_cache() { + FILE* fp = fopen_path(LOCALE_FILE, "r"); + char buffer[80]; + if (fp != NULL) { + fgets(buffer, sizeof(buffer), fp); + int j = 0; + int i; + for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) { + if (!isspace(buffer[i])) { + buffer[j++] = buffer[i]; + } + } + buffer[j] = 0; + locale = strdup(buffer); + } +} + int main(int argc, char **argv) { time_t start = time(NULL); @@ -779,18 +811,13 @@ main(int argc, char **argv) { printf("Starting recovery on %s", ctime(&start)); - Device* device = make_device(); - ui = device->GetUI(); - - ui->Init(); - ui->SetBackground(RecoveryUI::NONE); load_volume_table(); get_args(&argc, &argv); int previous_runs = 0; const char *send_intent = NULL; const char *update_package = NULL; - int wipe_data = 0, wipe_cache = 0; + int wipe_data = 0, wipe_cache = 0, show_text = 0; bool just_exit = false; int arg; @@ -801,14 +828,27 @@ main(int argc, char **argv) { case 'u': update_package = optarg; break; case 'w': wipe_data = wipe_cache = 1; break; case 'c': wipe_cache = 1; break; - case 't': ui->ShowText(true); break; + case 't': show_text = 1; break; case 'x': just_exit = true; break; + case 'l': locale = optarg; break; case '?': LOGE("Invalid command argument\n"); continue; } } + if (locale == NULL) { + load_locale_from_cache(); + } + printf("locale is [%s]\n", locale); + + Device* device = make_device(); + ui = device->GetUI(); + + ui->Init(); + ui->SetBackground(RecoveryUI::NONE); + if (show_text) ui->ShowText(true); + #ifdef HAVE_SELINUX struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } @@ -868,10 +908,13 @@ main(int argc, char **argv) { if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n"); } else if (!just_exit) { - status = INSTALL_ERROR; // No command specified + status = INSTALL_NONE; // No command specified + ui->SetBackground(RecoveryUI::NO_COMMAND); } - if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR); + if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { + ui->SetBackground(RecoveryUI::ERROR); + } if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { prompt_and_wait(device); } diff --git a/res/images/erasing_text.png b/res/images/erasing_text.png new file mode 100644 index 0000000000000000000000000000000000000000..f92119772c1a5184c3844e593c69961e0ab247ca GIT binary patch literal 4511 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 qg^kz3#)fz;Y`hjWM*Lsa!u}V#445&=(@ 0 && !show_text) { + if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && + installing_frames > 0 && !show_text) { installingFrame = (installingFrame + 1) % installing_frames; redraw = 1; } @@ -283,6 +296,13 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { } } +void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) { + int result = res_create_localized_surface(filename, surface); + if (result < 0) { + LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + } +} + void ScreenRecoveryUI::Init() { gr_init(); @@ -295,11 +315,19 @@ void ScreenRecoveryUI::Init() text_cols = gr_fb_width() / CHAR_WIDTH; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; - LoadBitmap("icon_installing", &backgroundIcon[INSTALLING]); + LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); + backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); + backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; + LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); + LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]); + LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]); + LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); + LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); + int i; progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * @@ -321,14 +349,6 @@ void ScreenRecoveryUI::Init() sprintf(filename, "icon_installing_overlay%02d", i+1); LoadBitmap(filename, installationOverlay+i); } - - // Adjust the offset to account for the positioning of the - // base image on the screen. - if (backgroundIcon[INSTALLING] != NULL) { - gr_surface bg = backgroundIcon[INSTALLING]; - install_overlay_offset_x += (gr_fb_width() - gr_get_width(bg)) / 2; - install_overlay_offset_y += (gr_fb_height() - gr_get_height(bg)) / 2; - } } else { installationOverlay = NULL; } @@ -343,6 +363,17 @@ void ScreenRecoveryUI::SetBackground(Icon icon) pthread_mutex_lock(&updateMutex); currentIcon = icon; update_screen_locked(); + + // Adjust the offset to account for the positioning of the + // base image on the screen. + if (backgroundIcon[icon] != NULL) { + gr_surface bg = backgroundIcon[icon]; + gr_surface text = backgroundText[icon]; + overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2; + overlay_offset_y = install_overlay_offset_y + + (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2; + } + pthread_mutex_unlock(&updateMutex); } diff --git a/screen_ui.h b/screen_ui.h index 34929ee1..16ee741b 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -57,7 +57,8 @@ class ScreenRecoveryUI : public RecoveryUI { int installingFrame; pthread_mutex_t updateMutex; - gr_surface backgroundIcon[3]; + gr_surface backgroundIcon[5]; + gr_surface backgroundText[5]; gr_surface *installationOverlay; gr_surface *progressBarIndeterminate; gr_surface progressBarEmpty; @@ -92,6 +93,7 @@ class ScreenRecoveryUI : public RecoveryUI { int indeterminate_frames; int installing_frames; int install_overlay_offset_x, install_overlay_offset_y; + int overlay_offset_x, overlay_offset_y; void draw_install_overlay_locked(int frame); void draw_background_locked(Icon icon); @@ -104,7 +106,7 @@ class ScreenRecoveryUI : public RecoveryUI { void progress_loop(); void LoadBitmap(const char* filename, gr_surface* surface); - + void LoadLocalizedBitmap(const char* filename, gr_surface* surface); }; #endif // RECOVERY_UI_H diff --git a/ui.h b/ui.h index 0d3b7bb9..ccbb466a 100644 --- a/ui.h +++ b/ui.h @@ -31,7 +31,7 @@ class RecoveryUI { virtual void Init(); // Set the overall recovery state ("background image"). - enum Icon { NONE, INSTALLING, ERROR }; + enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; virtual void SetBackground(Icon icon) = 0; // --- progress indicator ---