summaryrefslogtreecommitdiff
path: root/firmware/drivers/lcd-remote-1bit-v.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/lcd-remote-1bit-v.c')
-rw-r--r--firmware/drivers/lcd-remote-1bit-v.c907
1 files changed, 907 insertions, 0 deletions
diff --git a/firmware/drivers/lcd-remote-1bit-v.c b/firmware/drivers/lcd-remote-1bit-v.c
new file mode 100644
index 0000000000..c81ccc83c9
--- /dev/null
+++ b/firmware/drivers/lcd-remote-1bit-v.c
@@ -0,0 +1,907 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Richard S. La Charité III
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "config.h"
21#include "cpu.h"
22#include "lcd.h"
23#include "lcd-remote.h"
24#include "kernel.h"
25#include "thread.h"
26#include <string.h>
27#include <stdlib.h>
28#include "file.h"
29#include "debug.h"
30#include "system.h"
31#include "font.h"
32#include "rbunicode.h"
33#include "bidi.h"
34
35#define SCROLLABLE_LINES (((LCD_REMOTE_HEIGHT+4)/5 < 32) ? (LCD_REMOTE_HEIGHT+4)/5 : 32)
36
37/*** globals ***/
38
39fb_remote_data lcd_remote_framebuffer[LCD_REMOTE_FBHEIGHT][LCD_REMOTE_FBWIDTH]
40 IBSS_ATTR;
41
42static int drawmode = DRMODE_SOLID;
43static int xmargin = 0;
44static int ymargin = 0;
45static int curfont = FONT_SYSFIXED;
46
47/* scrolling */
48static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
49static void scroll_thread(void);
50static long scroll_stack[DEFAULT_STACK_SIZE/sizeof(long)];
51static const char scroll_name[] = "remote_scroll";
52static int scroll_ticks = 12; /* # of ticks between updates*/
53static int scroll_delay = HZ/2; /* ticks delay before start */
54static int scroll_step = 6; /* pixels per scroll step */
55static int bidir_limit = 50; /* percent */
56static struct scrollinfo scroll[SCROLLABLE_LINES];
57
58static const char scroll_tick_table[16] = {
59 /* Hz values:
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
62};
63
64/* remote hotplug */
65#ifndef SIMULATOR
66struct event_queue remote_scroll_queue;
67#endif
68
69/*** parameter handling ***/
70
71void lcd_remote_set_drawmode(int mode)
72{
73 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
74}
75
76int lcd_remote_get_drawmode(void)
77{
78 return drawmode;
79}
80
81void lcd_remote_setmargins(int x, int y)
82{
83 xmargin = x;
84 ymargin = y;
85}
86
87int lcd_remote_getxmargin(void)
88{
89 return xmargin;
90}
91
92int lcd_remote_getymargin(void)
93{
94 return ymargin;
95}
96
97
98void lcd_remote_setfont(int newfont)
99{
100 curfont = newfont;
101}
102
103int lcd_remote_getstringsize(const unsigned char *str, int *w, int *h)
104{
105 return font_getstringsize(str, w, h, curfont);
106}
107
108/*** low-level drawing functions ***/
109
110static void setpixel(int x, int y)
111{
112 lcd_remote_framebuffer[y>>3][x] |= 1 << (y & 7);
113}
114
115static void clearpixel(int x, int y)
116{
117 lcd_remote_framebuffer[y>>3][x] &= ~(1 << (y & 7));
118}
119
120static void flippixel(int x, int y)
121{
122 lcd_remote_framebuffer[y>>3][x] ^= 1 << (y & 7);
123}
124
125static void nopixel(int x, int y)
126{
127 (void)x;
128 (void)y;
129}
130
131lcd_remote_pixelfunc_type* const lcd_remote_pixelfuncs[8] = {
132 flippixel, nopixel, setpixel, setpixel,
133 nopixel, clearpixel, nopixel, clearpixel
134};
135
136static void flipblock(fb_remote_data *address, unsigned mask, unsigned bits)
137 ICODE_ATTR;
138static void flipblock(fb_remote_data *address, unsigned mask, unsigned bits)
139{
140 *address ^= bits & mask;
141}
142
143static void bgblock(fb_remote_data *address, unsigned mask, unsigned bits)
144 ICODE_ATTR;
145static void bgblock(fb_remote_data *address, unsigned mask, unsigned bits)
146{
147 *address &= bits | ~mask;
148}
149
150static void fgblock(fb_remote_data *address, unsigned mask, unsigned bits)
151 ICODE_ATTR;
152static void fgblock(fb_remote_data *address, unsigned mask, unsigned bits)
153{
154 *address |= bits & mask;
155}
156
157static void solidblock(fb_remote_data *address, unsigned mask, unsigned bits)
158 ICODE_ATTR;
159static void solidblock(fb_remote_data *address, unsigned mask, unsigned bits)
160{
161 unsigned data = *address;
162
163 bits ^= data;
164 *address = data ^ (bits & mask);
165}
166
167static void flipinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
168 ICODE_ATTR;
169static void flipinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
170{
171 *address ^= ~bits & mask;
172}
173
174static void bginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
175 ICODE_ATTR;
176static void bginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
177{
178 *address &= ~(bits & mask);
179}
180
181static void fginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
182 ICODE_ATTR;
183static void fginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
184{
185 *address |= ~bits & mask;
186}
187
188static void solidinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
189 ICODE_ATTR;
190static void solidinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
191{
192 unsigned data = *address;
193
194 bits = ~bits ^ data;
195 *address = data ^ (bits & mask);
196}
197
198lcd_remote_blockfunc_type* const lcd_remote_blockfuncs[8] = {
199 flipblock, bgblock, fgblock, solidblock,
200 flipinvblock, bginvblock, fginvblock, solidinvblock
201};
202
203/*** drawing functions ***/
204
205/* Clear the whole display */
206void lcd_remote_clear_display(void)
207{
208 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
209
210 memset(lcd_remote_framebuffer, bits, sizeof lcd_remote_framebuffer);
211 scrolling_lines = 0;
212}
213
214/* Set a single pixel */
215void lcd_remote_drawpixel(int x, int y)
216{
217 if (((unsigned)x < LCD_REMOTE_WIDTH) && ((unsigned)y < LCD_REMOTE_HEIGHT))
218 lcd_remote_pixelfuncs[drawmode](x, y);
219}
220
221/* Draw a line */
222void lcd_remote_drawline(int x1, int y1, int x2, int y2)
223{
224 int numpixels;
225 int i;
226 int deltax, deltay;
227 int d, dinc1, dinc2;
228 int x, xinc1, xinc2;
229 int y, yinc1, yinc2;
230 lcd_remote_pixelfunc_type *pfunc = lcd_remote_pixelfuncs[drawmode];
231
232 deltax = abs(x2 - x1);
233 deltay = abs(y2 - y1);
234 xinc2 = 1;
235 yinc2 = 1;
236
237 if (deltax >= deltay)
238 {
239 numpixels = deltax;
240 d = 2 * deltay - deltax;
241 dinc1 = deltay * 2;
242 dinc2 = (deltay - deltax) * 2;
243 xinc1 = 1;
244 yinc1 = 0;
245 }
246 else
247 {
248 numpixels = deltay;
249 d = 2 * deltax - deltay;
250 dinc1 = deltax * 2;
251 dinc2 = (deltax - deltay) * 2;
252 xinc1 = 0;
253 yinc1 = 1;
254 }
255 numpixels++; /* include endpoints */
256
257 if (x1 > x2)
258 {
259 xinc1 = -xinc1;
260 xinc2 = -xinc2;
261 }
262
263 if (y1 > y2)
264 {
265 yinc1 = -yinc1;
266 yinc2 = -yinc2;
267 }
268
269 x = x1;
270 y = y1;
271
272 for (i = 0; i < numpixels; i++)
273 {
274 if (((unsigned)x < LCD_REMOTE_WIDTH) && ((unsigned)y < LCD_REMOTE_HEIGHT))
275 pfunc(x, y);
276
277 if (d < 0)
278 {
279 d += dinc1;
280 x += xinc1;
281 y += yinc1;
282 }
283 else
284 {
285 d += dinc2;
286 x += xinc2;
287 y += yinc2;
288 }
289 }
290}
291
292/* Draw a horizontal line (optimised) */
293void lcd_remote_hline(int x1, int x2, int y)
294{
295 int x;
296 fb_remote_data *dst, *dst_end;
297 unsigned mask;
298 lcd_remote_blockfunc_type *bfunc;
299
300 /* direction flip */
301 if (x2 < x1)
302 {
303 x = x1;
304 x1 = x2;
305 x2 = x;
306 }
307
308 /* nothing to draw? */
309 if (((unsigned)y >= LCD_REMOTE_HEIGHT) || (x1 >= LCD_REMOTE_WIDTH)
310 || (x2 < 0))
311 return;
312
313 /* clipping */
314 if (x1 < 0)
315 x1 = 0;
316 if (x2 >= LCD_REMOTE_WIDTH)
317 x2 = LCD_REMOTE_WIDTH-1;
318
319 bfunc = lcd_remote_blockfuncs[drawmode];
320 dst = &lcd_remote_framebuffer[y>>3][x1];
321 mask = 1 << (y & 7);
322
323 dst_end = dst + x2 - x1;
324 do
325 bfunc(dst++, mask, 0xFFu);
326 while (dst <= dst_end);
327}
328
329/* Draw a vertical line (optimised) */
330void lcd_remote_vline(int x, int y1, int y2)
331{
332 int ny;
333 fb_remote_data *dst;
334 unsigned mask, mask_bottom;
335 lcd_remote_blockfunc_type *bfunc;
336
337 /* direction flip */
338 if (y2 < y1)
339 {
340 ny = y1;
341 y1 = y2;
342 y2 = ny;
343 }
344
345 /* nothing to draw? */
346 if (((unsigned)x >= LCD_REMOTE_WIDTH) || (y1 >= LCD_REMOTE_HEIGHT)
347 || (y2 < 0))
348 return;
349
350 /* clipping */
351 if (y1 < 0)
352 y1 = 0;
353 if (y2 >= LCD_REMOTE_HEIGHT)
354 y2 = LCD_REMOTE_HEIGHT-1;
355
356 bfunc = lcd_remote_blockfuncs[drawmode];
357 dst = &lcd_remote_framebuffer[y1>>3][x];
358 ny = y2 - (y1 & ~7);
359 mask = 0xFFu << (y1 & 7);
360 mask_bottom = 0xFFu >> (~ny & 7);
361
362 for (; ny >= 8; ny -= 8)
363 {
364 bfunc(dst, mask, 0xFFu);
365 dst += LCD_REMOTE_WIDTH;
366 mask = 0xFFu;
367 }
368 mask &= mask_bottom;
369 bfunc(dst, mask, 0xFFu);
370}
371
372/* Draw a rectangular box */
373void lcd_remote_drawrect(int x, int y, int width, int height)
374{
375 if ((width <= 0) || (height <= 0))
376 return;
377
378 int x2 = x + width - 1;
379 int y2 = y + height - 1;
380
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);
385}
386
387/* Fill a rectangular area */
388void lcd_remote_fillrect(int x, int y, int width, int height)
389{
390 int ny;
391 fb_remote_data *dst, *dst_end;
392 unsigned mask, mask_bottom;
393 unsigned bits = 0;
394 lcd_remote_blockfunc_type *bfunc;
395 bool fillopt = false;
396
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))
400 return;
401
402 /* clipping */
403 if (x < 0)
404 {
405 width += x;
406 x = 0;
407 }
408 if (y < 0)
409 {
410 height += y;
411 y = 0;
412 }
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;
417
418 if (drawmode & DRMODE_INVERSEVID)
419 {
420 if (drawmode & DRMODE_BG)
421 {
422 fillopt = true;
423 }
424 }
425 else
426 {
427 if (drawmode & DRMODE_FG)
428 {
429 fillopt = true;
430 bits = 0xFFu;
431 }
432 }
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);
438
439 for (; ny >= 8; ny -= 8)
440 {
441 if (fillopt && (mask == 0xFFu))
442 memset(dst, bits, width);
443 else
444 {
445 fb_remote_data *dst_row = dst;
446
447 dst_end = dst_row + width;
448 do
449 bfunc(dst_row++, mask, 0xFFu);
450 while (dst_row < dst_end);
451 }
452
453 dst += LCD_REMOTE_WIDTH;
454 mask = 0xFFu;
455 }
456 mask &= mask_bottom;
457
458 if (fillopt && (mask == 0xFFu))
459 memset(dst, bits, width);
460 else
461 {
462 dst_end = dst + width;
463 do
464 bfunc(dst++, mask, 0xFFu);
465 while (dst < dst_end);
466 }
467}
468
469/* About Rockbox' internal bitmap format:
470 *
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
473 * at top.
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.
477 *
478 * This is the same as the internal lcd hw format. */
479
480/* Draw a partial bitmap */
481void 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)
483 ICODE_ATTR;
484void 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)
486{
487 int shift, ny;
488 fb_remote_data *dst, *dst_end;
489 unsigned mask, mask_bottom;
490 lcd_remote_blockfunc_type *bfunc;
491
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))
495 return;
496
497 /* clipping */
498 if (x < 0)
499 {
500 width += x;
501 src_x -= x;
502 x = 0;
503 }
504 if (y < 0)
505 {
506 height += y;
507 src_y -= y;
508 y = 0;
509 }
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;
514
515 src += stride * (src_y >> 3) + src_x; /* move starting point */
516 src_y &= 7;
517 y -= src_y;
518 dst = &lcd_remote_framebuffer[y>>3][x];
519 shift = y & 7;
520 ny = height - 1 + shift + src_y;
521
522 bfunc = lcd_remote_blockfuncs[drawmode];
523 mask = 0xFFu << (shift + src_y);
524 mask_bottom = 0xFFu >> (~ny & 7);
525
526 if (shift == 0)
527 {
528 bool copyopt = (drawmode == DRMODE_SOLID);
529
530 for (; ny >= 8; ny -= 8)
531 {
532 if (copyopt && (mask == 0xFFu))
533 memcpy(dst, src, width);
534 else
535 {
536 const unsigned char *src_row = src;
537 fb_remote_data *dst_row = dst;
538
539 dst_end = dst_row + width;
540 do
541 bfunc(dst_row++, mask, *src_row++);
542 while (dst_row < dst_end);
543 }
544
545 src += stride;
546 dst += LCD_REMOTE_WIDTH;
547 mask = 0xFFu;
548 }
549 mask &= mask_bottom;
550
551 if (copyopt && (mask == 0xFFu))
552 memcpy(dst, src, width);
553 else
554 {
555 dst_end = dst + width;
556 do
557 bfunc(dst++, mask, *src++);
558 while (dst < dst_end);
559 }
560 }
561 else
562 {
563 dst_end = dst + width;
564 do
565 {
566 const unsigned char *src_col = src++;
567 fb_remote_data *dst_col = dst++;
568 unsigned mask_col = mask;
569 unsigned data = 0;
570
571 for (y = ny; y >= 8; y -= 8)
572 {
573 data |= *src_col << shift;
574
575 if (mask_col & 0xFFu)
576 {
577 bfunc(dst_col, mask_col, data);
578 mask_col = 0xFFu;
579 }
580 else
581 mask_col >>= 8;
582
583 src_col += stride;
584 dst_col += LCD_REMOTE_WIDTH;
585 data >>= 8;
586 }
587 data |= *src_col << shift;
588 bfunc(dst_col, mask_col & mask_bottom, data);
589 }
590 while (dst < dst_end);
591 }
592}
593
594/* Draw a full bitmap */
595void lcd_remote_bitmap(const unsigned char *src, int x, int y, int width,
596 int height)
597{
598 lcd_remote_bitmap_part(src, 0, 0, width, x, y, width, height);
599}
600
601/* put a string at a given pixel position, skipping first ofs pixel columns */
602static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str)
603{
604 unsigned short ch;
605 unsigned short *ucs;
606 struct font* pf = font_get(curfont);
607
608 ucs = bidi_l2v(str, 1);
609
610 while ((ch = *ucs++) != 0 && x < LCD_REMOTE_WIDTH)
611 {
612 int width;
613 const unsigned char *bits;
614
615 /* get proportional width and glyph bits */
616 width = font_get_width(pf, ch);
617
618 if (ofs > width)
619 {
620 ofs -= width;
621 continue;
622 }
623
624 bits = font_get_bits(pf, ch);
625
626 lcd_remote_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
627 pf->height);
628
629 x += width - ofs;
630 ofs = 0;
631 }
632}
633
634/* put a string at a given pixel position */
635void lcd_remote_putsxy(int x, int y, const unsigned char *str)
636{
637 lcd_remote_putsxyofs(x, y, 0, str);
638}
639
640/*** line oriented text output ***/
641
642/* put a string at a given char position */
643void lcd_remote_puts(int x, int y, const unsigned char *str)
644{
645 lcd_remote_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
646}
647
648void lcd_remote_puts_style(int x, int y, const unsigned char *str, int style)
649{
650 lcd_remote_puts_style_offset(x, y, str, style, 0);
651}
652
653void lcd_remote_puts_offset(int x, int y, const unsigned char *str, int offset)
654{
655 lcd_remote_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
656}
657
658/* put a string at a given char position, style, and pixel position,
659 * skipping first offset pixel columns */
660void lcd_remote_puts_style_offset(int x, int y, const unsigned char *str,
661 int style, int offset)
662{
663 int xpos,ypos,w,h,xrect;
664 int lastmode = drawmode;
665
666 /* make sure scrolling is turned off on the line we are updating */
667 scrolling_lines &= ~(1 << y);
668
669 if(!str || !str[0])
670 return;
671
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);
681 drawmode = lastmode;
682}
683
684/*** scrolling ***/
685
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. */
688void lcd_remote_invertscroll(int x, int y)
689{
690 struct scrollinfo* s;
691
692 (void)x;
693
694 s = &scroll[y];
695 s->invert = !s->invert;
696}
697
698void lcd_remote_stop_scroll(void)
699{
700 scrolling_lines=0;
701}
702
703void lcd_remote_scroll_speed(int speed)
704{
705 scroll_ticks = scroll_tick_table[speed];
706}
707
708void lcd_remote_scroll_step(int step)
709{
710 scroll_step = step;
711}
712
713void lcd_remote_scroll_delay(int ms)
714{
715 scroll_delay = ms / (HZ / 10);
716}
717
718void lcd_remote_bidir_scroll(int percent)
719{
720 bidir_limit = percent;
721}
722
723void lcd_remote_puts_scroll(int x, int y, const unsigned char *string)
724{
725 lcd_remote_puts_scroll_style(x, y, string, STYLE_DEFAULT);
726}
727
728void lcd_remote_puts_scroll_style(int x, int y, const unsigned char *string, int style)
729{
730 lcd_remote_puts_scroll_style_offset(x, y, string, style, 0);
731}
732
733void lcd_remote_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
734{
735 lcd_remote_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
736}
737
738void lcd_remote_puts_scroll_style_offset(int x, int y, const unsigned char *string,
739 int style, int offset)
740{
741 struct scrollinfo* s;
742 int w, h;
743
744 s = &scroll[y];
745
746 s->start_tick = current_tick + scroll_delay;
747 s->invert = false;
748 if (style & STYLE_INVERT) {
749 s->invert = true;
750 lcd_remote_puts_style_offset(x,y,string,STYLE_INVERT,offset);
751 }
752 else
753 lcd_remote_puts_offset(x,y,string,offset);
754
755 lcd_remote_getstringsize(string, &w, &h);
756
757 if (LCD_REMOTE_WIDTH - x * 8 - xmargin < w) {
758 /* prepare scroll line */
759 char *end;
760
761 memset(s->line, 0, sizeof s->line);
762 strcpy(s->line, (char *)string);
763
764 /* get width */
765 s->width = lcd_remote_getstringsize((unsigned char *)s->line, &w, &h);
766
767 /* scroll bidirectional or forward only depending on the string
768 width */
769 if ( bidir_limit ) {
770 s->bidir = s->width < (LCD_REMOTE_WIDTH - xmargin) *
771 (100 + bidir_limit) / 100;
772 }
773 else
774 s->bidir = false;
775
776 if (!s->bidir) { /* add spaces if scrolling in the round */
777 strcat(s->line, " ");
778 /* get new width incl. spaces */
779 s->width = lcd_remote_getstringsize((unsigned char *)s->line, &w, &h);
780 }
781
782 end = strchr(s->line, '\0');
783 strncpy(end, (char *)string, LCD_REMOTE_WIDTH/2);
784
785 s->len = utf8length((char *)string);
786 s->offset = offset;
787 s->startx = xmargin + x * s->width / s->len;;
788 s->backward = false;
789 scrolling_lines |= (1<<y);
790 }
791 else
792 /* force a bit switch-off since it doesn't scroll */
793 scrolling_lines &= ~(1<<y);
794}
795
796static void scroll_thread(void)
797{
798 struct font* pf;
799 struct scrollinfo* s;
800 long next_tick = current_tick;
801 long delay = 0;
802 int index;
803 int xpos, ypos;
804 int lastmode;
805#ifndef SIMULATOR
806 struct event ev;
807#endif
808
809 /* initialize scroll struct array */
810 scrolling_lines = 0;
811
812 while ( 1 ) {
813
814#ifdef SIMULATOR
815 sleep(delay);
816#else
817 if (remote_initialized)
818 queue_wait_w_tmo(&remote_scroll_queue, &ev, delay);
819 else
820 queue_wait(&remote_scroll_queue, &ev);
821
822 switch (ev.id)
823 {
824 case REMOTE_INIT_LCD:
825 lcd_remote_on();
826 lcd_remote_update();
827 break;
828
829 case REMOTE_DEINIT_LCD:
830 lcd_remote_off();
831 break;
832 }
833
834 delay = next_tick - current_tick - 1;
835 if (delay >= 0)
836 continue;
837#endif
838 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
839 /* really scroll? */
840 if ( !(scrolling_lines&(1<<index)) )
841 continue;
842
843 s = &scroll[index];
844
845 /* check pause */
846 if (TIME_BEFORE(current_tick, s->start_tick))
847 continue;
848
849 if (s->backward)
850 s->offset -= scroll_step;
851 else
852 s->offset += scroll_step;
853
854 pf = font_get(curfont);
855 xpos = s->startx;
856 ypos = ymargin + index * pf->height;
857
858 if (s->bidir) { /* scroll bidirectional */
859 if (s->offset <= 0) {
860 /* at beginning of line */
861 s->offset = 0;
862 s->backward = false;
863 s->start_tick = current_tick + scroll_delay * 2;
864 }
865 if (s->offset >= s->width - (LCD_REMOTE_WIDTH - xpos)) {
866 /* at end of line */
867 s->offset = s->width - (LCD_REMOTE_WIDTH - xpos);
868 s->backward = true;
869 s->start_tick = current_tick + scroll_delay * 2;
870 }
871 }
872 else {
873 /* scroll forward the whole time */
874 if (s->offset >= s->width)
875 s->offset %= s->width;
876 }
877
878 lastmode = drawmode;
879 drawmode = s->invert ?
880 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
881 lcd_remote_putsxyofs(xpos, ypos, s->offset, s->line);
882 drawmode = lastmode;
883 lcd_remote_update_rect(xpos, ypos, LCD_REMOTE_WIDTH - xpos, pf->height);
884 }
885
886 next_tick += scroll_ticks;
887 delay = next_tick - current_tick - 1;
888 if (delay < 0)
889 {
890 next_tick = current_tick + 1;
891 delay = 0;
892 }
893 }
894}
895
896/* LCD init */
897void lcd_remote_init(void)
898{
899#ifndef SIMULATOR
900 /* Call device specific init */
901 lcd_remote_init_device();
902 /* private queue */
903 queue_init(&remote_scroll_queue, false);
904#endif
905 create_thread(scroll_thread, scroll_stack,
906 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
907}