1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 by Richard S. La Charité III
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
23 #include "lcd-remote.h"
32 #include "rbunicode.h"
35 #define SCROLLABLE_LINES (((LCD_REMOTE_HEIGHT+4)/5 < 32) ? (LCD_REMOTE_HEIGHT+4)/5 : 32)
39 fb_remote_data lcd_remote_framebuffer
[LCD_REMOTE_FBHEIGHT
][LCD_REMOTE_FBWIDTH
]
42 static int drawmode
= DRMODE_SOLID
;
43 static int xmargin
= 0;
44 static int ymargin
= 0;
45 static int curfont
= FONT_SYSFIXED
;
48 static volatile int scrolling_lines
=0; /* Bitpattern of which lines are scrolling */
49 static void scroll_thread(void);
50 static long scroll_stack
[DEFAULT_STACK_SIZE
/sizeof(long)];
51 static const char scroll_name
[] = "remote_scroll";
52 static int scroll_ticks
= 12; /* # of ticks between updates*/
53 static int scroll_delay
= HZ
/2; /* ticks delay before start */
54 static int scroll_step
= 6; /* pixels per scroll step */
55 static int bidir_limit
= 50; /* percent */
56 static struct scrollinfo scroll
[SCROLLABLE_LINES
];
58 static const char scroll_tick_table
[16] = {
60 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
61 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
66 struct event_queue remote_scroll_queue
;
69 /*** parameter handling ***/
71 void lcd_remote_set_drawmode(int mode
)
73 drawmode
= mode
& (DRMODE_SOLID
|DRMODE_INVERSEVID
);
76 int lcd_remote_get_drawmode(void)
81 void lcd_remote_setmargins(int x
, int y
)
87 int lcd_remote_getxmargin(void)
92 int lcd_remote_getymargin(void)
98 void lcd_remote_setfont(int newfont
)
103 int lcd_remote_getstringsize(const unsigned char *str
, int *w
, int *h
)
105 return font_getstringsize(str
, w
, h
, curfont
);
108 /*** low-level drawing functions ***/
110 static void setpixel(int x
, int y
)
112 lcd_remote_framebuffer
[y
>>3][x
] |= 1 << (y
& 7);
115 static void clearpixel(int x
, int y
)
117 lcd_remote_framebuffer
[y
>>3][x
] &= ~(1 << (y
& 7));
120 static void flippixel(int x
, int y
)
122 lcd_remote_framebuffer
[y
>>3][x
] ^= 1 << (y
& 7);
125 static void nopixel(int x
, int y
)
131 lcd_remote_pixelfunc_type
* const lcd_remote_pixelfuncs
[8] = {
132 flippixel
, nopixel
, setpixel
, setpixel
,
133 nopixel
, clearpixel
, nopixel
, clearpixel
136 static void flipblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
138 static void flipblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
140 *address
^= bits
& mask
;
143 static void bgblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
145 static void bgblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
147 *address
&= bits
| ~mask
;
150 static void fgblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
152 static void fgblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
154 *address
|= bits
& mask
;
157 static void solidblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
159 static void solidblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
161 unsigned data
= *address
;
164 *address
= data
^ (bits
& mask
);
167 static void flipinvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
169 static void flipinvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
171 *address
^= ~bits
& mask
;
174 static void bginvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
176 static void bginvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
178 *address
&= ~(bits
& mask
);
181 static void fginvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
183 static void fginvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
185 *address
|= ~bits
& mask
;
188 static void solidinvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
190 static void solidinvblock(fb_remote_data
*address
, unsigned mask
, unsigned bits
)
192 unsigned data
= *address
;
195 *address
= data
^ (bits
& mask
);
198 lcd_remote_blockfunc_type
* const lcd_remote_blockfuncs
[8] = {
199 flipblock
, bgblock
, fgblock
, solidblock
,
200 flipinvblock
, bginvblock
, fginvblock
, solidinvblock
203 /*** drawing functions ***/
205 /* Clear the whole display */
206 void lcd_remote_clear_display(void)
208 unsigned bits
= (drawmode
& DRMODE_INVERSEVID
) ? 0xFFu
: 0;
210 memset(lcd_remote_framebuffer
, bits
, sizeof lcd_remote_framebuffer
);
214 /* Set a single pixel */
215 void lcd_remote_drawpixel(int x
, int y
)
217 if (((unsigned)x
< LCD_REMOTE_WIDTH
) && ((unsigned)y
< LCD_REMOTE_HEIGHT
))
218 lcd_remote_pixelfuncs
[drawmode
](x
, y
);
222 void lcd_remote_drawline(int x1
, int y1
, int x2
, int y2
)
230 lcd_remote_pixelfunc_type
*pfunc
= lcd_remote_pixelfuncs
[drawmode
];
232 deltax
= abs(x2
- x1
);
233 deltay
= abs(y2
- y1
);
237 if (deltax
>= deltay
)
240 d
= 2 * deltay
- deltax
;
242 dinc2
= (deltay
- deltax
) * 2;
249 d
= 2 * deltax
- deltay
;
251 dinc2
= (deltax
- deltay
) * 2;
255 numpixels
++; /* include endpoints */
272 for (i
= 0; i
< numpixels
; i
++)
274 if (((unsigned)x
< LCD_REMOTE_WIDTH
) && ((unsigned)y
< LCD_REMOTE_HEIGHT
))
292 /* Draw a horizontal line (optimised) */
293 void lcd_remote_hline(int x1
, int x2
, int y
)
296 fb_remote_data
*dst
, *dst_end
;
298 lcd_remote_blockfunc_type
*bfunc
;
308 /* nothing to draw? */
309 if (((unsigned)y
>= LCD_REMOTE_HEIGHT
) || (x1
>= LCD_REMOTE_WIDTH
)
316 if (x2
>= LCD_REMOTE_WIDTH
)
317 x2
= LCD_REMOTE_WIDTH
-1;
319 bfunc
= lcd_remote_blockfuncs
[drawmode
];
320 dst
= &lcd_remote_framebuffer
[y
>>3][x1
];
323 dst_end
= dst
+ x2
- x1
;
325 bfunc(dst
++, mask
, 0xFFu
);
326 while (dst
<= dst_end
);
329 /* Draw a vertical line (optimised) */
330 void lcd_remote_vline(int x
, int y1
, int y2
)
334 unsigned mask
, mask_bottom
;
335 lcd_remote_blockfunc_type
*bfunc
;
345 /* nothing to draw? */
346 if (((unsigned)x
>= LCD_REMOTE_WIDTH
) || (y1
>= LCD_REMOTE_HEIGHT
)
353 if (y2
>= LCD_REMOTE_HEIGHT
)
354 y2
= LCD_REMOTE_HEIGHT
-1;
356 bfunc
= lcd_remote_blockfuncs
[drawmode
];
357 dst
= &lcd_remote_framebuffer
[y1
>>3][x
];
359 mask
= 0xFFu
<< (y1
& 7);
360 mask_bottom
= 0xFFu
>> (~ny
& 7);
362 for (; ny
>= 8; ny
-= 8)
364 bfunc(dst
, mask
, 0xFFu
);
365 dst
+= LCD_REMOTE_WIDTH
;
369 bfunc(dst
, mask
, 0xFFu
);
372 /* Draw a rectangular box */
373 void lcd_remote_drawrect(int x
, int y
, int width
, int height
)
375 if ((width
<= 0) || (height
<= 0))
378 int x2
= x
+ width
- 1;
379 int y2
= y
+ height
- 1;
381 lcd_remote_vline(x
, y
, y2
);
382 lcd_remote_vline(x2
, y
, y2
);
383 lcd_remote_hline(x
, x2
, y
);
384 lcd_remote_hline(x
, x2
, y2
);
387 /* Fill a rectangular area */
388 void lcd_remote_fillrect(int x
, int y
, int width
, int height
)
391 fb_remote_data
*dst
, *dst_end
;
392 unsigned mask
, mask_bottom
;
394 lcd_remote_blockfunc_type
*bfunc
;
395 bool fillopt
= false;
397 /* nothing to draw? */
398 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_REMOTE_WIDTH
)
399 || (y
>= LCD_REMOTE_HEIGHT
) || (x
+ width
<= 0) || (y
+ height
<= 0))
413 if (x
+ width
> LCD_REMOTE_WIDTH
)
414 width
= LCD_REMOTE_WIDTH
- x
;
415 if (y
+ height
> LCD_REMOTE_HEIGHT
)
416 height
= LCD_REMOTE_HEIGHT
- y
;
418 if (drawmode
& DRMODE_INVERSEVID
)
420 if (drawmode
& DRMODE_BG
)
427 if (drawmode
& DRMODE_FG
)
433 bfunc
= lcd_remote_blockfuncs
[drawmode
];
434 dst
= &lcd_remote_framebuffer
[y
>>3][x
];
435 ny
= height
- 1 + (y
& 7);
436 mask
= 0xFFu
<< (y
& 7);
437 mask_bottom
= 0xFFu
>> (~ny
& 7);
439 for (; ny
>= 8; ny
-= 8)
441 if (fillopt
&& (mask
== 0xFFu
))
442 memset(dst
, bits
, width
);
445 fb_remote_data
*dst_row
= dst
;
447 dst_end
= dst_row
+ width
;
449 bfunc(dst_row
++, mask
, 0xFFu
);
450 while (dst_row
< dst_end
);
453 dst
+= LCD_REMOTE_WIDTH
;
458 if (fillopt
&& (mask
== 0xFFu
))
459 memset(dst
, bits
, width
);
462 dst_end
= dst
+ width
;
464 bfunc(dst
++, mask
, 0xFFu
);
465 while (dst
< dst_end
);
469 /* About Rockbox' internal bitmap format:
471 * A bitmap contains one bit for every pixel that defines if that pixel is
472 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
474 * The bytes are stored in row-major order, with byte 0 being top left,
475 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
476 * 0..7, the second row defines pixel row 8..15 etc.
478 * This is the same as the internal lcd hw format. */
480 /* Draw a partial bitmap */
481 void lcd_remote_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
482 int stride
, int x
, int y
, int width
, int height
)
484 void lcd_remote_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
485 int stride
, int x
, int y
, int width
, int height
)
488 fb_remote_data
*dst
, *dst_end
;
489 unsigned mask
, mask_bottom
;
490 lcd_remote_blockfunc_type
*bfunc
;
492 /* nothing to draw? */
493 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_REMOTE_WIDTH
)
494 || (y
>= LCD_REMOTE_HEIGHT
) || (x
+ width
<= 0) || (y
+ height
<= 0))
510 if (x
+ width
> LCD_REMOTE_WIDTH
)
511 width
= LCD_REMOTE_WIDTH
- x
;
512 if (y
+ height
> LCD_REMOTE_HEIGHT
)
513 height
= LCD_REMOTE_HEIGHT
- y
;
515 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
518 dst
= &lcd_remote_framebuffer
[y
>>3][x
];
520 ny
= height
- 1 + shift
+ src_y
;
522 bfunc
= lcd_remote_blockfuncs
[drawmode
];
523 mask
= 0xFFu
<< (shift
+ src_y
);
524 mask_bottom
= 0xFFu
>> (~ny
& 7);
528 bool copyopt
= (drawmode
== DRMODE_SOLID
);
530 for (; ny
>= 8; ny
-= 8)
532 if (copyopt
&& (mask
== 0xFFu
))
533 memcpy(dst
, src
, width
);
536 const unsigned char *src_row
= src
;
537 fb_remote_data
*dst_row
= dst
;
539 dst_end
= dst_row
+ width
;
541 bfunc(dst_row
++, mask
, *src_row
++);
542 while (dst_row
< dst_end
);
546 dst
+= LCD_REMOTE_WIDTH
;
551 if (copyopt
&& (mask
== 0xFFu
))
552 memcpy(dst
, src
, width
);
555 dst_end
= dst
+ width
;
557 bfunc(dst
++, mask
, *src
++);
558 while (dst
< dst_end
);
563 dst_end
= dst
+ width
;
566 const unsigned char *src_col
= src
++;
567 fb_remote_data
*dst_col
= dst
++;
568 unsigned mask_col
= mask
;
571 for (y
= ny
; y
>= 8; y
-= 8)
573 data
|= *src_col
<< shift
;
575 if (mask_col
& 0xFFu
)
577 bfunc(dst_col
, mask_col
, data
);
584 dst_col
+= LCD_REMOTE_WIDTH
;
587 data
|= *src_col
<< shift
;
588 bfunc(dst_col
, mask_col
& mask_bottom
, data
);
590 while (dst
< dst_end
);
594 /* Draw a full bitmap */
595 void lcd_remote_bitmap(const unsigned char *src
, int x
, int y
, int width
,
598 lcd_remote_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
601 /* put a string at a given pixel position, skipping first ofs pixel columns */
602 static void lcd_remote_putsxyofs(int x
, int y
, int ofs
, const unsigned char *str
)
606 struct font
* pf
= font_get(curfont
);
608 ucs
= bidi_l2v(str
, 1);
610 while ((ch
= *ucs
++) != 0 && x
< LCD_REMOTE_WIDTH
)
613 const unsigned char *bits
;
615 /* get proportional width and glyph bits */
616 width
= font_get_width(pf
, ch
);
624 bits
= font_get_bits(pf
, ch
);
626 lcd_remote_bitmap_part(bits
, ofs
, 0, width
, x
, y
, width
- ofs
,
634 /* put a string at a given pixel position */
635 void lcd_remote_putsxy(int x
, int y
, const unsigned char *str
)
637 lcd_remote_putsxyofs(x
, y
, 0, str
);
640 /*** line oriented text output ***/
642 /* put a string at a given char position */
643 void lcd_remote_puts(int x
, int y
, const unsigned char *str
)
645 lcd_remote_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, 0);
648 void lcd_remote_puts_style(int x
, int y
, const unsigned char *str
, int style
)
650 lcd_remote_puts_style_offset(x
, y
, str
, style
, 0);
653 void lcd_remote_puts_offset(int x
, int y
, const unsigned char *str
, int offset
)
655 lcd_remote_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, offset
);
658 /* put a string at a given char position, style, and pixel position,
659 * skipping first offset pixel columns */
660 void lcd_remote_puts_style_offset(int x
, int y
, const unsigned char *str
,
661 int style
, int offset
)
663 int xpos
,ypos
,w
,h
,xrect
;
664 int lastmode
= drawmode
;
666 /* make sure scrolling is turned off on the line we are updating */
667 scrolling_lines
&= ~(1 << y
);
672 lcd_remote_getstringsize(str
, &w
, &h
);
673 xpos
= xmargin
+ x
*w
/ utf8length((char *)str
);
674 ypos
= ymargin
+ y
*h
;
675 drawmode
= (style
& STYLE_INVERT
) ?
676 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
677 lcd_remote_putsxyofs(xpos
, ypos
, offset
, str
);
678 drawmode
^= DRMODE_INVERSEVID
;
679 xrect
= xpos
+ MAX(w
- offset
, 0);
680 lcd_remote_fillrect(xrect
, ypos
, LCD_REMOTE_WIDTH
- xrect
, h
);
686 /* Reverse the invert setting of the scrolling line (if any) at given char
687 position. Setting will go into affect next time line scrolls. */
688 void lcd_remote_invertscroll(int x
, int y
)
690 struct scrollinfo
* s
;
694 if(y
>=SCROLLABLE_LINES
) return;
697 s
->invert
= !s
->invert
;
700 void lcd_remote_stop_scroll(void)
705 void lcd_remote_scroll_speed(int speed
)
707 scroll_ticks
= scroll_tick_table
[speed
];
710 void lcd_remote_scroll_step(int step
)
715 void lcd_remote_scroll_delay(int ms
)
717 scroll_delay
= ms
/ (HZ
/ 10);
720 void lcd_remote_bidir_scroll(int percent
)
722 bidir_limit
= percent
;
725 void lcd_remote_puts_scroll(int x
, int y
, const unsigned char *string
)
727 lcd_remote_puts_scroll_style(x
, y
, string
, STYLE_DEFAULT
);
730 void lcd_remote_puts_scroll_style(int x
, int y
, const unsigned char *string
, int style
)
732 lcd_remote_puts_scroll_style_offset(x
, y
, string
, style
, 0);
735 void lcd_remote_puts_scroll_offset(int x
, int y
, const unsigned char *string
, int offset
)
737 lcd_remote_puts_scroll_style_offset(x
, y
, string
, STYLE_DEFAULT
, offset
);
740 void lcd_remote_puts_scroll_style_offset(int x
, int y
, const unsigned char *string
,
741 int style
, int offset
)
743 struct scrollinfo
* s
;
746 if(y
>=SCROLLABLE_LINES
) return;
750 s
->start_tick
= current_tick
+ scroll_delay
;
752 if (style
& STYLE_INVERT
) {
754 lcd_remote_puts_style_offset(x
,y
,string
,STYLE_INVERT
,offset
);
757 lcd_remote_puts_offset(x
,y
,string
,offset
);
759 lcd_remote_getstringsize(string
, &w
, &h
);
761 if (LCD_REMOTE_WIDTH
- x
* 8 - xmargin
< w
) {
762 /* prepare scroll line */
765 memset(s
->line
, 0, sizeof s
->line
);
766 strcpy(s
->line
, (char *)string
);
769 s
->width
= lcd_remote_getstringsize((unsigned char *)s
->line
, &w
, &h
);
771 /* scroll bidirectional or forward only depending on the string
774 s
->bidir
= s
->width
< (LCD_REMOTE_WIDTH
- xmargin
) *
775 (100 + bidir_limit
) / 100;
780 if (!s
->bidir
) { /* add spaces if scrolling in the round */
781 strcat(s
->line
, " ");
782 /* get new width incl. spaces */
783 s
->width
= lcd_remote_getstringsize((unsigned char *)s
->line
, &w
, &h
);
786 end
= strchr(s
->line
, '\0');
787 strncpy(end
, (char *)string
, LCD_REMOTE_WIDTH
/2);
789 s
->len
= utf8length((char *)string
);
791 s
->startx
= xmargin
+ x
* s
->width
/ s
->len
;;
793 scrolling_lines
|= (1<<y
);
796 /* force a bit switch-off since it doesn't scroll */
797 scrolling_lines
&= ~(1<<y
);
800 static void scroll_thread(void)
803 struct scrollinfo
* s
;
804 long next_tick
= current_tick
;
813 /* initialize scroll struct array */
821 if (remote_initialized
)
822 queue_wait_w_tmo(&remote_scroll_queue
, &ev
, delay
);
824 queue_wait(&remote_scroll_queue
, &ev
);
828 case REMOTE_INIT_LCD
:
833 case REMOTE_DEINIT_LCD
:
838 delay
= next_tick
- current_tick
- 1;
842 for ( index
= 0; index
< SCROLLABLE_LINES
; index
++ ) {
844 if ( !(scrolling_lines
&(1<<index
)) )
850 if (TIME_BEFORE(current_tick
, s
->start_tick
))
854 s
->offset
-= scroll_step
;
856 s
->offset
+= scroll_step
;
858 pf
= font_get(curfont
);
860 ypos
= ymargin
+ index
* pf
->height
;
862 if (s
->bidir
) { /* scroll bidirectional */
863 if (s
->offset
<= 0) {
864 /* at beginning of line */
867 s
->start_tick
= current_tick
+ scroll_delay
* 2;
869 if (s
->offset
>= s
->width
- (LCD_REMOTE_WIDTH
- xpos
)) {
871 s
->offset
= s
->width
- (LCD_REMOTE_WIDTH
- xpos
);
873 s
->start_tick
= current_tick
+ scroll_delay
* 2;
877 /* scroll forward the whole time */
878 if (s
->offset
>= s
->width
)
879 s
->offset
%= s
->width
;
883 drawmode
= s
->invert
?
884 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
885 lcd_remote_putsxyofs(xpos
, ypos
, s
->offset
, s
->line
);
887 lcd_remote_update_rect(xpos
, ypos
, LCD_REMOTE_WIDTH
- xpos
, pf
->height
);
890 next_tick
+= scroll_ticks
;
891 delay
= next_tick
- current_tick
- 1;
894 next_tick
= current_tick
+ 1;
901 void lcd_remote_init(void)
904 /* Call device specific init */
905 lcd_remote_init_device();
907 queue_init(&remote_scroll_queue
, false);
909 create_thread(scroll_thread
, scroll_stack
,
910 sizeof(scroll_stack
), scroll_name
IF_PRIO(, PRIORITY_USER_INTERFACE
)
911 IF_COP(, CPU
, false));