cf2b55da8beeedc2f6290fefd115bf74fb0f3beb
[kugel-rb.git] / firmware / drivers / lcd-charcell.c
blobcf2b55da8beeedc2f6290fefd115bf74fb0f3beb
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 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include <stdio.h>
24 #include "config.h"
25 #include "hwcompat.h"
26 #include "stdarg.h"
27 #include "lcd.h"
28 #include "kernel.h"
29 #include "thread.h"
30 #include <string.h>
31 #include <stdlib.h>
32 #include "debug.h"
33 #include "file.h"
34 #include "system.h"
35 #include "lcd-charcell.h"
36 #include "rbunicode.h"
37 #include "scroll_engine.h"
39 /** definitions **/
41 #define VARIABLE_XCHARS 16 /* number of software user-definable characters */
42 /* There must be mappings for this many characters in the 0xe000 unicode range
43 * in lcd-charset-<target>.c */
45 #define NO_PATTERN (-1)
47 static int find_xchar(unsigned long ucs);
49 /** globals **/
51 unsigned char lcd_charbuffer[LCD_HEIGHT][LCD_WIDTH]; /* The "frame"buffer */
52 static unsigned char lcd_substbuffer[LCD_HEIGHT][LCD_WIDTH];
53 struct pattern_info lcd_patterns[MAX_HW_PATTERNS];
54 struct cursor_info lcd_cursor;
56 static unsigned char xfont_variable[VARIABLE_XCHARS][HW_PATTERN_SIZE];
57 static bool xfont_variable_locked[VARIABLE_XCHARS];
58 static int xspace; /* stores xhcar id of ' ' - often needed */
60 static struct viewport default_vp =
62 .x = 0,
63 .y = 0,
64 .width = LCD_WIDTH,
65 .height = LCD_HEIGHT,
68 static struct viewport* current_vp = &default_vp;
70 /* LCD init */
71 void lcd_init (void)
73 lcd_init_device();
74 lcd_charset_init();
75 memset(lcd_patterns, 0, sizeof(lcd_patterns));
76 xspace = find_xchar(' ');
77 memset(lcd_charbuffer, xchar_info[xspace].hw_char, sizeof(lcd_charbuffer));
78 scroll_init();
81 /* Viewports */
83 void lcd_set_viewport(struct viewport* vp)
85 if (vp == NULL)
86 current_vp = &default_vp;
87 else
88 current_vp = vp;
90 #if defined(SIMULATOR)
91 /* Force the viewport to be within bounds. If this happens it should
92 * be considered an error - the viewport will not draw as it might be
93 * expected.
95 if((unsigned) current_vp->x > (unsigned) LCD_WIDTH
96 || (unsigned) current_vp->y > (unsigned) LCD_HEIGHT
97 || current_vp->x + current_vp->width > LCD_WIDTH
98 || current_vp->y + current_vp->height > LCD_HEIGHT)
100 #if !defined(HAVE_VIEWPORT_CLIP)
101 DEBUGF("ERROR: "
102 #else
103 DEBUGF("NOTE: "
104 #endif
105 "set_viewport out of bounds: x: %d y: %d width: %d height:%d\n",
106 current_vp->x, current_vp->y,
107 current_vp->width, current_vp->height);
110 #endif
113 void lcd_update_viewport(void)
115 lcd_update();
118 /** parameter handling **/
120 int lcd_getwidth(void)
122 return current_vp->width;
125 int lcd_getheight(void)
127 return current_vp->height;
130 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
132 int width = utf8length(str);
134 if (w)
135 *w = width;
136 if (h)
137 *h = 1;
139 return width;
142 /** low-level functions **/
144 static int find_xchar(unsigned long ucs)
146 int low = 0;
147 int high = xchar_info_size - 1;
151 int probe = (low + high) >> 1;
153 if (xchar_info[probe].ucs < ucs)
154 low = probe + 1;
155 else if (xchar_info[probe].ucs > ucs)
156 high = probe - 1;
157 else
158 return probe;
160 while (low <= high);
162 /* Not found: return index of no-char symbol (last symbol, hardcoded). */
163 return xchar_info_size - 1;
166 static int glyph_to_pat(unsigned glyph)
168 int i;
170 for (i = 0; i < lcd_pattern_count; i++)
171 if (lcd_patterns[i].glyph == glyph)
172 return i;
174 return NO_PATTERN;
177 static void lcd_free_pat(int pat)
179 int x, y;
181 if (pat != NO_PATTERN)
183 for (x = 0; x < LCD_WIDTH; x++)
184 for (y = 0; y < LCD_HEIGHT; y++)
185 if (pat == lcd_charbuffer[y][x])
186 lcd_charbuffer[y][x] = lcd_substbuffer[y][x];
188 if (lcd_cursor.enabled && pat == lcd_cursor.hw_char)
189 lcd_cursor.hw_char = lcd_cursor.subst_char;
191 lcd_patterns[pat].count = 0;
195 static int lcd_get_free_pat(int xchar)
197 static int last_used_pat = 0;
199 int pat = last_used_pat; /* start from last used pattern */
200 int least_pat = pat; /* pattern with least priority */
201 int least_priority = lcd_patterns[pat].priority;
202 int i;
204 for (i = 0; i < lcd_pattern_count; i++)
206 if (++pat >= lcd_pattern_count) /* Keep 'pat' within limits */
207 pat = 0;
209 if (lcd_patterns[pat].count == 0)
211 last_used_pat = pat;
212 return pat;
214 if (lcd_patterns[pat].priority < least_priority)
216 least_priority = lcd_patterns[pat].priority;
217 least_pat = pat;
220 if (xchar_info[xchar].priority > least_priority) /* prioritized char */
222 lcd_free_pat(least_pat);
223 last_used_pat = least_pat;
224 return least_pat;
226 return NO_PATTERN;
229 static const unsigned char *glyph_to_pattern(unsigned glyph)
231 if (glyph & 0x8000)
232 return xfont_variable[glyph & 0x7fff];
233 else
234 return xfont_fixed[glyph];
237 static int map_xchar(int xchar, unsigned char *substitute)
239 int pat;
240 unsigned glyph;
242 if (xchar_info[xchar].priority > 0) /* soft char */
244 glyph = xchar_info[xchar].glyph;
245 pat = glyph_to_pat(glyph);
247 if (pat == NO_PATTERN) /* not yet mapped */
249 pat = lcd_get_free_pat(xchar); /* try to map */
251 if (pat == NO_PATTERN) /* failed: just use substitute */
252 return xchar_info[xchar].hw_char;
253 else
254 { /* define pattern */
255 lcd_patterns[pat].priority = xchar_info[xchar].priority;
256 lcd_patterns[pat].glyph = glyph;
257 memcpy(lcd_patterns[pat].pattern, glyph_to_pattern(glyph),
258 HW_PATTERN_SIZE);
261 lcd_patterns[pat].count++; /* increase reference count */
262 *substitute = xchar_info[xchar].hw_char;
263 return pat;
265 else /* hardware char */
266 return xchar_info[xchar].hw_char;
269 static void lcd_putxchar(int x, int y, int xchar)
271 int lcd_char;
273 /* Adjust for viewport */
274 x += current_vp->x;
275 y += current_vp->y;
277 #if defined(HAVE_VIEWPORT_CLIP)
278 if((unsigned)x > (unsigned)LCD_WIDTH || (unsigned)y > (unsigned)LCD_HEIGHT)
279 return;
280 #endif
282 lcd_char = lcd_charbuffer[y][x];
284 if (lcd_char < lcd_pattern_count) /* old char was soft */
285 lcd_patterns[lcd_char].count--; /* decrease old reference count */
287 lcd_charbuffer[y][x] = map_xchar(xchar, &lcd_substbuffer[y][x]);
290 /** user-definable pattern handling **/
292 unsigned long lcd_get_locked_pattern(void)
294 int i = 0;
296 for (i = 0; i < VARIABLE_XCHARS; i++)
298 if (!xfont_variable_locked[i])
300 xfont_variable_locked[i] = true;
301 return 0xe000 + i; /* hard-coded */
304 return 0;
307 void lcd_unlock_pattern(unsigned long ucs)
309 int xchar = find_xchar(ucs);
310 unsigned glyph = xchar_info[xchar].glyph;
312 if (glyph & 0x8000) /* variable extended char */
314 lcd_free_pat(glyph_to_pat(glyph));
315 xfont_variable_locked[glyph & 0x7fff] = false;
319 void lcd_define_pattern(unsigned long ucs, const char *pattern)
321 int xchar = find_xchar(ucs);
322 unsigned glyph = xchar_info[xchar].glyph;
323 int pat;
325 if (glyph & 0x8000) /* variable extended char */
327 memcpy(xfont_variable[glyph & 0x7fff], pattern, HW_PATTERN_SIZE);
328 pat = glyph_to_pat(glyph);
329 if (pat != NO_PATTERN)
330 memcpy(lcd_patterns[pat].pattern, pattern, HW_PATTERN_SIZE);
334 /** output functions **/
336 /* Clear the whole display */
337 void lcd_clear_display(void)
339 int x, y;
340 struct viewport* old_vp = current_vp;
342 lcd_scroll_info.lines = 0;
343 lcd_remove_cursor();
345 /* Set the default viewport - required for lcd_putxchar */
346 current_vp = &default_vp;
348 for (x = 0; x < LCD_WIDTH; x++)
349 for (y = 0; y < LCD_HEIGHT; y++)
350 lcd_putxchar(x, y, xspace);
352 current_vp = old_vp;
355 /* Clear the current viewport */
356 void lcd_clear_viewport(void)
358 int x, y;
360 if (current_vp == &default_vp)
362 lcd_clear_display();
364 else
366 /* Remove the cursor if it is within the current viewport */
367 if (lcd_cursor.enabled &&
368 (lcd_cursor.x >= current_vp->x) &&
369 (lcd_cursor.x <= current_vp->x + current_vp->width) &&
370 (lcd_cursor.y >= current_vp->y) &&
371 (lcd_cursor.y <= current_vp->y + current_vp->height))
373 lcd_remove_cursor();
376 for (x = 0; x < current_vp->width; x++)
377 for (y = 0; y < current_vp->height; y++)
378 lcd_putxchar(x, y, xspace);
380 lcd_scroll_stop(current_vp);
384 /* Put an unicode character at the given position */
385 void lcd_putc(int x, int y, unsigned long ucs)
387 if ((unsigned)x >= (unsigned)current_vp->width ||
388 (unsigned)y >= (unsigned)current_vp->height)
389 return;
391 lcd_putxchar(x, y, find_xchar(ucs));
394 /* Show cursor (alternating with existing character) at the given position */
395 void lcd_put_cursor(int x, int y, unsigned long cursor_ucs)
397 if ((unsigned)x >= (unsigned)current_vp->width ||
398 (unsigned)y >= (unsigned)current_vp->height ||
399 lcd_cursor.enabled)
400 return;
402 lcd_cursor.enabled = true;
403 lcd_cursor.visible = false;
404 lcd_cursor.hw_char = map_xchar(find_xchar(cursor_ucs), &lcd_cursor.subst_char);
405 lcd_cursor.x = current_vp->x + x;
406 lcd_cursor.y = current_vp->y + y;
407 lcd_cursor.downcount = 0;
408 lcd_cursor.divider = MAX((HZ/2) / lcd_scroll_info.ticks, 1);
411 /* Remove the cursor */
412 void lcd_remove_cursor(void)
414 if (lcd_cursor.enabled)
416 if (lcd_cursor.hw_char < lcd_pattern_count) /* soft char, unmap */
417 lcd_patterns[lcd_cursor.hw_char].count--;
419 lcd_cursor.enabled = lcd_cursor.visible = false;
423 /* Put a string at a given position, skipping first ofs chars */
424 static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
426 unsigned short ucs;
427 const unsigned char *utf8 = str;
429 while (*utf8 && x < current_vp->width)
431 utf8 = utf8decode(utf8, &ucs);
433 if (ofs > 0)
435 ofs--;
436 continue;
438 lcd_putxchar(x++, y, find_xchar(ucs));
440 return x;
443 /* Put a string at a given position */
444 void lcd_putsxy(int x, int y, const unsigned char *str)
446 if ((unsigned)y >= (unsigned)current_vp->height)
447 return;
449 lcd_putsxyofs(x, y, 0, str);
452 /*** Line oriented text output ***/
454 /* Put a string at a given char position */
455 void lcd_puts(int x, int y, const unsigned char *str)
457 lcd_puts_offset(x, y, str, 0);
460 /* Formatting version of lcd_puts */
461 void lcd_putsf(int x, int y, const unsigned char *fmt, ...)
463 va_list ap;
464 char buf[256];
465 va_start(ap, fmt);
466 vsnprintf(buf, sizeof (buf), fmt, ap);
467 va_end(ap);
468 lcd_puts(x, y, buf);
471 /* Put a string at a given char position, skipping first offset chars */
472 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
474 if ((unsigned)y >= (unsigned)current_vp->height)
475 return;
477 /* make sure scrolling is turned off on the line we are updating */
478 lcd_scroll_stop_line(current_vp, y);
480 x = lcd_putsxyofs(x, y, offset, str);
481 while (x < current_vp->width)
482 lcd_putxchar(x++, y, xspace);
485 /** scrolling **/
486 void lcd_puts_scroll(int x, int y, const unsigned char *string)
488 lcd_puts_scroll_offset(x, y, string, 0);
491 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
492 int offset)
494 struct scrollinfo* s;
495 int len;
497 if ((unsigned)y >= (unsigned)current_vp->height)
498 return;
500 /* remove any previously scrolling line at the same location */
501 lcd_scroll_stop_line(current_vp, y);
503 if (lcd_scroll_info.lines >= LCD_SCROLLABLE_LINES) return;
505 s = &lcd_scroll_info.scroll[lcd_scroll_info.lines];
507 s->start_tick = current_tick + lcd_scroll_info.delay;
509 lcd_puts_offset(x, y, string, offset);
510 len = utf8length(string);
512 if (current_vp->width - x < len)
514 /* prepare scroll line */
515 char *end;
517 memset(s->line, 0, sizeof s->line);
518 strcpy(s->line, string);
520 /* get width */
521 s->len = utf8length(s->line);
523 /* scroll bidirectional or forward only depending on the string width */
524 if (lcd_scroll_info.bidir_limit)
526 s->bidir = s->len < (current_vp->width) *
527 (100 + lcd_scroll_info.bidir_limit) / 100;
529 else
530 s->bidir = false;
532 if (!s->bidir) /* add spaces if scrolling in the round */
534 strcat(s->line, " ");
535 /* get new width incl. spaces */
536 s->len += SCROLL_SPACING;
539 end = strchr(s->line, '\0');
540 strlcpy(end, string, utf8seek(s->line, current_vp->width));
542 s->vp = current_vp;
543 s->y = y;
544 s->offset = offset;
545 s->startx = x;
546 s->backward = false;
547 lcd_scroll_info.lines++;
551 void lcd_scroll_fn(void)
553 struct scrollinfo* s;
554 int index;
555 int xpos, ypos;
556 bool update;
557 struct viewport* old_vp = current_vp;
559 update = false;
560 for ( index = 0; index < lcd_scroll_info.lines; index++ ) {
561 s = &lcd_scroll_info.scroll[index];
563 /* check pause */
564 if (TIME_BEFORE(current_tick, s->start_tick))
565 continue;
567 lcd_set_viewport(s->vp);
569 if (s->backward)
570 s->offset--;
571 else
572 s->offset++;
574 xpos = s->startx;
575 ypos = s->y;
577 if (s->bidir) /* scroll bidirectional */
579 if (s->offset <= 0) {
580 /* at beginning of line */
581 s->offset = 0;
582 s->backward = false;
583 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
585 if (s->offset >= s->len - (current_vp->width - xpos)) {
586 /* at end of line */
587 s->offset = s->len - (current_vp->width - xpos);
588 s->backward = true;
589 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
592 else /* scroll forward the whole time */
594 if (s->offset >= s->len)
595 s->offset -= s->len;
598 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
599 update = true;
602 lcd_set_viewport(old_vp);
604 if (lcd_cursor.enabled)
606 if (--lcd_cursor.downcount <= 0)
608 lcd_cursor.downcount = lcd_cursor.divider;
609 lcd_cursor.visible = !lcd_cursor.visible;
610 update = true;
614 if (update)
615 lcd_update();