From 1ad881ba60370aeace64e8f4bd49ca49732cf16b Mon Sep 17 00:00:00 2001 From: Rekketstone Date: Thu, 16 Jan 2025 13:55:08 -0700 Subject: [PATCH] Attempt to fix scrolling in vim Atleast now it can "emulate" scrolling --- cowterm.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 293 insertions(+), 6 deletions(-) diff --git a/cowterm.c b/cowterm.c index 99808ee..1633d99 100644 --- a/cowterm.c +++ b/cowterm.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -40,6 +41,8 @@ static Pixmap buffer_pixmap = None; static int master_fd = -1; static int needs_refresh = 0; static int window_focused = 0; +static int scroll_top = 0; // 0 means entire terminal +static int scroll_bottom = 0; // 0 means entire terminal // Cursor stuff static int cursor_visible = 1; @@ -435,6 +438,233 @@ static void scroll_up(void) { needs_refresh = 1; } +static void scroll_region_up(int amount) { + char **temp_buffer = NULL; + unsigned long **temp_color_buffer = NULL; + int **temp_attr_buffer = NULL; + unsigned long **temp_bg_color_buffer = NULL; + + temp_buffer = malloc(term_rows * sizeof(char *)); + temp_color_buffer = malloc(term_rows * sizeof(unsigned long *)); + temp_attr_buffer = malloc(term_rows * sizeof(int *)); + temp_bg_color_buffer = malloc(term_rows * sizeof(unsigned long *)); + + if (!temp_buffer || !temp_color_buffer || !temp_attr_buffer || + !temp_bg_color_buffer) { + fprintf(stderr, "Failed to allocate memory for temp buffers\n"); + return; + } + + for (int i = 0; i < term_rows; i++) { + temp_buffer[i] = malloc(term_cols * sizeof(char)); + temp_color_buffer[i] = malloc(term_cols * sizeof(unsigned long)); + temp_attr_buffer[i] = malloc(term_cols * sizeof(int)); + temp_bg_color_buffer[i] = malloc(term_cols * sizeof(unsigned long)); + if (!temp_buffer[i] || !temp_color_buffer[i] || !temp_attr_buffer[i] || + !temp_bg_color_buffer[i]) { + fprintf(stderr, "Failed to allocate memory for temp buffer row\n"); + free(temp_buffer); + free(temp_color_buffer); + free(temp_attr_buffer); + free(temp_bg_color_buffer); + return; + } + } + + if (scroll_top == 0 && scroll_bottom == 0) { + for (int i = 0; i < term_rows; i++) { + memcpy(temp_buffer[i], terminal_buffer[i], term_cols * sizeof(char)); + memcpy(temp_color_buffer[i], color_buffer[i], + term_cols * sizeof(unsigned long)); + memcpy(temp_attr_buffer[i], attr_buffer[i], term_cols * sizeof(int)); + memcpy(temp_bg_color_buffer[i], bg_color_buffer[i], + term_cols * sizeof(unsigned long)); + } + for (int j = 0; j < amount; j++) { + // Move all lines up one position + for (int y = 0; y < term_rows - 1; y++) { + memcpy(terminal_buffer[y], temp_buffer[y + 1], term_cols); + memcpy(color_buffer[y], temp_color_buffer[y + 1], + term_cols * sizeof(unsigned long)); + memcpy(attr_buffer[y], temp_attr_buffer[y + 1], + term_cols * sizeof(int)); + memcpy(bg_color_buffer[y], temp_bg_color_buffer[y + 1], + term_cols * sizeof(unsigned long)); + } + + // Clear the bottom line + memset(terminal_buffer[term_rows - 1], ' ', term_cols); + 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; + } + } + } else { + for (int i = 0; i < term_rows; i++) { + memcpy(temp_buffer[i], terminal_buffer[i], term_cols * sizeof(char)); + memcpy(temp_color_buffer[i], color_buffer[i], + term_cols * sizeof(unsigned long)); + memcpy(temp_attr_buffer[i], attr_buffer[i], term_cols * sizeof(int)); + memcpy(temp_bg_color_buffer[i], bg_color_buffer[i], + term_cols * sizeof(unsigned long)); + } + for (int j = 0; j < amount; j++) { + int start = (scroll_top - 1); + int end = (scroll_bottom - 1); + if (start < 0) + start = 0; + if (end < 0) + end = 0; + if (end >= term_rows) + end = term_rows - 1; + // Move all lines up one position + for (int y = start; y < end; y++) { + memcpy(terminal_buffer[y], temp_buffer[y + 1], term_cols); + memcpy(color_buffer[y], temp_color_buffer[y + 1], + term_cols * sizeof(unsigned long)); + memcpy(attr_buffer[y], temp_attr_buffer[y + 1], + term_cols * sizeof(int)); + memcpy(bg_color_buffer[y], temp_bg_color_buffer[y + 1], + term_cols * sizeof(unsigned long)); + } + + // Clear the bottom line + memset(terminal_buffer[end], ' ', term_cols); + for (int x = 0; x < term_cols; x++) { + color_buffer[end][x] = current_color; + attr_buffer[end][x] = current_attr; + bg_color_buffer[end][x] = current_bg_color; + } + } + } + + for (int i = 0; i < term_rows; i++) { + free(temp_buffer[i]); + free(temp_color_buffer[i]); + free(temp_attr_buffer[i]); + free(temp_bg_color_buffer[i]); + } + free(temp_buffer); + free(temp_color_buffer); + free(temp_attr_buffer); + free(temp_bg_color_buffer); + needs_refresh = 1; +} + +static void scroll_region_down(int amount) { + char **temp_buffer = NULL; + unsigned long **temp_color_buffer = NULL; + int **temp_attr_buffer = NULL; + unsigned long **temp_bg_color_buffer = NULL; + + temp_buffer = malloc(term_rows * sizeof(char *)); + temp_color_buffer = malloc(term_rows * sizeof(unsigned long *)); + temp_attr_buffer = malloc(term_rows * sizeof(int *)); + temp_bg_color_buffer = malloc(term_rows * sizeof(unsigned long *)); + + if (!temp_buffer || !temp_color_buffer || !temp_attr_buffer || + !temp_bg_color_buffer) { + fprintf(stderr, "Failed to allocate memory for temp buffers\n"); + return; + } + for (int i = 0; i < term_rows; i++) { + temp_buffer[i] = malloc(term_cols * sizeof(char)); + temp_color_buffer[i] = malloc(term_cols * sizeof(unsigned long)); + temp_attr_buffer[i] = malloc(term_cols * sizeof(int)); + temp_bg_color_buffer[i] = malloc(term_cols * sizeof(unsigned long)); + + if (!temp_buffer[i] || !temp_color_buffer[i] || !temp_attr_buffer[i] || + !temp_bg_color_buffer[i]) { + fprintf(stderr, "Failed to allocate memory for temp buffer row\n"); + free(temp_buffer); + free(temp_color_buffer); + free(temp_attr_buffer); + free(temp_bg_color_buffer); + return; + } + } + + if (scroll_top == 0 && scroll_bottom == 0) { + for (int i = 0; i < term_rows; i++) { + memcpy(temp_buffer[i], terminal_buffer[i], term_cols * sizeof(char)); + memcpy(temp_color_buffer[i], color_buffer[i], + term_cols * sizeof(unsigned long)); + memcpy(temp_attr_buffer[i], attr_buffer[i], term_cols * sizeof(int)); + memcpy(temp_bg_color_buffer[i], bg_color_buffer[i], + term_cols * sizeof(unsigned long)); + } + for (int j = 0; j < amount; j++) { + // Move all lines down one position + for (int y = term_rows - 1; y > 0; y--) { + memcpy(terminal_buffer[y], temp_buffer[y - 1], term_cols); + memcpy(color_buffer[y], temp_color_buffer[y - 1], + term_cols * sizeof(unsigned long)); + memcpy(attr_buffer[y], temp_attr_buffer[y - 1], + term_cols * sizeof(int)); + memcpy(bg_color_buffer[y], temp_bg_color_buffer[y - 1], + term_cols * sizeof(unsigned long)); + } + // Clear the top line + memset(terminal_buffer[0], ' ', term_cols); + for (int x = 0; x < term_cols; x++) { + color_buffer[0][x] = current_color; + attr_buffer[0][x] = current_attr; + bg_color_buffer[0][x] = current_bg_color; + } + } + } else { + for (int i = 0; i < term_rows; i++) { + memcpy(temp_buffer[i], terminal_buffer[i], term_cols * sizeof(char)); + memcpy(temp_color_buffer[i], color_buffer[i], + term_cols * sizeof(unsigned long)); + memcpy(temp_attr_buffer[i], attr_buffer[i], term_cols * sizeof(int)); + memcpy(temp_bg_color_buffer[i], bg_color_buffer[i], + term_cols * sizeof(unsigned long)); + } + for (int j = 0; j < amount; j++) { + int start = (scroll_top - 1); + int end = (scroll_bottom - 1); + if (start < 0) + start = 0; + if (end < 0) + end = 0; + if (end >= term_rows) + end = term_rows - 1; + + // Move all lines down one position + for (int y = end; y > start; y--) { + memcpy(terminal_buffer[y], temp_buffer[y - 1], term_cols); + memcpy(color_buffer[y], temp_color_buffer[y - 1], + term_cols * sizeof(unsigned long)); + memcpy(attr_buffer[y], temp_attr_buffer[y - 1], + term_cols * sizeof(int)); + memcpy(bg_color_buffer[y], temp_bg_color_buffer[y - 1], + term_cols * sizeof(unsigned long)); + } + + // Clear the top line + memset(terminal_buffer[start], ' ', term_cols); + for (int x = 0; x < term_cols; x++) { + color_buffer[start][x] = current_color; + attr_buffer[start][x] = current_attr; + bg_color_buffer[start][x] = current_bg_color; + } + } + } + for (int i = 0; i < term_rows; i++) { + free(temp_buffer[i]); + free(temp_color_buffer[i]); + free(temp_attr_buffer[i]); + free(temp_bg_color_buffer[i]); + } + free(temp_buffer); + free(temp_color_buffer); + free(temp_attr_buffer); + free(temp_bg_color_buffer); + needs_refresh = 1; +} + // Alternative buffer visual: // // Normal buffer: Alternate buffer: @@ -453,7 +683,8 @@ static void scroll_up(void) { // 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) { +static void handle_alternate_buffer(const char *buf, int *idx, int max_len, + Window window) { char num_buf[32] = {0}; size_t num_idx = 0; @@ -488,6 +719,8 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { bg_color_buffer[i] = calloc(term_cols, sizeof(unsigned long)); } using_alternate = 1; + needs_refresh = 1; + draw_terminal(display, window); } } else if (using_alternate) { for (int i = 0; i < term_rows; i++) { @@ -506,6 +739,8 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { bg_color_buffer = saved_colors; saved_terminal = NULL; using_alternate = 0; + needs_refresh = 1; + draw_terminal(display, window); } break; case 1048: // Save/restore cursor @@ -537,6 +772,8 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { bg_color_buffer[i] = calloc(term_cols, sizeof(unsigned long)); } using_alternate = 1; + needs_refresh = 1; + draw_terminal(display, window); } } else if (using_alternate) { for (int i = 0; i < term_rows; i++) { @@ -557,6 +794,8 @@ static void handle_alternate_buffer(const char *buf, int *idx, int max_len) { cursor_x = saved_cursor_x; cursor_y = saved_cursor_y; using_alternate = 0; + needs_refresh = 1; + draw_terminal(display, window); } break; } @@ -583,7 +822,7 @@ static unsigned long get_bright_ansi_color(int index) { // 34 = Blue 35 = Magenta 36 = Cyan 37 = White // 0 sets everything back to plain white // 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, Window window) { char num_buf[32] = {0}; int params[32] = {0}; size_t num_idx = 0; @@ -592,7 +831,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { // Handle '?' prefix for private sequences if (buf[*idx] == '?') { - handle_alternate_buffer(buf, idx, max_len); + handle_alternate_buffer(buf, idx, max_len, window); return; } @@ -678,6 +917,54 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { needs_refresh = 1; } break; + case 'r': // Set scrolling region + if (param_idx == 0) { + scroll_top = 0; // Entire terminal + scroll_bottom = 0; + } else if (param_idx == 1) { + scroll_top = (params[0] <= 0) + ? 1 + : (params[0] > term_rows ? term_rows : params[0]); + scroll_bottom = 0; + } else if (param_idx >= 2) { + scroll_top = (params[0] <= 0) + ? 1 + : (params[0] > term_rows ? term_rows : params[0]); + scroll_bottom = (params[1] <= 0) + ? term_rows + : (params[1] > term_rows ? term_rows : params[1]); + + // if top is bigger than bottom, swap values + if (scroll_top > scroll_bottom) { + int tmp = scroll_top; + scroll_top = scroll_bottom; + scroll_bottom = tmp; + break; + case 'S': // Scroll up + scroll_region_up(params[0] == 0 ? 1 : params[0]); + needs_refresh = 1; + break; + case 'T': // Scroll down + scroll_region_down(params[0] == 0 ? 1 : params[0]); + needs_refresh = 1; + break; + case 'E': // Cursor next line + cursor_x = 0; + cursor_y += params[0] ? params[0] : 1; + if (cursor_y >= term_rows) { + cursor_y = term_rows - 1; + } + break; + case 'F': // Cursor line before + cursor_x = 0; + cursor_y -= params[0] ? params[0] : 1; + if (cursor_y < 0) { + cursor_y = 0; + } + break; + } + } + break; case 'm': // Handling of text for (int i = 0; i < param_idx; i++) { if (params[i] == 0) { // Reset Everything @@ -722,7 +1009,7 @@ static void parse_ansi_code(const char *buf, int *idx, int max_len) { } } -static int master_cb(int fd, void *data) { +static int master_cb(int fd, void *data, Window window) { if (!terminal_buffer || !color_buffer || !attr_buffer || !bg_color_buffer) return 0; char buf[4096]; @@ -740,7 +1027,7 @@ static int master_cb(int fd, void *data) { if (bytes_read > 0) { for (int i = 0; i < bytes_read; i++) { if (buf[i] == '\033' && i + 1 < bytes_read && buf[i + 1] == '[') { - parse_ansi_code(buf, &i, bytes_read); + parse_ansi_code(buf, &i, bytes_read, window); continue; } if (buf[i] == '\n') { @@ -1043,7 +1330,7 @@ int main(void) { FD_ZERO(&fds); FD_SET(master, &fds); if (select(master + 1, &fds, NULL, NULL, &tv) > 0) { - master_cb(master, &ctx); + master_cb(master, &ctx, window); } check_refresh(display, window);