diff --git a/src/main.cpp b/src/main.cpp index cdb36777..d4f56b08 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,8 +2,6 @@ #include "renderer/renderer.h" struct Context { - int start_x, start_y; - GridSize start_grid_size; bool start_maximized; bool start_fullscreen; bool disable_fullscreen; @@ -46,19 +44,13 @@ void ToggleFullscreen(HWND hwnd, Context *context) { void ProcessMPackMessage(Context *context, mpack_tree_t *tree) { MPackMessageResult result = MPackExtractMessageResult(tree); - if (result.type == MPackMessageType::Response) { + switch (result.type) { + case MPackMessageType::Response: { assert(result.response.msg_id <= context->nvim->next_msg_id); switch (context->nvim->msg_id_to_method[result.response.msg_id]) { - case NvimRequest::vim_get_api_info: { - mpack_node_t top_level_map = mpack_node_array_at(result.params, 1); - mpack_node_t version_map = mpack_node_map_value_at(top_level_map, 0); - int64_t api_level = mpack_node_map_cstr(version_map, "api_level").data->value.i; - assert(api_level > 6); - } break; case NvimRequest::nvim_eval: { Vec guifont_buffer; NvimParseConfig(context->nvim, result.params, &guifont_buffer); - if (!guifont_buffer.empty()) { bool updated = RendererUpdateGuiFont(context->renderer, guifont_buffer.data(), strlen(guifont_buffer.data())); if(!updated) { @@ -72,45 +64,27 @@ void ProcessMPackMessage(Context *context, mpack_tree_t *tree) { free(command); } } - - if (context->start_grid_size.rows != 0 && - context->start_grid_size.cols != 0) { - PixelSize start_size = RendererGridToPixelSize(context->renderer, - context->start_grid_size.rows, context->start_grid_size.cols); - RECT client_rect; - GetClientRect(context->hwnd, &client_rect); - MoveWindow(context->hwnd, client_rect.left, client_rect.top, - start_size.width, start_size.height, false); - } - - // Attach the renderer now that the window size is determined - RendererAttach(context->renderer); - auto [rows, cols] = RendererPixelsToGridSize(context->renderer, - context->renderer->pixel_size.width, context->renderer->pixel_size.height); - NvimSendUIAttach(context->nvim, rows, cols); - - if (context->start_x != CW_USEDEFAULT || - context->start_y != CW_USEDEFAULT) { - SetWindowPos(context->hwnd, NULL, - context->start_x, context->start_y, - 0, 0, - SWP_NOSIZE | SWP_NOREDRAW | SWP_NOACTIVATE | - SWP_NOOWNERZORDER | SWP_NOZORDER); - } - if (context->start_fullscreen) { - ToggleFullscreen(context->hwnd, context); - } - } break; - case NvimRequest::nvim_input: - case NvimRequest::nvim_input_mouse: - case NvimRequest::nvim_command: { } break; + case NvimRequest::vim_get_api_info: + case NvimRequest::nvim_input: + case NvimRequest::nvim_input_mouse: + case NvimRequest::nvim_command: { + } break; } - } - else if (result.type == MPackMessageType::Notification) { + } break; + case MPackMessageType::Notification: { if (MPackMatchString(result.notification.name, "redraw")) { RendererRedraw(context->renderer, result.params, context->start_maximized); } + } break; + case MPackMessageType::Request: { + if (MPackMatchString(result.request.method, "vimenter")) { + // nvim has read user init file, we can now request info if we want + // like additional startup settings or something else + NvimSendResponse(context->nvim, result.request.msg_id); + NvimQueryConfig(context->nvim); + } + } break; } } @@ -400,7 +374,7 @@ BOOL ShouldUseDarkMode() return false; } -int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_line, int n_cmd_show) { +int WINAPI wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, _In_ LPWSTR p_cmd_line, _In_ int n_cmd_show) { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); int n_args; @@ -410,16 +384,16 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin bool disable_ligatures = false; bool disable_fullscreen = false; float linespace_factor = 1.0f; - int64_t rows = 0; - int64_t cols = 0; - int64_t x = CW_USEDEFAULT; - int64_t y = CW_USEDEFAULT; - - constexpr int MAX_NVIM_CMD_LINE_SIZE = 32767; - wchar_t nvim_command_line[MAX_NVIM_CMD_LINE_SIZE] = {}; - wcscpy_s(nvim_command_line, MAX_NVIM_CMD_LINE_SIZE, L"nvim --embed"); - int cmd_line_size_left = MAX_NVIM_CMD_LINE_SIZE - wcslen(L"nvim --embed"); - + int64_t start_rows = 0; + int64_t start_cols = 0; + int64_t start_pos_x = CW_USEDEFAULT; + int64_t start_pos_y = CW_USEDEFAULT; + + struct WArg { + wchar_t *arg; + size_t len; + }; + Vec nvim_args; // Skip argv[0] for(int i = 1; i < n_args; ++i) { @@ -437,13 +411,13 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin } else if(!wcsncmp(cmd_line_args[i], L"--geometry=", wcslen(L"--geometry="))) { wchar_t *end_ptr; - cols = wcstol(&cmd_line_args[i][11], &end_ptr, 10); - rows = wcstol(end_ptr + 1, nullptr, 10); + start_cols = wcstol(&cmd_line_args[i][11], &end_ptr, 10); + start_rows = wcstol(end_ptr + 1, nullptr, 10); } else if(!wcsncmp(cmd_line_args[i], L"--position=", wcslen(L"--position="))) { wchar_t *end_ptr; - x = wcstol(&cmd_line_args[i][11], &end_ptr, 10); - y = wcstol(end_ptr + 1, nullptr, 10); + start_pos_x = wcstol(&cmd_line_args[i][11], &end_ptr, 10); + start_pos_y = wcstol(end_ptr + 1, nullptr, 10); } else if(!wcsncmp(cmd_line_args[i], L"--linespace-factor=", wcslen(L"--linespace-factor="))) { wchar_t *end_ptr; @@ -454,15 +428,10 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin } // Otherwise assume the argument is a filename to open else { - size_t arg_size = wcslen(cmd_line_args[i]); - if(arg_size <= (cmd_line_size_left + 3)) { - wcscat_s(nvim_command_line, cmd_line_size_left, L" \""); - cmd_line_size_left -= 2; - wcscat_s(nvim_command_line, cmd_line_size_left, cmd_line_args[i]); - cmd_line_size_left -= arg_size; - wcscat_s(nvim_command_line, cmd_line_size_left, L"\""); - cmd_line_size_left -= 1; - } + nvim_args.push_back(WArg{ + .arg = cmd_line_args[i], + .len = wcslen(cmd_line_args[i]) + }); } } @@ -473,10 +442,10 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin .style = CS_HREDRAW | CS_VREDRAW, .lpfnWndProc = WndProc, .hInstance = instance, - .hIcon = static_cast(LoadImage(GetModuleHandle(NULL), L"NVIM_ICON", IMAGE_ICON, LR_DEFAULTSIZE, LR_DEFAULTSIZE, 0)), + .hIcon = static_cast(LoadImage(GetModuleHandle(NULL), L"NVIM_ICON", IMAGE_ICON, LR_DEFAULTSIZE, LR_DEFAULTSIZE, 0)), .hCursor = LoadCursor(NULL, IDC_ARROW), .lpszClassName = window_class_name, - .hIconSm = static_cast(LoadImage(GetModuleHandle(NULL), L"NVIM_ICON", IMAGE_ICON, LR_DEFAULTSIZE, LR_DEFAULTSIZE, 0)) + .hIconSm = static_cast(LoadImage(GetModuleHandle(NULL), L"NVIM_ICON", IMAGE_ICON, LR_DEFAULTSIZE, LR_DEFAULTSIZE, 0)) }; if (!RegisterClassEx(&window_class)) { @@ -486,12 +455,6 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin Nvim nvim {}; Renderer renderer {}; Context context { - .start_x = static_cast(x), - .start_y = static_cast(y), - .start_grid_size { - .rows = static_cast(rows), - .cols = static_cast(cols) - }, .start_maximized = start_maximized, .start_fullscreen = start_fullscreen, .disable_fullscreen = disable_fullscreen, @@ -525,10 +488,47 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin BOOL should_use_dark_mode = ShouldUseDarkMode(); DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &should_use_dark_mode, sizeof(BOOL)); RendererInitialize(&renderer, hwnd, disable_ligatures, linespace_factor, context.saved_dpi_scaling); + + // Prepare nvim command line + size_t nvim_command_len = wcslen(L"nvim --embed"); + for (const auto& nvim_command_arg : nvim_args) { + nvim_command_len += wcslen(L" \"\"") + nvim_command_arg.len; + } + wchar_t *nvim_command_line = static_cast(malloc(sizeof(wchar_t) * (nvim_command_len + 1))); + int cur_len = _snwprintf_s(nvim_command_line, nvim_command_len + 1, nvim_command_len, L"nvim --embed"); + for (const auto& [arg, len] : nvim_args) { + cur_len += _snwprintf_s(nvim_command_line + cur_len, nvim_command_len + 1 - cur_len, nvim_command_len - cur_len, + L" \"%s\"", arg, static_cast(len)); + } + NvimInitialize(&nvim, nvim_command_line, hwnd); + // Forceably update the window to prevent any frames where the window is blank. Windows API docs // specify that SetWindowPos should be called with these arguments after SetWindowLong is called. - SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + UINT window_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED; + int window_w = 0, window_h = 0; + + if (start_rows != 0 && start_cols != 0) { + PixelSize start_size = RendererGridToPixelSize(context.renderer, + start_rows, start_cols); + window_w = start_size.width; + window_h = start_size.height; + window_flags = window_flags & ~SWP_NOSIZE; + } + if (start_pos_x != CW_USEDEFAULT || start_pos_y != CW_USEDEFAULT) { + window_flags = window_flags & ~SWP_NOMOVE; + } + SetWindowPos(hwnd, HWND_TOP, start_pos_x, start_pos_y, window_w, window_h, window_flags); + + if (start_fullscreen) { + ToggleFullscreen(context.hwnd, &context); + } + + // Attach the renderer now that the window size is determined + RendererAttach(context.renderer); + auto [rows, cols] = RendererPixelsToGridSize(context.renderer, + context.renderer->pixel_size.width, context.renderer->pixel_size.height); + NvimSendUIAttach(context.nvim, rows, cols); MSG msg; uint32_t previous_width = 0, previous_height = 0; @@ -551,6 +551,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR p_cmd_lin NvimShutdown(&nvim); UnregisterClass(window_class_name, instance); DestroyWindow(hwnd); + free(nvim_command_line); return nvim.exit_code; } diff --git a/src/nvim/nvim.cpp b/src/nvim/nvim.cpp index 99bb7023..e0771852 100644 --- a/src/nvim/nvim.cpp +++ b/src/nvim/nvim.cpp @@ -21,24 +21,28 @@ static size_t ReadFromNvim(mpack_tree_t *tree, char *buffer, size_t count) { return bytes_read; } +struct NvimMsgBroker { + Nvim *nvim; + mpack_tree_t *tree; +}; + DWORD WINAPI NvimMessageHandler(LPVOID param) { - Nvim *nvim = static_cast(param); - mpack_tree_t *tree = static_cast(malloc(sizeof(mpack_tree_t))); - mpack_tree_init_stream(tree, ReadFromNvim, nvim->stdout_read, Megabytes(20), 1024 * 1024); + NvimMsgBroker *broker = static_cast(param); while (true) { - mpack_tree_parse(tree); - if (mpack_tree_error(tree) != mpack_ok) { + mpack_tree_parse(broker->tree); + if (mpack_tree_error(broker->tree) != mpack_ok) { break; } // Blocking, dubious thread safety. Seems to work though... - SendMessage(nvim->hwnd, WM_NVIM_MESSAGE, reinterpret_cast(tree), 0); + SendMessage(broker->nvim->hwnd, WM_NVIM_MESSAGE, reinterpret_cast(broker->tree), 0); } - mpack_tree_destroy(tree); - free(tree); - PostMessage(nvim->hwnd, WM_DESTROY, 0, 0); + mpack_tree_destroy(broker->tree); + PostMessage(broker->nvim->hwnd, WM_DESTROY, 0, 0); + free(broker->tree); + free(broker); return 0; } @@ -99,31 +103,32 @@ void NvimInitialize(Nvim *nvim, wchar_t *command_line, HWND hwnd) { ); AssignProcessToJobObject(job_object, nvim->process_info.hProcess); - DWORD _; - CreateThread( - nullptr, - 0, - NvimMessageHandler, - nvim, - 0, - &_ - ); - CreateThread( - nullptr, - 0, NvimProcessMonitor, - nvim, - 0, &_ - ); + // Do the initial messages with nvim in sync + + mpack_tree_t *tree_reader = static_cast(malloc(sizeof(mpack_tree_t))); + mpack_tree_init_stream(tree_reader, ReadFromNvim, nvim->stdout_read, Megabytes(20), 1'048'576); - // Query api info char data[MAX_MPACK_OUTBOUND_MESSAGE_SIZE]; mpack_writer_t writer; mpack_writer_init(&writer, data, MAX_MPACK_OUTBOUND_MESSAGE_SIZE); + + // Query api info MPackStartRequest(RegisterRequest(nvim, vim_get_api_info), NVIM_REQUEST_NAMES[vim_get_api_info], &writer); mpack_start_array(&writer, 0); mpack_finish_array(&writer); size_t size = MPackFinishMessage(&writer); MPackSendData(nvim->stdin_write, data, size); + mpack_tree_parse(tree_reader); + if (mpack_tree_error(tree_reader)) { + return; + } + MPackMessageResult result = MPackExtractMessageResult(tree_reader); + if (result.type == MPackMessageType::Response){ + mpack_node_t top_level_map = mpack_node_array_at(result.params, 1); + mpack_node_t version_map = mpack_node_map_value_at(top_level_map, 0); + int64_t api_level = mpack_node_map_cstr(version_map, "api_level").data->value.i; + assert(api_level > 6); + } // Set g:nvy global variable mpack_writer_init(&writer, data, MAX_MPACK_OUTBOUND_MESSAGE_SIZE); @@ -135,14 +140,45 @@ void NvimInitialize(Nvim *nvim, wchar_t *command_line, HWND hwnd) { size = MPackFinishMessage(&writer); MPackSendData(nvim->stdin_write, data, size); - // Query stdpath to find the users init.vim + // Setup neovim to send a blocking request so we can finalize seting up before + // buffer mpack_writer_init(&writer, data, MAX_MPACK_OUTBOUND_MESSAGE_SIZE); - MPackStartRequest(RegisterRequest(nvim, nvim_eval), NVIM_REQUEST_NAMES[nvim_eval], &writer); + MPackStartRequest(RegisterRequest(nvim, nvim_command), NVIM_REQUEST_NAMES[nvim_command], &writer); mpack_start_array(&writer, 1); - mpack_write_cstr(&writer, "stdpath('config')"); + mpack_write_cstr(&writer, "autocmd VimEnter * call rpcrequest(1, 'vimenter')"); mpack_finish_array(&writer); size = MPackFinishMessage(&writer); MPackSendData(nvim->stdin_write, data, size); + mpack_tree_parse(tree_reader); + if (mpack_tree_error(tree_reader)) { + return; + } + result = MPackExtractMessageResult(tree_reader); // get the result just in case... + + // send the tree reader to the broker to be used by the stdout reader thread + NvimMsgBroker *broker = static_cast(malloc(sizeof(NvimMsgBroker))); + if (broker) { + broker->nvim = nvim; + broker->tree = tree_reader; + + DWORD _; + CreateThread( + nullptr, + 0, + NvimMessageHandler, + broker, + 0, + &_ + ); + CreateThread( + nullptr, + 0, + NvimProcessMonitor, + nvim, + 0, + &_ + ); + } } void NvimShutdown(Nvim *nvim) { @@ -154,79 +190,12 @@ void NvimShutdown(Nvim *nvim) { CloseHandle(nvim->stdin_write); CloseHandle(nvim->stdout_read); CloseHandle(nvim->stdout_write); - CloseHandle(nvim->process_info.hThread); TerminateProcess(nvim->process_info.hProcess, 0); CloseHandle(nvim->process_info.hProcess); + CloseHandle(nvim->process_info.hThread); } } -void NvimParseConfig(Nvim *nvim, mpack_node_t config_node, Vec *guifont_out) { - char path[MAX_PATH]; - const char *config_path = mpack_node_str(config_node); - size_t config_path_strlen = mpack_node_strlen(config_node); - strncpy_s(path, MAX_PATH, config_path, config_path_strlen); - strcat_s(path, MAX_PATH - config_path_strlen - 1, "\\init.vim"); - - HANDLE config_file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (config_file == INVALID_HANDLE_VALUE) { - return; - } - - char *buffer; - LARGE_INTEGER file_size; - if (!GetFileSizeEx(config_file, &file_size)) { - CloseHandle(config_file); - return; - } - buffer = static_cast(malloc(file_size.QuadPart)); - - DWORD bytes_read; - if (!ReadFile(config_file, buffer, file_size.QuadPart, &bytes_read, NULL)) { - CloseHandle(config_file); - free(buffer); - return; - } - CloseHandle(config_file); - - char *strtok_context; - char *line = strtok_s(buffer, "\r\n", &strtok_context); - while (line) { - char *guifont = strstr(line, "set guifont="); - if (guifont) { - // Check if we're inside a comment - int leading_count = guifont - line; - bool inside_comment = false; - for (int i = 0; i < leading_count; ++i) { - if (line[i] == '"') { - inside_comment = !inside_comment; - } - } - if (!inside_comment) { - guifont_out->clear(); - - int line_offset = (guifont - line + strlen("set guifont=")); - int guifont_strlen = strlen(line) - line_offset; - int escapes = 0; - for (int i = 0; i < guifont_strlen; ++i) { - if (line[line_offset + i] == '\\' && i < (guifont_strlen - 1) && line[line_offset + i + 1] == ' ') { - guifont_out->push_back(' '); - ++i; - continue; - } - guifont_out->push_back(line[i + line_offset]); - - } - guifont_out->push_back('\0'); - } - } - line = strtok_s(NULL, "\r\n", &strtok_context); - } - - free(buffer); -} - void NvimSendUIAttach(Nvim *nvim, int grid_rows, int grid_cols) { char data[MAX_MPACK_OUTBOUND_MESSAGE_SIZE]; mpack_writer_t writer; @@ -565,6 +534,85 @@ bool NvimProcessKeyDown(Nvim *nvim, int virtual_key) { return true; } +void NvimQueryConfig(Nvim *nvim) { + char data[MAX_MPACK_OUTBOUND_MESSAGE_SIZE]; + mpack_writer_t writer; + mpack_writer_init(&writer, data, MAX_MPACK_OUTBOUND_MESSAGE_SIZE); + MPackStartRequest(RegisterRequest(nvim, nvim_eval), NVIM_REQUEST_NAMES[nvim_eval], &writer); + mpack_start_array(&writer, 1); + mpack_write_cstr(&writer, "stdpath('config')"); + mpack_finish_array(&writer); + size_t size = MPackFinishMessage(&writer); + MPackSendData(nvim->stdin_write, data, size); +} + +void NvimParseConfig(Nvim *nvim, mpack_node_t config_node, Vec *guifont_out) { + char path[MAX_PATH]; + const char *config_path = mpack_node_str(config_node); + size_t config_path_strlen = mpack_node_strlen(config_node); + strncpy_s(path, MAX_PATH, config_path, config_path_strlen); + strcat_s(path, MAX_PATH - config_path_strlen - 1, "\\init.vim"); + + HANDLE config_file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (config_file == INVALID_HANDLE_VALUE) { + return; + } + + char *buffer; + LARGE_INTEGER file_size; + if (!GetFileSizeEx(config_file, &file_size)) { + CloseHandle(config_file); + return; + } + buffer = static_cast(malloc(file_size.QuadPart)); + + DWORD bytes_read; + if (!ReadFile(config_file, buffer, file_size.QuadPart, &bytes_read, NULL)) { + CloseHandle(config_file); + free(buffer); + return; + } + CloseHandle(config_file); + + char *strtok_context; + char *line = strtok_s(buffer, "\r\n", &strtok_context); + while (line) { + char *guifont = strstr(line, "set guifont="); + if (guifont) { + // Check if we're inside a comment + int leading_count = guifont - line; + bool inside_comment = false; + for (int i = 0; i < leading_count; ++i) { + if (line[i] == '"') { + inside_comment = !inside_comment; + } + } + if (!inside_comment) { + guifont_out->clear(); + + int line_offset = (guifont - line + strlen("set guifont=")); + int guifont_strlen = strlen(line) - line_offset; + int escapes = 0; + for (int i = 0; i < guifont_strlen; ++i) { + if (line[line_offset + i] == '\\' && i < (guifont_strlen - 1) && line[line_offset + i + 1] == ' ') { + guifont_out->push_back(' '); + ++i; + continue; + } + guifont_out->push_back(line[i + line_offset]); + + } + guifont_out->push_back('\0'); + } + } + line = strtok_s(NULL, "\r\n", &strtok_context); + } + + free(buffer); +} + void NvimSendCommand(Nvim *nvim, const char *command) { char data[MAX_MPACK_OUTBOUND_MESSAGE_SIZE]; mpack_writer_t writer; @@ -577,6 +625,20 @@ void NvimSendCommand(Nvim *nvim, const char *command) { MPackSendData(nvim->stdin_write, data, size); } +void NvimSendResponse(Nvim *nvim, int64_t req_id) { + char data[MAX_MPACK_OUTBOUND_MESSAGE_SIZE]; + mpack_writer_t writer; + mpack_writer_init(&writer, data, MAX_MPACK_OUTBOUND_MESSAGE_SIZE); + + mpack_start_array(&writer, 4); + mpack_write_i64(&writer, 1); + mpack_write_i64(&writer, req_id); + mpack_write_nil(&writer); + mpack_write_int(&writer, 0); + size_t size = MPackFinishMessage(&writer); + MPackSendData(nvim->stdin_write, data, size); +} + void NvimOpenFile(Nvim *nvim, const wchar_t *file_name, bool open_new_buffer) { char utf8_encoded[MAX_PATH]{}; WideCharToMultiByte(CP_UTF8, 0, file_name, -1, utf8_encoded, MAX_PATH, NULL, NULL); diff --git a/src/nvim/nvim.h b/src/nvim/nvim.h index 7fd02499..b8dc1a24 100644 --- a/src/nvim/nvim.h +++ b/src/nvim/nvim.h @@ -57,6 +57,7 @@ struct Nvim { void NvimInitialize(Nvim *nvim, wchar_t *command_line, HWND hwnd); void NvimShutdown(Nvim *nvim); +void NvimQueryConfig(Nvim *nvim); void NvimParseConfig(Nvim *nvim, mpack_node_t config_node, Vec *guifont_out); void NvimSendCommand(Nvim *nvim, const char *command); @@ -65,8 +66,8 @@ void NvimSendResize(Nvim *nvim, int grid_rows, int grid_cols); void NvimSendChar(Nvim *nvim, wchar_t input_char); void NvimSendSysChar(Nvim *nvim, wchar_t sys_char); void NvimSendInput(Nvim *nvim, const char* input_chars); -void NvimSendInput(Nvim *nvim, int virtual_key, int flags); void NvimSendMouseInput(Nvim *nvim, MouseButton button, MouseAction action, int mouse_row, int mouse_col); +void NvimSendResponse(Nvim *nvim, int64_t req_id); bool NvimProcessKeyDown(Nvim *nvim, int virtual_key); void NvimOpenFile(Nvim *nvim, const wchar_t *file_name, bool open_new_buffer = false); void NvimSetFocus(Nvim *nvim);