a bit of code cleanup.. use a single function to get the statusbar height (or lack...
[Rockbox.git] / firmware / drivers / lcd-charcell.c
blob1bc634cd2fbb7212543b64f3ea4e947d7532a5dd
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"
32 #include "scroll_engine.h"
34 /** definitions **/
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 struct viewport default_vp =
57 .x = 0,
58 .y = 0,
59 .width = LCD_WIDTH,
60 .height = LCD_HEIGHT,
61 .xmargin = 0,
62 .ymargin = 0,
65 static struct viewport* current_vp = &default_vp;
67 /* LCD init */
68 void lcd_init (void)
70 lcd_init_device();
71 lcd_charset_init();
72 memset(lcd_patterns, 0, sizeof(lcd_patterns));
73 xspace = find_xchar(' ');
74 memset(lcd_charbuffer, xchar_info[xspace].hw_char, sizeof(lcd_charbuffer));
75 scroll_init();
78 /* Viewports */
80 void lcd_set_viewport(struct viewport* vp)
82 if (vp == NULL)
83 current_vp = &default_vp;
84 else
85 current_vp = vp;
88 void lcd_update_viewport(void)
90 lcd_update();
93 /** parameter handling **/
95 void lcd_setmargins(int x, int y)
97 current_vp->xmargin = x;
98 current_vp->ymargin = y;
101 int lcd_getxmargin(void)
103 return current_vp->xmargin;
106 int lcd_getymargin(void)
108 return current_vp->ymargin;
111 int lcd_getwidth(void)
113 return current_vp->width;
116 int lcd_getheight(void)
118 return current_vp->height;
121 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
123 int width = utf8length(str);
125 if (w)
126 *w = width;
127 if (h)
128 *h = 1;
130 return width;
133 /** low-level functions **/
135 static int find_xchar(unsigned long ucs)
137 int low = 0;
138 int high = xchar_info_size - 1;
142 int probe = (low + high) >> 1;
144 if (xchar_info[probe].ucs < ucs)
145 low = probe + 1;
146 else if (xchar_info[probe].ucs > ucs)
147 high = probe - 1;
148 else
149 return probe;
151 while (low <= high);
153 /* Not found: return index of no-char symbol (last symbol, hardcoded). */
154 return xchar_info_size - 1;
157 static int glyph_to_pat(unsigned glyph)
159 int i;
161 for (i = 0; i < lcd_pattern_count; i++)
162 if (lcd_patterns[i].glyph == glyph)
163 return i;
165 return NO_PATTERN;
168 static void lcd_free_pat(int pat)
170 int x, y;
172 if (pat != NO_PATTERN)
174 for (x = 0; x < LCD_WIDTH; x++)
175 for (y = 0; y < LCD_HEIGHT; y++)
176 if (pat == lcd_charbuffer[y][x])
177 lcd_charbuffer[y][x] = lcd_substbuffer[y][x];
179 if (lcd_cursor.enabled && pat == lcd_cursor.hw_char)
180 lcd_cursor.hw_char = lcd_cursor.subst_char;
182 lcd_patterns[pat].count = 0;
186 static int lcd_get_free_pat(int xchar)
188 static int last_used_pat = 0;
190 int pat = last_used_pat; /* start from last used pattern */
191 int least_pat = pat; /* pattern with least priority */
192 int least_priority = lcd_patterns[pat].priority;
193 int i;
195 for (i = 0; i < lcd_pattern_count; i++)
197 if (++pat >= lcd_pattern_count) /* Keep 'pat' within limits */
198 pat = 0;
200 if (lcd_patterns[pat].count == 0)
202 last_used_pat = pat;
203 return pat;
205 if (lcd_patterns[pat].priority < least_priority)
207 least_priority = lcd_patterns[pat].priority;
208 least_pat = pat;
211 if (xchar_info[xchar].priority > least_priority) /* prioritized char */
213 lcd_free_pat(least_pat);
214 last_used_pat = least_pat;
215 return least_pat;
217 return NO_PATTERN;
220 static const unsigned char *glyph_to_pattern(unsigned glyph)
222 if (glyph & 0x8000)
223 return xfont_variable[glyph & 0x7fff];
224 else
225 return xfont_fixed[glyph];
228 static int map_xchar(int xchar, unsigned char *substitute)
230 int pat;
231 unsigned glyph;
233 if (xchar_info[xchar].priority > 0) /* soft char */
235 glyph = xchar_info[xchar].glyph;
236 pat = glyph_to_pat(glyph);
238 if (pat == NO_PATTERN) /* not yet mapped */
240 pat = lcd_get_free_pat(xchar); /* try to map */
242 if (pat == NO_PATTERN) /* failed: just use substitute */
243 return xchar_info[xchar].hw_char;
244 else
245 { /* define pattern */
246 lcd_patterns[pat].priority = xchar_info[xchar].priority;
247 lcd_patterns[pat].glyph = glyph;
248 memcpy(lcd_patterns[pat].pattern, glyph_to_pattern(glyph),
249 HW_PATTERN_SIZE);
252 lcd_patterns[pat].count++; /* increase reference count */
253 *substitute = xchar_info[xchar].hw_char;
254 return pat;
256 else /* hardware char */
257 return xchar_info[xchar].hw_char;
260 static void lcd_putxchar(int x, int y, int xchar)
262 int lcd_char;
264 /* Adjust for viewport */
265 x += current_vp->x;
266 y += current_vp->y;
268 lcd_char = lcd_charbuffer[y][x];
270 if (lcd_char < lcd_pattern_count) /* old char was soft */
271 lcd_patterns[lcd_char].count--; /* decrease old reference count */
273 lcd_charbuffer[y][x] = map_xchar(xchar, &lcd_substbuffer[y][x]);
276 /** user-definable pattern handling **/
278 unsigned long lcd_get_locked_pattern(void)
280 int i = 0;
282 for (i = 0; i < VARIABLE_XCHARS; i++)
284 if (!xfont_variable_locked[i])
286 xfont_variable_locked[i] = true;
287 return 0xe000 + i; /* hard-coded */
290 return 0;
293 void lcd_unlock_pattern(unsigned long ucs)
295 int xchar = find_xchar(ucs);
296 unsigned glyph = xchar_info[xchar].glyph;
298 if (glyph & 0x8000) /* variable extended char */
300 lcd_free_pat(glyph_to_pat(glyph));
301 xfont_variable_locked[glyph & 0x7fff] = false;
305 void lcd_define_pattern(unsigned long ucs, const char *pattern)
307 int xchar = find_xchar(ucs);
308 unsigned glyph = xchar_info[xchar].glyph;
309 int pat;
311 if (glyph & 0x8000) /* variable extended char */
313 memcpy(xfont_variable[glyph & 0x7fff], pattern, HW_PATTERN_SIZE);
314 pat = glyph_to_pat(glyph);
315 if (pat != NO_PATTERN)
316 memcpy(lcd_patterns[pat].pattern, pattern, HW_PATTERN_SIZE);
320 /** output functions **/
322 /* Clear the whole display */
323 void lcd_clear_display(void)
325 int x, y;
326 struct viewport* old_vp = current_vp;
328 lcd_scroll_info.lines = 0;
329 lcd_remove_cursor();
331 /* Set the default viewport - required for lcd_putxchar */
332 current_vp = &default_vp;
334 for (x = 0; x < LCD_WIDTH; x++)
335 for (y = 0; y < LCD_HEIGHT; y++)
336 lcd_putxchar(x, y, xspace);
338 current_vp = old_vp;
341 /* Clear the current viewport */
342 void lcd_clear_viewport(void)
344 int x, y;
346 if (current_vp == &default_vp)
348 lcd_clear_display();
350 else
352 /* Remove the cursor if it is within the current viewport */
353 if (lcd_cursor.enabled &&
354 (lcd_cursor.x >= current_vp->x) &&
355 (lcd_cursor.x <= current_vp->x + current_vp->width) &&
356 (lcd_cursor.y >= current_vp->y) &&
357 (lcd_cursor.y <= current_vp->y + current_vp->height))
359 lcd_remove_cursor();
362 for (x = 0; x < current_vp->width; x++)
363 for (y = 0; y < current_vp->height; y++)
364 lcd_putxchar(x, y, xspace);
366 lcd_scroll_stop(current_vp);
370 /* Put an unicode character at the given position */
371 void lcd_putc(int x, int y, unsigned long ucs)
373 if ((unsigned)x >= (unsigned)current_vp->width ||
374 (unsigned)y >= (unsigned)current_vp->height)
375 return;
377 lcd_putxchar(x, y, find_xchar(ucs));
380 /* Show cursor (alternating with existing character) at the given position */
381 void lcd_put_cursor(int x, int y, unsigned long cursor_ucs)
383 if ((unsigned)x >= (unsigned)current_vp->width ||
384 (unsigned)y >= (unsigned)current_vp->height ||
385 lcd_cursor.enabled)
386 return;
388 lcd_cursor.enabled = true;
389 lcd_cursor.visible = false;
390 lcd_cursor.hw_char = map_xchar(find_xchar(cursor_ucs), &lcd_cursor.subst_char);
391 lcd_cursor.x = current_vp->x + x;
392 lcd_cursor.y = current_vp->y + y;
393 lcd_cursor.downcount = 0;
394 lcd_cursor.divider = MAX((HZ/2) / lcd_scroll_info.ticks, 1);
397 /* Remove the cursor */
398 void lcd_remove_cursor(void)
400 if (lcd_cursor.enabled)
402 if (lcd_cursor.hw_char < lcd_pattern_count) /* soft char, unmap */
403 lcd_patterns[lcd_cursor.hw_char].count--;
405 lcd_cursor.enabled = lcd_cursor.visible = false;
409 /* Put a string at a given position, skipping first ofs chars */
410 static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
412 unsigned short ucs;
413 const unsigned char *utf8 = str;
415 while (*utf8 && x < current_vp->width)
417 utf8 = utf8decode(utf8, &ucs);
419 if (ofs > 0)
421 ofs--;
422 continue;
424 lcd_putxchar(x++, y, find_xchar(ucs));
426 return x;
429 /* Put a string at a given position */
430 void lcd_putsxy(int x, int y, const unsigned char *str)
432 if ((unsigned)y >= (unsigned)current_vp->height)
433 return;
435 lcd_putsxyofs(x, y, 0, str);
438 /*** Line oriented text output ***/
440 /* Put a string at a given char position */
441 void lcd_puts(int x, int y, const unsigned char *str)
443 lcd_puts_offset(x, y, str, 0);
446 /* Put a string at a given char position, skipping first offset chars */
447 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
449 if ((unsigned)y >= (unsigned)current_vp->height)
450 return;
452 /* make sure scrolling is turned off on the line we are updating */
453 lcd_scroll_stop_line(current_vp, y);
455 x = lcd_putsxyofs(x, y, offset, str);
456 while (x < current_vp->width)
457 lcd_putxchar(x++, y, xspace);
460 /** scrolling **/
461 void lcd_puts_scroll(int x, int y, const unsigned char *string)
463 lcd_puts_scroll_offset(x, y, string, 0);
466 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
467 int offset)
469 struct scrollinfo* s;
470 int len;
472 if ((unsigned)y >= (unsigned)current_vp->height)
473 return;
475 /* remove any previously scrolling line at the same location */
476 lcd_scroll_stop_line(current_vp, y);
478 if (lcd_scroll_info.lines >= LCD_SCROLLABLE_LINES) return;
480 s = &lcd_scroll_info.scroll[lcd_scroll_info.lines];
482 s->start_tick = current_tick + lcd_scroll_info.delay;
484 lcd_puts_offset(x, y, string, offset);
485 len = utf8length(string);
487 if (current_vp->width - x - current_vp->xmargin < len)
489 /* prepare scroll line */
490 char *end;
492 memset(s->line, 0, sizeof s->line);
493 strcpy(s->line, string);
495 /* get width */
496 s->len = utf8length(s->line);
498 /* scroll bidirectional or forward only depending on the string width */
499 if (lcd_scroll_info.bidir_limit)
501 s->bidir = s->len < (current_vp->width - current_vp->xmargin) *
502 (100 + lcd_scroll_info.bidir_limit) / 100;
504 else
505 s->bidir = false;
507 if (!s->bidir) /* add spaces if scrolling in the round */
509 strcat(s->line, " ");
510 /* get new width incl. spaces */
511 s->len += SCROLL_SPACING;
514 end = strchr(s->line, '\0');
515 strncpy(end, string, utf8seek(s->line, current_vp->width));
517 s->vp = current_vp;
518 s->y = y;
519 s->offset = offset;
520 s->startx = current_vp->xmargin + x;
521 s->backward = false;
522 lcd_scroll_info.lines++;
526 void lcd_scroll_fn(void)
528 struct scrollinfo* s;
529 int index;
530 int xpos, ypos;
531 bool update;
532 struct viewport* old_vp = current_vp;
534 update = false;
535 for ( index = 0; index < lcd_scroll_info.lines; index++ ) {
536 s = &lcd_scroll_info.scroll[index];
538 /* check pause */
539 if (TIME_BEFORE(current_tick, s->start_tick))
540 continue;
542 lcd_set_viewport(s->vp);
544 if (s->backward)
545 s->offset--;
546 else
547 s->offset++;
549 xpos = s->startx;
550 ypos = current_vp->ymargin + s->y;
552 if (s->bidir) /* scroll bidirectional */
554 if (s->offset <= 0) {
555 /* at beginning of line */
556 s->offset = 0;
557 s->backward = false;
558 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
560 if (s->offset >= s->len - (current_vp->width - xpos)) {
561 /* at end of line */
562 s->offset = s->len - (current_vp->width - xpos);
563 s->backward = true;
564 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
567 else /* scroll forward the whole time */
569 if (s->offset >= s->len)
570 s->offset -= s->len;
573 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
574 update = true;
577 lcd_set_viewport(old_vp);
579 if (lcd_cursor.enabled)
581 if (--lcd_cursor.downcount <= 0)
583 lcd_cursor.downcount = lcd_cursor.divider;
584 lcd_cursor.visible = !lcd_cursor.visible;
585 update = true;
589 if (update)
590 lcd_update();