forked from len0rd/rockbox
when calling splash, splash_progress repeatedly text rearranges and the width changes leaving artifacts on the screen from the previously displayed prompt save the max width seen between calls to ensure the splash prompt is never more narrow. Height could be stored in a similar manner but I deemed it unnecessary as typically the text gets longer and the prompt increases in height while the reflow causes the prompt to get more narrow. keep track of the current activity and reset the max_width when activities change (except bootloader) Change-Id: I3303bd9135ab1d7bcf28bd5c28d0ab5346bf2697
306 lines
9.1 KiB
C
306 lines
9.1 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) Daniel Stenberg (2002)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "stdarg.h"
|
|
#include "string.h"
|
|
#include "rbunicode.h"
|
|
#include "stdio.h"
|
|
#include "kernel.h"
|
|
#include "screen_access.h"
|
|
#include "lang.h"
|
|
#include "settings.h"
|
|
#include "talk.h"
|
|
#include "splash.h"
|
|
#include "viewport.h"
|
|
#include "strptokspn_r.h"
|
|
#include "scrollbar.h"
|
|
#include "font.h"
|
|
#ifndef BOOTLOADER
|
|
#include "misc.h" /* get_current_activity */
|
|
#endif
|
|
|
|
static long progress_next_tick = 0;
|
|
|
|
#define MAXLINES (LCD_HEIGHT/6)
|
|
#define MAXBUFFER 512
|
|
#define RECT_SPACING 3
|
|
#define SPLASH_MEMORY_INTERVAL (HZ)
|
|
|
|
static bool splash_internal(struct screen * screen, const char *fmt, va_list ap,
|
|
struct viewport *vp, int addl_lines)
|
|
{
|
|
static int max_width[NB_SCREENS] = {2*RECT_SPACING};
|
|
#ifndef BOOTLOADER
|
|
static enum current_activity last_act = ACTIVITY_UNKNOWN;
|
|
enum current_activity act = get_current_activity();
|
|
|
|
if (last_act != act) /* changed activities reset max_width */
|
|
{
|
|
FOR_NB_SCREENS(i)
|
|
max_width[i] = 2*RECT_SPACING;
|
|
last_act = act;
|
|
}
|
|
#endif
|
|
/* prevent screen artifacts by keeping the max width seen */
|
|
int min_width = max_width[screen->screen_type];
|
|
char splash_buf[MAXBUFFER];
|
|
struct splash_lines {
|
|
const char *str;
|
|
size_t len;
|
|
} lines[MAXLINES];
|
|
const char *next;
|
|
const char *lastbreak = NULL;
|
|
const char *store = NULL;
|
|
int line = 0;
|
|
int x = 0;
|
|
int y, i;
|
|
int space_w, w, chr_h;
|
|
int width, height;
|
|
int maxw = min_width - 2*RECT_SPACING;
|
|
int fontnum = vp->font;
|
|
|
|
char lastbrkchr;
|
|
size_t len, next_len;
|
|
const char matchstr[] = "\r\n\f\v\t ";
|
|
font_getstringsize(" ", &space_w, &chr_h, fontnum);
|
|
y = chr_h + (addl_lines * chr_h);
|
|
|
|
vsnprintf(splash_buf, sizeof(splash_buf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
/* break splash string into display lines, doing proper word wrap */
|
|
next = strptokspn_r(splash_buf, matchstr, &next_len, &store);
|
|
if (!next)
|
|
return false; /* nothing to display */
|
|
|
|
lines[line].len = next_len;
|
|
lines[line].str = next;
|
|
while (true)
|
|
{
|
|
w = font_getstringnsize(next, next_len, NULL, NULL, fontnum);
|
|
if (lastbreak)
|
|
{
|
|
len = next - lastbreak;
|
|
int next_w = len * space_w;
|
|
if (x + next_w + w > vp->width - RECT_SPACING*2 || lastbrkchr != ' ')
|
|
{ /* too wide, or control character wrap */
|
|
if (x > maxw)
|
|
maxw = x;
|
|
if ((y + chr_h * 2 > vp->height) || (line >= (MAXLINES-1)))
|
|
break; /* screen full or out of lines */
|
|
x = 0;
|
|
y += chr_h;
|
|
|
|
/* split when it fits since we didn't find a valid token to break on */
|
|
size_t nl = next_len;
|
|
while (w > vp->width && --nl > 0)
|
|
w = font_getstringnsize(next, nl, NULL, NULL, fontnum);
|
|
|
|
if (nl > 1 && nl != next_len)
|
|
{
|
|
next_len = nl;
|
|
store = next + nl; /* move the start pos for the next token read */
|
|
}
|
|
|
|
lines[++line].len = next_len;
|
|
lines[line].str = next;
|
|
}
|
|
else
|
|
{
|
|
/* restore & calculate spacing */
|
|
lines[line].len += next_len + 1;
|
|
x += next_w;
|
|
}
|
|
}
|
|
x += w;
|
|
|
|
lastbreak = next + next_len;
|
|
lastbrkchr = *lastbreak;
|
|
|
|
next = strptokspn_r(NULL, matchstr, &next_len, &store);
|
|
|
|
if (!next)
|
|
{ /* no more words */
|
|
if (x > maxw)
|
|
maxw = x;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* prepare viewport
|
|
* First boundaries, then the background filling, then the border and finally
|
|
* the text*/
|
|
|
|
screen->scroll_stop();
|
|
|
|
width = maxw + 2*RECT_SPACING;
|
|
height = y + 2*RECT_SPACING;
|
|
|
|
if (width > vp->width)
|
|
width = vp->width;
|
|
if (height > vp->height)
|
|
height = vp->height;
|
|
|
|
vp->x += (vp->width - width) / 2;
|
|
vp->y += (vp->height - height) / 2;
|
|
vp->width = width;
|
|
vp->height = height;
|
|
|
|
/* prevent artifacts by locking to max width observed on repeated calls */
|
|
max_width[screen->screen_type] = width;
|
|
|
|
vp->flags |= VP_FLAG_ALIGN_CENTER;
|
|
#if LCD_DEPTH > 1
|
|
unsigned fg = 0, bg = 0;
|
|
bool broken = false;
|
|
|
|
if (screen->depth > 1)
|
|
{
|
|
fg = screen->get_foreground();
|
|
bg = screen->get_background();
|
|
|
|
broken = (fg == bg) ||
|
|
(bg == 63422 && fg == 65535); /* -> iPod reFresh themes from '22 */
|
|
|
|
vp->drawmode = DRMODE_FG;
|
|
/* can't do vp->fg_pattern here, since set_foreground does a bit more on
|
|
* greyscale */
|
|
screen->set_foreground(broken ? SCREEN_COLOR_TO_NATIVE(screen, LCD_LIGHTGRAY) :
|
|
bg); /* gray as fallback for broken themes */
|
|
}
|
|
else
|
|
#endif
|
|
vp->drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
|
|
|
|
screen->fill_viewport();
|
|
|
|
#if LCD_DEPTH > 1
|
|
if (screen->depth > 1)
|
|
/* can't do vp->fg_pattern here, since set_foreground does a bit more on
|
|
* greyscale */
|
|
screen->set_foreground(broken ? SCREEN_COLOR_TO_NATIVE(screen, LCD_BLACK) :
|
|
fg); /* black as fallback for broken themes */
|
|
else
|
|
#endif
|
|
vp->drawmode = DRMODE_SOLID;
|
|
|
|
screen->draw_border_viewport();
|
|
|
|
/* print the message to screen */
|
|
for(i = 0, y = RECT_SPACING; i <= line; i++, y+= chr_h)
|
|
{
|
|
screen->putsxyf(0, y, "%.*s", lines[i].len, lines[i].str);
|
|
}
|
|
return true; /* needs update */
|
|
}
|
|
|
|
void splashf(int ticks, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
/* If fmt is a lang ID then get the corresponding string (which
|
|
still might contain % place holders). */
|
|
fmt = P2STR((unsigned char *)fmt);
|
|
FOR_NB_SCREENS(i)
|
|
{
|
|
struct screen * screen = &(screens[i]);
|
|
struct viewport vp;
|
|
viewport_set_defaults(&vp, screen->screen_type);
|
|
struct viewport *last_vp = screen->set_viewport(&vp);
|
|
|
|
va_start(ap, fmt);
|
|
if (splash_internal(screen, fmt, ap, &vp, 0))
|
|
screen->update_viewport();
|
|
va_end(ap);
|
|
|
|
screen->set_viewport(last_vp);
|
|
}
|
|
if (ticks)
|
|
sleep(ticks);
|
|
}
|
|
|
|
void splash(int ticks, const char *str)
|
|
{
|
|
#if !defined(SIMULATOR)
|
|
long id;
|
|
/* fmt may be a so called virtual pointer. See settings.h. */
|
|
if((id = P2ID((const unsigned char*)str)) >= 0)
|
|
/* If fmt specifies a voicefont ID, and voice menus are
|
|
enabled, then speak it. */
|
|
cond_talk_ids_fq(id);
|
|
#endif
|
|
splashf(ticks, "%s", P2STR((const unsigned char*)str));
|
|
}
|
|
|
|
/* set delay before progress meter is shown */
|
|
void splash_progress_set_delay(long delay_ticks)
|
|
{
|
|
progress_next_tick = current_tick + delay_ticks;
|
|
}
|
|
|
|
/* splash a progress meter */
|
|
void splash_progress(int current, int total, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int vp_flag = VP_FLAG_VP_DIRTY;
|
|
/* progress update tick */
|
|
long now = current_tick;
|
|
|
|
if (current < total)
|
|
{
|
|
if(TIME_BEFORE(now, progress_next_tick))
|
|
return;
|
|
/* limit to 20fps */
|
|
progress_next_tick = now + HZ/20;
|
|
vp_flag = 0; /* don't mark vp dirty to prevent flashing */
|
|
}
|
|
|
|
/* If fmt is a lang ID then get the corresponding string (which
|
|
still might contain % place holders). */
|
|
fmt = P2STR((unsigned char *)fmt);
|
|
FOR_NB_SCREENS(i)
|
|
{
|
|
struct screen * screen = &(screens[i]);
|
|
struct viewport vp;
|
|
viewport_set_defaults(&vp, screen->screen_type);
|
|
struct viewport *last_vp = screen->set_viewport_ex(&vp, vp_flag);
|
|
|
|
va_start(ap, fmt);
|
|
if (splash_internal(screen, fmt, ap, &vp, 1))
|
|
{
|
|
int size = screen->getcharheight();
|
|
int x = RECT_SPACING;
|
|
int y = vp.height - size - RECT_SPACING;
|
|
int w = vp.width - RECT_SPACING * 2;
|
|
int h = size;
|
|
#ifdef HAVE_LCD_COLOR
|
|
const int sb_flags = HORIZONTAL | FOREGROUND;
|
|
#else
|
|
const int sb_flags = HORIZONTAL;
|
|
#endif
|
|
gui_scrollbar_draw(screen, x, y, w, h, total, 0, current, sb_flags);
|
|
|
|
screen->update_viewport();
|
|
}
|
|
va_end(ap);
|
|
|
|
screen->set_viewport(last_vp);
|
|
}
|
|
}
|