1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * LCD driver for horizontally-packed 2bpp greyscale display
12 * Based on code from the rockbox lcd's driver
14 * Copyright (c) 2006 Seven Le Mesle (sevlm@free.fr)
16 * All files in this archive are subject to the GNU General Public License.
17 * See the file COPYING in the source tree root for full license agreement.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
34 #include "rbunicode.h"
37 #define SCROLLABLE_LINES (((LCD_HEIGHT+4)/5 < 32) ? (LCD_HEIGHT+4)/5 : 32)
41 unsigned char lcd_framebuffer
[LCD_FBHEIGHT
][LCD_FBWIDTH
] IBSS_ATTR
;
43 static const unsigned char pixmask
[4] ICONST_ATTR
= {
44 0xC0, 0x30, 0x0C, 0x03
47 static fb_data
* lcd_backdrop
= NULL
;
48 static long lcd_backdrop_offset IDATA_ATTR
= 0;
50 static unsigned fg_pattern IDATA_ATTR
= 0xFF; /* initially black */
51 static unsigned bg_pattern IDATA_ATTR
= 0x00; /* initially white */
52 static int drawmode
= DRMODE_SOLID
;
53 static int xmargin
= 0;
54 static int ymargin
= 0;
55 static int curfont
= FONT_SYSFIXED
;
58 static volatile int scrolling_lines
=0; /* Bitpattern of which lines are scrolling */
59 static void scroll_thread(void);
60 static long scroll_stack
[DEFAULT_STACK_SIZE
/sizeof(long)];
61 static const char scroll_name
[] = "scroll";
62 static int scroll_ticks
= 12; /* # of ticks between updates*/
63 static int scroll_delay
= HZ
/2; /* ticks delay before start */
64 static int scroll_step
= 6; /* pixels per scroll step */
65 static int bidir_limit
= 50; /* percent */
66 static struct scrollinfo scroll
[SCROLLABLE_LINES
];
68 static const char scroll_tick_table
[16] = {
70 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
71 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
79 /* Call device specific init */
81 create_thread(scroll_thread
, scroll_stack
,
82 sizeof(scroll_stack
), scroll_name
IF_PRIO(, PRIORITY_USER_INTERFACE
));
85 /*** parameter handling ***/
87 void lcd_set_drawmode(int mode
)
89 drawmode
= mode
& (DRMODE_SOLID
|DRMODE_INVERSEVID
);
92 int lcd_get_drawmode(void)
97 void lcd_set_foreground(unsigned brightness
)
99 fg_pattern
= 0x55 * (~brightness
& 3);
102 unsigned lcd_get_foreground(void)
104 return ~fg_pattern
& 3;
107 void lcd_set_background(unsigned brightness
)
109 bg_pattern
= 0x55 * (~brightness
& 3);
112 unsigned lcd_get_background(void)
114 return ~bg_pattern
& 3;
117 void lcd_set_drawinfo(int mode
, unsigned fg_brightness
, unsigned bg_brightness
)
119 lcd_set_drawmode(mode
);
120 lcd_set_foreground(fg_brightness
);
121 lcd_set_background(bg_brightness
);
124 void lcd_setmargins(int x
, int y
)
130 int lcd_getxmargin(void)
135 int lcd_getymargin(void)
140 void lcd_setfont(int newfont
)
145 int lcd_getstringsize(const unsigned char *str
, int *w
, int *h
)
147 return font_getstringsize(str
, w
, h
, curfont
);
150 /*** low-level drawing functions ***/
152 static void setpixel(int x
, int y
)
154 unsigned mask
= pixmask
[x
& 3];
155 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
156 unsigned data
= *address
;
158 *address
= data
^ ((data
^ fg_pattern
) & mask
);
161 static void clearpixel(int x
, int y
)
163 unsigned mask
= pixmask
[x
& 3];
164 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
165 unsigned data
= *address
;
167 *address
= data
^ ((data
^ bg_pattern
) & mask
);
170 static void clearimgpixel(int x
, int y
)
172 unsigned mask
= pixmask
[x
& 3];
173 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
174 unsigned data
= *address
;
176 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
);
179 static void flippixel(int x
, int y
)
181 unsigned mask
= pixmask
[x
& 3];
182 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
187 static void nopixel(int x
, int y
)
193 lcd_pixelfunc_type
* const lcd_pixelfuncs_bgcolor
[8] = {
194 flippixel
, nopixel
, setpixel
, setpixel
,
195 nopixel
, clearpixel
, nopixel
, clearpixel
198 lcd_pixelfunc_type
* const lcd_pixelfuncs_backdrop
[8] = {
199 flippixel
, nopixel
, setpixel
, setpixel
,
200 nopixel
, clearimgpixel
, nopixel
, clearimgpixel
203 lcd_pixelfunc_type
* const * lcd_pixelfuncs
= lcd_pixelfuncs_bgcolor
;
206 /* 'mask' and 'bits' contain 2 bits per pixel */
207 static void flipblock(fb_data
*address
, unsigned mask
, unsigned bits
)
209 static void flipblock(fb_data
*address
, unsigned mask
, unsigned bits
)
211 *address
^= bits
& mask
;
214 static void bgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
216 static void bgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
218 unsigned data
= *address
;
220 *address
= data
^ ((data
^ bg_pattern
) & mask
& ~bits
);
223 static void bgimgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
225 static void bgimgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
227 unsigned data
= *address
;
229 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
& ~bits
);
232 static void fgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
234 static void fgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
236 unsigned data
= *address
;
238 *address
= data
^ ((data
^ fg_pattern
) & mask
& bits
);
241 static void solidblock(fb_data
*address
, unsigned mask
, unsigned bits
)
243 static void solidblock(fb_data
*address
, unsigned mask
, unsigned bits
)
245 unsigned data
= *address
;
246 unsigned bgp
= bg_pattern
;
248 bits
= bgp
^ ((bgp
^ fg_pattern
) & bits
);
249 *address
= data
^ ((data
^ bits
) & mask
);
252 static void solidimgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
254 static void solidimgblock(fb_data
*address
, unsigned mask
, unsigned bits
)
256 unsigned data
= *address
;
257 unsigned bgp
= *(address
+ lcd_backdrop_offset
);
259 bits
= bgp
^ ((bgp
^ fg_pattern
) & bits
);
260 *address
= data
^ ((data
^ bits
) & mask
);
263 static void flipinvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
265 static void flipinvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
267 *address
^= ~bits
& mask
;
270 static void bginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
272 static void bginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
274 unsigned data
= *address
;
276 *address
= data
^ ((data
^ bg_pattern
) & mask
& bits
);
279 static void bgimginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
281 static void bgimginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
283 unsigned data
= *address
;
285 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
& bits
);
288 static void fginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
290 static void fginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
292 unsigned data
= *address
;
294 *address
= data
^ ((data
^ fg_pattern
) & mask
& ~bits
);
297 static void solidinvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
299 static void solidinvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
301 unsigned data
= *address
;
302 unsigned fgp
= fg_pattern
;
304 bits
= fgp
^ ((fgp
^ bg_pattern
) & bits
);
305 *address
= data
^ ((data
^ bits
) & mask
);
308 static void solidimginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
310 static void solidimginvblock(fb_data
*address
, unsigned mask
, unsigned bits
)
312 unsigned data
= *address
;
313 unsigned fgp
= fg_pattern
;
315 bits
= fgp
^ ((fgp
^ *(address
+ lcd_backdrop_offset
)) & bits
);
316 *address
= data
^ ((data
^ bits
) & mask
);
319 lcd_blockfunc_type
* const lcd_blockfuncs_bgcolor
[8] = {
320 flipblock
, bgblock
, fgblock
, solidblock
,
321 flipinvblock
, bginvblock
, fginvblock
, solidinvblock
324 lcd_blockfunc_type
* const lcd_blockfuncs_backdrop
[8] = {
325 flipblock
, bgimgblock
, fgblock
, solidimgblock
,
326 flipinvblock
, bgimginvblock
, fginvblock
, solidimginvblock
329 lcd_blockfunc_type
* const * lcd_blockfuncs
= lcd_blockfuncs_bgcolor
;
332 void lcd_set_backdrop(fb_data
* backdrop
)
334 lcd_backdrop
= backdrop
;
337 lcd_backdrop_offset
= (long)backdrop
- (long)lcd_framebuffer
;
338 lcd_pixelfuncs
= lcd_pixelfuncs_backdrop
;
339 lcd_blockfuncs
= lcd_blockfuncs_backdrop
;
343 lcd_backdrop_offset
= 0;
344 lcd_pixelfuncs
= lcd_pixelfuncs_bgcolor
;
345 lcd_blockfuncs
= lcd_blockfuncs_bgcolor
;
349 fb_data
* lcd_get_backdrop(void)
355 static inline void setblock(fb_data
*address
, unsigned mask
, unsigned bits
)
357 unsigned data
= *address
;
360 *address
= data
^ (bits
& mask
);
363 /*** drawing functions ***/
365 /* Clear the whole display */
366 void lcd_clear_display(void)
368 if (drawmode
& DRMODE_INVERSEVID
)
370 memset(lcd_framebuffer
, fg_pattern
, sizeof lcd_framebuffer
);
375 memcpy(lcd_framebuffer
, lcd_backdrop
, sizeof lcd_framebuffer
);
377 memset(lcd_framebuffer
, bg_pattern
, sizeof lcd_framebuffer
);
382 /* Set a single pixel */
383 void lcd_drawpixel(int x
, int y
)
385 if (((unsigned)x
< LCD_WIDTH
) && ((unsigned)y
< LCD_HEIGHT
))
386 lcd_pixelfuncs
[drawmode
](x
, y
);
390 void lcd_drawline(int x1
, int y1
, int x2
, int y2
)
398 lcd_pixelfunc_type
*pfunc
= lcd_pixelfuncs
[drawmode
];
400 deltax
= abs(x2
- x1
);
401 deltay
= abs(y2
- y1
);
405 if (deltax
>= deltay
)
408 d
= 2 * deltay
- deltax
;
410 dinc2
= (deltay
- deltax
) * 2;
417 d
= 2 * deltax
- deltay
;
419 dinc2
= (deltax
- deltay
) * 2;
423 numpixels
++; /* include endpoints */
440 for (i
= 0; i
< numpixels
; i
++)
442 if (((unsigned)x
< LCD_WIDTH
) && ((unsigned)y
< LCD_HEIGHT
))
460 /* Draw a horizontal line (optimised) */
461 void lcd_hline(int x1
, int x2
, int y
)
465 unsigned mask
, mask_right
;
466 lcd_blockfunc_type
*bfunc
;
476 /* nothing to draw? */
477 if (((unsigned)y
>= LCD_HEIGHT
) || (x1
>= LCD_WIDTH
) || (x2
< 0))
486 bfunc
= lcd_blockfuncs
[drawmode
];
487 dst
= &lcd_framebuffer
[y
][x1
>>2];
489 mask
= 0xFFu
>> (2 * (x1
& 3));
490 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
492 for (; nx
>= 4; nx
-= 4)
494 bfunc(dst
++, mask
, 0xFFu
);
498 bfunc(dst
, mask
, 0xFFu
);
501 /* Draw a vertical line (optimised) */
502 void lcd_vline(int x
, int y1
, int y2
)
505 unsigned char *dst
, *dst_end
;
507 lcd_blockfunc_type
*bfunc
;
517 /* nothing to draw? */
518 if (((unsigned)x
>= LCD_WIDTH
) || (y1
>= LCD_HEIGHT
) || (y2
< 0))
524 if (y2
>= LCD_HEIGHT
)
527 bfunc
= lcd_blockfuncs
[drawmode
];
528 dst
= &lcd_framebuffer
[y1
][x
>>2];
529 mask
= pixmask
[x
& 3];
531 dst_end
= dst
+ (y2
- y1
) * LCD_FBWIDTH
;
534 bfunc(dst
, mask
, 0xFFu
);
537 while (dst
<= dst_end
);
540 /* Draw a rectangular box */
541 void lcd_drawrect(int x
, int y
, int width
, int height
)
543 if ((width
<= 0) || (height
<= 0))
546 int x2
= x
+ width
- 1;
547 int y2
= y
+ height
- 1;
550 lcd_vline(x2
, y
, y2
);
552 lcd_hline(x
, x2
, y2
);
555 /* Fill a rectangular area */
556 void lcd_fillrect(int x
, int y
, int width
, int height
)
559 unsigned char *dst
, *dst_end
;
560 unsigned mask
, mask_right
;
561 lcd_blockfunc_type
*bfunc
;
563 /* nothing to draw? */
564 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_WIDTH
) || (y
>= LCD_HEIGHT
)
565 || (x
+ width
<= 0) || (y
+ height
<= 0))
579 if (x
+ width
> LCD_WIDTH
)
580 width
= LCD_WIDTH
- x
;
581 if (y
+ height
> LCD_HEIGHT
)
582 height
= LCD_HEIGHT
- y
;
584 bfunc
= lcd_blockfuncs
[drawmode
];
585 dst
= &lcd_framebuffer
[y
][x
>>2];
586 nx
= width
- 1 + (x
& 3);
587 mask
= 0xFFu
>> (2 * (x
& 3));
588 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
590 for (; nx
>= 4; nx
-= 4)
592 unsigned char *dst_col
= dst
;
594 dst_end
= dst_col
+ height
* LCD_FBWIDTH
;
597 bfunc(dst_col
, mask
, 0xFFu
);
598 dst_col
+= LCD_FBWIDTH
;
600 while (dst_col
< dst_end
);
607 dst_end
= dst
+ height
* LCD_FBWIDTH
;
610 bfunc(dst
, mask
, 0xFFu
);
613 while (dst
< dst_end
);
616 /* About Rockbox' internal monochrome bitmap format:
618 * A bitmap contains one bit for every pixel that defines if that pixel is
619 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
621 * The bytes are stored in row-major order, with byte 0 being top left,
622 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
623 * 0..7, the second row defines pixel row 8..15 etc. */
625 /* Draw a partial monochrome bitmap */
626 void lcd_mono_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
627 int stride
, int x
, int y
, int width
, int height
)
629 void lcd_mono_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
630 int stride
, int x
, int y
, int width
, int height
)
633 const unsigned char * src_end
;
634 lcd_pixelfunc_type
* fgfunc
;
635 lcd_pixelfunc_type
* bgfunc
;
637 /* nothing to draw? */
638 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_WIDTH
) || (y
>= LCD_HEIGHT
)
639 || (x
+ width
<= 0) || (y
+ height
<= 0))
655 if (x
+ width
> LCD_WIDTH
)
656 width
= LCD_WIDTH
- x
;
657 if (y
+ height
> LCD_HEIGHT
)
658 height
= LCD_HEIGHT
- y
;
660 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
662 src_end
= src
+ width
;
664 fgfunc
= lcd_pixelfuncs
[drawmode
];
665 bgfunc
= lcd_pixelfuncs
[drawmode
^ DRMODE_INVERSEVID
];
669 const unsigned char *src_col
= src
++;
670 unsigned data
= *src_col
>> src_y
;
671 int numbits
= 8 - ((int)src_y
);
695 while (src
< src_end
);
698 /* Draw a full monochrome bitmap */
699 void lcd_mono_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
701 lcd_mono_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
704 /* About Rockbox' internal native bitmap format:
706 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
707 * 10 = dark grey, 11 = black. Bits within a byte are arranged horizontally,
709 * The bytes are stored in row-major order, with byte 0 being top left,
710 * byte 1 2nd from left etc. Each row of bytes defines one pixel row.
712 * This is the same as the internal lcd hw format. */
714 /* Draw a partial native bitmap */
715 void lcd_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
716 int stride
, int x
, int y
, int width
, int height
)
718 void lcd_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
719 int stride
, int x
, int y
, int width
, int height
)
722 unsigned char *dst
, *dst_end
;
723 unsigned mask
, mask_right
;
725 /* nothing to draw? */
726 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_WIDTH
) || (y
>= LCD_HEIGHT
)
727 || (x
+ width
<= 0) || (y
+ height
<= 0))
743 if (x
+ width
> LCD_WIDTH
)
744 width
= LCD_WIDTH
- x
;
745 if (y
+ height
> LCD_HEIGHT
)
746 height
= LCD_HEIGHT
- y
;
748 stride
= (stride
+ 3) >> 2; /* convert to no. of bytes */
750 src
+= stride
* src_y
+ (src_x
>> 2); /* move starting point */
753 dst
= &lcd_framebuffer
[y
][x
>>2];
755 nx
= width
- 1 + shift
+ src_x
;
757 mask
= 0xFF00u
>> (2 * (shift
+ src_x
));
758 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
761 dst_end
= dst
+ height
* LCD_FBWIDTH
;
764 const unsigned char *src_row
= src
;
765 unsigned char *dst_row
= dst
;
766 unsigned mask_row
= mask
>> 8;
769 for (x
= nx
; x
>= 4; x
-= 4)
771 data
= (data
<< 8) | *src_row
++;
775 setblock(dst_row
, mask_row
, data
>> shift
);
783 data
= (data
<< 8) | *src_row
;
784 setblock(dst_row
, mask_row
& mask_right
, data
>> shift
);
789 while (dst
< dst_end
);
792 /* Draw a full native bitmap */
793 void lcd_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
795 lcd_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
798 /* put a string at a given pixel position, skipping first ofs pixel columns */
799 static void lcd_putsxyofs(int x
, int y
, int ofs
, const unsigned char *str
)
803 struct font
* pf
= font_get(curfont
);
805 ucs
= bidi_l2v(str
, 1);
807 while ((ch
= *ucs
++) != 0 && x
< LCD_WIDTH
)
810 const unsigned char *bits
;
812 /* get proportional width and glyph bits */
813 width
= font_get_width(pf
,ch
);
821 bits
= font_get_bits(pf
, ch
);
823 lcd_mono_bitmap_part(bits
, ofs
, 0, width
, x
, y
, width
- ofs
,
831 /* put a string at a given pixel position */
832 void lcd_putsxy(int x
, int y
, const unsigned char *str
)
834 lcd_putsxyofs(x
, y
, 0, str
);
837 /*** line oriented text output ***/
839 /* put a string at a given char position */
840 void lcd_puts(int x
, int y
, const unsigned char *str
)
842 lcd_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, 0);
845 void lcd_puts_style(int x
, int y
, const unsigned char *str
, int style
)
847 lcd_puts_style_offset(x
, y
, str
, style
, 0);
850 void lcd_puts_offset(int x
, int y
, const unsigned char *str
, int offset
)
852 lcd_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, offset
);
855 /* put a string at a given char position, style, and pixel position,
856 * skipping first offset pixel columns */
857 void lcd_puts_style_offset(int x
, int y
, const unsigned char *str
,
858 int style
, int offset
)
860 int xpos
,ypos
,w
,h
,xrect
;
861 int lastmode
= drawmode
;
863 /* make sure scrolling is turned off on the line we are updating */
864 scrolling_lines
&= ~(1 << y
);
869 lcd_getstringsize(str
, &w
, &h
);
870 xpos
= xmargin
+ x
*w
/ utf8length((char *)str
);
871 ypos
= ymargin
+ y
*h
;
872 drawmode
= (style
& STYLE_INVERT
) ?
873 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
874 lcd_putsxyofs(xpos
, ypos
, offset
, str
);
875 drawmode
^= DRMODE_INVERSEVID
;
876 xrect
= xpos
+ MAX(w
- offset
, 0);
877 lcd_fillrect(xrect
, ypos
, LCD_WIDTH
- xrect
, h
);
883 /* Reverse the invert setting of the scrolling line (if any) at given char
884 position. Setting will go into affect next time line scrolls. */
885 void lcd_invertscroll(int x
, int y
)
887 struct scrollinfo
* s
;
892 s
->invert
= !s
->invert
;
895 void lcd_stop_scroll(void)
900 void lcd_scroll_speed(int speed
)
902 scroll_ticks
= scroll_tick_table
[speed
];
905 void lcd_scroll_step(int step
)
910 void lcd_scroll_delay(int ms
)
912 scroll_delay
= ms
/ (HZ
/ 10);
915 void lcd_bidir_scroll(int percent
)
917 bidir_limit
= percent
;
920 void lcd_puts_scroll(int x
, int y
, const unsigned char *string
)
922 lcd_puts_scroll_style(x
, y
, string
, STYLE_DEFAULT
);
925 void lcd_puts_scroll_style(int x
, int y
, const unsigned char *string
, int style
)
927 lcd_puts_scroll_style_offset(x
, y
, string
, style
, 0);
930 void lcd_puts_scroll_offset(int x
, int y
, const unsigned char *string
, int offset
)
932 lcd_puts_scroll_style_offset(x
, y
, string
, STYLE_DEFAULT
, offset
);
935 void lcd_puts_scroll_style_offset(int x
, int y
, const unsigned char *string
,
936 int style
, int offset
)
938 struct scrollinfo
* s
;
943 s
->start_tick
= current_tick
+ scroll_delay
;
945 if (style
& STYLE_INVERT
) {
947 lcd_puts_style_offset(x
,y
,string
,STYLE_INVERT
,offset
);
950 lcd_puts_offset(x
,y
,string
,offset
);
952 lcd_getstringsize(string
, &w
, &h
);
954 if (LCD_WIDTH
- x
* 8 - xmargin
< w
) {
955 /* prepare scroll line */
958 memset(s
->line
, 0, sizeof s
->line
);
959 strcpy(s
->line
, (char *)string
);
962 s
->width
= lcd_getstringsize((unsigned char *)s
->line
, &w
, &h
);
964 /* scroll bidirectional or forward only depending on the string
967 s
->bidir
= s
->width
< (LCD_WIDTH
- xmargin
) *
968 (100 + bidir_limit
) / 100;
973 if (!s
->bidir
) { /* add spaces if scrolling in the round */
974 strcat(s
->line
, " ");
975 /* get new width incl. spaces */
976 s
->width
= lcd_getstringsize((unsigned char *)s
->line
, &w
, &h
);
979 end
= strchr(s
->line
, '\0');
980 strncpy(end
, (char *)string
, LCD_WIDTH
/2);
982 s
->len
= utf8length((char *)string
);
984 s
->startx
= xmargin
+ x
* s
->width
/ s
->len
;;
986 scrolling_lines
|= (1<<y
);
989 /* force a bit switch-off since it doesn't scroll */
990 scrolling_lines
&= ~(1<<y
);
993 static void scroll_thread(void)
996 struct scrollinfo
* s
;
1001 /* initialize scroll struct array */
1002 scrolling_lines
= 0;
1005 for ( index
= 0; index
< SCROLLABLE_LINES
; index
++ ) {
1006 /* really scroll? */
1007 if ( !(scrolling_lines
&(1<<index
)) )
1013 if (TIME_BEFORE(current_tick
, s
->start_tick
))
1017 s
->offset
-= scroll_step
;
1019 s
->offset
+= scroll_step
;
1021 pf
= font_get(curfont
);
1023 ypos
= ymargin
+ index
* pf
->height
;
1025 if (s
->bidir
) { /* scroll bidirectional */
1026 if (s
->offset
<= 0) {
1027 /* at beginning of line */
1029 s
->backward
= false;
1030 s
->start_tick
= current_tick
+ scroll_delay
* 2;
1032 if (s
->offset
>= s
->width
- (LCD_WIDTH
- xpos
)) {
1033 /* at end of line */
1034 s
->offset
= s
->width
- (LCD_WIDTH
- xpos
);
1036 s
->start_tick
= current_tick
+ scroll_delay
* 2;
1040 /* scroll forward the whole time */
1041 if (s
->offset
>= s
->width
)
1042 s
->offset
%= s
->width
;
1045 lastmode
= drawmode
;
1046 drawmode
= s
->invert
?
1047 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
1048 lcd_putsxyofs(xpos
, ypos
, s
->offset
, s
->line
);
1049 drawmode
= lastmode
;
1050 lcd_update_rect(xpos
, ypos
, LCD_WIDTH
- xpos
, pf
->height
);
1053 sleep(scroll_ticks
);