summaryrefslogtreecommitdiffstats
path: root/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui.c')
-rw-r--r--ui.c248
1 files changed, 192 insertions, 56 deletions
diff --git a/ui.c b/ui.c
index 84a9b85c2..0744da4d9 100644
--- a/ui.c
+++ b/ui.c
@@ -14,18 +14,22 @@
* limitations under the License.
*/
+#include <errno.h>
+#include <fcntl.h>
#include <linux/input.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/reboot.h>
+#include <sys/stat.h>
#include <sys/time.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "common.h"
+#include <cutils/android_reboot.h>
#include "minui/minui.h"
#include "recovery_ui.h"
@@ -35,30 +39,32 @@
#define CHAR_WIDTH 10
#define CHAR_HEIGHT 18
-#define PROGRESSBAR_INDETERMINATE_STATES 6
-#define PROGRESSBAR_INDETERMINATE_FPS 15
+#define UI_WAIT_KEY_TIMEOUT_SEC 120
+
+UIParameters ui_parameters = {
+ 6, // indeterminate progress bar frames
+ 20, // fps
+ 7, // installation icon frames (0 == static image)
+ 23, 83, // installation icon overlay offset
+};
static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER;
static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
-static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
+static gr_surface *gInstallationOverlay;
+static gr_surface *gProgressBarIndeterminate;
static gr_surface gProgressBarEmpty;
static gr_surface gProgressBarFill;
static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
{ &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
{ &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" },
- { &gProgressBarIndeterminate[0], "indeterminate1" },
- { &gProgressBarIndeterminate[1], "indeterminate2" },
- { &gProgressBarIndeterminate[2], "indeterminate3" },
- { &gProgressBarIndeterminate[3], "indeterminate4" },
- { &gProgressBarIndeterminate[4], "indeterminate5" },
- { &gProgressBarIndeterminate[5], "indeterminate6" },
{ &gProgressBarEmpty, "progress_empty" },
{ &gProgressBarFill, "progress_fill" },
{ NULL, NULL },
};
-static gr_surface gCurrentIcon = NULL;
+static int gCurrentIcon = 0;
+static int gInstallingFrame = 0;
static enum ProgressBarType {
PROGRESSBAR_TYPE_NONE,
@@ -68,7 +74,7 @@ static enum ProgressBarType {
// Progress bar scope of current operation
static float gProgressScopeStart = 0, gProgressScopeSize = 0, gProgress = 0;
-static time_t gProgressScopeTime, gProgressScopeDuration;
+static double gProgressScopeTime, gProgressScopeDuration;
// Set to 1 when both graphics pages are the same (except for the progress bar)
static int gPagesIdentical = 0;
@@ -78,6 +84,7 @@ static char text[MAX_ROWS][MAX_COLS];
static int text_cols = 0, text_rows = 0;
static int text_col = 0, text_row = 0, text_top = 0;
static int show_text = 0;
+static int show_text_ever = 0; // has show_text ever been 1?
static char menu[MAX_ROWS][MAX_COLS];
static int show_menu = 0;
@@ -89,20 +96,46 @@ static pthread_cond_t key_queue_cond = PTHREAD_COND_INITIALIZER;
static int key_queue[256], key_queue_len = 0;
static volatile char key_pressed[KEY_MAX + 1];
+// Return the current time as a double (including fractions of a second).
+static double now() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+// Draw the given frame over the installation overlay animation. The
+// background is not cleared or draw with the base icon first; we
+// assume that the frame already contains some other frame of the
+// animation. Does nothing if no overlay animation is defined.
+// Should only be called with gUpdateMutex locked.
+static void draw_install_overlay_locked(int frame) {
+ if (gInstallationOverlay == NULL) return;
+ gr_surface surface = gInstallationOverlay[frame];
+ int iconWidth = gr_get_width(surface);
+ int iconHeight = gr_get_height(surface);
+ gr_blit(surface, 0, 0, iconWidth, iconHeight,
+ ui_parameters.install_overlay_offset_x,
+ ui_parameters.install_overlay_offset_y);
+}
+
// Clear the screen and draw the currently selected background icon (if any).
// Should only be called with gUpdateMutex locked.
-static void draw_background_locked(gr_surface icon)
+static void draw_background_locked(int icon)
{
gPagesIdentical = 0;
gr_color(0, 0, 0, 255);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
if (icon) {
- int iconWidth = gr_get_width(icon);
- int iconHeight = gr_get_height(icon);
+ gr_surface surface = gBackgroundIcon[icon];
+ int iconWidth = gr_get_width(surface);
+ int iconHeight = gr_get_height(surface);
int iconX = (gr_fb_width() - iconWidth) / 2;
int iconY = (gr_fb_height() - iconHeight) / 2;
- gr_blit(icon, 0, 0, iconWidth, iconHeight, iconX, iconY);
+ gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
+ if (icon == BACKGROUND_ICON_INSTALLING) {
+ draw_install_overlay_locked(gInstallingFrame);
+ }
}
}
@@ -110,35 +143,39 @@ static void draw_background_locked(gr_surface icon)
// Should only be called with gUpdateMutex locked.
static void draw_progress_locked()
{
- if (gProgressBarType == PROGRESSBAR_TYPE_NONE) return;
+ if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) {
+ draw_install_overlay_locked(gInstallingFrame);
+ }
- int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
- int width = gr_get_width(gProgressBarEmpty);
- int height = gr_get_height(gProgressBarEmpty);
+ if (gProgressBarType != PROGRESSBAR_TYPE_NONE) {
+ int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
+ int width = gr_get_width(gProgressBarEmpty);
+ int height = gr_get_height(gProgressBarEmpty);
- int dx = (gr_fb_width() - width)/2;
- int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
+ int dx = (gr_fb_width() - width)/2;
+ int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
- // Erase behind the progress bar (in case this was a progress-only update)
- gr_color(0, 0, 0, 255);
- gr_fill(dx, dy, width, height);
+ // Erase behind the progress bar (in case this was a progress-only update)
+ gr_color(0, 0, 0, 255);
+ gr_fill(dx, dy, width, height);
- if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
- float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
- int pos = (int) (progress * width);
+ if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
+ float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
+ int pos = (int) (progress * width);
- if (pos > 0) {
- gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
- }
- if (pos < width-1) {
- gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
+ if (pos > 0) {
+ gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
+ }
+ if (pos < width-1) {
+ gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
+ }
}
- }
- if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
- static int frame = 0;
- gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
- frame = (frame + 1) % PROGRESSBAR_INDETERMINATE_STATES;
+ if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
+ static int frame = 0;
+ gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
+ frame = (frame + 1) % ui_parameters.indeterminate_frames;
+ }
}
}
@@ -203,7 +240,7 @@ static void update_progress_locked(void)
draw_screen_locked(); // Must redraw the whole screen
gPagesIdentical = 1;
} else {
- draw_progress_locked(); // Draw only the progress bar
+ draw_progress_locked(); // Draw only the progress bar and overlays
}
gr_flip();
}
@@ -211,29 +248,49 @@ static void update_progress_locked(void)
// Keeps the progress bar updated, even when the process is otherwise busy.
static void *progress_thread(void *cookie)
{
+ double interval = 1.0 / ui_parameters.update_fps;
for (;;) {
- usleep(1000000 / PROGRESSBAR_INDETERMINATE_FPS);
+ double start = now();
pthread_mutex_lock(&gUpdateMutex);
+ int redraw = 0;
+
+ // update the installation animation, if active
+ // skip this if we have a text overlay (too expensive to update)
+ if (gCurrentIcon == BACKGROUND_ICON_INSTALLING &&
+ ui_parameters.installing_frames > 0 &&
+ !show_text) {
+ gInstallingFrame =
+ (gInstallingFrame + 1) % ui_parameters.installing_frames;
+ redraw = 1;
+ }
+
// update the progress bar animation, if active
// skip this if we have a text overlay (too expensive to update)
if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE && !show_text) {
- update_progress_locked();
+ redraw = 1;
}
// move the progress bar forward on timed intervals, if configured
int duration = gProgressScopeDuration;
if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && duration > 0) {
- int elapsed = time(NULL) - gProgressScopeTime;
+ double elapsed = now() - gProgressScopeTime;
float progress = 1.0 * elapsed / duration;
if (progress > 1.0) progress = 1.0;
if (progress > gProgress) {
gProgress = progress;
- update_progress_locked();
+ redraw = 1;
}
}
+ if (redraw) update_progress_locked();
+
pthread_mutex_unlock(&gUpdateMutex);
+ double end = now();
+ // minimum of 20ms delay between frames
+ double delay = interval - (end-start);
+ if (delay < 0.02) delay = 0.02;
+ usleep((long)(delay * 1000000));
}
return NULL;
}
@@ -295,12 +352,13 @@ static void *input_thread(void *cookie)
if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
pthread_mutex_lock(&gUpdateMutex);
show_text = !show_text;
+ if (show_text) show_text_ever = 1;
update_screen_locked();
pthread_mutex_unlock(&gUpdateMutex);
}
if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
- reboot(RB_AUTOBOOT);
+ android_reboot(ANDROID_RB_RESTART, 0, 0);
}
}
return NULL;
@@ -323,13 +381,47 @@ void ui_init(void)
for (i = 0; BITMAPS[i].name != NULL; ++i) {
int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
if (result < 0) {
- if (result == -2) {
- LOGI("Bitmap %s missing header\n", BITMAPS[i].name);
- } else {
- LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
+ LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
+ }
+ }
+
+ gProgressBarIndeterminate = malloc(ui_parameters.indeterminate_frames *
+ sizeof(gr_surface));
+ for (i = 0; i < ui_parameters.indeterminate_frames; ++i) {
+ char filename[40];
+ // "indeterminate01.png", "indeterminate02.png", ...
+ sprintf(filename, "indeterminate%02d", i+1);
+ int result = res_create_surface(filename, gProgressBarIndeterminate+i);
+ if (result < 0) {
+ LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
+ }
+ }
+
+ if (ui_parameters.installing_frames > 0) {
+ gInstallationOverlay = malloc(ui_parameters.installing_frames *
+ sizeof(gr_surface));
+ for (i = 0; i < ui_parameters.installing_frames; ++i) {
+ char filename[40];
+ // "icon_installing_overlay01.png",
+ // "icon_installing_overlay02.png", ...
+ sprintf(filename, "icon_installing_overlay%02d", i+1);
+ int result = res_create_surface(filename, gInstallationOverlay+i);
+ if (result < 0) {
+ LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
}
- *BITMAPS[i].surface = NULL;
}
+
+ // Adjust the offset to account for the positioning of the
+ // base image on the screen.
+ if (gBackgroundIcon[BACKGROUND_ICON_INSTALLING] != NULL) {
+ gr_surface bg = gBackgroundIcon[BACKGROUND_ICON_INSTALLING];
+ ui_parameters.install_overlay_offset_x +=
+ (gr_fb_width() - gr_get_width(bg)) / 2;
+ ui_parameters.install_overlay_offset_y +=
+ (gr_fb_height() - gr_get_height(bg)) / 2;
+ }
+ } else {
+ gInstallationOverlay = NULL;
}
pthread_t t;
@@ -340,7 +432,7 @@ void ui_init(void)
void ui_set_background(int icon)
{
pthread_mutex_lock(&gUpdateMutex);
- gCurrentIcon = gBackgroundIcon[icon];
+ gCurrentIcon = icon;
update_screen_locked();
pthread_mutex_unlock(&gUpdateMutex);
}
@@ -361,7 +453,7 @@ void ui_show_progress(float portion, int seconds)
gProgressBarType = PROGRESSBAR_TYPE_NORMAL;
gProgressScopeStart += gProgressScopeSize;
gProgressScopeSize = portion;
- gProgressScopeTime = time(NULL);
+ gProgressScopeTime = now();
gProgressScopeDuration = seconds;
gProgress = 0;
update_progress_locked();
@@ -481,23 +573,67 @@ int ui_text_visible()
return visible;
}
+int ui_text_ever_visible()
+{
+ pthread_mutex_lock(&gUpdateMutex);
+ int ever_visible = show_text_ever;
+ pthread_mutex_unlock(&gUpdateMutex);
+ return ever_visible;
+}
+
void ui_show_text(int visible)
{
pthread_mutex_lock(&gUpdateMutex);
show_text = visible;
+ if (show_text) show_text_ever = 1;
update_screen_locked();
pthread_mutex_unlock(&gUpdateMutex);
}
+// Return true if USB is connected.
+static int usb_connected() {
+ int fd = open("/sys/class/switch/usb_connected/state", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to open /sys/class/switch/usb_connected/state: %s\n",
+ strerror(errno));
+ return 0;
+ }
+
+ char buf;
+ int connected = (read(fd, &buf, 1) == 1) && (buf == '1');
+ if (close(fd) < 0) {
+ printf("failed to close /sys/class/switch/usb_connected/state: %s\n",
+ strerror(errno));
+ }
+ return connected;
+}
+
int ui_wait_key()
{
pthread_mutex_lock(&key_queue_mutex);
- while (key_queue_len == 0) {
- pthread_cond_wait(&key_queue_cond, &key_queue_mutex);
- }
- int key = key_queue[0];
- memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+ // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
+ // plugged in.
+ do {
+ struct timeval now;
+ struct timespec timeout;
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec;
+ timeout.tv_nsec = now.tv_usec * 1000;
+ timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
+
+ int rc = 0;
+ while (key_queue_len == 0 && rc != ETIMEDOUT) {
+ rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
+ &timeout);
+ }
+ } while (usb_connected() && key_queue_len == 0);
+
+ int key = -1;
+ if (key_queue_len > 0) {
+ key = key_queue[0];
+ memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+ }
pthread_mutex_unlock(&key_queue_mutex);
return key;
}