@ -1,14 +1,15 @@
/**
/**
* TODO file description
* HD44780 utf8 - capable display buffer
*/
*/
# include <string.h>
# include <string.h>
# include <assert.h>
# include <assert.h>
//#include <stdio.h>
# include "lcdbuf.h"
# include "lcdbuf.h"
/** Initialize the struct */
/** Initialize the struct */
void LcdBuffer_Init ( struct LcdBuffer * self , const struct cgrom_e ntry * cgrom , const struct cgram_pattern * custom_symbols )
void LcdBuffer_Init ( struct LcdBuffer * self , const struct LcdBuf_CGROM_E ntry * cgrom , const struct LcdBuf_CGRAM_Symbol * custom_symbols )
{
{
assert ( self ) ;
assert ( self ) ;
assert ( cgrom ) ;
assert ( cgrom ) ;
@ -25,15 +26,17 @@ void LcdBuffer_Clear(struct LcdBuffer *self)
assert ( self ) ;
assert ( self ) ;
memset ( self - > cgram , 0 , sizeof ( self - > cgram ) ) ;
memset ( self - > cgram , 0 , sizeof ( self - > cgram ) ) ;
memset ( self - > screen , 32 , sizeof ( self - > screen ) ) ;
memset ( self - > screen , ' ' , sizeof ( self - > screen ) ) ;
memset ( self - > dirty_extents , 0 , sizeof ( self - > dirty_extents ) ) ;
memset ( self - > dirty_extents , 0 , sizeof ( self - > dirty_extents ) ) ;
self - > full_repaint_required = false ;
// everything must be written, who knows what was left on the display before!
self - > full_repaint_required = true ;
}
}
/** Write what needs to be written to the HW, clear all dirty marks */
/** Write what needs to be written to the HW, clear all dirty marks */
void LcdBuffer_Flush ( struct LcdBuffer * self )
void LcdBuffer_Flush ( struct LcdBuffer * self )
{
{
for ( int i = 0 ; i < 8 ; i + + ) {
for ( int i = 0 ; i < LCDBUF_CGRAM_CAPACITY ; i + + ) {
if ( self - > cgram [ i ] . refcount > 0 & & self - > cgram [ i ] . dirty ) {
if ( self - > cgram [ i ] . refcount > 0 & & self - > cgram [ i ] . dirty ) {
LcdBuffer_IO_WriteCGRAM ( i , self - > custom_symbols [ self - > cgram [ i ] . symbol_index ] . data ) ;
LcdBuffer_IO_WriteCGRAM ( i , self - > custom_symbols [ self - > cgram [ i ] . symbol_index ] . data ) ;
self - > cgram [ i ] . dirty = false ;
self - > cgram [ i ] . dirty = false ;
@ -41,13 +44,29 @@ void LcdBuffer_Flush(struct LcdBuffer *self)
}
}
if ( self - > full_repaint_required ) {
if ( self - > full_repaint_required ) {
self - > full_repaint_required = false ;
// Check if we have anything on the display - if not, just clear screen
bool any_nonspace = false ;
for ( int r = 0 ; ! any_nonspace & & r < LINE_NUM ; r + + ) {
for ( int c = 0 ; ! any_nonspace & & c < LINE_LEN ; c + + ) {
if ( self - > screen [ r ] [ c ] ! = ' ' ) {
any_nonspace = true ;
}
}
}
if ( ! any_nonspace ) {
LcdBuffer_IO_Clear ( ) ;
return ;
}
for ( int r = 0 ; r < LINE_NUM ; r + + ) {
for ( int r = 0 ; r < LINE_NUM ; r + + ) {
LcdBuffer_IO_WriteAt ( r , 0 , self - > screen [ r ] , LINE_LEN ) ;
LcdBuffer_IO_WriteAt ( r , 0 , self - > screen [ r ] , LINE_LEN ) ;
}
}
memset ( self - > dirty_extents , 0 , sizeof ( self - > dirty_extents ) ) ;
memset ( self - > dirty_extents , 0 , sizeof ( self - > dirty_extents ) ) ;
} else {
} else {
for ( int e = 0 ; e < BUFLEN_DIRTY_LIST ; e + + ) {
for ( int e = 0 ; e < LCD BUF_DIRTY_LIST_LEN ; e + + ) {
struct DirtyExtent * ext = & self - > dirty_extents [ e ] ;
struct LcdBuf_ DirtyExtent * ext = & self - > dirty_extents [ e ] ;
if ( ! ext - > count ) {
if ( ! ext - > count ) {
continue ;
continue ;
}
}
@ -60,7 +79,7 @@ void LcdBuffer_Flush(struct LcdBuffer *self)
/** Fully write everything to the display */
/** Fully write everything to the display */
void LcdBuffer_FlushAll ( struct LcdBuffer * self )
void LcdBuffer_FlushAll ( struct LcdBuffer * self )
{
{
for ( int i = 0 ; i < 8 ; i + + ) {
for ( int i = 0 ; i < LCDBUF_CGRAM_CAPACITY ; i + + ) {
if ( self - > cgram [ i ] . refcount > 0 ) {
if ( self - > cgram [ i ] . refcount > 0 ) {
LcdBuffer_IO_WriteCGRAM ( i , self - > custom_symbols [ self - > cgram [ i ] . symbol_index ] . data ) ;
LcdBuffer_IO_WriteCGRAM ( i , self - > custom_symbols [ self - > cgram [ i ] . symbol_index ] . data ) ;
}
}
@ -75,11 +94,27 @@ void LcdBuffer_FlushAll(struct LcdBuffer *self)
self - > full_repaint_required = false ;
self - > full_repaint_required = false ;
}
}
static void mark_dirty ( struct LcdBuffer * self , uint8_t row , uint8_t col )
//static void show_dirty_slots(const struct LcdBuffer *self) {
// printf("\n\n");
// for (int i = 0; i < LCDBUF_DIRTY_LIST_LEN; i++) {
// if (self->dirty_extents[i].count == 0) {
// continue;
// }
// printf("dirty_extent(%d): col %d, row %d, len %d\n", i, self->dirty_extents[i].row, self->dirty_extents[i].col, self->dirty_extents[i].count);
// }
// printf("\n");
//}
static void mark_dirty ( struct LcdBuffer * self , lcdbuf_pos_t row , lcdbuf_pos_t col )
{
{
// partial updates are not needed if everything will be written anyway
if ( self - > full_repaint_required ) {
return ;
}
int first_empty_extent_slot = - 1 ;
int first_empty_extent_slot = - 1 ;
for ( int i = 0 ; i < BUFLEN_DIRTY_LIST ; i + + ) {
for ( int i = 0 ; i < LCD BUF_DIRTY_LIST_LEN ; i + + ) {
struct DirtyExtent * ext = & self - > dirty_extents [ i ] ;
struct LcdBuf_ DirtyExtent * ext = & self - > dirty_extents [ i ] ;
if ( ext - > count = = 0 ) {
if ( ext - > count = = 0 ) {
// unused
// unused
if ( first_empty_extent_slot = = - 1 ) {
if ( first_empty_extent_slot = = - 1 ) {
@ -94,60 +129,63 @@ static void mark_dirty(struct LcdBuffer *self, uint8_t row, uint8_t col)
// this is a filled extent
// this is a filled extent
if ( ext - > col < col & & ext - > col + ext - > count > col ) {
if ( ( ext - > col < = col ) & & ( ext - > col + ext - > count > col ) ) {
// already in this extent
// already in this extent
// show_dirty_slots(self);
return ;
return ;
}
}
if ( col < ext - > col & & ( ext - > col - col ) < = 5 ) {
if ( ( col < ext - > col ) & & ( ( ext - > col - col ) < = LCDBUF_DIRTY_EXTEND ) ) {
ext - > count + = ( ext - > col - col ) ;
ext - > count + = ext - > col - col ;
ext - > col = col ;
ext - > col = col ;
// show_dirty_slots(self);
return ;
return ;
}
}
if ( col > = ext - > col + ext - > count & & ( col - ext - > col + ext - > count ) < = 5 ) {
if ( ( col > = ext - > col + ext - > count ) & & ( ( col - ( ext - > col + ext - > count ) ) < = LCDBUF_DIRTY_EXTEND ) ) {
ext - > count + = ( col - ext - > col + ext - > count ) ;
ext - > count + = col - ( ext - > col + ext - > count ) + 1 ;
// show_dirty_slots(self);
return ;
return ;
}
}
}
}
if ( first_empty_extent_slot = = - 1 ) {
if ( first_empty_extent_slot = = - 1 ) {
// printf("Give up on dirty extents\n");
self - > full_repaint_required = true ;
self - > full_repaint_required = true ;
} else {
} else {
// printf("New dirty extent: #%d\n", first_empty_extent_slot);
self - > dirty_extents [ first_empty_extent_slot ] . col = col ;
self - > dirty_extents [ first_empty_extent_slot ] . col = col ;
self - > dirty_extents [ first_empty_extent_slot ] . row = row ;
self - > dirty_extents [ first_empty_extent_slot ] . row = row ;
self - > dirty_extents [ first_empty_extent_slot ] . count = 1 ;
self - > dirty_extents [ first_empty_extent_slot ] . count = 1 ;
}
}
// show_dirty_slots(self);
}
}
/** Set one utf8 character at a position */
/** Set one utf8 character at a position */
void LcdBuffer_Set ( struct LcdBuffer * self , uint8_t row , uint8 _t col , struct Utf8Char ch )
void LcdBuffer_Set ( struct LcdBuffer * self , lcdbuf_pos_t row , lcdbuf_pos _t col , struct Utf8Char ch )
{
{
// printf("set %d:%d\n", row, col);
assert ( self ) ;
assert ( self ) ;
assert ( row < LINE_NUM ) ;
assert ( row < LINE_NUM ) ;
assert ( col < LINE_LEN ) ;
assert ( col < LINE_LEN ) ;
uint8_t oldchar = self - > screen [ row ] [ col ] ;
const uint8_t oldchar = self - > screen [ row ] [ col ] ;
if ( oldchar > = 8 & & oldchar = = ch . uint ) {
if ( oldchar > = LCDBUF_CGRAM_CAPACITY & & oldchar = = ch . uint ) {
// No change
// No change
return ;
return ;
}
}
// Fast path for standard ASCII
// Fast path for standard ASCII - assuming this extent of the table is always the same!
if ( ch . uint > = 32 & & ch . uint < 126 & & ch . uint ! = ' \\ ' ) { // A00 has YEN in place of BACKSLASH
if ( ch . uint > = 32 & & ch . uint < 126 & & ch . uint ! = ' \\ ' ) { // A00 has YEN in place of BACKSLASH
// normal ASCII
// normal ASCII
if ( oldchar < 8 ) {
// release refcount on the CGRAM cell
self - > cgram [ oldchar ] . refcount - = 1 ;
}
self - > screen [ row ] [ col ] = ch . uint ;
self - > screen [ row ] [ col ] = ch . uint ;
goto done_dirty ;
goto done_dirty ;
}
}
// Find if it's in CGROM
// Find if it's in CGROM
const struct cgrom_e ntry * rom = self - > cgrom ;
const struct LcdBuf_CGROM_E ntry * rom = self - > cgrom ;
for ( ; ; ) {
for ( ; ; ) {
if ( rom - > symbol . uint = = 0 ) {
if ( rom - > symbol . uint = = 0 ) {
// End of the lookup table
// End of the lookup table
@ -156,11 +194,6 @@ void LcdBuffer_Set(struct LcdBuffer *self, uint8_t row, uint8_t col, struct Utf8
if ( rom - > symbol . uint = = ch . uint ) {
if ( rom - > symbol . uint = = ch . uint ) {
// found it!
// found it!
if ( oldchar < 8 ) {
// release refcount on the CGRAM cell
self - > cgram [ oldchar ] . refcount - = 1 ;
}
self - > screen [ row ] [ col ] = rom - > address ;
self - > screen [ row ] [ col ] = rom - > address ;
goto done_dirty ;
goto done_dirty ;
}
}
@ -170,37 +203,27 @@ void LcdBuffer_Set(struct LcdBuffer *self, uint8_t row, uint8_t col, struct Utf8
// Check if the same custom char is already used - if so, increment refcount and reuse it
// Check if the same custom char is already used - if so, increment refcount and reuse it
int first_empty_custom_slot = - 1 ;
int first_empty_custom_slot = - 1 ;
for ( int i = 0 ; i < 8 ; i + + ) {
for ( uint8_t slot = 0 ; slot < LCDBUF_CGRAM_CAPACITY ; slot + + ) {
if ( self - > cgram [ i ] . refcount > 0 ) {
if ( self - > cgram [ slot ] . refcount > 0 ) {
if ( self - > cgram [ i ] . uint = = ch . uint ) {
if ( self - > cgram [ slot ] . uint = = ch . uint ) {
if ( oldchar = = i ) {
if ( oldchar = = slot ) {
// No change, was already the same custom
// No change, was already the same custom
return ;
return ;
}
}
if ( oldchar < 8 ) {
self - > cgram [ slot ] . refcount + + ;
// release refcount on the CGRAM cell
self - > screen [ row ] [ col ] = slot ;
self - > cgram [ oldchar ] . refcount - = 1 ;
}
self - > cgram [ i ] . refcount + = 1 ;
self - > screen [ row ] [ col ] = i ;
goto done_dirty ;
goto done_dirty ;
}
}
} else if ( first_empty_custom_slot = = - 1 ) {
} else if ( first_empty_custom_slot = = - 1 ) {
first_empty_custom_slot = i ;
first_empty_custom_slot = slot ;
}
}
}
}
// New custom pattern is needed
// New custom pattern is needed
if ( oldchar < 8 ) {
uint16_t index = 0 ;
// release refcount on the CGRAM cell
const struct LcdBuf_CGRAM_Symbol * pattern = self - > custom_symbols ;
self - > cgram [ oldchar ] . refcount - = 1 ;
}
uint32_t index = 0 ;
const struct cgram_pattern * pattern = self - > custom_symbols ;
for ( ; ; ) {
for ( ; ; ) {
if ( pattern - > symbol . uint = = 0 ) {
if ( pattern - > symbol . uint = = 0 ) {
// End of the lookup table
// End of the lookup table
@ -212,8 +235,14 @@ void LcdBuffer_Set(struct LcdBuffer *self, uint8_t row, uint8_t col, struct Utf8
if ( first_empty_custom_slot = = - 1 ) {
if ( first_empty_custom_slot = = - 1 ) {
// Whoops, out of slots. Show a fallback glyph
// Whoops, out of slots. Show a fallback glyph
if ( oldchar ! = pattern - > fallback ) {
if ( pattern - > fallback ! = 0 ) {
self - > screen [ row ] [ col ] = pattern - > fallback ;
if ( oldchar ! = pattern - > fallback ) {
self - > screen [ row ] [ col ] = pattern - > fallback ;
goto done_dirty ;
}
} else {
// TODO kick out some other CGRAM entry that has fallback?
self - > screen [ row ] [ col ] = LCDBUF_SUBSTITUTION_CHAR ;
goto done_dirty ;
goto done_dirty ;
}
}
return ;
return ;
@ -234,14 +263,20 @@ void LcdBuffer_Set(struct LcdBuffer *self, uint8_t row, uint8_t col, struct Utf8
}
}
// Fallback, no way to show this glyph
// Fallback, no way to show this glyph
self - > screen [ row ] [ col ] = ' ? ' ;
self - > screen [ row ] [ col ] = LCDBUF_SUBSTITUTION_CHAR ;
done_dirty :
done_dirty :
if ( oldchar < LCDBUF_CGRAM_CAPACITY ) {
// release refcount on the CGRAM cell
assert ( self - > cgram [ oldchar ] . refcount > 0 ) ;
self - > cgram [ oldchar ] . refcount - - ;
}
mark_dirty ( self , row , col ) ;
mark_dirty ( self , row , col ) ;
}
}
/** Write a UTF8 string at a position */
/** Write a UTF8 string at a position */
void LcdBuffer_Write ( struct LcdBuffer * self , uint8_t row , uint8_t col , char * utf_string )
void LcdBuffer_Write ( struct LcdBuffer * self , lcdbuf_pos_t row , lcdbuf_pos _t col , char * utf_string )
{
{
struct Utf8Iterator iter ;
struct Utf8Iterator iter ;
Utf8Iterator_Init ( & iter , utf_string ) ;
Utf8Iterator_Init ( & iter , utf_string ) ;