diff --git a/config.mk b/config.mk index 6727dca..5da52a1 100644 --- a/config.mk +++ b/config.mk @@ -12,7 +12,7 @@ LIBS = -L/usr/X11R6/lib -lX11 -lutil ${FREETYPELIBS} # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 -CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${INCS} ${CPPFLAGS} +CFLAGS = -g -std=c99 -pedantic -Wall -Wextra -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} # compiler and linker diff --git a/cowterm.c b/cowterm.c index b9f8014..e582405 100644 --- a/cowterm.c +++ b/cowterm.c @@ -23,29 +23,36 @@ #define CHAR_WIDTH 8 #define CHAR_HEIGHT 16 +// General window/terminal stuff static pid_t child_pid; static char **terminal_buffer = NULL; static unsigned long **color_buffer = NULL; static int **attr_buffer = NULL; static int term_rows; static int term_cols; -static int cursor_x = 0; -static int cursor_y = 0; static unsigned long current_color; static int current_attr = 0; static GC gc = NULL; static GC gc_bold = NULL; static Display *display = NULL; -static int cursor_visible = 1; -static struct timeval last_blink; static struct timeval last_refresh; static Pixmap buffer_pixmap = None; static int master_fd = -1; static int needs_refresh = 0; +static int window_focused = 0; + +// Cursor stuff +static int cursor_visible = 1; +static struct timeval last_blink; +static int cursor_x = 0; +static int cursor_y = 0; + +// Font stuff static XFontStruct *regular_font = NULL; static XFontStruct *bold_font = NULL; static XFontStruct *italic_font = NULL; -static int window_focused = 0; +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; @@ -55,8 +62,12 @@ static int saved_cursor_x = 0; static int saved_cursor_y = 0; static int using_alternate = 0; -#define ATTR_BOLD 1 -#define ATTR_ITALIC 2 +#define ATTR_BOLD (1 << 0) +#define ATTR_ITALIC (1 << 1) +#define ATTR_REVERSE (1 << 2) +#define ATTR_UNDERLINE (1 << 3) +#define ATTR_DIM (1 << 4) +#define ATTR_BLINK (1 << 5) // Pesky static smth smth error, so we "declare" it early static void draw_terminal(Display *display, Window window); @@ -144,12 +155,16 @@ static void resize_buffers(int new_rows, int new_cols) { size_t row_size = ((new_cols * sizeof(char) + 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 *)); 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 = + malloc(new_rows * sizeof(unsigned long *)); - if (!new_term_buffer || !new_color_buffer || !new_attr_buffer) { + if (!new_term_buffer || !new_color_buffer || !new_attr_buffer || + !new_bg_color_buffer) { fprintf(stderr, "Failed to allocate memory for buffers\n"); return; } @@ -158,33 +173,40 @@ static void resize_buffers(int new_rows, int new_cols) { new_term_buffer[i] = malloc(row_size); new_color_buffer[i] = malloc(color_row_size); new_attr_buffer[i] = malloc(attr_row_size); + new_bg_color_buffer[i] = malloc(bg_color_row_size); - if (!new_term_buffer[i] || !new_color_buffer[i] || !new_attr_buffer[i]) { + if (!new_term_buffer[i] || !new_color_buffer[i] || !new_attr_buffer[i] || + !new_bg_color_buffer[i]) { fprintf(stderr, "Failed to allocate memory for buffer row\n"); // Clean up allocated memory for (int j = 0; j < i; j++) { free(new_term_buffer[j]); free(new_color_buffer[j]); free(new_attr_buffer[j]); + free(new_bg_color_buffer[j]); } free(new_term_buffer); free(new_color_buffer); free(new_attr_buffer); + free(new_bg_color_buffer); return; } if (i < term_rows && i < new_rows && terminal_buffer && color_buffer && - attr_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_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; } } } else { @@ -192,25 +214,29 @@ static void resize_buffers(int new_rows, int new_cols) { for (int x = 0; 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; } } } // Free old buffers after successful allocation - if (terminal_buffer && color_buffer && attr_buffer) { + if (terminal_buffer && color_buffer && attr_buffer && bg_color_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]); } free(terminal_buffer); free(color_buffer); free(attr_buffer); + free(bg_color_buffer); } terminal_buffer = new_term_buffer; color_buffer = new_color_buffer; attr_buffer = new_attr_buffer; + bg_color_buffer = new_bg_color_buffer; term_rows = new_rows; term_cols = new_cols; @@ -258,24 +284,41 @@ static void destroy_cb(Display *display, Window window) { } free(attr_buffer); } + if (bg_color_buffer) { + for (int i = 0; i < term_rows; i++) { + free(bg_color_buffer[i]); + } + free(bg_color_buffer); + } if (master_fd >= 0) close(master_fd); exit(0); } static void draw_char(Display *display, Drawable d, int x, int y) { - if (!gc || !terminal_buffer || !color_buffer || !attr_buffer) + if (!gc || !terminal_buffer || !color_buffer || !attr_buffer || + !bg_color_buffer) return; if (x < 0 || x >= term_cols || y < 0 || y >= term_rows) return; + unsigned long fg_color = color_buffer[y][x]; + unsigned long bg_color = bg_color_buffer[y][x]; + int attrs = attr_buffer[y][x]; + + if (attrs & ATTR_REVERSE) { + unsigned long temp = fg_color; + fg_color = bg_color; + bg_color = temp; + } + // Clear the character position first - XSetForeground(display, gc, XBlackPixel(display, DefaultScreen(display))); + XSetForeground(display, gc, bg_color); XFillRectangle(display, d, gc, x * CHAR_WIDTH, y * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT); + XSetForeground(display, gc, fg_color); char str[2] = {terminal_buffer[y][x], '\0'}; - XSetForeground(display, gc, color_buffer[y][x]); // Select appropriate font based on attributes XFontStruct *font = regular_font; @@ -288,6 +331,11 @@ static void draw_char(Display *display, Drawable d, int x, int y) { XDrawString(display, d, gc, x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 2, str, 1); + + if (attrs & ATTR_UNDERLINE) { + XDrawLine(display, d, gc, x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 1, + (x + 1) * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 1); + } } static void draw_terminal(Display *display, Window window) { @@ -350,13 +398,14 @@ static void check_refresh(Display *display, Window window) { } static void clear_terminal(Display *display, Window window) { - if (!terminal_buffer || !color_buffer || !attr_buffer) + if (!terminal_buffer || !color_buffer || !attr_buffer || !bg_color_buffer) return; 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; + bg_color_buffer[y][x] = current_bg_color; } } cursor_x = 0; @@ -372,6 +421,8 @@ static void scroll_up(void) { memcpy(color_buffer[y], color_buffer[y + 1], term_cols * sizeof(unsigned long)); memcpy(attr_buffer[y], attr_buffer[y + 1], term_cols * sizeof(int)); + memcpy(bg_color_buffer[y], bg_color_buffer[y + 1], + term_cols * sizeof(unsigned long)); } // Clear the bottom line @@ -379,6 +430,7 @@ static void scroll_up(void) { for (int x = 0; x < term_cols; x++) { color_buffer[term_rows - 1][x] = current_color; attr_buffer[term_rows - 1][x] = current_attr; + bg_color_buffer[term_rows - 1][x] = current_bg_color; } needs_refresh = 1; } @@ -424,13 +476,16 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { saved_terminal = terminal_buffer; saved_colors = color_buffer; saved_attrs = attr_buffer; + saved_colors = bg_color_buffer; terminal_buffer = malloc(term_rows * sizeof(char *)); color_buffer = malloc(term_rows * sizeof(unsigned long *)); attr_buffer = malloc(term_rows * sizeof(int *)); + bg_color_buffer = malloc(term_rows * sizeof(unsigned long *)); 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)); + bg_color_buffer[i] = calloc(term_cols, sizeof(unsigned long)); } using_alternate = 1; } @@ -439,13 +494,16 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { free(terminal_buffer[i]); free(color_buffer[i]); free(attr_buffer[i]); + free(bg_color_buffer[i]); } free(terminal_buffer); free(color_buffer); free(attr_buffer); + free(bg_color_buffer); terminal_buffer = saved_terminal; color_buffer = saved_colors; attr_buffer = saved_attrs; + bg_color_buffer = saved_colors; saved_terminal = NULL; using_alternate = 0; } @@ -467,13 +525,16 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { saved_terminal = terminal_buffer; saved_colors = color_buffer; saved_attrs = attr_buffer; + saved_colors = bg_color_buffer; terminal_buffer = malloc(term_rows * sizeof(char *)); color_buffer = malloc(term_rows * sizeof(unsigned long *)); attr_buffer = malloc(term_rows * sizeof(int *)); + bg_color_buffer = malloc(term_rows * sizeof(unsigned long *)); 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)); + bg_color_buffer[i] = calloc(term_cols, sizeof(unsigned long)); } using_alternate = 1; } @@ -482,13 +543,16 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { free(terminal_buffer[i]); free(color_buffer[i]); free(attr_buffer[i]); + free(bg_color_buffer[i]); } free(terminal_buffer); free(color_buffer); free(attr_buffer); + free(bg_color_buffer); terminal_buffer = saved_terminal; color_buffer = saved_colors; attr_buffer = saved_attrs; + bg_color_buffer = saved_colors; saved_terminal = NULL; cursor_x = saved_cursor_x; cursor_y = saved_cursor_y; @@ -500,6 +564,19 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { } } +static unsigned long get_ansi_color(int index) { + static const unsigned long colors[] = {0x000000, RED, GREEN, YELLOW, + BLUE, MAGENTA, CYAN, 0xFFFFFF}; + return colors[index & 7]; +} + +static unsigned long get_bright_ansi_color(int index) { + static const unsigned long colors[] = { + DARK_GRAY, LIGHT_RED, LIGHT_GREEN, LIGHT_YELLOW, + LIGHT_BLUE, LIGHT_MAGENTA, LIGHT_CYAN, 0xFFFFFF}; + return colors[index & 7]; +} + // Handles those pesky ANSI color escape codes that terminals use. // When \033[m is sent to the terminal, it changes text color: // 30 = Black 31 = Red 32 = Green 33 = Yellow @@ -585,6 +662,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { 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; } break; case 'J': // Clear screen @@ -594,6 +672,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { 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; @@ -601,71 +680,40 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { break; case 'm': // Handling of text for (int i = 0; i < param_idx; i++) { - switch (params[i]) { - case 0: + if (params[i] == 0) { // Reset Everything current_color = XWhitePixel(display, DefaultScreen(display)); + current_bg_color = XBlackPixel(display, DefaultScreen(display)); current_attr = 0; - break; - case 1: + } else if (params[i] == 1) { // Apply ATTRS current_attr |= ATTR_BOLD; - break; - case 3: + } else if (params[i] == 2) { + current_attr |= ATTR_DIM; + } else if (params[i] == 3) { current_attr |= ATTR_ITALIC; - break; - case 22: + } else if (params[i] == 4) { + current_attr |= ATTR_UNDERLINE; + } else if (params[i] == 5) { + current_attr |= ATTR_BLINK; + } else if (params[i] == 7) { + current_attr |= ATTR_REVERSE; + } else if (params[i] == 22) { // Reset ATTRS current_attr &= ~ATTR_BOLD; - break; - case 23: + } else if (params[i] == 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; + } else if (params[i] == 24) { + current_attr &= ~ATTR_UNDERLINE; + } else if (params[i] == 27) { + current_attr &= ~ATTR_REVERSE; + } else if (params[i] >= 30 && params[i] <= 37) { // Foreground colors + current_color = get_ansi_color(params[i] - 30); + } else if (params[i] >= 40 && params[i] <= 47) { // Background colors + current_bg_color = get_ansi_color(params[i] - 40); + } else if (params[i] >= 90 && + params[i] <= 97) { // Bright foreground colors + current_color = get_bright_ansi_color(params[i] - 90); + } else if (params[i] >= 100 && + params[i] <= 107) { // Bright background colors + current_bg_color = get_bright_ansi_color(params[i] - 100); } } break; @@ -673,7 +721,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { } static int master_cb(int fd, void *data) { - if (!terminal_buffer || !color_buffer || !attr_buffer) + if (!terminal_buffer || !color_buffer || !attr_buffer || !bg_color_buffer) return 0; char buf[4096]; ssize_t bytes_read; @@ -699,8 +747,8 @@ static int master_cb(int fd, void *data) { if (cursor_y >= term_rows) { scroll_up(); cursor_y = term_rows - 1; - needs_refresh = 1; } + needs_refresh = 1; } else if (buf[i] == '\r') { cursor_x = 0; } else if (buf[i] == '\b') { @@ -708,12 +756,21 @@ static int master_cb(int fd, void *data) { cursor_x--; terminal_buffer[cursor_y][cursor_x] = ' '; 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; } } else { if (cursor_y < term_rows && cursor_x < term_cols) { 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; cursor_x++; if (cursor_x >= term_cols) { @@ -722,7 +779,6 @@ static int master_cb(int fd, void *data) { if (cursor_y >= term_rows) { scroll_up(); cursor_y = term_rows - 1; - needs_refresh = 1; } } } @@ -769,12 +825,28 @@ static void key_press_cb(XKeyEvent *event, void *data) { if (keysym == XK_Return || keysym == XK_KP_Enter) { write(*master, "\r", 1); // Shell handles return } else if (keysym == XK_BackSpace) { - write(*master, "\b", 1); // Shell handles backspace + if (cursor_x > 0) { + write(*master, "\b \b", 3); // Shell handles backspace + } else if (cursor_y > 0) { + char seq[8]; + snprintf(seq, sizeof(seq), "\033[A\033[%dC", term_cols); + write(*master, seq, strlen(seq)); + } } else if (keysym == XK_Left) { - write(*master, "\033[D", 3); + if (cursor_x > 0) { + write(*master, "\033[D", 3); + } else if (cursor_y > 0) { + char seq[8]; + snprintf(seq, sizeof(seq), "\033[A\033[%dC", term_cols); + write(*master, seq, strlen(seq)); + } needs_refresh = 1; } else if (keysym == XK_Right) { - write(*master, "\033[C", 3); + if (cursor_x < term_cols - 1) { + write(*master, "\033[C", 3); + } else if (cursor_y < term_rows - 1) { + write(*master, "\033[B\033[G", 6); + } needs_refresh = 1; } else if (keysym == XK_Up) { // Go up through history write(*master, "\033[A", 3); @@ -786,6 +858,9 @@ static void key_press_cb(XKeyEvent *event, void *data) { write(*master, "\t", 1); // Shell handles autocomplete } else if (len > 0) { write(*master, buf, len); + if (cursor_x >= term_cols - 1) { + write(*master, "\r\n", 2); + } } } @@ -807,8 +882,9 @@ int main(void) { terminal_buffer = malloc(term_rows * sizeof(char *)); color_buffer = malloc(term_rows * sizeof(unsigned long *)); attr_buffer = malloc(term_rows * sizeof(int *)); + bg_color_buffer = malloc(term_rows * sizeof(unsigned long *)); - if (!terminal_buffer || !color_buffer || !attr_buffer) { + if (!terminal_buffer || !color_buffer || !attr_buffer || !bg_color_buffer) { fprintf(stderr, "Failed to allocate memory for buffers\n"); return 1; } @@ -817,8 +893,10 @@ int main(void) { terminal_buffer[i] = malloc(term_cols * sizeof(char)); color_buffer[i] = malloc(term_cols * sizeof(unsigned long)); attr_buffer[i] = malloc(term_cols * sizeof(int)); + bg_color_buffer[i] = malloc(term_cols * sizeof(unsigned long)); - if (!terminal_buffer[i] || !color_buffer[i] || !attr_buffer[i]) { + if (!terminal_buffer[i] || !color_buffer[i] || !attr_buffer[i] || + !bg_color_buffer[i]) { fprintf(stderr, "Failed to allocate memory for buffer row\n"); return 1; } @@ -834,8 +912,15 @@ int main(void) { XInitThreads(); current_color = XWhitePixel(display, DefaultScreen(display)); + current_bg_color = XBlackPixel(display, DefaultScreen(display)); current_attr = 0; + for (int y = 0; y < term_rows; y++) { + for (int x = 0; x < term_cols; x++) { + bg_color_buffer[y][x] = current_bg_color; + } + } + for (int y = 0; y < term_rows; y++) { for (int x = 0; x < term_cols; x++) { color_buffer[y][x] = current_color;