qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Gerd Hoffmann <kraxel@redhat.com>
To: qemu-devel@nongnu.org
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Eric Blake <eblake@redhat.com>,
	libvir-list@redhat.com, Markus Armbruster <armbru@redhat.com>,
	Gerd Hoffmann <kraxel@redhat.com>,
	"Dr. David Alan Gilbert" <dgilbert@redhat.com>,
	Samuel Thibault <samuel.thibault@ens-lyon.org>
Subject: [Qemu-devel] [PULL 2/7] curses: support wide input
Date: Thu,  7 Mar 2019 16:11:36 +0100	[thread overview]
Message-ID: <20190307151141.6483-3-kraxel@redhat.com> (raw)
In-Reply-To: <20190307151141.6483-1-kraxel@redhat.com>

From: Samuel Thibault <samuel.thibault@ens-lyon.org>

This makes use of wide curses functions instead of 8bit functions. This
allows to type e.g. accented letters.

Unfortunately, key codes are then returned with values that could be
confused with wide characters by ncurses, so we need to add a maybe_keycode
variable to know whether the returned value is a key code or a character
(curses with wide support), or possibly both (curses without wide support).

The translation tables thus also need to be separated into key code
translation and character translation.  The curses2foo helper makes it easier
to use them.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Message-id: 20190304210532.7840-1-samuel.thibault@ens-lyon.org
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 ui/curses_keys.h | 113 +++++++++++++++++++++++++++--------------------
 ui/curses.c      |  76 +++++++++++++++++++++++++------
 2 files changed, 127 insertions(+), 62 deletions(-)

diff --git a/ui/curses_keys.h b/ui/curses_keys.h
index e9195a167192..71e04acdc756 100644
--- a/ui/curses_keys.h
+++ b/ui/curses_keys.h
@@ -49,22 +49,28 @@
 /* curses won't detect a Control + Alt + 1, so use Alt + 1 */
 #define QEMU_KEY_CONSOLE0   (2 | ALT)   /* (curses2keycode['1'] | ALT) */
 
+#define CURSES_CHARS        0x100       /* Support latin1 only */
 #define CURSES_KEYS         KEY_MAX     /* KEY_MAX defined in <curses.h> */
 
