Skip to content

Commit

Permalink
terminal: (wip) use raster of code points
Browse files Browse the repository at this point in the history
  • Loading branch information
vroland committed May 5, 2020
1 parent 13002cc commit 43cc17e
Showing 1 changed file with 183 additions and 33 deletions.
216 changes: 183 additions & 33 deletions examples/terminal/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,156 @@ int log_to_uart(const char* fmt, va_list args) {
return result;
}

typedef struct {
char mask; /* char data will be bitwise AND with this */
char lead; /* start bytes of current char in utf-8 encoded character */
uint32_t beg; /* beginning of codepoint range */
uint32_t end; /* end of codepoint range */
int bits_stored; /* the number of bits from the codepoint that fits in char */
}utf_t;

static utf_t * utf[] = {
/* mask lead beg end bits */
[0] = &(utf_t){0b00111111, 0b10000000, 0, 0, 6 },
[1] = &(utf_t){0b01111111, 0b00000000, 0000, 0177, 7 },
[2] = &(utf_t){0b00011111, 0b11000000, 0200, 03777, 5 },
[3] = &(utf_t){0b00001111, 0b11100000, 04000, 0177777, 4 },
[4] = &(utf_t){0b00000111, 0b11110000, 0200000, 04177777, 3 },
&(utf_t){0},
};

int codepoint_len(const uint32_t cp)
{
int len = 0;
for(utf_t **u = utf; *u; ++u) {
if((cp >= (*u)->beg) && (cp <= (*u)->end)) {
break;
}
++len;
}
if(len > 4) /* Out of bounds */
exit(1);

return len;
}

void to_utf8(char chr[5], const uint32_t cp)
{
const int bytes = codepoint_len(cp);

int shift = utf[0]->bits_stored * (bytes - 1);
chr[0] = (cp >> shift & utf[bytes]->mask) | utf[bytes]->lead;
shift -= utf[0]->bits_stored;
for(int i = 1; i < bytes; ++i) {
chr[i] = (cp >> shift & utf[0]->mask) | utf[0]->lead;
shift -= utf[0]->bits_stored;
}
chr[bytes] = '\0';
}

static int utf8_len(uint8_t ch)
{
int len = 0;
for(utf_t **u = utf; *u; ++u) {
if((ch & ~(*u)->mask) == (*u)->lead) {
break;
}
++len;
}
if(len > 4) { /* Malformed leading byte */
exit(1);
}
return len;
}

uint32_t to_cp(const char chr[4])
{
int bytes = utf8_len(*chr);
int shift = utf[0]->bits_stored * (bytes - 1);
uint32_t codep = (*chr++ & utf[bytes]->mask) << shift;

for(int i = 1; i < bytes; ++i, ++chr) {
shift -= utf[0]->bits_stored;
codep |= ((char)*chr & utf[0]->mask) << shift;
}

return codep;
}



// inspired by the st - the simple terminal, suckless.org

// A line is a sequence of code points.
typedef uint32_t* Line;

typedef struct {
int x;
int y;
} Cursor;

typedef struct {
/// Number of rows.
int row;
/// Number of columns.
int col;
Line* line;
Cursor cursor;
} Term;


static uint8_t uart_str_buffer[BUF_SIZE];
static uint8_t* uart_buffer_end = uart_str_buffer;
static uint8_t* uart_buffer_start = uart_str_buffer;
uint32_t read_char() {
int remaining = uart_buffer_end - uart_buffer_start;
if (uart_buffer_start >= uart_buffer_end
|| utf8_len(*uart_buffer_start) > remaining) {

memmove(uart_str_buffer, uart_buffer_start, remaining);
uart_buffer_start = uart_str_buffer;
uart_buffer_end = uart_buffer_start + remaining;
int unfilled = uart_str_buffer + BUF_SIZE - uart_buffer_end;
int len = uart_read_bytes(UART_NUM_1, uart_buffer_end, unfilled, 20 / portTICK_RATE_MS);
uart_buffer_end += len;
if (len < 0) {
ESP_LOGE("terminal", "uart read error");
return 0;
} else if (len == 0) {
return 0;
}
remaining = uart_buffer_end - uart_buffer_start;
}

int bytes = utf8_len(*uart_buffer_start);
if (remaining < bytes) {
return 0;
}
int shift = utf[0]->bits_stored * (bytes - 1);
uint32_t codep = (*uart_buffer_start++ & utf[bytes]->mask) << shift;

for (int i = 1; i < bytes; ++i, ++uart_buffer_start) {
shift -= utf[0]->bits_stored;
codep |= ((uint8_t)*uart_buffer_start & utf[0]->mask) << shift;
}
return codep;
}

