Wheel acceleration for e200. A general acceleration interface intended for use on...
[Rockbox.git] / firmware / drivers / lcd-charcell.c
blobefc9aa5b1f5b332278829bea3ce4bbb31c25b9fb
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Jens Arnold
11 * Based on the work of Alan Korr, Kjell Ericson and others
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.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include "config.h"
21 #include "hwcompat.h"
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 "system.h"
30 #include "lcd-charcell.h"
31 #include "rbunicode.h"
33 /** definitions **/
35 #define SCROLLABLE_LINES LCD_HEIGHT
36 #define VARIABLE_XCHARS 16 /* number of software user-definable characters */
37 /* There must be mappings for this many characters in the 0xe000 unicode range
38 * in lcd-charset-<target>.c */
40 #define NO_PATTERN (-1)
42 static int find_xchar(unsigned long ucs);
44 /** globals **/
46 unsigned char lcd_charbuffer[LCD_HEIGHT][LCD_WIDTH]; /* The "frame"buffer */
47 static unsigned char lcd_substbuffer[LCD_HEIGHT][LCD_WIDTH];
48 struct pattern_info lcd_patterns[MAX_HW_PATTERNS];
49 struct cursor_info lcd_cursor;
51 static unsigned char xfont_variable[VARIABLE_XCHARS][HW_PATTERN_SIZE];
52 static bool xfont_variable_locked[VARIABLE_XCHARS];
53 static int xspace; /* stores xhcar id of ' ' - often needed */
55 static int xmargin = 0;
56 static int ymargin = 0;
58 /* scrolling */
59 static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
60 static void scroll_thread(void);
61 static char scroll_stack[DEFAULT_STACK_SIZE];
62 static const char scroll_name[] = "scroll";
63 static int scroll_ticks = 12; /* # of ticks between updates */
64 static int scroll_delay = HZ/2; /* delay before starting scroll */
65 static int bidir_limit = 50; /* percent */
66 static int jump_scroll_delay = HZ/4; /* delay between jump scroll jumps */
67 static int jump_scroll = 0; /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
68 static struct scrollinfo scroll[SCROLLABLE_LINES];
70 static const char scroll_tick_table[16] = {
71 /* Hz values:
72 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
73 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
76 /* LCD init */
77 void lcd_init (void)
79 lcd_init_device();
80 lcd_charset_init();
81 memset(lcd_patterns, 0, sizeof(lcd_patterns));
82 xspace = find_xchar(' ');
83 memset(lcd_charbuffer, xchar_info[xspace].hw_char, sizeof(lcd_charbuffer));
85 create_thread(scroll_thread, scroll_stack,
86 sizeof(scroll_stack), scroll_name
87 IF_PRIO(, PRIORITY_USER_INTERFACE)
88 IF_COP(, CPU, false));
91 /** parameter handling **/
93 void lcd_setmargins(int x, int y)
95 xmargin = x;
96 ymargin = y;
99 int lcd_getxmargin(void)
101 return xmargin;
104 int lcd_getymargin(void)
106 return ymargin;
109 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
111 int width = utf8length(str);
113 if (w)
114 *w = width;
115 if (h)
116 *h = 1;
118 return width;
121 /** low-level functions **/
123 static int find_xchar(unsigned long ucs)
125 int low = 0;
126 int high = xchar_info_size - 1;
130 int probe = (low + high) >> 1;
132 if (xchar_info[probe].ucs < ucs)
133 low = probe + 1;
134 else if (xchar_info[probe].ucs > ucs)
135 high = probe - 1;
136 else
137 return probe;
139 while (low <= high);
141 /* Not found: return index of no-char symbol (last symbol, hardcoded). */
142 return xchar_info_size - 1;
145 static int glyph_to_pat(unsigned glyph)
147 int i;
149 for (i = 0; i < lcd_pattern_count; i++)
150 if (lcd_patterns[i].glyph == glyph)
151 return i;
153 return NO_PATTERN;
156 static void lcd_free_pat(int pat)
158 int x, y;
160 if (pat != NO_PATTERN)
162 for (x = 0; x < LCD_WIDTH; x++)
163 for (y = 0; y < LCD_HEIGHT; y++)
164 if (pat == lcd_charbuffer[y][x])
165 lcd_charbuffer[y][x] = lcd_substbuffer[y][x];
167 if (lcd_cursor.enabled && pat == lcd_cursor.hw_char)
168 lcd_cursor.hw_char = lcd_cursor.subst_char;
170 lcd_patterns[pat].count = 0;
174 static int lcd_get_free_pat(int xchar)
176 static int last_used_pat = 0;
178 int pat = last_used_pat; /* start from last used pattern */
179 int least_pat = pat; /* pattern with least priority */
180 int least_priority = lcd_patterns[pat].priority;
181 int i;
183 for (i = 0; i < lcd_pattern_count; i++)
185 if (++pat >= lcd_pattern_count) /* Keep 'pat' within limits */
186 pat = 0;
188 if (lcd_patterns[pat].count == 0)
190 last_used_pat = pat;
191 return pat;
193 if (lcd_patterns[pat].priority < least_priority)
195 least_priority = lcd_patterns[pat].priority;
196 least_pat = pat;
199 if (xchar_info[xchar].priority > least_priority) /* prioritized char */
201 lcd_free_pat(least_pat);
202 last_used_pat = least_pat;
203 return least_pat;
205 return NO_PATTERN;
208 static const unsigned char *glyph_to_pattern(unsigned glyph)
210 if (glyph & 0x8000)
211 return xfont_variable[glyph & 0x7fff];
212 else
213 return xfont_fixed[glyph];
216 static int map_xchar(int xchar, unsigned char *substitute)
218 int pat;
219 unsigned glyph;
221 if (xchar_info[xchar].priority > 0) /* soft char */
223 glyph = xchar_info[xchar].glyph;
224 pat = glyph_to_pat(glyph);
226 if (pat == NO_PATTERN) /* not yet mapped */
228 pat = lcd_get_free_pat(xchar); /* try to map */
230 if (pat == NO_PATTERN) /* failed: just use substitute */
231 return xchar_info[xchar].hw_char;
232 else
233 { /* define pattern */
234 lcd_patterns[pat].priority = xchar_info[xchar].priority;
235 lcd_patterns[pat].glyph = glyph;
236 memcpy(lcd_patterns[pat].pattern, glyph_to_pattern(glyph),
237 HW_PATTERN_SIZE);
240 lcd_patterns[pat].count++; /* increase reference count */
241 *substitute = xchar_info[xchar].hw_char;
242 return pat;
244 else /* hardware char */
245 return xchar_info[xchar].hw_char;
248 static void lcd_putxchar(int x, int y, int xchar)
250 int lcd_char = lcd_charbuffer[y][x];
252 if (lcd_char < lcd_pattern_count) /* old char was soft */
253 lcd_patterns[lcd_char].count--; /* decrease old reference count */
255 lcd_charbuffer[y][x] = map_xchar(xchar, &lcd_substbuffer[y][x]);
258 /** user-definable pattern handling **/
260 unsigned long lcd_get_locked_pattern(void)
262 int i = 0;
264 for (i = 0; i < VARIABLE_XCHARS; i++)
266 if (!xfont_variable_locked[i])
268 xfont_variable_locked[i] = true;
269 return 0xe000 + i; /* hard-coded */
272 return 0;
275 void lcd_unlock_pattern(unsigned long ucs)
277 int xchar = find_xchar(ucs);
278 unsigned glyph = xchar_info[xchar].glyph;
280 if (glyph & 0x8000) /* variable extended char */
282 lcd_free_pat(glyph_to_pat(glyph));
283 xfont_variable_locked[glyph & 0x7fff] = false;
287 void lcd_define_pattern(unsigned long ucs, const char *pattern)
289 int xchar = find_xchar(ucs);
290 unsigned glyph = xchar_info[xchar].glyph;
291 int pat;
293 if (glyph & 0x8000) /* variable extended char */
295 memcpy(xfont_variable[glyph & 0x7fff], pattern, HW_PATTERN_SIZE);
296 pat = glyph_to_pat(glyph);
297 if (pat != NO_PATTERN)
298 memcpy(lcd_patterns[pat].pattern, pattern, HW_PATTERN_SIZE);
302 /** output functions **/
304 /* Clear the whole display */
305 void lcd_clear_display(void)
307 int x, y;
309 lcd_stop_scroll();
310 lcd_remove_cursor();
312 for (x = 0; x < LCD_WIDTH; x++)
313 for (y = 0; y < LCD_HEIGHT; y++)
314 lcd_putxchar(x, y, xspace);
317 /* Put an unicode character at the given position */
318 void lcd_putc(int x, int y, unsigned long ucs)
320 if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT)
321 return;
323 lcd_putxchar(x, y, find_xchar(ucs));
326 /* Show cursor (alternating with existing character) at the given position */
327 void lcd_put_cursor(int x, int y, unsigned long cursor_ucs)
329 if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT
330 || lcd_cursor.enabled)
331 return;
333 lcd_cursor.enabled = true;
334 lcd_cursor.visible = false;
335 lcd_cursor.hw_char = map_xchar(find_xchar(cursor_ucs), &lcd_cursor.subst_char);
336 lcd_cursor.x = x;
337 lcd_cursor.y = y;
338 lcd_cursor.downcount = 0;
339 lcd_cursor.divider = MAX((HZ/2) / scroll_ticks, 1);
342 /* Remove the cursor */
343 void lcd_remove_cursor(void)
345 if (lcd_cursor.enabled)
347 if (lcd_cursor.hw_char < lcd_pattern_count) /* soft char, unmap */
348 lcd_patterns[lcd_cursor.hw_char].count--;
350 lcd_cursor.enabled = lcd_cursor.visible = false;
354 /* Put a string at a given position, skipping first ofs chars */
355 static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
357 unsigned short ucs;
358 const unsigned char *utf8 = str;
360 while (*utf8 && x < LCD_WIDTH)
362 utf8 = utf8decode(utf8, &ucs);
364 if (ofs > 0)
366 ofs--;
367 continue;
369 lcd_putxchar(x++, y, find_xchar(ucs));
371 return x;
374 /* Put a string at a given position */
375 void lcd_putsxy(int x, int y, const unsigned char *str)
377 if ((unsigned)y >= LCD_HEIGHT)
378 return;
380 lcd_putsxyofs(x, y, 0, str);
383 /*** Line oriented text output ***/
385 /* Put a string at a given char position */
386 void lcd_puts(int x, int y, const unsigned char *str)
388 lcd_puts_offset(x, y, str, 0);
391 /* Put a string at a given char position, skipping first offset chars */
392 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
394 x += xmargin;
395 y += ymargin;
397 if ((unsigned)y >= LCD_HEIGHT)
398 return;
400 /* make sure scrolling is turned off on the line we are updating */
401 scrolling_lines &= ~(1 << y);
403 x = lcd_putsxyofs(x, y, offset, str);
404 while (x < LCD_WIDTH)
405 lcd_putxchar(x++, y, xspace);
408 /** scrolling **/
410 void lcd_stop_scroll(void)
412 scrolling_lines=0;
415 void lcd_scroll_speed(int speed)
417 scroll_ticks = scroll_tick_table[speed];
420 void lcd_scroll_delay(int ms)
422 scroll_delay = ms / (HZ / 10);
425 void lcd_bidir_scroll(int percent)
427 bidir_limit = percent;
430 void lcd_jump_scroll(int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
432 jump_scroll = mode;
435 void lcd_jump_scroll_delay(int ms)
437 jump_scroll_delay = ms / (HZ / 10);
440 void lcd_puts_scroll(int x, int y, const unsigned char *string)
442 lcd_puts_scroll_offset(x, y, string, 0);
445 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
446 int offset)
448 struct scrollinfo* s;
449 int len;
451 if(y>=SCROLLABLE_LINES) return;
453 s = &scroll[y];
455 s->start_tick = current_tick + scroll_delay;
457 lcd_puts_offset(x, y, string, offset);
458 len = utf8length(string);
460 if (LCD_WIDTH - x - xmargin < len)
462 /* prepare scroll line */
463 char *end;
465 memset(s->line, 0, sizeof s->line);
466 strcpy(s->line, string);
468 /* get width */
469 s->len = utf8length(s->line);
471 /* scroll bidirectional or forward only depending on the string width */
472 if (bidir_limit)
474 s->bidir = s->len < (LCD_WIDTH - xmargin) * (100 + bidir_limit) / 100;
476 else
477 s->bidir = false;
479 if (!s->bidir) /* add spaces if scrolling in the round */
481 strcat(s->line, " ");
482 /* get new width incl. spaces */
483 s->len += SCROLL_SPACING;
486 end = strchr(s->line, '\0');
487 strncpy(end, string, utf8seek(s->line, LCD_WIDTH));
489 s->offset = offset;
490 s->startx = xmargin + x;
491 s->backward = false;
492 scrolling_lines |= (1<<y);
494 else
495 /* force a bit switch-off since it doesn't scroll */
496 scrolling_lines &= ~(1<<y);
499 static void scroll_thread(void)
501 struct scrollinfo* s;
502 int index;
503 int xpos, ypos;
504 bool update;
506 /* initialize scroll struct array */
507 scrolling_lines = 0;
509 while (1)
511 update = false;
512 for (index = 0; index < SCROLLABLE_LINES; index++)
514 /* really scroll? */
515 if (!(scrolling_lines&(1<<index)))
516 continue;
518 s = &scroll[index];
520 /* check pause */
521 if (TIME_BEFORE(current_tick, s->start_tick))
522 continue;
524 if (s->backward)
525 s->offset--;
526 else
527 s->offset++;
529 xpos = s->startx;
530 ypos = ymargin + index;
532 if (s->bidir) /* scroll bidirectional */
534 if (s->offset <= 0)
536 /* at beginning of line */
537 s->offset = 0;
538 s->backward = false;
539 s->start_tick = current_tick + scroll_delay * 2;
541 if (s->offset >= s->len - (LCD_WIDTH - xpos))
543 /* at end of line */
544 s->offset = s->len - (LCD_WIDTH - xpos);
545 s->backward = true;
546 s->start_tick = current_tick + scroll_delay * 2;
549 else /* scroll forward the whole time */
551 if (s->offset >= s->len)
552 s->offset -= s->len;
554 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
555 update = true;
557 if (lcd_cursor.enabled)
559 if (--lcd_cursor.downcount <= 0)
561 lcd_cursor.downcount = lcd_cursor.divider;
562 lcd_cursor.visible = !lcd_cursor.visible;
563 update = true;
566 if (update)
567 lcd_update();
569 sleep(scroll_ticks);