Unicode support!

Took a lot of debugging...
This commit is contained in:
cowmonk 2025-01-22 09:44:44 -07:00
parent 63522a14fc
commit 14296c3b6b
2 changed files with 103 additions and 52 deletions

View File

@ -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";

149
cowterm.c
View File

@ -4,6 +4,7 @@
#include <X11/keysym.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <pty.h>
#include <signal.h>
#include <stdio.h>
@ -15,6 +16,7 @@
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <wchar.h>
#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);