(hopefully) fix some broken download stuff I introduced yesterday.
[Rockbox.git] / firmware / drivers / lcd-16bit.c
blobbd5d09368c9748e42b393554852cac0f8bce5ae7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Dave Chapman
12 * Rockbox driver for 16-bit colour LCDs
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "config.h"
23 #include "cpu.h"
24 #include "lcd.h"
25 #include "kernel.h"
26 #include "thread.h"
27 #include <string.h>
28 #include <stdlib.h>
29 #include "memory.h"
30 #include "file.h"
31 #include "debug.h"
32 #include "system.h"
33 #include "font.h"
34 #include "rbunicode.h"
35 #include "bidi.h"
36 #include "scroll_engine.h"
38 enum fill_opt {
39 OPT_NONE = 0,
40 OPT_SET,
41 OPT_COPY
44 /*** globals ***/
45 fb_data lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH] IRAM_LCDFRAMEBUFFER __attribute__ ((aligned (16)));
48 static fb_data* lcd_backdrop = NULL;
49 static long lcd_backdrop_offset IDATA_ATTR = 0;
51 #if !defined(TOSHIBA_GIGABEAT_F) || defined(SIMULATOR)
52 static unsigned fg_pattern IDATA_ATTR = LCD_DEFAULT_FG;
53 static unsigned bg_pattern IDATA_ATTR = LCD_DEFAULT_BG;
54 #else
55 unsigned fg_pattern IDATA_ATTR = LCD_DEFAULT_FG;
56 unsigned bg_pattern IDATA_ATTR = LCD_DEFAULT_BG;
57 #endif
59 static int drawmode = DRMODE_SOLID;
60 static int xmargin = 0;
61 static int ymargin = 0;
62 static int curfont = FONT_SYSFIXED;
64 /* LCD init */
65 void lcd_init(void)
67 lcd_clear_display();
69 /* Call device specific init */
70 lcd_init_device();
71 scroll_init();
74 /*** parameter handling ***/
76 void lcd_set_drawmode(int mode)
78 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
81 int lcd_get_drawmode(void)
83 return drawmode;
86 void lcd_set_foreground(unsigned color)
88 fg_pattern = color;
91 unsigned lcd_get_foreground(void)
93 return fg_pattern;
96 void lcd_set_background(unsigned color)
98 bg_pattern = color;
101 unsigned lcd_get_background(void)
103 return bg_pattern;
106 void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
108 lcd_set_drawmode(mode);
109 fg_pattern = fg_color;
110 bg_pattern = bg_color;
113 void lcd_setmargins(int x, int y)
115 xmargin = x;
116 ymargin = y;
119 int lcd_getxmargin(void)
121 return xmargin;
124 int lcd_getymargin(void)
126 return ymargin;
129 void lcd_setfont(int newfont)
131 curfont = newfont;
134 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
136 return font_getstringsize(str, w, h, curfont);
139 /*** low-level drawing functions ***/
141 #define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)])
143 static void setpixel(fb_data *address) ICODE_ATTR;
144 static void setpixel(fb_data *address)
146 *address = fg_pattern;
149 static void clearpixel(fb_data *address) ICODE_ATTR;
150 static void clearpixel(fb_data *address)
152 *address = bg_pattern;
155 static void clearimgpixel(fb_data *address) ICODE_ATTR;
156 static void clearimgpixel(fb_data *address)
158 *address = *(fb_data *)((long)address + lcd_backdrop_offset);
161 static void flippixel(fb_data *address) ICODE_ATTR;
162 static void flippixel(fb_data *address)
164 *address = ~(*address);
167 static void nopixel(fb_data *address) ICODE_ATTR;
168 static void nopixel(fb_data *address)
170 (void)address;
173 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_bgcolor[8] = {
174 flippixel, nopixel, setpixel, setpixel,
175 nopixel, clearpixel, nopixel, clearpixel
178 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_backdrop[8] = {
179 flippixel, nopixel, setpixel, setpixel,
180 nopixel, clearimgpixel, nopixel, clearimgpixel
183 lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
185 void lcd_set_backdrop(fb_data* backdrop)
187 lcd_backdrop = backdrop;
188 if (backdrop)
190 lcd_backdrop_offset = (long)backdrop - (long)&lcd_framebuffer[0][0];
191 lcd_fastpixelfuncs = lcd_fastpixelfuncs_backdrop;
193 else
195 lcd_backdrop_offset = 0;
196 lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
200 fb_data* lcd_get_backdrop(void)
202 return lcd_backdrop;
205 /*** drawing functions ***/
207 /* Clear the whole display */
208 void lcd_clear_display(void)
210 fb_data *dst = LCDADDR(0, 0);
212 if (drawmode & DRMODE_INVERSEVID)
214 memset16(dst, fg_pattern, LCD_WIDTH*LCD_HEIGHT);
216 else
218 if (!lcd_backdrop)
219 memset16(dst, bg_pattern, LCD_WIDTH*LCD_HEIGHT);
220 else
221 memcpy(dst, lcd_backdrop, sizeof(lcd_framebuffer));
224 lcd_scroll_info.lines = 0;
227 /* Set a single pixel */
228 void lcd_drawpixel(int x, int y)
230 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
231 lcd_fastpixelfuncs[drawmode](LCDADDR(x, y));
234 /* Draw a line */
235 void lcd_drawline(int x1, int y1, int x2, int y2)
237 int numpixels;
238 int i;
239 int deltax, deltay;
240 int d, dinc1, dinc2;
241 int x, xinc1, xinc2;
242 int y, yinc1, yinc2;
243 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
245 deltax = abs(x2 - x1);
246 deltay = abs(y2 - y1);
247 xinc2 = 1;
248 yinc2 = 1;
250 if (deltax >= deltay)
252 numpixels = deltax;
253 d = 2 * deltay - deltax;
254 dinc1 = deltay * 2;
255 dinc2 = (deltay - deltax) * 2;
256 xinc1 = 1;
257 yinc1 = 0;
259 else
261 numpixels = deltay;
262 d = 2 * deltax - deltay;
263 dinc1 = deltax * 2;
264 dinc2 = (deltax - deltay) * 2;
265 xinc1 = 0;
266 yinc1 = 1;
268 numpixels++; /* include endpoints */
270 if (x1 > x2)
272 xinc1 = -xinc1;
273 xinc2 = -xinc2;
276 if (y1 > y2)
278 yinc1 = -yinc1;
279 yinc2 = -yinc2;
282 x = x1;
283 y = y1;
285 for (i = 0; i < numpixels; i++)
287 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
288 pfunc(LCDADDR(x, y));
290 if (d < 0)
292 d += dinc1;
293 x += xinc1;
294 y += yinc1;
296 else
298 d += dinc2;
299 x += xinc2;
300 y += yinc2;
305 /* Draw a horizontal line (optimised) */
306 void lcd_hline(int x1, int x2, int y)
308 int x, width;
309 unsigned bits = 0;
310 enum fill_opt fillopt = OPT_NONE;
311 fb_data *dst, *dst_end;
312 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
314 /* direction flip */
315 if (x2 < x1)
317 x = x1;
318 x1 = x2;
319 x2 = x;
322 /* nothing to draw? */
323 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
324 return;
326 /* clipping */
327 if (x1 < 0)
328 x1 = 0;
329 if (x2 >= LCD_WIDTH)
330 x2 = LCD_WIDTH-1;
332 if (drawmode & DRMODE_INVERSEVID)
334 if (drawmode & DRMODE_BG)
336 if (!lcd_backdrop)
338 fillopt = OPT_SET;
339 bits = bg_pattern;
341 else
342 fillopt = OPT_COPY;
345 else
347 if (drawmode & DRMODE_FG)
349 fillopt = OPT_SET;
350 bits = fg_pattern;
353 dst = LCDADDR(x1, y);
354 width = x2 - x1 + 1;
356 switch (fillopt)
358 case OPT_SET:
359 memset16(dst, bits, width);
360 break;
362 case OPT_COPY:
363 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
364 width * sizeof(fb_data));
365 break;
367 case OPT_NONE:
368 dst_end = dst + width;
370 pfunc(dst++);
371 while (dst < dst_end);
372 break;
376 /* Draw a vertical line (optimised) */
377 void lcd_vline(int x, int y1, int y2)
379 int y;
380 fb_data *dst, *dst_end;
381 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
383 /* direction flip */
384 if (y2 < y1)
386 y = y1;
387 y1 = y2;
388 y2 = y;
391 /* nothing to draw? */
392 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
393 return;
395 /* clipping */
396 if (y1 < 0)
397 y1 = 0;
398 if (y2 >= LCD_HEIGHT)
399 y2 = LCD_HEIGHT-1;
401 dst = LCDADDR(x, y1);
402 dst_end = dst + (y2 - y1) * LCD_WIDTH;
406 pfunc(dst);
407 dst += LCD_WIDTH;
409 while (dst <= dst_end);
412 /* Draw a rectangular box */
413 void lcd_drawrect(int x, int y, int width, int height)
415 if ((width <= 0) || (height <= 0))
416 return;
418 int x2 = x + width - 1;
419 int y2 = y + height - 1;
421 lcd_vline(x, y, y2);
422 lcd_vline(x2, y, y2);
423 lcd_hline(x, x2, y);
424 lcd_hline(x, x2, y2);
427 /* Fill a rectangular area */
428 void lcd_fillrect(int x, int y, int width, int height)
430 unsigned bits = 0;
431 enum fill_opt fillopt = OPT_NONE;
432 fb_data *dst, *dst_end;
433 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
435 /* nothing to draw? */
436 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
437 || (x + width <= 0) || (y + height <= 0))
438 return;
440 /* clipping */
441 if (x < 0)
443 width += x;
444 x = 0;
446 if (y < 0)
448 height += y;
449 y = 0;
451 if (x + width > LCD_WIDTH)
452 width = LCD_WIDTH - x;
453 if (y + height > LCD_HEIGHT)
454 height = LCD_HEIGHT - y;
456 if (drawmode & DRMODE_INVERSEVID)
458 if (drawmode & DRMODE_BG)
460 if (!lcd_backdrop)
462 fillopt = OPT_SET;
463 bits = bg_pattern;
465 else
466 fillopt = OPT_COPY;
469 else
471 if (drawmode & DRMODE_FG)
473 fillopt = OPT_SET;
474 bits = fg_pattern;
477 dst = LCDADDR(x, y);
478 dst_end = dst + height * LCD_WIDTH;
482 fb_data *dst_row, *row_end;
484 switch (fillopt)
486 case OPT_SET:
487 memset16(dst, bits, width);
488 break;
490 case OPT_COPY:
491 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
492 width * sizeof(fb_data));
493 break;
495 case OPT_NONE:
496 dst_row = dst;
497 row_end = dst_row + width;
499 pfunc(dst_row++);
500 while (dst_row < row_end);
501 break;
503 dst += LCD_WIDTH;
505 while (dst < dst_end);
508 /* About Rockbox' internal monochrome bitmap format:
510 * A bitmap contains one bit for every pixel that defines if that pixel is
511 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
512 * at top.
513 * The bytes are stored in row-major order, with byte 0 being top left,
514 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
515 * 0..7, the second row defines pixel row 8..15 etc.
517 * This is the mono bitmap format used on all other targets so far; the
518 * pixel packing doesn't really matter on a 8bit+ target. */
520 /* Draw a partial monochrome bitmap */
522 void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
523 int stride, int x, int y, int width, int height)
524 ICODE_ATTR;
525 void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
526 int stride, int x, int y, int width, int height)
528 const unsigned char *src_end;
529 bool has_backdrop;
530 fb_data *dst, *dst_end, *backdrop;
531 lcd_fastpixelfunc_type *fgfunc, *bgfunc;
533 /* nothing to draw? */
534 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
535 || (x + width <= 0) || (y + height <= 0))
536 return;
538 /* clipping */
539 if (x < 0)
541 width += x;
542 src_x -= x;
543 x = 0;
545 if (y < 0)
547 height += y;
548 src_y -= y;
549 y = 0;
551 if (x + width > LCD_WIDTH)
552 width = LCD_WIDTH - x;
553 if (y + height > LCD_HEIGHT)
554 height = LCD_HEIGHT - y;
556 src += stride * (src_y >> 3) + src_x; /* move starting point */
557 src_y &= 7;
558 src_end = src + width;
560 dst = LCDADDR(x, y);
561 has_backdrop = (lcd_backdrop != NULL);
562 backdrop = lcd_backdrop + y * LCD_WIDTH + x;
563 fgfunc = lcd_fastpixelfuncs[drawmode];
564 bgfunc = lcd_fastpixelfuncs[drawmode ^ DRMODE_INVERSEVID];
567 const unsigned char *src_col = src++;
568 unsigned data = *src_col >> src_y;
569 fb_data *dst_col = dst++;
570 int numbits = 8 - src_y;
571 fb_data *backdrop_col = backdrop++;
572 dst_end = dst_col + height * LCD_WIDTH;
575 switch (drawmode)
577 case DRMODE_SOLID:
578 if (data & 0x01)
579 *dst_col = fg_pattern;
580 else
581 *dst_col = has_backdrop ? *backdrop_col : bg_pattern;
582 break;
583 case DRMODE_FG:
584 if (data & 0x01)
585 *dst_col = fg_pattern;
586 break;
587 case (DRMODE_SOLID|DRMODE_INVERSEVID):
588 if (data & 0x01)
589 *dst_col = has_backdrop ? *backdrop_col : bg_pattern;
590 else
591 *dst_col = fg_pattern;
592 break;
593 default:
594 if (data & 0x01)
595 fgfunc(dst_col);
596 else
597 bgfunc(dst_col);
598 break;
600 dst_col += LCD_WIDTH;
601 backdrop_col += LCD_WIDTH;
602 data >>= 1;
603 if (--numbits == 0)
605 src_col += stride;
606 data = *src_col;
607 numbits = 8;
610 while (dst_col < dst_end);
612 while (src < src_end);
614 /* Draw a full monochrome bitmap */
615 void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
617 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
620 /* Draw a partial native bitmap */
621 void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
622 int stride, int x, int y, int width, int height)
623 ICODE_ATTR;
624 void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
625 int stride, int x, int y, int width, int height)
627 fb_data *dst, *dst_end;
629 /* nothing to draw? */
630 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
631 || (x + width <= 0) || (y + height <= 0))
632 return;
634 /* clipping */
635 if (x < 0)
637 width += x;
638 src_x -= x;
639 x = 0;
641 if (y < 0)
643 height += y;
644 src_y -= y;
645 y = 0;
647 if (x + width > LCD_WIDTH)
648 width = LCD_WIDTH - x;
649 if (y + height > LCD_HEIGHT)
650 height = LCD_HEIGHT - y;
652 src += stride * src_y + src_x; /* move starting point */
653 dst = LCDADDR(x, y);
654 dst_end = dst + height * LCD_WIDTH;
658 memcpy(dst, src, width * sizeof(fb_data));
659 src += stride;
660 dst += LCD_WIDTH;
662 while (dst < dst_end);
665 /* Draw a full native bitmap */
666 void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
668 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
671 #if !defined(TOSHIBA_GIGABEAT_F) || defined(SIMULATOR)
672 /* Draw a partial native bitmap */
673 void lcd_bitmap_transparent_part(const fb_data *src, int src_x, int src_y,
674 int stride, int x, int y, int width,
675 int height) ICODE_ATTR;
676 void lcd_bitmap_transparent_part(const fb_data *src, int src_x, int src_y,
677 int stride, int x, int y, int width,
678 int height)
680 fb_data *dst, *dst_end;
682 /* nothing to draw? */
683 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
684 || (x + width <= 0) || (y + height <= 0))
685 return;
687 /* clipping */
688 if (x < 0)
690 width += x;
691 src_x -= x;
692 x = 0;
694 if (y < 0)
696 height += y;
697 src_y -= y;
698 y = 0;
700 if (x + width > LCD_WIDTH)
701 width = LCD_WIDTH - x;
702 if (y + height > LCD_HEIGHT)
703 height = LCD_HEIGHT - y;
705 src += stride * src_y + src_x; /* move starting point */
706 dst = LCDADDR(x, y);
707 dst_end = dst + height * LCD_WIDTH;
711 int i;
712 for(i = 0;i < width;i++)
714 if (src[i] == REPLACEWITHFG_COLOR)
715 dst[i] = fg_pattern;
716 else if(src[i] != TRANSPARENT_COLOR)
717 dst[i] = src[i];
719 src += stride;
720 dst += LCD_WIDTH;
722 while (dst < dst_end);
724 #endif /* !defined(TOSHIBA_GIGABEAT_F) || defined(SIMULATOR) */
726 /* Draw a full native bitmap with a transparent color */
727 void lcd_bitmap_transparent(const fb_data *src, int x, int y,
728 int width, int height)
730 lcd_bitmap_transparent_part(src, 0, 0, width, x, y, width, height);
733 /* put a string at a given pixel position, skipping first ofs pixel columns */
734 static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
736 unsigned short ch;
737 unsigned short *ucs;
738 struct font* pf = font_get(curfont);
740 ucs = bidi_l2v(str, 1);
742 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
744 int width;
745 const unsigned char *bits;
747 /* get proportional width and glyph bits */
748 width = font_get_width(pf,ch);
750 if (ofs > width)
752 ofs -= width;
753 continue;
756 bits = font_get_bits(pf, ch);
758 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
760 x += width - ofs;
761 ofs = 0;
765 /* put a string at a given pixel position */
766 void lcd_putsxy(int x, int y, const unsigned char *str)
768 lcd_putsxyofs(x, y, 0, str);
771 /*** line oriented text output ***/
773 /* put a string at a given char position */
774 void lcd_puts(int x, int y, const unsigned char *str)
776 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
779 void lcd_puts_style(int x, int y, const unsigned char *str, int style)
781 lcd_puts_style_offset(x, y, str, style, 0);
784 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
786 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
789 /* put a string at a given char position, style, and pixel position,
790 * skipping first offset pixel columns */
791 void lcd_puts_style_offset(int x, int y, const unsigned char *str, int style,
792 int offset)
794 int xpos,ypos,w,h,xrect;
795 int lastmode = drawmode;
796 int oldfgcolor = fg_pattern;
797 int oldbgcolor = bg_pattern;
799 /* make sure scrolling is turned off on the line we are updating */
800 lcd_scroll_info.lines &= ~(1 << y);
802 if(!str || !str[0])
803 return;
805 lcd_getstringsize(str, &w, &h);
806 xpos = xmargin + x*w / utf8length(str);
807 ypos = ymargin + y*h;
808 drawmode = (style & STYLE_INVERT) ?
809 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
810 if (style & STYLE_COLORED) {
811 if (drawmode == DRMODE_SOLID)
812 fg_pattern = style & STYLE_COLOR_MASK;
813 else
814 bg_pattern = style & STYLE_COLOR_MASK;
816 lcd_putsxyofs(xpos, ypos, offset, str);
817 drawmode ^= DRMODE_INVERSEVID;
818 xrect = xpos + MAX(w - offset, 0);
819 lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
820 drawmode = lastmode;
821 fg_pattern = oldfgcolor;
822 bg_pattern = oldbgcolor;
825 /*** scrolling ***/
826 void lcd_puts_scroll(int x, int y, const unsigned char *string)
828 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
831 void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
833 lcd_puts_scroll_style_offset(x, y, string, style, 0);
836 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
838 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
841 void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
842 int style, int offset)
844 struct scrollinfo* s;
845 int w, h;
847 if(y>=LCD_SCROLLABLE_LINES) return;
849 s = &lcd_scroll_info.scroll[y];
851 s->start_tick = current_tick + lcd_scroll_info.delay;
852 s->invert = false;
853 if (style & STYLE_INVERT) {
854 s->invert = true;
856 lcd_puts_style_offset(x,y,string,style,offset);
858 lcd_getstringsize(string, &w, &h);
860 if (LCD_WIDTH - x * 8 - xmargin < w) {
861 /* prepare scroll line */
862 char *end;
864 memset(s->line, 0, sizeof s->line);
865 strcpy(s->line, string);
867 /* get width */
868 s->width = lcd_getstringsize(s->line, &w, &h);
870 /* scroll bidirectional or forward only depending on the string
871 width */
872 if ( lcd_scroll_info.bidir_limit ) {
873 s->bidir = s->width < (LCD_WIDTH - xmargin) *
874 (100 + lcd_scroll_info.bidir_limit) / 100;
876 else
877 s->bidir = false;
879 if (!s->bidir) { /* add spaces if scrolling in the round */
880 strcat(s->line, " ");
881 /* get new width incl. spaces */
882 s->width = lcd_getstringsize(s->line, &w, &h);
885 end = strchr(s->line, '\0');
886 strncpy(end, string, LCD_WIDTH/2);
888 s->len = utf8length(string);
889 s->offset = offset;
890 s->startx = xmargin + x * s->width / s->len;
891 s->backward = false;
892 s->line_color = (style&STYLE_COLORED)?
893 (style&STYLE_COLOR_MASK): -1;
894 lcd_scroll_info.lines |= (1<<y);
896 else
897 /* force a bit switch-off since it doesn't scroll */
898 lcd_scroll_info.lines &= ~(1<<y);
901 void lcd_scroll_fn(void)
903 struct font* pf;
904 struct scrollinfo* s;
905 int index;
906 int xpos, ypos;
907 int lastmode;
908 unsigned old_fgcolor = fg_pattern;
909 unsigned old_bgcolor = bg_pattern;
911 for ( index = 0; index < LCD_SCROLLABLE_LINES; index++ ) {
912 /* really scroll? */
913 if ((lcd_scroll_info.lines & (1 << index)) == 0)
914 continue;
916 s = &lcd_scroll_info.scroll[index];
918 /* check pause */
919 if (TIME_BEFORE(current_tick, s->start_tick))
920 continue;
922 if (s->line_color >= 0) {
923 if (s->invert) {
924 fg_pattern = old_fgcolor;
925 bg_pattern = s->line_color;
927 else {
928 fg_pattern = s->line_color;
929 bg_pattern = old_bgcolor;
933 if (s->backward)
934 s->offset -= lcd_scroll_info.step;
935 else
936 s->offset += lcd_scroll_info.step;
938 pf = font_get(curfont);
939 xpos = s->startx;
940 ypos = ymargin + index * pf->height;
942 if (s->bidir) { /* scroll bidirectional */
943 if (s->offset <= 0) {
944 /* at beginning of line */
945 s->offset = 0;
946 s->backward = false;
947 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
949 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
950 /* at end of line */
951 s->offset = s->width - (LCD_WIDTH - xpos);
952 s->backward = true;
953 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
956 else {
957 /* scroll forward the whole time */
958 if (s->offset >= s->width)
959 s->offset %= s->width;
962 lastmode = drawmode;
963 drawmode = s->invert ?
964 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
965 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
966 drawmode = lastmode;
967 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
970 fg_pattern = old_fgcolor;
971 bg_pattern = old_bgcolor;