Skip to content

Commit e40b9d4

Browse files
committed
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)
1 parent d93090f commit e40b9d4

File tree

6 files changed

+288
-205
lines changed

6 files changed

+288
-205
lines changed

src/gui.c

+52-73
Original file line numberDiff line numberDiff line change
@@ -2896,104 +2896,86 @@ gui_wait_for_chars_3(
28962896
* or FAIL otherwise.
28972897
*/
28982898
static int
2899-
gui_wait_for_chars_or_timer(long wtime)
2899+
gui_wait_for_chars_or_timer(
2900+
long wtime,
2901+
int *interrupted UNUSED,
2902+
int ignore_input UNUSED)
29002903
{
29012904
#ifdef FEAT_TIMERS
2902-
return ui_wait_for_chars_or_timer(wtime, gui_wait_for_chars_3, NULL, 0);
2905+
return ui_wait_for_chars_or_timer(wtime, gui_wait_for_chars_3,
2906+
interrupted, ignore_input);
29032907
#else
29042908
return gui_mch_wait_for_chars(wtime);
29052909
#endif
29062910
}
29072911

29082912
/*
29092913
* The main GUI input routine. Waits for a character from the keyboard.
2910-
* wtime == -1 Wait forever.
2911-
* wtime == 0 Don't wait.
2912-
* wtime > 0 Wait wtime milliseconds for a character.
2913-
* Returns OK if a character was found to be available within the given time,
2914-
* or FAIL otherwise.
2914+
* "wtime" == -1 Wait forever.
2915+
* "wtime" == 0 Don't wait.
2916+
* "wtime" > 0 Wait wtime milliseconds for a character.
2917+
*
2918+
* Returns the number of characters read or zero when timed out or interrupted.
2919+
* "buf" may be NULL, in which case a non-zero number is returned if characters
2920+
* are available.
29152921
*/
2916-
int
2917-
gui_wait_for_chars(long wtime, int tb_change_cnt)
2922+
static int
2923+
gui_wait_for_chars_buf(
2924+
char_u *buf,
2925+
int maxlen,
2926+
long wtime, // don't use "time", MIPS cannot handle it
2927+
int tb_change_cnt)
29182928
{
2919-
int retval;
2920-
#if defined(ELAPSED_FUNC)
2921-
elapsed_T start_tv;
2922-
#endif
2929+
int retval;
29232930

29242931
#ifdef FEAT_MENU
2925-
/*
2926-
* If we're going to wait a bit, update the menus and mouse shape for the
2927-
* current State.
2928-
*/
2932+
// If we're going to wait a bit, update the menus and mouse shape for the
2933+
// current State.
29292934
if (wtime != 0)
29302935
gui_update_menus(0);
29312936
#endif
29322937

29332938
gui_mch_update();
2934-
if (input_available()) /* Got char, return immediately */
2935-
return OK;
2936-
if (wtime == 0) /* Don't wait for char */
2937-
return FAIL;
2938-
2939-
/* Before waiting, flush any output to the screen. */
2940-
gui_mch_flush();
2941-
2942-
if (wtime > 0)
2939+
if (input_available()) // Got char, return immediately
29432940
{
2944-
/* Blink when waiting for a character. Probably only does something
2945-
* for showmatch() */
2946-
gui_mch_start_blink();
2947-
retval = gui_wait_for_chars_or_timer(wtime);
2948-
gui_mch_stop_blink(TRUE);
2949-
return retval;
2941+
if (buf != NULL && !typebuf_changed(tb_change_cnt))
2942+
return read_from_input_buf(buf, (long)maxlen);
2943+
return 0;
29502944
}
2945+
if (wtime == 0) // Don't wait for char
2946+
return FAIL;
29512947

2952-
#if defined(ELAPSED_FUNC)
2953-
ELAPSED_INIT(start_tv);
2954-
#endif
2948+
// Before waiting, flush any output to the screen.
2949+
gui_mch_flush();
29552950

2956-
/*
2957-
* While we are waiting indefinitely for a character, blink the cursor.
2958-
*/
2951+
// Blink while waiting for a character.
29592952
gui_mch_start_blink();
29602953

2961-
retval = FAIL;
2962-
/*
2963-
* We may want to trigger the CursorHold event. First wait for
2964-
* 'updatetime' and if nothing is typed within that time, and feedkeys()
2965-
* wasn't used, put the K_CURSORHOLD key in the input buffer.
2966-
*/
2967-
if (gui_wait_for_chars_or_timer(p_ut) == OK)
2968-
retval = OK;
2969-
else if (trigger_cursorhold()
2970-
#if defined(ELAPSED_FUNC)
2971-
&& ELAPSED_FUNC(start_tv) >= p_ut
2972-
#endif
2973-
&& typebuf.tb_change_cnt == tb_change_cnt)
2974-
{
2975-
char_u buf[3];
2976-
2977-
/* Put K_CURSORHOLD in the input buffer. */
2978-
buf[0] = CSI;
2979-
buf[1] = KS_EXTRA;
2980-
buf[2] = (int)KE_CURSORHOLD;
2981-
add_to_input_buf(buf, 3);
2982-
2983-
retval = OK;
2984-
}
2985-
2986-
if (retval == FAIL && typebuf.tb_change_cnt == tb_change_cnt)
2987-
{
2988-
/* Blocking wait. */
2989-
before_blocking();
2990-
retval = gui_wait_for_chars_or_timer(-1L);
2991-
}
2954+
// Common function to loop until "wtime" is met, while handling timers and
2955+
// other callbacks.
2956+
retval = inchar_loop(buf, maxlen, wtime, tb_change_cnt,
2957+
gui_wait_for_chars_or_timer, NULL);
29922958

29932959
gui_mch_stop_blink(TRUE);
2960+
29942961
return retval;
29952962
}
29962963

2964+
/*
2965+
* Wait for a character from the keyboard without actually reading it.
2966+
* Also deals with timers.
2967+
* wtime == -1 Wait forever.
2968+
* wtime == 0 Don't wait.
2969+
* wtime > 0 Wait wtime milliseconds for a character.
2970+
* Returns OK if a character was found to be available within the given time,
2971+
* or FAIL otherwise.
2972+
*/
2973+
int
2974+
gui_wait_for_chars(long wtime, int tb_change_cnt)
2975+
{
2976+
return gui_wait_for_chars_buf(NULL, 0, wtime, tb_change_cnt);
2977+
}
2978+
29972979
/*
29982980
* Equivalent of mch_inchar() for the GUI.
29992981
*/
@@ -3004,10 +2986,7 @@ gui_inchar(
30042986
long wtime, /* milli seconds */
30052987
int tb_change_cnt)
30062988
{
3007-
if (gui_wait_for_chars(wtime, tb_change_cnt)
3008-
&& !typebuf_changed(tb_change_cnt))
3009-
return read_from_input_buf(buf, (long)maxlen);
3010-
return 0;
2989+
return gui_wait_for_chars_buf(buf, maxlen, wtime, tb_change_cnt);
30112990
}
30122991

30132992
/*

src/os_unix.c

+17-132
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,21 @@ mch_write(char_u *s, int len)
355355
RealWaitForChar(read_cmd_fd, p_wd, NULL, NULL);
356356
}
357357

358+
/*
359+
* Function passed to inchar_loop() to handle window resizing.
360+
* If "check_only" is TRUE: Return whether there was a resize.
361+
* If "check_only" is FALSE: Deal with the window resized.
362+
*/
363+
static int
364+
resize_func(int check_only)
365+
{
366+
if (check_only)
367+
return do_resize;
368+
while (do_resize)
369+
handle_resize();
370+
return FALSE;
371+
}
372+
358373
/*
359374
* mch_inchar(): low level input function.
360375
* Get a characters from the keyboard.
@@ -370,138 +385,8 @@ mch_inchar(
370385
long wtime, /* don't use "time", MIPS cannot handle it */
371386
int tb_change_cnt)
372387
{
373-
int len;
374-
int interrupted = FALSE;
375-
int did_start_blocking = FALSE;
376-
long wait_time;
377-
long elapsed_time = 0;
378-
#ifdef ELAPSED_FUNC
379-
elapsed_T start_tv;
380-
381-
ELAPSED_INIT(start_tv);
382-
#endif
383-
384-
/* repeat until we got a character or waited long enough */
385-
for (;;)
386-
{
387-
/* Check if window changed size while we were busy, perhaps the ":set
388-
* columns=99" command was used. */
389-
while (do_resize)
390-
handle_resize();
391-
392-
#ifdef MESSAGE_QUEUE
393-
// Only process messages when waiting.
394-
if (wtime != 0)
395-
{
396-
parse_queued_messages();
397-
// If input was put directly in typeahead buffer bail out here.
398-
if (typebuf_changed(tb_change_cnt))
399-
return 0;
400-
}
401-
#endif
402-
if (wtime < 0 && did_start_blocking)
403-
/* blocking and already waited for p_ut */
404-
wait_time = -1;
405-
else
406-
{
407-
if (wtime >= 0)
408-
wait_time = wtime;
409-
else
410-
/* going to block after p_ut */
411-
wait_time = p_ut;
412-
#ifdef ELAPSED_FUNC
413-
elapsed_time = ELAPSED_FUNC(start_tv);
414-
#endif
415-
wait_time -= elapsed_time;
416-
if (wait_time < 0)
417-
{
418-
if (wtime >= 0)
419-
/* no character available within "wtime" */
420-
return 0;
421-
422-
else
423-
{
424-
/* no character available within 'updatetime' */
425-
did_start_blocking = TRUE;
426-
if (trigger_cursorhold() && maxlen >= 3
427-
&& !typebuf_changed(tb_change_cnt))
428-
{
429-
buf[0] = K_SPECIAL;
430-
buf[1] = KS_EXTRA;
431-
buf[2] = (int)KE_CURSORHOLD;
432-
return 3;
433-
}
434-
/*
435-
* If there is no character available within 'updatetime'
436-
* seconds flush all the swap files to disk.
437-
* Also done when interrupted by SIGWINCH.
438-
*/
439-
before_blocking();
440-
continue;
441-
}
442-
}
443-
}
444-
445-
#ifdef FEAT_JOB_CHANNEL
446-
/* Checking if a job ended requires polling. Do this every 100 msec. */
447-
if (has_pending_job() && (wait_time < 0 || wait_time > 100L))
448-
wait_time = 100L;
449-
/* If there is readahead then parse_queued_messages() timed out and we
450-
* should call it again soon. */
451-
if ((wait_time < 0 || wait_time > 100L) && channel_any_readahead())
452-
wait_time = 10L;
453-
#endif
454-
#ifdef FEAT_BEVAL_GUI
455-
if (p_beval && wait_time > 100L)
456-
/* The 'balloonexpr' may indirectly invoke a callback while waiting
457-
* for a character, need to check often. */
458-
wait_time = 100L;
459-
#endif
460-
461-
/*
462-
* We want to be interrupted by the winch signal
463-
* or by an event on the monitored file descriptors.
464-
*/
465-
if (WaitForChar(wait_time, &interrupted, FALSE))
466-
{
467-
/* If input was put directly in typeahead buffer bail out here. */
468-
if (typebuf_changed(tb_change_cnt))
469-
return 0;
470-
471-
/*
472-
* For some terminals we only get one character at a time.
473-
* We want the get all available characters, so we could keep on
474-
* trying until none is available
475-
* For some other terminals this is quite slow, that's why we don't
476-
* do it.
477-
*/
478-
len = read_from_input_buf(buf, (long)maxlen);
479-
if (len > 0)
480-
return len;
481-
continue;
482-
}
483-
484-
/* no character available */
485-
#ifndef ELAPSED_FUNC
486-
/* estimate the elapsed time */
487-
elapsed_time += wait_time;
488-
#endif
489-
490-
if (do_resize /* interrupted by SIGWINCH signal */
491-
#ifdef FEAT_CLIENTSERVER
492-
|| server_waiting()
493-
#endif
494-
#ifdef MESSAGE_QUEUE
495-
|| interrupted
496-
#endif
497-
|| wait_time > 0
498-
|| (wtime < 0 && !did_start_blocking))
499-
continue;
500-
501-
/* no character available or interrupted */
502-
break;
503-
}
504-
return 0;
388+
return inchar_loop(buf, maxlen, wtime, tb_change_cnt,
389+
WaitForChar, resize_func);
505390
}
506391

507392
static void

src/proto/ui.pro

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
void ui_write(char_u *s, int len);
33
void ui_inchar_undo(char_u *s, int len);
44
int ui_inchar(char_u *buf, int maxlen, long wtime, int tb_change_cnt);
5+
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));
56
int ui_wait_for_chars_or_timer(long wtime, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int *interrupted, int ignore_input);
67
int ui_char_avail(void);
78
void ui_delay(long msec, int ignoreinput);

src/testdir/screendump.vim

+4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ func RunVimInTerminal(arguments, options)
5858
let cmd .= ' -v ' . a:arguments
5959
let buf = term_start(cmd, {'curwin': 1, 'term_rows': rows, 'term_cols': cols})
6060
if &termwinsize == ''
61+
" in the GUI we may end up with a different size, try to set it.
62+
if term_getsize(buf) != [rows, cols]
63+
call term_setsize(buf, rows, cols)
64+
endif
6165
call assert_equal([rows, cols], term_getsize(buf))
6266
else
6367
let rows = term_getsize(buf)[0]

0 commit comments

Comments
 (0)