summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binding.c19
-rw-r--r--src/binding.h13
-rw-r--r--src/buffer.c88
-rw-r--r--src/buffer.h54
-rw-r--r--src/command.h7
-rw-r--r--src/display.c19
-rw-r--r--src/display.h4
-rw-r--r--src/keyboard.c22
-rw-r--r--src/keyboard.h8
-rw-r--r--src/main.c135
-rw-r--r--src/minibuffer.c95
-rw-r--r--src/minibuffer.h30
-rw-r--r--src/reactor.c4
-rw-r--r--src/reactor.h1
-rw-r--r--src/text.c8
-rw-r--r--src/text.h6
16 files changed, 430 insertions, 83 deletions
diff --git a/src/binding.c b/src/binding.c
index 953c0d8..191bc0d 100644
--- a/src/binding.c
+++ b/src/binding.c
@@ -34,8 +34,8 @@ void keymap_destroy(struct keymap *keymap) {
keymap->nbindings = 0;
}
-struct command *lookup_key(struct keymap *keymaps, uint32_t nkeymaps,
- struct key *key, struct commands *commands) {
+struct lookup_result lookup_key(struct keymap *keymaps, uint32_t nkeymaps,
+ struct key *key, struct commands *commands) {
// lookup in order in the keymaps
for (uint32_t kmi = 0; kmi < nkeymaps; ++kmi) {
struct keymap *keymap = &keymaps[kmi];
@@ -44,14 +44,21 @@ struct command *lookup_key(struct keymap *keymaps, uint32_t nkeymaps,
struct binding *binding = &keymap->bindings[bi];
if (key->c == binding->key.c && key->mod == binding->key.mod) {
if (binding->type == BindingType_Command) {
- return lookup_command_by_hash(commands, binding->command);
+ return (struct lookup_result){
+ .found = true,
+ .type = BindingType_Command,
+ .command = lookup_command_by_hash(commands, binding->command),
+ };
} else if (binding->type == BindingType_Keymap) {
- // TODO
- return NULL;
+ return (struct lookup_result){
+ .found = true,
+ .type = BindingType_Keymap,
+ .keymap = binding->keymap,
+ };
}
}
}
}
- return NULL;
+ return (struct lookup_result){.found = false};
}
diff --git a/src/binding.h b/src/binding.h
index 260a463..8a703c3 100644
--- a/src/binding.h
+++ b/src/binding.h
@@ -32,6 +32,15 @@ struct binding {
};
};
+struct lookup_result {
+ bool found;
+ uint8_t type;
+ union {
+ struct command *command;
+ struct keymap *keymap;
+ };
+};
+
struct commands;
struct keymap keymap_create(const char *name, uint32_t capacity);
@@ -39,5 +48,5 @@ void keymap_bind_keys(struct keymap *keymap, struct binding *bindings,
uint32_t nbindings);
void keymap_destroy(struct keymap *keymap);
-struct command *lookup_key(struct keymap *keymaps, uint32_t nkeymaps,
- struct key *key, struct commands *commands);
+struct lookup_result lookup_key(struct keymap *keymaps, uint32_t nkeymaps,
+ struct key *key, struct commands *commands);
diff --git a/src/buffer.c b/src/buffer.c
index fb071da..d6f3726 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1,12 +1,17 @@
#include "buffer.h"
#include "binding.h"
+#include "bits/stdint-uintn.h"
#include "display.h"
+#include "minibuffer.h"
+#include "reactor.h"
+#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <unistd.h>
struct buffer buffer_create(const char *name) {
struct buffer b =
@@ -119,12 +124,52 @@ void buffer_end_of_line(struct buffer *buffer) {
void buffer_beginning_of_line(struct buffer *buffer) { buffer->dot_col = 0; }
-struct buffer buffer_from_file(const char *filename) {
- // TODO: create a reader for the file that calls add_text
- return (struct buffer){.filename = filename, .name = filename};
+struct buffer buffer_from_file(const char *filename, struct reactor *reactor) {
+ struct buffer b = buffer_create(filename);
+ b.filename = filename;
+ if (access(b.filename, F_OK) == 0) {
+ FILE *file = fopen(filename, "r");
+
+ while (true) {
+ uint8_t buff[4096];
+ int bytes = fread(buff, 1, 4096, file);
+ if (bytes > 0) {
+ buffer_add_text(&b, buff, bytes);
+ } else if (bytes == 0) {
+ break; // EOF
+ } else {
+ // TODO: handle error
+ }
+ }
+
+ fclose(file);
+ }
+
+ b.dot_col = 0;
+ b.dot_line = 0;
+
+ return b;
+}
+
+void write_line(struct text_chunk *chunk, void *userdata) {
+ FILE *file = (FILE *)userdata;
+ fwrite(chunk->text, 1, chunk->nbytes, file);
+ fputc('\n', file);
}
-int buffer_to_file(struct buffer *buffer) { return 0; }
+void buffer_to_file(struct buffer *buffer) {
+ // TODO: handle errors
+ FILE *file = fopen(buffer->filename, "w");
+
+ uint32_t nlines = text_num_lines(buffer->text);
+ struct text_chunk lastline = text_get_line(buffer->text, nlines - 1);
+ uint32_t nlines_to_write = lastline.nbytes == 0 ? nlines - 1 : nlines;
+
+ text_for_each_line(buffer->text, 0, nlines_to_write, write_line, file);
+ minibuffer_echo_timeout(4, "wrote %d lines to %s", nlines_to_write,
+ buffer->filename);
+ fclose(file);
+}
int buffer_add_text(struct buffer *buffer, uint8_t *text, uint32_t nbytes) {
uint32_t lines_added, cols_added;
@@ -140,15 +185,27 @@ void buffer_newline(struct buffer *buffer) {
buffer_add_text(buffer, (uint8_t *)"\n", 1);
}
-bool modeline_update(struct buffer *buffer, uint32_t width) {
+bool modeline_update(struct buffer *buffer, uint32_t width,
+ uint64_t frame_time) {
char buf[width * 4];
+ static uint64_t samples[10] = {0};
+ static uint32_t samplei = 0;
+ static uint64_t avg = 0;
+
+ // calc a moving average with a window of the last 10 frames
+ ++samplei;
+ samplei %= 10;
+ avg += 0.1 * (frame_time - samples[samplei]);
+ samples[samplei] = frame_time;
+
time_t now = time(NULL);
struct tm *lt = localtime(&now);
char left[128], right[128];
- snprintf(left, 128, "--- %-16s (%d, %d)", buffer->name, buffer->dot_line + 1,
+ snprintf(left, 128, " %-16s (%d, %d)", buffer->name, buffer->dot_line + 1,
buffer->dot_col);
- snprintf(right, 128, "%02d:%02d", lt->tm_hour, lt->tm_min);
+ snprintf(right, 128, "(%.2f ms) %02d:%02d", frame_time / 1e6, lt->tm_hour,
+ lt->tm_min);
snprintf(buf, width * 4, "\x1b[100m%s%*s%s\x1b[0m", left,
(int)(width - (strlen(left) + strlen(right))), "", right);
@@ -161,8 +218,13 @@ bool modeline_update(struct buffer *buffer, uint32_t width) {
}
}
-struct buffer_update buffer_begin_frame(struct buffer *buffer, uint32_t width,
- uint32_t height, alloc_fn frame_alloc) {
+struct buffer_update buffer_update(struct buffer *buffer, uint32_t width,
+ uint32_t height, alloc_fn frame_alloc,
+ struct reactor *reactor,
+ uint64_t frame_time) {
+
+ struct buffer_update upd = (struct buffer_update){.cmds = 0, .ncmds = 0};
+
// reserve space for modeline
uint32_t bufheight = height - 1;
uint32_t nlines =
@@ -175,7 +237,7 @@ struct buffer_update buffer_begin_frame(struct buffer *buffer, uint32_t width,
buffer->lines_rendered = text_num_lines(buffer->text);
- if (modeline_update(buffer, width)) {
+ if (modeline_update(buffer, width, frame_time)) {
cmds[ncmds] = (struct render_cmd){
.col = 0,
.row = height - 1,
@@ -185,7 +247,7 @@ struct buffer_update buffer_begin_frame(struct buffer *buffer, uint32_t width,
++ncmds;
}
- return (struct buffer_update){.cmds = cmds, .ncmds = ncmds};
+ upd.cmds = cmds;
+ upd.ncmds = ncmds;
+ return upd;
}
-
-void buffer_end_frame(struct buffer *buffer, struct buffer_update *upd) {}
diff --git a/src/buffer.h b/src/buffer.h
index 66096b9..358aea5 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1,10 +1,12 @@
#include <stddef.h>
#include <stdint.h>
+#include <stdio.h>
#include "command.h"
#include "text.h"
struct keymap;
+struct reactor;
struct buffer {
const char *name;
@@ -50,20 +52,44 @@ void buffer_end_of_line(struct buffer *buffer);
void buffer_beginning_of_line(struct buffer *buffer);
void buffer_newline(struct buffer *buffer);
-struct buffer buffer_from_file(const char *filename);
-int buffer_to_file(struct buffer *buffer);
-
-struct buffer_update buffer_begin_frame(struct buffer *buffer, uint32_t width,
- uint32_t height, alloc_fn frame_alloc);
-void buffer_end_frame(struct buffer *buffer, struct buffer_update *upd);
+uint32_t buffer_add_pre_update_hook(struct buffer *buffer);
+uint32_t buffer_add_post_update_hook(struct buffer *buffer);
+uint32_t buffer_remove_pre_update_hook(struct buffer *buffer, uint32_t hook_id);
+uint32_t buffer_remove_post_update_hook(struct buffer *buffer,
+ uint32_t hook_id);
+
+struct buffer buffer_from_file(const char *filename, struct reactor *reactor);
+void buffer_to_file(struct buffer *buffer);
+
+struct buffer_update buffer_update(struct buffer *buffer, uint32_t width,
+ uint32_t height, alloc_fn frame_alloc,
+ struct reactor *reactor,
+ uint64_t frame_time);
+
+// commands
+#define BUFFER_WRAPCMD(fn) \
+ static void fn##_cmd(struct command_ctx ctx, int argc, const char *argv[]) { \
+ fn(ctx.current_buffer); \
+ }
+
+BUFFER_WRAPCMD(buffer_backward_delete_char);
+BUFFER_WRAPCMD(buffer_backward_char);
+BUFFER_WRAPCMD(buffer_forward_char);
+BUFFER_WRAPCMD(buffer_backward_line);
+BUFFER_WRAPCMD(buffer_forward_line);
+BUFFER_WRAPCMD(buffer_end_of_line);
+BUFFER_WRAPCMD(buffer_beginning_of_line);
+BUFFER_WRAPCMD(buffer_newline)
+BUFFER_WRAPCMD(buffer_to_file);
static struct command BUFFER_COMMANDS[] = {
- {.name = "backward-delete-char", .fn = buffer_backward_delete_char},
- {.name = "backward-char", .fn = buffer_backward_char},
- {.name = "forward-char", .fn = buffer_forward_char},
- {.name = "backward-line", .fn = buffer_backward_line},
- {.name = "forward-line", .fn = buffer_forward_line},
- {.name = "end-of-line", .fn = buffer_end_of_line},
- {.name = "beginning-of-line", .fn = buffer_beginning_of_line},
- {.name = "newline", .fn = buffer_newline},
+ {.name = "backward-delete-char", .fn = buffer_backward_delete_char_cmd},
+ {.name = "backward-char", .fn = buffer_backward_char_cmd},
+ {.name = "forward-char", .fn = buffer_forward_char_cmd},
+ {.name = "backward-line", .fn = buffer_backward_line_cmd},
+ {.name = "forward-line", .fn = buffer_forward_line_cmd},
+ {.name = "end-of-line", .fn = buffer_end_of_line_cmd},
+ {.name = "beginning-of-line", .fn = buffer_beginning_of_line_cmd},
+ {.name = "newline", .fn = buffer_newline_cmd},
+ {.name = "buffer-write-to-file", .fn = buffer_to_file_cmd},
};
diff --git a/src/command.h b/src/command.h
index 9515282..3e3bbfb 100644
--- a/src/command.h
+++ b/src/command.h
@@ -2,7 +2,12 @@
struct buffer;
-typedef void (*command_fn)(struct buffer *buffer);
+struct command_ctx {
+ struct buffer *current_buffer;
+};
+
+typedef void (*command_fn)(struct command_ctx ctx, int argc,
+ const char *argv[]);
struct command {
const char *name;
diff --git a/src/display.c b/src/display.c
index a9fc405..7f35907 100644
--- a/src/display.c
+++ b/src/display.c
@@ -84,13 +84,18 @@ void delete_to_eol() {
putbytes(bytes, 3);
}
-void display_update(struct display *display, struct render_cmd *cmds,
- uint32_t ncmds, uint32_t currow, uint32_t curcol) {
- for (uint64_t cmdi = 0; cmdi < ncmds; ++cmdi) {
- struct render_cmd *cmd = &cmds[cmdi];
- display_move_cursor(display, cmd->row, cmd->col);
- putbytes(cmd->data, cmd->len);
- delete_to_eol();
+void display_update(struct display *display, struct render_cmd_buf *cmd_bufs,
+ uint32_t ncmd_bufs, uint32_t currow, uint32_t curcol) {
+ for (uint32_t bufi = 0; bufi < ncmd_bufs; ++bufi) {
+ uint64_t ncmds = cmd_bufs[bufi].ncmds;
+ struct render_cmd *cmds = cmd_bufs[bufi].cmds;
+
+ for (uint64_t cmdi = 0; cmdi < ncmds; ++cmdi) {
+ struct render_cmd *cmd = &cmds[cmdi];
+ display_move_cursor(display, cmd->row, cmd->col);
+ putbytes(cmd->data, cmd->len);
+ delete_to_eol();
+ }
}
display_move_cursor(display, currow, curcol);
diff --git a/src/display.h b/src/display.h
index 18200d7..1132dd8 100644
--- a/src/display.h
+++ b/src/display.h
@@ -28,5 +28,5 @@ void display_destroy(struct display *display);
void display_clear(struct display *display);
void display_move_cursor(struct display *display, uint32_t row, uint32_t col);
-void display_update(struct display *display, struct render_cmd *cmds,
- uint32_t ncmds, uint32_t currow, uint32_t curcol);
+void display_update(struct display *display, struct render_cmd_buf *cmd_bufs,
+ uint32_t ncmd_bufs, uint32_t currow, uint32_t curcol);
diff --git a/src/keyboard.c b/src/keyboard.c
index 012ec5a..aaeccd2 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1,6 +1,8 @@
#include "keyboard.h"
#include "reactor.h"
+#include "stdio.h"
+#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
@@ -41,8 +43,8 @@ void parse_keys(uint8_t *bytes, uint32_t nbytes, struct key *out_keys,
*out_nkeys = nkps;
}
-struct keyboard_update keyboard_begin_frame(struct keyboard *kbd,
- struct reactor *reactor) {
+struct keyboard_update keyboard_update(struct keyboard *kbd,
+ struct reactor *reactor) {
struct keyboard_update upd =
(struct keyboard_update){.keys = {0}, .nkeys = 0};
@@ -67,8 +69,20 @@ struct keyboard_update keyboard_begin_frame(struct keyboard *kbd,
return upd;
}
-void keyboard_end_frame(struct keyboard *kbd) {}
-
bool key_equal(struct key *key, uint8_t mod, uint8_t c) {
return key->c == c && key->mod == mod;
}
+
+void key_name(struct key *key, char *buf, size_t capacity) {
+ const char *mod = "";
+ switch (key->mod) {
+ case Ctrl:
+ mod = "c-";
+ break;
+ case Meta:
+ mod = "m-";
+ break;
+ }
+
+ snprintf(buf, capacity, "%s%c", mod, tolower((char)key->c));
+}
diff --git a/src/keyboard.h b/src/keyboard.h
index 1a437fc..4078213 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -1,4 +1,5 @@
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
enum modifiers {
@@ -27,9 +28,8 @@ struct reactor;
struct keyboard keyboard_create(struct reactor *reactor);
-struct keyboard_update keyboard_begin_frame(struct keyboard *kbd,
- struct reactor *reactor);
-
-void keyboard_end_frame(struct keyboard *kbd);
+struct keyboard_update keyboard_update(struct keyboard *kbd,
+ struct reactor *reactor);
bool key_equal(struct key *key, uint8_t mod, uint8_t c);
+void key_name(struct key *key, char *buf, size_t capacity);
diff --git a/src/main.c b/src/main.c
index 1b9aaac..3874691 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,15 +1,16 @@
+#include <assert.h>
#include <locale.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
-#include <assert.h>
-#include <signal.h>
+#include <time.h>
#include "binding.h"
#include "buffer.h"
#include "display.h"
+#include "minibuffer.h"
#include "reactor.h"
struct frame_allocator {
@@ -46,13 +47,35 @@ bool running = true;
void terminate() { running = false; }
-void unimplemented_command(struct buffer *buffer) {}
-void exit_editor(struct buffer *buffer) { terminate(); }
+void _abort(struct command_ctx ctx, int argc, const char *argv[]) {
+ minibuffer_echo_timeout(4, "💣 aborted");
+}
+
+void unimplemented_command(struct command_ctx ctx, int argc,
+ const char *argv[]) {}
+void exit_editor(struct command_ctx ctx, int argc, const char *argv[]) {
+ terminate();
+}
static struct command GLOBAL_COMMANDS[] = {
{.name = "find-file", .fn = unimplemented_command},
+ {.name = "abort", .fn = _abort},
{.name = "exit", .fn = exit_editor}};
+uint64_t calc_frame_time_ns(struct timespec *timers, uint32_t num_timer_pairs) {
+ uint64_t total = 0;
+ for (uint32_t ti = 0; ti < num_timer_pairs * 2; ti += 2) {
+ struct timespec *start_timer = &timers[ti];
+ struct timespec *end_timer = &timers[ti + 1];
+
+ total +=
+ ((uint64_t)end_timer->tv_sec * 1e9 + (uint64_t)end_timer->tv_nsec) -
+ ((uint64_t)start_timer->tv_sec * 1e9 + (uint64_t)start_timer->tv_nsec);
+ }
+
+ return total;
+}
+
int main(int argc, char *argv[]) {
const char *filename = NULL;
if (argc >= 1) {
@@ -83,53 +106,119 @@ int main(int argc, char *argv[]) {
sizeof(BUFFER_COMMANDS) / sizeof(BUFFER_COMMANDS[0]));
// keymaps
+ struct keymap *current_keymap = NULL;
struct keymap global_keymap = keymap_create("global", 32);
+ struct keymap ctrlx_map = keymap_create("c-x", 32);
struct binding global_binds[] = {
- BINDING(Ctrl, 'X', "exit"),
+ PREFIX(Ctrl, 'X', &ctrlx_map),
+ };
+ struct binding ctrlx_bindings[] = {
+ BINDING(Ctrl, 'C', "exit"),
+ BINDING(Ctrl, 'G', "abort"),
+ BINDING(Ctrl, 'S', "buffer-write-to-file"),
};
keymap_bind_keys(&global_keymap, global_binds,
sizeof(global_binds) / sizeof(global_binds[0]));
+ keymap_bind_keys(&ctrlx_map, ctrlx_bindings,
+ sizeof(ctrlx_bindings) / sizeof(ctrlx_bindings[0]));
- // TODO: load initial buffer
struct buffer curbuf = buffer_create("welcome");
- const char *welcome_txt = "Welcome to the editor for datagubbar 👴\n";
- buffer_add_text(&curbuf, (uint8_t *)welcome_txt, strlen(welcome_txt));
+ if (filename != NULL) {
+ curbuf = buffer_from_file(filename, &reactor);
+ } else {
+ const char *welcome_txt = "Welcome to the editor for datagubbar 👴\n";
+ buffer_add_text(&curbuf, (uint8_t *)welcome_txt, strlen(welcome_txt));
+ }
+
+ minibuffer_init(display.height - 1);
+
+ struct timespec buffer_begin, buffer_end, display_begin, display_end,
+ keyboard_begin, keyboard_end;
+
+ uint64_t frame_time = 0;
+
+ struct render_cmd_buf render_bufs[2] = {
+ {.source = "minibuffer"},
+ {.source = "buffer"},
+ };
while (running) {
+ clock_gettime(CLOCK_MONOTONIC, &buffer_begin);
+
+ // update minibuffer
+ struct minibuffer_update minibuf_upd = minibuffer_update(frame_alloc);
+ render_bufs[0].cmds = minibuf_upd.cmds;
+ render_bufs[0].ncmds = minibuf_upd.ncmds;
+
// update current buffer
- struct buffer_update buf_upd = buffer_begin_frame(
- &curbuf, display.width, display.height - 1, frame_alloc);
+ struct buffer_update buf_upd =
+ buffer_update(&curbuf, display.width, display.height - 1, frame_alloc,
+ &reactor, frame_time);
+ render_bufs[1].cmds = buf_upd.cmds;
+ render_bufs[1].ncmds = buf_upd.ncmds;
+
+ clock_gettime(CLOCK_MONOTONIC, &buffer_end);
// update screen
- if (buf_upd.ncmds > 0) {
- display_update(&display, buf_upd.cmds, buf_upd.ncmds, curbuf.dot_line,
- curbuf.dot_col);
+ clock_gettime(CLOCK_MONOTONIC, &display_begin);
+ if (render_bufs[0].ncmds > 0 || render_bufs[1].ncmds > 0) {
+ display_update(&display, render_bufs, 2, curbuf.dot_line, curbuf.dot_col);
}
+ clock_gettime(CLOCK_MONOTONIC, &display_end);
+
reactor_update(&reactor);
+ clock_gettime(CLOCK_MONOTONIC, &keyboard_begin);
struct keymap *local_keymaps = NULL;
uint32_t nbuffer_keymaps = buffer_keymaps(&curbuf, &local_keymaps);
- struct keyboard_update kbd_upd = keyboard_begin_frame(&kbd, &reactor);
+ struct keyboard_update kbd_upd = keyboard_update(&kbd, &reactor);
for (uint32_t ki = 0; ki < kbd_upd.nkeys; ++ki) {
struct key *k = &kbd_upd.keys[ki];
- // check first the global keymap, then the buffer ones
- struct command *cmd = lookup_key(&global_keymap, 1, k, &commands);
- if (cmd == NULL) {
- cmd = lookup_key(local_keymaps, nbuffer_keymaps, k, &commands);
+ struct lookup_result res = {.found = false};
+ if (current_keymap != NULL) {
+ res = lookup_key(current_keymap, 1, k, &commands);
+ } else {
+ // check first the global keymap, then the buffer ones
+ res = lookup_key(&global_keymap, 1, k, &commands);
+ if (!res.found) {
+ res = lookup_key(local_keymaps, nbuffer_keymaps, k, &commands);
+ }
}
- if (cmd != NULL) {
- cmd->fn(&curbuf);
+ if (res.found) {
+ switch (res.type) {
+ case BindingType_Command: {
+ const char *argv[] = {};
+ res.command->fn((struct command_ctx){.current_buffer = &curbuf}, 0,
+ argv);
+ current_keymap = NULL;
+ break;
+ }
+ case BindingType_Keymap: {
+ char keyname[16];
+ key_name(k, keyname, 16);
+ minibuffer_echo("%s", keyname);
+ current_keymap = res.keymap;
+ break;
+ }
+ }
+ } else if (current_keymap != NULL) {
+ minibuffer_echo_timeout(4, "key is not bound!");
+ current_keymap = NULL;
} else {
buffer_add_text(&curbuf, &k->c, 1);
}
}
+ clock_gettime(CLOCK_MONOTONIC, &keyboard_end);
+
+ // calculate frame time
+ struct timespec timers[] = {buffer_begin, buffer_end, display_begin,
+ display_end, keyboard_begin, keyboard_end};
+ frame_time = calc_frame_time_ns(timers, 3);
- keyboard_end_frame(&kbd);
- buffer_end_frame(&curbuf, &buf_upd);
frame_allocator_clear(&frame_allocator);
}
diff --git a/src/minibuffer.c b/src/minibuffer.c
new file mode 100644
index 0000000..4e6e3b7
--- /dev/null
+++ b/src/minibuffer.c
@@ -0,0 +1,95 @@
+#include "minibuffer.h"
+#include "display.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static struct minibuffer g_minibuffer = {0};
+
+void minibuffer_init(uint32_t row) {
+ g_minibuffer.buffer = malloc(4096);
+ g_minibuffer.capacity = 4096;
+ g_minibuffer.nbytes = 0;
+ g_minibuffer.row = row;
+ g_minibuffer.dirty = false;
+}
+
+void minibuffer_destroy() {
+ free(g_minibuffer.buffer);
+ g_minibuffer.capacity = 0;
+ g_minibuffer.dirty = false;
+}
+
+struct minibuffer_update minibuffer_update(alloc_fn frame_alloc) {
+ // TODO: multiline
+ if (g_minibuffer.nbytes == 0 && !g_minibuffer.dirty) {
+ return (struct minibuffer_update){.cmds = NULL, .ncmds = 0};
+ }
+
+ struct timespec current;
+ clock_gettime(CLOCK_MONOTONIC, &current);
+ if (current.tv_sec < g_minibuffer.expires.tv_sec) {
+ struct render_cmd *cmds =
+ (struct render_cmd *)frame_alloc(sizeof(struct render_cmd));
+
+ cmds[0].col = 0;
+ cmds[0].row = g_minibuffer.row;
+ cmds[0].data = g_minibuffer.buffer;
+ cmds[0].len = g_minibuffer.nbytes;
+
+ g_minibuffer.dirty = false;
+
+ return (struct minibuffer_update){
+ .cmds = cmds,
+ .ncmds = 1,
+ };
+ } else {
+ g_minibuffer.nbytes = 0;
+ g_minibuffer.dirty = false;
+ // send a clear draw command
+ struct render_cmd *cmds =
+ (struct render_cmd *)frame_alloc(sizeof(struct render_cmd));
+
+ cmds[0].col = 0;
+ cmds[0].row = g_minibuffer.row;
+ cmds[0].data = NULL;
+ cmds[0].len = 0;
+
+ return (struct minibuffer_update){
+ .cmds = cmds,
+ .ncmds = 1,
+ };
+ }
+}
+
+void echo(uint32_t timeout, const char *fmt, va_list args) {
+ size_t nbytes =
+ vsnprintf((char *)g_minibuffer.buffer, g_minibuffer.capacity, fmt, args);
+
+ // vsnprintf returns how many characters it would have wanted to write in case
+ // of overflow
+ g_minibuffer.nbytes =
+ nbytes > g_minibuffer.capacity ? g_minibuffer.capacity : nbytes;
+ g_minibuffer.dirty = true;
+
+ clock_gettime(CLOCK_MONOTONIC, &g_minibuffer.expires);
+ g_minibuffer.expires.tv_sec += timeout;
+}
+
+void minibuffer_echo(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ echo(1000, fmt, args);
+ va_end(args);
+}
+
+void minibuffer_echo_timeout(uint32_t timeout, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ echo(timeout, fmt, args);
+ va_end(args);
+}
+
+bool minibuffer_displaying() { return g_minibuffer.nbytes > 0; }
+void minibuffer_clear() { g_minibuffer.expires.tv_nsec = 0; }
diff --git a/src/minibuffer.h b/src/minibuffer.h
new file mode 100644
index 0000000..55eeb7e
--- /dev/null
+++ b/src/minibuffer.h
@@ -0,0 +1,30 @@
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+struct minibuffer_update {
+ struct render_cmd *cmds;
+ uint64_t ncmds;
+};
+
+struct minibuffer {
+ uint8_t *buffer;
+ uint32_t capacity;
+ uint32_t nbytes;
+ uint32_t row;
+ bool dirty;
+ struct timespec expires;
+};
+
+typedef void *(alloc_fn)(size_t);
+
+void minibuffer_init(uint32_t row);
+void minibuffer_destroy();
+
+struct minibuffer_update minibuffer_update(alloc_fn frame_alloc);
+
+void minibuffer_echo(const char *fmt, ...);
+void minibuffer_echo_timeout(uint32_t timeout, const char *fmt, ...);
+void minibuffer_clear();
+bool minibuffer_displaying();
diff --git a/src/reactor.c b/src/reactor.c
index eb70047..7bdb4a4 100644
--- a/src/reactor.c
+++ b/src/reactor.c
@@ -38,6 +38,10 @@ uint32_t reactor_register_interest(struct reactor *reactor, int fd,
return fd;
}
+void reactor_unregister_interest(struct reactor *reactor, uint32_t ev_id) {
+ epoll_ctl(reactor->epoll_fd, EPOLL_CTL_DEL, ev_id, NULL);
+}
+
bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id) {
struct events *events = (struct events *)reactor->events;
for (uint32_t ei = 0; ei < events->nevents; ++ei) {
diff --git a/src/reactor.h b/src/reactor.h
index 7e028e8..01e2443 100644
--- a/src/reactor.h
+++ b/src/reactor.h
@@ -17,3 +17,4 @@ void reactor_update(struct reactor *reactor);
bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id);
uint32_t reactor_register_interest(struct reactor *reactor, int fd,
enum interest interest);
+void reactor_unregister_interest(struct reactor *reactor, uint32_t ev_id);
diff --git a/src/text.c b/src/text.c
index 64cbd7b..b2e43f9 100644
--- a/src/text.c
+++ b/src/text.c
@@ -307,13 +307,13 @@ uint32_t text_render(struct text *text, uint32_t line, uint32_t nlines,
return ncmds;
}
-void text_for_each_chunk(struct text *text, chunk_cb callback) {
+void text_for_each_chunk(struct text *text, chunk_cb callback, void *userdata) {
// if representation of text is changed, this can be changed as well
- text_for_each_line(text, 0, text->nlines, callback);
+ text_for_each_line(text, 0, text->nlines, callback, userdata);
}
void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
- chunk_cb callback) {
+ chunk_cb callback, void *userdata) {
for (uint32_t li = line; li < (line + nlines); ++li) {
struct line *src_line = &text->lines[li];
struct text_chunk line = (struct text_chunk){
@@ -321,7 +321,7 @@ void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
.nbytes = src_line->nbytes,
.nchars = src_line->nchars,
};
- callback(&line);
+ callback(&line, userdata);
}
}
diff --git a/src/text.h b/src/text.h
index 399eaf4..a057a47 100644
--- a/src/text.h
+++ b/src/text.h
@@ -29,11 +29,11 @@ struct text_chunk {
uint32_t nchars;
};
-typedef void (*chunk_cb)(struct text_chunk *chunk);
+typedef void (*chunk_cb)(struct text_chunk *chunk, void *userdata);
void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
- chunk_cb callback);
+ chunk_cb callback, void *userdata);
-void text_for_each_chunk(struct text *text, chunk_cb callback);
+void text_for_each_chunk(struct text *text, chunk_cb callback, void *userdata);
struct text_chunk text_get_line(struct text *text, uint32_t line);