-static const int curses2keysym[CURSES_KEYS] = {
-    [0 ... (CURSES_KEYS - 1)] = -1,
+static const int _curses2keysym[CURSES_CHARS] = {
+    [0 ... (CURSES_CHARS - 1)] = -1,
 
     [0x7f] = KEY_BACKSPACE,
     ['\r'] = KEY_ENTER,
     ['\n'] = KEY_ENTER,
     [27] = 27,
+};
+
+static const int _curseskey2keysym[CURSES_KEYS] = {
+    [0 ... (CURSES_KEYS - 1)] = -1,
+
     [KEY_BTAB] = '\t' | KEYSYM_SHIFT,
     [KEY_SPREVIOUS] = KEY_PPAGE | KEYSYM_SHIFT,
     [KEY_SNEXT] = KEY_NPAGE | KEYSYM_SHIFT,
 };
 
-static const int curses2keycode[CURSES_KEYS] = {
-    [0 ... (CURSES_KEYS - 1)] = -1,
+static const int _curses2keycode[CURSES_CHARS] = {
+    [0 ... (CURSES_CHARS - 1)] = -1,
 
     [0x01b] = 1, /* Escape */
     ['1'] = 2,
@@ -80,7 +86,6 @@ static const int curses2keycode[CURSES_KEYS] = {
     ['-'] = 12,
     ['='] = 13,
     [0x07f] = 14, /* Backspace */
-    [KEY_BACKSPACE] = 14, /* Backspace */
 
     ['\t'] = 15, /* Tab */
     ['q'] = 16,
@@ -97,7 +102,6 @@ static const int curses2keycode[CURSES_KEYS] = {
     [']'] = 27,
     ['\n'] = 28, /* Return */
     ['\r'] = 28, /* Return */
-    [KEY_ENTER] = 28, /* Return */
 
     ['a'] = 30,
     ['s'] = 31,
@@ -126,33 +130,6 @@ static const int curses2keycode[CURSES_KEYS] = {
 
     [' '] = 57,
 
-    [KEY_F(1)] = 59, /* Function Key 1 */
-    [KEY_F(2)] = 60, /* Function Key 2 */
-    [KEY_F(3)] = 61, /* Function Key 3 */
-    [KEY_F(4)] = 62, /* Function Key 4 */
-    [KEY_F(5)] = 63, /* Function Key 5 */
-    [KEY_F(6)] = 64, /* Function Key 6 */
-    [KEY_F(7)] = 65, /* Function Key 7 */
-    [KEY_F(8)] = 66, /* Function Key 8 */
-    [KEY_F(9)] = 67, /* Function Key 9 */
-    [KEY_F(10)] = 68, /* Function Key 10 */
-    [KEY_F(11)] = 87, /* Function Key 11 */
-    [KEY_F(12)] = 88, /* Function Key 12 */
-
-    [KEY_HOME] = 71 | GREY, /* Home */
-    [KEY_UP] = 72 | GREY, /* Up Arrow */
-    [KEY_PPAGE] = 73 | GREY, /* Page Up */
-    [KEY_LEFT] = 75 | GREY, /* Left Arrow */
-    [KEY_RIGHT] = 77 | GREY, /* Right Arrow */
-    [KEY_END] = 79 | GREY, /* End */
-    [KEY_DOWN] = 80 | GREY, /* Down Arrow */
-    [KEY_NPAGE] = 81 | GREY, /* Page Down */
-    [KEY_IC] = 82 | GREY, /* Insert */
-    [KEY_DC] = 83 | GREY, /* Delete */
-
-    [KEY_SPREVIOUS] = 73 | GREY | SHIFT, /* Shift + Page Up */
-    [KEY_SNEXT] = 81 | GREY | SHIFT, /* Shift + Page Down */
-
     ['!'] = 2 | SHIFT,
     ['@'] = 3 | SHIFT,
     ['#'] = 4 | SHIFT,
@@ -166,7 +143,6 @@ static const int curses2keycode[CURSES_KEYS] = {
     ['_'] = 12 | SHIFT,
     ['+'] = 13 | SHIFT,
 
-    [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */
     ['Q'] = 16 | SHIFT,
     ['W'] = 17 | SHIFT,
     ['E'] = 18 | SHIFT,
@@ -205,19 +181,6 @@ static const int curses2keycode[CURSES_KEYS] = {
     ['>'] = 52 | SHIFT,
     ['?'] = 53 | SHIFT,
 
-    [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */
-    [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */
-    [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */
-    [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */
-    [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */
-    [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */
-    [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */
-    [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */
-    [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */
-    [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */
-    [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */
-    [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */
-
     ['Q' - '@'] = 16 | CNTRL, /* Control + q */
     ['W' - '@'] = 17 | CNTRL, /* Control + w */
     ['E' - '@'] = 18 | CNTRL, /* Control + e */
@@ -249,13 +212,67 @@ static const int curses2keycode[CURSES_KEYS] = {
 
 };
 
-static const int curses2qemu[CURSES_KEYS] = {
+static const int _curseskey2keycode[CURSES_KEYS] = {
     [0 ... (CURSES_KEYS - 1)] = -1,
 
+    [KEY_BACKSPACE] = 14, /* Backspace */
+
+    [KEY_ENTER] = 28, /* Return */
+
+    [KEY_F(1)] = 59, /* Function Key 1 */
+    [KEY_F(2)] = 60, /* Function Key 2 */
+    [KEY_F(3)] = 61, /* Function Key 3 */
+    [KEY_F(4)] = 62, /* Function Key 4 */
+    [KEY_F(5)] = 63, /* Function Key 5 */
+    [KEY_F(6)] = 64, /* Function Key 6 */
+    [KEY_F(7)] = 65, /* Function Key 7 */
+    [KEY_F(8)] = 66, /* Function Key 8 */
+    [KEY_F(9)] = 67, /* Function Key 9 */
+    [KEY_F(10)] = 68, /* Function Key 10 */
+    [KEY_F(11)] = 87, /* Function Key 11 */
+    [KEY_F(12)] = 88, /* Function Key 12 */
+
+    [KEY_HOME] = 71 | GREY, /* Home */
+    [KEY_UP] = 72 | GREY, /* Up Arrow */
+    [KEY_PPAGE] = 73 | GREY, /* Page Up */
+    [KEY_LEFT] = 75 | GREY, /* Left Arrow */
+    [KEY_RIGHT] = 77 | GREY, /* Right Arrow */
+    [KEY_END] = 79 | GREY, /* End */
+    [KEY_DOWN] = 80 | GREY, /* Down Arrow */
+    [KEY_NPAGE] = 81 | GREY, /* Page Down */
+    [KEY_IC] = 82 | GREY, /* Insert */
+    [KEY_DC] = 83 | GREY, /* Delete */
+
+    [KEY_SPREVIOUS] = 73 | GREY | SHIFT, /* Shift + Page Up */
+    [KEY_SNEXT] = 81 | GREY | SHIFT, /* Shift + Page Down */
+
+    [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */
+
+    [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */
+    [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */
+    [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */
+    [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */
+    [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */
+    [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */
+    [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */
+    [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */
+    [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */
+    [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */
+    [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */
+    [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */
+};
+
+static const int _curses2qemu[CURSES_CHARS] = {
+    [0 ... (CURSES_CHARS - 1)] = -1,
+
     ['\n'] = '\n',
     ['\r'] = '\n',
 
     [0x07f] = QEMU_KEY_BACKSPACE,
+};
+
+static const int _curseskey2qemu[CURSES_KEYS] = {
+    [0 ... (CURSES_KEYS - 1)] = -1,
 
     [KEY_DOWN] = QEMU_KEY_DOWN,
     [KEY_UP] = QEMU_KEY_UP,
diff --git a/ui/curses.c b/ui/curses.c
index 870273de51a9..37954ce1b046 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -42,6 +42,12 @@
 #define FONT_HEIGHT 16
 #define FONT_WIDTH 8
 
+enum maybe_keycode {
+    CURSES_KEYCODE,
+    CURSES_CHAR,
+    CURSES_CHAR_OR_KEYCODE,
+};
+
 static DisplayChangeListener *dcl;
 static console_ch_t screen[160 * 100];
 static WINDOW *screenpad = NULL;
@@ -194,9 +200,54 @@ static void curses_cursor_position(DisplayChangeListener *dcl,
 
 static kbd_layout_t *kbd_layout = NULL;
 
+static wint_t console_getch(enum maybe_keycode *maybe_keycode)
+{
+    wint_t ret;
+    switch (get_wch(&ret)) {
+    case KEY_CODE_YES:
+        *maybe_keycode = CURSES_KEYCODE;
+        break;
+    case OK:
+        *maybe_keycode = CURSES_CHAR;
+        break;
+    case ERR:
+        ret = -1;
+        break;
+    }
+    return ret;
+}
+
+static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
+                      int chr, enum maybe_keycode maybe_keycode)
+{
+    int ret = -1;
+    if (maybe_keycode == CURSES_CHAR) {
+        if (chr < CURSES_CHARS) {
+            ret = _curses2foo[chr];
+        }
+    } else {
+        if (chr < CURSES_KEYS) {
+            ret = _curseskey2foo[chr];
+        }
+        if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
+            chr < CURSES_CHARS) {
+            ret = _curses2foo[chr];
+        }
+    }
+    return ret;
+}
+
+#define curses2keycode(chr, maybe_keycode) \
+    curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
+#define curses2keysym(chr, maybe_keycode) \
+    curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
+#define curses2qemu(chr, maybe_keycode) \
+    curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
+
 static void curses_refresh(DisplayChangeListener *dcl)
 {
     int chr, keysym, keycode, keycode_alt;
+    enum maybe_keycode maybe_keycode;
 
     curses_winch_check();
 
@@ -212,14 +263,14 @@ static void curses_refresh(DisplayChangeListener *dcl)
 
     while (1) {
         /* while there are any pending key strokes to process */
-        chr = getch();
+        chr = console_getch(&maybe_keycode);
 
-        if (chr == ERR)
+        if (chr == -1)
             break;
 
 #ifdef KEY_RESIZE
         /* this shouldn't occur when we use a custom SIGWINCH handler */
-        if (chr == KEY_RESIZE) {
+        if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
             clear();
             refresh();
             curses_calc_pad();
@@ -228,17 +279,19 @@ static void curses_refresh(DisplayChangeListener *dcl)
         }
 #endif
 
-        keycode = curses2keycode[chr];
+        keycode = curses2keycode(chr, maybe_keycode);
         keycode_alt = 0;
 
         /* alt or esc key */
         if (keycode == 1) {
-            int nextchr = getch();
+            enum maybe_keycode next_maybe_keycode;
+            int nextchr = console_getch(&next_maybe_keycode);
 
-            if (nextchr != ERR) {
+            if (nextchr != -1) {
                 chr = nextchr;
+                maybe_keycode = next_maybe_keycode;
                 keycode_alt = ALT;
-                keycode = curses2keycode[chr];
+                keycode = curses2keycode(chr, maybe_keycode);
 
                 if (keycode != -1) {
                     keycode |= ALT;
@@ -258,9 +311,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
         }
 
         if (kbd_layout) {
-            keysym = -1;
-            if (chr < CURSES_KEYS)
-                keysym = curses2keysym[chr];
+            keysym = curses2keysym(chr, maybe_keycode);
 
             if (keysym == -1) {
                 if (chr < ' ') {
@@ -326,10 +377,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
                 qemu_input_event_send_key_delay(0);
             }
         } else {
-            keysym = -1;
-            if (chr < CURSES_KEYS) {
-                keysym = curses2qemu[chr];
-            }
+            keysym = curses2qemu(chr, maybe_keycode);
             if (keysym == -1)
                 keysym = chr;
 
-- 
2.18.1

  parent reply	other threads:[~2019-03-07 15:11 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-03-07 15:11 [Qemu-devel] [PULL 0/7] Ui 20190307 patches Gerd Hoffmann
2019-03-07 15:11 ` [Qemu-devel] [PULL 1/7] Reduce curses escdelay from 1s to 25ms Gerd Hoffmann
2019-03-07 15:11 ` Gerd Hoffmann [this message]
2019-03-07 15:11 ` [Qemu-devel] [PULL 3/7] iconv: detect and make curses depend on it Gerd Hoffmann
2019-03-07 15:11 ` [Qemu-devel] [PULL 4/7] curses: add option to specify VGA font encoding Gerd Hoffmann
2019-03-07 15:11 ` [Qemu-devel] [PULL 5/7] vnc: fix update stalls Gerd Hoffmann
2019-03-07 15:11 ` [Qemu-devel] [PULL 6/7] vnc: allow specifying a custom authorization object name Gerd Hoffmann
2019-03-07 15:11 ` [Qemu-devel] [PULL 7/7] monitor: deprecate acl_show, acl_reset, acl_policy, acl_add, acl_remove Gerd Hoffmann
2019-03-08 14:04 ` [Qemu-devel] [PULL 0/7] Ui 20190307 patches Peter Maydell
2019-03-11  8:33   ` Gerd Hoffmann
2019-03-11 13:52     ` Samuel Thibault

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190307151141.6483-3-kraxel@redhat.com \
    --to=kraxel@redhat.com \
    --cc=armbru@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=eblake@redhat.com \
    --cc=libvir-list@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=samuel.thibault@ens-lyon.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).