diff options
Diffstat (limited to 'firmware/drivers/lcd-remote-1bit-v.c')
-rw-r--r-- | firmware/drivers/lcd-remote-1bit-v.c | 907 |
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 | |||
39 | fb_remote_data lcd_remote_framebuffer[LCD_REMOTE_FBHEIGHT][LCD_REMOTE_FBWIDTH] | ||
40 | IBSS_ATTR; | ||
41 | |||
42 | static int drawmode = DRMODE_SOLID; | ||
43 | static int xmargin = 0; | ||
44 | static int ymargin = 0; | ||
45 | static int curfont = FONT_SYSFIXED; | ||
46 | |||
47 | /* scrolling */ | ||
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]; | ||
57 | |||
58 | static 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 | ||
66 | struct event_queue remote_scroll_queue; | ||
67 | #endif | ||
68 | |||
69 | /*** parameter handling ***/ | ||
70 | |||
71 | void lcd_remote_set_drawmode(int mode) | ||
72 | { | ||
73 | drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID); | ||
74 | } | ||
75 | |||
76 | int lcd_remote_get_drawmode(void) | ||
77 | { | ||
78 | return drawmode; | ||
79 | } | ||
80 | |||
81 | void lcd_remote_setmargins(int x, int y) | ||
82 | { | ||
83 | xmargin = x; | ||
84 | ymargin = y; | ||
85 | } | ||
86 | |||
87 | int lcd_remote_getxmargin(void) | ||
88 | { | ||
89 | return xmargin; | ||
90 | } | ||
91 | |||
92 | int lcd_remote_getymargin(void) | ||
93 | { | ||
94 | return ymargin; | ||
95 | } | ||
96 | |||
97 | |||
98 | void lcd_remote_setfont(int newfont) | ||
99 | { | ||
100 | curfont = newfont; | ||
101 | } | ||
102 | |||
103 | int 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 | |||
110 | static void setpixel(int x, int y) | ||
111 | { | ||
112 | lcd_remote_framebuffer[y>>3][x] |= 1 << (y & 7); | ||
113 | } | ||
114 | |||
115 | static void clearpixel(int x, int y) | ||
116 | { | ||
117 | lcd_remote_framebuffer[y>>3][x] &= ~(1 << (y & 7)); | ||
118 | } | ||
119 | |||
120 | static void flippixel(int x, int y) | ||
121 | { | ||
122 | lcd_remote_framebuffer[y>>3][x] ^= 1 << (y & 7); | ||
123 | } | ||
124 | |||
125 | static void nopixel(int x, int y) | ||
126 | { | ||
127 | (void)x; | ||
128 | (void)y; | ||
129 | } | ||
130 | |||
131 | lcd_remote_pixelfunc_type* const lcd_remote_pixelfuncs[8] = { | ||
132 | flippixel, nopixel, setpixel, setpixel, | ||
133 | nopixel, clearpixel, nopixel, clearpixel | ||
134 | }; | ||
135 | |||
136 | static void flipblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
137 | ICODE_ATTR; | ||
138 | static void flipblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
139 | { | ||
140 | *address ^= bits & mask; | ||
141 | } | ||
142 | |||
143 | static void bgblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
144 | ICODE_ATTR; | ||
145 | static void bgblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
146 | { | ||
147 | *address &= bits | ~mask; | ||
148 | } | ||
149 | |||
150 | static void fgblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
151 | ICODE_ATTR; | ||
152 | static void fgblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
153 | { | ||
154 | *address |= bits & mask; | ||
155 | } | ||
156 | |||
157 | static void solidblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
158 | ICODE_ATTR; | ||
159 | static 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 | |||
167 | static void flipinvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
168 | ICODE_ATTR; | ||
169 | static void flipinvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
170 | { | ||
171 | *address ^= ~bits & mask; | ||
172 | } | ||
173 | |||
174 | static void bginvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
175 | ICODE_ATTR; | ||
176 | static void bginvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
177 | { | ||
178 | *address &= ~(bits & mask); | ||
179 | } | ||
180 | |||
181 | static void fginvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
182 | ICODE_ATTR; | ||
183 | static void fginvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
184 | { | ||
185 | *address |= ~bits & mask; | ||
186 | } | ||
187 | |||
188 | static void solidinvblock(fb_remote_data *address, unsigned mask, unsigned bits) | ||
189 | ICODE_ATTR; | ||
190 | static 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 | |||
198 | lcd_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 */ | ||
206 | void 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 */ | ||
215 | void 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 */ | ||
222 | void 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) */ | ||
293 | void 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) */ | ||
330 | void 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 */ | ||
373 | void 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 */ | ||
388 | void 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 */ | ||
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) | ||
483 | ICODE_ATTR; | ||
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) | ||
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 */ | ||
595 | void 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 */ | ||
602 | static 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 */ | ||
635 | void 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 */ | ||
643 | void 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 | |||
648 | void 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 | |||
653 | void 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 */ | ||
660 | void 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. */ | ||
688 | void 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 | |||
698 | void lcd_remote_stop_scroll(void) | ||
699 | { | ||
700 | scrolling_lines=0; | ||
701 | } | ||
702 | |||
703 | void lcd_remote_scroll_speed(int speed) | ||
704 | { | ||
705 | scroll_ticks = scroll_tick_table[speed]; | ||
706 | } | ||
707 | |||
708 | void lcd_remote_scroll_step(int step) | ||
709 | { | ||
710 | scroll_step = step; | ||
711 | } | ||
712 | |||
713 | void lcd_remote_scroll_delay(int ms) | ||
714 | { | ||
715 | scroll_delay = ms / (HZ / 10); | ||
716 | } | ||
717 | |||
718 | void lcd_remote_bidir_scroll(int percent) | ||
719 | { | ||
720 | bidir_limit = percent; | ||
721 | } | ||
722 | |||
723 | void 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 | |||
728 | void 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 | |||
733 | void 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 | |||
738 | void 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 | |||
796 | static 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 */ | ||
897 | void 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 | } | ||