>[quoted lines by Peter Maydell on 2014/05/25 at 10:11 +0100]
>Ah, I see. Still, I think it makes more sense for the queue and delay
>to be in the common key handling code, not in the curses frontend
>specifically.
This patch, attached as qemu-kbddelay-1.patch, is a rework of the former curses
UI patch so that the delay applies to key events in general. A new option,
-kbddelay [duration=]msecs, controls it. As before, the default is 0, meaning
no delay, in which case the timer and queue aren't used thus retaining the
original behaviour.
--
Dave Mielke | 2213 Fox Crescent | The Bible is the very Word of God.
Phone: 1-613-726-0014 | Ottawa, Ontario | http://Mielke.cc/bible/
EMail: [email protected] | Canada K2A 1H7 | http://FamilyRadio.com/
diff --git a/include/ui/input.h b/include/ui/input.h
index 3d3d487..17c77c8 100644
--- a/include/ui/input.h
+++ b/include/ui/input.h
@@ -35,6 +35,7 @@ void qemu_input_event_sync(void);
InputEvent *qemu_input_event_new_key(KeyValue *key, bool down);
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
+void qemu_input_event_set_keyboard_delay(uint64_t duration);
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
int qemu_input_key_value_to_number(const KeyValue *value);
int qemu_input_key_value_to_qcode(const KeyValue *value);
diff --git a/qemu-options.hx b/qemu-options.hx
index c2c0823..5d0a8b1 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -262,6 +262,24 @@ de en-us fi fr-be hr it lv nl-be pt sl tr
The default is @code{en-us}.
ETEXI
+DEF("kbddelay", HAS_ARG, QEMU_OPTION_kbddelay,
+ "-kbddelay [duration=]msecs\n"
+ " ensure a minimum delay between keyboard events\n"
+ " duration: the minimum delay between keyboard events
(default: 0)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -kbddelay [duration=]@var{msecs}
+@findex -kbddelay
+Set the minimum delay between keyboard events to @var{msecs} milliseconds.
+The default is 0 (no minimum delay).
+This option exists in order to deal with problematic applications which run
+into trouble when keys are typed too quickly. Certain UIs, like curses,
+aggravate the problem because they must turn a single user keyboard action
into
+several keyboard events being delivered to the virtual machine. Only try this
+option if you encounter an unexpected keyboard input issue, for example the
+input not being recognized at all. A duration of 20 seems to be reasonable.
+ETEXI
+
DEF("audio-help", 0, QEMU_OPTION_audio_help,
"-audio-help print list of audio drivers and their options\n",
diff --git a/ui/input.c b/ui/input.c
index fc91fba..eb6c1dd 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -188,7 +188,9 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool
down)
return evt;
}
-void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
+static void qemu_input_event_send_key_immediate(QemuConsole *src,
+ KeyValue *key,
+ bool down)
{
InputEvent *evt;
evt = qemu_input_event_new_key(key, down);
@@ -197,6 +199,82 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue
*key, bool down)
qapi_free_InputEvent(evt);
}
+struct PendingKeyEvent {
+ struct PendingKeyEvent *next;
+ QemuConsole *src;
+ KeyValue *key;
+ bool down;
+};
+
+static struct PendingKeyEvent *firstPendingKeyEvent = NULL;
+static struct PendingKeyEvent *lastPendingKeyEvent = NULL;
+
+static unsigned long long pendingKeyEventDelay = 0;
+static QEMUTimer *pendingKeyEventTimer = NULL;
+
+static void qemu_input_begin_pending_key_event(void)
+{
+ struct PendingKeyEvent *event = firstPendingKeyEvent;
+
+ qemu_input_event_send_key_immediate(event->src, event->key, event->down);
+ timer_mod(pendingKeyEventTimer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
pendingKeyEventDelay);
+}
+
+static void qemu_input_end_pending_key_event(void *opaque)
+{
+ struct PendingKeyEvent *event = firstPendingKeyEvent;
+
+ if ((firstPendingKeyEvent = event->next))
+ {
+ qemu_input_begin_pending_key_event();
+ }
+ else
+ {
+ lastPendingKeyEvent = NULL;
+ }
+
+ g_free(event);
+}
+
+void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
+{
+ if (pendingKeyEventDelay)
+ {
+ struct PendingKeyEvent *event;
+
+ event = g_malloc(sizeof(*event));
+ event->next = NULL;
+ event->src = src;
+ event->key = key;
+ event->down = down;
+
+ if (lastPendingKeyEvent)
+ {
+ lastPendingKeyEvent->next = event;
+ lastPendingKeyEvent = event;
+ }
+ else
+ {
+ if (!pendingKeyEventTimer)
+ {
+ pendingKeyEventTimer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
qemu_input_end_pending_key_event, NULL);
+ }
+
+ firstPendingKeyEvent = lastPendingKeyEvent = event;
+ qemu_input_begin_pending_key_event();
+ }
+ }
+ else
+ {
+ qemu_input_event_send_key_immediate(src, key, down);
+ }
+}
+
+void qemu_input_event_set_keyboard_delay(uint64_t duration)
+{
+ pendingKeyEventDelay = duration;
+}
+
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
{
KeyValue *key = g_new0(KeyValue, 1);
diff --git a/vl.c b/vl.c
index 709d8cd..490621b 100644
--- a/vl.c
+++ b/vl.c
@@ -77,6 +77,7 @@ int main(int argc, char **argv)
#include "net/slirp.h"
#include "monitor/monitor.h"
#include "ui/console.h"
+#include "ui/input.h"
#include "sysemu/sysemu.h"
#include "exec/gdbstub.h"
#include "qemu/timer.h"
@@ -1387,6 +1388,20 @@ static QemuOptsList qemu_smp_opts = {
},
};
+static QemuOptsList qemu_kbddelay_opts = {
+ .name = "kbddelay",
+ .implied_opt_name = "duration",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_kbddelay_opts.head),
+ .merge_lists = true,
+ .desc = {
+ {
+ .name = "duration",
+ .type = QEMU_OPT_NUMBER,
+ },
+ { /* end of list */ }
+ },
+};
+
static void smp_parse(QemuOpts *opts)
{
if (opts) {
@@ -3021,6 +3036,7 @@ int main(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_realtime_opts);
qemu_add_opts(&qemu_msg_opts);
qemu_add_opts(&qemu_name_opts);
+ qemu_add_opts(&qemu_kbddelay_opts);
runstate_init();
@@ -3408,6 +3424,30 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_k:
keyboard_layout = optarg;
break;
+ case QEMU_OPTION_kbddelay: {
+ uint64_t duration_value;
+ const char *duration_string;
+
+ opts = qemu_opts_parse(qemu_find_opts("kbddelay"), optarg, 1);
+ if (!opts) {
+ exit(EXIT_FAILURE);
+ }
+
+ duration_string = qemu_opt_get(opts, "duration");
+ if (!duration_string) {
+ error_report("invalid -kbddelay option, missing 'duration'
option");
+ exit(EXIT_FAILURE);
+ }
+
+ if (*duration_string) {
+ duration_value = qemu_opt_get_number(opts, "duration", 20);
+ } else {
+ duration_value = 20;
+ }
+
+ qemu_input_event_set_keyboard_delay(duration_value);
+ break;
+ }
case QEMU_OPTION_localtime:
rtc_utc = 0;
break;