Files
neovim/src/nvim/ui_bridge.c
dundargoc 66360675cf build: allow IWYU to fix includes for all .c files
Allow Include What You Use to remove unnecessary includes and only
include what is necessary. This helps with reducing compilation times
and makes it easier to visualise which dependencies are actually
required.

Work on https://github.com/neovim/neovim/issues/549, but doesn't close
it since this only works fully for .c files and not headers.
2022-11-15 10:30:03 +01:00

224 lines
6.9 KiB
C

// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// UI wrapper that sends requests to the UI thread.
// Used by the built-in TUI and libnvim-based UIs.
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/event/loop.h"
#include "nvim/grid_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/main.h"
#include "nvim/memory.h"
#include "nvim/ui.h"
#include "nvim/ui_bridge.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_bridge.c.generated.h"
#endif
#define UI(b) (((UIBridgeData *)b)->ui)
// Schedule a function call on the UI bridge thread.
#define UI_BRIDGE_CALL(ui, name, argc, ...) \
((UIBridgeData *)ui)->scheduler(event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
#define INT2PTR(i) ((void *)(intptr_t)i)
#define PTR2INT(p) ((Integer)(intptr_t)p)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events_bridge.generated.h"
#endif
UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
{
UIBridgeData *rv = xcalloc(1, sizeof(UIBridgeData));
rv->ui = ui;
rv->bridge.rgb = ui->rgb;
rv->bridge.width = ui->width;
rv->bridge.height = ui->height;
rv->bridge.stop = ui_bridge_stop;
rv->bridge.grid_resize = ui_bridge_grid_resize;
rv->bridge.grid_clear = ui_bridge_grid_clear;
rv->bridge.grid_cursor_goto = ui_bridge_grid_cursor_goto;
rv->bridge.mode_info_set = ui_bridge_mode_info_set;
rv->bridge.update_menu = ui_bridge_update_menu;
rv->bridge.busy_start = ui_bridge_busy_start;
rv->bridge.busy_stop = ui_bridge_busy_stop;
rv->bridge.mouse_on = ui_bridge_mouse_on;
rv->bridge.mouse_off = ui_bridge_mouse_off;
rv->bridge.mode_change = ui_bridge_mode_change;
rv->bridge.grid_scroll = ui_bridge_grid_scroll;
rv->bridge.hl_attr_define = ui_bridge_hl_attr_define;
rv->bridge.bell = ui_bridge_bell;
rv->bridge.visual_bell = ui_bridge_visual_bell;
rv->bridge.default_colors_set = ui_bridge_default_colors_set;
rv->bridge.flush = ui_bridge_flush;
rv->bridge.suspend = ui_bridge_suspend;
rv->bridge.set_title = ui_bridge_set_title;
rv->bridge.set_icon = ui_bridge_set_icon;
rv->bridge.screenshot = ui_bridge_screenshot;
rv->bridge.option_set = ui_bridge_option_set;
rv->bridge.raw_line = ui_bridge_raw_line;
rv->bridge.inspect = ui_bridge_inspect;
rv->scheduler = scheduler;
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
rv->bridge.ui_ext[i] = ui->ui_ext[i];
}
rv->ui_main = ui_main;
uv_mutex_init(&rv->mutex);
uv_cond_init(&rv->cond);
uv_mutex_lock(&rv->mutex);
rv->ready = false;
if (uv_thread_create(&rv->ui_thread, ui_thread_run, rv)) {
abort();
}
// Suspend the main thread until CONTINUE is called by the UI thread.
while (!rv->ready) {
uv_cond_wait(&rv->cond, &rv->mutex);
}
uv_mutex_unlock(&rv->mutex);
ui_attach_impl(&rv->bridge, 0);
return &rv->bridge;
}
void ui_bridge_stopped(UIBridgeData *bridge)
{
uv_mutex_lock(&bridge->mutex);
bridge->stopped = true;
uv_mutex_unlock(&bridge->mutex);
}
static void ui_thread_run(void *data)
{
UIBridgeData *bridge = data;
bridge->ui_main(bridge, bridge->ui);
}
static void ui_bridge_stop(UI *b)
{
// Detach bridge first, so that "stop" is the last event the TUI loop
// receives from the main thread. #8041
ui_detach_impl(b, 0);
UIBridgeData *bridge = (UIBridgeData *)b;
bool stopped = bridge->stopped = false;
UI_BRIDGE_CALL(b, stop, 1, b);
for (;;) {
uv_mutex_lock(&bridge->mutex);
stopped = bridge->stopped;
uv_mutex_unlock(&bridge->mutex);
if (stopped) { // -V547
break;
}
// TODO(justinmk): Remove this. Use a cond-wait above. #9274
loop_poll_events(&main_loop, 10); // Process one event.
}
uv_thread_join(&bridge->ui_thread);
uv_mutex_destroy(&bridge->mutex);
uv_cond_destroy(&bridge->cond);
xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922
xfree(b);
}
static void ui_bridge_stop_event(void **argv)
{
UI *ui = UI(argv[0]);
ui->stop(ui);
}
static void ui_bridge_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs,
Array info)
{
HlAttrs *a = xmalloc(sizeof(HlAttrs));
*a = attrs;
UI_BRIDGE_CALL(ui, hl_attr_define, 3, ui, INT2PTR(id), a);
}
static void ui_bridge_hl_attr_define_event(void **argv)
{
UI *ui = UI(argv[0]);
Array info = ARRAY_DICT_INIT;
ui->hl_attr_define(ui, PTR2INT(argv[1]), *((HlAttrs *)argv[2]),
*((HlAttrs *)argv[2]), info);
xfree(argv[2]);
}
static void ui_bridge_raw_line_event(void **argv)
{
UI *ui = UI(argv[0]);
ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
(LineFlags)PTR2INT(argv[7]), argv[8], argv[9]);
xfree(argv[8]);
xfree(argv[9]);
}
static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr, LineFlags flags,
const schar_T *chunk, const sattr_T *attrs)
{
size_t ncol = (size_t)(endcol - startcol);
schar_T *c = xmemdup(chunk, ncol * sizeof(schar_T));
sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
UI_BRIDGE_CALL(ui, raw_line, 10, ui, INT2PTR(grid), INT2PTR(row),
INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
INT2PTR(clearattr), INT2PTR(flags), c, hl);
}
static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;
uv_mutex_lock(&data->mutex);
UI_BRIDGE_CALL(b, suspend, 1, b);
data->ready = false;
// Suspend the main thread until CONTINUE is called by the UI thread.
while (!data->ready) {
uv_cond_wait(&data->cond, &data->mutex);
}
uv_mutex_unlock(&data->mutex);
}
static void ui_bridge_suspend_event(void **argv)
{
UI *ui = UI(argv[0]);
ui->suspend(ui);
}
static void ui_bridge_option_set(UI *ui, String name, Object value)
{
String copy_name = copy_string(name, NULL);
Object *copy_value = xmalloc(sizeof(Object));
*copy_value = copy_object(value, NULL);
UI_BRIDGE_CALL(ui, option_set, 4, ui, copy_name.data,
INT2PTR(copy_name.size), copy_value);
// TODO(bfredl): when/if TUI/bridge teardown is refactored to use events, the
// commit that introduced this special case can be reverted.
// For now this is needed for nvim_list_uis().
if (strequal(name.data, "termguicolors")) {
ui->rgb = value.data.boolean;
}
}
static void ui_bridge_option_set_event(void **argv)
{
UI *ui = UI(argv[0]);
String name = (String){ .data = argv[1], .size = (size_t)argv[2] };
Object value = *(Object *)argv[3];
ui->option_set(ui, name, value);
api_free_string(name);
api_free_object(value);
xfree(argv[3]);
}
static void ui_bridge_inspect(UI *ui, Dictionary *info)
{
PUT(*info, "chan", INTEGER_OBJ(0));
}