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 * 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 ****************************************************************************/
30 #include "string-extra.h"
35 #include "lcd-charcell.h"
36 #include "rbunicode.h"
37 #include "scroll_engine.h"
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
);
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
=
68 static struct viewport
* current_vp
= &default_vp
;
75 memset(lcd_patterns
, 0, sizeof(lcd_patterns
));
76 xspace
= find_xchar(' ');
77 memset(lcd_charbuffer
, xchar_info
[xspace
].hw_char
, sizeof(lcd_charbuffer
));
83 void lcd_set_viewport(struct viewport
* vp
)
86 current_vp
= &default_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
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)
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
);
113 void lcd_update_viewport(void)
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
);
142 /** low-level functions **/
144 static int find_xchar(unsigned long ucs
)
147 int high
= xchar_info_size
- 1;
151 int probe
= (low
+ high
) >> 1;
153 if (xchar_info
[probe
].ucs
< ucs
)
155 else if (xchar_info
[probe
].ucs
> ucs
)
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
)
170 for (i
= 0; i
< lcd_pattern_count
; i
++)
171 if (lcd_patterns
[i
].glyph
== glyph
)
177 static void lcd_free_pat(int pat
)
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
;
204 for (i
= 0; i
< lcd_pattern_count
; i
++)
206 if (++pat
>= lcd_pattern_count
) /* Keep 'pat' within limits */
209 if (lcd_patterns
[pat
].count
== 0)
214 if (lcd_patterns
[pat
].priority
< least_priority
)
216 least_priority
= lcd_patterns
[pat
].priority
;
220 if (xchar_info
[xchar
].priority
> least_priority
) /* prioritized char */
222 lcd_free_pat(least_pat
);
223 last_used_pat
= least_pat
;
229 static const unsigned char *glyph_to_pattern(unsigned glyph
)
232 return xfont_variable
[glyph
& 0x7fff];
234 return xfont_fixed
[glyph
];
237 static int map_xchar(int xchar
, unsigned char *substitute
)
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
;
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
),
261 lcd_patterns
[pat
].count
++; /* increase reference count */
262 *substitute
= xchar_info
[xchar
].hw_char
;
265 else /* hardware char */
266 return xchar_info
[xchar
].hw_char
;
269 static void lcd_putxchar(int x
, int y
, int xchar
)
273 /* Adjust for viewport */
277 #if defined(HAVE_VIEWPORT_CLIP)
278 if((unsigned)x
> (unsigned)LCD_WIDTH
|| (unsigned)y
> (unsigned)LCD_HEIGHT
)
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)
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 */
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
;
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)
340 struct viewport
* old_vp
= current_vp
;
342 lcd_scroll_info
.lines
= 0;
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
);
355 /* Clear the current viewport */
356 void lcd_clear_viewport(void)
360 if (current_vp
== &default_vp
)
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
))
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
)
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
||
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
)
427 const unsigned char *utf8
= str
;
429 while (*utf8
&& x
< current_vp
->width
)
431 utf8
= utf8decode(utf8
, &ucs
);
438 lcd_putxchar(x
++, y
, find_xchar(ucs
));
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
)
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
, ...)
466 vsnprintf(buf
, sizeof (buf
), fmt
, ap
);
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
)
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
);
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
,
494 struct scrollinfo
* s
;
497 if ((unsigned)y
>= (unsigned)current_vp
->height
)
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 */
517 memset(s
->line
, 0, sizeof s
->line
);
518 strcpy(s
->line
, string
);
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;
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
));
547 lcd_scroll_info
.lines
++;
551 void lcd_scroll_fn(void)
553 struct scrollinfo
* s
;
557 struct viewport
* old_vp
= current_vp
;
560 for ( index
= 0; index
< lcd_scroll_info
.lines
; index
++ ) {
561 s
= &lcd_scroll_info
.scroll
[index
];
564 if (TIME_BEFORE(current_tick
, s
->start_tick
))
567 lcd_set_viewport(s
->vp
);
577 if (s
->bidir
) /* scroll bidirectional */
579 if (s
->offset
<= 0) {
580 /* at beginning of line */
583 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
* 2;
585 if (s
->offset
>= s
->len
- (current_vp
->width
- xpos
)) {
587 s
->offset
= s
->len
- (current_vp
->width
- xpos
);
589 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
* 2;
592 else /* scroll forward the whole time */
594 if (s
->offset
>= s
->len
)
598 lcd_putsxyofs(xpos
, ypos
, s
->offset
, s
->line
);
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
;