mirror of
https://github.com/neovim/neovim.git
synced 2026-01-31 01:22:25 +10:00
- Add `kl_shift_at` macro and backing function. This can be used to shift elements at arbitrary positions. `kl_shift` is now defined on top of the new macro. - Change shift/push API, now `kl_push` accepts an object as parameter and `kl_shift` returns the object instead of a status. An assertion against shifting at the end of a list(or empty lists) was added. - Add `kl_iter` and `kl_iter_at` macros. `kl_iter_at` is for starting the iteration at arbitrary positions.
178 lines
4.4 KiB
C
178 lines
4.4 KiB
C
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <uv.h>
|
|
|
|
#include "nvim/os/event.h"
|
|
#include "nvim/os/input.h"
|
|
#include "nvim/msgpack_rpc/defs.h"
|
|
#include "nvim/msgpack_rpc/channel.h"
|
|
#include "nvim/msgpack_rpc/server.h"
|
|
#include "nvim/msgpack_rpc/helpers.h"
|
|
#include "nvim/os/signal.h"
|
|
#include "nvim/os/rstream.h"
|
|
#include "nvim/os/wstream.h"
|
|
#include "nvim/os/job.h"
|
|
#include "nvim/vim.h"
|
|
#include "nvim/memory.h"
|
|
#include "nvim/misc2.h"
|
|
#include "nvim/ui.h"
|
|
#include "nvim/screen.h"
|
|
#include "nvim/terminal.h"
|
|
|
|
#include "nvim/lib/klist.h"
|
|
|
|
// event will be cleaned up after it gets processed
|
|
#define _destroy_event(x) // do nothing
|
|
KLIST_INIT(Event, Event, _destroy_event)
|
|
|
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
# include "os/event.c.generated.h"
|
|
#endif
|
|
// deferred_events: Events that should be processed as the K_EVENT special key
|
|
// immediate_events: Events that should be processed after exiting libuv event
|
|
// loop(to avoid recursion), but before returning from
|
|
// `event_poll`
|
|
static klist_t(Event) *deferred_events = NULL, *immediate_events = NULL;
|
|
static int deferred_events_allowed = 0;
|
|
|
|
void event_init(void)
|
|
{
|
|
// Initialize the event queues
|
|
deferred_events = kl_init(Event);
|
|
immediate_events = kl_init(Event);
|
|
// early msgpack-rpc initialization
|
|
msgpack_rpc_init_method_table();
|
|
msgpack_rpc_helpers_init();
|
|
// 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();
|
|
// finish mspgack-rpc initialization
|
|
channel_init();
|
|
server_init();
|
|
terminal_init();
|
|
}
|
|
|
|
void event_teardown(void)
|
|
{
|
|
if (!deferred_events) {
|
|
// Not initialized(possibly a --version invocation)
|
|
return;
|
|
}
|
|
|
|
process_events_from(immediate_events);
|
|
process_events_from(deferred_events);
|
|
input_stop();
|
|
channel_teardown();
|
|
job_teardown();
|
|
server_teardown();
|
|
signal_teardown();
|
|
terminal_teardown();
|
|
|
|
// this last `uv_run` will return after all handles are stopped, it will
|
|
// also take care of finishing any uv_close calls made by other *_teardown
|
|
// functions.
|
|
do {
|
|
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
|
} while (uv_loop_close(uv_default_loop()));
|
|
}
|
|
|
|
// Wait for some event
|
|
void event_poll(int ms)
|
|
{
|
|
static int recursive = 0;
|
|
|
|
if (recursive++) {
|
|
abort(); // Should not re-enter uv_run
|
|
}
|
|
|
|
uv_run_mode run_mode = UV_RUN_ONCE;
|
|
uv_timer_t timer;
|
|
|
|
if (ms > 0) {
|
|
uv_timer_init(uv_default_loop(), &timer);
|
|
// Use a repeating timeout of ms milliseconds to make sure
|
|
// we do not block indefinitely for I/O.
|
|
uv_timer_start(&timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
|
|
} 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;
|
|
}
|
|
|
|
loop(run_mode);
|
|
|
|
if (ms > 0) {
|
|
// Ensure the timer handle is closed and run the event loop
|
|
// once more to let libuv perform it's cleanup
|
|
uv_timer_stop(&timer);
|
|
uv_close((uv_handle_t *)&timer, NULL);
|
|
loop(UV_RUN_NOWAIT);
|
|
}
|
|
|
|
recursive--; // Can re-enter uv_run now
|
|
|
|
// In case this is run before event_init, don't process any events.
|
|
if (immediate_events) {
|
|
process_events_from(immediate_events);
|
|
}
|
|
}
|
|
|
|
bool event_has_deferred(void)
|
|
{
|
|
return deferred_events_allowed && !kl_empty(deferred_events);
|
|
}
|
|
|
|
void event_enable_deferred(void)
|
|
{
|
|
++deferred_events_allowed;
|
|
}
|
|
|
|
void event_disable_deferred(void)
|
|
{
|
|
--deferred_events_allowed;
|
|
}
|
|
|
|
// Queue an event
|
|
void event_push(Event event, bool deferred)
|
|
{
|
|
// Sometimes libuv will run pending callbacks(timer for example) before
|
|
// blocking for a poll. If this happens and the callback pushes a event to one
|
|
// of the queues, the event would only be processed after the poll
|
|
// returns(user hits a key for example). To avoid this scenario, we call
|
|
// uv_stop when a event is enqueued.
|
|
uv_stop(uv_default_loop());
|
|
kl_push(Event, deferred ? deferred_events : immediate_events, event);
|
|
}
|
|
|
|
void event_process(void)
|
|
{
|
|
process_events_from(deferred_events);
|
|
}
|
|
|
|
static void process_events_from(klist_t(Event) *queue)
|
|
{
|
|
while (!kl_empty(queue)) {
|
|
Event event = kl_shift(Event, queue);
|
|
event.handler(event);
|
|
}
|
|
}
|
|
|
|
static void timer_cb(uv_timer_t *handle)
|
|
{
|
|
}
|
|
|
|
static void loop(uv_run_mode run_mode)
|
|
{
|
|
DLOG("Enter event loop");
|
|
uv_run(uv_default_loop(), run_mode);
|
|
DLOG("Exit event loop");
|
|
}
|