diff options
Diffstat (limited to 'src/nvim/os/event.c')
-rw-r--r-- | src/nvim/os/event.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/nvim/os/event.c b/src/nvim/os/event.c new file mode 100644 index 0000000000..2c25852778 --- /dev/null +++ b/src/nvim/os/event.c @@ -0,0 +1,148 @@ +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <uv.h> + +#include "lib/klist.h" +#include "os/event.h" +#include "os/input.h" +#include "os/channel.h" +#include "os/server.h" +#include "os/signal.h" +#include "os/rstream.h" +#include "os/job.h" +#include "vim.h" +#include "memory.h" +#include "misc2.h" + +// event will be cleaned up after it gets processed +#define _destroy_event(x) // do nothing +KLIST_INIT(Event, Event, _destroy_event) + +static klist_t(Event) *event_queue; +static uv_timer_t timer; +static uv_prepare_t timer_prepare; +static void timer_cb(uv_timer_t *handle); +static void timer_prepare_cb(uv_prepare_t *); + +void event_init() +{ + // Initialize the event queue + event_queue = kl_init(Event); + // Initialize input events + input_init(); + // Timer to wake the event loop if a timeout argument is passed to + // `event_poll` + // Signals + signal_init(); + // Jobs + job_init(); + // Channels + channel_init(); + // Servers + server_init(); + uv_timer_init(uv_default_loop(), &timer); + // This prepare handle that actually starts the timer + uv_prepare_init(uv_default_loop(), &timer_prepare); +} + +void event_teardown() +{ + channel_teardown(); + job_teardown(); + server_teardown(); +} + +// Wait for some event +bool event_poll(int32_t ms) +{ + bool timed_out; + uv_run_mode run_mode = UV_RUN_ONCE; + + if (input_ready()) { + // If there's a pending input event to be consumed, do it now + return true; + } + + input_start(); + timed_out = false; + + if (ms > 0) { + // Timeout passed as argument to the timer + timer.data = &timed_out; + // We only start the timer after the loop is running, for that we + // use an prepare handle(pass the interval as data to it) + timer_prepare.data = &ms; + uv_prepare_start(&timer_prepare, timer_prepare_cb); + } else if (ms == 0) { + // For ms == 0, we need to do a non-blocking event poll by + // setting the run mode to UV_RUN_NOWAIT. + run_mode = UV_RUN_NOWAIT; + } + + do { + // Run one event loop iteration, blocking for events if run_mode is + // UV_RUN_ONCE + uv_run(uv_default_loop(), run_mode); + } while ( + // Continue running if ... + !input_ready() && // we have no input + kl_empty(event_queue) && // no events are waiting to be processed + run_mode != UV_RUN_NOWAIT && // ms != 0 + !timed_out); // we didn't get a timeout + + input_stop(); + + if (ms > 0) { + // Stop the timer + uv_timer_stop(&timer); + } + + return input_ready() || event_is_pending(); +} + +bool event_is_pending() +{ + return !kl_empty(event_queue); +} + +// Push an event to the queue +void event_push(Event event) +{ + *kl_pushp(Event, event_queue) = event; +} + +// Runs the appropriate action for each queued event +void event_process() +{ + Event event; + + while (kl_shift(Event, event_queue, &event) == 0) { + switch (event.type) { + case kEventSignal: + signal_handle(event); + break; + case kEventRStreamData: + rstream_read_event(event); + break; + case kEventJobExit: + job_exit_event(event); + break; + default: + abort(); + } + } +} + +// Set a flag in the `event_poll` loop for signaling of a timeout +static void timer_cb(uv_timer_t *handle) +{ + *((bool *)handle->data) = true; +} + +static void timer_prepare_cb(uv_prepare_t *handle) +{ + uv_timer_start(&timer, timer_cb, *(uint32_t *)timer_prepare.data, 0); + uv_prepare_stop(&timer_prepare); +} |