ANSI Escape Code Handling

Better handling of ANSI Escape Code Handling
This commit is contained in:
cowmonk 2025-01-21 15:56:56 -07:00
parent a044864f92
commit 264f53a342

237
cowterm.c
View File

@ -365,33 +365,14 @@ static void draw_terminal(Display *display, Window window) {
if (!terminal_buffer || !color_buffer || !attr_buffer)
return;
if (buffer_pixmap == None)
buffer_pixmap = XCreatePixmap(
display, DefaultRootWindow(display), term_cols * CHAR_WIDTH,
term_rows * CHAR_HEIGHT, DefaultDepth(display, DefaultScreen(display)));
XSetForeground(display, gc, current_bg_color);
XFillRectangle(display, buffer_pixmap, gc, 0, 0, term_cols * CHAR_WIDTH,
term_rows * CHAR_HEIGHT);
if (needs_refresh) {
int start_y = scroll_top ? scroll_top - 1 : 0;
int end_y = scroll_bottom ? scroll_bottom : term_rows;
if (end_y > term_rows) {
end_y = term_rows;
}
if (scroll_top == 0 && scroll_bottom == 0) {
// Full screen refresh
for (int y = 0; y < term_rows; y++) {
for (int x = 0; x < term_cols; x++) {
draw_char(display, buffer_pixmap, x, y);
}
}
} else {
// Only refresh scroll region
for (int y = start_y; y < end_y; y++) {
for (int x = 0; x < term_cols; x++) {
draw_char(display, buffer_pixmap, x, y);
}
}
// Draw all characters
for (int y = 0; y < term_rows; y++) {
for (int x = 0; x < term_cols; x++) {
draw_char(display, buffer_pixmap, x, y);
}
}
@ -745,6 +726,19 @@ static unsigned long get_bright_ansi_color(int index) {
// P.S. yes I had to google this up
static void parse_ansi_code(const char *buf, int *idx, int max_len,
Window window) {
if (*idx + 1 < max_len) {
// Skip CSI sequences that we don't need to process
if (buf[*idx] == '\033' && buf[*idx + 1] == '(') {
*idx += 2;
return;
}
// Skip other common terminal sequences
if (buf[*idx] == '\033' && strchr("=>", buf[*idx + 1])) {
*idx += 2;
return;
}
}
char num_buf[32] = {0};
int params[32] = {0};
size_t num_idx = 0;
@ -911,15 +905,76 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len,
}
} break;
case 'K': // Clear line
for (int i = cursor_x; i < term_cols; i++) {
terminal_buffer[cursor_y][i] = ' ';
color_buffer[cursor_y][i] = current_color;
attr_buffer[cursor_y][i] = current_attr;
bg_color_buffer[cursor_y][i] = current_bg_color;
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] = ' ';
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 1: // Clear from cursor to beginning of line
for (int x = 0; x <= cursor_x; x++) {
terminal_buffer[cursor_y][x] = ' ';
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++) {
color_buffer[cursor_y][x] = current_color;
attr_buffer[cursor_y][x] = current_attr;
bg_color_buffer[cursor_y][x] = current_bg_color;
}
break;
}
needs_refresh = 1;
break;
case 'J': // Clear screen
if (params[0] == 2) {
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] = ' ';
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++) {
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] = ' ';
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++) {
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)
current_color = XWhitePixel(display, DefaultScreen(display));
current_bg_color = XBlackPixel(display, DefaultScreen(display));
current_attr = 0;
@ -932,6 +987,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len,
}
}
needs_refresh = 1;
break;
}
break;
case 'r': // Set scrolling region
@ -1051,56 +1107,81 @@ static int master_cb(int fd, void *data, Window window) {
if (n <= 0)
return 1;
for (int i = 0; i < n; i++) {
if (buf[i] == '\033' && i + 1 < n && buf[i + 1] == '[') {
parse_ansi_code(buf, &i, n, window);
continue;
}
switch (buf[i]) {
case '\n':
cursor_x = 0;
if (++cursor_y >= term_rows) {
scroll_region_up(1);
cursor_y = term_rows - 1;
if (buf[i] == '\033') {
if (i + 1 < n && buf[i + 1] == '[') {
parse_ansi_code(buf, &i, n, window);
continue;
}
needs_refresh = 1;
break;
case '\r':
cursor_x = 0;
break;
case '\b':
if (cursor_x > 0) {
cursor_x--;
terminal_buffer[cursor_y][cursor_x] = ' ';
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;
} else if (cursor_y > 0) {
cursor_y--;
cursor_x = term_cols - 1;
while (cursor_x > 0 && terminal_buffer[cursor_y][cursor_x - 1] == ' ')
cursor_x--;
needs_refresh = 1;
if (i + 1 < n && (buf[i + 1] == '(' || buf[i + 1] == ')')) {
if (i + 2 < n) {
i += 2; // Skip ESC, (, and the character set identifier
}
continue;
}
break;
default:
if (cursor_y >= term_rows || cursor_x >= term_cols)
break;
if (buf[i] >= 32 && buf[i] <= 126) {
terminal_buffer[cursor_y][cursor_x] = buf[i];
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;
}
// Handle other simple escape sequences
if (i + 1 < n) {
switch (buf[i + 1]) {
case '=': // Application keypad
case '>': // Normal keypad
case '7': // Save cursor position
case '8': // Restore cursor position
case 'E': // Next line
case 'M': // Reverse line feed
i++; // Skip the command character
continue;
}
}
break;
continue;
}
if ((buf[i] >= 32 && buf[i] <= 126) || buf[i] == '\n' || buf[i] == '\r' ||
buf[i] == '\b' || buf[i] == '\t') {
switch (buf[i]) {
case '\n':
cursor_x = 0;
if (++cursor_y >= term_rows) {
scroll_region_up(1);
cursor_y = term_rows - 1;
}
needs_refresh = 1;
break;
case '\r':
cursor_x = 0;
break;
case '\b':
if (cursor_x > 0) {
cursor_x--;
terminal_buffer[cursor_y][cursor_x] = ' ';
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;
} else if (cursor_y > 0) {
cursor_y--;
cursor_x = term_cols - 1;
while (cursor_x > 0 && terminal_buffer[cursor_y][cursor_x - 1] == ' ')
cursor_x--;
needs_refresh = 1;
}
break;
default:
if (cursor_y >= term_rows || cursor_x >= term_cols)
break;
if ((buf[i] >= 32 && buf[i] <= 126) || buf[i] == '\t') {
terminal_buffer[cursor_y][cursor_x] = buf[i];
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;
}
}
}
break;
}
}
}
cursor_visible = 1;