se

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit d10c7baf68af22417aca93aec7fbba6bfe5a41f1
parent bbedbc1d1b7e6ad195c0469e88e3b6b73675e5b3
Author: Samdal <samdal@protonmail.com>
Date:   Tue, 11 Jan 2022 22:50:35 +0100

reworked cursor, multiple windows

Diffstat:
M.clang_complete | 1+
Mconfig.def.h | 48+++++++++++++++++++++++++++++++++++++++++++-----
Mst.c | 956+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mst.h | 153++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mwin.h | 11+++++++++--
Mx.c | 422++++++++++++++++++++++++++++++++++++++++----------------------------------------
6 files changed, 869 insertions(+), 722 deletions(-)

diff --git a/.clang_complete b/.clang_complete @@ -4,3 +4,4 @@ -I/usr/include/glib-2.0 -I/usr/include/libpng16 -I/usr/lib/glib-2.0/include +-D_XOPEN_SOURCE diff --git a/config.def.h b/config.def.h @@ -1,5 +1,45 @@ /* See LICENSE file for copyright and license details. */ +#include "win.h" +#undef Glyph +#include <unistd.h> +#include <X11/cursorfont.h> +#include <X11/Xft/Xft.h> + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) + +// default functions used for the config +// for the funtions implementation see the x.c file +static void numlock(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void cursor_move_x_relative(const Arg* arg); +static void cursor_move_y_relative(const Arg* arg); +static void save_buffer(const Arg* arg); +static void toggle_selection(const Arg* arg); +static void move_cursor_to_offset(const Arg* arg); +static void move_cursor_to_end_of_buffer(const Arg* arg); +static void clipboard_copy(const Arg* arg); +static void clipboard_paste(const Arg* arg); +static void undo(const Arg* arg); +static void redo(const Arg* arg); + +static void cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason); + +static void buffer_content_callback(struct file_buffer* buffer, int offset, enum buffer_content_reason reason); + //TODO: make this file friendly to IDE's /* @@ -61,11 +101,9 @@ static const char *colorname[] = { * Default colors (colorname index) * foreground, background, cursor, reverse cursor */ -unsigned int defaultfg = 7; -unsigned int defaultbg = 0; +Glyph_ default_attributes = {.bg = 0, .fg = 7}; static unsigned int defaultcs = 256; -int default_mode = MODE_UTF8; int undo_buffers = 32; /* @@ -97,8 +135,8 @@ static unsigned int mousebg = 0; */ static unsigned int defaultattr = 11; -void(*cursor_movement_callback)(int, int, enum cursor_reason) = cursor_callback; -void(*buffer_contents_updated)(Buffer*, int, int, enum buffer_content_reason) = buffer_content_callback; +void(*cursor_movement_callback)(struct window_buffer*, enum cursor_reason) = cursor_callback; +void(*buffer_contents_updated)(struct file_buffer*, int, enum buffer_content_reason) = buffer_content_callback; /* Internal keyboard shortcuts. */ #define MODKEY Mod1Mask diff --git a/st.c b/st.c @@ -5,6 +5,7 @@ #include <stdarg.h> #include <string.h> #include <assert.h> +#include <stddef.h> #include "st.h" #include "win.h" @@ -13,20 +14,11 @@ #define UTF_INVALID 0xFFFD #define UTF_SIZ 4 -/* macros */ -#define IS_SET(flag) ((term.mode & (flag)) != 0) - Term term; -static void tclearregion(int, int, int, int); -static void treset(void); -static void tsetdirt(int, int); -static void tswapscreen(void); -static void tfulldirt(void); - -static void drawregion(int, int, int, int); +extern struct window_buffer* focused_window; +extern int cursor_x, cursor_y; static void colour_selection(Glyph* letter); -static void set_selection_start_end(int* x1, int* x2, int* y1, int* y2, Buffer* buffer); static int t_decode_utf8_buffer(const char* buffer, const int buflen, Rune* u); @@ -151,185 +143,341 @@ tattrset(int attr) } void -tsetdirt(int top, int bot) +tnew(int col, int row) { - int i; - - LIMIT(top, 0, term.row-1); - LIMIT(bot, 0, term.row-1); - - for (i = top; i <= bot; i++) - term.dirty[i] = 1; + term = (Term){0}; + tresize(col, row); + tsetregion(0, 0, term.col-1, term.row-1, ' '); } int -tisdirty() +buffer_seek_char(const struct file_buffer* buf, int offset, char byte) { - for (int i = 0; i < term.row-1; i++) - if (term.dirty[i]) - return 1; - return 0; + LIMIT(offset, 0, buf->len-1); + char* new_buf = memchr(buf->contents + offset, byte, buf->len - offset); + if (!new_buf) return -1; + return new_buf - buf->contents; +} +int +buffer_seek_char_backwards(const struct file_buffer* buf, int offset, char byte) +{ + LIMIT(offset, 0, buf->len-1); + for (int n = offset-1; n >= 0; n--) { + if (buf->contents[n] == byte) { + return n+1; + } + } + return -1; } -void -tsetdirtattr(int attr) +int +buffer_seek_string(const struct file_buffer* buf, int offset, const char* string) { - int i, j; + LIMIT(offset, 0, buf->len-1); + int str_len = strlen(string); - for (i = 0; i < term.row-1; i++) { - for (j = 0; j < term.col-1; j++) { - if (term.line[i][j].mode & attr) { - tsetdirt(i, i); - break; - } + for (int n = offset; n < buf->len - str_len; n++) { + if (!memcmp(buf->contents + n, string, str_len)) { + printf("n = %d\n", n); + return n; } } + return -1; } - -void -tfulldirt(void) +int +buffer_seek_string_backwards(const struct file_buffer* buf, int offset, const char* string) { - tsetdirt(0, term.row-1); + int str_len = strlen(string); + offset += str_len; + LIMIT(offset, 0, buf->len-1); + + for (int n = offset - str_len; n >= 0; n--) { + if (!memcmp(buf->contents + n, string, str_len)) { + printf("n = %d\n", n); + return n; + } + } + return -1; } void -treset(void) -{ - uint i; +buffer_move_on_line(struct window_buffer* buf, int amount, enum cursor_reason callback_reason) +{ + const struct file_buffer* fb = get_file_buffer((buf)); + + if (amount < 0) { + /* + * we cant get the size of a utf8 char backwards + * therefore we move all the way to the start of the line, + * then a seeker will try to find the cursor pos + * the follower will then be *amount* steps behind, + * when the seeker reaches the cursor + * the follower will be the new cursor position + */ + + int line_start = buffer_seek_char_backwards(fb, buf->cursor_offset, '\n'); + amount = abs(MAX(line_start - buf->cursor_offset, amount)); + assert(amount < 2048); + + char moves[amount]; + int seek_pos = line_start, follower_pos = line_start; + int n = 0; + while (seek_pos < buf->cursor_offset) { + Rune u; + int charsize = t_decode_utf8_buffer(fb->contents + seek_pos, fb->len - seek_pos, &u); + seek_pos += charsize; + if (n < amount) { + moves[n++] = charsize; + } else { + follower_pos += moves[0]; + memmove(moves, moves + 1, amount - 1); + moves[amount - 1] = charsize; + } + } + buf->cursor_offset = follower_pos; - term.c = (TCursor){{ - .mode = ATTR_NULL, - .fg = defaultfg, - .bg = defaultbg - }, .x = 0, .y = 0}; + LIMIT(buf->cursor_offset, 0, fb->len-1); + } else if (amount > 0) { + for (int charsize = 0; + buf->cursor_offset < fb->len && amount > 0 && fb->contents[buf->cursor_offset + charsize] != '\n'; + buf->cursor_offset += charsize, amount--) { + Rune u; + charsize = t_decode_utf8_buffer(fb->contents + buf->cursor_offset, fb->len - buf->cursor_offset, &u); + if (u != '\n' && u != '\t') + if (wcwidth(u) <= 0) + amount++; + if (buf->cursor_offset + charsize >= fb->len) + break; + } + } - memset(term.tabs, 0, term.col * sizeof(*term.tabs)); - for (i = tabspaces; i < term.col; i += tabspaces) - term.tabs[i] = 1; - term.mode = default_mode; + if (callback_reason && cursor_movement_callback) + cursor_movement_callback(buf, callback_reason); +} - for (i = 0; i < 2; i++) { - tclearregion(0, 0, term.col-1, term.row-1); - tswapscreen(); +void +buffer_move_lines(struct window_buffer* buf, int amount, enum cursor_reason callback_reason) +{ + const struct file_buffer* fb = get_file_buffer((buf)); + int offset = buf->cursor_offset; + if (amount > 0) { + while (amount-- && offset >= 0) + offset = buffer_seek_char(fb, offset, '\n')+1; + if (offset < 0) + offset = fb->len-1; + } else if (amount < 0) { + while (amount++ && offset >= 0) + offset = buffer_seek_char_backwards(fb, offset, '\n')-1; } + buffer_move_to_offset(buf, offset, callback_reason); } void -tnew(int col, int row) +buffer_move_to_offset(struct window_buffer* buf, int offset, enum cursor_reason callback_reason) { - term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; - tresize(col, row); - treset(); + const struct file_buffer* fb = get_file_buffer((buf)); + LIMIT(offset, 0, fb->len-1); + buf->cursor_offset = offset; + + if (callback_reason && cursor_movement_callback) + cursor_movement_callback(buf, callback_reason); } void -tswapscreen(void) +buffer_move_to_x(struct window_buffer* buf, int x, enum cursor_reason callback_reason) { - Line *tmp = term.line; + assert(buf); + struct file_buffer* fb = get_file_buffer(buf); - term.line = term.alt; - term.alt = tmp; - term.mode ^= MODE_ALTSCREEN; - tfulldirt(); + int offset = buffer_seek_char_backwards(fb, buf->cursor_offset, '\n'); + if (offset < 0) + offset = 0; + buffer_move_to_offset(buf, offset, 0); + + int x_counter = 0; + + while (offset < fb->len) { + if (fb->contents[offset] == '\t') { + offset++; + if (x_counter <= 0) x_counter += 1; + while (x_counter % tabspaces != 0) x_counter += 1; + x_counter += 1; + continue; + } else if (fb->contents[offset] == '\n') { + break; + } + Rune u = 0; + int charsize = t_decode_utf8_buffer(fb->contents + offset, fb->len - offset, &u); + x_counter += wcwidth(u); + if (x_counter <= x) { + offset += charsize; + if (x_counter == x) + break; + } else { + break; + } + } + buffer_move_to_offset(buf, offset, callback_reason); } void -buffer_scroll(Buffer* buffer, int xscroll, int yscroll) +window_buffer_split(struct window_split_node* parent, float ratio, enum window_split_mode mode) { - if (yscroll) { - buffer->y_scroll += yscroll; - // prevent you from scrolling outside of the screen - if (buffer->y_scroll < 0) - buffer->y_scroll = 0; - else - buffer_snap_cursor(buffer, 0); - } + assert(parent); + assert(parent->mode == WINDOW_SINGULAR); + assert(mode != WINDOW_SINGULAR); - if (xscroll) { - buffer->x_scroll += xscroll; - if (buffer->x_scroll < 0) - buffer->x_scroll = 0; - } + parent->node1 = xmalloc(sizeof(struct window_split_node)); + parent->node2 = xmalloc(sizeof(struct window_split_node)); - while(buffer->x_scroll > 0 && term.c.x+1 < term.col) { - term.c.x++; - buffer->x_scroll--; - } + *parent->node1 = *parent; + *parent->node2 = *parent; + parent->node1->parent = parent; + parent->node2->parent = parent; + + // set the new childrens children to NULL for good measure + parent->node1->node1 = NULL; + parent->node1->node2 = NULL; + parent->node2->node1 = NULL; + parent->node2->node2 = NULL; + + parent->mode = mode; + parent->ratio = ratio; + parent->window = (struct window_buffer){0}; } void -buffer_move_cursor(Buffer* buffer, int x, int y, enum cursor_reason callback_reason) +window_write_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy) { - term.c.y = (y <= 0) ? 0 : (y >= term.row) ? term.row-1 : y; - int ydiff = y - term.c.y; + assert(root); - term.c.x = (x <= 0) ? 0 : (x >= term.col-1) ? term.col-1 : x; - if (term.c.x > 0 && term.line[term.c.y][term.c.x].mode & ATTR_WDUMMY) - term.c.x--; + if (root->mode == WINDOW_SINGULAR) { + buffer_write_to_screen(&root->window, minx, miny, maxx, maxy); + } else if (IS_HORISONTAL(root->mode)) { + int middlex = ((float)(maxx - minx) * root->ratio) + minx; - int xdiff = x - term.c.x; + // print seperator + tsetregion(middlex+1, miny, middlex+1, maxy, L'│'); - if (callback_reason) { - buffer_scroll(buffer, xdiff, ydiff); - if (cursor_movement_callback && callback_reason != CURSOR_SCROLL_ONLY) { - cursor_movement_callback(term.c.x, term.c.y, callback_reason); + window_write_tree_to_screen(root->node1, minx, miny, middlex, maxy); + window_write_tree_to_screen(root->node2, middlex+2, miny, maxx, maxy); + + // print connecting borders + if (middlex+1 >= term.col) + return; + if (miny-1 >= 0 && miny-1 < term.row) { + if (term.line[miny-1][middlex+1].u == L'┬' || + term.line[miny-1][middlex+1].u == L'┴' || + term.line[miny-1][middlex+1].u == L'├' || + term.line[miny-1][middlex+1].u == L'┤') + tsetchar(L'┼', middlex+1, miny-1); + if (term.line[miny-1][middlex+1].u == L'─') + tsetchar(L'┬', middlex+1, miny-1); + } + if (maxy+1 >= 0 && maxy+1 < term.row) { + if (term.line[maxy+1][middlex+1].u == L'┬' || + term.line[maxy+1][middlex+1].u == L'┴' || + term.line[maxy+1][middlex+1].u == L'├' || + term.line[maxy+1][middlex+1].u == L'┤') + tsetchar(L'┼', middlex+1, maxy+1); + if (term.line[maxy+1][middlex+1].u == L'─') + tsetchar(L'┴', middlex+1, maxy+1); + } + } else if (root->mode == WINDOW_VERTICAL) { + int middley = ((float)(maxy - miny) * root->ratio) + miny; + + // print seperator + tsetregion(minx, middley+1, maxx, middley+1, L'─'); + + window_write_tree_to_screen(root->node1, minx, miny, maxx, middley); + window_write_tree_to_screen(root->node2, minx, middley+2, maxx, maxy); + + // print connecting borders + if (middley+1 >= term.row) + return; + if (minx-1 >= 0 && minx-1 < term.col) { + if (term.line[middley+1][minx-1].u == L'┬' || + term.line[middley+1][minx-1].u == L'┴' || + term.line[middley+1][minx-1].u == L'├' || + term.line[middley+1][minx-1].u == L'┤') + tsetchar(L'┼', minx-1, middley+1); + if (term.line[middley+1][minx-1].u == L'│') + tsetchar(L'├', minx-1, middley+1); + } + if (maxx+1 >= 0 && maxx+1 < term.col) { + if (term.line[middley+1][maxx+1].u == L'┤') + if (term.line[middley+1][maxx+1].u == L'┬' || + term.line[middley+1][maxx+1].u == L'┴' || + term.line[middley+1][maxx+1].u == L'├' || + term.line[middley+1][maxx+1].u == L'┤') + tsetchar(L'┼', maxx+1, middley+1); + if (term.line[middley+1][maxx+1].u == L'│') + tsetchar(L'┤', maxx+1, middley+1); } } } -void -buffer_move_cursor_relative(Buffer* buffer, int x, int y, enum cursor_reason callback_reason) +int +is_correct_mode(enum window_split_mode mode, enum move_directons move) { - int xmoves = x; - x = term.c.x; - y += term.c.y; + if (move == MOVE_RIGHT || move == MOVE_LEFT) + return IS_HORISONTAL(mode); + if (move == MOVE_UP || move == MOVE_DOWN) + return (mode == WINDOW_VERTICAL); + return 0; +} - int y_prev = term.c.y, x_prev = term.c.x; +struct window_split_node* +window_switch_to_window(struct window_split_node* current, enum move_directons move) +{ + assert(current); + if (!current->parent) return current; + assert(current->mode == WINDOW_SINGULAR); + struct window_split_node* old_current = current; - if (xmoves > 0) { - buffer_move_cursor(buffer, term.c.x, y, 0); - char* repl = &(buffer->contents[buffer_snap_cursor(buffer, 1)]); - char* last = &(buffer->contents[buffer->len]); - for (int charsize; - repl < last && xmoves && *repl != '\n'; - repl += charsize, xmoves--) { - Rune u; - charsize = t_decode_utf8_buffer(repl, last - repl, &u); - - if (*repl == '\t') { - if (x == 0) - x++; - while (x % tabspaces != 0) - x++; - x++; - } else { - x += wcwidth(u); + if (move == MOVE_RIGHT || move == MOVE_DOWN) { + // traverse up the tree to the right + for (; current->parent; current = current->parent) { + if (is_correct_mode(current->parent->mode, move) && current->parent->node1 == current) { + // traverse down until a screen is found + current = current->parent->node2; + while(current->mode != WINDOW_SINGULAR) + current = current->node1; + + return current; + } + } + } else if (move == MOVE_LEFT || move == MOVE_UP) { + // traverse up the tree to the left + for (; current->parent; current = current->parent) { + if (is_correct_mode(current->parent->mode, move) && current->parent->node2 == current) { + // traverse down until a screen is found + current = current->parent->node1; + while(current->mode != WINDOW_SINGULAR) + current = current->node2; + + return current; } } - } else if (xmoves < 0) { - while(xmoves++ < 0 && term.c.x > 0) - buffer_move_cursor(buffer, term.c.x-1, term.c.y, 0); - x = term.c.x; } - buffer_move_cursor(buffer, x_prev, y_prev, 0); - buffer_move_cursor(buffer, x, y, callback_reason); + return old_current; } -int -buffer_new(Buffer* buffer, char* file_path) +struct file_buffer +buffer_new(char* file_path) { - assert(buffer); if (!file_path) { die("creating new buffers not implemented\n"); } - memset(buffer, 0, sizeof(Buffer)); - buffer->file_path = file_path; + struct file_buffer buffer = {0}; + buffer.file_path = file_path; FILE *file = fopen(file_path, "rb"); if (!file) { fprintf(stderr, "---error reading file \"%s\"---\n", file_path); - return -1; + die(""); + return buffer; } fseek(file, 0L, SEEK_END); @@ -343,162 +491,263 @@ buffer_new(Buffer* buffer, char* file_path) die("you are opening a huge file(>10MiB), not allowed"); } - buffer->len = readsize; - buffer->capacity = readsize + 100; + buffer.len = readsize; + buffer.capacity = readsize + 100; - buffer->contents = xmalloc(buffer->capacity); + buffer.contents = xmalloc(buffer.capacity); - fread(buffer->contents, 1, readsize, file); + char bom[4] = {0}; + fread(bom, 1, 3, file); + if (strcmp(bom, "\xEF\xBB\xBF")) + rewind(file); + else + buffer.mode |= BUFFER_UTF8_SIGNED; + fread(buffer.contents, 1, readsize, file); fclose(file); - buffer->undo_buffer_len = undo_buffers; - buffer->ub = xmalloc(sizeof(struct undo_buffer) * undo_buffers); - memset(buffer->ub, 0, sizeof(struct undo_buffer) * undo_buffers); - buffer->available_redo_buffers = 0; - buffer->current_undo_buffer = 0; + buffer.ub = xmalloc(sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT); + memset(buffer.ub, 0, sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT); if (buffer_contents_updated) - buffer_contents_updated(buffer, term.c.x, term.c.y, BUFFER_CONTENT_INIT); + buffer_contents_updated(&buffer, 0, BUFFER_CONTENT_INIT); - return buffer->len; + return buffer; } void -buffer_insert(Buffer* buffer, const char* new_content, const int len, const int offset, int do_not_callback) +buffer_insert(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback) { - assert(buffer->contents); - if (offset > buffer->len) - die("writing past buffer %s\n", buffer->file_path); + assert(buf->contents); + if (offset > buf->len) + die("writing past buf %s\n", buf->file_path); - if (buffer->len + len >= buffer->capacity) { - buffer->capacity = buffer->len + len + 256; - buffer->contents = xrealloc(buffer->contents, buffer->capacity); + if (buf->len + len >= buf->capacity) { + buf->capacity = buf->len + len + 256; + buf->contents = xrealloc(buf->contents, buf->capacity); } - if (offset < buffer->len) - memmove(buffer->contents+offset+len, buffer->contents+offset, buffer->len-offset); - buffer->len += len; + if (offset < buf->len) + memmove(buf->contents+offset+len, buf->contents+offset, buf->len-offset); + buf->len += len; - memcpy(buffer->contents+offset, new_content, len); + memcpy(buf->contents+offset, new_content, len); if (buffer_contents_updated && !do_not_callback) - buffer_contents_updated(buffer, term.c.x, term.c.y, BUFFER_CONTENT_NORMAL_EDIT); + buffer_contents_updated(buf, offset, BUFFER_CONTENT_NORMAL_EDIT); } void -buffer_change(Buffer* buffer, const char* new_content, const int len, const int offset, int do_not_callback) +buffer_change(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback) { - assert(buffer->contents); - if (offset > buffer->len) - die("writing past buffer %s\n", buffer->file_path); - - if (offset + len > buffer->len) { - buffer->len = offset + len; - if (buffer->len >= buffer->capacity) { - buffer->capacity = buffer->len + len + 256; - buffer->contents = xrealloc(buffer->contents, buffer->capacity); + assert(buf->contents); + if (offset > buf->len) + die("writing past buf %s\n", buf->file_path); + + if (offset + len > buf->len) { + buf->len = offset + len; + if (buf->len >= buf->capacity) { + buf->capacity = buf->len + len + 256; + buf->contents = xrealloc(buf->contents, buf->capacity); } } - memcpy(buffer->contents+offset, new_content, len); + memcpy(buf->contents+offset, new_content, len); if (buffer_contents_updated && !do_not_callback) - buffer_contents_updated(buffer, term.c.x, term.c.y, BUFFER_CONTENT_NORMAL_EDIT); + buffer_contents_updated(buf, offset, BUFFER_CONTENT_NORMAL_EDIT); } void -buffer_remove(Buffer* buffer, const int offset, int len, int do_not_calculate_charsize, int do_not_callback) +buffer_remove(struct file_buffer* buf, const int offset, int len, int do_not_calculate_charsize, int do_not_callback) { - assert(buffer->contents); - if (offset > buffer->len) - die("deleting past buffer %s\n", buffer->file_path); + assert(buf->contents); + if (offset > buf->len) + die("deleting past buffer (offset is %d len is %d)\n", offset, buf->len); int removed_len = 0; if (do_not_calculate_charsize) { removed_len = len; } else { while (len--) { - int charsize = t_decode_utf8_buffer(buffer->contents + offset, buffer->len - offset, NULL); - if (buffer->len - charsize < 0) + int charsize = t_decode_utf8_buffer(buf->contents + offset, buf->len - offset, NULL); + if (buf->len - charsize < 0) return; removed_len += charsize; } } - buffer->len -= removed_len; - memmove(buffer->contents+offset, buffer->contents+offset+removed_len, buffer->len-offset); + buf->len -= removed_len; + memmove(buf->contents+offset, buf->contents+offset+removed_len, buf->len-offset); if (buffer_contents_updated && !do_not_callback) - buffer_contents_updated(buffer, term.c.x, term.c.y, BUFFER_CONTENT_NORMAL_EDIT); + buffer_contents_updated(buf, offset, BUFFER_CONTENT_NORMAL_EDIT); } -void -buffer_x_scroll(Buffer* buffer, const int amount) +int +buffer_get_charsize(Rune u, int cur_x_pos) { - buffer->x_scroll += amount; - if (buffer->x_scroll < 0) { - buffer->x_scroll = 0; - return; - } + if (u == '\t') + return 8 - (cur_x_pos % tabspaces); + if (u == '\n') + return 0; + return wcwidth(u); - // prevent you from scrolling outside of the screen - buffer_snap_cursor(buffer, 0); } void -buffer_write_to_screen(Buffer* buffer) +buffer_offset_to_xy(struct window_buffer* buf, int offset, int maxx, int* cx, int* cy) { - //TODO: render tabs - Glyph attr = term.c.attr; + assert(buf); + struct file_buffer* fb = get_file_buffer(buf); + + LIMIT(offset, 0, fb->len-1); + *cx = *cy = 0; + + char* repl = fb->contents; + char* last = repl + offset; - int line = buffer->y_scroll; - const int xscroll = buffer->x_scroll; - int x = 0, y = 0; - tclearregion(0, 0, term.col-1, term.row-1); + char* new_repl; + if (WRAP_BUFFER && maxx > 0) { + int yscroll = 0; + while ((new_repl = memchr(repl, '\n', last - repl))) { + if (++yscroll >= buf->y_scroll) + break; + repl = new_repl+1; + } + *cy = yscroll - buf->y_scroll; + } else { + while ((new_repl = memchr(repl, '\n', last - repl))) { + repl = new_repl+1; + *cy += 1; + } + *cy -= buf->y_scroll; + } + + while (repl < last) { +#if WRAP_BUFFER + if (maxx > 0 && (*repl == '\n' || *cx >= maxx)) { + *cy += 1; + *cx = 0; + repl++; + continue; + } +#endif // WRAP_BUFFER + if (*repl == '\t') { + repl++; + if (*cx <= 0) *cx += 1; + while (*cx % tabspaces != 0) *cx += 1; + *cx += 1; + continue; + } + Rune u; + repl += t_decode_utf8_buffer(repl, last - repl, &u); + *cx += wcwidth(u); + } +} - char* repl = buffer->contents; - char* last = repl + buffer->len; +void +buffer_write_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, int maxy) +{ + assert(buf); + struct file_buffer* fb = get_file_buffer(buf); + + LIMIT(maxx, 0, term.col-1); + LIMIT(maxy, 0, term.row-1); + LIMIT(minx, 0, maxx-1); + LIMIT(miny, 0, maxy-1); + + int x = minx, y = miny; + tsetregion(minx, miny, maxx, maxy, ' '); + + // force the screen in a place where the cursor is visable + int ox, oy; + buffer_offset_to_xy(buf, buf->cursor_offset, maxx - minx, &ox, &oy); + ox += minx - maxx; + int xscroll = 0; + if (ox > 0) + xscroll = ox; + if (oy < 0) { + buf->y_scroll += oy; + } else { + oy += miny - maxy; + if (oy > 0) + buf->y_scroll += oy; + } + if (buf->y_scroll < 0) + buf->y_scroll = 0; + // move to y_scroll + char* repl = fb->contents; + char* last = repl + fb->len; char* new_repl; + int line = buf->y_scroll; while ((new_repl = memchr(repl, '\n', last - repl))) { if (--line < 0) break; - if (new_repl+1 < last) { + else if (new_repl+1 < last) repl = new_repl+1; - } else { + else return; - } } + // actually write to the screen + int once = 0; for (int charsize = 1; repl < last && charsize; repl += charsize) { - if (x - xscroll >= term.col) + // if the buffer being drawn is focused, set the cursor position global + if (!once && buf == focused_window && repl - fb->contents >= buf->cursor_offset) { + once = 1; + cursor_x = x; + cursor_y = y; + LIMIT(cursor_x, minx, maxx); + LIMIT(cursor_y, miny, maxy); + } + + +#if WRAP_BUFFER + xscroll = 0; + if (*repl == '\n' || x >= maxx) { +#else + if (x - xscroll > maxx) repl = memchr(repl, '\n', last - repl); if (*repl == '\n') { - x = 0; - if (++y >= term.row) +#endif // WRAP_BUFFER + + x = minx; + if (++y > maxy) break; + +#if WRAP_BUFFER + if (*repl == '\n') { + charsize = 1; + continue; + } +#else charsize = 1; continue; +#endif // WRAP_BUFFER + } else if (*repl == '\t') { charsize = 1; if (x <= 0) { - x += tsetchar(' ', attr, x - xscroll, y); - if (x >= term.col) + x += tsetchar(' ', x - xscroll, y); + if (x >= maxx) continue; } - while (abs(x) % tabspaces != 0 && x - xscroll < term.col) - x += tsetchar(' ', attr, x - xscroll, y); + while (x % tabspaces != 0 && x - xscroll <= maxx) + x += tsetchar(' ', x - xscroll, y); - if (x - xscroll >= term.col) - continue; - x += tsetchar(' ', attr, x, y); + if (x - xscroll <= maxx) + x += tsetchar(' ', x, y); continue; } Rune u; charsize = t_decode_utf8_buffer(repl, last - repl, &u); - int width = 1; - if (x - xscroll >= 0) - width = tsetchar(u, attr, x - xscroll, y); + int width; + if (x - xscroll >= minx) + width = tsetchar(u, x - xscroll, y); + else + width = wcwidth(u); x += width; } + buffer_write_selection(buf, minx, miny, maxx, maxy); } void @@ -511,71 +760,58 @@ colour_selection(Glyph* letter) int -buffer_is_selection_start_top_left(Buffer* buffer) +buffer_is_selection_start_top_left(const struct file_buffer* buf) { - return (buffer->s1o <= buffer->s2o) ? 1 : 0; + return (buf->s1o <= buf->s2o) ? 1 : 0; } void -buffer_move_cursor_to_selection_start(Buffer* buffer) +buffer_move_cursor_to_selection_start(struct window_buffer* buf) { - if (buffer_is_selection_start_top_left(buffer)) - buffer_move_cursor_to_offset(buffer, buffer->s1o, 0); + const struct file_buffer* fb = get_file_buffer(buf); + if (buffer_is_selection_start_top_left(fb)) + buffer_move_to_offset(buf, fb->s1o, CURSOR_SNAPPED); else - buffer_move_cursor_to_offset(buffer, buffer->s2o, 0); + buffer_move_to_offset(buf, fb->s2o, CURSOR_SNAPPED); } void -set_selection_start_end(int* x1, int* x2, int* y1, int* y2, Buffer* buffer) +buffer_write_selection(struct window_buffer* buf, int minx, int miny, int maxx, int maxy) { - int prevx = term.c.x, prevy = term.c.y; - int prevxscroll = buffer->x_scroll, prevyscroll = buffer->y_scroll; - buffer_move_cursor_to_offset(buffer, buffer->s1o, 1); - int s1xscroll = buffer->x_scroll, s1yscroll = buffer->y_scroll; - if (buffer_is_selection_start_top_left(buffer)) { - *x1 = term.c.x + (s1xscroll - prevxscroll); - *y1 = term.c.y + (s1yscroll - prevyscroll); - *x2 = prevx+1; - *y2 = prevy; - } else { - *x1 = prevx; - *y1 = prevy; - *x2 = term.c.x+1 + (s1xscroll - prevxscroll); - *y2 = term.c.y + (s1yscroll - prevyscroll); - } - buffer_move_cursor(buffer, prevx, prevy, 0); - buffer->x_scroll = prevxscroll; - buffer->y_scroll = prevyscroll; - LIMIT(*x1, 0, term.col-1); - LIMIT(*x2, 0, term.col-1); - LIMIT(*y1, 0, term.row-1); - LIMIT(*y2, 0, term.row-1); -} + assert(buf); + struct file_buffer* fb = get_file_buffer(buf); -void -buffer_write_selection(Buffer* buffer) -{ - if (!(buffer->mode & BUFFER_SELECTION_ON)) + LIMIT(maxx, 0, term.col-1); + LIMIT(maxy, 0, term.row-1); + LIMIT(minx, 0, maxx-1); + LIMIT(miny, 0, maxy-1); + + //TODO: implement alternative selection modes + if (!(fb->mode & BUFFER_SELECTION_ON)) return; - // colour selected area int x, y, x2, y2; + if (buffer_is_selection_start_top_left(fb)) { + buffer_offset_to_xy(buf, fb->s1o, maxx - minx, &x, &y); + buffer_offset_to_xy(buf, fb->s2o, maxx - minx, &x2, &y2); + } else { + buffer_offset_to_xy(buf, fb->s2o, maxx - minx, &x, &y); + buffer_offset_to_xy(buf, fb->s1o, maxx - minx, &x2, &y2); + } + x += minx, x2 += minx + 1; + y += miny, y2 += miny; - //TODO: implement alternative selection modes - set_selection_start_end(&x, &x2, &y, &y2, buffer); for(; y < y2; y++) { - for(; x < term.col; x++) { + for(; x < maxx; x++) colour_selection(&term.line[y][x]); - } x = 0; } - for(; x < x2; x++) { + for(; x < x2; x++) colour_selection(&term.line[y][x]); - } } -char* buffer_get_selection(Buffer* buffer, int* selection_len) +char* buffer_get_selection(struct file_buffer* buffer, int* selection_len) { if (!(buffer->mode & BUFFER_SELECTION_ON)) return NULL; @@ -599,7 +835,7 @@ char* buffer_get_selection(Buffer* buffer, int* selection_len) } void -buffer_remove_selection(Buffer* buffer) +buffer_remove_selection(struct file_buffer* buffer) { if (!(buffer->mode & BUFFER_SELECTION_ON)) return; @@ -615,11 +851,11 @@ buffer_remove_selection(Buffer* buffer) len = end - start; buffer_remove(buffer, start, len, 1, 1); if (buffer_contents_updated) - buffer_contents_updated(buffer, term.c.x, term.c.y, BUFFER_CONTENT_BIG_CHANGE); + buffer_contents_updated(buffer, start, BUFFER_CONTENT_BIG_CHANGE); } void -buffer_write_to_filepath(const Buffer* buffer) +buffer_write_to_filepath(const struct file_buffer* buffer) { if (!buffer->file_path) return; @@ -627,6 +863,8 @@ buffer_write_to_filepath(const Buffer* buffer) FILE* file = fopen(buffer->file_path, "w"); assert(file); + if (buffer->mode & BUFFER_UTF8_SIGNED) + fwrite("\xEF\xBB\xBF", 1, 3, file); fwrite(buffer->contents, sizeof(char), buffer->len, file); fclose(file); @@ -642,105 +880,16 @@ t_decode_utf8_buffer(const char* buffer, const int buflen, Rune* u) if (!u) u = &u_tmp; - if (IS_SET(MODE_UTF8)) { - /* process a complete utf8 char */ - charsize = utf8decode(buffer, u, buflen); - } else { - *u = buffer[0] & 0xFF; - charsize = 1; - } + /* process a complete utf8 char */ + charsize = utf8decode(buffer, u, buflen); return charsize; } int -buffer_snap_cursor(Buffer* buffer, int do_not_callback) -{ - char* repl = buffer->contents; - char* last = repl + buffer->len; - int line = buffer->y_scroll + term.c.y; - const int xscroll = buffer->x_scroll; - int x = 0, y = 0; - - char* new_repl; - while ((new_repl = memchr(repl, '\n', last - repl))) { - if (line-- <= 0) - break; - else if (line - term.c.y < 0) - y++; - - if (new_repl+1 < last) { - repl = new_repl+1; - } else { - buffer_move_cursor_to_offset(buffer, buffer->len, 0); - return buffer->len; - } - } - - for (int charsize = 1; repl < last; repl += charsize) { - if (x - xscroll >= term.c.x || *repl == '\n') { - break; - } else if (*repl == '\t') { - int prevx = x; - if (x - xscroll + 1 == 0) - x++; - while ((x+1) % tabspaces != 0) - x++; - x += 2; - if (x - xscroll > term.c.x) { - x = prevx; - break; - } - charsize = 1; - continue; - } - Rune u; - if (!(charsize = t_decode_utf8_buffer(repl, last - repl, &u))) - return -1; - x += wcwidth(u); - } - x -= xscroll; - if (term.c.x != x || term.c.y != y) - buffer_move_cursor(buffer, x, y, (do_not_callback) ? 0 : CURSOR_SNAPPED); - return repl - buffer->contents; -} - -void -buffer_move_cursor_to_offset(Buffer* buffer, int offset, int do_not_callback) -{ - if (offset > buffer->len-1) - offset = buffer->len-1; - char* repl = buffer->contents; - char* last = repl + offset; - int x = 0, y = 0; - - char* new_repl; - while ((new_repl = memchr(repl, '\n', last - repl))) { - y++; - repl = new_repl+1; - } - for (int charsize; repl < last; repl += charsize) { - Rune u; - if(!(charsize = t_decode_utf8_buffer(repl, last - repl, &u))) - return; - if (*repl == '\t') { - if (x == 0) - x++; - while (x % tabspaces != 0) - x++; - x++; - } else { - x += wcwidth(u); - } - } - - buffer->x_scroll = buffer->y_scroll = 0; - buffer_move_cursor(buffer, x, y, (do_not_callback) ? 0 : CURSOR_SNAPPED); -} - -int -tsetchar(Rune u, Glyph attr, int x, int y) +tsetchar(Rune u, int x, int y) { + Glyph attr = default_attributes; if (y >= term.row || x >= term.col || y < 0 || x < 0) return 1; @@ -761,7 +910,6 @@ tsetchar(Rune u, Glyph attr, int x, int y) term.line[y][x-1].mode &= ~ATTR_WIDE; } - term.dirty[y] = 1; term.line[y][x] = attr; term.line[y][x].u = u; @@ -769,7 +917,7 @@ tsetchar(Rune u, Glyph attr, int x, int y) } void -tclearregion(int x1, int y1, int x2, int y2) +tsetregion(int x1, int y1, int x2, int y2, Rune u) { int x, y, temp; @@ -784,12 +932,11 @@ tclearregion(int x1, int y1, int x2, int y2) LIMIT(y2, 0, term.row-1); for (y = y1; y <= y2; y++) { - term.dirty[y] = 1; for (x = x1; x <= x2; x++) { - term.line[y][x].fg = term.c.attr.fg; - term.line[y][x].bg = term.c.attr.bg; + term.line[y][x].fg = default_attributes.fg; + term.line[y][x].bg = default_attributes.bg; term.line[y][x].mode = 0; - term.line[y][x].u = ' '; + term.line[y][x].u = u; } } } @@ -800,7 +947,6 @@ tresize(int col, int row) int i; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); - TCursor c; if (col < 1 || row < 1) { fprintf(stderr, @@ -808,110 +954,48 @@ tresize(int col, int row) return; } - /* - * slide screen to keep cursor where we expect it - - * tscrollup would work here, but we can optimize to - * memmove because we're freeing the earlier lines - */ - for (i = 0; i <= term.c.y - row; i++) { - free(term.line[i]); - free(term.alt[i]); - } - /* ensure that both src and dst are not NULL */ - if (i > 0) { - memmove(term.line, term.line + i, row * sizeof(Line)); - memmove(term.alt, term.alt + i, row * sizeof(Line)); - } - for (i += row; i < term.row; i++) { - free(term.line[i]); - free(term.alt[i]); - } - /* resize to new height */ + if (row < term.row) + for (i = row; i < term.row; i++) + free(term.line[i]); + term.line = xrealloc(term.line, row * sizeof(Line)); - term.alt = xrealloc(term.alt, row * sizeof(Line)); - term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); - term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + if (row > term.row) + for (i = term.row; i < row; i++) + term.line[i] = NULL; /* resize each row to new width, zero-pad if needed */ - for (i = 0; i < minrow; i++) { + for (i = 0; i < row; i++) term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); - term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); - } - /* allocate any new rows */ - for (/* i = minrow */; i < row; i++) { - term.line[i] = xmalloc(col * sizeof(Glyph)); - term.alt[i] = xmalloc(col * sizeof(Glyph)); - } - if (col > term.col) { - int* bp = term.tabs + term.col; - - memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); - while (--bp > term.tabs && !*bp) - /* nothing */ ; - for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) - *bp = 1; - } /* update terminal size */ term.col = col; term.row = row; - /* move cursor */ - term.c.x = MIN(term.c.x, term.col-1); - term.c.y = MIN(term.c.y, term.row-1); - if (cursor_movement_callback) - cursor_movement_callback(term.c.x, term.c.y, CURSOR_WINDOW_RESIZED); - /* Clearing both screens (it makes dirty all lines) */ - c = term.c; - for (i = 0; i < 2; i++) { - if (mincol < col && 0 < minrow) { - tclearregion(mincol, 0, col - 1, minrow - 1); - } - if (0 < col && minrow < row) { - tclearregion(0, minrow, col - 1, row - 1); - } - tswapscreen(); - } - term.c = c; -} -void -resettitle(void) -{ - xsettitle(NULL); + /* Clear screen */ + if (mincol < col && 0 < minrow) + tsetregion(mincol, 0, col - 1, minrow - 1, ' '); + if (0 < col && minrow < row) + tsetregion(0, minrow, col - 1, row - 1, ' '); } -void -drawregion(int x1, int y1, int x2, int y2) -{ - int y; - - for (y = y1; y < y2; y++) { - if (!term.dirty[y]) - continue; - - term.dirty[y] = 0; - xdrawline(term.line[y], x1, y, x2); - } -} void -draw(void) +draw(int cursor_x, int cursor_y) { + LIMIT(cursor_x, 0, term.col-1); + LIMIT(cursor_y, 0, term.row-1); + if (!xstartdraw()) return; - if (term.line[term.c.y][term.c.x].mode & ATTR_WDUMMY) - term.c.x--; + if (term.line[cursor_y][cursor_x].mode & ATTR_WDUMMY) + cursor_x--; - drawregion(0, 0, term.col, term.row); - xdrawcursor(term.c.x, term.c.y, term.line[term.c.y][term.c.x]); + for (int y = 0; y < term.row; y++) + xdrawline(term.line[y], 0, y, term.col); - xfinishdraw(); -} + xdrawcursor(cursor_x, cursor_y, term.line[cursor_y][cursor_x]); -void -redraw(void) -{ - tfulldirt(); - draw(); + xfinishdraw(); } diff --git a/st.h b/st.h @@ -1,4 +1,6 @@ /* See LICENSE for license details. */ +#ifndef _ST_H +#define _ST_H #include <stdint.h> #include <sys/types.h> @@ -14,8 +16,6 @@ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ (a).bg != (b).bg) -#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ - (t1.tv_nsec-t2.tv_nsec)/1E6) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) @@ -43,13 +43,13 @@ typedef unsigned short ushort; typedef uint_least32_t Rune; -#define Glyph Glyph_ typedef struct { Rune u; /* character code */ ushort mode; /* attribute flags */ uint32_t fg; /* foreground */ uint32_t bg; /* background */ -} Glyph; +} Glyph_; +#define Glyph Glyph_ typedef Glyph *Line; @@ -62,85 +62,99 @@ typedef union { const char *s; } Arg; +struct undo_buffer { + char* contents; // not null terminated + int len, capacity; + int cursor_offset; + int y_scroll; +}; + +struct window_buffer { + int y_scroll; + int cursor_offset; + int cursor_col; // last right_left movement for snapping the cursor + + int buffer_index; // index into an array storing buffers +}; + +enum window_split_mode { + WINDOW_SINGULAR, + WINDOW_HORISONTAL, + WINDOW_VERTICAL, + WINDOW_HORISONTAL_NOT_RESISABLE, +}; +#define IS_HORISONTAL(mode) ((mode) == WINDOW_HORISONTAL || (mode) == WINDOW_HORISONTAL_NOT_RESISABLE) + +struct window_split_node { + struct window_buffer window; + enum window_split_mode mode; + float ratio; + struct window_split_node *node1, *node2, *parent; +}; + +void window_buffer_split(struct window_split_node* parent, float ratio, enum window_split_mode mode); +// uses focused_window to draw the cursor +void window_write_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy); + +enum move_directons { + MOVE_RIGHT, + MOVE_LEFT, + MOVE_UP, + MOVE_DOWN, +}; +struct window_split_node* window_switch_to_window(struct window_split_node* current, enum move_directons move); + + enum buffer_flags { BUFFER_SELECTION_ON = 1 << 0, BUFFER_BLOCK_SELECT = 1 << 1, BUFFER_READ_ONLY = 1 << 2, -}; - -struct undo_buffer { - char* contents; // not null terminated - int len, capacity; - int cx, cy; // cursor x and y - int xscroll, yscroll; + BUFFER_UTF8_SIGNED = 1 << 3, }; /* Contents of a file buffer */ -typedef struct { +struct file_buffer { char* file_path; char* contents; // !! NOT NULL TERMINATED !! int len; int capacity; - int y_scroll, x_scroll; - int cursor_col; int mode; // flags - int s1o, s2o; // selection start offset and end offset struct undo_buffer* ub; - int undo_buffer_len; int current_undo_buffer; int available_redo_buffers; -} Buffer; - -void bufferwrite(const char* buf, const int buflen); -int buffer_new(Buffer* buffer, char* file_path); -void buffer_insert(Buffer* buffer, const char* new_content, const int len, const int offset, int do_not_callback); -void buffer_change(Buffer* buffer, const char* new_content, const int len, const int offset, int do_not_callback); -void buffer_remove(Buffer* buffer, const int offset, int len, int do_not_calculate_charsize, int do_not_callback); -void buffer_write_to_screen(Buffer* buffer); -void buffer_write_to_filepath(const Buffer* buffer); -int buffer_snap_cursor(Buffer* buffer, int do_not_callback); -void buffer_move_cursor_to_offset(Buffer* buffer, int offset, int do_not_callback); -void buffer_scroll(Buffer* buffer, int xscroll, int yscroll); - -void buffer_write_selection(Buffer* buffer); + int s1o, s2o; // selection start offset and end offset + //TODO: colour instructions +}; + +struct file_buffer buffer_new(char* file_path); +void buffer_insert(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback); +void buffer_change(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback); +void buffer_remove(struct file_buffer* buf, const int offset, int len, int do_not_calculate_charsize, int do_not_callback); +void buffer_offset_to_xy(struct window_buffer* buf, int offset, int maxx, int* cx, int* cy); +void buffer_write_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, int maxy); +void buffer_write_to_filepath(const struct file_buffer* buffer); + +void buffer_write_selection(struct window_buffer* buf, int minx, int miny, int maxx, int maxy); /////////////////////////////////// // returns a null terminated string containing the selection // the returned value must be freed by the reciever // for conveniance the length of the string may be taken with the pointer // a selection_len of NULL wil be ignored -char* buffer_get_selection(Buffer* buffer, int* selection_len); -int buffer_is_selection_start_top_left(Buffer* buffer); -void buffer_move_cursor_to_selection_start(Buffer* buffer); -void buffer_remove_selection(Buffer* buffer); +char* buffer_get_selection(struct file_buffer* buf, int* selection_len); +int buffer_is_selection_start_top_left(const struct file_buffer* buffer); +void buffer_move_cursor_to_selection_start(struct window_buffer* buffer); +void buffer_remove_selection(struct file_buffer* buffer); void die(const char *, ...); -void redraw(void); -void draw(void); -enum term_mode { - MODE_INSERT = 1 << 1, - MODE_ALTSCREEN = 1 << 2, - MODE_UTF8 = 1 << 6, -}; - -//TODO: make these attributes more fit this program -typedef struct { - Glyph attr; /* current char attributes */ - int x; - int y; -} TCursor; +void draw(int cursor_x, int cursor_y); /* Internal representation of the screen */ typedef struct { int row; /* nb row */ int col; /* nb col */ Line *line; /* screen */ - Line *alt; /* alternate screen */ - int *dirty; /* dirtyness of lines */ - TCursor c; /* cursor */ int ocx, ocy; // old cursor - int mode; /* terminal mode flags */ - int *tabs; Rune lastc; /* last printed char outside of sequence, 0 if control */ } Term; extern Term term; @@ -148,15 +162,10 @@ extern Term term; int tattrset(int); void tnew(int, int); void tresize(int, int); -void tsetdirtattr(int); - -int tisdirty(); -int tsetchar(Rune, Glyph, int, int); - -void resettitle(void); +void tsetregion(int x1, int y1, int x2, int y2, Rune u); +int tsetchar(Rune u, int x, int y); size_t utf8encode(Rune, char *); - void *xmalloc(size_t); void *xrealloc(void *, size_t); @@ -178,19 +187,27 @@ enum buffer_content_reason { BUFFER_CONTENT_INIT, }; -void buffer_move_cursor(Buffer* buffer, int x, int y, enum cursor_reason callback_reason); -void buffer_move_cursor_relative(Buffer* buffer, int x, int y, enum cursor_reason callback_reason); +void buffer_move_on_line(struct window_buffer* buf, int amount, enum cursor_reason callback_reason); +void buffer_move_lines(struct window_buffer* buf, int amount, enum cursor_reason callback_reason); +void buffer_move_to_offset(struct window_buffer* buf, int offset, enum cursor_reason callback_reason); +void buffer_move_to_x(struct window_buffer* buf, int x, enum cursor_reason callback_reason); +int buffer_seek_char(const struct file_buffer* buf, int offset, char byte); +int buffer_seek_char_backwards(const struct file_buffer* buf, int offset, char byte); +int buffer_seek_string(const struct file_buffer* buf, int offset, const char* string); +int buffer_seek_string_backwards(const struct file_buffer* buf, int offset, const char* string); /* callbacks */ -extern void(*cursor_movement_callback)(int, int, enum cursor_reason); // cursor x & y, callback_reason -extern void(*buffer_contents_updated)(Buffer*, int, int, enum buffer_content_reason); // modified buffer, cursor x & y +extern void(*cursor_movement_callback)(struct window_buffer*, enum cursor_reason); // buffer, current_pos, reason +extern void(*buffer_contents_updated)(struct file_buffer*, int, enum buffer_content_reason); // modified buffer, current_pos // TODO: planned callbacks: // buffer written /* config.h globals */ +extern Glyph default_attributes; extern char *termname; extern unsigned int tabspaces; -extern unsigned int defaultfg; -extern unsigned int defaultbg; -extern int default_mode; -extern int undo_buffers; + +#define UNDO_BUFFERS_COUNT 32 +#define WRAP_BUFFER 0 + +#endif // _ST_H diff --git a/win.h b/win.h @@ -1,4 +1,8 @@ /* See LICENSE for license details. */ +#ifndef _WIN_H +#define _WIN_H + +#include "st.h" enum win_mode { MODE_VISIBLE = 1 << 0, @@ -21,8 +25,11 @@ void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); void xseticontitle(char *); -void xsettitle(char *); int xsetcursor(int); void xsetpointermotion(int); int xstartdraw(void); -void xximspot(int, int); + +struct file_buffer* get_file_buffer(struct window_buffer* buf); +int new_file_buffer(struct file_buffer buf); + +#endif // _WIN_H diff --git a/x.c b/x.c @@ -15,51 +15,18 @@ #include <X11/Xft/Xft.h> #include <X11/XKBlib.h> -#include "st.h" #include "win.h" -/* types used in config.h */ -typedef struct { - uint mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Shortcut; - -/* X modifiers */ -#define XK_ANY_MOD UINT_MAX -#define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13|1<<14) - -/* function definitions used in config.h */ -static void numlock(const Arg *); -static void zoom(const Arg *); -static void zoomabs(const Arg *); -static void zoomreset(const Arg *); -static void cursor_move_x_relative(const Arg* arg); -static void cursor_move_y_relative(const Arg* arg); -static void cursor_move_absolute(const Arg* arg); -static void save_buffer(const Arg* arg); -static void toggle_selection(const Arg* arg); -static void move_cursor_to_offset(const Arg* arg); -static void move_cursor_to_end_of_buffer(const Arg* arg); -static void clipboard_copy(const Arg* arg); -static void clipboard_paste(const Arg* arg); -static void set_line_dirty(int y); -static void undo(const Arg* arg); -static void redo(const Arg* arg); - -static void scroll_buffer_when_resize(int x, int y, enum cursor_reason callback_reason); -static void keep_cursor_col(int x, int y, enum cursor_reason callback_reason); -static void move_selection(int x, int y, enum cursor_reason callback_reason); -static void cursor_callback(int x, int y, enum cursor_reason callback_reason); - -static void add_to_undo_buffer(Buffer* buffer, int x, int y, enum buffer_content_reason reason); -static void buffer_content_callback(Buffer* buffer, int x, int y, enum buffer_content_reason reason); - /* config.h for applying patches and the configuration. */ #include "config.h" +#define Glyph Glyph_ + +static void keep_cursor_col(struct window_buffer* buf, enum cursor_reason callback_reason); +static void move_selection(struct window_buffer* buf, enum cursor_reason callback_reason); + +static void add_to_undo_buffer(struct file_buffer* buffer, int offset, enum buffer_content_reason reason); + /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 @@ -151,6 +118,7 @@ static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); +static void xsettitle(char *); static void expose(XEvent *); static void visibility(XEvent *); @@ -163,7 +131,7 @@ static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); static int match(uint, uint); -static void buffer_copy_ub_to_current(Buffer* buffer); +static void buffer_copy_ub_to_current(struct window_buffer* buffer); static void run(void); @@ -182,13 +150,21 @@ static void (*handler[LASTEvent])(XEvent *) = { }; /* Globals */ +static struct window_split_node root_node = {.mode = WINDOW_SINGULAR}; +static struct window_split_node* focused_node = &root_node; + +// cursor is set when calling buffer_write_to_screen and the buffer is focused_window +struct window_buffer* focused_window = {0}; +int cursor_x, cursor_y; + +static struct file_buffer* file_buffers; +static int available_buffer_slots; static DC dc; static XWindow xw; static Atom xtarget; static char* copy_buffer; static int copy_len; static TermWindow win; -static Buffer focused_buffer = {0}; /* Font Ring Cache */ enum { @@ -226,6 +202,22 @@ numlock(const Arg *dummy) win.mode ^= MODE_NUMLOCK; } +void window_split(const Arg *arg) +{ + window_buffer_split(focused_node, 0.5, arg->i); + focused_node = focused_node->node2; + focused_window = &focused_node->window; +} + +void window_move(const Arg *arg) +{ + struct window_split_node* new_node = window_switch_to_window(focused_node, arg->i); + if (new_node->mode == WINDOW_SINGULAR) { + focused_node = new_node; + focused_window = &focused_node->window; + } +} + void zoom(const Arg *arg) { @@ -241,7 +233,6 @@ zoomabs(const Arg *arg) xunloadfonts(); xloadfonts(usedfont, arg->f); cresize(0, 0); - redraw(); xhints(); } @@ -259,59 +250,52 @@ zoomreset(const Arg *arg) void cursor_move_x_relative(const Arg* arg) { - buffer_move_cursor_relative(&focused_buffer, arg->i, 0, - CURSOR_RIGHT_LEFT_MOVEMENT); - buffer_snap_cursor(&focused_buffer, 0); + buffer_move_on_line(focused_window, arg->i, CURSOR_RIGHT_LEFT_MOVEMENT); } void cursor_move_y_relative(const Arg* arg) { - int x = (focused_buffer.x_scroll) ? - focused_buffer.cursor_col - focused_buffer.x_scroll : focused_buffer.cursor_col; - buffer_move_cursor(&focused_buffer, x, term.c.y + arg->i, CURSOR_UP_DOWN_MOVEMENT); - buffer_snap_cursor(&focused_buffer, 0); -} - -void cursor_move_absolute(const Arg* arg) -{ - buffer_move_cursor(&focused_buffer, arg->vec2i[0], arg->vec2i[1], 1); + buffer_move_lines(focused_window, arg->i, 0); + buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT); } void save_buffer(const Arg* arg) { - buffer_write_to_filepath(&focused_buffer); + buffer_write_to_filepath(get_file_buffer(focused_window)); } void toggle_selection(const Arg* arg) { - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - focused_buffer.mode &= ~(BUFFER_SELECTION_ON); + struct file_buffer* fb = get_file_buffer(focused_window); + if (fb->mode & BUFFER_SELECTION_ON) { + fb->mode &= ~(BUFFER_SELECTION_ON); } else { - focused_buffer.mode |= BUFFER_SELECTION_ON; - focused_buffer.s1o = focused_buffer.s2o = buffer_snap_cursor(&focused_buffer, 0); + fb->mode |= BUFFER_SELECTION_ON; + fb->s1o = fb->s2o = focused_window->cursor_offset; } } void move_cursor_to_offset(const Arg* arg) { - buffer_move_cursor_to_offset(&focused_buffer, arg->i, 0); + focused_window->cursor_offset = arg->i; } void move_cursor_to_end_of_buffer(const Arg* arg) { - buffer_move_cursor_to_offset(&focused_buffer, focused_buffer.len, 0); + focused_window->cursor_offset = get_file_buffer(focused_window)->len-1; } void clipboard_copy(const Arg* arg) { + struct file_buffer* fb = get_file_buffer(focused_window); int len; - char* temp = buffer_get_selection(&focused_buffer, &len); + char* temp = buffer_get_selection(fb, &len); if (!temp) return; if (copy_buffer) @@ -319,8 +303,8 @@ clipboard_copy(const Arg* arg) copy_buffer = temp; copy_len = len; - buffer_move_cursor_to_selection_start(&focused_buffer); - focused_buffer.mode &= ~BUFFER_SELECTION_ON; + buffer_move_cursor_to_selection_start(focused_window); + fb->mode &= ~BUFFER_SELECTION_ON; Atom clipboard; clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); @@ -338,45 +322,43 @@ clipboard_paste(const Arg* arg) } void -buffer_copy_ub_to_current(Buffer* buffer) +buffer_copy_ub_to_current(struct window_buffer* buffer) { - assert(buffer); - struct undo_buffer* cub = &buffer->ub[buffer->current_undo_buffer]; + struct file_buffer* fb = get_file_buffer(buffer); + struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer]; assert(cub->contents); - buffer->contents = xrealloc(buffer->contents, cub->capacity); - memcpy(buffer->contents, cub->contents, cub->capacity); - buffer->len = cub->len; - buffer->capacity = cub->capacity; + fb->contents = xrealloc(fb->contents, cub->capacity); + memcpy(fb->contents, cub->contents, cub->capacity); + fb->len = cub->len; + fb->capacity = cub->capacity; - term.c.x = cub->cx, term.c.y = cub->cy; - buffer->x_scroll = cub->xscroll; - buffer->y_scroll = cub->yscroll; - buffer_move_cursor_relative(buffer, 0, 0, 1); + buffer_move_to_offset(buffer, cub->cursor_offset, CURSOR_SNAPPED); + buffer->y_scroll = cub->y_scroll; } void undo(const Arg* arg) { - assert(&focused_buffer); - if (focused_buffer.current_undo_buffer == 0) + struct file_buffer* fb = get_file_buffer(focused_window); + if (fb->current_undo_buffer == 0) return; - focused_buffer.current_undo_buffer--; - focused_buffer.available_redo_buffers++; + fb->current_undo_buffer--; + fb->available_redo_buffers++; - buffer_copy_ub_to_current(&focused_buffer); + buffer_copy_ub_to_current(focused_window); } void redo(const Arg* arg) { - assert(&focused_buffer); - if (focused_buffer.available_redo_buffers == 0) + struct file_buffer* fb = get_file_buffer(focused_window); + if (fb->available_redo_buffers == 0) return; - focused_buffer.available_redo_buffers--; - focused_buffer.current_undo_buffer++; + fb->available_redo_buffers--; + fb->current_undo_buffer++; - buffer_copy_ub_to_current(&focused_buffer); + buffer_copy_ub_to_current(focused_window); } void @@ -457,17 +439,18 @@ selnotify(XEvent *e) *repl++ = '\n'; } - if (focused_buffer.contents) { - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - buffer_remove_selection(&focused_buffer); - buffer_move_cursor_to_selection_start(&focused_buffer); - focused_buffer.mode &= ~(BUFFER_SELECTION_ON); + struct file_buffer* fb = get_file_buffer(focused_window); + if (fb->contents) { + if (fb->mode & BUFFER_SELECTION_ON) { + buffer_remove_selection(fb); + buffer_move_cursor_to_selection_start(focused_window); + fb->mode &= ~(BUFFER_SELECTION_ON); } - int offset = buffer_snap_cursor(&focused_buffer, 0); - buffer_insert(&focused_buffer, (char*)data, nitems * format / 8, offset, 1); - buffer_move_cursor_to_offset(&focused_buffer, offset + (nitems * format / 8), 0); + int offset = focused_window->cursor_offset; + buffer_insert(fb, (char*)data, nitems * format / 8, offset, 1); + buffer_move_to_offset(focused_window, offset + (nitems * format / 8), 0); if (buffer_contents_updated) - buffer_contents_updated(&focused_buffer, term.c.x, term.c.y, BUFFER_CONTENT_BIG_CHANGE); + buffer_contents_updated(fb, offset, BUFFER_CONTENT_BIG_CHANGE); } XFree(data); /* number of 32-bit chunks returned */ @@ -508,7 +491,6 @@ selrequest(XEvent *e) XA_ATOM, 32, PropModeReplace, (uchar *) &string, 1); xev.property = xsre->property; - puts("xa_targets"); } else if (xsre->target == xtarget || xsre->target == XA_STRING) { /* * xith XA_STRING non ascii characters may be incorrect in the @@ -517,7 +499,7 @@ selrequest(XEvent *e) clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); int sel_len; if (xsre->selection == XA_PRIMARY) { - seltext = buffer_get_selection(&focused_buffer, &sel_len); + seltext = buffer_get_selection(get_file_buffer(focused_window), &sel_len); } else if (xsre->selection == clipboard) { seltext = copy_buffer; sel_len = copy_len; @@ -528,8 +510,6 @@ selrequest(XEvent *e) return; } if (seltext) { - //printf("\"%s\"\n", seltext); - puts("seltext"); XChangeProperty(xsre->display, xsre->requestor, xsre->property, xsre->target, 8, PropModeReplace, @@ -537,69 +517,50 @@ selrequest(XEvent *e) xev.property = xsre->property; if (seltext != copy_buffer) free(seltext); - } else { - puts("seltextno"); } } - puts("sending event"); /* all done, send a notification to the listener */ if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) fprintf(stderr, "Error sending SelectionNotify event\n"); } void -keep_cursor_col(int x, int y, enum cursor_reason callback_reason) +keep_cursor_col(struct window_buffer* buf, enum cursor_reason callback_reason) { static int last_cursor_col; if (callback_reason == CURSOR_COMMAND_MOVEMENT || callback_reason == CURSOR_RIGHT_LEFT_MOVEMENT) { - focused_buffer.cursor_col = x + focused_buffer.x_scroll; - last_cursor_col = x; + int y; + buffer_offset_to_xy(buf, buf->cursor_offset, -1, &buf->cursor_col, &y); + last_cursor_col = buf->cursor_col; } else if (callback_reason == CURSOR_WINDOW_RESIZED) { + int x, y; + buffer_offset_to_xy(buf, buf->cursor_offset, -1, &x, &y); if (last_cursor_col != x) - focused_buffer.cursor_col = x + focused_buffer.x_scroll; + buf->cursor_col = x; } } -void -scroll_buffer_when_resize(int x, int y, enum cursor_reason callback_reason) +void move_selection(struct window_buffer* buf, enum cursor_reason callback_reason) { - static int lastx, lasty; - - if (callback_reason == CURSOR_WINDOW_RESIZED && focused_buffer.contents) { - if (lastx < x) lastx = x; - buffer_scroll(&focused_buffer, lastx - x, lasty - y); - } - lastx = x, lasty = y; -} - -void move_selection(int x, int y, enum cursor_reason callback_reason) -{ - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - focused_buffer.s2o = buffer_snap_cursor(&focused_buffer, 0); + struct file_buffer* fb = get_file_buffer(buf); + if (fb->mode & BUFFER_SELECTION_ON) { + fb->s2o = buf->cursor_offset; } } void -cursor_callback(int x, int y, enum cursor_reason callback_reason) +cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason) { - keep_cursor_col(x, y, callback_reason); - scroll_buffer_when_resize(x, y, callback_reason); - move_selection(x, y, callback_reason); - set_line_dirty(y); - - printf("%d %d reason %d\n", x, y, callback_reason); -} + keep_cursor_col(buf, callback_reason); + move_selection(buf, callback_reason); -void -set_line_dirty(int y) -{ - term.dirty[y] = 1; + printf("mved to: %d | reason: %d\n", buf->cursor_offset, callback_reason); } void -add_to_undo_buffer(Buffer* buffer, int x, int y, enum buffer_content_reason reason) +add_to_undo_buffer(struct file_buffer* buffer, int offset, enum buffer_content_reason reason) { static time_t last_normal_edit; static int edits; @@ -624,9 +585,9 @@ add_to_undo_buffer(Buffer* buffer, int x, int y, enum buffer_content_reason reas goto copy_undo_buffer; } - if (buffer->current_undo_buffer == buffer->undo_buffer_len-1) { + if (buffer->current_undo_buffer == UNDO_BUFFERS_COUNT-1) { char* begin_buffer = buffer->ub[0].contents; - memmove(buffer->ub, &(buffer->ub[1]), (buffer->undo_buffer_len-1) * sizeof(struct undo_buffer)); + memmove(buffer->ub, &(buffer->ub[1]), (UNDO_BUFFERS_COUNT-1) * sizeof(struct undo_buffer)); buffer->ub[buffer->current_undo_buffer].contents = begin_buffer; } else { buffer->current_undo_buffer++; @@ -639,16 +600,47 @@ copy_undo_buffer: ; memcpy(cub->contents, buffer->contents, buffer->capacity); cub->len = buffer->len; cub->capacity = buffer->capacity; - cub->cx = x, cub->cy = y; - cub->xscroll = buffer->x_scroll; - cub->yscroll = buffer->y_scroll; + cub->cursor_offset = offset; + if (focused_window) + cub->y_scroll = focused_window->y_scroll; + else + cub->y_scroll = 0; } void -buffer_content_callback(Buffer* buffer, int x, int y, enum buffer_content_reason reason) +buffer_content_callback(struct file_buffer* buffer, int offset, enum buffer_content_reason reason) { - set_line_dirty(y); - add_to_undo_buffer(buffer, x, y, reason); + add_to_undo_buffer(buffer, offset, reason); +} + +struct file_buffer* +get_file_buffer(struct window_buffer* buf) +{ + assert(file_buffers); + + if (buf->buffer_index < 0) + buf->buffer_index = available_buffer_slots-1; + else if (buf->buffer_index >= available_buffer_slots) + buf->buffer_index = 0; + + return (file_buffers[buf->buffer_index].contents) ? &(file_buffers[buf->buffer_index]) : NULL; +} + +int +new_file_buffer(struct file_buffer buf) +{ + assert(buf.contents); + for(int n = 0; n < available_buffer_slots; n++) { + if (!file_buffers[n].contents) { + file_buffers[n] = buf; + return n; + } + } + + available_buffer_slots++; + file_buffers = xrealloc(file_buffers, sizeof(struct file_buffer) * available_buffer_slots); + file_buffers[available_buffer_slots-1] = buf; + return available_buffer_slots-1; } void @@ -764,7 +756,7 @@ xsetcolorname(int x, const char *name) void xclear(int x1, int y1, int x2, int y2) { - XftDrawRect(xw.draw, &dc.col[defaultbg], + XftDrawRect(xw.draw, &dc.col[default_attributes.bg], x1, y1, x2-x1, y2-y1); } @@ -1072,8 +1064,8 @@ xinit(int cols, int rows) xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; /* Events */ - xw.attrs.background_pixel = dc.col[defaultbg].pixel; - xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.background_pixel = dc.col[default_attributes.bg].pixel; + xw.attrs.border_pixel = dc.col[default_attributes.bg].pixel; xw.attrs.bit_gravity = NorthWestGravity; xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask @@ -1093,7 +1085,7 @@ xinit(int cols, int rows) &gcvalues); xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, DefaultDepth(xw.dpy, xw.scr)); - XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XSetForeground(xw.dpy, dc.gc, dc.col[default_attributes.bg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); /* font spec buffer */ @@ -1137,7 +1129,7 @@ xinit(int cols, int rows) PropModeReplace, (uchar *)&thispid, 1); win.mode = MODE_NUMLOCK; - resettitle(); + xsettitle(NULL); xhints(); XMapWindow(xw.dpy, xw.win); XSync(xw.dpy, False); @@ -1408,7 +1400,7 @@ xdrawcursor(int cx, int cy, Glyph g) */ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; - g.fg = defaultbg; + g.fg = default_attributes.bg; g.bg = defaultcs; drawcol = dc.col[g.bg]; @@ -1536,14 +1528,10 @@ xfinishdraw(void) { XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); - XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XSetForeground(xw.dpy, dc.gc, dc.col[default_attributes.bg].pixel); } -void -expose(XEvent *ev) -{ - redraw(); -} +void expose(XEvent *ev) {} // do nothing void visibility(XEvent *ev) @@ -1638,76 +1626,76 @@ kpress(XEvent *ev) } /* 2. defaults for actions like backspace, del, enter etc*/ - int offset = buffer_snap_cursor(&focused_buffer, 0); + int offset = focused_window->cursor_offset; + struct file_buffer* fb = get_file_buffer(focused_window); if (offset == -1) return; switch (ksym) { case XK_BackSpace: - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - buffer_remove_selection(&focused_buffer); - buffer_move_cursor_to_selection_start(&focused_buffer); - focused_buffer.mode &= ~(BUFFER_SELECTION_ON); - buffer_snap_cursor(&focused_buffer, 0); + if (fb->mode & BUFFER_SELECTION_ON) { + buffer_remove_selection(fb); + buffer_move_cursor_to_selection_start(focused_window); + fb->mode &= ~(BUFFER_SELECTION_ON); return; } if (offset <= 0) return; - offset--; - if (term.c.x == 0) - buffer_move_cursor(&focused_buffer, INT32_MAX/2, term.c.y-1, 1); - else - buffer_move_cursor_relative(&focused_buffer, -1, 0, 1); - - buffer_snap_cursor(&focused_buffer, 0); + if (fb->contents[offset-1] == '\n') { + buffer_move_lines(focused_window, -1, CURSOR_COMMAND_MOVEMENT); + } else { + buffer_move_on_line(focused_window, -1, CURSOR_COMMAND_MOVEMENT); + } + offset = focused_window->cursor_offset; /* FALLTHROUGH */ case XK_Delete: - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - buffer_remove_selection(&focused_buffer); - buffer_move_cursor_to_selection_start(&focused_buffer); - focused_buffer.mode &= ~(BUFFER_SELECTION_ON); - buffer_snap_cursor(&focused_buffer, 0); + if (fb->mode & BUFFER_SELECTION_ON) { + buffer_remove_selection(fb); + buffer_move_cursor_to_selection_start(focused_window); + fb->mode &= ~(BUFFER_SELECTION_ON); return; } - buffer_remove(&focused_buffer, offset, 1, 0, 0); + buffer_remove(fb, offset, 1, 0, 0); return; case XK_Return: - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - buffer_remove_selection(&focused_buffer); - buffer_move_cursor_to_selection_start(&focused_buffer); - focused_buffer.mode &= ~(BUFFER_SELECTION_ON); - offset = buffer_snap_cursor(&focused_buffer, 0); + if (fb->mode & BUFFER_SELECTION_ON) { + buffer_remove_selection(fb); + buffer_move_cursor_to_selection_start(focused_window); + fb->mode &= ~(BUFFER_SELECTION_ON); + offset = focused_window->cursor_offset; } - buffer_insert(&focused_buffer, "\n", 1, offset, 0); - buffer_move_cursor_relative(&focused_buffer, -term.col, 1, 1); + buffer_insert(fb, "\n", 1, offset, 0); + buffer_move_to_offset(focused_window, offset+1, CURSOR_COMMAND_MOVEMENT); return; - case XK_Home: - buffer_move_cursor(&focused_buffer, -focused_buffer.x_scroll-1, term.c.y, 1); + case XK_Home: { + int new_offset = buffer_seek_char_backwards(fb, offset, '\n'); + if (new_offset < 0) + new_offset = 0; + buffer_move_to_offset(focused_window, new_offset, CURSOR_COMMAND_MOVEMENT); return; - case XK_End: - buffer_move_cursor_to_offset(&focused_buffer, - (char*)memchr(&(focused_buffer.contents[offset]), '\n', focused_buffer.len - offset) - - focused_buffer.contents, 0); - buffer_move_cursor_relative(&focused_buffer, 0, 0, CURSOR_COMMAND_MOVEMENT); + } + case XK_End: { + int new_offset = buffer_seek_char(fb, offset, '\n'); + if (new_offset < 0) + new_offset = fb->len-1; + buffer_move_to_offset(focused_window, new_offset, CURSOR_COMMAND_MOVEMENT); return; + } case XK_Page_Down: - buffer_scroll(&focused_buffer, 0, (term.row-1) / 2); - buffer_move_cursor(&focused_buffer, term.c.x, term.c.x, 1); - buffer_snap_cursor(&focused_buffer, 0); + buffer_move_lines(focused_window, (term.row-1) / 2, CURSOR_COMMAND_MOVEMENT); + focused_window->y_scroll += (term.row-1) / 2; + //TODO: make cursor follow return; case XK_Page_Up: - buffer_scroll(&focused_buffer, 0, -((term.row-1) / 2)); - buffer_move_cursor(&focused_buffer, term.c.x, term.c.x, 1); - buffer_snap_cursor(&focused_buffer, 0); + buffer_move_lines(focused_window, -((term.row-1) / 2), CURSOR_COMMAND_MOVEMENT); + focused_window->y_scroll -= (term.row-1) / 2; + //TODO: make cursor follow return; case XK_Tab: - buffer_insert(&focused_buffer, "\t", 1, offset, 0); - buffer_move_cursor_relative(&focused_buffer, 1, 0, 1); - break; - case XK_F1: - printf("%d\n", offset); + buffer_insert(fb, "\t", 1, offset, 0); + buffer_move_on_line(focused_window, 1, CURSOR_COMMAND_MOVEMENT); return; } //TODO: keybinds for escape and tab @@ -1724,13 +1712,13 @@ kpress(XEvent *ev) // TODO: allow blocking of the bufferwrite, redirecting to keybinds with multiple characther length if (buf[0] >= 32) { - if (focused_buffer.mode & BUFFER_SELECTION_ON) { - buffer_remove_selection(&focused_buffer); - buffer_move_cursor_to_selection_start(&focused_buffer); - focused_buffer.mode &= ~(BUFFER_SELECTION_ON); + if (fb->mode & BUFFER_SELECTION_ON) { + buffer_remove_selection(fb); + buffer_move_cursor_to_selection_start(focused_window); + fb->mode &= ~(BUFFER_SELECTION_ON); } - buffer_insert(&focused_buffer, buf, len, offset, 0); - buffer_move_cursor_relative(&focused_buffer, 1, 0, 1); + buffer_insert(fb, buf, len, offset, 0); + buffer_move_on_line(focused_window, 1, CURSOR_COMMAND_MOVEMENT); } else { printf("unhandled control character %x\n", buf[0]); } @@ -1795,20 +1783,22 @@ run(void) XNextEvent(xw.dpy, &ev); if (XFilterEvent(&ev, None)) continue; - if (handler[ev.type]) + if (handler[ev.type]) { (handler[ev.type])(&ev); - xev = 1; + xev = 1; + } } - if (!tisdirty() && !xev) { + if (!xev) { nanosleep(&(struct timespec){.tv_nsec = 1e6}, NULL); continue; } - buffer_write_to_screen(&focused_buffer); - buffer_write_selection(&focused_buffer); - draw(); + tsetregion(0, 0, term.col-1, term.row-1, ' '); + window_write_tree_to_screen(&root_node, 0, 0, term.col-1, term.row-1); + + draw(cursor_x,cursor_y); XFlush(xw.dpy); } } @@ -1833,7 +1823,17 @@ main(int argc, char *argv[]) tnew(cols, rows); xinit(cols, rows); xsetenv(); - buffer_new(&focused_buffer, "./x.c"); + + //buffer_new("/home/halvard/gunslinger/gs.h"); + //buffer_new("/home/halvard/test.c"); + + root_node.mode = WINDOW_SINGULAR; + root_node.window.buffer_index = + new_file_buffer(buffer_new("/home/halvard/gunslinger/gs.h")); + //new_file_buffer(buffer_new("/home/halvard/Code/C/se/st/st.c")); + //new_file_buffer(buffer_new("/home/halvard/test.c")); + focused_node = &root_node; + focused_window = &focused_node->window; run(); return 0;