mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +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_timer_init(&loop->uv, &loop->children_kill_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++) { | ||||
|     abort();  // Should not re-enter uv_run | ||||
|   } | ||||
|  | ||||
|   uv_run_mode mode = UV_RUN_ONCE; | ||||
|   bool timeout_expired = false; | ||||
|  | ||||
|   if (ms > 0) { | ||||
|     // Use a repeating timeout of ms milliseconds to make sure | ||||
|     // we do not block indefinitely for I/O. | ||||
|     *((bool *)loop->poll_timer.data) = false;  // reset "timeout expired" flag | ||||
|     // 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); | ||||
|   } else if (ms == 0) { | ||||
|     // 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); | ||||
|  | ||||
|   if (ms > 0) { | ||||
|     timeout_expired = *((bool *)loop->poll_timer.data); | ||||
|     uv_timer_stop(&loop->poll_timer); | ||||
|   } | ||||
|  | ||||
|   loop->recursive--;  // Can re-enter uv_run now | ||||
|   multiqueue_process_events(loop->fast_events); | ||||
|   return timeout_expired; | ||||
| } | ||||
|  | ||||
| /// Schedules an event from another thread. | ||||
| @@ -111,7 +116,7 @@ bool loop_close(Loop *loop, bool wait) | ||||
|   uv_mutex_destroy(&loop->mutex); | ||||
|   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->poll_timer, NULL); | ||||
|   uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb); | ||||
|   uv_close((uv_handle_t *)&loop->async, NULL); | ||||
|   uint64_t start = wait ? os_hrtime() : 0; | ||||
|   while (true) { | ||||
| @@ -163,5 +168,11 @@ static void async_cb(uv_async_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; | ||||
|   Loop *loop; | ||||
|   bool stop; | ||||
|   uv_timer_t after_startup_timer; | ||||
|   unibi_var_t params[9]; | ||||
|   char buf[OUTBUF_SIZE]; | ||||
|   size_t bufpos; | ||||
| @@ -291,18 +290,6 @@ static void terminfo_stop(UI *ui) | ||||
|   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) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
| @@ -312,8 +299,16 @@ static void tui_terminal_start(UI *ui) | ||||
|   update_size(ui); | ||||
|   signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH); | ||||
|   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) | ||||
| @@ -347,8 +342,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui) | ||||
| #ifdef UNIX | ||||
|   signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT); | ||||
| #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 | ||||
|   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, | ||||
|                          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) { | ||||
|     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); | ||||
|   term_input_destroy(&data->input); | ||||
|   signal_watcher_stop(&data->cont_handle); | ||||
| @@ -379,6 +382,10 @@ static void tui_main(UIBridgeData *bridge, UI *ui) | ||||
|   xfree(ui); | ||||
| } | ||||
|  | ||||
| static void tui_dummy_event(void **argv) | ||||
| { | ||||
| } | ||||
|  | ||||
| static void tui_scheduler(Event event, void *d) | ||||
| { | ||||
|   UI *ui = d; | ||||
| @@ -1100,6 +1107,7 @@ static void suspend_event(void **argv) | ||||
|     loop_poll_events(data->loop, -1); | ||||
|   } | ||||
|   tui_terminal_start(ui); | ||||
|   tui_terminal_after_startup(ui); | ||||
|   if (enable_mouse) { | ||||
|     tui_mouse_on(ui); | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes