302 is also a valid redirect. Also keep the original query when redirecting.
[Rockbox.git] / firmware / drivers / lcd-charcell.c
blob0fd41481c5e0f54845f8791e6f25f4a559b3c5c1
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 int xmargin = 0;
56 static int ymargin = 0;
58 /* LCD init */
59 void lcd_init (void)
61 lcd_init_device();
62 lcd_charset_init();
63 memset(lcd_patterns, 0, sizeof(lcd_patterns));
64 xspace = find_xchar(' ');
65 memset(lcd_charbuffer, xchar_info[xspace].hw_char, sizeof(lcd_charbuffer));
66 scroll_init();
69 /** parameter handling **/
71 void lcd_setmargins(int x, int y)
73 xmargin = x;
74 ymargin = y;
77 int lcd_getxmargin(void)
79 return xmargin;
82 int lcd_getymargin(void)
84 return ymargin;
87 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
89 int width = utf8length(str);
91 if (w)
92 *w = width;
93 if (h)
94 *h = 1;
96 return width;
99 /** low-level functions **/
101 static int find_xchar(unsigned long ucs)
103 int low = 0;
104 int high = xchar_info_size - 1;
108 int probe = (low + high) >> 1;
110 if (xchar_info[probe].ucs < ucs)
111 low = probe + 1;
112 else if (xchar_info[probe].ucs > ucs)
113 high = probe - 1;
114 else
115 return probe;
117 while (low <= high);
119 /* Not found: return index of no-char symbol (last symbol, hardcoded). */
120 return xchar_info_size - 1;
123 static int glyph_to_pat(unsigned glyph)
125 int i;
127 for (i = 0; i < lcd_pattern_count; i++)
128 if (lcd_patterns[i].glyph == glyph)
129 return i;
131 return NO_PATTERN;
134 static void lcd_free_pat(int pat)
136 int x, y;
138 if (pat != NO_PATTERN)
140 for (x = 0; x < LCD_WIDTH; x++)
141 for (y = 0; y < LCD_HEIGHT; y++)
142 if (pat == lcd_charbuffer[y][x])
143 lcd_charbuffer[y][x] = lcd_substbuffer[y][x];
145 if (lcd_cursor.enabled && pat == lcd_cursor.hw_char)
146 lcd_cursor.hw_char = lcd_cursor.subst_char;
148 lcd_patterns[pat].count = 0;
152 static int lcd_get_free_pat(int xchar)
154 static int last_used_pat = 0;
156 int pat = last_used_pat; /* start from last used pattern */
157 int least_pat = pat; /* pattern with least priority */
158 int least_priority = lcd_patterns[pat].priority;
159 int i;
161 for (i = 0; i < lcd_pattern_count; i++)
163 if (++pat >= lcd_pattern_count) /* Keep 'pat' within limits */
164 pat = 0;
166 if (lcd_patterns[pat].count == 0)
168 last_used_pat = pat;
169 return pat;
171 if (lcd_patterns[pat].priority < least_priority)
173 least_priority = lcd_patterns[pat].priority;
174 least_pat = pat;
177 if (xchar_info[xchar].priority > least_priority) /* prioritized char */
179 lcd_free_pat(least_pat);
180 last_used_pat = least_pat;
181 return least_pat;
183 return NO_PATTERN;
186 static const unsigned char *glyph_to_pattern(unsigned glyph)
188 if (glyph & 0x8000)
189 return xfont_variable[glyph & 0x7fff];
190 else
191 return xfont_fixed[glyph];
194 static int map_xchar(int xchar, unsigned char *substitute)
196 int pat;
197 unsigned glyph;
199 if (xchar_info[xchar].priority > 0) /* soft char */
201 glyph = xchar_info[xchar].glyph;
202 pat = glyph_to_pat(glyph);
204 if (pat == NO_PATTERN) /* not yet mapped */
206 pat = lcd_get_free_pat(xchar); /* try to map */
208 if (pat == NO_PATTERN) /* failed: just use substitute */
209 return xchar_info[xchar].hw_char;
210 else
211 { /* define pattern */
212 lcd_patterns[pat].priority = xchar_info[xchar].priority;
213 lcd_patterns[pat].glyph = glyph;
214 memcpy(lcd_patterns[pat].pattern, glyph_to_pattern(glyph),
215 HW_PATTERN_SIZE);
218 lcd_patterns[pat].count++; /* increase reference count */
219 *substitute = xchar_info[xchar].hw_char;
220 return pat;
222 else /* hardware char */
223 return xchar_info[xchar].hw_char;
226 static void lcd_putxchar(int x, int y, int xchar)
228 int lcd_char = lcd_charbuffer[y][x];
230 if (lcd_char < lcd_pattern_count) /* old char was soft */
231 lcd_patterns[lcd_char].count--; /* decrease old reference count */
233 lcd_charbuffer[y][x] = map_xchar(xchar, &lcd_substbuffer[y][x]);
236 /** user-definable pattern handling **/
238 unsigned long lcd_get_locked_pattern(void)
240 int i = 0;
242 for (i = 0; i < VARIABLE_XCHARS; i++)
244 if (!xfont_variable_locked[i])
246 xfont_variable_locked[i] = true;
247 return 0xe000 + i; /* hard-coded */
250 return 0;
253 void lcd_unlock_pattern(unsigned long ucs)
255 int xchar = find_xchar(ucs);
256 unsigned glyph = xchar_info[xchar].glyph;
258 if (glyph & 0x8000) /* variable extended char */
260 lcd_free_pat(glyph_to_pat(glyph));
261 xfont_variable_locked[glyph & 0x7fff] = false;
265 void lcd_define_pattern(unsigned long ucs, const char *pattern)
267 int xchar = find_xchar(ucs);
268 unsigned glyph = xchar_info[xchar].glyph;
269 int pat;
271 if (glyph & 0x8000) /* variable extended char */
273 memcpy(xfont_variable[glyph & 0x7fff], pattern, HW_PATTERN_SIZE);
274 pat = glyph_to_pat(glyph);
275 if (pat != NO_PATTERN)
276 memcpy(lcd_patterns[pat].pattern, pattern, HW_PATTERN_SIZE);
280 /** output functions **/
282 /* Clear the whole display */
283 void lcd_clear_display(void)
285 int x, y;
287 lcd_stop_scroll();
288 lcd_remove_cursor();
290 for (x = 0; x < LCD_WIDTH; x++)
291 for (y = 0; y < LCD_HEIGHT; y++)
292 lcd_putxchar(x, y, xspace);
295 /* Put an unicode character at the given position */
296 void lcd_putc(int x, int y, unsigned long ucs)
298 if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT)
299 return;
301 lcd_putxchar(x, y, find_xchar(ucs));
304 /* Show cursor (alternating with existing character) at the given position */
305 void lcd_put_cursor(int x, int y, unsigned long cursor_ucs)
307 if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT
308 || lcd_cursor.enabled)
309 return;
311 lcd_cursor.enabled = true;
312 lcd_cursor.visible = false;
313 lcd_cursor.hw_char = map_xchar(find_xchar(cursor_ucs), &lcd_cursor.subst_char);
314 lcd_cursor.x = x;
315 lcd_cursor.y = y;
316 lcd_cursor.downcount = 0;
317 lcd_cursor.divider = MAX((HZ/2) / lcd_scroll_info.ticks, 1);
320 /* Remove the cursor */
321 void lcd_remove_cursor(void)
323 if (lcd_cursor.enabled)
325 if (lcd_cursor.hw_char < lcd_pattern_count) /* soft char, unmap */
326 lcd_patterns[lcd_cursor.hw_char].count--;
328 lcd_cursor.enabled = lcd_cursor.visible = false;
332 /* Put a string at a given position, skipping first ofs chars */
333 static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
335 unsigned short ucs;
336 const unsigned char *utf8 = str;
338 while (*utf8 && x < LCD_WIDTH)
340 utf8 = utf8decode(utf8, &ucs);
342 if (ofs > 0)
344 ofs--;
345 continue;
347 lcd_putxchar(x++, y, find_xchar(ucs));
349 return x;
352 /* Put a string at a given position */
353 void lcd_putsxy(int x, int y, const unsigned char *str)
355 if ((unsigned)y >= LCD_HEIGHT)
356 return;
358 lcd_putsxyofs(x, y, 0, str);
361 /*** Line oriented text output ***/
363 /* Put a string at a given char position */
364 void lcd_puts(int x, int y, const unsigned char *str)
366 lcd_puts_offset(x, y, str, 0);
369 /* Put a string at a given char position, skipping first offset chars */
370 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
372 x += xmargin;
373 y += ymargin;
375 if ((unsigned)y >= LCD_HEIGHT)
376 return;
378 /* make sure scrolling is turned off on the line we are updating */
379 lcd_scroll_info.lines &= ~(1 << y);
381 x = lcd_putsxyofs(x, y, offset, str);
382 while (x < LCD_WIDTH)
383 lcd_putxchar(x++, y, xspace);
386 /** scrolling **/
387 void lcd_puts_scroll(int x, int y, const unsigned char *string)
389 lcd_puts_scroll_offset(x, y, string, 0);
392 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
393 int offset)
395 struct scrollinfo* s;
396 int len;
398 if(y>=LCD_SCROLLABLE_LINES) return;
400 s = &lcd_scroll_info.scroll[y];
402 s->start_tick = current_tick + lcd_scroll_info.delay;
404 lcd_puts_offset(x, y, string, offset);
405 len = utf8length(string);
407 if (LCD_WIDTH - x - xmargin < len)
409 /* prepare scroll line */
410 char *end;
412 memset(s->line, 0, sizeof s->line);
413 strcpy(s->line, string);
415 /* get width */
416 s->len = utf8length(s->line);
418 /* scroll bidirectional or forward only depending on the string width */
419 if (lcd_scroll_info.bidir_limit)
421 s->bidir = s->len < (LCD_WIDTH - xmargin) *
422 (100 + lcd_scroll_info.bidir_limit) / 100;
424 else
425 s->bidir = false;
427 if (!s->bidir) /* add spaces if scrolling in the round */
429 strcat(s->line, " ");
430 /* get new width incl. spaces */
431 s->len += SCROLL_SPACING;
434 end = strchr(s->line, '\0');
435 strncpy(end, string, utf8seek(s->line, LCD_WIDTH));
437 s->offset = offset;
438 s->startx = xmargin + x;
439 s->backward = false;
440 lcd_scroll_info.lines |= (1<<y);
442 else
443 /* force a bit switch-off since it doesn't scroll */
444 lcd_scroll_info.lines &= ~(1<<y);
447 void lcd_scroll_fn(void)
449 struct scrollinfo* s;
450 int index;
451 int xpos, ypos;
452 bool update;
454 update = false;
455 for (index = 0; index < LCD_SCROLLABLE_LINES; index++)
457 /* really scroll? */
458 if ((lcd_scroll_info.lines & (1 << index)) == 0)
459 continue;
461 s = &lcd_scroll_info.scroll[index];
463 /* check pause */
464 if (TIME_BEFORE(current_tick, s->start_tick))
465 continue;
467 if (s->backward)
468 s->offset--;
469 else
470 s->offset++;
472 xpos = s->startx;
473 ypos = ymargin + index;
475 if (s->bidir) /* scroll bidirectional */
477 if (s->offset <= 0) {
478 /* at beginning of line */
479 s->offset = 0;
480 s->backward = false;
481 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
483 if (s->offset >= s->len - (LCD_WIDTH - xpos)) {
484 /* at end of line */
485 s->offset = s->len - (LCD_WIDTH - xpos);
486 s->backward = true;
487 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
490 else /* scroll forward the whole time */
492 if (s->offset >= s->len)
493 s->offset -= s->len;
496 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
497 update = true;
500 if (lcd_cursor.enabled)
502 if (--lcd_cursor.downcount <= 0)
504 lcd_cursor.downcount = lcd_cursor.divider;
505 lcd_cursor.visible = !lcd_cursor.visible;
506 update = true;
510 if (update)
511 lcd_update();