diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-01-27 16:55:47 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-01-27 16:55:47 +0100 |
commit | e40b9d47bf8f8f716d3ef5a95c8ecbbdc0a501f9 (patch) | |
tree | 7891a85cc4c4d8f9c173309dfdf7e870777a8046 /src/ui.c | |
parent | d93090f41f70c521cfad5b25efcb0024b9480082 (diff) | |
download | vim-git-e40b9d47bf8f8f716d3ef5a95c8ecbbdc0a501f9.tar.gz |
patch 8.1.0834: GUI may wait too long before dealing with messagesv8.1.0834
Problem: GUI may wait too long before dealing with messages. Returning
early may cause a mapping to time out.
Solution: Use the waiting loop from Unix also for the GUI.
(closes #3817, closes #3824)
Diffstat (limited to 'src/ui.c')
-rw-r--r-- | src/ui.c | 212 |
1 files changed, 212 insertions, 0 deletions
@@ -178,6 +178,48 @@ ui_inchar( ctrl_c_interrupts = FALSE; } + /* + * Here we call gui_inchar() or mch_inchar(), the GUI or machine-dependent + * input function. The functionality they implement is like this: + * + * while (not timed out) + * { + * handle-resize; + * parse-queued-messages; + * if (waited for 'updatetime') + * trigger-cursorhold; + * ui_wait_for_chars_or_timer() + * if (character available) + * break; + * } + * + * ui_wait_for_chars_or_timer() does: + * + * while (not timed out) + * { + * if (any-timer-triggered) + * invoke-timer-callback; + * wait-for-character(); + * if (character available) + * break; + * } + * + * wait-for-character() does: + * while (not timed out) + * { + * Wait for event; + * if (something on channel) + * read/write channel; + * else if (resized) + * handle_resize(); + * else if (system event) + * deal-with-system-event; + * else if (character available) + * break; + * } + * + */ + #ifdef FEAT_GUI if (gui.in_use) retval = gui_inchar(buf, maxlen, wtime, tb_change_cnt); @@ -205,6 +247,176 @@ theend: return retval; } +#if defined(UNIX) || defined(FEAT_GUI) || defined(PROTO) +/* + * Common code for mch_inchar() and gui_inchar(): Wait for a while or + * indefinitely until characters are available, dealing with timers and + * messages on channels. + * + * "buf" may be NULL if the available characters are not to be returned, only + * check if they are available. + * + * Return the number of characters that are available. + * If "wtime" == 0 do not wait for characters. + * If "wtime" == n wait a short time for characters. + * If "wtime" == -1 wait forever for characters. + */ + int +inchar_loop( + char_u *buf, + int maxlen, + long wtime, // don't use "time", MIPS cannot handle it + int tb_change_cnt, + int (*wait_func)(long wtime, int *interrupted, int ignore_input), + int (*resize_func)(int check_only)) +{ + int len; + int interrupted = FALSE; + int did_start_blocking = FALSE; + long wait_time; + long elapsed_time = 0; +#ifdef ELAPSED_FUNC + elapsed_T start_tv; + + ELAPSED_INIT(start_tv); +#endif + + /* repeat until we got a character or waited long enough */ + for (;;) + { + /* Check if window changed size while we were busy, perhaps the ":set + * columns=99" command was used. */ + if (resize_func != NULL) + resize_func(FALSE); + +#ifdef MESSAGE_QUEUE + // Only process messages when waiting. + if (wtime != 0) + { + parse_queued_messages(); + // If input was put directly in typeahead buffer bail out here. + if (typebuf_changed(tb_change_cnt)) + return 0; + } +#endif + if (wtime < 0 && did_start_blocking) + // blocking and already waited for p_ut + wait_time = -1; + else + { + if (wtime >= 0) + wait_time = wtime; + else + // going to block after p_ut + wait_time = p_ut; +#ifdef ELAPSED_FUNC + elapsed_time = ELAPSED_FUNC(start_tv); +#endif + wait_time -= elapsed_time; + if (wait_time <= 0) + { + if (wtime >= 0) + // no character available within "wtime" + return 0; + + // No character available within 'updatetime'. + did_start_blocking = TRUE; + if (trigger_cursorhold() && maxlen >= 3 + && !typebuf_changed(tb_change_cnt)) + { + // Put K_CURSORHOLD in the input buffer or return it. + if (buf == NULL) + { + char_u ibuf[3]; + + ibuf[0] = CSI; + ibuf[1] = KS_EXTRA; + ibuf[2] = (int)KE_CURSORHOLD; + add_to_input_buf(ibuf, 3); + } + else + { + buf[0] = K_SPECIAL; + buf[1] = KS_EXTRA; + buf[2] = (int)KE_CURSORHOLD; + } + return 3; + } + + // There is no character available within 'updatetime' seconds: + // flush all the swap files to disk. Also done when + // interrupted by SIGWINCH. + before_blocking(); + continue; + } + } + +#ifdef FEAT_JOB_CHANNEL + if (wait_time < 0 || wait_time > 100L) + { + // Checking if a job ended requires polling. Do this at least + // every 100 msec. + if (has_pending_job()) + wait_time = 100L; + + // If there is readahead then parse_queued_messages() timed out and + // we should call it again soon. + if (channel_any_readahead()) + wait_time = 10L; + } +#endif +#ifdef FEAT_BEVAL_GUI + if (p_beval && wait_time > 100L) + // The 'balloonexpr' may indirectly invoke a callback while waiting + // for a character, need to check often. + wait_time = 100L; +#endif + + // Wait for a character to be typed or another event, such as the winch + // signal or an event on the monitored file descriptors. + if (wait_func(wait_time, &interrupted, FALSE)) + { + // If input was put directly in typeahead buffer bail out here. + if (typebuf_changed(tb_change_cnt)) + return 0; + + // We might have something to return now. + if (buf == NULL) + // "buf" is NULL, we were just waiting, not actually getting + // input. + return input_available(); + + len = read_from_input_buf(buf, (long)maxlen); + if (len > 0) + return len; + continue; + } + // Timed out or interrupted with no character available. + +#ifndef ELAPSED_FUNC + // estimate the elapsed time + elapsed_time += wait_time; +#endif + + if ((resize_func != NULL && resize_func(TRUE)) +#ifdef FEAT_CLIENTSERVER + || server_waiting() +#endif +#ifdef MESSAGE_QUEUE + || interrupted +#endif + || wait_time > 0 + || (wtime < 0 && !did_start_blocking)) + // no character available, but something to be done, keep going + continue; + + // no character available or interrupted, return zero + break; + } + return 0; +} +#endif + #if defined(FEAT_TIMERS) || defined(PROTO) /* * Wait for a timer to fire or "wait_func" to return non-zero. |