Patch 8.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)
Files: src/ui.c, src/proto/ui.pro, src/os_unix.c, src/gui.c,
src/testdir/screendump.vim
*** ../vim-8.1.0833/src/ui.c 2019-01-26 17:28:22.236599060 +0100
--- src/ui.c 2019-01-27 16:50:31.054860125 +0100
***************
*** 178,183 ****
--- 178,225 ----
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,210 ****
--- 247,422 ----
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.
*** ../vim-8.1.0833/src/proto/ui.pro 2018-05-17 13:52:54.000000000 +0200
--- src/proto/ui.pro 2019-01-26 22:35:56.703647735 +0100
***************
*** 2,7 ****
--- 2,8 ----
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);
*** ../vim-8.1.0833/src/os_unix.c 2019-01-26 15:12:52.558260916 +0100
--- src/os_unix.c 2019-01-26 22:36:20.799486197 +0100
***************
*** 356,361 ****
--- 356,376 ----
}
/*
+ * 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.
* Return the number of characters that are available.
***************
*** 370,507 ****
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;
}
static void
--- 385,392 ----
long wtime, /* don't use "time", MIPS cannot handle it */
int tb_change_cnt)
{
! return inchar_loop(buf, maxlen, wtime, tb_change_cnt,
! WaitForChar, resize_func);
}
static void
*** ../vim-8.1.0833/src/gui.c 2019-01-26 17:28:22.224599141 +0100
--- src/gui.c 2019-01-26 23:17:38.097634554 +0100
***************
*** 2896,2905 ****
* or FAIL otherwise.
*/
static int
! gui_wait_for_chars_or_timer(long wtime)
{
#ifdef FEAT_TIMERS
! return ui_wait_for_chars_or_timer(wtime, gui_wait_for_chars_3, NULL, 0);
#else
return gui_mch_wait_for_chars(wtime);
#endif
--- 2896,2909 ----
* or FAIL otherwise.
*/
static int
! 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,
! interrupted, ignore_input);
#else
return gui_mch_wait_for_chars(wtime);
#endif
***************
*** 2907,3000 ****
/*
* 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 OK if a character was found to be available within the given time,
! * or FAIL otherwise.
*/
! 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;
}
/*
* Equivalent of mch_inchar() for the GUI.
*/
int
--- 2911,2982 ----
/*
* 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.
+ * Returns OK if a character was found to be available within the given time,
+ * or FAIL otherwise.
+ */
+ int
+ gui_wait_for_chars(long wtime, int tb_change_cnt)
+ {
+ return gui_wait_for_chars_buf(NULL, 0, wtime, tb_change_cnt);
+ }
+
+ /*
* Equivalent of mch_inchar() for the GUI.
*/
int
***************
*** 3004,3013 ****
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;
}
/*
--- 2986,2992 ----
long wtime, /* milli seconds */
int tb_change_cnt)
{
! return gui_wait_for_chars_buf(buf, maxlen, wtime, tb_change_cnt);
}
/*
*** ../vim-8.1.0833/src/testdir/screendump.vim 2018-12-04 22:24:12.193693584
+0100
--- src/testdir/screendump.vim 2019-01-27 16:20:34.371242071 +0100
***************
*** 58,63 ****
--- 58,67 ----
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]
*** ../vim-8.1.0833/src/version.c 2019-01-27 15:07:35.161741346 +0100
--- src/version.c 2019-01-27 16:36:02.924759049 +0100
***************
*** 785,786 ****
--- 785,788 ----
{ /* Add new patch number below this line */
+ /**/
+ 834,
/**/
--
We do not stumble over mountains, but over molehills.
Confucius
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.