summaryrefslogtreecommitdiff
path: root/firmware/drivers/lcd-charcell.c
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2007-03-26 07:52:13 +0000
committerJens Arnold <amiconn@rockbox.org>2007-03-26 07:52:13 +0000
commitad4e3d665734b14a28f1ba5fa874663772dab3e7 (patch)
treebff44652495f1319a4d11ed63b3d4e90cb11197f /firmware/drivers/lcd-charcell.c
parent165f62d0cd771660e4b8d2ba7475e14d0d6f2e9f (diff)
downloadrockbox-ad4e3d665734b14a28f1ba5fa874663772dab3e7.tar.gz
rockbox-ad4e3d665734b14a28f1ba5fa874663772dab3e7.zip
First step of charcell LCD code rework: * Make it fully unicode aware so that adding non-ISO8859-1 scripts becomes possible (limited by the LCD capabilities of course). * Make the API more similar to the bitmap LCD code's API. * Moved hardware dependent parts to target tree. * Simplified code. * Jumpscroll temporarily non-functional.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12916 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/lcd-charcell.c')
-rw-r--r--firmware/drivers/lcd-charcell.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/firmware/drivers/lcd-charcell.c b/firmware/drivers/lcd-charcell.c
new file mode 100644
index 0000000000..ce0eca94ca
--- /dev/null
+++ b/firmware/drivers/lcd-charcell.c
@@ -0,0 +1,612 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Jens Arnold
11 * Based on the work of Alan Korr, Kjell Ericson and others
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20#include "config.h"
21#include "hwcompat.h"
22
23#include "lcd.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 "lcd-charcell.h"
32#include "rbunicode.h"
33
34/** definitions **/
35
36#define SCROLLABLE_LINES LCD_HEIGHT
37#define VARIABLE_XCHARS 16 /* number of software user-definable characters */
38
39#define NO_PATTERN (-1)
40
41#define SCROLL_MODE_OFF 0
42#define SCROLL_MODE_RUN 1
43
44/* track usage of user-definable characters */
45struct pattern_info {
46 short count;
47 unsigned short xchar;
48};
49
50struct cursor_info {
51 unsigned char hw_char;
52 bool enabled;
53 bool visible;
54 int x;
55 int y;
56 int divider;
57 int downcount;
58};
59
60static int find_xchar(unsigned long ucs);
61
62/** globals **/
63
64/* The "frame"buffer */
65static unsigned char lcd_buffer[LCD_WIDTH][LCD_HEIGHT];
66#ifdef SIMULATOR
67unsigned char hardware_buffer_lcd[LCD_WIDTH][LCD_HEIGHT];
68#endif
69
70static int xmargin = 0;
71static int ymargin = 0;
72
73static unsigned char xfont_variable[VARIABLE_XCHARS][(HW_PATTERN_SIZE+3)&~3];
74 /* round up pattern size to a multiple of 4 bytes for faster access */
75static bool xfont_variable_locked[VARIABLE_XCHARS];
76static struct pattern_info hw_pattern[MAX_HW_PATTERNS];
77static struct cursor_info cursor;
78
79/* scrolling */
80static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
81static void scroll_thread(void);
82static char scroll_stack[DEFAULT_STACK_SIZE];
83static const char scroll_name[] = "scroll";
84static int scroll_ticks = 12; /* # of ticks between updates */
85static int scroll_delay = HZ/2; /* delay before starting scroll */
86static int bidir_limit = 50; /* percent */
87static int jump_scroll_delay = HZ/4; /* delay between jump scroll jumps */
88static int jump_scroll = 0; /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
89static struct scrollinfo scroll[SCROLLABLE_LINES];
90
91static const char scroll_tick_table[16] = {
92 /* Hz values:
93 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
94 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
95};
96
97/* LCD init */
98void lcd_init (void)
99{
100 lcd_init_device();
101 lcd_charset_init();
102 memset(hw_pattern, 0, sizeof(hw_pattern));
103 memset(lcd_buffer, xchar_info[find_xchar(' ')].hw_char, sizeof(lcd_buffer));
104
105 create_thread(scroll_thread, scroll_stack,
106 sizeof(scroll_stack), scroll_name
107 IF_PRIO(, PRIORITY_USER_INTERFACE)
108 IF_COP(, CPU, false));
109}
110
111/** parameter handling **/
112
113void lcd_setmargins(int x, int y)
114{
115 xmargin = x;
116 ymargin = y;
117}
118
119int lcd_getxmargin(void)
120{
121 return xmargin;
122}
123
124int lcd_getymargin(void)
125{
126 return ymargin;
127}
128
129int lcd_getstringsize(const unsigned char *str, int *w, int *h)
130{
131 int width = utf8length(str);
132
133 if (w)
134 *w = width;
135 if (h)
136 *h = 1;
137
138 return width;
139}
140
141/** low-level functions **/
142
143static int find_xchar(unsigned long ucs)
144{
145 int low = 0;
146 int high = xchar_info_size - 1;
147
148 do
149 {
150 int probe = (low + high) >> 1;
151
152 if (xchar_info[probe].ucs < ucs)
153 low = probe + 1;
154 else if (xchar_info[probe].ucs > ucs)
155 high = probe - 1;
156 else
157 return probe;
158 }
159 while (low <= high);
160
161 /* Not found: return index of no-char symbol (last symbol, hardcoded). */
162 return xchar_info_size - 1;
163}
164
165static int xchar_to_pat(int xchar)
166{
167 int i;
168
169 for (i = 0; i < hw_pattern_count; i++)
170 if (hw_pattern[i].xchar == xchar)
171 return i;
172
173 return NO_PATTERN;
174}
175
176static const unsigned char *xchar_to_glyph(int xchar)
177{
178 unsigned index = xchar_info[xchar].glyph;
179
180 if (index & 0x8000)
181 return xfont_variable[index & 0x7fff];
182 else
183 return xfont_fixed[index];
184}
185
186static void lcd_free_pat(int xchar)
187{
188 int x, y;
189 unsigned char substitute;
190 int pat = xchar_to_pat(xchar);
191
192 if (pat != NO_PATTERN)
193 {
194 substitute = xchar_info[xchar].hw_char;
195
196 for (x = 0; x < LCD_WIDTH; x++)
197 {
198 for (y = 0; y < LCD_HEIGHT; y++)
199 {
200 if (pat == lcd_buffer[x][y])
201 {
202 lcd_buffer[x][y] = substitute;
203#ifdef SIMULATOR
204 hardware_buffer_lcd[x][y] = substitute;
205#else
206 lcd_put_hw_char(x, y, substitute);
207#endif
208 }
209 }
210 }
211 if (cursor.enabled && pat == cursor.hw_char)
212 cursor.hw_char = substitute;
213
214 hw_pattern[pat].count = 0;
215#ifdef SIMULATOR
216 lcd_update();
217#endif
218 }
219}
220
221static int lcd_get_free_pat(int xchar)
222{
223 static int last_used_pat = 0;
224
225 int pat = last_used_pat; /* start from last used pattern */
226 int least_pat = pat; /* pattern with least priority */
227 int least_priority = xchar_info[hw_pattern[pat].xchar].priority;
228 int i;
229
230 for (i = 0; i < hw_pattern_count; i++)
231 {
232 if (++pat >= hw_pattern_count) /* Keep 'pat' within limits */
233 pat = 0;
234
235 if (hw_pattern[pat].count == 0)
236 {
237 hw_pattern[pat].xchar = xchar;
238 last_used_pat = pat;
239 return pat;
240 }
241 if (xchar_info[hw_pattern[pat].xchar].priority < least_priority)
242 {
243 least_priority = xchar_info[hw_pattern[pat].xchar].priority;
244 least_pat = pat;
245 }
246 }
247 if (xchar_info[xchar].priority > least_priority) /* prioritized char */
248 {
249 lcd_free_pat(hw_pattern[least_pat].xchar);
250 hw_pattern[least_pat].xchar = xchar;
251 last_used_pat = least_pat;
252 return least_pat;
253 }
254 return NO_PATTERN;
255}
256
257static int map_xchar(int xchar)
258{
259 int pat;
260
261 if (xchar_info[xchar].priority > 0) /* soft char */
262 {
263 pat = xchar_to_pat(xchar);
264
265 if (pat == NO_PATTERN) /* not yet mapped */
266 {
267 pat = lcd_get_free_pat(xchar); /* try to map */
268 if (pat == NO_PATTERN) /* failed: just use substitute */
269 return xchar_info[xchar].hw_char;
270 else /* define pattern */
271 lcd_define_hw_pattern(pat, xchar_to_glyph(xchar));
272 }
273 hw_pattern[pat].count++; /* increase reference count */
274 return pat;
275 }
276 else /* hardware char */
277 return xchar_info[xchar].hw_char;
278}
279
280static void lcd_putxchar(int x, int y, int xchar)
281{
282 int lcd_char = lcd_buffer[x][y];
283
284 if (lcd_char < hw_pattern_count) /* old char was soft */
285 hw_pattern[lcd_char].count--; /* decrease old reference count */
286
287 lcd_buffer[x][y] = lcd_char = map_xchar(xchar);
288#ifdef SIMULATOR
289 hardware_buffer_lcd[x][y] = lcd_char;
290 lcd_update();
291#else
292 lcd_put_hw_char(x, y, lcd_char);
293#endif
294}
295
296/** user-definable pattern handling **/
297
298unsigned long lcd_get_locked_pattern(void)
299{
300 int i = 0;
301
302 for (i = 0; i < VARIABLE_XCHARS; i++)
303 {
304 if (!xfont_variable_locked[i])
305 {
306 xfont_variable_locked[i] = true;
307 return 0xe000 + i; /* hard-coded */
308 }
309 }
310 return 0;
311}
312
313void lcd_unlock_pattern(unsigned long ucs)
314{
315 int xchar = find_xchar(ucs);
316 int index = xchar_info[xchar].glyph;
317
318 if (index & 0x8000) /* variable extended char */
319 {
320 lcd_free_pat(xchar);
321 xfont_variable_locked[index & 0x7fff] = false;
322 }
323}
324
325void lcd_define_pattern(unsigned long ucs, const char *pattern)
326{
327 int xchar = find_xchar(ucs);
328 int index = xchar_info[xchar].glyph;
329 int pat;
330
331 if (index & 0x8000) /* variable extended char */
332 {
333 memcpy(xfont_variable[index & 0x7fff], pattern, HW_PATTERN_SIZE);
334 pat = xchar_to_pat(xchar);
335 if (pat != NO_PATTERN)
336 lcd_define_hw_pattern(pat, pattern);
337 }
338}
339
340/** output functions **/
341
342/* Clear the whole display */
343void lcd_clear_display(void)
344{
345 int x, y;
346 int xchar = find_xchar(' ');
347
348 lcd_stop_scroll();
349 lcd_remove_cursor();
350
351 for (x = 0; x < LCD_WIDTH; x++)
352 for (y = 0; y < LCD_HEIGHT; y++)
353 lcd_putxchar(x, y, xchar);
354}
355
356/* Put an unicode character at the given position */
357void lcd_putc(int x, int y, unsigned long ucs)
358{
359 if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT)
360 return;
361
362 lcd_putxchar(x, y, find_xchar(ucs));
363}
364
365/* Show cursor (alternating with existing character) at the given position */
366void lcd_put_cursor(int x, int y, unsigned long cursor_ucs)
367{
368 if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT
369 || cursor.enabled)
370 return;
371
372 cursor.enabled = true;
373 cursor.visible = false;
374 cursor.hw_char = map_xchar(find_xchar(cursor_ucs));
375 cursor.x = x;
376 cursor.y = y;
377 cursor.downcount = 0;
378 cursor.divider = 4;
379}
380
381/* Remove the cursor */
382void lcd_remove_cursor(void)
383{
384 if (cursor.enabled)
385 {
386 if (cursor.hw_char < hw_pattern_count) /* soft char, unmap */
387 hw_pattern[cursor.hw_char].count--;
388
389 cursor.enabled = false;
390#ifdef SIMULATOR
391 hardware_buffer_lcd[cursor.x][cursor.y] = lcd_buffer[cursor.x][cursor.y];
392#else
393 lcd_put_hw_char(cursor.x, cursor.y, lcd_buffer[cursor.x][cursor.y]);
394#endif
395 }
396}
397
398/* Put a string at a given position, skipping first ofs chars */
399static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
400{
401 unsigned short ucs;
402 const unsigned char *utf8 = str;
403
404 while (*utf8 && x < LCD_WIDTH)
405 {
406 utf8 = utf8decode(utf8, &ucs);
407
408 if (ofs > 0)
409 {
410 ofs--;
411 continue;
412 }
413 lcd_putc(x++, y, ucs);
414 }
415 return x;
416}
417
418/* Put a string at a given position */
419void lcd_putsxy(int x, int y, const unsigned char *str)
420{
421 lcd_putsxyofs(x, y, 0, str);
422}
423
424/*** Line oriented text output ***/
425
426/* Put a string at a given char position */
427void lcd_puts(int x, int y, const unsigned char *str)
428{
429 lcd_puts_offset(x, y, str, 0);
430}
431
432/* Put a string at a given char position, skipping first offset chars */
433void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
434{
435 /* make sure scrolling is turned off on the line we are updating */
436 scrolling_lines &= ~(1 << y);
437
438 x += xmargin;
439 y += ymargin;
440
441 x = lcd_putsxyofs(x, y, offset, str);
442 while (x < LCD_WIDTH)
443 lcd_putc(x++, y, ' ');
444}
445
446/** scrolling **/
447
448void lcd_stop_scroll(void)
449{
450 scrolling_lines=0;
451}
452
453void lcd_scroll_speed(int speed)
454{
455 scroll_ticks = scroll_tick_table[speed];
456}
457
458void lcd_scroll_delay(int ms)
459{
460 scroll_delay = ms / (HZ / 10);
461}
462
463void lcd_bidir_scroll(int percent)
464{
465 bidir_limit = percent;
466}
467
468void lcd_jump_scroll(int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
469{
470 jump_scroll = mode;
471}
472
473void lcd_jump_scroll_delay(int ms)
474{
475 jump_scroll_delay = ms / (HZ / 10);
476}
477
478void lcd_puts_scroll(int x, int y, const unsigned char *string)
479{
480 lcd_puts_scroll_offset(x, y, string, 0);
481}
482
483void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
484 int offset)
485{
486 struct scrollinfo* s;
487 int len;
488
489 s = &scroll[y];
490
491 s->start_tick = current_tick + scroll_delay;
492
493 lcd_puts_offset(x, y, string, offset);
494 len = utf8length(string);
495
496 if (LCD_WIDTH - xmargin < len)
497 {
498 /* prepare scroll line */
499 char *end;
500
501 memset(s->line, 0, sizeof s->line);
502 strcpy(s->line, string);
503
504 /* get width */
505 s->len = utf8length(s->line);
506
507 /* scroll bidirectional or forward only depending on the string width */
508 if (bidir_limit)
509 {
510 s->bidir = s->len < (LCD_WIDTH - xmargin) * (100 + bidir_limit) / 100;
511 }
512 else
513 s->bidir = false;
514
515 if (!s->bidir) /* add spaces if scrolling in the round */
516 {
517 strcat(s->line, " ");
518 /* get new width incl. spaces */
519 s->len += SCROLL_SPACING;
520 }
521
522 end = strchr(s->line, '\0');
523 strncpy(end, string, LCD_WIDTH);
524
525 s->offset = offset;
526 s->startx = xmargin + x;
527 s->backward = false;
528 scrolling_lines |= (1<<y);
529 }
530 else
531 /* force a bit switch-off since it doesn't scroll */
532 scrolling_lines &= ~(1<<y);
533}
534
535static void scroll_thread(void)
536{
537 struct scrollinfo* s;
538 int index;
539 int xpos, ypos;
540
541 /* initialize scroll struct array */
542 scrolling_lines = 0;
543
544 while (1)
545 {
546 for (index = 0; index < SCROLLABLE_LINES; index++)
547 {
548 /* really scroll? */
549 if (!(scrolling_lines&(1<<index)))
550 continue;
551
552 s = &scroll[index];
553
554 /* check pause */
555 if (TIME_BEFORE(current_tick, s->start_tick))
556 continue;
557
558 if (s->backward)
559 s->offset--;
560 else
561 s->offset++;
562
563 xpos = s->startx;
564 ypos = ymargin + index;
565
566 if (s->bidir) /* scroll bidirectional */
567 {
568 if (s->offset <= 0)
569 {
570 /* at beginning of line */
571 s->offset = 0;
572 s->backward = false;
573 s->start_tick = current_tick + scroll_delay * 2;
574 }
575 if (s->offset >= s->len - (LCD_WIDTH - xpos))
576 {
577 /* at end of line */
578 s->offset = s->len - (LCD_WIDTH - xpos);
579 s->backward = true;
580 s->start_tick = current_tick + scroll_delay * 2;
581 }
582 }
583 else /* scroll forward the whole time */
584 {
585 if (s->offset >= s->len)
586 s->offset -= s->len;
587 }
588 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
589 }
590 if (cursor.enabled)
591 {
592 if (--cursor.downcount < 0)
593 {
594 int lcd_char;
595
596 cursor.downcount = cursor.divider;
597 cursor.visible = !cursor.visible;
598 lcd_char = cursor.visible ? cursor.hw_char
599 : lcd_buffer[cursor.x][cursor.y];
600#ifdef SIMULATOR
601 hardware_buffer_lcd[cursor.x][cursor.y] = lcd_char;
602#else
603 lcd_put_hw_char(cursor.x, cursor.y, lcd_char);
604#endif
605 }
606 }
607#ifdef SIMULATOR
608 lcd_update();
609#endif
610 sleep(scroll_ticks);
611 }
612}