aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/event.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os/event.c')
-rw-r--r--src/nvim/os/event.c148
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);
+}