diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 037a4d75fd..a989b7e0be 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -359,11 +359,11 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than /// requested if the buffer becomes full). -Integer nvim_input(String keys) +Integer nvim_input(uint64_t channel_id, String keys) FUNC_API_SINCE(1) FUNC_API_FAST { may_trigger_vim_suspend_resume(false); - return (Integer)input_enqueue(keys); + return (Integer)input_enqueue(channel_id, keys); } /// Send mouse event from GUI. diff --git a/src/nvim/event/proc.c b/src/nvim/event/proc.c index 808bf794f0..89bd34e46b 100644 --- a/src/nvim/event/proc.c +++ b/src/nvim/event/proc.c @@ -416,13 +416,19 @@ static void exit_event(void **argv) os_exit(status); } else { assert(status == 0); // Called from rpc_close(), which passes 0 as status. - preserve_exit(NULL); + // TODO(justinmk): + // - make this optional ("nvim --nohup")? + // - dynamic: can be set on the channel at any time + // - TUI sets this on SIGUP + // preserve_exit(NULL); } } } +/// Performs self-exit when the RPC channel is closed. void exit_from_channel(int status) { + DLOG("self-exit triggered by closed RPC channel..."); multiqueue_put(main_loop.fast_events, exit_event, (void *)(intptr_t)status); } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 8079b32ede..87f46518fb 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -220,8 +220,7 @@ static size_t receive_msgpack(RStream *stream, const char *rbuf, size_t c, void if (eof) { channel_close(channel->id, kChannelPartRpc, NULL); char buf[256]; - snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client", - channel->id); + snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the peer", channel->id); chan_close_with_error(channel, buf, LOGLVL_INF); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b9ce891b49..2b10299031 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -14,13 +14,16 @@ #include #include +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/ui.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" +#include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" @@ -58,11 +61,13 @@ #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/os/input.h" +#include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/plines.h" #include "nvim/profile.h" @@ -5121,14 +5126,55 @@ static void nv_window(cmdarg_T *cap) } } -/// CTRL-Z: Suspend +/// CTRL-Z: Suspend, or Detach UI static void nv_suspend(cmdarg_T *cap) { clearop(cap->oap); if (VIsual_active) { end_visual_mode(); // stop Visual mode } - do_cmdline_cmd("st"); + + if (input_chan_id == 0 || /*input_chan_id == 1 ||*/ is_internal_call(input_chan_id)) { + if (input_chan_id == 1) { + ILOG("xxx ui_channel=%" PRId64, input_chan_id); + } + // TODO(justinmk): show a confirm dialog if multiple UIs are attached? + if (ui_active() > 1) { + emsg("UIs are connected, no sussy baka"); + } else { + do_cmdline_cmd("suspend"); + } + return; + } + + // come on pooky let's burn this burn this mf down + ILOG("xxx DETACHED ui_channel=%" PRId64, input_chan_id); + + // MAXSIZE_TEMP_ARRAY(args2, 1); + // ADD_C(args2, INTEGER_OBJ((Integer)ui_client_channel_id)); + // MAXSIZE_TEMP_ARRAY(args, 2); + // ADD_C(args, CSTR_TO_OBJ("chanclose")); // CSTR_AS_OBJ + // ADD_C(args, ARRAY_OBJ(args2)); + // rpc_send_event(ui_client_channel_id, "nvim_call_function", args); + + // Only detaches UI, but we want to exit the client fully. + // rpc_send_event(input_chan_id, "nvim_ui_detach", (Array)ARRAY_DICT_INIT); + // { + // Error err; + // nvim_ui_detach(input_chan_id, &err); + // if (ERROR_SET(&err)) { + // emsg(err.msg); // UI may have disappeared already. + // } + // api_clear_error(&err); + // } + + { + const char *err; + bool rv = channel_close(input_chan_id, kChannelPartAll, &err); + if (!rv) { + emsg(err); // UI may have disappeared already. + } + } } /// "gv": Reselect the previous Visual area. If Visual already active, diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 7c5293a8b8..73e786b123 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -273,7 +273,7 @@ void input_enqueue_raw(const char *data, size_t size) input_write_pos += to_write; } -size_t input_enqueue(String keys) +size_t input_enqueue(uint64_t chan_id, String keys) { const char *ptr = keys.data; const char *end = ptr + keys.size; @@ -323,6 +323,8 @@ size_t input_enqueue(String keys) size_t rv = (size_t)(ptr - keys.data); process_ctrl_c(); + input_chan_id = chan_id; + return rv; } diff --git a/src/nvim/os/input.h b/src/nvim/os/input.h index abef46072b..66f68ce41e 100644 --- a/src/nvim/os/input.h +++ b/src/nvim/os/input.h @@ -8,6 +8,9 @@ #include "nvim/macros_defs.h" EXTERN bool used_stdin INIT( = false); +/// Last channel that invoked 'nvim_input`. +/// TODO(justinmk): race condition if multiple UIs/scripts send input? +EXTERN uint64_t input_chan_id INIT( = 0); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/input.h.generated.h" diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 6bfcadebf2..3f4f7b7eec 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -61,12 +61,17 @@ uint64_t ui_client_start_server(int argc, char **argv) Channel *channel = channel_job_start(args, get_vim_var_str(VV_PROGPATH), CALLBACK_READER_INIT, on_err, CALLBACK_NONE, - false, true, true, false, kChannelStdinPipe, + false, true, true, true, kChannelStdinPipe, NULL, 0, 0, NULL, &exit_status); if (!channel) { return 0; } + // channel->stream.proc.out.s.close_cb = on_stdio_close; + // channel->stream.proc.in.close_cb = on_stdio_close; + channel->stream.proc.in.internal_close_cb = on_stdio_close; + channel->stream.proc.in.internal_data = channel; + // If stdin is not a pty, it is forwarded to the client. // Replace stdin in the TUI process with the tty fd. if (ui_client_forward_stdin) { @@ -81,6 +86,14 @@ uint64_t ui_client_start_server(int argc, char **argv) return channel->id; } +static void on_stdio_close(Stream *stream, void *data) + FUNC_ATTR_NORETURN +{ + // Channel *chan = data; + ELOG("xxxxxxxxxxxxxxxxxxxxxxxxxxx"); + os_exit(0); +} + /// Attaches this client to the UI channel, and sets its client info. void ui_client_attach(int width, int height, char *term, bool rgb) {