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.

Raspunde prin e-mail lui