#include <assert.h>
#include <inttypes.h>
#include <msgpack/pack.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/ui.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
#include "nvim/channel.h"
#include "nvim/channel_defs.h"
#include "nvim/eval.h"
#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
#include "nvim/event/wstream.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf))
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/ui.c.generated.h"
# include "ui_events_remote.generated.h" // IWYU pragma: export
#endif
static PMap(uint64_t) connected_uis = MAP_INIT;
#define mpack_w(b, byte) *(*(b))++ = (char)(byte);
static void mpack_w2(char **b, uint32_t v)
{
*(*b)++ = (char)((v >> 8) & 0xff);
*(*b)++ = (char)(v & 0xff);
}
static void mpack_w4(char **b, uint32_t v)
{
*(*b)++ = (char)((v >> 24) & 0xff);
*(*b)++ = (char)((v >> 16) & 0xff);
*(*b)++ = (char)((v >> 8) & 0xff);
*(*b)++ = (char)(v & 0xff);
}
static void mpack_uint(char **buf, uint32_t val)
{
if (val > 0xffff) {
mpack_w(buf, 0xce);
mpack_w4(buf, val);
} else if (val > 0xff) {
mpack_w(buf, 0xcd);
mpack_w2(buf, val);
} else if (val > 0x7f) {
mpack_w(buf, 0xcc);
mpack_w(buf, val);
} else {
mpack_w(buf, val);
}
}
static void mpack_bool(char **buf, bool val)
{
mpack_w(buf, 0xc2 | (val ? 1 : 0));
}
static void mpack_array(char **buf, uint32_t len)
{
if (len < 0x10) {
mpack_w(buf, 0x90 | len);
} else if (len < 0x10000) {
mpack_w(buf, 0xdc);
mpack_w2(buf, len);
} else {
mpack_w(buf, 0xdd);
mpack_w4(buf, len);
}
}
static char *mpack_array_dyn16(char **buf)
{
mpack_w(buf, 0xdc);
char *pos = *buf;
mpack_w2(buf, 0xFFEF);
return pos;
}
static void mpack_str(char **buf, const char *str, size_t len)
{
assert(sizeof(schar_T) - 1 < 0x20);
mpack_w(buf, 0xa0 | len);
memcpy(*buf, str, len);
*buf += len;
}
static void remote_ui_destroy(UI *ui)
FUNC_ATTR_NONNULL_ALL
{
UIData *data = ui->data;
kv_destroy(data->call_buf);
XFREE_CLEAR(ui->term_name);
xfree(ui);
}
void remote_ui_disconnect(uint64_t channel_id)
{
UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
if (!ui) {
return;
}
pmap_del(uint64_t)(&connected_uis, channel_id, NULL);
ui_detach_impl(ui, channel_id);
remote_ui_destroy(ui);
}
#ifdef EXITFREE
void remote_ui_free_all_mem(void)
{
UI *ui;
map_foreach_value(&connected_uis, ui, {
remote_ui_destroy(ui);
});
map_destroy(uint64_t, &connected_uis);
}
#endif
/// Wait until ui has connected on stdio channel if only_stdio
/// is true, otherwise any channel.
void remote_ui_wait_for_attach(bool only_stdio)
{
if (only_stdio) {
Channel *channel = find_channel(CHAN_STDIO);
if (!channel) {
// this function should only be called in --embed mode, stdio channel
// can be assumed.
abort();
}
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1,
map_has(uint64_t, &connected_uis, CHAN_STDIO));
} else {
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, -1,
ui_active());
}
}
/// Activates UI events on the channel.
///
/// Entry point of all UI clients. Allows |\-\-embed| to continue startup.
/// Implies that the client is ready to show the UI. Adds the client to the
/// list of UIs. |nvim_list_uis()|
///
/// @note If multiple UI clients are attached, the global screen dimensions
/// degrade to the smallest client. E.g. if client A requests 80x40 but
/// client B requests 200x100, the global screen has size 80x40.
///
/// @param channel_id
/// @param width Requested screen columns
/// @param height Requested screen rows
/// @param options |ui-option| map
/// @param[out] err Error details, if any
void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictionary options,
Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
if (map_has(uint64_t, &connected_uis, channel_id)) {
api_set_error(err, kErrorTypeException,
"UI already attached to channel: %" PRId64, channel_id);
return;
}
if (width <= 0 || height <= 0) {
api_set_error(err, kErrorTypeValidation,
"Expected width > 0 and height > 0");
return;
}
UI *ui = xcalloc(1, sizeof(UI));
ui->width = (int)width;
ui->height = (int)height;
ui->pum_row = -1.0;
ui->pum_col = -1.0;
ui->rgb = true;
CLEAR_FIELD(ui->ui_ext);
for (size_t i = <
|