summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2014-01-12 01:30:26 +0100
committerThomas Martitz <kugel@rockbox.org>2014-01-12 01:34:06 +0100
commit488a1b983e1c2fac14de25aa781caf12628e53c8 (patch)
tree8d5b5b33e0c7eb9e5222b843f84c6b2d65d1a352
parent656261bde1122612d1bf8ffc3c992c75a7fbc52e (diff)
downloadrockbox-488a1b983e1c2fac14de25aa781caf12628e53c8.tar.gz
rockbox-488a1b983e1c2fac14de25aa781caf12628e53c8.zip
put_line/scrolling: Make the scroll engine inform custom scrollers about start/stop of scrolling.
With the new lcd_putsxy_scroll_func() code can register custom scroll functions (put_line() makes use of that). In order for the custom scroller to be able to properly manage its userdata pointer (set via struct scrollinfo::userdata) the scroll engine must inform the scroller about start and stop of scrolling. To inform about start the lcd_scroll_* functions now return true when the line will scroll. To inform about stop the scroll engine calls into the scroller one last time, with the text set to NULL. put_line() can use this to release the userdata registered per scrolling line so that it can be recycled. This fixes that some scrolling lines became glitchy after some time because the userdata was recycled too early. Change-Id: Iff0a6ce2a4f9ae2bada1b8e62f4f5950224942a9
-rw-r--r--apps/gui/line.c49
-rw-r--r--apps/plugin.h2
-rw-r--r--apps/screen_access.h4
-rw-r--r--firmware/drivers/lcd-bitmap-common.c69
-rw-r--r--firmware/drivers/lcd-scroll.c12
-rw-r--r--firmware/export/lcd-remote.h4
-rw-r--r--firmware/export/lcd.h4
-rw-r--r--firmware/export/scroll_engine.h5
8 files changed, 95 insertions, 54 deletions
diff --git a/apps/gui/line.c b/apps/gui/line.c
index d561f08c76..e2eb6f277b 100644
--- a/apps/gui/line.c
+++ b/apps/gui/line.c
@@ -50,18 +50,27 @@ static void style_line(struct screen *display, int x, int y, struct line_desc *l
50static void put_text(struct screen *display, int x, int y, struct line_desc *line, 50static void put_text(struct screen *display, int x, int y, struct line_desc *line,
51 const char *text, bool prevent_scroll, int text_skip_pixels); 51 const char *text, bool prevent_scroll, int text_skip_pixels);
52 52
53struct line_desc_scroll {
54 struct line_desc desc; /* must be first! */
55 bool used;
56};
57
58#define NOINLINE __attribute__ ((noinline))
53 59
54static struct line_desc *get_line_desc(void) 60struct line_desc_scroll *get_line_desc(void) NOINLINE;
61struct line_desc_scroll *get_line_desc(void)
55{ 62{
56 static struct line_desc lines[MAX_LINES]; 63 static struct line_desc_scroll lines[MAX_LINES];
57 static unsigned line_index; 64 static unsigned line_index;
58 struct line_desc *ret; 65 struct line_desc_scroll *this;
59 66
60 ret = &lines[line_index++]; 67 do {
61 if (line_index >= ARRAYLEN(lines)) 68 this = &lines[line_index++];
62 line_index = 0; 69 if (line_index >= ARRAYLEN(lines))
70 line_index = 0;
71 } while (this->used);
63 72
64 return ret; 73 return this;
65} 74}
66 75
67static void scroller(struct scrollinfo *s, struct screen *display) 76static void scroller(struct scrollinfo *s, struct screen *display)
@@ -72,9 +81,17 @@ static void scroller(struct scrollinfo *s, struct screen *display)
72 * line padding. this needs to be corrected for calling style_line(). 81 * line padding. this needs to be corrected for calling style_line().
73 * The alternative would be to really redraw only the text area, 82 * The alternative would be to really redraw only the text area,
74 * but that would complicate the code a lot */ 83 * but that would complicate the code a lot */
75 struct line_desc *line = s->userdata; 84 struct line_desc_scroll *line = s->userdata;
76 style_line(display, s->x, s->y - (line->height/2 - display->getcharheight()/2), line); 85 if (!s->line)
77 put_text(display, s->x, s->y, line, s->line, true, s->offset); 86 {
87 line->used = false;
88 }
89 else
90 if (s->line)
91 {
92 style_line(display, s->x, s->y - (line->desc.height/2 - display->getcharheight()/2), &line->desc);
93 put_text(display, s->x, s->y, &line->desc, s->line, true, s->offset);
94 }
78} 95}
79 96
80static void scroller_main(struct scrollinfo *s) 97static void scroller_main(struct scrollinfo *s)
@@ -126,14 +143,16 @@ static void put_text(struct screen *display,
126 143
127 if (line->scroll && !prevent_scroll) 144 if (line->scroll && !prevent_scroll)
128 { 145 {
129 struct line_desc *line_data = get_line_desc(); 146 bool scrolls;
130 *line_data = *line; 147 struct line_desc_scroll *line_data = get_line_desc();
148 line_data->desc = *line;
131 /* precalculate to avoid doing it in the scroller, it's save to 149 /* precalculate to avoid doing it in the scroller, it's save to
132 * do this on the copy of the original line_desc*/ 150 * do this on the copy of the original line_desc*/
133 if (line_data->height == -1) 151 if (line_data->desc.height == -1)
134 line_data->height = display->getcharheight(); 152 line_data->desc.height = display->getcharheight();
135 display->putsxy_scroll_func(x, y, text, 153 scrolls = display->putsxy_scroll_func(x, y, text,
136 scrollers[display->screen_type], line_data, text_skip_pixels); 154 scrollers[display->screen_type], line_data, text_skip_pixels);
155 line_data->used = scrolls;
137 } 156 }
138 else 157 else
139 display->putsxy_scroll_func(x, y, text, NULL, NULL, text_skip_pixels); 158 display->putsxy_scroll_func(x, y, text, NULL, NULL, text_skip_pixels);
diff --git a/apps/plugin.h b/apps/plugin.h
index 2cf40d5758..5d6527d7a4 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -197,7 +197,7 @@ struct plugin_api {
197 void (*lcd_putsxyf)(int x, int y, const unsigned char *fmt, ...); 197 void (*lcd_putsxyf)(int x, int y, const unsigned char *fmt, ...);
198 void (*lcd_puts)(int x, int y, const unsigned char *string); 198 void (*lcd_puts)(int x, int y, const unsigned char *string);
199 void (*lcd_putsf)(int x, int y, const unsigned char *fmt, ...); 199 void (*lcd_putsf)(int x, int y, const unsigned char *fmt, ...);
200 void (*lcd_puts_scroll)(int x, int y, const unsigned char* string); 200 bool (*lcd_puts_scroll)(int x, int y, const unsigned char* string);
201 void (*lcd_scroll_stop)(void); 201 void (*lcd_scroll_stop)(void);
202#ifdef HAVE_LCD_CHARCELLS 202#ifdef HAVE_LCD_CHARCELLS
203 void (*lcd_define_pattern)(unsigned long ucs, const char *pattern); 203 void (*lcd_define_pattern)(unsigned long ucs, const char *pattern);
diff --git a/apps/screen_access.h b/apps/screen_access.h
index 7efc38b174..c4a87849b8 100644
--- a/apps/screen_access.h
+++ b/apps/screen_access.h
@@ -126,8 +126,8 @@ struct screen
126 void (*putsxy)(int x, int y, const unsigned char *str); 126 void (*putsxy)(int x, int y, const unsigned char *str);
127 void (*puts)(int x, int y, const unsigned char *str); 127 void (*puts)(int x, int y, const unsigned char *str);
128 void (*putsf)(int x, int y, const unsigned char *str, ...); 128 void (*putsf)(int x, int y, const unsigned char *str, ...);
129 void (*puts_scroll)(int x, int y, const unsigned char *string); 129 bool (*puts_scroll)(int x, int y, const unsigned char *string);
130 void (*putsxy_scroll_func)(int x, int y, const unsigned char *string, 130 bool (*putsxy_scroll_func)(int x, int y, const unsigned char *string,
131 void (*scroll_func)(struct scrollinfo *), 131 void (*scroll_func)(struct scrollinfo *),
132 void *data, int x_offset); 132 void *data, int x_offset);
133 void (*scroll_speed)(int speed); 133 void (*scroll_speed)(int speed);
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index a71f5b2862..22430d4e50 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -316,15 +316,19 @@ static struct scrollinfo* find_scrolling_line(int x, int y)
316 316
317void LCDFN(scroll_fn)(struct scrollinfo* s) 317void LCDFN(scroll_fn)(struct scrollinfo* s)
318{ 318{
319 /* with line == NULL when scrolling stops. This scroller
320 * maintains no userdata so there is nothing left to do */
321 if (!s->line)
322 return;
319 /* Fill with background/backdrop to clear area. 323 /* Fill with background/backdrop to clear area.
320 * cannot use clear_viewport_rect() since stops scrolling as well */ 324 * cannot use clear_viewport_rect() since would stop scrolling as well */
321 LCDFN(set_drawmode)(DRMODE_SOLID|DRMODE_INVERSEVID); 325 LCDFN(set_drawmode)(DRMODE_SOLID|DRMODE_INVERSEVID);
322 LCDFN(fillrect)(s->x, s->y, s->width, s->height); 326 LCDFN(fillrect)(s->x, s->y, s->width, s->height);
323 LCDFN(set_drawmode)(DRMODE_SOLID); 327 LCDFN(set_drawmode)(DRMODE_SOLID);
324 LCDFN(putsxyofs)(s->x, s->y, s->offset, s->line); 328 LCDFN(putsxyofs)(s->x, s->y, s->offset, s->line);
325} 329}
326 330
327static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string, 331static bool LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
328 int x_offset, 332 int x_offset,
329 bool linebased, 333 bool linebased,
330 void (*scroll_func)(struct scrollinfo *), 334 void (*scroll_func)(struct scrollinfo *),
@@ -336,7 +340,7 @@ static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
336 bool restart; 340 bool restart;
337 341
338 if (!string) 342 if (!string)
339 return; 343 return false;
340 344
341 /* prepare rectangle for scrolling. x and y must be calculated early 345 /* prepare rectangle for scrolling. x and y must be calculated early
342 * for find_scrolling_line() to work */ 346 * for find_scrolling_line() to work */
@@ -347,32 +351,25 @@ static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
347 width = current_vp->width - x; 351 width = current_vp->width - x;
348 352
349 if (y >= current_vp->height) 353 if (y >= current_vp->height)
350 return; 354 return false;
351 355
352 s = find_scrolling_line(x, y); 356 s = find_scrolling_line(x, y);
353 restart = !s; 357 restart = !s;
354 358
355 if (restart) {
356 /* remove any previously scrolling line at the same location */
357 LCDFN(scroll_stop_viewport_rect)(current_vp, x, y, width, height);
358 LCDFN(putsxyofs)(x, y, x_offset, string);
359
360 if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES))
361 return;
362 }
363
364 /* get width (pixeks) of the string */ 359 /* get width (pixeks) of the string */
365 LCDFN(getstringsize)(string, &w, &h); 360 LCDFN(getstringsize)(string, &w, &h);
366 361
367 /* check if scrolling is actually necessary (consider the actual start 362 /* Remove any previously scrolling line at the same location. If
368 * of the line) */ 363 * the string width is too small to scroll the scrolling line is
369 if (width >= w) 364 * cleared as well */
370 return; 365 if (w < width || restart) {
371 366 LCDFN(scroll_stop_viewport_rect)(current_vp, x, y, width, height);
372 if (restart) { 367 LCDFN(putsxyofs)(x, y, x_offset, string);
373 /* prepare scroll line */ 368 /* nothing to scroll, or out of scrolling lines. Either way, get out */
369 if (w < width || LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES))
370 return false;
371 /* else restarting: prepare scroll line */
374 s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines]; 372 s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines];
375 s->start_tick = current_tick + LCDFN(scroll_info).delay;
376 } 373 }
377 374
378 /* copy contents to the line buffer */ 375 /* copy contents to the line buffer */
@@ -385,9 +382,6 @@ static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
385 else 382 else
386 s->bidir = false; 383 s->bidir = false;
387 384
388 s->scroll_func = scroll_func;
389 s->userdata = data;
390
391 if (restart) { 385 if (restart) {
392 s->offset = x_offset; 386 s->offset = x_offset;
393 s->backward = false; 387 s->backward = false;
@@ -397,26 +391,41 @@ static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
397 s->width = width; 391 s->width = width;
398 s->height = height; 392 s->height = height;
399 s->vp = current_vp; 393 s->vp = current_vp;
394 s->start_tick = current_tick + LCDFN(scroll_info).delay;
400 LCDFN(scroll_info).lines++; 395 LCDFN(scroll_info).lines++;
401 } else { 396 } else {
402 /* if only the text was updated render immediately */ 397 /* not restarting, however we are about to assign new userdata;
403 LCDFN(scroll_now(s)); 398 * therefore tell the scroller that it can release the previous userdata */
399 s->line = NULL;
400 s->scroll_func(s);
404 } 401 }
402
403 s->scroll_func = scroll_func;
404 s->userdata = data;
405
406 /* if only the text was updated render immediately */
407 if (!restart)
408 LCDFN(scroll_now(s));
409
410 return true;
405} 411}
406 412
407void LCDFN(putsxy_scroll_func)(int x, int y, const unsigned char *string, 413bool LCDFN(putsxy_scroll_func)(int x, int y, const unsigned char *string,
408 void (*scroll_func)(struct scrollinfo *), 414 void (*scroll_func)(struct scrollinfo *),
409 void *data, int x_offset) 415 void *data, int x_offset)
410{ 416{
417 bool retval = false;
411 if (!scroll_func) 418 if (!scroll_func)
412 LCDFN(putsxyofs)(x, y, x_offset, string); 419 LCDFN(putsxyofs)(x, y, x_offset, string);
413 else 420 else
414 LCDFN(puts_scroll_worker)(x, y, string, x_offset, false, scroll_func, data); 421 retval = LCDFN(puts_scroll_worker)(x, y, string, x_offset, false, scroll_func, data);
422
423 return retval;
415} 424}
416 425
417void LCDFN(puts_scroll)(int x, int y, const unsigned char *string) 426bool LCDFN(puts_scroll)(int x, int y, const unsigned char *string)
418{ 427{
419 LCDFN(puts_scroll_worker)(x, y, string, 0, true, LCDFN(scroll_fn), NULL); 428 return LCDFN(puts_scroll_worker)(x, y, string, 0, true, LCDFN(scroll_fn), NULL);
420} 429}
421 430
422#if !defined(HAVE_LCD_COLOR) || !defined(MAIN_LCD) 431#if !defined(HAVE_LCD_COLOR) || !defined(MAIN_LCD)
diff --git a/firmware/drivers/lcd-scroll.c b/firmware/drivers/lcd-scroll.c
index a1bde9fe12..5162f9a100 100644
--- a/firmware/drivers/lcd-scroll.c
+++ b/firmware/drivers/lcd-scroll.c
@@ -30,7 +30,7 @@
30#define MAIN_LCD 30#define MAIN_LCD
31#endif 31#endif
32 32
33static struct scrollinfo LCDFN(scroll)[LCD_SCROLLABLE_LINES]; 33static struct scrollinfo LCDFN(scroll)[LCDM(SCROLLABLE_LINES)];
34 34
35struct scroll_screen_info LCDFN(scroll_info) = 35struct scroll_screen_info LCDFN(scroll_info) =
36{ 36{
@@ -51,6 +51,13 @@ struct scroll_screen_info LCDFN(scroll_info) =
51 51
52void LCDFN(scroll_stop)(void) 52void LCDFN(scroll_stop)(void)
53{ 53{
54 for (int i = 0; i < LCDFN(scroll_info).lines; i++)
55 {
56 /* inform scroller about end of scrolling */
57 struct scrollinfo *s = &LCDFN(scroll_info).scroll[i];
58 s->line = NULL;
59 s->scroll_func(s);
60 }
54 LCDFN(scroll_info).lines = 0; 61 LCDFN(scroll_info).lines = 0;
55} 62}
56 63
@@ -66,6 +73,9 @@ void LCDFN(scroll_stop_viewport_rect)(const struct viewport *vp, int x, int y, i
66 && (x < (s->x+s->width) && (x+width) >= s->x) 73 && (x < (s->x+s->width) && (x+width) >= s->x)
67 && (y < (s->y+s->height) && (y+height) >= s->y)) 74 && (y < (s->y+s->height) && (y+height) >= s->y))
68 { 75 {
76 /* inform scroller about end of scrolling */
77 s->line = NULL;
78 s->scroll_func(s);
69 /* If i is not the last active line in the array, then move 79 /* If i is not the last active line in the array, then move
70 the last item to position i. This compacts 80 the last item to position i. This compacts
71 the scroll array at the same time of removing the line */ 81 the scroll array at the same time of removing the line */
diff --git a/firmware/export/lcd-remote.h b/firmware/export/lcd-remote.h
index bc26a23cb7..1819a4de72 100644
--- a/firmware/export/lcd-remote.h
+++ b/firmware/export/lcd-remote.h
@@ -176,8 +176,8 @@ extern void lcd_remote_clear_viewport(void);
176extern void lcd_remote_puts(int x, int y, const unsigned char *str); 176extern void lcd_remote_puts(int x, int y, const unsigned char *str);
177extern void lcd_remote_putsf(int x, int y, const unsigned char *fmt, ...); 177extern void lcd_remote_putsf(int x, int y, const unsigned char *fmt, ...);
178extern void lcd_remote_putc(int x, int y, unsigned short ch); 178extern void lcd_remote_putc(int x, int y, unsigned short ch);
179extern void lcd_remote_puts_scroll(int x, int y, const unsigned char *str); 179extern bool lcd_remote_puts_scroll(int x, int y, const unsigned char *str);
180extern void lcd_remote_putsxy_scroll_func(int x, int y, const unsigned char *string, 180extern bool lcd_remote_putsxy_scroll_func(int x, int y, const unsigned char *string,
181 void (*scroll_func)(struct scrollinfo *), 181 void (*scroll_func)(struct scrollinfo *),
182 void *data, int x_offset); 182 void *data, int x_offset);
183 183
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index 87476d9dda..386ac5a8bf 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -179,8 +179,8 @@ extern void lcd_putsxy_style_offset(int x, int y, const unsigned char *str,
179extern void lcd_puts(int x, int y, const unsigned char *string); 179extern void lcd_puts(int x, int y, const unsigned char *string);
180extern void lcd_putsf(int x, int y, const unsigned char *fmt, ...); 180extern void lcd_putsf(int x, int y, const unsigned char *fmt, ...);
181extern void lcd_putc(int x, int y, unsigned long ucs); 181extern void lcd_putc(int x, int y, unsigned long ucs);
182extern void lcd_puts_scroll(int x, int y, const unsigned char* string); 182extern bool lcd_puts_scroll(int x, int y, const unsigned char* string);
183extern void lcd_putsxy_scroll_func(int x, int y, const unsigned char *string, 183extern bool lcd_putsxy_scroll_func(int x, int y, const unsigned char *string,
184 void (*scroll_func)(struct scrollinfo *), 184 void (*scroll_func)(struct scrollinfo *),
185 void *data, int x_offset); 185 void *data, int x_offset);
186 186
diff --git a/firmware/export/scroll_engine.h b/firmware/export/scroll_engine.h
index 64e1d6d6ae..ce230a218c 100644
--- a/firmware/export/scroll_engine.h
+++ b/firmware/export/scroll_engine.h
@@ -79,7 +79,10 @@ struct scrollinfo
79 bool bidir; 79 bool bidir;
80 long start_tick; 80 long start_tick;
81 81
82 /* support for custom scrolling functions */ 82 /* support for custom scrolling functions,
83 * must be called with ::line == NULL to indicate that the line
84 * stops scrolling or when the userdata pointer is going to be changed
85 * (the custom scroller can release the userdata then) */
83 void (*scroll_func)(struct scrollinfo *s); 86 void (*scroll_func)(struct scrollinfo *s);
84 void *userdata; 87 void *userdata;
85}; 88};