summaryrefslogtreecommitdiff
path: root/src/ui.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-01-27 16:55:47 +0100
committerBram Moolenaar <Bram@vim.org>2019-01-27 16:55:47 +0100
commite40b9d47bf8f8f716d3ef5a95c8ecbbdc0a501f9 (patch)
tree7891a85cc4c4d8f9c173309dfdf7e870777a8046 /src/ui.c
parentd93090f41f70c521cfad5b25efcb0024b9480082 (diff)
downloadvim-git-e40b9d47bf8f8f716d3ef5a95c8ecbbdc0a501f9.tar.gz
patch 8.1.0834: GUI may wait too long before dealing with messagesv8.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)
Diffstat (limited to 'src/ui.c')
-rw-r--r--src/ui.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/ui.c b/src/ui.c
index 9150dafa6..0d08c5d05 100644
--- 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.