feat(ui): detach UI via [count]ctrl-z

how to get server-side channel-id from client:

    diff --git a/src/nvim/main.c b/src/nvim/main.c
    index f699d4484fe2..421277a396f1 100644
    --- a/src/nvim/main.c
    +++ b/src/nvim/main.c
    @@ -656,7 +656,6 @@ int main(int argc, char **argv)

       TIME_MSG("before starting main loop");
       ILOG("starting main loop");
    -  ELOG("xxx %" PRId64, ui_client_channel_id);

       // Main loop: never returns.
       normal_enter(false, false);
    @@ -917,26 +916,6 @@ static uint64_t server_connect(char *server_addr, const char **errmsg)
         *errmsg = error;
         return 0;
       }
    -
    -  Error err = ERROR_INIT;
    -  Arena arena = ARENA_EMPTY;
    -
    -  Array args = arena_array(&arena, (size_t)1);
    -  ADD(args, INTEGER_OBJ(0));
    -  ArenaMem res_mem = NULL;
    -  Object result = rpc_send_call(chan, "nvim_get_chan_info", args, &res_mem, &err);
    -  if (!ERROR_SET(&err)) {
    -    arena_mem_free(res_mem);
    -    ui_server_chan = result.data.dictionary.items[0].value.data.integer;
    -    assert(ui_server_chan > 0);
    -  }
    -  arena_mem_free(arena_finish(&arena));
    -
    -  if (ERROR_SET(&err)) {
    -    ELOG("nvim_get_chan_info failed: %s", err.msg);
    -    api_clear_error(&err);
    -  }
    -
       return chan;
     }

    @@ -966,7 +945,6 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
         }

         ui_client_channel_id = chan;
    -    ELOG("xxx main:remote_request ui_channel=%" PRId64, ui_client_channel_id);
         return;
       }

    diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
    index b59972022613..928bd4c0a525 100644
    --- a/src/nvim/ui_client.h
    +++ b/src/nvim/ui_client.h
    @@ -16,8 +16,6 @@ EXTERN sattr_T *grid_line_buf_attr INIT( = NULL);

     // Client-side UI channel. Zero during early startup or if not a (--remote-ui) UI client.
     EXTERN uint64_t ui_client_channel_id INIT( = 0);
    -// UI client server-side channel id.
    -EXTERN int64_t ui_server_chan INIT( = 0);

     // exit status from embedded nvim process
     EXTERN int ui_client_exit_status INIT( = 0);
This commit is contained in:
Justin M. Keyes 2024-09-09 17:31:19 +02:00
parent 7ed9fd0573
commit b6b9b71c43
7 changed files with 78 additions and 9 deletions

View File

@ -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.

View File

@ -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);
}

View File

@ -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);
}

View File

@ -14,13 +14,16 @@
#include <string.h>
#include <time.h>
#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,

View File

@ -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;
}

View File

@ -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"

View File

@ -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)
{