int calculate_horizontal_advance(GFXfont* font, Line line, int col) {
int total = 0;
for (int i = 0; i < col; i++) {
int cp = line[i];
GFXglyph* glyph;
get_glyph(font, cp, &glyph);

if (!glyph) {
ESP_LOGW("terminal", "no glyph for %d", cp);
}
total += glyph->advance_x;
}
return total;
}

void epd_task() {
epd_init();
delay(300);
Expand All @@ -66,55 +216,55 @@ void epd_task() {
// Still log to the serial output
esp_log_set_vprintf(log_to_uart);

// Configure a temporary buffer for the incoming data
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);

uint8_t *framebuffer = (uint8_t *)heap_caps_malloc(EPD_WIDTH * EPD_HEIGHT / 2, MALLOC_CAP_SPIRAM);
memset(framebuffer, 0xFF, EPD_WIDTH * EPD_HEIGHT / 2);

delay(1000);

uart_write_bytes(UART_NUM_1, "listening\n", 11);

int cur_x = 100;
int line_start_x = 50;
int cur_x = line_start_x;
int cur_y = 100;

uint8_t current_string[256] = {0};
uint8_t* current_string_ptr = current_string;
uint32_t current_string[256] = {0};
int current_str_index = 0;

while (true) {
// Read data from the UART
int len = uart_read_bytes(UART_NUM_1, data, BUF_SIZE - 1, 20 / portTICK_RATE_MS);
data[len] = 0;

uint32_t chr = read_char();
if (chr > 0) {
ESP_LOGI("terminal", "read char %d", chr);
}


char data[5];
to_utf8(data, chr);

// FIXME: handle control characters in mid-stream
if (data[0] == '\b') {
uint8_t skipped = 0;
// skip a utf8 code point backwards
do {
current_string_ptr--;
skipped++;
} while ((*current_string_ptr & 0xC0) == 0x80);

int tmp_cur_x = 0;
int tmp_cur_y = 0;
int x, y, w, h;
get_text_bounds((GFXfont *) &FiraSans, (char *) current_string_ptr, &tmp_cur_x, &tmp_cur_y, &x, &y, &w, &h);

int new_cur_x = cur_x - tmp_cur_x;
int new_cur_y = cur_y - tmp_cur_y;
cur_x = new_cur_x;
cur_y = new_cur_y;
if (chr == '\b') {
current_str_index--;
char old_data[5];
to_utf8(old_data, current_string[current_str_index]);
current_string[current_str_index] = 0;

epd_poweron();
write_mode((GFXfont *) &FiraSans, (char *) current_string_ptr, &new_cur_x, &new_cur_y, NULL, WHITE_ON_WHITE);
epd_poweroff();
int new_horizontal_advance = calculate_horizontal_advance((GFXfont *) &FiraSans, current_string, current_str_index);
int new_cur_x = new_horizontal_advance + line_start_x;

memset(current_string_ptr, 0, skipped);
// we assume horizontal scripts
cur_x = new_cur_x;

} else if (len > 0) {
strncpy((char*)current_string_ptr, (char*)data, len);
current_string_ptr += len;
epd_poweron();
write_mode((GFXfont *) &FiraSans, (char *) old_data, &new_cur_x, &cur_y, NULL, WHITE_ON_WHITE);
epd_poweroff();
} else if (chr == '\r') {
cur_x = 100;
} else if (chr == '\n') {
cur_y += ((GFXfont *)&FiraSans)->advance_y;
} else if (chr > 0) {
current_string[current_str_index] = chr;
current_str_index++;

uint32_t t1 = millis();
int tmp_cur_x = cur_x;
Expand All @@ -127,7 +277,7 @@ void epd_task() {

uint32_t t2 = millis();
ESP_LOGI("main", "overall rendering took %dms.\n", t2 - t1);
uart_write_bytes(UART_NUM_1, (const char *) data, len);
uart_write_bytes(UART_NUM_1, (const char *) data, 1);
}
}
free(framebuffer);
Expand Down

0 comments on commit 43cc17e

Please sign in to comment.