diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/allocator.h | 51 | ||||
| -rw-r--r-- | src/buffer.c | 4 | ||||
| -rw-r--r-- | src/command.c | 5 | ||||
| -rw-r--r-- | src/command.h | 54 | ||||
| -rw-r--r-- | src/display.c | 8 | ||||
| -rw-r--r-- | src/display.h | 3 | ||||
| -rw-r--r-- | src/keyboard.c | 86 | ||||
| -rw-r--r-- | src/keyboard.h | 104 | ||||
| -rw-r--r-- | src/main.c | 34 | ||||
| -rw-r--r-- | src/reactor-linux.c (renamed from src/reactor.c) | 21 | ||||
| -rw-r--r-- | src/reactor.h | 7 |
11 files changed, 284 insertions, 93 deletions
diff --git a/src/allocator.h b/src/allocator.h new file mode 100644 index 0000000..d89bd29 --- /dev/null +++ b/src/allocator.h @@ -0,0 +1,51 @@ +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +/** + * Simple bump allocator that can be used for + * allocations with a frame lifetime. + */ +struct frame_allocator { + uint8_t *buf; + size_t offset; + size_t capacity; +}; + +/** + * Create a new frame allocator + * + * @param capacity The capacity in bytes of the frame allocator + * @returns The frame allocator + */ +struct frame_allocator frame_allocator_create(size_t capacity) { + return (struct frame_allocator){ + .capacity = capacity, .offset = 0, .buf = (uint8_t *)malloc(capacity)}; +} + +/** + * Allocate memory in this @ref frame_allocator "frame allocator" + * + * @param alloc The allocator to allocate in + * @param sz The size in bytes to allocate. + * @returns void* representing the start of the allocated region on success, + * NULL on failure. + */ +void *frame_allocator_alloc(struct frame_allocator *alloc, size_t sz) { + if (alloc->offset + sz > alloc->capacity) { + return NULL; + } + + void *mem = alloc->buf + alloc->offset; + alloc->offset += sz; + + return mem; +} + +/** + * Clear this @ref frame_allocator "frame allocator". + * + * This does not free any memory, but simply resets the offset to 0. + * @param alloc The frame allocator to clear + */ +void frame_allocator_clear(struct frame_allocator *alloc) { alloc->offset = 0; } diff --git a/src/buffer.c b/src/buffer.c index c3d2925..895922d 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -56,8 +56,8 @@ struct buffer buffer_create(char *name, bool modeline) { BINDING(Ctrl, 'A', "beginning-of-line"), BINDING(Ctrl, 'E', "end-of-line"), - BINDING(Ctrl, 'M', "newline"), - BINDING(Ctrl, 'I', "indent"), + BINDING(ENTER, "newline"), + BINDING(TAB, "indent"), BINDING(Ctrl, 'K', "kill-line"), BINDING(DELETE, "delete-char"), diff --git a/src/command.c b/src/command.c index f8add1b..b2f0c71 100644 --- a/src/command.c +++ b/src/command.c @@ -4,6 +4,11 @@ #include <stdlib.h> +struct hashed_command { + uint32_t hash; + struct command command; +}; + struct commands command_registry_create(uint32_t capacity) { return (struct commands){ .commands = calloc(capacity, sizeof(struct hashed_command)), diff --git a/src/command.h b/src/command.h index 278a894..20c7d74 100644 --- a/src/command.h +++ b/src/command.h @@ -1,4 +1,4 @@ -/** +/** @file command.h * Commands and command registries */ #include <stdint.h> @@ -7,28 +7,70 @@ struct buffer; struct buffers; struct window; +/** + * Execution context for a command + */ struct command_ctx { + /** + * The current list of buffers. + * + * Can be used to insert new buffers or + * inspect existing. + */ struct buffers *buffers; + + /** + * The currently active window. + */ struct window *active_window; + + /** + * A registry of available commands. + * + * Can be used to execute other commands as part of a command implementation. + */ struct commands *commands; + + /** + * The command that is currently being executed + */ struct command *self; + + /** + * User data set up by the command currently being executed. + */ void *userdata; }; +/** A command function callback which holds the implementation of a command */ typedef int32_t (*command_fn)(struct command_ctx ctx, int argc, const char *argv[]); +/** + * A command that can be bound to a key or executed directly + */ struct command { + /** + * Name of the command + * + * Used to look the command up for execution and keybinds. + */ const char *name; + + /** + * Implementation of command behavior + */ command_fn fn; - void *userdata; -}; -struct hashed_command { - uint32_t hash; - struct command command; + /** + * Userdata passed to each invocation of the command. + */ + void *userdata; }; +/** + * A command registry + */ struct commands { struct hashed_command *commands; uint32_t ncommands; diff --git a/src/display.c b/src/display.c index 3a2a0d9..de99e36 100644 --- a/src/display.c +++ b/src/display.c @@ -106,7 +106,11 @@ void display_destroy(struct display *display) { tcsetattr(0, TCSADRAIN, &display->orig_term); } -void putbyte(uint8_t c) { putc(c, stdout); } +void putbyte(uint8_t c) { + if (c != '\r') { + putc(c, stdout); + } +} void putbyte_ws(uint8_t c, bool show_whitespace) { if (show_whitespace && c == '\t') { @@ -114,7 +118,7 @@ void putbyte_ws(uint8_t c, bool show_whitespace) { } else if (show_whitespace && c == ' ') { fputs("\x1b[90m·\x1b[0m", stdout); } else { - fputc(c, stdout); + putbyte(c); } } diff --git a/src/display.h b/src/display.h index 9a1cee5..7ffc660 100644 --- a/src/display.h +++ b/src/display.h @@ -13,6 +13,8 @@ struct display { struct render_command; struct command_list; +/** Typedef for any allocation function */ +typedef void *(*alloc_fn)(size_t); struct display display_create(); void display_resize(struct display *display); @@ -25,7 +27,6 @@ void display_begin_render(struct display *display); void display_render(struct display *display, struct command_list *command_list); void display_end_render(struct display *display); -typedef void *(*alloc_fn)(size_t); struct command_list *command_list_create(uint32_t capacity, alloc_fn allocator, uint32_t xoffset, uint32_t yoffset, const char *name); diff --git a/src/keyboard.c b/src/keyboard.c index e76630e..7059dec 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -6,6 +6,7 @@ #include <ctype.h> #include <errno.h> +#include <stdlib.h> #include <string.h> #include <termios.h> #include <unistd.h> @@ -18,46 +19,46 @@ struct keyboard keyboard_create(struct reactor *reactor) { term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 0; tcsetattr(0, TCSADRAIN, &term); + return keyboard_create_fd(reactor, STDIN_FILENO); +} +struct keyboard keyboard_create_fd(struct reactor *reactor, int fd) { return (struct keyboard){ - .reactor_event_id = - reactor_register_interest(reactor, STDIN_FILENO, ReadInterest), - .has_data = false, + .fd = fd, + .reactor_event_id = reactor_register_interest(reactor, fd, ReadInterest), }; } void parse_keys(uint8_t *bytes, uint32_t nbytes, struct key *out_keys, uint32_t *out_nkeys) { + // TODO: can be optimized if "bytes" contains no special chars uint32_t nkps = 0; for (uint32_t bytei = 0; bytei < nbytes; ++bytei) { uint8_t b = bytes[bytei]; + bool has_more = bytei + 1 < nbytes; + uint8_t next = has_more ? bytes[bytei + 1] : 0; + struct key *kp = &out_keys[nkps]; if (b == 0x1b) { // meta - struct key *kp = &out_keys[nkps]; kp->start = bytei; kp->mod = Meta; - } else if (b == '[' || - b == '0') { // special char (function keys, pgdn, etc) - struct key *kp = &out_keys[nkps]; - if (kp->mod & Meta) { - kp->mod = Spec; - } - } else if (b >= 0x00 && b <= 0x1f) { // ctrl char - struct key *kp = &out_keys[nkps]; + } else if (has_more && isalnum(next) && kp->mod & Meta && + (b == '[' || + b == '0')) { // special char (function keys, pgdn, etc) + kp->mod = Spec; + } else if (b == 0x7f) { // ? kp->mod |= Ctrl; - kp->key = b | 0x40; + kp->key = '?'; kp->start = bytei; kp->end = bytei + 1; ++nkps; - } else if (b == 0x7f) { // ? - struct key *kp = &out_keys[nkps]; + } else if (iscntrl(b)) { // ctrl char kp->mod |= Ctrl; - kp->key = '?'; + kp->key = b | 0x40; kp->start = bytei; kp->end = bytei + 1; ++nkps; } else { - struct key *kp = &out_keys[nkps]; if (kp->mod & Spec && b == '~') { // skip tilde in special chars kp->end = bytei + 1; @@ -66,19 +67,18 @@ void parse_keys(uint8_t *bytes, uint32_t nbytes, struct key *out_keys, kp->key = b; kp->end = bytei + 1; - bool has_more = bytei + 1 < nbytes; - - if (kp->mod & Meta || - (kp->mod & Spec && !(has_more && bytes[bytei + 1] == '~'))) { + if (kp->mod & Meta || (kp->mod & Spec && next != '~')) { ++nkps; } - } else if (utf8_byte_is_unicode_start(b)) { + } else if (utf8_byte_is_unicode_continuation(b)) { + // do nothing for these + } else if (utf8_byte_is_unicode_start(b)) { // unicode char kp->mod = None; kp->key = 0; kp->start = bytei; kp->end = bytei + utf8_nbytes(bytes + bytei, nbytes - bytei, 1); ++nkps; - } else { + } else { // normal ASCII char kp->mod = None; kp->key = b; kp->start = bytei; @@ -92,36 +92,44 @@ void parse_keys(uint8_t *bytes, uint32_t nbytes, struct key *out_keys, } struct keyboard_update keyboard_update(struct keyboard *kbd, - struct reactor *reactor) { + struct reactor *reactor, + alloc_fn frame_alloc) { struct keyboard_update upd = (struct keyboard_update){ - .keys = {0}, + .keys = NULL, .nkeys = 0, .nbytes = 0, - .raw = {0}, + .raw = NULL, }; - if (!kbd->has_data) { - if (reactor_poll_event(reactor, kbd->reactor_event_id)) { - kbd->has_data = true; - } else { - return upd; - } + // check if there is anything to do + if (!reactor_poll_event(reactor, kbd->reactor_event_id)) { + return upd; + } + + // read all input in chunks of `bufsize` bytes + const uint32_t bufsize = 128; + uint8_t *buf = malloc(bufsize), *writepos = buf; + int nbytes = 0, nread = 0; + while ((nread = read(kbd->fd, writepos, bufsize)) == bufsize) { + nbytes += bufsize; + buf = realloc(buf, nbytes + bufsize); + writepos = buf + nbytes; } - int nbytes = read(STDIN_FILENO, upd.raw, 64); + nbytes += nread; if (nbytes > 0) { upd.nbytes = nbytes; - parse_keys(upd.raw, upd.nbytes, upd.keys, &upd.nkeys); + upd.raw = frame_alloc(nbytes); + memcpy(upd.raw, buf, nbytes); + upd.keys = frame_alloc(sizeof(struct key) * nbytes); + memset(upd.keys, 0, sizeof(struct key) * nbytes); - if (nbytes < 64) { - kbd->has_data = false; - } - } else if (nbytes == EAGAIN) { - kbd->has_data = false; + parse_keys(upd.raw, upd.nbytes, upd.keys, &upd.nkeys); } + free(buf); return upd; } diff --git a/src/keyboard.h b/src/keyboard.h index 9bf36de..a44a58f 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -2,48 +2,144 @@ #include <stddef.h> #include <stdint.h> +/** + * Key press modifiers + * + * Modifiers a key press can have. + */ enum modifiers { + /** No modifier, bare key press */ None = 0, + + /** Ctrl key */ Ctrl = 1 << 0, + + /** Meta (Alt) key */ Meta = 1 << 1, + + /** Special key (F keys, arrow keys, etc) */ Spec = 1 << 2, }; +/** Backspace key */ #define BACKSPACE Ctrl, '?' +/** Tab key */ +#define TAB Ctrl, 'I' +/** Enter key */ +#define ENTER Ctrl, 'M' +/** Delete key */ #define DELETE Spec, '3' +/** Up arrow key */ #define UP Spec, 'A' +/** Down arrow key */ #define DOWN Spec, 'B' +/** Right arrow key */ #define RIGHT Spec, 'C' +/** Left arrow key */ #define LEFT Spec, 'D' +/** + * A key press + */ struct key { + /** The key pressed, will be 0 for a unicode char */ uint8_t key; + /** Modifier keys pressed (or-ed together) */ uint8_t mod; + /** Index where this key press starts in the raw input buffer */ uint8_t start; + /** Index where this key press ends in the raw input buffer */ uint8_t end; }; +/** + * The keyboard used to input characters. + */ struct keyboard { uint32_t reactor_event_id; - bool has_data; + int fd; }; +/** + * The result of updating the keyboard + */ struct keyboard_update { - struct key keys[32]; + /** The key presses */ + struct key *keys; + /** Number of key presses in @ref keys */ uint32_t nkeys; - uint8_t raw[64]; + /** The raw input */ + uint8_t *raw; + /** The number of bytes in the raw input */ uint32_t nbytes; }; struct reactor; +/** Typedef for any allocation function */ +typedef void *(*alloc_fn)(size_t); +/** + * Create a new keyboard + * + * @param reactor @ref reactor "Reactor" to use for polling keyboard for + * readiness. + * @returns The created keyboard. + */ struct keyboard keyboard_create(struct reactor *reactor); +/** + * Create a new keyboard, reading input from fd + * + * @param reactor @ref reactor "Reactor" to use for polling keyboard for + * readiness. + * @param fd The file descriptor to get input from + * @returns The created keyboard. + */ +struct keyboard keyboard_create_fd(struct reactor *reactor, int fd); + +/** + * Update the keyboard. + * + * This will check the reactor for readiness to avoid blocking. If there is + * data, it will be read and converted to key presses. + * + * @param kbd The @ref keyboard to update. + * @param reactor The @ref reactor used when creating the @ref keyboard. + * @returns An instance of @ref keyboard_update representing the result of the + * update operation. + */ struct keyboard_update keyboard_update(struct keyboard *kbd, - struct reactor *reactor); + struct reactor *reactor, + alloc_fn frame_alloc); +/** + * Does key represent the same key press as mod and c. + * + * @param key The key to check. + * @param mod Modifier of a key to compare against. + * @param c Char of a key to compare against. + * @returns true if key represents the same key press as mod together with c, + * false otherwise + */ bool key_equal_char(struct key *key, uint8_t mod, uint8_t c); + +/** + * Does key1 represent the same key press as key2? + * + * @param key1 First key to compare. + * @param key2 Second key to compare. + * @returns true if key1 and key2 represents the same key press, false + * otherwise. + */ bool key_equal(struct key *key1, struct key *key2); + +/** + * Get a text representation of a key + * + * @param key @ref key "Key" to get text representation for. + * @param buf character buffer for holding the result. + * @param capacity The capacity of buf. + */ void key_name(struct key *key, char *buf, size_t capacity); @@ -6,6 +6,7 @@ #include <string.h> #include <time.h> +#include "allocator.h" #include "binding.h" #include "buffer.h" #include "buffers.h" @@ -13,30 +14,6 @@ #include "minibuffer.h" #include "reactor.h" -struct frame_allocator { - uint8_t *buf; - size_t offset; - size_t capacity; -}; - -struct frame_allocator frame_allocator_create(size_t capacity) { - return (struct frame_allocator){ - .capacity = capacity, .offset = 0, .buf = (uint8_t *)malloc(capacity)}; -} - -void *frame_allocator_alloc(struct frame_allocator *alloc, size_t sz) { - if (alloc->offset + sz > alloc->capacity) { - return NULL; - } - - void *mem = alloc->buf + alloc->offset; - alloc->offset += sz; - - return mem; -} - -void frame_allocator_clear(struct frame_allocator *alloc) { alloc->offset = 0; } - struct frame_allocator frame_allocator; void *frame_alloc(size_t sz) { @@ -107,7 +84,7 @@ int main(int argc, char *argv[]) { frame_allocator = frame_allocator_create(16 * 1024 * 1024); // create reactor - struct reactor reactor = reactor_create(); + struct reactor *reactor = reactor_create(); // initialize display display = display_create(); @@ -115,7 +92,7 @@ int main(int argc, char *argv[]) { signal(SIGWINCH, resized); // init keyboard - struct keyboard kbd = keyboard_create(&reactor); + struct keyboard kbd = keyboard_create(reactor); // commands struct commands commands = command_registry_create(32); @@ -243,13 +220,14 @@ int main(int argc, char *argv[]) { clock_gettime(CLOCK_MONOTONIC, &display_end); // this blocks for events, so if nothing has happened we block here. - reactor_update(&reactor); + reactor_update(reactor); clock_gettime(CLOCK_MONOTONIC, &keyboard_begin); struct keymap *local_keymaps = NULL; uint32_t nbuffer_keymaps = buffer_keymaps(active_window->buffer, &local_keymaps); - struct keyboard_update kbd_upd = keyboard_update(&kbd, &reactor); + struct keyboard_update kbd_upd = + keyboard_update(&kbd, reactor, frame_alloc); uint32_t input_data_idx = 0; for (uint32_t ki = 0; ki < kbd_upd.nkeys; ++ki) { diff --git a/src/reactor.c b/src/reactor-linux.c index 7bdb4a4..e488fef 100644 --- a/src/reactor.c +++ b/src/reactor-linux.c @@ -4,24 +4,33 @@ #include <stdlib.h> #include <sys/epoll.h> +struct reactor { + int epoll_fd; + void *events; +}; + struct events { struct epoll_event events[10]; uint32_t nevents; }; -struct reactor reactor_create() { +struct reactor *reactor_create() { int epollfd = epoll_create1(0); if (epollfd == -1) { perror("epoll_create1"); } - return (struct reactor){ - .epoll_fd = epollfd, - .events = calloc(1, sizeof(struct events)), - }; + struct reactor *r = (struct reactor *)calloc(1, sizeof(struct reactor)); + r->epoll_fd = epollfd; + r->events = calloc(1, sizeof(struct events)); + + return r; } -void reactor_destroy(struct reactor *reactor) { free(reactor->events); } +void reactor_destroy(struct reactor *reactor) { + free(reactor->events); + free(reactor); +} uint32_t reactor_register_interest(struct reactor *reactor, int fd, enum interest interest) { diff --git a/src/reactor.h b/src/reactor.h index 01e2443..e54afda 100644 --- a/src/reactor.h +++ b/src/reactor.h @@ -6,12 +6,9 @@ enum interest { WriteInterest = 2, }; -struct reactor { - int epoll_fd; - void *events; -}; +struct reactor; -struct reactor reactor_create(); +struct reactor *reactor_create(); void reactor_destroy(struct reactor *reactor); void reactor_update(struct reactor *reactor); bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id); |
