Alternative buffering and ANSI Escape Codes
This kind helped with stabilizing vim and htop, still gotta fix them tho.
This commit is contained in:
parent
a04a1870cc
commit
f3b05c3929
@ -11,7 +11,7 @@ INCS = -I/usr/X11R6/include -I${FREETYPEINC}
|
|||||||
LIBS = -L/usr/X11R6/lib -lX11 -lutil ${FREETYPELIBS}
|
LIBS = -L/usr/X11R6/lib -lX11 -lutil ${FREETYPELIBS}
|
||||||
|
|
||||||
# flags
|
# flags
|
||||||
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700
|
CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700
|
||||||
CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${INCS} ${CPPFLAGS}
|
CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${INCS} ${CPPFLAGS}
|
||||||
LDFLAGS = ${LIBS}
|
LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
354
cowterm.c
354
cowterm.c
@ -47,6 +47,14 @@ static XFontStruct *bold_font = NULL;
|
|||||||
static XFontStruct *italic_font = NULL;
|
static XFontStruct *italic_font = NULL;
|
||||||
static int window_focused = 0;
|
static int window_focused = 0;
|
||||||
|
|
||||||
|
// Alternatives to handle the ANSI escape codes
|
||||||
|
static char **saved_terminal = NULL;
|
||||||
|
static unsigned long **saved_colors = NULL;
|
||||||
|
static int **saved_attrs = NULL;
|
||||||
|
static int saved_cursor_x = 0;
|
||||||
|
static int saved_cursor_y = 0;
|
||||||
|
static int using_alternate = 0;
|
||||||
|
|
||||||
#define ATTR_BOLD 1
|
#define ATTR_BOLD 1
|
||||||
#define ATTR_ITALIC 2
|
#define ATTR_ITALIC 2
|
||||||
|
|
||||||
@ -375,6 +383,123 @@ static void scroll_up(void) {
|
|||||||
needs_refresh = 1;
|
needs_refresh = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alternative buffer visual:
|
||||||
|
//
|
||||||
|
// Normal buffer: Alternate buffer:
|
||||||
|
// [C][o][w] [ ][ ][ ]
|
||||||
|
// [T][e][r] [ ][ ][ ]
|
||||||
|
// [m][!][ ] <-> [ ][ ][ ]
|
||||||
|
//
|
||||||
|
// When applications like vim switch to alternate buffer it SHOULD:
|
||||||
|
// 1. Save the current buffer state (saved_terminal etc.)
|
||||||
|
// 2. creates a new empty alternate buffer
|
||||||
|
// 3. The application draws to the alternate buffer
|
||||||
|
// 4. When done, alternate buffer is freed
|
||||||
|
// 5. And the original buffer state is restored
|
||||||
|
//
|
||||||
|
// References:
|
||||||
|
// saved_terminal = backup of normal buffer
|
||||||
|
// saved_colors/attrs = backup of attributes
|
||||||
|
// using_alternate = tracks which buffer is active
|
||||||
|
static void handle_alternate_buffer(const char *buf, int *idx, int max_len) {
|
||||||
|
char num_buf[32] = {0};
|
||||||
|
size_t num_idx = 0;
|
||||||
|
|
||||||
|
(*idx)++;
|
||||||
|
while (*idx < max_len && buf[*idx] != 'h' && buf[*idx] != 'l') {
|
||||||
|
if (num_idx >= sizeof(num_buf) - 1)
|
||||||
|
break;
|
||||||
|
if (buf[*idx] >= '0' && buf[*idx] <= '9') {
|
||||||
|
num_buf[num_idx++] = buf[*idx];
|
||||||
|
}
|
||||||
|
(*idx)++;
|
||||||
|
}
|
||||||
|
if (num_buf[0] != '\0') {
|
||||||
|
int code = atoi(num_buf);
|
||||||
|
if (buf[*idx] == 'h' || buf[*idx] == 'l') {
|
||||||
|
switch (code) {
|
||||||
|
case 1047: // Alternate screen buffer
|
||||||
|
if (buf[*idx] == 'h') {
|
||||||
|
if (!saved_terminal) {
|
||||||
|
saved_terminal = terminal_buffer;
|
||||||
|
saved_colors = color_buffer;
|
||||||
|
saved_attrs = attr_buffer;
|
||||||
|
terminal_buffer = malloc(term_rows * sizeof(char *));
|
||||||
|
color_buffer = malloc(term_rows * sizeof(unsigned long *));
|
||||||
|
attr_buffer = malloc(term_rows * sizeof(int *));
|
||||||
|
for (int i = 0; i < term_rows; i++) {
|
||||||
|
terminal_buffer[i] = calloc(term_cols, sizeof(char));
|
||||||
|
color_buffer[i] = calloc(term_cols, sizeof(unsigned long));
|
||||||
|
attr_buffer[i] = calloc(term_cols, sizeof(int));
|
||||||
|
}
|
||||||
|
using_alternate = 1;
|
||||||
|
}
|
||||||
|
} else if (using_alternate) {
|
||||||
|
for (int i = 0; i < term_rows; i++) {
|
||||||
|
free(terminal_buffer[i]);
|
||||||
|
free(color_buffer[i]);
|
||||||
|
free(attr_buffer[i]);
|
||||||
|
}
|
||||||
|
free(terminal_buffer);
|
||||||
|
free(color_buffer);
|
||||||
|
free(attr_buffer);
|
||||||
|
terminal_buffer = saved_terminal;
|
||||||
|
color_buffer = saved_colors;
|
||||||
|
attr_buffer = saved_attrs;
|
||||||
|
saved_terminal = NULL;
|
||||||
|
using_alternate = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1048: // Save/restore cursor
|
||||||
|
if (buf[*idx] == 'h') {
|
||||||
|
saved_cursor_x = cursor_x;
|
||||||
|
saved_cursor_y = cursor_y;
|
||||||
|
} else {
|
||||||
|
cursor_x = saved_cursor_x;
|
||||||
|
cursor_y = saved_cursor_y;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1049: // Combined alternate buffer + cursor
|
||||||
|
if (buf[*idx] == 'h') {
|
||||||
|
saved_cursor_x = cursor_x;
|
||||||
|
saved_cursor_y = cursor_y;
|
||||||
|
if (!saved_terminal) {
|
||||||
|
saved_terminal = terminal_buffer;
|
||||||
|
saved_colors = color_buffer;
|
||||||
|
saved_attrs = attr_buffer;
|
||||||
|
terminal_buffer = malloc(term_rows * sizeof(char *));
|
||||||
|
color_buffer = malloc(term_rows * sizeof(unsigned long *));
|
||||||
|
attr_buffer = malloc(term_rows * sizeof(int *));
|
||||||
|
for (int i = 0; i < term_rows; i++) {
|
||||||
|
terminal_buffer[i] = calloc(term_cols, sizeof(char));
|
||||||
|
color_buffer[i] = calloc(term_cols, sizeof(unsigned long));
|
||||||
|
attr_buffer[i] = calloc(term_cols, sizeof(int));
|
||||||
|
}
|
||||||
|
using_alternate = 1;
|
||||||
|
}
|
||||||
|
} else if (using_alternate) {
|
||||||
|
for (int i = 0; i < term_rows; i++) {
|
||||||
|
free(terminal_buffer[i]);
|
||||||
|
free(color_buffer[i]);
|
||||||
|
free(attr_buffer[i]);
|
||||||
|
}
|
||||||
|
free(terminal_buffer);
|
||||||
|
free(color_buffer);
|
||||||
|
free(attr_buffer);
|
||||||
|
terminal_buffer = saved_terminal;
|
||||||
|
color_buffer = saved_colors;
|
||||||
|
attr_buffer = saved_attrs;
|
||||||
|
saved_terminal = NULL;
|
||||||
|
cursor_x = saved_cursor_x;
|
||||||
|
cursor_y = saved_cursor_y;
|
||||||
|
using_alternate = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handles those pesky ANSI color escape codes that terminals use.
|
// Handles those pesky ANSI color escape codes that terminals use.
|
||||||
// When \033[<number>m is sent to the terminal, it changes text color:
|
// When \033[<number>m is sent to the terminal, it changes text color:
|
||||||
// 30 = Black 31 = Red 32 = Green 33 = Yellow
|
// 30 = Black 31 = Red 32 = Green 33 = Yellow
|
||||||
@ -382,85 +507,168 @@ static void scroll_up(void) {
|
|||||||
// 0 sets everything back to plain white
|
// 0 sets everything back to plain white
|
||||||
// P.S. yes I had to google this up
|
// P.S. yes I had to google this up
|
||||||
static void parse_ansi_code(const char *buf, int *idx, int max_len) {
|
static void parse_ansi_code(const char *buf, int *idx, int max_len) {
|
||||||
char num_buf[16] = {0};
|
char num_buf[32] = {0};
|
||||||
int num_idx = 0;
|
int params[32] = {0};
|
||||||
|
size_t num_idx = 0;
|
||||||
|
size_t param_idx = 0;
|
||||||
(*idx)++; // Skip [
|
(*idx)++; // Skip [
|
||||||
|
|
||||||
while (*idx < max_len && buf[*idx] != 'm') {
|
// Handle '?' prefix for private sequences
|
||||||
|
if (buf[*idx] == '?') {
|
||||||
|
handle_alternate_buffer(buf, idx, max_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*idx < max_len && buf[*idx] != 'm' && buf[*idx] != 'H' &&
|
||||||
|
buf[*idx] != 'A' && buf[*idx] != 'B' && buf[*idx] != 'C' &&
|
||||||
|
buf[*idx] != 'D' && buf[*idx] != 'K' && buf[*idx] != 'J' &&
|
||||||
|
buf[*idx] != 'f' && buf[*idx] != 'r' && buf[*idx] != 's' &&
|
||||||
|
buf[*idx] != 'u' && buf[*idx] != 'h' && buf[*idx] != 'l') {
|
||||||
|
if (num_idx >= sizeof(num_buf) - 1 ||
|
||||||
|
param_idx >= sizeof(params) / sizeof(params[0]) - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (buf[*idx] >= '0' && buf[*idx] <= '9') {
|
if (buf[*idx] >= '0' && buf[*idx] <= '9') {
|
||||||
num_buf[num_idx++] = buf[*idx];
|
num_buf[num_idx++] = buf[*idx];
|
||||||
|
} else if (buf[*idx] == ';') {
|
||||||
|
if (num_buf[0] != '\0') {
|
||||||
|
params[param_idx++] = atoi(num_buf);
|
||||||
|
memset(num_buf, 0, sizeof(num_buf));
|
||||||
|
num_idx = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(*idx)++;
|
(*idx)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_buf[0] != '\0') {
|
if (num_buf[0] != '\0' && param_idx < sizeof(params) / sizeof(params[0])) {
|
||||||
int code = atoi(num_buf);
|
params[param_idx++] = atoi(num_buf);
|
||||||
switch (code) {
|
}
|
||||||
case 0:
|
|
||||||
current_color = XWhitePixel(display, DefaultScreen(display));
|
switch (buf[*idx]) {
|
||||||
current_attr = 0;
|
case 'H': // Set cursor position
|
||||||
break;
|
case 'f': // Alternative set cursor position
|
||||||
case 1:
|
if (param_idx >= 2) {
|
||||||
current_attr |= ATTR_BOLD;
|
cursor_y =
|
||||||
break;
|
(params[0] - 1 < 0)
|
||||||
case 3:
|
? 0
|
||||||
current_attr |= ATTR_ITALIC;
|
: ((params[0] - 1) >= term_rows ? term_rows - 1 : params[0] - 1);
|
||||||
break;
|
cursor_x =
|
||||||
case 22:
|
(params[1] - 1 < 0)
|
||||||
current_attr &= ~ATTR_BOLD;
|
? 0
|
||||||
break;
|
: ((params[1] - 1) >= term_cols ? term_cols - 1 : params[1] - 1);
|
||||||
case 23:
|
} else {
|
||||||
current_attr &= ~ATTR_ITALIC;
|
cursor_x = cursor_y = 0;
|
||||||
break;
|
|
||||||
case 30:
|
|
||||||
current_color = XBlackPixel(display, DefaultScreen(display));
|
|
||||||
break;
|
|
||||||
case 31:
|
|
||||||
current_color = RED; // Red
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
current_color = GREEN; // Green
|
|
||||||
break;
|
|
||||||
case 33:
|
|
||||||
current_color = YELLOW; // Yellow
|
|
||||||
break;
|
|
||||||
case 34:
|
|
||||||
current_color = BLUE; // Blue
|
|
||||||
break;
|
|
||||||
case 35:
|
|
||||||
current_color = MAGENTA; // Magenta
|
|
||||||
break;
|
|
||||||
case 36:
|
|
||||||
current_color = CYAN; // Cyan
|
|
||||||
break;
|
|
||||||
case 37:
|
|
||||||
current_color = XWhitePixel(display, DefaultScreen(display));
|
|
||||||
break;
|
|
||||||
case 90:
|
|
||||||
current_color = DARK_GRAY; // Dark gray
|
|
||||||
break;
|
|
||||||
case 91:
|
|
||||||
current_color = LIGHT_RED; // Light red
|
|
||||||
break;
|
|
||||||
case 92:
|
|
||||||
current_color = LIGHT_GREEN; // Light green
|
|
||||||
break;
|
|
||||||
case 93:
|
|
||||||
current_color = LIGHT_YELLOW; // Light yellow
|
|
||||||
break;
|
|
||||||
case 94:
|
|
||||||
current_color = LIGHT_BLUE; // Light blue
|
|
||||||
break;
|
|
||||||
case 95:
|
|
||||||
current_color = LIGHT_MAGENTA; // Light magenta
|
|
||||||
break;
|
|
||||||
case 96:
|
|
||||||
current_color = LIGHT_CYAN; // Light cyan
|
|
||||||
break;
|
|
||||||
case 97:
|
|
||||||
current_color = 0xFFFFFF; // Bright white teehee
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 'A': // Cursor up
|
||||||
|
cursor_y -= params[0] ? params[0] : 1;
|
||||||
|
if (cursor_y < 0)
|
||||||
|
cursor_y = 0;
|
||||||
|
break;
|
||||||
|
case 'B': // Cursor down
|
||||||
|
cursor_y += params[0] ? params[0] : 1;
|
||||||
|
if (cursor_y >= term_rows)
|
||||||
|
cursor_y = term_rows - 1;
|
||||||
|
break;
|
||||||
|
case 'C': // Cursor forward
|
||||||
|
cursor_x += params[0] ? params[0] : 1;
|
||||||
|
if (cursor_x >= term_cols)
|
||||||
|
cursor_x = term_cols - 1;
|
||||||
|
break;
|
||||||
|
case 'D': // Cursor back
|
||||||
|
cursor_x -= params[0] ? params[0] : 1;
|
||||||
|
if (cursor_x < 0)
|
||||||
|
cursor_x = 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'J': // Clear screen
|
||||||
|
if (params[0] == 2) {
|
||||||
|
for (int y = 0; 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
needs_refresh = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm': // Handling of text
|
||||||
|
for (int i = 0; i < param_idx; i++) {
|
||||||
|
switch (params[i]) {
|
||||||
|
case 0:
|
||||||
|
current_color = XWhitePixel(display, DefaultScreen(display));
|
||||||
|
current_attr = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
current_attr |= ATTR_BOLD;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
current_attr |= ATTR_ITALIC;
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
current_attr &= ~ATTR_BOLD;
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
current_attr &= ~ATTR_ITALIC;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
current_color = XBlackPixel(display, DefaultScreen(display));
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
current_color = RED;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
current_color = GREEN;
|
||||||
|
break;
|
||||||
|
case 33:
|
||||||
|
current_color = YELLOW;
|
||||||
|
break;
|
||||||
|
case 34:
|
||||||
|
current_color = BLUE;
|
||||||
|
break;
|
||||||
|
case 35:
|
||||||
|
current_color = MAGENTA;
|
||||||
|
break;
|
||||||
|
case 36:
|
||||||
|
current_color = CYAN;
|
||||||
|
break;
|
||||||
|
case 37:
|
||||||
|
current_color = XWhitePixel(display, DefaultScreen(display));
|
||||||
|
break;
|
||||||
|
case 90:
|
||||||
|
current_color = DARK_GRAY;
|
||||||
|
break;
|
||||||
|
case 91:
|
||||||
|
current_color = LIGHT_RED;
|
||||||
|
break;
|
||||||
|
case 92:
|
||||||
|
current_color = LIGHT_GREEN;
|
||||||
|
break;
|
||||||
|
case 93:
|
||||||
|
current_color = LIGHT_YELLOW;
|
||||||
|
break;
|
||||||
|
case 94:
|
||||||
|
current_color = LIGHT_BLUE;
|
||||||
|
break;
|
||||||
|
case 95:
|
||||||
|
current_color = LIGHT_MAGENTA;
|
||||||
|
break;
|
||||||
|
case 96:
|
||||||
|
current_color = LIGHT_CYAN;
|
||||||
|
break;
|
||||||
|
case 97:
|
||||||
|
current_color = 0xFFFFFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,6 +770,12 @@ static void key_press_cb(XKeyEvent *event, void *data) {
|
|||||||
write(*master, "\r", 1);
|
write(*master, "\r", 1);
|
||||||
} else if (keysym == XK_BackSpace) {
|
} else if (keysym == XK_BackSpace) {
|
||||||
write(*master, "\b", 1);
|
write(*master, "\b", 1);
|
||||||
|
} else if (keysym == XK_Left) {
|
||||||
|
write(*master, "\033[D", 3);
|
||||||
|
needs_refresh = 1;
|
||||||
|
} else if (keysym == XK_Right) {
|
||||||
|
write(*master, "\033[C", 3);
|
||||||
|
needs_refresh = 1;
|
||||||
} else if (len > 0) {
|
} else if (len > 0) {
|
||||||
write(*master, buf, len);
|
write(*master, buf, len);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user