]> granicus.if.org Git - vim/commitdiff
patch 8.1.0834: GUI may wait too long before dealing with messages v8.1.0834
authorBram Moolenaar <Bram@vim.org>
Sun, 27 Jan 2019 15:55:47 +0000 (16:55 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 27 Jan 2019 15:55:47 +0000 (16:55 +0100)
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)

src/gui.c
src/os_unix.c
src/proto/ui.pro
src/testdir/screendump.vim
src/ui.c
src/version.c

index c22f47716bd41b759ffb246c0da184e3a32037bd..39968954a8734abd3e98df33ba446afa25aea53a 100644 (file)
--- a/src/gui.c
+++ b/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,93 +2911,71 @@ 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 OK if a character was found to be available within the given time,
- * or FAIL otherwise.
+ * "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.
  */
-    int
-gui_wait_for_chars(long wtime, int tb_change_cnt)
+    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;
-#if defined(ELAPSED_FUNC)
-    elapsed_T  start_tv;
-#endif
+    int            retval;
 
 #ifdef FEAT_MENU
-    /*
-     * If we're going to wait a bit, update the menus and mouse shape for the
-     * current State.
-     */
+    // 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)
+    if (input_available())     // Got char, return immediately
     {
-       /* 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 (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;
 
-#if defined(ELAPSED_FUNC)
-    ELAPSED_INIT(start_tv);
-#endif
+    // Before waiting, flush any output to the screen.
+    gui_mch_flush();
 
-    /*
-     * While we are waiting indefinitely for a character, blink the cursor.
-     */
+    // Blink while waiting for a character.
     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);
-    }
+    // 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.
  */
@@ -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);
 }
 
 /*
index 765cd57e7abebed8917737760e193bf2a84f838b..c616e06fe1cf7f7eab44abee2f444cb1a80995a7 100644 (file)
@@ -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
index ac830f0fcb68176274f08a2590db5c7844092a45..774a308966efc254d65c54c1bd4aec4657423425 100644 (file)
@@ -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);
index 139f708c071bf5b8c0cf35c184d4d4beee5eb262..b46a477b73676f61916047f17cbc791a54000e25 100644 (file)
@@ -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]
index 9150dafa6ea1bffa4e9fd0da7aad49f9188cd3c3..0d08c5d0537525410628d2dd6078a6d29136e66e 100644 (file)
--- a/src/ui.c
+++ b/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.
index 9572886632a0892dd94a5f0db7fcb346b6f531f9..0a6dce29f956dec81a03b35a96b227969f2227fe 100644 (file)
@@ -783,6 +783,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    834,
 /**/
     833,
 /**/