summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/gui/skin_engine/skin_render.c9
-rw-r--r--apps/screen_access.c4
-rw-r--r--apps/screen_access.h2
-rw-r--r--firmware/drivers/lcd-bitmap-common.c180
-rw-r--r--firmware/drivers/lcd-scroll.c111
-rw-r--r--firmware/export/scroll_engine.h30
-rw-r--r--firmware/scroll_engine.c19
7 files changed, 201 insertions, 154 deletions
diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c
index 67f1f0f448..28483cbc49 100644
--- a/apps/gui/skin_engine/skin_render.c
+++ b/apps/gui/skin_engine/skin_render.c
@@ -779,7 +779,9 @@ void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
if (refresh_type && needs_update)
{
if (info.force_redraw)
- display->scroll_stop_viewport_line(&skin_viewport->vp, info.line_number);
+ display->scroll_stop_viewport_rect(&skin_viewport->vp,
+ 0, info.line_number*display->getcharheight(),
+ skin_viewport->vp.width, display->getcharheight());
write_line(display, align, info.line_number,
info.line_scrolls, info.text_style);
}
@@ -967,8 +969,11 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
/* only update if the line needs to be, and there is something to write */
if (refresh_type && needs_update)
{
+ struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, viewer->vp);
if (!info.force_redraw)
- display->scroll_stop_viewport_line(&skin_viewport->vp, info.line_number);
+ display->scroll_stop_viewport_rect(vp,
+ 0, info.line_number*display->getcharheight(),
+ vp->width, display->getcharheight());
write_line(display, align, info.line_number,
info.line_scrolls, info.text_style);
}
diff --git a/apps/screen_access.c b/apps/screen_access.c
index 161e596aa2..f454e0adef 100644
--- a/apps/screen_access.c
+++ b/apps/screen_access.c
@@ -247,9 +247,9 @@ struct screen screens[NB_SCREENS] =
.scroll_delay=&lcd_scroll_delay,
.clear_display=&lcd_clear_display,
.clear_viewport=&lcd_clear_viewport,
+ .scroll_stop_viewport_rect=&lcd_scroll_stop_viewport_rect,
.scroll_stop=&lcd_scroll_stop,
.scroll_stop_viewport=&lcd_scroll_stop_viewport,
- .scroll_stop_viewport_line=&lcd_scroll_stop_viewport_line,
.update=&lcd_update,
.update_viewport=&lcd_update_viewport,
.backlight_on=&backlight_on,
@@ -348,9 +348,9 @@ struct screen screens[NB_SCREENS] =
.scroll_delay=&lcd_remote_scroll_delay,
.clear_display=&lcd_remote_clear_display,
.clear_viewport=&lcd_remote_clear_viewport,
+ .scroll_stop_viewport_rect=&lcd_remote_scroll_stop_viewport_rect,
.scroll_stop=&lcd_remote_scroll_stop,
.scroll_stop_viewport=&lcd_remote_scroll_stop_viewport,
- .scroll_stop_viewport_line=&lcd_remote_scroll_stop_viewport_line,
.update=&lcd_remote_update,
.update_viewport=&lcd_remote_update_viewport,
.backlight_on=&remote_backlight_on,
diff --git a/apps/screen_access.h b/apps/screen_access.h
index 448437c637..90b63ea338 100644
--- a/apps/screen_access.h
+++ b/apps/screen_access.h
@@ -149,7 +149,7 @@ struct screen
void (*clear_viewport)(void);
void (*scroll_stop)(void);
void (*scroll_stop_viewport)(const struct viewport *vp);
- void (*scroll_stop_viewport_line)(const struct viewport *vp, int line);
+ void (*scroll_stop_viewport_rect)(const struct viewport* vp, int x, int y, int width, int height);
void (*update)(void);
void (*update_viewport)(void);
void (*backlight_on)(void);
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index c04f57ef22..f3e700a4a1 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -412,12 +412,10 @@ static void LCDFN(putsxyofs_style)(int xpos, int ypos,
/*** Line oriented text output ***/
-/* put a string at a given char position */
void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str,
int style, int x_offset, int y_offset)
{
int xpos, ypos, h;
- LCDFN(scroll_stop_viewport_line)(current_vp, y);
if(!str)
return;
@@ -425,14 +423,15 @@ void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str,
if ((style&STYLE_XY_PIXELS) == 0)
{
xpos = x * LCDFN(getstringsize)(" ", NULL, NULL);
- ypos = y * h + y_offset;
+ ypos = y * h;
}
else
{
xpos = x;
- ypos = y + y_offset;
+ ypos = y;
}
- LCDFN(putsxyofs_style)(xpos, ypos, str, style, h, x_offset);
+ LCDFN(scroll_stop_viewport_rect)(current_vp, x, y, current_vp->width - x, h);
+ LCDFN(putsxyofs_style)(xpos, ypos+y_offset, str, style, h, x_offset);
}
void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
@@ -469,7 +468,7 @@ void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)
/*** scrolling ***/
-static struct scrollinfo* find_scrolling_line(int line)
+static struct scrollinfo* find_scrolling_line(int x, int y)
{
struct scrollinfo* s = NULL;
int i;
@@ -477,93 +476,101 @@ static struct scrollinfo* find_scrolling_line(int line)
for(i=0; i<LCDFN(scroll_info).lines; i++)
{
s = &LCDFN(scroll_info).scroll[i];
- if (s->y == line && s->vp == current_vp)
+ if (s->x == x && s->y == y && s->vp == current_vp)
return s;
}
return NULL;
}
-void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
- int style, int x_offset, int y_offset)
+void LCDFN(scroll_fn)(struct scrollinfo* s)
+{
+ LCDFN(putsxyofs_style)(s->x, s->y, s->line, s->style, s->height, s->offset);
+}
+
+static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
+ int style, int x_offset, int y_offset,
+ bool linebased,
+ void (*scroll_func)(struct scrollinfo *),
+ void *data)
{
struct scrollinfo* s;
- char *end;
- int w, h;
- int len;
- bool restart = false;
- int space_width;
+ int width, height;
+ int w, h, cwidth, margin;
+ bool restart;
- if (!string || ((unsigned)y >= (unsigned)current_vp->height))
+ if (!string)
return;
- s = find_scrolling_line(y);
- if (!s)
- restart = true;
+ /* prepare rectangle for scrolling. x and y must be calculated early
+ * for find_scrolling_line() to work */
+ cwidth = font_get(current_vp->font)->maxwidth;
+ height = current_vp->line_height ?: (int)font_get(current_vp->font)->height;
+ y = y * (linebased ? height : 1) + y_offset;
+ x = x * (linebased ? cwidth : 1);
+ width = current_vp->width - x;
- if (restart)
- {
+ if (y >= current_vp->height)
+ return;
+
+ s = find_scrolling_line(x, y);
+ restart = !s;
+
+ if (restart) {
/* remove any previously scrolling line at the same location */
- LCDFN(scroll_stop_viewport_line)(current_vp, y);
+ LCDFN(scroll_stop_viewport_rect)(current_vp, x, y, width, height);
+ LCDFN(putsxyofs_style)(x, y, string, style, height, x_offset);
- if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) return;
- LCDFN(puts_style_xyoffset)(x, y, string, style, x_offset, y_offset);
+ if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES))
+ return;
}
+ /* get width (pixeks) of the string */
LCDFN(getstringsize)(string, &w, &h);
- if (current_vp->width - x * 8 >= w)
+ /* check if scrolling is actually necessary (consider the actual start
+ * of the line) */
+ margin = x * linebased ? cwidth : 1;
+ if (current_vp->width >= margin+w)
return;
- if (restart)
- {
+ if (restart) {
/* prepare scroll line */
s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines];
s->start_tick = current_tick + LCDFN(scroll_info).delay;
}
- strlcpy(s->line, string, sizeof s->line);
- space_width = LCDFN(getstringsize)(" ", NULL, NULL);
- /* get width */
- LCDFN(getstringsize)(s->line, &w, &h);
- if (!restart && s->width > w)
- {
- if (s->startx > w)
- s->startx = w;
- }
- s->width = w;
-
- /* scroll bidirectional or forward only depending on the string
- width */
+ /* copy contents to the line buffer */
+ strlcpy(s->linebuffer, string, sizeof(s->linebuffer));
+ /* scroll bidirectional or forward only depending on the string width */
if ( LCDFN(scroll_info).bidir_limit ) {
- s->bidir = s->width < (current_vp->width) *
+ s->bidir = w < (current_vp->width) *
(100 + LCDFN(scroll_info).bidir_limit) / 100;
}
else
s->bidir = false;
- if (!s->bidir) { /* add spaces if scrolling in the round */
- strlcat(s->line, " ", sizeof s->line);
- /* get new width incl. spaces */
- s->width += space_width * 3;
- }
-
- end = strchr(s->line, '\0');
- len = sizeof s->line - (end - s->line);
- strlcpy(end, string, MIN(current_vp->width/2, len));
+ s->scroll_func = scroll_func;
+ s->userdata = data;
- s->vp = current_vp;
- s->y = y;
- if (restart)
- {
+ if (restart) {
s->offset = x_offset;
- s->startx = x * space_width;
s->backward = false;
s->style = style;
+ /* assign the rectangle. not necessary if continuing an earlier line */
+ s->x = x;
+ s->y = y;
+ s->width = width;
+ s->height = height;
+ s->vp = current_vp;
+ LCDFN(scroll_info).lines++;
}
- s->y_offset = y_offset;
+}
- if (restart)
- LCDFN(scroll_info).lines++;
+void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
+ int style, int x_offset, int y_offset)
+{
+ LCDFN(puts_scroll_worker)(x, y, string, style, x_offset, y_offset,
+ true, LCDFN(scroll_fn), NULL);
}
void LCDFN(puts_scroll)(int x, int y, const unsigned char *string)
@@ -583,65 +590,6 @@ void LCDFN(puts_scroll_offset)(int x, int y, const unsigned char *string,
LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset);
}
-void LCDFN(scroll_fn)(void)
-{
- struct scrollinfo* s;
- int index;
- int xpos, ypos, height;
- struct viewport* old_vp = current_vp;
- bool makedelay;
-
- for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) {
- s = &LCDFN(scroll_info).scroll[index];
-
- /* check pause */
- if (TIME_BEFORE(current_tick, s->start_tick))
- continue;
-
- LCDFN(set_viewport)(s->vp);
- height = s->vp->line_height ?: (int)font_get(s->vp->font)->height;
-
- if (s->backward)
- s->offset -= LCDFN(scroll_info).step;
- else
- s->offset += LCDFN(scroll_info).step;
-
- xpos = s->startx;
- ypos = s->y * height + s->y_offset;
-
- makedelay = false;
- if (s->bidir) { /* scroll bidirectional */
- if (s->offset <= 0) {
- /* at beginning of line */
- s->offset = 0;
- s->backward = false;
- makedelay = true;
- }
- else if (s->offset >= s->width - (current_vp->width - xpos)) {
- /* at end of line */
- s->offset = s->width - (current_vp->width - xpos);
- s->backward = true;
- makedelay = true;
- }
- }
- else {
- /* scroll forward the whole time */
- if (s->offset >= s->width) {
- s->offset = 0;
- makedelay = true;
- }
- }
-
- if (makedelay)
- s->start_tick = current_tick + LCDFN(scroll_info).delay +
- LCDFN(scroll_info).ticks;
-
- LCDFN(putsxyofs_style)(xpos, ypos, s->line, s->style, height, s->offset);
- LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width-xpos, height);
- }
- LCDFN(set_viewport)(old_vp);
-}
-
void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
int style, int x_offset)
{
diff --git a/firmware/drivers/lcd-scroll.c b/firmware/drivers/lcd-scroll.c
index ffd4663c79..31c2cf20b0 100644
--- a/firmware/drivers/lcd-scroll.c
+++ b/firmware/drivers/lcd-scroll.c
@@ -54,30 +54,27 @@ void LCDFN(scroll_stop)(void)
LCDFN(scroll_info).lines = 0;
}
-/* Stop scrolling line y in the specified viewport, or all lines if y < 0 */
-void LCDFN(scroll_stop_viewport_line)(const struct viewport *current_vp, int line)
+/* Clears scrolling lines that intersect with the area */
+void LCDFN(scroll_stop_viewport_rect)(const struct viewport *vp, int x, int y, int width, int height)
{
int i = 0;
-
while (i < LCDFN(scroll_info).lines)
{
- struct viewport *vp = LCDFN(scroll_info).scroll[i].vp;
- if (((vp == current_vp)) &&
- ((line < 0) || (LCDFN(scroll_info).scroll[i].y == line)))
+ struct scrollinfo *s = &LCDFN(scroll_info).scroll[i];
+ /* check if the specified area crosses the viewport in some way */
+ if (s->vp == vp
+ && (x < (s->x+s->width) && (x+width) >= s->x)
+ && (y < (s->y+s->height) && (y+height) >= s->y))
{
/* If i is not the last active line in the array, then move
- the last item to position i */
+ the last item to position i. This compacts
+ the scroll array at the same time of removing the line */
if ((i + 1) != LCDFN(scroll_info).lines)
{
LCDFN(scroll_info).scroll[i] =
LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines-1];
}
LCDFN(scroll_info).lines--;
-
- /* A line can only appear once, so we're done,
- * unless we are clearing the whole viewport */
- if (line >= 0)
- return ;
}
else
{
@@ -87,9 +84,9 @@ void LCDFN(scroll_stop_viewport_line)(const struct viewport *current_vp, int lin
}
/* Stop all scrolling lines in the specified viewport */
-void LCDFN(scroll_stop_viewport)(const struct viewport *current_vp)
+void LCDFN(scroll_stop_viewport)(const struct viewport *vp)
{
- LCDFN(scroll_stop_viewport_line)(current_vp, -1);
+ LCDFN(scroll_stop_viewport_rect)(vp, 0, 0, vp->width, vp->height);
}
void LCDFN(scroll_speed)(int speed)
@@ -125,3 +122,89 @@ void LCDFN(jump_scroll_delay)(int ms)
LCDFN(scroll_info).jump_scroll_delay = ms / (HZ / 10);
}
#endif
+
+static void LCDFN(scroll_worker)(void)
+{
+ int index, width;
+ bool makedelay;
+ static char line_buf[SCROLL_LINE_SIZE];
+ bool is_default;
+ struct scroll_screen_info *si = &LCDFN(scroll_info);
+ struct scrollinfo *s;
+ struct viewport *vp;
+
+ unsigned fg_pattern, bg_pattern, drawmode;
+
+ for ( index = 0; index < si->lines; index++ ) {
+ s = &si->scroll[index];
+
+ /* check pause */
+ if (TIME_BEFORE(current_tick, s->start_tick))
+ continue;
+
+ s->start_tick = current_tick;
+
+ /* this runs out of the ui thread, thus we need to
+ * save and restore the current viewport since the ui thread
+ * is unaware of the swapped viewports. the vp must
+ * be switched early so that lcd_getstringsize() picks the
+ * correct font */
+ vp = LCDFN(get_viewport)(&is_default);
+ LCDFN(set_viewport)(s->vp);
+
+ width = LCDFN(getstringsize)(s->linebuffer, NULL, NULL);
+ makedelay = false;
+
+ if (s->backward)
+ s->offset -= si->step;
+ else
+ s->offset += si->step;
+
+ if (s->bidir) { /* scroll bidirectional */
+
+ s->line = s->linebuffer;
+ if (s->offset <= 0) {
+ /* at beginning of line */
+ s->offset = 0;
+ s->backward = false;
+ makedelay = true;
+ }
+ else if (s->offset >= width - (s->width - s->x)) {
+ /* at end of line */
+ s->offset = width - (s->width - s->x);
+ s->backward = true;
+ makedelay = true;
+ }
+ }
+ else {
+
+ snprintf(line_buf, sizeof(line_buf)-1, "%s%s%s",
+ s->linebuffer, " ", s->linebuffer);
+ s->line = line_buf;
+ width += LCDFN(getstringsize)(" ", NULL, NULL);
+ /* scroll forward the whole time */
+ if (s->offset >= width) {
+ s->offset = 0;
+ makedelay = true;
+ }
+ }
+
+ /* Stash and restore these three, so that the scroll_func
+ * can do whatever it likes without destroying the state */
+ fg_pattern = s->vp->fg_pattern;
+ bg_pattern = s->vp->bg_pattern;
+ drawmode = s->vp->drawmode;
+
+ s->scroll_func(s);
+ LCDFN(update_viewport_rect)(s->x, s->y, s->width, s->height);
+
+ s->vp->fg_pattern = fg_pattern;
+ s->vp->bg_pattern = bg_pattern;
+ s->vp->drawmode = drawmode;
+
+ LCDFN(set_viewport)(vp);
+
+ if (makedelay)
+ s->start_tick += si->delay + si->ticks;
+ }
+}
diff --git a/firmware/export/scroll_engine.h b/firmware/export/scroll_engine.h
index 01a9a5e33d..c7eb97aecc 100644
--- a/firmware/export/scroll_engine.h
+++ b/firmware/export/scroll_engine.h
@@ -37,19 +37,19 @@ extern void lcd_scroll_delay(int ms);
extern void lcd_scroll_stop(void);
extern void lcd_scroll_stop_viewport(const struct viewport *vp);
-extern void lcd_scroll_stop_viewport_line(const struct viewport *vp, int line);
-extern void lcd_scroll_fn(void);
+extern void lcd_scroll_stop_viewport_rect(const struct viewport *vp, int x, int y, int width, int height);
#ifdef HAVE_REMOTE_LCD
extern void lcd_remote_scroll_speed(int speed);
extern void lcd_remote_scroll_delay(int ms);
extern void lcd_remote_scroll_stop(void);
extern void lcd_remote_scroll_stop_viewport(const struct viewport *vp);
-extern void lcd_remote_scroll_stop_viewport_line(const struct viewport *vp, int line);
-extern void lcd_remote_scroll_fn(void);
+extern void lcd_remote_scroll_stop_viewport_rect(const struct viewport *vp, int x, int y, int width, int height);
#endif
-/* internal usage, but in multiple drivers */
+/* internal usage, but in multiple drivers
+ * larger than the normal linebuffer since it holds the line a second
+ * time (+3 spaces) for non-bidir scrolling */
#define SCROLL_SPACING 3
#ifdef HAVE_LCD_BITMAP
#define SCROLL_LINE_SIZE (MAX_PATH + SCROLL_SPACING + 3*LCD_WIDTH/2 + 2)
@@ -60,21 +60,27 @@ extern void lcd_remote_scroll_fn(void);
struct scrollinfo
{
struct viewport* vp;
- char line[SCROLL_LINE_SIZE];
+ char linebuffer[9*MAX_PATH/10];
+ const char *line;
#ifdef HAVE_LCD_CHARCELLS
int len; /* length of line in chars */
#endif
- int y; /* Position of the line on the screen (char co-ordinates) */
+ /* rectangle for the line */
+ int x, y; /* relative to the viewort */
+ int width, height;
+ /* pixel to skip from the beginning of the string, increments as the text scrolls */
int offset;
- int startx;
- int y_offset; /* y offset of the line, used for pixel-accurate list scrolling */
#ifdef HAVE_LCD_BITMAP
- int width; /* length of line in pixels */
int style; /* line style */
-#endif/* HAVE_LCD_BITMAP */
- bool backward; /* scroll presently forward or backward? */
+#endif /* HAVE_LCD_BITMAP */
+ /* scroll presently forward or backward? */
+ bool backward;
bool bidir;
long start_tick;
+
+ /* support for custom scrolling functions */
+ void (*scroll_func)(struct scrollinfo *s);
+ void *userdata;
};
struct scroll_screen_info
diff --git a/firmware/scroll_engine.c b/firmware/scroll_engine.c
index d134f7b2ce..d1bc2976a1 100644
--- a/firmware/scroll_engine.c
+++ b/firmware/scroll_engine.c
@@ -23,6 +23,7 @@
*
****************************************************************************/
+#include <stdio.h>
#include "config.h"
#include "gcc_extensions.h"
#include "cpu.h"
@@ -36,16 +37,20 @@
#endif
#include "scroll_engine.h"
+
+/* private helper function for the scroll engine. Do not use in apps/.
+ * defined in lcd-bitmap-common.c */
+extern struct viewport *lcd_get_viewport(bool *is_defaut);
+#ifdef HAVE_REMOTE_LCD
+extern struct viewport *lcd_remote_get_viewport(bool *is_defaut);
+#endif
+
static const char scroll_tick_table[18] = {
/* Hz values [f(x)=100.8/(x+.048)]:
1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33, 49.2, 96.2 */
100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1
};
-/* imported private functions from lcd-bitmap-common.c */
-extern struct viewport *lcd_get_viewport(void);
-extern struct viewport *lcd_remote_get_viewport(void);
-
static void scroll_thread(void);
static char scroll_stack[DEFAULT_STACK_SIZE*3];
static const char scroll_name[] = "scroll";
@@ -156,7 +161,7 @@ static void scroll_thread(void)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
if (lcd_active())
#endif
- lcd_scroll_fn();
+ lcd_scroll_worker();
lcd_scroll_info.last_scroll = current_tick;
}
@@ -165,7 +170,7 @@ static void scroll_thread(void)
if (scroll & SCROLL_LCD_REMOTE)
{
- lcd_remote_scroll_fn();
+ lcd_remote_scroll_worker();
lcd_remote_scroll_info.last_scroll = current_tick;
}
}
@@ -179,7 +184,7 @@ static void scroll_thread(void)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
if (lcd_active())
#endif
- lcd_scroll_fn();
+ lcd_scroll_worker();
}
}
#endif /* HAVE_REMOTE_LCD */