diff src/ui.c @ 15653:59a1ff689b4d v8.1.0834

patch 8.1.0834: GUI may wait too long before dealing with messages commit https://github.com/vim/vim/commit/e40b9d47bf8f8f716d3ef5a95c8ecbbdc0a501f9 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 27 16:55:47 2019 +0100 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 27 Jan 2019 17:00:07 +0100
parents 6f1c7e9a6393
children 2202ab00e9f9
line wrap: on
line diff
--- 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.