The attached patch (qemu-curses-delay-1.patch) allows the user to specify that 
he needs -display curses to insert a delay in between key events. The current 
behaviour is that it inserts key events immediately, one right after another, 
which has proven to be too fast for some applications. Please let me know if 
there are any improvements you'd like me to make to this patch, with the goal 
that I'm hoping you'll accept it. A more detailed description of this patch is 
as follows:

Add support for the "-display curses" option to accept suboptions (-display 
curses[,option...), and add the "delay=<msecs>" suboption. This suboption 
causes a millisecond-based delay to be inserted in between key events so that 
they won't be inserted immediately one after another. The delay is performed 
using a qemu virtual clock timer. If the "delay" option isn't specified, or if 
"delay=0" is specified, then the timer isn't used, thus making the default be 
the original behaviour. The "=<msecs>" operand is optional - if it isn't 
specified then 20 is assumed.

-- 
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: d...@mielke.cc | Canada  K2A 1H7   | http://FamilyRadio.com/
diff --git a/include/ui/console.h b/include/ui/console.h
index 8a86617..d131260 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -338,6 +338,7 @@ static inline int vnc_display_pw_expire(DisplayState *ds, 
time_t expires)
 #endif
 
 /* curses.c */
+void curses_parse_options(const char *options);
 void curses_display_init(DisplayState *ds, int full_screen);
 
 /* input.c */
diff --git a/qemu-options.hx b/qemu-options.hx
index c2c0823..1e407bd 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -820,9 +820,11 @@ ETEXI
 
 DEF("display", HAS_ARG, QEMU_OPTION_display,
     "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n"
-    "            [,window_close=on|off]|curses|none|\n"
-    "            gtk[,grab_on_hover=on|off]|\n"
-    "            vnc=<display>[,<optargs>]\n"
+    "            [,window_close=on|off]\n"
+    "         none\n"
+    "         curses[,delay[=<msecs>]]\n"
+    "         gtk[,grab_on_hover=on|off]\n"
+    "         vnc=<display>[,<optargs>]\n"
     "                select display type\n", QEMU_ARCH_ALL)
 STEXI
 @item -display @var{type}
diff --git a/ui/curses.c b/ui/curses.c
index b044790..45719a4 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -176,6 +176,82 @@ static void curses_cursor_position(DisplayChangeListener 
*dcl,
 
 static kbd_layout_t *kbd_layout = NULL;
 
+struct KeyEvent {
+    struct KeyEvent *next;
+    int code;
+    bool down;
+};
+
+static struct KeyEvent *firstKeyEvent = NULL;
+static struct KeyEvent *lastKeyEvent = NULL;
+
+static unsigned long long keyEventDelay = 0;
+static QEMUTimer *keyEventTimer = NULL;
+
+static void curses_begin_key_event(void);
+
+static void curses_send_key_event(int code, bool down)
+{
+    qemu_input_event_send_key_number(NULL, code, down);
+}
+
+static void curses_end_key_event(void *opaque)
+{
+    struct KeyEvent *event = firstKeyEvent;
+
+    if ((firstKeyEvent = event->next))
+    {
+        curses_begin_key_event();
+    }
+    else
+    {
+        lastKeyEvent = NULL;
+    }
+
+    g_free(event);
+}
+
+static void curses_begin_key_event(void)
+{
+    struct KeyEvent *event = firstKeyEvent;
+
+    if (!keyEventTimer)
+    {
+        keyEventTimer = timer_new_ms(QEMU_CLOCK_VIRTUAL, curses_end_key_event, 
NULL);
+    }
+
+    curses_send_key_event(event->code, event->down);
+    timer_mod(keyEventTimer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 
keyEventDelay);
+}
+
+static void curses_enqueue_key_event(int code, bool down)
+{
+    if (keyEventDelay)
+    {
+        struct KeyEvent *event;
+
+        event = g_malloc(sizeof(*event));
+        event->next = NULL;
+        event->code = code;
+        event->down = down;
+
+        if (lastKeyEvent)
+        {
+            lastKeyEvent->next = event;
+            lastKeyEvent = event;
+        }
+        else
+        {
+            firstKeyEvent = lastKeyEvent = event;
+            curses_begin_key_event();
+        }
+    }
+    else
+    {
+        curses_send_key_event(code, down);
+    }
+}
+
 static void curses_refresh(DisplayChangeListener *dcl)
 {
     int chr, nextchr, keysym, keycode, keycode_alt;
@@ -276,32 +352,32 @@ static void curses_refresh(DisplayChangeListener *dcl)
             /* since terminals don't know about key press and release
              * events, we need to emit both for each key received */
             if (keycode & SHIFT) {
-                qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
+                curses_enqueue_key_event(SHIFT_CODE, true);
             }
             if (keycode & CNTRL) {
-                qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
+                curses_enqueue_key_event(CNTRL_CODE, true);
             }
             if (keycode & ALT) {
-                qemu_input_event_send_key_number(NULL, ALT_CODE, true);
+                curses_enqueue_key_event(ALT_CODE, true);
             }
             if (keycode & ALTGR) {
-                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
+                curses_enqueue_key_event(GREY | ALT_CODE, true);
             }
 
-            qemu_input_event_send_key_number(NULL, keycode, true);
-            qemu_input_event_send_key_number(NULL, keycode, false);
+            curses_enqueue_key_event(keycode, true);
+            curses_enqueue_key_event(keycode, false);
 
             if (keycode & ALTGR) {
-                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
+                curses_enqueue_key_event(GREY | ALT_CODE, false);
             }
             if (keycode & ALT) {
-                qemu_input_event_send_key_number(NULL, ALT_CODE, false);
+                curses_enqueue_key_event(ALT_CODE, false);
             }
             if (keycode & CNTRL) {
-                qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
+                curses_enqueue_key_event(CNTRL_CODE, false);
             }
             if (keycode & SHIFT) {
-                qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
+                curses_enqueue_key_event(SHIFT_CODE, false);
             }
         } else {
             keysym = curses2qemu[chr];
@@ -349,6 +425,51 @@ static void curses_keyboard_setup(void)
     }
 }
 
+void curses_parse_options(const char *options)
+{
+    keyEventDelay = 0;
+
+    while (*options)
+    {
+        const char *next;
+        char *end;
+
+        if (!strstart(options, ",", &next))
+        {
+            break;
+        }
+        options = next;
+
+        if (strstart(options, "delay", &next))
+        {
+            options = next;
+            keyEventDelay = 20;
+
+            if (strstart(options, "=", &next))
+            {
+                options = next;
+
+                if (parse_uint(options, &keyEventDelay, &end, 10))
+                {
+                    break;
+                }
+                options = end;
+            }
+        }
+
+        else
+        {
+            break;
+        }
+    }
+
+    if (*options)
+    {
+        fprintf(stderr, "qemu: invalid Curses display option: %s\n", options);
+        exit(1);
+    }
+}
+
 static const DisplayChangeListenerOps dcl_ops = {
     .dpy_name        = "curses",
     .dpy_text_update = curses_update,
diff --git a/vl.c b/vl.c
index 709d8cd..bd6c583 100644
--- a/vl.c
+++ b/vl.c
@@ -2300,6 +2300,7 @@ static DisplayType select_display(const char *p)
     } else if (strstart(p, "curses", &opts)) {
 #ifdef CONFIG_CURSES
         display = DT_CURSES;
+        curses_parse_options(opts);
 #else
         fprintf(stderr, "Curses support is disabled\n");
         exit(1);

Reply via email to