diff options
author | hlpr98 <hlpr98@gmail.com> | 2019-05-27 22:04:24 +0530 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2022-12-31 10:43:28 +0100 |
commit | 24488169564c39a506c235bf6a33b8e23a8cb528 (patch) | |
tree | 3b30c1fcc3b4ce2fef81d45eebe61baa4f0315ed /src | |
parent | 4ace9e7e417fe26c8b73ff1d6042e6e4f3df9ebf (diff) | |
download | rneovim-24488169564c39a506c235bf6a33b8e23a8cb528.tar.gz rneovim-24488169564c39a506c235bf6a33b8e23a8cb528.tar.bz2 rneovim-24488169564c39a506c235bf6a33b8e23a8cb528.zip |
feat(tui): run TUI as external process
Diffstat (limited to 'src')
-rwxr-xr-x | src/nvim/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 35 | ||||
-rw-r--r-- | src/nvim/api/ui_events.in.h | 17 | ||||
-rw-r--r-- | src/nvim/event/libuv_process.c | 8 | ||||
-rw-r--r-- | src/nvim/event/libuv_process.h | 2 | ||||
-rw-r--r-- | src/nvim/event/process.c | 4 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 40 | ||||
-rwxr-xr-x | src/nvim/generators/gen_api_ui_events.lua | 63 | ||||
-rw-r--r-- | src/nvim/globals.h | 8 | ||||
-rw-r--r-- | src/nvim/main.c | 52 | ||||
-rw-r--r-- | src/nvim/tui/input.c | 55 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 135 | ||||
-rw-r--r-- | src/nvim/ui.c | 15 | ||||
-rw-r--r-- | src/nvim/ui_bridge.c | 223 | ||||
-rw-r--r-- | src/nvim/ui_bridge.h | 47 | ||||
-rw-r--r-- | src/nvim/ui_client.c | 33 |
16 files changed, 293 insertions, 448 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 5a64128bcb..92f75dbf75 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,7 +39,6 @@ set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generat set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h) set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) -set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h) set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) @@ -220,7 +219,6 @@ foreach(sfile ${NVIM_SOURCES} ${GENERATED_API_DISPATCH} "${GENERATED_UI_EVENTS_CALL}" "${GENERATED_UI_EVENTS_REMOTE}" - "${GENERATED_UI_EVENTS_BRIDGE}" "${GENERATED_KEYSETS}" "${GENERATED_UI_EVENTS_CLIENT}" ) @@ -318,7 +316,6 @@ add_custom_command( OUTPUT ${GENERATED_UI_EVENTS} ${GENERATED_UI_EVENTS_CALL} ${GENERATED_UI_EVENTS_REMOTE} - ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} ${GENERATED_UI_EVENTS_CLIENT} COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} @@ -326,7 +323,6 @@ add_custom_command( ${GENERATED_UI_EVENTS} ${GENERATED_UI_EVENTS_CALL} ${GENERATED_UI_EVENTS_REMOTE} - ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} ${GENERATED_UI_EVENTS_CLIENT} DEPENDS diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index aeccddb9ea..ef6ced1ee0 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -14,9 +14,6 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" #include "nvim/channel.h" -#include "nvim/event/loop.h" -#include "nvim/event/wstream.h" -#include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" #include "nvim/main.h" @@ -30,6 +27,7 @@ #include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" +#include "nvim/window.h" typedef struct { uint64_t channel_id; @@ -285,6 +283,19 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enabl api_free_dictionary(opts); } +/// Tells the nvim server if focus was gained by the GUI or not +void nvim_ui_set_focus(uint64_t channel_id, Boolean gained, Error *error) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY +{ + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { + api_set_error(error, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); + return; + } + + autocmd_schedule_focusgained((bool)gained); +} + /// Deactivates UI events on the channel. /// /// Removes the client from the list of UIs. |nvim_list_uis()| @@ -404,6 +415,24 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e return; } + if (strequal(name.data, "term_ttyin")) { + if (value.type != kObjectTypeInteger) { + api_set_error(error, kErrorTypeValidation, "term_ttyin must be a Integer"); + return; + } + stdin_isatty = (int)value.data.integer; + return; + } + + if (strequal(name.data, "term_ttyout")) { + if (value.type != kObjectTypeInteger) { + api_set_error(error, kErrorTypeValidation, "term_ttyout must be a Integer"); + return; + } + stdout_isatty = (int)value.data.integer; + return; + } + // LEGACY: Deprecated option, use `ext_cmdline` instead. bool is_popupmenu = strequal(name.data, "popupmenu_external"); diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 21400862b9..d5c79272b7 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -31,7 +31,7 @@ void visual_bell(void) void flush(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL; void suspend(void) - FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(3); void set_title(String title) FUNC_API_SINCE(3); void set_icon(String icon) @@ -39,7 +39,7 @@ void set_icon(String icon) void screenshot(String path) FUNC_API_SINCE(7) FUNC_API_REMOTE_IMPL; void option_set(String name, Object value) - FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(4); // Stop event is not exported as such, represented by EOF in the msgpack stream. void stop(void) FUNC_API_NOEXPORT; @@ -73,9 +73,9 @@ void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer Integer cterm_bg) FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL; void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) - FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; void hl_group_set(String name, Integer id) - FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(6); void grid_resize(Integer grid, Integer width, Integer height) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL; void grid_clear(Integer grid) @@ -112,8 +112,9 @@ void win_hide(Integer grid) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_close(Integer grid) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; + void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char) - FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; + FUNC_API_SINCE(6) FUNC_API_COMPOSITOR_IMPL; void win_viewport(Integer grid, Window win, Integer topline, Integer botline, Integer curline, Integer curcol, Integer line_count) @@ -149,11 +150,11 @@ void cmdline_block_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void wildmenu_show(Array items) - FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL; void wildmenu_select(Integer selected) - FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL; void wildmenu_hide(void) - FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL; void msg_show(String kind, Array content, Boolean replace_last) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index c5d3b94c95..cf4ff16c4d 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -40,11 +40,19 @@ int libuv_process_spawn(LibuvProcess *uvproc) #endif uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.cwd = proc->cwd; + uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio_count = 3; uvproc->uvstdio[0].flags = UV_IGNORE; uvproc->uvstdio[1].flags = UV_IGNORE; uvproc->uvstdio[2].flags = UV_IGNORE; + + // TODO: this should just be single flag! + if (TUI_process && !is_remote_client && !stdin_isatty) { + uvproc->uvopts.stdio_count = 4; + uvproc->uvstdio[3].data.fd = 0; + uvproc->uvstdio[3].flags = UV_INHERIT_FD; + } uvproc->uv.data = proc; if (proc->env) { diff --git a/src/nvim/event/libuv_process.h b/src/nvim/event/libuv_process.h index 8f987847d8..4472839944 100644 --- a/src/nvim/event/libuv_process.h +++ b/src/nvim/event/libuv_process.h @@ -10,7 +10,7 @@ typedef struct libuv_process { Process process; uv_process_t uv; uv_process_options_t uvopts; - uv_stdio_container_t uvstdio[3]; + uv_stdio_container_t uvstdio[4]; } LibuvProcess; static inline LibuvProcess libuv_process_init(Loop *loop, void *data) diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index e74e95669d..52a9394e88 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -404,6 +404,10 @@ static void on_process_exit(Process *proc) ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status, proc->stopped_time); + if (TUI_process && !is_remote_client) { + // Set only in "builtin" TUI + server_process_exit_status = proc->status; + } // Process has terminated, but there could still be data to be read from the // OS. We are still in the libuv loop, so we cannot call code that polls for // more data directly. Instead delay the reading after the libuv loop by diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index d4fe455f82..240b99ca29 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -60,6 +60,12 @@ for i = 6, #arg do if public and not fn.noexport then functions[#functions + 1] = tmp[j] function_names[fn.name] = true + if #fn.parameters >= 2 and fn.parameters[2][1] == 'Array' and fn.parameters[2][2] == 'uidata' then + -- function recieves the "args" as a parameter + fn.receives_array_args = true + -- remove the args parameter + table.remove(fn.parameters, 2) + end if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then -- this function should receive the channel id fn.receives_channel_id = true @@ -159,7 +165,7 @@ local exported_attributes = {'name', 'return_type', 'method', 'since', 'deprecated_since'} local exported_functions = {} for _,f in ipairs(functions) do - if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event") then + if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event" or f.name == "redraw") then local f_exported = {} for _,attr in ipairs(exported_attributes) do f_exported[attr] = f[attr] @@ -264,11 +270,13 @@ for i = 1, #functions do output:write('\n '..rt..' '..converted..';') end output:write('\n') - output:write('\n if (args.size != '..#fn.parameters..') {') - output:write('\n api_set_error(error, kErrorTypeException, \ - "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') - output:write('\n goto cleanup;') - output:write('\n }\n') + if not fn.receives_array_args then + output:write('\n if (args.size != '..#fn.parameters..') {') + output:write('\n api_set_error(error, kErrorTypeException, \ + "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') + output:write('\n goto cleanup;') + output:write('\n }\n') + end -- Validation/conversion for each argument for j = 1, #fn.parameters do @@ -350,12 +358,28 @@ for i = 1, #functions do if fn.receives_channel_id then -- if the function receives the channel id, pass it as first argument if #args > 0 or fn.can_fail then - output:write('channel_id, '..call_args) + output:write('channel_id, ') + if fn.receives_array_args then + -- if the function recieves the array args, pass it the second argument + output:write('args, ') + end + output:write(call_args) else output:write('channel_id') + if fn.receives_array_args then + output:write(', args') + end end else - output:write(call_args) + if fn.receives_array_args then + if #args > 0 or fn.call_fail then + output:write('args, '..call_args) + else + output:write('args') + end + else + output:write(call_args) + end end if fn.arena_return then diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index ea66be7ee8..c9a2e3ff66 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -3,14 +3,13 @@ local mpack = require('mpack') local nvimdir = arg[1] package.path = nvimdir .. '/?.lua;' .. package.path -assert(#arg == 8) +assert(#arg == 7) local input = io.open(arg[2], 'rb') local proto_output = io.open(arg[3], 'wb') local call_output = io.open(arg[4], 'wb') local remote_output = io.open(arg[5], 'wb') -local bridge_output = io.open(arg[6], 'wb') -local metadata_output = io.open(arg[7], 'wb') -local client_output = io.open(arg[8], 'wb') +local metadata_output = io.open(arg[6], 'wb') +local client_output = io.open(arg[7], 'wb') local c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) @@ -119,62 +118,6 @@ for i = 1, #events do remote_output:write(' push_call(ui, "'..ev.name..'", args);\n') remote_output:write('}\n\n') end - - if not ev.bridge_impl and not ev.noexport then - local send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', '' - local argc = 1 - for j = 1, #ev.parameters do - local param = ev.parameters[j] - local copy = 'copy_'..param[2] - if param[1] == 'String' then - send = send..' String copy_'..param[2]..' = copy_string('..param[2]..', NULL);\n' - argv = argv..', '..copy..'.data, INT2PTR('..copy..'.size)' - recv = (recv..' String '..param[2].. - ' = (String){.data = argv['..argc..'],'.. - '.size = (size_t)argv['..(argc+1)..']};\n') - recv_argv = recv_argv..', '..param[2] - recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n' - argc = argc+2 - elseif param[1] == 'Array' then - send = send..' Array '..copy..' = copy_array('..param[2]..', NULL);\n' - argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)' - recv = (recv..' Array '..param[2].. - ' = (Array){.items = argv['..argc..'],'.. - '.size = (size_t)argv['..(argc+1)..']};\n') - recv_argv = recv_argv..', '..param[2] - recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n' - argc = argc+2 - elseif param[1] == 'Object' then - send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n' - send = send..' *'..copy..' = copy_object('..param[2]..', NULL);\n' - argv = argv..', '..copy - recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n' - recv_argv = recv_argv..', '..param[2] - recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'.. - ' xfree(argv['..argc..']);\n') - argc = argc+1 - elseif param[1] == 'Integer' or param[1] == 'Boolean' then - argv = argv..', INT2PTR('..param[2]..')' - recv_argv = recv_argv..', PTR2INT(argv['..argc..'])' - argc = argc+1 - else - assert(false) - end - end - bridge_output:write('static void ui_bridge_'..ev.name.. - '_event(void **argv)\n{\n') - bridge_output:write(' UI *ui = UI(argv[0]);\n') - bridge_output:write(recv) - bridge_output:write(' ui->'..ev.name..'(ui'..recv_argv..');\n') - bridge_output:write(recv_cleanup) - bridge_output:write('}\n\n') - - bridge_output:write('static void ui_bridge_'..ev.name) - write_signature(bridge_output, ev, 'UI *ui') - bridge_output:write('\n{\n') - bridge_output:write(send) - bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n') - end end if not (ev.remote_only and ev.remote_impl) then diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 52a48a8389..a88360696d 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -850,6 +850,14 @@ EXTERN linenr_T printer_page_num; EXTERN bool typebuf_was_filled INIT(= false); // received text from client // or from feedkeys() +EXTERN bool is_remote_client INIT(= false); // Initially the TUI is not + // a remote client + +EXTERN bool TUI_process INIT(= false); // This is the TUI process + + +EXTERN long server_process_exit_status INIT(= false); // Used by TUI process + #ifdef BACKSLASH_IN_FILENAME EXTERN char psepc INIT(= '\\'); // normal path separator character EXTERN char psepcN INIT(= '/'); // abnormal path separator character diff --git a/src/nvim/main.c b/src/nvim/main.c index 9e14e911ff..422495519a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -97,6 +97,10 @@ #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/signal.h" +#ifndef MSWIN +# include "nvim/os/pty_process_unix.h" +#endif +#include "nvim/tui/tui.h" // values for "window_layout" enum { @@ -137,6 +141,7 @@ void event_init(void) // early msgpack-rpc initialization msgpack_rpc_helpers_init(); + // Initialize input events input_init(); signal_init(); // finish mspgack-rpc initialization @@ -291,7 +296,13 @@ int main(int argc, char **argv) } } - server_init(params.listen_addr); + bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode); + // bool is_remote_client = false; // TODO: rename to specifically for --remote-ui + // + if (!(is_remote_client || use_builtin_ui)) { + server_init(params.listen_addr); + } + if (params.remote) { remote_request(¶ms, params.remote, params.server_addr, argc, argv); } @@ -352,7 +363,7 @@ int main(int argc, char **argv) // Wait for UIs to set up Nvim or show early messages // and prompts (--cmd, swapfile dialog, …). bool use_remote_ui = (embedded_mode && !headless_mode); - bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode); + TUI_process = is_remote_client || use_builtin_ui; if (use_remote_ui || use_builtin_ui) { TIME_MSG("waiting for UI"); if (use_remote_ui) { @@ -376,6 +387,31 @@ int main(int argc, char **argv) abort(); // unreachable } + // Setting up the remote connection. + // This has to be always after ui_builtin_start or + // after the start of atleast one GUI + // as size of "uis[]" must be greater than 1 + if (TUI_process) { + input_stop(); // Stop reading input, let the UI take over. + uint64_t rv = ui_client_start(params.argc, params.argv, + (params.edit_type == EDIT_STDIN + && !recoverymode)); + if (!rv) { + // cannot continue without a channel + // TODO: use ui_call_stop() ? + tui_exit_safe(ui_get_by_index(1)); + ELOG("RPC: ", NULL, -1, true, + "Could not establish connection with address : %s", params.server_addr); + os_msg("Could not establish connection with remote server\n"); + getout(1); + } + // TODO: fuuu, deduplicate with ui_client_channel_id block above + ui_client_channel_id = rv; + ui_client_execute(ui_client_channel_id); + abort(); // unreachable + } + + // Default mappings (incl. menus) Error err = ERROR_INIT; Object o = NLUA_EXEC_STATIC("return vim._init_default_mappings()", @@ -384,6 +420,7 @@ int main(int argc, char **argv) api_clear_error(&err); assert(o.type == kObjectTypeNil); api_free_object(o); + TIME_MSG("init default mappings"); init_default_autocmds(); @@ -624,6 +661,9 @@ void os_exit(int r) free_all_mem(); #endif + if (TUI_process && !is_remote_client) { + r = (int)server_process_exit_status; + } exit(r); } @@ -1376,6 +1416,13 @@ scripterror: // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299 if (edit_stdin(had_stdin_file, parmp)) { parmp->edit_type = EDIT_STDIN; + // TODO: copy + bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode); + if (use_builtin_ui && !is_remote_client) { + // must be set only in builtin TUI + // TODO + //implicit_readstdin = true; + } } TIME_MSG("parsing arguments"); @@ -2149,6 +2196,7 @@ static void usage(void) os_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); os_msg(_(" --headless Don't start a user interface\n")); os_msg(_(" --listen <address> Serve RPC API from this address\n")); + os_msg(_(" --connect <address> Specify Nvim server to connect to\n")); os_msg(_(" --noplugin Don't load plugins\n")); os_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); os_msg(_(" --server <address> Specify RPC server to send commands to\n")); diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 9171f79c37..ca1f7c25d7 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -32,6 +32,7 @@ #endif #include "nvim/event/rstream.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/ui.h" #define KEY_BUFFER_SIZE 0xfff @@ -226,8 +227,9 @@ static void tinput_wait_enqueue(void **argv) ADD(args, INTEGER_OBJ(input->paste)); // 'phase' rpc_send_event(ui_client_channel_id, "nvim_paste", args); } else { - multiqueue_put(main_loop.events, tinput_paste_event, 3, - keys.data, keys.size, (intptr_t)input->paste); + // TODO + // multiqueue_put(main_loop.events, tinput_paste_event, 3, + // keys.data, keys.size, (intptr_t)input->paste); } if (input->paste == 1) { // Paste phase: "continue" @@ -248,7 +250,9 @@ static void tinput_wait_enqueue(void **argv) consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; arena_mem_free(res_mem); } else { - consumed = input_enqueue(keys); + // TODO + // consumed = input_enqueue(keys); + abort(); } if (consumed) { rbuffer_consumed(input->key_buffer, consumed); @@ -259,38 +263,15 @@ static void tinput_wait_enqueue(void **argv) } } } - uv_mutex_lock(&input->key_buffer_mutex); - input->waiting = false; - uv_cond_signal(&input->key_buffer_cond); - uv_mutex_unlock(&input->key_buffer_mutex); } -static void tinput_paste_event(void **argv) -{ - String keys = { .data = argv[0], .size = (size_t)argv[1] }; - intptr_t phase = (intptr_t)argv[2]; - - Error err = ERROR_INIT; - nvim_paste(keys, true, phase, &err); - if (ERROR_SET(&err)) { - semsg("paste: %s", err.msg); - api_clear_error(&err); - } - - api_free_string(keys); -} static void tinput_flush(TermInput *input, bool wait_until_empty) { size_t drain_boundary = wait_until_empty ? 0 : 0xff; + // TODO: fuuuuuuuuuuuuuuu do { - uv_mutex_lock(&input->key_buffer_mutex); - loop_schedule_fast(&main_loop, event_create(tinput_wait_enqueue, 1, input)); - input->waiting = true; - while (input->waiting) { - uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex); - } - uv_mutex_unlock(&input->key_buffer_mutex); + tinput_wait_enqueue((void**)&input); } while (rbuffer_size(input->key_buffer) > drain_boundary); } @@ -569,8 +550,10 @@ static bool handle_focus_event(TermInput *input) || !rbuffer_cmp(input->read_stream.buffer, "\x1b[O", 3))) { bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; // Advance past the sequence - rbuffer_consumed(input->read_stream.buffer, 3); - autocmd_schedule_focusgained(focus_gained); + + Array args = ARRAY_DICT_INIT; + ADD(args, BOOLEAN_OBJ(focus_gained)); + rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args); return true; } return false; @@ -617,10 +600,13 @@ static HandleState handle_bracketed_paste(TermInput *input) return kNotApplicable; } -static void set_bg_deferred(void **argv) +static void set_bg(char *bgvalue) { - char *bgvalue = argv[0]; - set_tty_background(bgvalue); + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstr_to_string("term_background"))); + ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); + + rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } // During startup, tui.c requests the background color (see `ext.get_bg`). @@ -704,8 +690,7 @@ static HandleState handle_background_color(TermInput *input) double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601 char *bgvalue = luminance < 0.5 ? "dark" : "light"; DLOG("bg response: %s", bgvalue); - loop_schedule_deferred(&main_loop, - event_create(set_bg_deferred, 1, bgvalue)); + set_bg(bgvalue); input->waiting_for_bg_response = 0; } else if (!done && !bad) { // An incomplete sequence was found, waiting for the next input. diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3010a7b612..4634c77a1f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1,7 +1,7 @@ // 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 -// Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread. +// Terminal UI functions. Invoked (by UI_CALL) on the UI process. #include <assert.h> #include <signal.h> @@ -48,7 +48,7 @@ #include "nvim/tui/terminfo.h" #include "nvim/tui/tui.h" #include "nvim/ugrid.h" -#include "nvim/ui_bridge.h" +#include "nvim/msgpack_rpc/channel.h" // Space reserved in two output buffers to make the cursor normal or invisible // when flushing. No existing terminal will require 32 bytes to do that. @@ -91,7 +91,7 @@ typedef struct { } Rect; struct TUIData { - UIBridgeData *bridge; + UI *ui; Loop *loop; unibi_var_t params[9]; char buf[OUTBUF_SIZE]; @@ -159,18 +159,19 @@ struct TUIData { int get_extkeys; } unibi_ext; char *space_buf; + bool stopped; }; static int got_winch = 0; static bool cursor_style_enabled = false; - +char *termname_local; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" #endif UI *tui_start(void) { - UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop(). + UI *ui = xcalloc(1, sizeof(UI)); // Freed by tui_data_destroy(). ui->stop = tui_stop; ui->grid_resize = tui_grid_resize; ui->grid_clear = tui_grid_clear; @@ -198,15 +199,18 @@ UI *tui_start(void) CLEAR_FIELD(ui->ui_ext); ui->ui_ext[kUILinegrid] = true; ui->ui_ext[kUITermColors] = true; + + tui_main(ui); + ui_attach_impl(ui, 0); - return ui_bridge_attach(ui, tui_main, tui_scheduler); + return ui; } void tui_enable_extkeys(TUIData *data) { TermInput input = data->input; unibi_term *ut = data->ut; - UI *ui = data->bridge->ui; + UI *ui = data->ui; switch (input.extkeys_type) { case kExtkeysCSIu: @@ -237,13 +241,6 @@ static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, char *b return unibi_run(str, data->params, buf, len); } -static void termname_set_event(void **argv) -{ - char *termname = argv[0]; - set_tty_option("term", termname); - // Do not free termname, it is freed by set_tty_option. -} - static void terminfo_start(UI *ui) { TUIData *data = ui->data; @@ -294,22 +291,19 @@ static void terminfo_start(UI *ui) #endif // Set up unibilium/terminfo. - char *termname = NULL; + termname_local = NULL; if (term) { os_env_var_lock(); data->ut = unibi_from_term(term); os_env_var_unlock(); if (data->ut) { - termname = xstrdup(term); + termname_local = xstrdup(term); data->term = xstrdup(term); } } if (!data->ut) { - data->ut = terminfo_from_builtin(term, &termname); + data->ut = terminfo_from_builtin(term, &termname_local); } - // Update 'term' option. - loop_schedule_deferred(&main_loop, - event_create(termname_set_event, 1, termname)); // None of the following work over SSH; see :help TERM . const char *colorterm = os_getenv("COLORTERM"); @@ -467,7 +461,7 @@ static void tui_terminal_stop(UI *ui) if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) { // Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075 ELOG("TUI already stopped (race?)"); - ui->data = NULL; // Flag UI as "stopped". + data->stopped = true; return; } tinput_stop(&data->input); @@ -479,26 +473,27 @@ static void tui_terminal_stop(UI *ui) static void tui_stop(UI *ui) { tui_terminal_stop(ui); - ui->data = NULL; // Flag UI as "stopped". + TUIData *data = ui->data; + data->stopped = true; } /// Returns true if UI `ui` is stopped. static bool tui_is_stopped(UI *ui) { - return ui->data == NULL; + TUIData *data = ui->data; + return data->stopped; } -/// Main function of the TUI thread. -static void tui_main(UIBridgeData *bridge, UI *ui) +// Main function for TUI +static void tui_main(UI *ui) { - Loop tui_loop; - loop_init(&tui_loop, NULL); TUIData *data = xcalloc(1, sizeof(TUIData)); ui->data = data; - data->bridge = bridge; - data->loop = &tui_loop; + data->ui = ui; data->is_starting = true; data->screenshot = NULL; + data->stopped = false; + data->loop = &main_loop; kv_init(data->invalid_regions); signal_watcher_init(data->loop, &data->winch_handle, ui); signal_watcher_init(data->loop, &data->cont_handle, data); @@ -510,43 +505,54 @@ static void tui_main(UIBridgeData *bridge, UI *ui) kv_push(data->attrs, HLATTRS_INIT); data->input.tk_ti_hook_fn = tui_tk_ti_getstr; - tinput_init(&data->input, &tui_loop); + tinput_init(&data->input, &main_loop); tui_terminal_start(ui); + // TODO: borked! + // loop_schedule(&main_loop, event_create(show_termcap_event, 1, data->ut)); - // Allow main thread to continue, we are ready to handle UI callbacks. - CONTINUE(bridge); +} - // "Active" loop: first ~100 ms of startup. - for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) { - ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); - } - if (!tui_is_stopped(ui)) { - tui_terminal_after_startup(ui); - } - // "Passive" (I/O-driven) loop: TUI thread "main loop". +void tui_execute(void) + FUNC_ATTR_NORETURN +{ + UI *ui = ui_get_by_index(1); + LOOP_PROCESS_EVENTS(&main_loop, main_loop.events, -1); + tui_io_driven_loop(ui); + tui_exit_safe(ui); + getout(0); +} + +// Doesn't return until the TUI is closed (by call of tui_stop()) +static void tui_io_driven_loop(UI *ui){ + // "Passive" (I/O-driven) loop: TUI process's "main loop". while (!tui_is_stopped(ui)) { - loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed + loop_poll_events(&main_loop, -1); } +} - ui_bridge_stopped(bridge); - tinput_destroy(&data->input); - signal_watcher_stop(&data->cont_handle); - signal_watcher_close(&data->cont_handle, NULL); - signal_watcher_close(&data->winch_handle, NULL); - loop_close(&tui_loop, false); +// TODO: call me when EXITFREE +#if 0 +static void tui_data_destroy(void **argv) { + UI *ui = argv[0]; + TUIData *data = ui->data; kv_destroy(data->invalid_regions); kv_destroy(data->attrs); xfree(data->space_buf); xfree(data->term); xfree(data); + xfree(ui); } +#endif -/// Handoff point between the main (ui_bridge) thread and the TUI thread. -static void tui_scheduler(Event event, void *d) -{ - UI *ui = d; +void tui_exit_safe(UI *ui) { TUIData *data = ui->data; - loop_schedule_fast(data->loop, event); // `tui_loop` local to tui_main(). + if (!tui_is_stopped(ui)) { + tui_stop(ui); + } + tinput_destroy(&data->input); + signal_watcher_stop(&data->cont_handle); + signal_watcher_close(&data->cont_handle, NULL); + signal_watcher_close(&data->winch_handle, NULL); } #ifdef UNIX @@ -1324,6 +1330,9 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751 static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info) { TUIData *data = ui->data; + attrs.cterm_ae_attr = cterm_attrs.cterm_ae_attr; + attrs.cterm_fg_color = cterm_attrs.cterm_fg_color; + attrs.cterm_bg_color = cterm_attrs.cterm_bg_color; kv_a(data->attrs, (size_t)id) = attrs; } @@ -1399,6 +1408,7 @@ static void tui_flush(UI *ui) flush_buf(ui); } +#if 0 /// Dumps termcap info to the messages area, if 'verbose' >= 3. static void show_verbose_terminfo(TUIData *data) { @@ -1448,6 +1458,7 @@ static void verbose_terminfo_event(void **argv) } api_clear_error(&err); } +#endif #ifdef UNIX static void suspend_event(void **argv) @@ -1459,20 +1470,19 @@ static void suspend_event(void **argv) data->cont_received = false; stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) signal_stop(); - kill(0, SIGTSTP); + kill(0, SIGTSTP); // make TUI process run in background signal_start(); while (!data->cont_received) { // poll the event loop until SIGCONT is received loop_poll_events(data->loop, -1); } + tui_terminal_start(ui); tui_terminal_after_startup(ui); if (enable_mouse) { tui_mouse_on(ui); } stream_set_blocking(input_global_fd(), false); // libuv expects this - // resume the main thread - CONTINUE(data->bridge); } #endif @@ -1547,6 +1557,13 @@ static void tui_option_set(UI *ui, String name, Object value) ui->rgb = value.data.boolean; data->print_attr_id = -1; invalidate(ui, 0, data->grid.height, 0, data->grid.width); + + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstr_as_string(xstrdup("rgb")))); + ADD(args, BOOLEAN_OBJ(value.data.boolean)); + rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); + } } else if (strequal(name.data, "ttimeout")) { data->input.ttimeout = value.data.boolean; } else if (strequal(name.data, "ttimeoutlen")) { @@ -1595,6 +1612,10 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I // printed immediately without an intervening newline. final_column_wrap(ui); } + + // TODO: wat + //xfree((void *) chunk); + //xfree((void *) attrs); } static void invalidate(UI *ui, int top, int bot, int left, int right) @@ -1659,8 +1680,8 @@ static void tui_guess_size(UI *ui) height = DFLT_ROWS; } - data->bridge->bridge.width = ui->width = width; - data->bridge->bridge.height = ui->height = height; + ui->width = width; + ui->height = height; } static void unibi_goto(UI *ui, int row, int col) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 7bde8d2f5a..232bfc8b3c 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -35,6 +35,7 @@ #include "nvim/ui_compositor.h" #include "nvim/vim.h" #include "nvim/window.h" +#include "nvim/msgpack_rpc/channel.h" #ifdef FEAT_TUI # include "nvim/tui/tui.h" #else @@ -150,6 +151,19 @@ void ui_builtin_start(void) #endif } +uint64_t ui_client_start(int argc, char **argv, bool pass_stdin) +{ + ui_comp_detach(uis[1]); // Bypassing compositor in client + uint64_t rv = ui_client_start_server(argc, argv, pass_stdin); + return rv; +} + +UI* ui_get_by_index(int idx) +{ + assert(idx < 16); + return uis[idx]; +} + bool ui_rgb_attached(void) { if (!headless_mode && p_tgc) { @@ -228,6 +242,7 @@ void ui_refresh(void) screen_resize(width, height); p_lz = save_p_lz; } else { + // TODO: not like this Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ((int)width)); ADD(args, INTEGER_OBJ((int)height)); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c deleted file mode 100644 index 25c230a941..0000000000 --- a/src/nvim/ui_bridge.c +++ /dev/null @@ -1,223 +0,0 @@ -// 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)); -} diff --git a/src/nvim/ui_bridge.h b/src/nvim/ui_bridge.h deleted file mode 100644 index 094367126a..0000000000 --- a/src/nvim/ui_bridge.h +++ /dev/null @@ -1,47 +0,0 @@ -// Bridge for communication between a UI thread and nvim core. -// Used by the built-in TUI and libnvim-based UIs. -#ifndef NVIM_UI_BRIDGE_H -#define NVIM_UI_BRIDGE_H - -#include <stdbool.h> -#include <uv.h> - -#include "nvim/event/defs.h" -#include "nvim/ui.h" - -struct ui_bridge_data; - -typedef struct ui_bridge_data UIBridgeData; -typedef void (*ui_main_fn)(UIBridgeData *bridge, UI *ui); -struct ui_bridge_data { - UI bridge; // actual UI passed to ui_attach - UI *ui; // UI pointer that will have its callback called in - // another thread - event_scheduler scheduler; - uv_thread_t ui_thread; - ui_main_fn ui_main; - uv_mutex_t mutex; - uv_cond_t cond; - // When the UI thread is called, the main thread will suspend until - // the call returns. This flag is used as a condition for the main - // thread to continue. - bool ready; - // When a stop request is sent from the main thread, it must wait until the UI - // thread finishes handling all events. This flag is set by the UI thread as a - // signal that it will no longer send messages to the main thread. - bool stopped; -}; - -#define CONTINUE(b) \ - do { \ - UIBridgeData *d = (UIBridgeData *)b; \ - uv_mutex_lock(&d->mutex); \ - d->ready = true; \ - uv_cond_signal(&d->cond); \ - uv_mutex_unlock(&d->mutex); \ - } while (0) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "ui_bridge.h.generated.h" -#endif -#endif // NVIM_UI_BRIDGE_H diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 27c63433a7..a56513f42f 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -9,6 +9,7 @@ #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/globals.h" +#include "nvim/eval.h" #include "nvim/highlight.h" #include "nvim/log.h" #include "nvim/main.h" @@ -24,6 +25,31 @@ #endif // uncrustify:on +uint64_t ui_client_start_server(int argc, char **argv, bool pass_stdin) +{ + varnumber_T exit_status; + char **args = xmalloc(((size_t)(2 + argc)) * sizeof(char*)); + int args_idx = 0; + args[args_idx++] = xstrdup((const char*)get_vim_var_str(VV_PROGPATH)); + args[args_idx++] = xstrdup("--embed"); + for (int i = 1; i < argc; i++) { + args[args_idx++] = xstrdup(argv[i]); + } + args[args_idx++] = NULL; // last value of argv should be NULL + + Channel *channel = channel_job_start(args, CALLBACK_READER_INIT, + CALLBACK_READER_INIT, CALLBACK_NONE, + false, true, true, false, kChannelStdinPipe, + NULL, 0, 0, NULL, &exit_status); + if (pass_stdin && !stdin_isatty) { + close(0); + dup(2); + } + + ui_client_init(channel->id); + return channel->id;; +} + void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; @@ -35,6 +61,13 @@ void ui_client_init(uint64_t chan) PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true)); + // TODO: PUT(opts, "term_name", STRING_OBJ(cstr_as_string(termname_local))); + PUT(opts, "term_colors", INTEGER_OBJ(t_colors)); + if (!is_remote_client) { + PUT(opts, "term_ttyin", INTEGER_OBJ(stdin_isatty)); + PUT(opts, "term_ttyout", INTEGER_OBJ(stdout_isatty)); + } + ADD(args, INTEGER_OBJ((int)width)); ADD(args, INTEGER_OBJ((int)height)); ADD(args, DICTIONARY_OBJ(opts)); |