diff options
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/lcd-bitmap-common.c | 180 | ||||
-rw-r--r-- | firmware/drivers/lcd-scroll.c | 111 |
2 files changed, 161 insertions, 130 deletions
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, | |||
412 | 412 | ||
413 | /*** Line oriented text output ***/ | 413 | /*** Line oriented text output ***/ |
414 | 414 | ||
415 | /* put a string at a given char position */ | ||
416 | void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str, | 415 | void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str, |
417 | int style, int x_offset, int y_offset) | 416 | int style, int x_offset, int y_offset) |
418 | { | 417 | { |
419 | int xpos, ypos, h; | 418 | int xpos, ypos, h; |
420 | LCDFN(scroll_stop_viewport_line)(current_vp, y); | ||
421 | if(!str) | 419 | if(!str) |
422 | return; | 420 | return; |
423 | 421 | ||
@@ -425,14 +423,15 @@ void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str, | |||
425 | if ((style&STYLE_XY_PIXELS) == 0) | 423 | if ((style&STYLE_XY_PIXELS) == 0) |
426 | { | 424 | { |
427 | xpos = x * LCDFN(getstringsize)(" ", NULL, NULL); | 425 | xpos = x * LCDFN(getstringsize)(" ", NULL, NULL); |
428 | ypos = y * h + y_offset; | 426 | ypos = y * h; |
429 | } | 427 | } |
430 | else | 428 | else |
431 | { | 429 | { |
432 | xpos = x; | 430 | xpos = x; |
433 | ypos = y + y_offset; | 431 | ypos = y; |
434 | } | 432 | } |
435 | LCDFN(putsxyofs_style)(xpos, ypos, str, style, h, x_offset); | 433 | LCDFN(scroll_stop_viewport_rect)(current_vp, x, y, current_vp->width - x, h); |
434 | LCDFN(putsxyofs_style)(xpos, ypos+y_offset, str, style, h, x_offset); | ||
436 | } | 435 | } |
437 | 436 | ||
438 | void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str, | 437 | 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) | |||
469 | 468 | ||
470 | /*** scrolling ***/ | 469 | /*** scrolling ***/ |
471 | 470 | ||
472 | static struct scrollinfo* find_scrolling_line(int line) | 471 | static struct scrollinfo* find_scrolling_line(int x, int y) |
473 | { | 472 | { |
474 | struct scrollinfo* s = NULL; | 473 | struct scrollinfo* s = NULL; |
475 | int i; | 474 | int i; |
@@ -477,93 +476,101 @@ static struct scrollinfo* find_scrolling_line(int line) | |||
477 | for(i=0; i<LCDFN(scroll_info).lines; i++) | 476 | for(i=0; i<LCDFN(scroll_info).lines; i++) |
478 | { | 477 | { |
479 | s = &LCDFN(scroll_info).scroll[i]; | 478 | s = &LCDFN(scroll_info).scroll[i]; |
480 | if (s->y == line && s->vp == current_vp) | 479 | if (s->x == x && s->y == y && s->vp == current_vp) |
481 | return s; | 480 | return s; |
482 | } | 481 | } |
483 | return NULL; | 482 | return NULL; |
484 | } | 483 | } |
485 | 484 | ||
486 | void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string, | 485 | void LCDFN(scroll_fn)(struct scrollinfo* s) |
487 | int style, int x_offset, int y_offset) | 486 | { |
487 | LCDFN(putsxyofs_style)(s->x, s->y, s->line, s->style, s->height, s->offset); | ||
488 | } | ||
489 | |||
490 | static void LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string, | ||
491 | int style, int x_offset, int y_offset, | ||
492 | bool linebased, | ||
493 | void (*scroll_func)(struct scrollinfo *), | ||
494 | void *data) | ||
488 | { | 495 | { |
489 | struct scrollinfo* s; | 496 | struct scrollinfo* s; |
490 | char *end; | 497 | int width, height; |
491 | int w, h; | 498 | int w, h, cwidth, margin; |
492 | int len; | 499 | bool restart; |
493 | bool restart = false; | ||
494 | int space_width; | ||
495 | 500 | ||
496 | if (!string || ((unsigned)y >= (unsigned)current_vp->height)) | 501 | if (!string) |
497 | return; | 502 | return; |
498 | 503 | ||
499 | s = find_scrolling_line(y); | 504 | /* prepare rectangle for scrolling. x and y must be calculated early |
500 | if (!s) | 505 | * for find_scrolling_line() to work */ |
501 | restart = true; | 506 | cwidth = font_get(current_vp->font)->maxwidth; |
507 | height = current_vp->line_height ?: (int)font_get(current_vp->font)->height; | ||
508 | y = y * (linebased ? height : 1) + y_offset; | ||
509 | x = x * (linebased ? cwidth : 1); | ||
510 | width = current_vp->width - x; | ||
502 | 511 | ||
503 | if (restart) | 512 | if (y >= current_vp->height) |
504 | { | 513 | return; |
514 | |||
515 | s = find_scrolling_line(x, y); | ||
516 | restart = !s; | ||
517 | |||
518 | if (restart) { | ||
505 | /* remove any previously scrolling line at the same location */ | 519 | /* remove any previously scrolling line at the same location */ |
506 | LCDFN(scroll_stop_viewport_line)(current_vp, y); | 520 | LCDFN(scroll_stop_viewport_rect)(current_vp, x, y, width, height); |
521 | LCDFN(putsxyofs_style)(x, y, string, style, height, x_offset); | ||
507 | 522 | ||
508 | if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) return; | 523 | if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) |
509 | LCDFN(puts_style_xyoffset)(x, y, string, style, x_offset, y_offset); | 524 | return; |
510 | } | 525 | } |
511 | 526 | ||
527 | /* get width (pixeks) of the string */ | ||
512 | LCDFN(getstringsize)(string, &w, &h); | 528 | LCDFN(getstringsize)(string, &w, &h); |
513 | 529 | ||
514 | if (current_vp->width - x * 8 >= w) | 530 | /* check if scrolling is actually necessary (consider the actual start |
531 | * of the line) */ | ||
532 | margin = x * linebased ? cwidth : 1; | ||
533 | if (current_vp->width >= margin+w) | ||
515 | return; | 534 | return; |
516 | 535 | ||
517 | if (restart) | 536 | if (restart) { |
518 | { | ||
519 | /* prepare scroll line */ | 537 | /* prepare scroll line */ |
520 | s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines]; | 538 | s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines]; |
521 | s->start_tick = current_tick + LCDFN(scroll_info).delay; | 539 | s->start_tick = current_tick + LCDFN(scroll_info).delay; |
522 | } | 540 | } |
523 | strlcpy(s->line, string, sizeof s->line); | ||
524 | space_width = LCDFN(getstringsize)(" ", NULL, NULL); | ||
525 | 541 | ||
526 | /* get width */ | 542 | /* copy contents to the line buffer */ |
527 | LCDFN(getstringsize)(s->line, &w, &h); | 543 | strlcpy(s->linebuffer, string, sizeof(s->linebuffer)); |
528 | if (!restart && s->width > w) | 544 | /* scroll bidirectional or forward only depending on the string width */ |
529 | { | ||
530 | if (s->startx > w) | ||
531 | s->startx = w; | ||
532 | } | ||
533 | s->width = w; | ||
534 | |||
535 | /* scroll bidirectional or forward only depending on the string | ||
536 | width */ | ||
537 | if ( LCDFN(scroll_info).bidir_limit ) { | 545 | if ( LCDFN(scroll_info).bidir_limit ) { |
538 | s->bidir = s->width < (current_vp->width) * | 546 | s->bidir = w < (current_vp->width) * |
539 | (100 + LCDFN(scroll_info).bidir_limit) / 100; | 547 | (100 + LCDFN(scroll_info).bidir_limit) / 100; |
540 | } | 548 | } |
541 | else | 549 | else |
542 | s->bidir = false; | 550 | s->bidir = false; |
543 | 551 | ||
544 | if (!s->bidir) { /* add spaces if scrolling in the round */ | 552 | s->scroll_func = scroll_func; |
545 | strlcat(s->line, " ", sizeof s->line); | 553 | s->userdata = data; |
546 | /* get new width incl. spaces */ | ||
547 | s->width += space_width * 3; | ||
548 | } | ||
549 | |||
550 | end = strchr(s->line, '\0'); | ||
551 | len = sizeof s->line - (end - s->line); | ||
552 | strlcpy(end, string, MIN(current_vp->width/2, len)); | ||
553 | 554 | ||
554 | s->vp = current_vp; | 555 | if (restart) { |
555 | s->y = y; | ||
556 | if (restart) | ||
557 | { | ||
558 | s->offset = x_offset; | 556 | s->offset = x_offset; |
559 | s->startx = x * space_width; | ||
560 | s->backward = false; | 557 | s->backward = false; |
561 | s->style = style; | 558 | s->style = style; |
559 | /* assign the rectangle. not necessary if continuing an earlier line */ | ||
560 | s->x = x; | ||
561 | s->y = y; | ||
562 | s->width = width; | ||
563 | s->height = height; | ||
564 | s->vp = current_vp; | ||
565 | LCDFN(scroll_info).lines++; | ||
562 | } | 566 | } |
563 | s->y_offset = y_offset; | 567 | } |
564 | 568 | ||
565 | if (restart) | 569 | void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string, |
566 | LCDFN(scroll_info).lines++; | 570 | int style, int x_offset, int y_offset) |
571 | { | ||
572 | LCDFN(puts_scroll_worker)(x, y, string, style, x_offset, y_offset, | ||
573 | true, LCDFN(scroll_fn), NULL); | ||
567 | } | 574 | } |
568 | 575 | ||
569 | void LCDFN(puts_scroll)(int x, int y, const unsigned char *string) | 576 | 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, | |||
583 | LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset); | 590 | LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset); |
584 | } | 591 | } |
585 | 592 | ||
586 | void LCDFN(scroll_fn)(void) | ||
587 | { | ||
588 | struct scrollinfo* s; | ||
589 | int index; | ||
590 | int xpos, ypos, height; | ||
591 | struct viewport* old_vp = current_vp; | ||
592 | bool makedelay; | ||
593 | |||
594 | for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) { | ||
595 | s = &LCDFN(scroll_info).scroll[index]; | ||
596 | |||
597 | /* check pause */ | ||
598 | if (TIME_BEFORE(current_tick, s->start_tick)) | ||
599 | continue; | ||
600 | |||
601 | LCDFN(set_viewport)(s->vp); | ||
602 | height = s->vp->line_height ?: (int)font_get(s->vp->font)->height; | ||
603 | |||
604 | if (s->backward) | ||
605 | s->offset -= LCDFN(scroll_info).step; | ||
606 | else | ||
607 | s->offset += LCDFN(scroll_info).step; | ||
608 | |||
609 | xpos = s->startx; | ||
610 | ypos = s->y * height + s->y_offset; | ||
611 | |||
612 | makedelay = false; | ||
613 | if (s->bidir) { /* scroll bidirectional */ | ||
614 | if (s->offset <= 0) { | ||
615 | /* at beginning of line */ | ||
616 | s->offset = 0; | ||
617 | s->backward = false; | ||
618 | makedelay = true; | ||
619 | } | ||
620 | else if (s->offset >= s->width - (current_vp->width - xpos)) { | ||
621 | /* at end of line */ | ||
622 | s->offset = s->width - (current_vp->width - xpos); | ||
623 | s->backward = true; | ||
624 | makedelay = true; | ||
625 | } | ||
626 | } | ||
627 | else { | ||
628 | /* scroll forward the whole time */ | ||
629 | if (s->offset >= s->width) { | ||
630 | s->offset = 0; | ||
631 | makedelay = true; | ||
632 | } | ||
633 | } | ||
634 | |||
635 | if (makedelay) | ||
636 | s->start_tick = current_tick + LCDFN(scroll_info).delay + | ||
637 | LCDFN(scroll_info).ticks; | ||
638 | |||
639 | LCDFN(putsxyofs_style)(xpos, ypos, s->line, s->style, height, s->offset); | ||
640 | LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width-xpos, height); | ||
641 | } | ||
642 | LCDFN(set_viewport)(old_vp); | ||
643 | } | ||
644 | |||
645 | void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string, | 593 | void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string, |
646 | int style, int x_offset) | 594 | int style, int x_offset) |
647 | { | 595 | { |
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) | |||
54 | LCDFN(scroll_info).lines = 0; | 54 | LCDFN(scroll_info).lines = 0; |
55 | } | 55 | } |
56 | 56 | ||
57 | /* Stop scrolling line y in the specified viewport, or all lines if y < 0 */ | 57 | /* Clears scrolling lines that intersect with the area */ |
58 | void LCDFN(scroll_stop_viewport_line)(const struct viewport *current_vp, int line) | 58 | void LCDFN(scroll_stop_viewport_rect)(const struct viewport *vp, int x, int y, int width, int height) |
59 | { | 59 | { |
60 | int i = 0; | 60 | int i = 0; |
61 | |||
62 | while (i < LCDFN(scroll_info).lines) | 61 | while (i < LCDFN(scroll_info).lines) |
63 | { | 62 | { |
64 | struct viewport *vp = LCDFN(scroll_info).scroll[i].vp; | 63 | struct scrollinfo *s = &LCDFN(scroll_info).scroll[i]; |
65 | if (((vp == current_vp)) && | 64 | /* check if the specified area crosses the viewport in some way */ |
66 | ((line < 0) || (LCDFN(scroll_info).scroll[i].y == line))) | 65 | if (s->vp == vp |
66 | && (x < (s->x+s->width) && (x+width) >= s->x) | ||
67 | && (y < (s->y+s->height) && (y+height) >= s->y)) | ||
67 | { | 68 | { |
68 | /* If i is not the last active line in the array, then move | 69 | /* If i is not the last active line in the array, then move |
69 | the last item to position i */ | 70 | the last item to position i. This compacts |
71 | the scroll array at the same time of removing the line */ | ||
70 | if ((i + 1) != LCDFN(scroll_info).lines) | 72 | if ((i + 1) != LCDFN(scroll_info).lines) |
71 | { | 73 | { |
72 | LCDFN(scroll_info).scroll[i] = | 74 | LCDFN(scroll_info).scroll[i] = |
73 | LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines-1]; | 75 | LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines-1]; |
74 | } | 76 | } |
75 | LCDFN(scroll_info).lines--; | 77 | LCDFN(scroll_info).lines--; |
76 | |||
77 | /* A line can only appear once, so we're done, | ||
78 | * unless we are clearing the whole viewport */ | ||
79 | if (line >= 0) | ||
80 | return ; | ||
81 | } | 78 | } |
82 | else | 79 | else |
83 | { | 80 | { |
@@ -87,9 +84,9 @@ void LCDFN(scroll_stop_viewport_line)(const struct viewport *current_vp, int lin | |||
87 | } | 84 | } |
88 | 85 | ||
89 | /* Stop all scrolling lines in the specified viewport */ | 86 | /* Stop all scrolling lines in the specified viewport */ |
90 | void LCDFN(scroll_stop_viewport)(const struct viewport *current_vp) | 87 | void LCDFN(scroll_stop_viewport)(const struct viewport *vp) |
91 | { | 88 | { |
92 | LCDFN(scroll_stop_viewport_line)(current_vp, -1); | 89 | LCDFN(scroll_stop_viewport_rect)(vp, 0, 0, vp->width, vp->height); |
93 | } | 90 | } |
94 | 91 | ||
95 | void LCDFN(scroll_speed)(int speed) | 92 | void LCDFN(scroll_speed)(int speed) |
@@ -125,3 +122,89 @@ void LCDFN(jump_scroll_delay)(int ms) | |||
125 | LCDFN(scroll_info).jump_scroll_delay = ms / (HZ / 10); | 122 | LCDFN(scroll_info).jump_scroll_delay = ms / (HZ / 10); |
126 | } | 123 | } |
127 | #endif | 124 | #endif |
125 | |||
126 | static void LCDFN(scroll_worker)(void) | ||
127 | { | ||
128 | int index, width; | ||
129 | bool makedelay; | ||
130 | static char line_buf[SCROLL_LINE_SIZE]; | ||
131 | bool is_default; | ||
132 | struct scroll_screen_info *si = &LCDFN(scroll_info); | ||
133 | struct scrollinfo *s; | ||
134 | struct viewport *vp; | ||
135 | |||
136 | unsigned fg_pattern, bg_pattern, drawmode; | ||
137 | |||
138 | for ( index = 0; index < si->lines; index++ ) { | ||
139 | s = &si->scroll[index]; | ||
140 | |||
141 | /* check pause */ | ||
142 | if (TIME_BEFORE(current_tick, s->start_tick)) | ||
143 | continue; | ||
144 | |||
145 | s->start_tick = current_tick; | ||
146 | |||
147 | /* this runs out of the ui thread, thus we need to | ||
148 | * save and restore the current viewport since the ui thread | ||
149 | * is unaware of the swapped viewports. the vp must | ||
150 | * be switched early so that lcd_getstringsize() picks the | ||
151 | * correct font */ | ||
152 | vp = LCDFN(get_viewport)(&is_default); | ||
153 | LCDFN(set_viewport)(s->vp); | ||
154 | |||
155 | width = LCDFN(getstringsize)(s->linebuffer, NULL, NULL); | ||
156 | makedelay = false; | ||
157 | |||
158 | if (s->backward) | ||
159 | s->offset -= si->step; | ||
160 | else | ||
161 | s->offset += si->step; | ||
162 | |||
163 | if (s->bidir) { /* scroll bidirectional */ | ||
164 | |||
165 | s->line = s->linebuffer; | ||
166 | if (s->offset <= 0) { | ||
167 | /* at beginning of line */ | ||
168 | s->offset = 0; | ||
169 | s->backward = false; | ||
170 | makedelay = true; | ||
171 | } | ||
172 | else if (s->offset >= width - (s->width - s->x)) { | ||
173 | /* at end of line */ | ||
174 | s->offset = width - (s->width - s->x); | ||
175 | s->backward = true; | ||
176 | makedelay = true; | ||
177 | } | ||
178 | } | ||
179 | else { | ||
180 | |||
181 | snprintf(line_buf, sizeof(line_buf)-1, "%s%s%s", | ||
182 | s->linebuffer, " ", s->linebuffer); | ||
183 | s->line = line_buf; | ||
184 | width += LCDFN(getstringsize)(" ", NULL, NULL); | ||
185 | /* scroll forward the whole time */ | ||
186 | if (s->offset >= width) { | ||
187 | s->offset = 0; | ||
188 | makedelay = true; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /* Stash and restore these three, so that the scroll_func | ||
193 | * can do whatever it likes without destroying the state */ | ||
194 | fg_pattern = s->vp->fg_pattern; | ||
195 | bg_pattern = s->vp->bg_pattern; | ||
196 | drawmode = s->vp->drawmode; | ||
197 | |||
198 | s->scroll_func(s); | ||
199 | LCDFN(update_viewport_rect)(s->x, s->y, s->width, s->height); | ||
200 | |||
201 | s->vp->fg_pattern = fg_pattern; | ||
202 | s->vp->bg_pattern = bg_pattern; | ||
203 | s->vp->drawmode = drawmode; | ||
204 | |||
205 | LCDFN(set_viewport)(vp); | ||
206 | |||
207 | if (makedelay) | ||
208 | s->start_tick += si->delay + si->ticks; | ||
209 | } | ||
210 | } | ||