From 14296c3b6b0b36dbb2160d4aee5c644765243ac1 Mon Sep 17 00:00:00 2001 From: cowmonk Date: Wed, 22 Jan 2025 09:44:44 -0700 Subject: [PATCH] Unicode support! Took a lot of debugging... --- config.h | 6 +-- cowterm.c | 149 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 103 insertions(+), 52 deletions(-) diff --git a/config.h b/config.h index 8bff5b7..b94cb93 100644 --- a/config.h +++ b/config.h @@ -33,11 +33,11 @@ static const unsigned long int LIGHT_CYAN = 0x80FFFF; // "-bitstream-terminal-medium-r-normal--18-140-100-100-c-110-iso8859-1" // Use 'xlsfonts' command to list available fonts static const char REGULAR_FONT[] = - "-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1"; + "-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso10646-1"; static const char BOLD_FONT[] = - "-misc-fixed-bold-r-normal--13-120-75-75-c-70-iso8859-1"; + "-misc-fixed-bold-r-normal--13-120-75-75-c-70-iso10646-1"; static const char ITALICS_FONT[] = - "-misc-fixed-medium-o-normal--13-120-75-75-c-70-iso8859-1"; + "-misc-fixed-medium-o-normal--13-120-75-75-c-70-iso10646-1"; // Change the window name to whatever you want lmao static const char window_name[] = "CowTerm"; diff --git a/cowterm.c b/cowterm.c index fc84ae7..6fae07f 100644 --- a/cowterm.c +++ b/cowterm.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include "config.h" @@ -26,7 +28,7 @@ // General window/terminal stuff static pid_t child_pid; -static char **terminal_buffer = NULL; +static wchar_t **terminal_buffer = NULL; static unsigned long **color_buffer = NULL; static int **attr_buffer = NULL; static int term_rows; @@ -58,7 +60,7 @@ static unsigned long current_bg_color; static unsigned long **bg_color_buffer = NULL; // Alternatives to handle the ANSI escape codes -static char **saved_terminal = NULL; +static wchar_t **saved_terminal = NULL; static unsigned long **saved_colors = NULL; static int **saved_attrs = NULL; static int saved_cursor_x = 0; @@ -155,12 +157,12 @@ static void resize_buffers(int new_rows, int new_cols) { if (new_cols < 1) new_cols = 1; - size_t row_size = ((new_cols * sizeof(char) + 15) & ~15); + size_t row_size = ((new_cols * sizeof(wchar_t) + 15) & ~15); size_t color_row_size = ((new_cols * sizeof(unsigned long) + 15) & ~15); size_t attr_row_size = ((new_cols * sizeof(int) + 15) & ~15); size_t bg_color_row_size = ((new_cols * sizeof(unsigned long) + 15) & ~15); - char **new_term_buffer = malloc(new_rows * sizeof(char *)); + wchar_t **new_term_buffer = malloc(new_rows * sizeof(char *)); unsigned long **new_color_buffer = malloc(new_rows * sizeof(unsigned long *)); int **new_attr_buffer = malloc(new_rows * sizeof(int *)); unsigned long **new_bg_color_buffer = @@ -198,23 +200,22 @@ static void resize_buffers(int new_rows, int new_cols) { if (i < term_rows && i < new_rows && terminal_buffer && color_buffer && attr_buffer && bg_color_buffer) { int copy_cols = (new_cols < term_cols) ? new_cols : term_cols; - memcpy(new_term_buffer[i], terminal_buffer[i], copy_cols * sizeof(char)); + memcpy(new_term_buffer[i], terminal_buffer[i], + copy_cols * sizeof(wchar_t)); memcpy(new_color_buffer[i], color_buffer[i], copy_cols * sizeof(unsigned long)); memcpy(new_attr_buffer[i], attr_buffer[i], copy_cols * sizeof(int)); memcpy(new_bg_color_buffer[i], bg_color_buffer[i], copy_cols * sizeof(unsigned long)); - if (new_cols > term_cols) { - memset(new_term_buffer[i] + term_cols, ' ', new_cols - term_cols); - for (int x = term_cols; x < new_cols; x++) { - new_color_buffer[i][x] = current_color; - new_attr_buffer[i][x] = current_attr; - new_bg_color_buffer[i][x] = current_bg_color; - } + for (int x = term_cols; x < new_cols; x++) { + new_term_buffer[i][x] = L' '; + new_color_buffer[i][x] = current_color; + new_attr_buffer[i][x] = current_attr; + new_bg_color_buffer[i][x] = current_bg_color; } } else { - memset(new_term_buffer[i], ' ', new_cols); for (int x = 0; x < new_cols; x++) { + new_term_buffer[i][x] = L' '; new_color_buffer[i][x] = current_color; new_attr_buffer[i][x] = current_attr; new_bg_color_buffer[i][x] = current_bg_color; @@ -223,16 +224,32 @@ static void resize_buffers(int new_rows, int new_cols) { } // Free old buffers after successful allocation - if (terminal_buffer && color_buffer && attr_buffer && bg_color_buffer) { + if (terminal_buffer) { for (int i = 0; i < term_rows; i++) { - free(terminal_buffer[i]); - free(color_buffer[i]); - free(attr_buffer[i]); - free(bg_color_buffer[i]); + if (terminal_buffer[i]) + free(terminal_buffer[i]); } free(terminal_buffer); + } + if (color_buffer) { + for (int i = 0; i < term_rows; i++) { + if (color_buffer[i]) + free(color_buffer[i]); + } free(color_buffer); + } + if (attr_buffer) { + for (int i = 0; i < term_rows; i++) { + if (attr_buffer[i]) + free(attr_buffer[i]); + } free(attr_buffer); + } + if (bg_color_buffer) { + for (int i = 0; i < term_rows; i++) { + if (bg_color_buffer[i]) + free(bg_color_buffer[i]); + } free(bg_color_buffer); } @@ -305,9 +322,14 @@ static void draw_char(Display *display, Drawable d, int x, int y) { if (x < 0 || x >= term_cols || y < 0 || y >= term_rows) return; - char c = terminal_buffer[y][x]; - if (c < 32 || c > 126) // Only allow printable ASCII characters - return; + wchar_t c = terminal_buffer[y][x]; + char utf8_buf[8] = {0}; + if (c == 0 || c == L' ') { + utf8_buf[0] = ' '; + } else { + wchar_t wstr[2] = {c, 0}; + wcstombs(utf8_buf, wstr, sizeof(utf8_buf)); + } unsigned long fg = color_buffer[y][x]; unsigned long bg = bg_color_buffer[y][x]; @@ -333,7 +355,6 @@ static void draw_char(Display *display, Drawable d, int x, int y) { XSetForeground(display, gc, fg); - char str[2] = {c, '\0'}; XFontStruct *font = regular_font; if (attr & ATTR_BOLD) @@ -342,8 +363,8 @@ static void draw_char(Display *display, Drawable d, int x, int y) { font = italic_font; XSetFont(display, gc, font->fid); - XDrawString(display, d, gc, x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 4, str, - 1); + XDrawString(display, d, gc, x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 4, + utf8_buf, strlen(utf8_buf)); if (attr & ATTR_UNDERLINE) { XDrawLine(display, d, gc, x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 1, @@ -415,7 +436,8 @@ static void scroll_region_up(int amount) { return; for (int i = start; i <= end - amount; i++) { - memcpy(terminal_buffer[i], terminal_buffer[i + amount], term_cols); + memcpy(terminal_buffer[i], terminal_buffer[i + amount], + term_cols * sizeof(wchar_t)); memcpy(color_buffer[i], color_buffer[i + amount], term_cols * sizeof(unsigned long)); memcpy(attr_buffer[i], attr_buffer[i + amount], term_cols * sizeof(int)); @@ -424,8 +446,8 @@ static void scroll_region_up(int amount) { } for (int i = end - amount + 1; i <= end; i++) { - memset(terminal_buffer[i], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[i][x] = L' '; color_buffer[i][x] = current_color; attr_buffer[i][x] = current_attr; bg_color_buffer[i][x] = current_bg_color; @@ -441,7 +463,7 @@ static void scroll_region_down(int amount) { if (end >= term_rows) end = term_rows - 1; - char **tbuf = malloc(term_rows * sizeof(char *)); + wchar_t **tbuf = malloc(term_rows * sizeof(wchar_t *)); unsigned long **cbuf = malloc(term_rows * sizeof(unsigned long *)); int **abuf = malloc(term_rows * sizeof(int *)); unsigned long **bbuf = malloc(term_rows * sizeof(unsigned long *)); @@ -452,7 +474,7 @@ static void scroll_region_down(int amount) { } for (int i = 0; i < term_rows; i++) { - tbuf[i] = malloc(term_cols); + tbuf[i] = malloc(term_cols * sizeof(wchar_t)); cbuf[i] = malloc(term_cols * sizeof(unsigned long)); abuf[i] = malloc(term_cols * sizeof(int)); bbuf[i] = malloc(term_cols * sizeof(unsigned long)); @@ -465,21 +487,21 @@ static void scroll_region_down(int amount) { if (scroll_top == 0 && scroll_bottom == 0) { for (int i = 0; i < term_rows; i++) { - memcpy(tbuf[i], terminal_buffer[i], term_cols); + memcpy(tbuf[i], terminal_buffer[i], term_cols * sizeof(wchar_t)); memcpy(cbuf[i], color_buffer[i], term_cols * sizeof(unsigned long)); memcpy(abuf[i], attr_buffer[i], term_cols * sizeof(int)); memcpy(bbuf[i], bg_color_buffer[i], term_cols * sizeof(unsigned long)); } for (int j = 0; j < amount; j++) { for (int y = term_rows - 1; y > 0; y--) { - memcpy(terminal_buffer[y], tbuf[y - 1], term_cols); + memcpy(terminal_buffer[y], tbuf[y - 1], term_cols * sizeof(wchar_t)); memcpy(color_buffer[y], cbuf[y - 1], term_cols * sizeof(unsigned long)); memcpy(attr_buffer[y], abuf[y - 1], term_cols * sizeof(int)); memcpy(bg_color_buffer[y], bbuf[y - 1], term_cols * sizeof(unsigned long)); } - memset(terminal_buffer[0], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[0][x] = L' '; color_buffer[0][x] = current_color; attr_buffer[0][x] = current_attr; bg_color_buffer[0][x] = current_bg_color; @@ -487,21 +509,21 @@ static void scroll_region_down(int amount) { } } else { for (int i = 0; i < term_rows; i++) { - memcpy(tbuf[i], terminal_buffer[i], term_cols); + memcpy(tbuf[i], terminal_buffer[i], term_cols * sizeof(wchar_t)); memcpy(cbuf[i], color_buffer[i], term_cols * sizeof(unsigned long)); memcpy(abuf[i], attr_buffer[i], term_cols * sizeof(int)); memcpy(bbuf[i], bg_color_buffer[i], term_cols * sizeof(unsigned long)); } for (int j = 0; j < amount; j++) { for (int y = end; y > start; y--) { - memcpy(terminal_buffer[y], tbuf[y - 1], term_cols); + memcpy(terminal_buffer[y], tbuf[y - 1], term_cols * sizeof(wchar_t)); memcpy(color_buffer[y], cbuf[y - 1], term_cols * sizeof(unsigned long)); memcpy(attr_buffer[y], abuf[y - 1], term_cols * sizeof(int)); memcpy(bg_color_buffer[y], bbuf[y - 1], term_cols * sizeof(unsigned long)); } - memset(terminal_buffer[start], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[start][x] = L' '; color_buffer[start][x] = current_color; attr_buffer[start][x] = current_attr; bg_color_buffer[start][x] = current_bg_color; @@ -936,7 +958,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len, switch (params[0]) { case 0: // Clear from cursor to end of line for (int x = cursor_x; x < term_cols; x++) { - terminal_buffer[cursor_y][x] = ' '; + terminal_buffer[cursor_y][x] = L' '; color_buffer[cursor_y][x] = current_color; attr_buffer[cursor_y][x] = current_attr; bg_color_buffer[cursor_y][x] = current_bg_color; @@ -944,15 +966,15 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len, break; case 1: // Clear from cursor to beginning of line for (int x = 0; x <= cursor_x; x++) { - terminal_buffer[cursor_y][x] = ' '; + terminal_buffer[cursor_y][x] = L' '; color_buffer[cursor_y][x] = current_color; attr_buffer[cursor_y][x] = current_attr; bg_color_buffer[cursor_y][x] = current_bg_color; } break; case 2: // Clear entire line - memset(terminal_buffer[cursor_y], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[cursor_y][x] = L' '; color_buffer[cursor_y][x] = current_color; attr_buffer[cursor_y][x] = current_attr; bg_color_buffer[cursor_y][x] = current_bg_color; @@ -960,46 +982,45 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len, break; } needs_refresh = 1; + draw_terminal(display, window); break; case 'J': // Clear screen switch (params[0]) { case 0: // Clear from cursor to end of screen // Clear current line from cursor for (int x = cursor_x; x < term_cols; x++) { - terminal_buffer[cursor_y][x] = ' '; + terminal_buffer[cursor_y][x] = L' '; color_buffer[cursor_y][x] = current_color; attr_buffer[cursor_y][x] = current_attr; bg_color_buffer[cursor_y][x] = current_bg_color; } // Clear all lines below cursor for (int y = cursor_y + 1; y < term_rows; y++) { - memset(terminal_buffer[y], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[y][x] = L' '; color_buffer[y][x] = current_color; attr_buffer[y][x] = current_attr; bg_color_buffer[y][x] = current_bg_color; } } - needs_refresh = 1; break; case 1: // Clear from cursor to beginning of screen // Clear current line up to cursor for (int x = 0; x <= cursor_x; x++) { - terminal_buffer[cursor_y][x] = ' '; + terminal_buffer[cursor_y][x] = L' '; color_buffer[cursor_y][x] = current_color; attr_buffer[cursor_y][x] = current_attr; bg_color_buffer[cursor_y][x] = current_bg_color; } // Clear all lines above cursor for (int y = 0; y < cursor_y; y++) { - memset(terminal_buffer[y], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[y][x] = L' '; color_buffer[y][x] = current_color; attr_buffer[y][x] = current_attr; bg_color_buffer[y][x] = current_bg_color; } } - needs_refresh = 1; break; case 2: // Clear entire screen case 3: // Clear entire screen and scrollback (treat same as 2) @@ -1007,16 +1028,19 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len, current_bg_color = XBlackPixel(display, DefaultScreen(display)); current_attr = 0; for (int y = 0; y < term_rows; y++) { - memset(terminal_buffer[y], ' ', term_cols); for (int x = 0; x < term_cols; x++) { + terminal_buffer[y][x] = L' '; color_buffer[y][x] = current_color; attr_buffer[y][x] = current_attr; bg_color_buffer[y][x] = current_bg_color; } } - needs_refresh = 1; + cursor_x = 0; + cursor_y = 0; break; } + needs_refresh = 1; + draw_terminal(display, window); break; case 'r': // Set scrolling region if (param_idx == 0) { @@ -1194,7 +1218,7 @@ static int master_cb(int fd, void *data, Window window) { continue; } if ((buf[i] >= 32 && buf[i] <= 126) || buf[i] == '\n' || buf[i] == '\r' || - buf[i] == '\b' || buf[i] == '\t') { + buf[i] == '\b' || buf[i] == '\t' || (buf[i] & 0x80)) { switch (buf[i]) { case '\n': cursor_x = 0; @@ -1239,6 +1263,27 @@ static int master_cb(int fd, void *data, Window window) { cursor_y = term_rows - 1; } } + } else if (buf[i] & 0x80) { // UTF-8 + mbstate_t ps; + memset(&ps, 0, sizeof(ps)); + wchar_t wc; + size_t len = mbrtowc(&wc, buf + i, n - i, &ps); + + if (len != (size_t)-1 && len != (size_t)-2 && len != 0) { + terminal_buffer[cursor_y][cursor_x] = wc; + color_buffer[cursor_y][cursor_x] = current_color; + attr_buffer[cursor_y][cursor_x] = current_attr; + bg_color_buffer[cursor_y][cursor_x] = current_bg_color; + needs_refresh = 1; + if (++cursor_x >= term_cols) { + cursor_x = 0; + if (++cursor_y >= term_rows) { + scroll_region_up(1); + cursor_y = term_rows - 1; + } + } + i += len - 1; // -1 because loop will increment i + } } break; } @@ -1331,19 +1376,24 @@ int main(void) { gettimeofday(&last_blink, 0); gettimeofday(&last_refresh, 0); - if (!(terminal_buffer = calloc(term_rows, sizeof(char *))) || + if (!(terminal_buffer = calloc(term_rows, sizeof(wchar_t *))) || !(color_buffer = calloc(term_rows, sizeof(unsigned long *))) || !(attr_buffer = calloc(term_rows, sizeof(int *))) || !(bg_color_buffer = calloc(term_rows, sizeof(unsigned long *)))) return 1; for (int i = 0; i < term_rows; i++) { - if (!(terminal_buffer[i] = calloc(term_cols, sizeof(char))) || + if (!(terminal_buffer[i] = calloc(term_cols, sizeof(wchar_t))) || !(color_buffer[i] = calloc(term_cols, sizeof(unsigned long))) || !(attr_buffer[i] = calloc(term_cols, sizeof(int))) || !(bg_color_buffer[i] = calloc(term_cols, sizeof(unsigned long)))) return 1; - memset(terminal_buffer[i], ' ', term_cols); + for (int j = 0; j < term_cols; j++) { + terminal_buffer[i][j] = L' '; + color_buffer[i][j] = current_color; + attr_buffer[i][j] = current_attr; + bg_color_buffer[i][j] = current_bg_color; + } } if (!(display = XOpenDisplay(0))) @@ -1402,6 +1452,7 @@ int main(void) { if (slave > 2) close(slave); + setlocale(LC_ALL, ""); char *sh = getenv("SHELL"); #if COLOR_256_SUPPORT setenv("TERM", "xterm-256color", 1);