1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
30 #include "lcd-charcell.h"
31 #include "rbunicode.h"
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
);
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;
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] = {
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
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
)
99 int lcd_getxmargin(void)
104 int lcd_getymargin(void)
109 int lcd_getstringsize(const unsigned char *str
, int *w
, int *h
)
111 int width
= utf8length(str
);
121 /** low-level functions **/
123 static int find_xchar(unsigned long ucs
)
126 int high
= xchar_info_size
- 1;
130 int probe
= (low
+ high
) >> 1;
132 if (xchar_info
[probe
].ucs
< ucs
)
134 else if (xchar_info
[probe
].ucs
> ucs
)
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
)
149 for (i
= 0; i
< lcd_pattern_count
; i
++)
150 if (lcd_patterns
[i
].glyph
== glyph
)
156 static void lcd_free_pat(int pat
)
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
;
183 for (i
= 0; i
< lcd_pattern_count
; i
++)
185 if (++pat
>= lcd_pattern_count
) /* Keep 'pat' within limits */
188 if (lcd_patterns
[pat
].count
== 0)
193 if (lcd_patterns
[pat
].priority
< least_priority
)
195 least_priority
= lcd_patterns
[pat
].priority
;
199 if (xchar_info
[xchar
].priority
> least_priority
) /* prioritized char */
201 lcd_free_pat(least_pat
);
202 last_used_pat
= least_pat
;
208 static const unsigned char *glyph_to_pattern(unsigned glyph
)
211 return xfont_variable
[glyph
& 0x7fff];
213 return xfont_fixed
[glyph
];
216 static int map_xchar(int xchar
, unsigned char *substitute
)
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
;
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
),
240 lcd_patterns
[pat
].count
++; /* increase reference count */
241 *substitute
= xchar_info
[xchar
].hw_char
;
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)
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 */
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
;
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)
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
)
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
)
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
);
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
)
358 const unsigned char *utf8
= str
;
360 while (*utf8
&& x
< LCD_WIDTH
)
362 utf8
= utf8decode(utf8
, &ucs
);
369 lcd_putxchar(x
++, y
, find_xchar(ucs
));
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
)
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
)
397 if ((unsigned)y
>= LCD_HEIGHT
)
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
);
410 void lcd_stop_scroll(void)
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 */
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
,
448 struct scrollinfo
* s
;
451 if(y
>=SCROLLABLE_LINES
) return;
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 */
465 memset(s
->line
, 0, sizeof s
->line
);
466 strcpy(s
->line
, string
);
469 s
->len
= utf8length(s
->line
);
471 /* scroll bidirectional or forward only depending on the string width */
474 s
->bidir
= s
->len
< (LCD_WIDTH
- xmargin
) * (100 + bidir_limit
) / 100;
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
));
490 s
->startx
= xmargin
+ x
;
492 scrolling_lines
|= (1<<y
);
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
;
506 /* initialize scroll struct array */
512 for (index
= 0; index
< SCROLLABLE_LINES
; index
++)
515 if (!(scrolling_lines
&(1<<index
)))
521 if (TIME_BEFORE(current_tick
, s
->start_tick
))
530 ypos
= ymargin
+ index
;
532 if (s
->bidir
) /* scroll bidirectional */
536 /* at beginning of line */
539 s
->start_tick
= current_tick
+ scroll_delay
* 2;
541 if (s
->offset
>= s
->len
- (LCD_WIDTH
- xpos
))
544 s
->offset
= s
->len
- (LCD_WIDTH
- xpos
);
546 s
->start_tick
= current_tick
+ scroll_delay
* 2;
549 else /* scroll forward the whole time */
551 if (s
->offset
>= s
->len
)
554 lcd_putsxyofs(xpos
, ypos
, s
->offset
, s
->line
);
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
;