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 | + |
M | config.def.h | | | 48 | +++++++++++++++++++++++++++++++++++++++++++----- |
M | st.c | | | 956 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
M | st.h | | | 153 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
M | win.h | | | 11 | +++++++++-- |
M | x.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;