Introduced LCD_FBHEIGHT in addition to the already existing LCD_FBWIDTH to ease frame...
[Rockbox.git] / firmware / drivers / lcd-2bit-horz.c
blob9ba52e1ba9c1f7abb68b6ab7a988d5284d5a68e2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
23 #include "config.h"
24 #include "cpu.h"
25 #include "lcd.h"
26 #include "kernel.h"
27 #include "thread.h"
28 #include <string.h>
29 #include <stdlib.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"
37 #define SCROLLABLE_LINES (((LCD_HEIGHT+4)/5 < 32) ? (LCD_HEIGHT+4)/5 : 32)
39 /*** globals ***/
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;
57 /* scrolling */
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] = {
69 /* Hz values:
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
75 /* LCD init */
76 void lcd_init(void)
78 lcd_clear_display();
79 /* Call device specific init */
80 lcd_init_device();
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)
94 return drawmode;
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)
126 xmargin = x;
127 ymargin = y;
130 int lcd_getxmargin(void)
132 return xmargin;
135 int lcd_getymargin(void)
137 return ymargin;
140 void lcd_setfont(int newfont)
142 curfont = 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];
184 *address ^= mask;
187 static void nopixel(int x, int y)
189 (void)x;
190 (void)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)
208 ICODE_ATTR;
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)
215 ICODE_ATTR;
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)
224 ICODE_ATTR;
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)
233 ICODE_ATTR;
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)
242 ICODE_ATTR;
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)
253 ICODE_ATTR;
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)
264 ICODE_ATTR;
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)
271 ICODE_ATTR;
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)
280 ICODE_ATTR;
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)
289 ICODE_ATTR;
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)
298 ICODE_ATTR;
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)
309 ICODE_ATTR;
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;
335 if (backdrop)
337 lcd_backdrop_offset = (long)backdrop - (long)lcd_framebuffer;
338 lcd_pixelfuncs = lcd_pixelfuncs_backdrop;
339 lcd_blockfuncs = lcd_blockfuncs_backdrop;
341 else
343 lcd_backdrop_offset = 0;
344 lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
345 lcd_blockfuncs = lcd_blockfuncs_bgcolor;
349 fb_data* lcd_get_backdrop(void)
351 return lcd_backdrop;
355 static inline void setblock(fb_data *address, unsigned mask, unsigned bits)
357 unsigned data = *address;
359 bits ^= data;
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);
372 else
374 if (lcd_backdrop)
375 memcpy(lcd_framebuffer, lcd_backdrop, sizeof lcd_framebuffer);
376 else
377 memset(lcd_framebuffer, bg_pattern, sizeof lcd_framebuffer);
379 scrolling_lines = 0;
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);
389 /* Draw a line */
390 void lcd_drawline(int x1, int y1, int x2, int y2)
392 int numpixels;
393 int i;
394 int deltax, deltay;
395 int d, dinc1, dinc2;
396 int x, xinc1, xinc2;
397 int y, yinc1, yinc2;
398 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
400 deltax = abs(x2 - x1);
401 deltay = abs(y2 - y1);
402 xinc2 = 1;
403 yinc2 = 1;
405 if (deltax >= deltay)
407 numpixels = deltax;
408 d = 2 * deltay - deltax;
409 dinc1 = deltay * 2;
410 dinc2 = (deltay - deltax) * 2;
411 xinc1 = 1;
412 yinc1 = 0;
414 else
416 numpixels = deltay;
417 d = 2 * deltax - deltay;
418 dinc1 = deltax * 2;
419 dinc2 = (deltax - deltay) * 2;
420 xinc1 = 0;
421 yinc1 = 1;
423 numpixels++; /* include endpoints */
425 if (x1 > x2)
427 xinc1 = -xinc1;
428 xinc2 = -xinc2;
431 if (y1 > y2)
433 yinc1 = -yinc1;
434 yinc2 = -yinc2;
437 x = x1;
438 y = y1;
440 for (i = 0; i < numpixels; i++)
442 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
443 pfunc(x, y);
445 if (d < 0)
447 d += dinc1;
448 x += xinc1;
449 y += yinc1;
451 else
453 d += dinc2;
454 x += xinc2;
455 y += yinc2;
460 /* Draw a horizontal line (optimised) */
461 void lcd_hline(int x1, int x2, int y)
463 int nx;
464 unsigned char *dst;
465 unsigned mask, mask_right;
466 lcd_blockfunc_type *bfunc;
468 /* direction flip */
469 if (x2 < x1)
471 nx = x1;
472 x1 = x2;
473 x2 = nx;
476 /* nothing to draw? */
477 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
478 return;
480 /* clipping */
481 if (x1 < 0)
482 x1 = 0;
483 if (x2 >= LCD_WIDTH)
484 x2 = LCD_WIDTH-1;
486 bfunc = lcd_blockfuncs[drawmode];
487 dst = &lcd_framebuffer[y][x1>>2];
488 nx = x2 - (x1 & ~3);
489 mask = 0xFFu >> (2 * (x1 & 3));
490 mask_right = 0xFFu << (2 * (~nx & 3));
492 for (; nx >= 4; nx -= 4)
494 bfunc(dst++, mask, 0xFFu);
495 mask = 0xFFu;
497 mask &= mask_right;
498 bfunc(dst, mask, 0xFFu);
501 /* Draw a vertical line (optimised) */
502 void lcd_vline(int x, int y1, int y2)
504 int y;
505 unsigned char *dst, *dst_end;
506 unsigned mask;
507 lcd_blockfunc_type *bfunc;
509 /* direction flip */
510 if (y2 < y1)
512 y = y1;
513 y1 = y2;
514 y2 = y;
517 /* nothing to draw? */
518 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
519 return;
521 /* clipping */
522 if (y1 < 0)
523 y1 = 0;
524 if (y2 >= LCD_HEIGHT)
525 y2 = LCD_HEIGHT-1;
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);
535 dst += LCD_FBWIDTH;
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))
544 return;
546 int x2 = x + width - 1;
547 int y2 = y + height - 1;
549 lcd_vline(x, y, y2);
550 lcd_vline(x2, y, y2);
551 lcd_hline(x, x2, y);
552 lcd_hline(x, x2, y2);
555 /* Fill a rectangular area */
556 void lcd_fillrect(int x, int y, int width, int height)
558 int nx;
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))
566 return;
568 /* clipping */
569 if (x < 0)
571 width += x;
572 x = 0;
574 if (y < 0)
576 height += y;
577 y = 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);
602 dst++;
603 mask = 0xFFu;
605 mask &= mask_right;
607 dst_end = dst + height * LCD_FBWIDTH;
610 bfunc(dst, mask, 0xFFu);
611 dst += LCD_FBWIDTH;
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
620 * at top.
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)
628 ICODE_ATTR;
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)
632 int ny, nx, ymax;
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))
640 return;
642 /* clipping */
643 if (x < 0)
645 width += x;
646 src_x -= x;
647 x = 0;
649 if (y < 0)
651 height += y;
652 src_y -= y;
653 y = 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 */
661 src_y &= 7;
662 src_end = src + width;
664 fgfunc = lcd_pixelfuncs[drawmode];
665 bgfunc = lcd_pixelfuncs[drawmode ^ DRMODE_INVERSEVID];
666 nx = x;
669 const unsigned char *src_col = src++;
670 unsigned data = *src_col >> src_y;
671 int numbits = 8 - ((int)src_y);
673 ymax = y + height;
674 ny = y;
677 if (data & 0x01)
678 fgfunc(nx,ny);
679 else
680 bgfunc(nx,ny);
682 ny++;
684 data >>= 1;
685 if (--numbits == 0)
687 src_col += stride;
688 data = *src_col;
689 numbits = 8;
692 while (ny < ymax);
693 nx++;
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,
708 * MSB at the left.
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)
717 ICODE_ATTR;
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)
721 int shift, nx;
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))
728 return;
730 /* clipping */
731 if (x < 0)
733 width += x;
734 src_x -= x;
735 x = 0;
737 if (y < 0)
739 height += y;
740 src_y -= y;
741 y = 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 */
751 src_x &= 3;
752 x -= src_x;
753 dst = &lcd_framebuffer[y][x>>2];
754 shift = x & 3;
755 nx = width - 1 + shift + src_x;
757 mask = 0xFF00u >> (2 * (shift + src_x));
758 mask_right = 0xFFu << (2 * (~nx & 3));
760 shift *= 2;
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;
767 unsigned data = 0;
769 for (x = nx; x >= 4; x -= 4)
771 data = (data << 8) | *src_row++;
773 if (mask_row & 0xFF)
775 setblock(dst_row, mask_row, data >> shift);
776 mask_row = 0xFF;
778 else
779 mask_row = mask;
781 dst_row++;
783 data = (data << 8) | *src_row;
784 setblock(dst_row, mask_row & mask_right, data >> shift);
786 src += stride;
787 dst += LCD_FBWIDTH;
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)
801 unsigned short ch;
802 unsigned short *ucs;
803 struct font* pf = font_get(curfont);
805 ucs = bidi_l2v(str, 1);
807 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
809 int width;
810 const unsigned char *bits;
812 /* get proportional width and glyph bits */
813 width = font_get_width(pf,ch);
815 if (ofs > width)
817 ofs -= width;
818 continue;
821 bits = font_get_bits(pf, ch);
823 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
824 pf->height);
826 x += width - ofs;
827 ofs = 0;
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);
866 if(!str || !str[0])
867 return;
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);
878 drawmode = lastmode;
881 /*** scrolling ***/
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;
889 (void)x;
891 s = &scroll[y];
892 s->invert = !s->invert;
895 void lcd_stop_scroll(void)
897 scrolling_lines=0;
900 void lcd_scroll_speed(int speed)
902 scroll_ticks = scroll_tick_table[speed];
905 void lcd_scroll_step(int step)
907 scroll_step = 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;
939 int w, h;
941 s = &scroll[y];
943 s->start_tick = current_tick + scroll_delay;
944 s->invert = false;
945 if (style & STYLE_INVERT) {
946 s->invert = true;
947 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
949 else
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 */
956 char *end;
958 memset(s->line, 0, sizeof s->line);
959 strcpy(s->line, (char *)string);
961 /* get width */
962 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
964 /* scroll bidirectional or forward only depending on the string
965 width */
966 if ( bidir_limit ) {
967 s->bidir = s->width < (LCD_WIDTH - xmargin) *
968 (100 + bidir_limit) / 100;
970 else
971 s->bidir = false;
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);
983 s->offset = offset;
984 s->startx = xmargin + x * s->width / s->len;;
985 s->backward = false;
986 scrolling_lines |= (1<<y);
988 else
989 /* force a bit switch-off since it doesn't scroll */
990 scrolling_lines &= ~(1<<y);
993 static void scroll_thread(void)
995 struct font* pf;
996 struct scrollinfo* s;
997 int index;
998 int xpos, ypos;
999 int lastmode;
1001 /* initialize scroll struct array */
1002 scrolling_lines = 0;
1004 while ( 1 ) {
1005 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
1006 /* really scroll? */
1007 if ( !(scrolling_lines&(1<<index)) )
1008 continue;
1010 s = &scroll[index];
1012 /* check pause */
1013 if (TIME_BEFORE(current_tick, s->start_tick))
1014 continue;
1016 if (s->backward)
1017 s->offset -= scroll_step;
1018 else
1019 s->offset += scroll_step;
1021 pf = font_get(curfont);
1022 xpos = s->startx;
1023 ypos = ymargin + index * pf->height;
1025 if (s->bidir) { /* scroll bidirectional */
1026 if (s->offset <= 0) {
1027 /* at beginning of line */
1028 s->offset = 0;
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);
1035 s->backward = true;
1036 s->start_tick = current_tick + scroll_delay * 2;
1039 else {
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);