diff options
| author | Albert Cervin <albert@acervin.com> | 2022-12-06 12:58:51 +0100 |
|---|---|---|
| committer | Albert Cervin <albert@acervin.com> | 2022-12-06 12:58:51 +0100 |
| commit | 66d50bd7b04922a91fbe3e4d49c68070ec1a7b14 (patch) | |
| tree | 87600e117f4262555bcc875b09e050536cbee492 | |
| parent | 78410b18e5d4d117b714eb9f34c689920c32a985 (diff) | |
| download | dged-66d50bd7b04922a91fbe3e4d49c68070ec1a7b14.tar.gz dged-66d50bd7b04922a91fbe3e4d49c68070ec1a7b14.tar.xz dged-66d50bd7b04922a91fbe3e4d49c68070ec1a7b14.zip | |
Add minibuffer
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/binding.c | 19 | ||||
| -rw-r--r-- | src/binding.h | 13 | ||||
| -rw-r--r-- | src/buffer.c | 88 | ||||
| -rw-r--r-- | src/buffer.h | 54 | ||||
| -rw-r--r-- | src/command.h | 7 | ||||
| -rw-r--r-- | src/display.c | 19 | ||||
| -rw-r--r-- | src/display.h | 4 | ||||
| -rw-r--r-- | src/keyboard.c | 22 | ||||
| -rw-r--r-- | src/keyboard.h | 8 | ||||
| -rw-r--r-- | src/main.c | 135 | ||||
| -rw-r--r-- | src/minibuffer.c | 95 | ||||
| -rw-r--r-- | src/minibuffer.h | 30 | ||||
| -rw-r--r-- | src/reactor.c | 4 | ||||
| -rw-r--r-- | src/reactor.h | 1 | ||||
| -rw-r--r-- | src/text.c | 8 | ||||
| -rw-r--r-- | src/text.h | 6 |
17 files changed, 431 insertions, 84 deletions
@@ -17,7 +17,7 @@ UNAME_S != uname -s CFLAGS = -Werror -g -std=c99 ifeq ($(UNAME_S),Linux) - DEFINES += -DLINUX + DEFINES += -DLINUX -D_XOPEN_SOURCE=700 endif $(objs-path)/test/%.o: %.c $(headers) 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); @@ -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, ¤t); + 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); @@ -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); } } @@ -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); |
