mirror of
https://github.com/vim/vim.git
synced 2025-08-31 20:53:42 -04:00
patch 8.1.0834: GUI may wait too long before dealing with messages
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)
This commit is contained in:
parent
d93090f41f
commit
e40b9d47bf
143
src/gui.c
143
src/gui.c
@ -2896,10 +2896,14 @@ gui_wait_for_chars_3(
|
||||
* or FAIL otherwise.
|
||||
*/
|
||||
static int
|
||||
gui_wait_for_chars_or_timer(long wtime)
|
||||
gui_wait_for_chars_or_timer(
|
||||
long wtime,
|
||||
int *interrupted UNUSED,
|
||||
int ignore_input UNUSED)
|
||||
{
|
||||
#ifdef FEAT_TIMERS
|
||||
return ui_wait_for_chars_or_timer(wtime, gui_wait_for_chars_3, NULL, 0);
|
||||
return ui_wait_for_chars_or_timer(wtime, gui_wait_for_chars_3,
|
||||
interrupted, ignore_input);
|
||||
#else
|
||||
return gui_mch_wait_for_chars(wtime);
|
||||
#endif
|
||||
@ -2907,6 +2911,59 @@ gui_wait_for_chars_or_timer(long wtime)
|
||||
|
||||
/*
|
||||
* The main GUI input routine. Waits for a character from the keyboard.
|
||||
* "wtime" == -1 Wait forever.
|
||||
* "wtime" == 0 Don't wait.
|
||||
* "wtime" > 0 Wait wtime milliseconds for a character.
|
||||
*
|
||||
* Returns the number of characters read or zero when timed out or interrupted.
|
||||
* "buf" may be NULL, in which case a non-zero number is returned if characters
|
||||
* are available.
|
||||
*/
|
||||
static int
|
||||
gui_wait_for_chars_buf(
|
||||
char_u *buf,
|
||||
int maxlen,
|
||||
long wtime, // don't use "time", MIPS cannot handle it
|
||||
int tb_change_cnt)
|
||||
{
|
||||
int retval;
|
||||
|
||||
#ifdef FEAT_MENU
|
||||
// If we're going to wait a bit, update the menus and mouse shape for the
|
||||
// current State.
|
||||
if (wtime != 0)
|
||||
gui_update_menus(0);
|
||||
#endif
|
||||
|
||||
gui_mch_update();
|
||||
if (input_available()) // Got char, return immediately
|
||||
{
|
||||
if (buf != NULL && !typebuf_changed(tb_change_cnt))
|
||||
return read_from_input_buf(buf, (long)maxlen);
|
||||
return 0;
|
||||
}
|
||||
if (wtime == 0) // Don't wait for char
|
||||
return FAIL;
|
||||
|
||||
// Before waiting, flush any output to the screen.
|
||||
gui_mch_flush();
|
||||
|
||||
// Blink while waiting for a character.
|
||||
gui_mch_start_blink();
|
||||
|
||||
// Common function to loop until "wtime" is met, while handling timers and
|
||||
// other callbacks.
|
||||
retval = inchar_loop(buf, maxlen, wtime, tb_change_cnt,
|
||||
gui_wait_for_chars_or_timer, NULL);
|
||||
|
||||
gui_mch_stop_blink(TRUE);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for a character from the keyboard without actually reading it.
|
||||
* Also deals with timers.
|
||||
* wtime == -1 Wait forever.
|
||||
* wtime == 0 Don't wait.
|
||||
* wtime > 0 Wait wtime milliseconds for a character.
|
||||
@ -2916,82 +2973,7 @@ gui_wait_for_chars_or_timer(long wtime)
|
||||
int
|
||||
gui_wait_for_chars(long wtime, int tb_change_cnt)
|
||||
{
|
||||
int retval;
|
||||
#if defined(ELAPSED_FUNC)
|
||||
elapsed_T start_tv;
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_MENU
|
||||
/*
|
||||
* If we're going to wait a bit, update the menus and mouse shape for the
|
||||
* current State.
|
||||
*/
|
||||
if (wtime != 0)
|
||||
gui_update_menus(0);
|
||||
#endif
|
||||
|
||||
gui_mch_update();
|
||||
if (input_available()) /* Got char, return immediately */
|
||||
return OK;
|
||||
if (wtime == 0) /* Don't wait for char */
|
||||
return FAIL;
|
||||
|
||||
/* Before waiting, flush any output to the screen. */
|
||||
gui_mch_flush();
|
||||
|
||||
if (wtime > 0)
|
||||
{
|
||||
/* Blink when waiting for a character. Probably only does something
|
||||
* for showmatch() */
|
||||
gui_mch_start_blink();
|
||||
retval = gui_wait_for_chars_or_timer(wtime);
|
||||
gui_mch_stop_blink(TRUE);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if defined(ELAPSED_FUNC)
|
||||
ELAPSED_INIT(start_tv);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* While we are waiting indefinitely for a character, blink the cursor.
|
||||
*/
|
||||
gui_mch_start_blink();
|
||||
|
||||
retval = FAIL;
|
||||
/*
|
||||
* We may want to trigger the CursorHold event. First wait for
|
||||
* 'updatetime' and if nothing is typed within that time, and feedkeys()
|
||||
* wasn't used, put the K_CURSORHOLD key in the input buffer.
|
||||
*/
|
||||
if (gui_wait_for_chars_or_timer(p_ut) == OK)
|
||||
retval = OK;
|
||||
else if (trigger_cursorhold()
|
||||
#if defined(ELAPSED_FUNC)
|
||||
&& ELAPSED_FUNC(start_tv) >= p_ut
|
||||
#endif
|
||||
&& typebuf.tb_change_cnt == tb_change_cnt)
|
||||
{
|
||||
char_u buf[3];
|
||||
|
||||
/* Put K_CURSORHOLD in the input buffer. */
|
||||
buf[0] = CSI;
|
||||
buf[1] = KS_EXTRA;
|
||||
buf[2] = (int)KE_CURSORHOLD;
|
||||
add_to_input_buf(buf, 3);
|
||||
|
||||
retval = OK;
|
||||
}
|
||||
|
||||
if (retval == FAIL && typebuf.tb_change_cnt == tb_change_cnt)
|
||||
{
|
||||
/* Blocking wait. */
|
||||
before_blocking();
|
||||
retval = gui_wait_for_chars_or_timer(-1L);
|
||||
}
|
||||
|
||||
gui_mch_stop_blink(TRUE);
|
||||
return retval;
|
||||
return gui_wait_for_chars_buf(NULL, 0, wtime, tb_change_cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3004,10 +2986,7 @@ gui_inchar(
|
||||
long wtime, /* milli seconds */
|
||||
int tb_change_cnt)
|
||||
{
|
||||
if (gui_wait_for_chars(wtime, tb_change_cnt)
|
||||
&& !typebuf_changed(tb_change_cnt))
|
||||
return read_from_input_buf(buf, (long)maxlen);
|
||||
return 0;
|
||||
return gui_wait_for_chars_buf(buf, maxlen, wtime, tb_change_cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
|
149
src/os_unix.c
149
src/os_unix.c
@ -355,6 +355,21 @@ mch_write(char_u *s, int len)
|
||||
RealWaitForChar(read_cmd_fd, p_wd, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function passed to inchar_loop() to handle window resizing.
|
||||
* If "check_only" is TRUE: Return whether there was a resize.
|
||||
* If "check_only" is FALSE: Deal with the window resized.
|
||||
*/
|
||||
static int
|
||||
resize_func(int check_only)
|
||||
{
|
||||
if (check_only)
|
||||
return do_resize;
|
||||
while (do_resize)
|
||||
handle_resize();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* mch_inchar(): low level input function.
|
||||
* Get a characters from the keyboard.
|
||||
@ -370,138 +385,8 @@ mch_inchar(
|
||||
long wtime, /* don't use "time", MIPS cannot handle it */
|
||||
int tb_change_cnt)
|
||||
{
|
||||
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. */
|
||||
while (do_resize)
|
||||
handle_resize();
|
||||
|
||||
#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;
|
||||
|
||||
else
|
||||
{
|
||||
/* no character available within 'updatetime' */
|
||||
did_start_blocking = TRUE;
|
||||
if (trigger_cursorhold() && maxlen >= 3
|
||||
&& !typebuf_changed(tb_change_cnt))
|
||||
{
|
||||
buf[0] = K_SPECIAL;
|
||||
buf[1] = KS_EXTRA;
|
||||
buf[2] = (int)KE_CURSORHOLD;
|
||||
return 3;
|
||||
}
|
||||
/*
|
||||
* If 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
|
||||
/* Checking if a job ended requires polling. Do this every 100 msec. */
|
||||
if (has_pending_job() && (wait_time < 0 || wait_time > 100L))
|
||||
wait_time = 100L;
|
||||
/* If there is readahead then parse_queued_messages() timed out and we
|
||||
* should call it again soon. */
|
||||
if ((wait_time < 0 || wait_time > 100L) && 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
|
||||
|
||||
/*
|
||||
* We want to be interrupted by the winch signal
|
||||
* or by an event on the monitored file descriptors.
|
||||
*/
|
||||
if (WaitForChar(wait_time, &interrupted, FALSE))
|
||||
{
|
||||
/* If input was put directly in typeahead buffer bail out here. */
|
||||
if (typebuf_changed(tb_change_cnt))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For some terminals we only get one character at a time.
|
||||
* We want the get all available characters, so we could keep on
|
||||
* trying until none is available
|
||||
* For some other terminals this is quite slow, that's why we don't
|
||||
* do it.
|
||||
*/
|
||||
len = read_from_input_buf(buf, (long)maxlen);
|
||||
if (len > 0)
|
||||
return len;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* no character available */
|
||||
#ifndef ELAPSED_FUNC
|
||||
/* estimate the elapsed time */
|
||||
elapsed_time += wait_time;
|
||||
#endif
|
||||
|
||||
if (do_resize /* interrupted by SIGWINCH signal */
|
||||
#ifdef FEAT_CLIENTSERVER
|
||||
|| server_waiting()
|
||||
#endif
|
||||
#ifdef MESSAGE_QUEUE
|
||||
|| interrupted
|
||||
#endif
|
||||
|| wait_time > 0
|
||||
|| (wtime < 0 && !did_start_blocking))
|
||||
continue;
|
||||
|
||||
/* no character available or interrupted */
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return inchar_loop(buf, maxlen, wtime, tb_change_cnt,
|
||||
WaitForChar, resize_func);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2,6 +2,7 @@
|
||||
void ui_write(char_u *s, int len);
|
||||
void ui_inchar_undo(char_u *s, int len);
|
||||
int ui_inchar(char_u *buf, int maxlen, long wtime, int tb_change_cnt);
|
||||
int inchar_loop(char_u *buf, int maxlen, long wtime, int tb_change_cnt, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int (*resize_func)(int check_only));
|
||||
int ui_wait_for_chars_or_timer(long wtime, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int *interrupted, int ignore_input);
|
||||
int ui_char_avail(void);
|
||||
void ui_delay(long msec, int ignoreinput);
|
||||
|
@ -58,6 +58,10 @@ func RunVimInTerminal(arguments, options)
|
||||
let cmd .= ' -v ' . a:arguments
|
||||
let buf = term_start(cmd, {'curwin': 1, 'term_rows': rows, 'term_cols': cols})
|
||||
if &termwinsize == ''
|
||||
" in the GUI we may end up with a different size, try to set it.
|
||||
if term_getsize(buf) != [rows, cols]
|
||||
call term_setsize(buf, rows, cols)
|
||||
endif
|
||||
call assert_equal([rows, cols], term_getsize(buf))
|
||||
else
|
||||
let rows = term_getsize(buf)[0]
|
||||
|
212
src/ui.c
212
src/ui.c
@ -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.
|
||||
|
@ -783,6 +783,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
834,
|
||||
/**/
|
||||
833,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user