mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	tui: rework deferred-termcodes ... again
- Revert timer-based approach.
- Instead, call loop_poll_events() with a timeout in an "active" loop,
  to infer that "TUI startup activity has mostly finished", but also to
  enforce a mininum time (100 ms) before emitting "enable focus
  reporting" termcode. (If TUI startup takes longer than that minimum
  time, it's probably a slow environment anyways.)
- Tickle `main_loop` by sending a dummy event.  Without this, the
  initial "focus-gained" response from the terminal may not get
  processed until the user hits a key.
ref #7720
ref #7664
ref #7649
ref #7664
ref 27f9b1c7b0
			
			
This commit is contained in:
		@@ -30,19 +30,22 @@ void loop_init(Loop *loop, void *data)
 | 
				
			|||||||
  uv_signal_init(&loop->uv, &loop->children_watcher);
 | 
					  uv_signal_init(&loop->uv, &loop->children_watcher);
 | 
				
			||||||
  uv_timer_init(&loop->uv, &loop->children_kill_timer);
 | 
					  uv_timer_init(&loop->uv, &loop->children_kill_timer);
 | 
				
			||||||
  uv_timer_init(&loop->uv, &loop->poll_timer);
 | 
					  uv_timer_init(&loop->uv, &loop->poll_timer);
 | 
				
			||||||
 | 
					  loop->poll_timer.data = xmalloc(sizeof(bool));  // "timeout expired" flag
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void loop_poll_events(Loop *loop, int ms)
 | 
					/// @returns true if `ms` timeout was reached
 | 
				
			||||||
 | 
					bool loop_poll_events(Loop *loop, int ms)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (loop->recursive++) {
 | 
					  if (loop->recursive++) {
 | 
				
			||||||
    abort();  // Should not re-enter uv_run
 | 
					    abort();  // Should not re-enter uv_run
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uv_run_mode mode = UV_RUN_ONCE;
 | 
					  uv_run_mode mode = UV_RUN_ONCE;
 | 
				
			||||||
 | 
					  bool timeout_expired = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (ms > 0) {
 | 
					  if (ms > 0) {
 | 
				
			||||||
    // Use a repeating timeout of ms milliseconds to make sure
 | 
					    *((bool *)loop->poll_timer.data) = false;  // reset "timeout expired" flag
 | 
				
			||||||
    // we do not block indefinitely for I/O.
 | 
					    // Dummy timer to ensure UV_RUN_ONCE does not block indefinitely for I/O.
 | 
				
			||||||
    uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
 | 
					    uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
 | 
				
			||||||
  } else if (ms == 0) {
 | 
					  } else if (ms == 0) {
 | 
				
			||||||
    // For ms == 0, do a non-blocking event poll.
 | 
					    // For ms == 0, do a non-blocking event poll.
 | 
				
			||||||
@@ -52,11 +55,13 @@ void loop_poll_events(Loop *loop, int ms)
 | 
				
			|||||||
  uv_run(&loop->uv, mode);
 | 
					  uv_run(&loop->uv, mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (ms > 0) {
 | 
					  if (ms > 0) {
 | 
				
			||||||
 | 
					    timeout_expired = *((bool *)loop->poll_timer.data);
 | 
				
			||||||
    uv_timer_stop(&loop->poll_timer);
 | 
					    uv_timer_stop(&loop->poll_timer);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loop->recursive--;  // Can re-enter uv_run now
 | 
					  loop->recursive--;  // Can re-enter uv_run now
 | 
				
			||||||
  multiqueue_process_events(loop->fast_events);
 | 
					  multiqueue_process_events(loop->fast_events);
 | 
				
			||||||
 | 
					  return timeout_expired;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Schedules an event from another thread.
 | 
					/// Schedules an event from another thread.
 | 
				
			||||||
@@ -111,7 +116,7 @@ bool loop_close(Loop *loop, bool wait)
 | 
				
			|||||||
  uv_mutex_destroy(&loop->mutex);
 | 
					  uv_mutex_destroy(&loop->mutex);
 | 
				
			||||||
  uv_close((uv_handle_t *)&loop->children_watcher, NULL);
 | 
					  uv_close((uv_handle_t *)&loop->children_watcher, NULL);
 | 
				
			||||||
  uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
 | 
					  uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
 | 
				
			||||||
  uv_close((uv_handle_t *)&loop->poll_timer, NULL);
 | 
					  uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb);
 | 
				
			||||||
  uv_close((uv_handle_t *)&loop->async, NULL);
 | 
					  uv_close((uv_handle_t *)&loop->async, NULL);
 | 
				
			||||||
  uint64_t start = wait ? os_hrtime() : 0;
 | 
					  uint64_t start = wait ? os_hrtime() : 0;
 | 
				
			||||||
  while (true) {
 | 
					  while (true) {
 | 
				
			||||||
@@ -163,5 +168,11 @@ static void async_cb(uv_async_t *handle)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void timer_cb(uv_timer_t *handle)
 | 
					static void timer_cb(uv_timer_t *handle)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  bool *timeout_expired = handle->data;
 | 
				
			||||||
 | 
					  *timeout_expired = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void timer_close_cb(uv_handle_t *handle)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  xfree(handle->data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,7 +70,6 @@ typedef struct {
 | 
				
			|||||||
  UIBridgeData *bridge;
 | 
					  UIBridgeData *bridge;
 | 
				
			||||||
  Loop *loop;
 | 
					  Loop *loop;
 | 
				
			||||||
  bool stop;
 | 
					  bool stop;
 | 
				
			||||||
  uv_timer_t after_startup_timer;
 | 
					 | 
				
			||||||
  unibi_var_t params[9];
 | 
					  unibi_var_t params[9];
 | 
				
			||||||
  char buf[OUTBUF_SIZE];
 | 
					  char buf[OUTBUF_SIZE];
 | 
				
			||||||
  size_t bufpos;
 | 
					  size_t bufpos;
 | 
				
			||||||
@@ -291,18 +290,6 @@ static void terminfo_stop(UI *ui)
 | 
				
			|||||||
  unibi_destroy(data->ut);
 | 
					  unibi_destroy(data->ut);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void after_startup_timer_cb(uv_timer_t *handle)
 | 
					 | 
				
			||||||
  FUNC_ATTR_NONNULL_ALL
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  UI *ui = handle->data;
 | 
					 | 
				
			||||||
  TUIData *data = ui->data;
 | 
					 | 
				
			||||||
  uv_timer_stop(&data->after_startup_timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Emit this after Nvim startup, not during.  This works around a tmux
 | 
					 | 
				
			||||||
  // 2.3 bug(?) which caused slow drawing during startup.  #7649
 | 
					 | 
				
			||||||
  unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void tui_terminal_start(UI *ui)
 | 
					static void tui_terminal_start(UI *ui)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  TUIData *data = ui->data;
 | 
					  TUIData *data = ui->data;
 | 
				
			||||||
@@ -312,8 +299,16 @@ static void tui_terminal_start(UI *ui)
 | 
				
			|||||||
  update_size(ui);
 | 
					  update_size(ui);
 | 
				
			||||||
  signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
 | 
					  signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
 | 
				
			||||||
  term_input_start(&data->input);
 | 
					  term_input_start(&data->input);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uv_timer_start(&data->after_startup_timer, after_startup_timer_cb, 500, 0);
 | 
					static void tui_terminal_after_startup(UI *ui)
 | 
				
			||||||
 | 
					  FUNC_ATTR_NONNULL_ALL
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  TUIData *data = ui->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Emit this after Nvim startup, not during.  This works around a tmux
 | 
				
			||||||
 | 
					  // 2.3 bug(?) which caused slow drawing during startup.  #7649
 | 
				
			||||||
 | 
					  unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tui_terminal_stop(UI *ui)
 | 
					static void tui_terminal_stop(UI *ui)
 | 
				
			||||||
@@ -347,8 +342,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
 | 
				
			|||||||
#ifdef UNIX
 | 
					#ifdef UNIX
 | 
				
			||||||
  signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
 | 
					  signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  uv_timer_init(&data->loop->uv, &data->after_startup_timer);
 | 
					 | 
				
			||||||
  data->after_startup_timer.data = ui;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
 | 
					#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
 | 
				
			||||||
  data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
 | 
					  data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
 | 
				
			||||||
@@ -363,11 +356,21 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
 | 
				
			|||||||
  loop_schedule_deferred(&main_loop,
 | 
					  loop_schedule_deferred(&main_loop,
 | 
				
			||||||
                         event_create(show_termcap_event, 1, data->ut));
 | 
					                         event_create(show_termcap_event, 1, data->ut));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // "Active" loop: first ~100 ms of startup.
 | 
				
			||||||
 | 
					  for (size_t ms = 0; ms < 100 && !data->stop;) {
 | 
				
			||||||
 | 
					    ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!data->stop) {
 | 
				
			||||||
 | 
					    tui_terminal_after_startup(ui);
 | 
				
			||||||
 | 
					    // Tickle `main_loop` with a dummy event, else the initial "focus-gained"
 | 
				
			||||||
 | 
					    // terminal response may not get processed until user hits a key.
 | 
				
			||||||
 | 
					    loop_schedule_deferred(&main_loop, event_create(tui_dummy_event, 0));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // "Passive" (I/O-driven) loop: TUI thread "main loop".
 | 
				
			||||||
  while (!data->stop) {
 | 
					  while (!data->stop) {
 | 
				
			||||||
    loop_poll_events(&tui_loop, -1);  // tui_loop.events is never processed
 | 
					    loop_poll_events(&tui_loop, -1);  // tui_loop.events is never processed
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uv_close((uv_handle_t *)&data->after_startup_timer, NULL);
 | 
					 | 
				
			||||||
  ui_bridge_stopped(bridge);
 | 
					  ui_bridge_stopped(bridge);
 | 
				
			||||||
  term_input_destroy(&data->input);
 | 
					  term_input_destroy(&data->input);
 | 
				
			||||||
  signal_watcher_stop(&data->cont_handle);
 | 
					  signal_watcher_stop(&data->cont_handle);
 | 
				
			||||||
@@ -379,6 +382,10 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
 | 
				
			|||||||
  xfree(ui);
 | 
					  xfree(ui);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tui_dummy_event(void **argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tui_scheduler(Event event, void *d)
 | 
					static void tui_scheduler(Event event, void *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  UI *ui = d;
 | 
					  UI *ui = d;
 | 
				
			||||||
@@ -1100,6 +1107,7 @@ static void suspend_event(void **argv)
 | 
				
			|||||||
    loop_poll_events(data->loop, -1);
 | 
					    loop_poll_events(data->loop, -1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  tui_terminal_start(ui);
 | 
					  tui_terminal_start(ui);
 | 
				
			||||||
 | 
					  tui_terminal_after_startup(ui);
 | 
				
			||||||
  if (enable_mouse) {
 | 
					  if (enable_mouse) {
 | 
				
			||||||
    tui_mouse_on(ui);
 | 
					    tui_mouse_on(ui);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user