From: Gerd Hoffmann <kraxel@redhat.com>
To: qemu-devel@nongnu.org
Cc: libvir-list@redhat.com, Paolo Bonzini <pbonzini@redhat.com>,
Gerd Hoffmann <kraxel@redhat.com>,
Peter Maydell <peter.maydell@linaro.org>
Subject: [Qemu-devel] [PULL 07/14] kbd-state: add keyboard state tracker
Date: Tue, 5 Feb 2019 11:57:51 +0100 [thread overview]
Message-ID: <20190205105758.4230-8-kraxel@redhat.com> (raw)
In-Reply-To: <20190205105758.4230-1-kraxel@redhat.com>
Now that most user interfaces are using QKeyCodes it is easier to have
common keyboard code useable by all user interfaces.
This patch adds helper code to track the state of all keyboard keys,
using a bitmap indexed by QKeyCode. Modifier state is tracked too,
as separate bitmap. That makes checking modifier state easier.
Likewise we can easily apply special handling for capslock & numlock
(toggles on keypress) and ctrl + shift (we have two keys for that).
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20190122092814.14919-2-kraxel@redhat.com
[ kraxel: added license boilerplate header ]
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
include/ui/kbd-state.h | 101 ++++++++++++++++++++++++++++++++++++++
ui/kbd-state.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++
ui/Makefile.objs | 2 +-
3 files changed, 232 insertions(+), 1 deletion(-)
create mode 100644 include/ui/kbd-state.h
create mode 100644 ui/kbd-state.c
diff --git a/include/ui/kbd-state.h b/include/ui/kbd-state.h
new file mode 100644
index 0000000000..d87833553a
--- /dev/null
+++ b/include/ui/kbd-state.h
@@ -0,0 +1,101 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+#ifndef QEMU_UI_KBD_STATE_H
+#define QEMU_UI_KBD_STATE_H 1
+
+#include "qapi/qapi-types-ui.h"
+
+typedef enum QKbdModifier QKbdModifier;
+
+enum QKbdModifier {
+ QKBD_MOD_NONE = 0,
+
+ QKBD_MOD_SHIFT,
+ QKBD_MOD_CTRL,
+ QKBD_MOD_ALT,
+ QKBD_MOD_ALTGR,
+
+ QKBD_MOD_NUMLOCK,
+ QKBD_MOD_CAPSLOCK,
+
+ QKBD_MOD__MAX
+};
+
+typedef struct QKbdState QKbdState;
+
+/**
+ * qkbd_state_init: init keyboard state tracker.
+ *
+ * Allocates and initializes keyboard state struct.
+ *
+ * @con: QemuConsole for this state tracker. Gets passed down to
+ * qemu_input_*() functions when sending key events to the guest.
+ */
+QKbdState *qkbd_state_init(QemuConsole *con);
+
+/**
+ * qkbd_state_free: free keyboard tracker state.
+ *
+ * @kbd: state tracker state.
+ */
+void qkbd_state_free(QKbdState *kbd);
+
+/**
+ * qkbd_state_key_event: process key event.
+ *
+ * Update keyboard state, send event to the guest.
+ *
+ * This function takes care to not send suspious events (keyup event
+ * for a key not pressed for example).
+ *
+ * @kbd: state tracker state.
+ * @qcode: the key pressed or released.
+ * @down: true for key down events, false otherwise.
+ */
+void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down);
+
+/**
+ * qkbd_state_set_delay: set key press delay.
+ *
+ * When set the specified delay will be added after each key event,
+ * using qemu_input_event_send_key_delay().
+ *
+ * @kbd: state tracker state.
+ * @delay_ms: the delay in miliseconds.
+ */
+void qkbd_state_set_delay(QKbdState *kbd, int delay_ms);
+
+/**
+ * qkbd_state_key_get: get key state.
+ *
+ * Returns true when the key is down.
+ *
+ * @kbd: state tracker state.
+ * @qcode: the key to query.
+ */
+bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode);
+
+/**
+ * qkbd_state_modifier_get: get modifier state.
+ *
+ * Returns true when the modifier is active.
+ *
+ * @kbd: state tracker state.
+ * @mod: the modifier to query.
+ */
+bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod);
+
+/**
+ * qkbd_state_lift_all_keys: lift all pressed keys.
+ *
+ * This sends key up events to the guest for all keys which are in
+ * down state.
+ *
+ * @kbd: state tracker state.
+ */
+void qkbd_state_lift_all_keys(QKbdState *kbd);
+
+#endif /* QEMU_UI_KBD_STATE_H */
diff --git a/ui/kbd-state.c b/ui/kbd-state.c
new file mode 100644
index 0000000000..ac14add70e
--- /dev/null
+++ b/ui/kbd-state.c
@@ -0,0 +1,130 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/bitmap.h"
+#include "qemu/queue.h"
+#include "ui/console.h"
+#include "ui/input.h"
+#include "ui/kbd-state.h"
+
+struct QKbdState {
+ QemuConsole *con;
+ int key_delay_ms;
+ DECLARE_BITMAP(keys, Q_KEY_CODE__MAX);
+ DECLARE_BITMAP(mods, QKBD_MOD__MAX);
+};
+
+static void qkbd_state_modifier_update(QKbdState *kbd,
+ QKeyCode qcode1, QKeyCode qcode2,
+ QKbdModifier mod)
+{
+ if (test_bit(qcode1, kbd->keys) || test_bit(qcode2, kbd->keys)) {
+ set_bit(mod, kbd->mods);
+ } else {
+ clear_bit(mod, kbd->mods);
+ }
+}
+
+bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod)
+{
+ return test_bit(mod, kbd->mods);
+}
+
+bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode)
+{
+ return test_bit(qcode, kbd->keys);
+}
+
+void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down)
+{
+ bool state = test_bit(qcode, kbd->keys);
+
+ if (state == down) {
+ /*
+ * Filter out events which don't change the keyboard state.
+ *
+ * Most notably this allows to simply send along all key-up
+ * events, and this function will filter out everything where
+ * the corresponding key-down event wasn't send to the guest,
+ * for example due to being a host hotkey.
+ */
+ return;
+ }
+
+ /* update key and modifier state */
+ change_bit(qcode, kbd->keys);
+ switch (qcode) {
+ case Q_KEY_CODE_SHIFT:
+ case Q_KEY_CODE_SHIFT_R:
+ qkbd_state_modifier_update(kbd, Q_KEY_CODE_SHIFT, Q_KEY_CODE_SHIFT_R,
+ QKBD_MOD_SHIFT);
+ break;
+ case Q_KEY_CODE_CTRL:
+ case Q_KEY_CODE_CTRL_R:
+ qkbd_state_modifier_update(kbd, Q_KEY_CODE_CTRL, Q_KEY_CODE_CTRL_R,
+ QKBD_MOD_CTRL);
+ break;
+ case Q_KEY_CODE_ALT:
+ qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT, Q_KEY_CODE_ALT,
+ QKBD_MOD_ALT);
+ break;
+ case Q_KEY_CODE_ALT_R:
+ qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT_R, Q_KEY_CODE_ALT_R,
+ QKBD_MOD_ALTGR);
+ break;
+ case Q_KEY_CODE_CAPS_LOCK:
+ if (down) {
+ change_bit(QKBD_MOD_CAPSLOCK, kbd->mods);
+ }
+ break;
+ case Q_KEY_CODE_NUM_LOCK:
+ if (down) {
+ change_bit(QKBD_MOD_NUMLOCK, kbd->mods);
+ }
+ break;
+ default:
+ /* keep gcc happy */
+ break;
+ }
+
+ /* send to guest */
+ if (qemu_console_is_graphic(kbd->con)) {
+ qemu_input_event_send_key_qcode(kbd->con, qcode, down);
+ if (kbd->key_delay_ms) {
+ qemu_input_event_send_key_delay(kbd->key_delay_ms);
+ }
+ }
+}
+
+void qkbd_state_lift_all_keys(QKbdState *kbd)
+{
+ int qcode;
+
+ for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) {
+ if (test_bit(qcode, kbd->keys)) {
+ qkbd_state_key_event(kbd, qcode, false);
+ }
+ }
+}
+
+void qkbd_state_set_delay(QKbdState *kbd, int delay_ms)
+{
+ kbd->key_delay_ms = delay_ms;
+}
+
+void qkbd_state_free(QKbdState *kbd)
+{
+ g_free(kbd);
+}
+
+QKbdState *qkbd_state_init(QemuConsole *con)
+{
+ QKbdState *kbd = g_new0(QKbdState, 1);
+
+ kbd->con = con;
+
+ return kbd;
+}
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 9b6f0c6b67..7f8b3da791 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -8,7 +8,7 @@ vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
-common-obj-y += input.o input-keymap.o input-legacy.o
+common-obj-y += input.o input-keymap.o input-legacy.o kbd-state.o
common-obj-$(CONFIG_LINUX) += input-linux.o
common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
common-obj-$(CONFIG_COCOA) += cocoa.o
--
2.9.3
next prev parent reply other threads:[~2019-02-05 10:58 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-02-05 10:57 [Qemu-devel] [PULL 00/14] Ui 20190205 patches Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 01/14] Remove deprecated -no-frame option Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 02/14] ui: don't send any event if delta_y == 0 Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 03/14] ui: listen for GDK_SMOOTH_SCROLL events Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 04/14] ui/sdl_keysym: Remove obsolete SDL1.2 related code Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 05/14] ui/cocoa.m: Fix macOS 10.14 deprecation warnings Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 06/14] ui/egl-helpers: Augment parameter list of egl_texture_blend() to convey scales of viewport Gerd Hoffmann
2019-02-05 10:57 ` Gerd Hoffmann [this message]
2019-02-05 10:57 ` [Qemu-devel] [PULL 08/14] sdl2: remove sdl2_reset_keys() function Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 09/14] kbd-state: use state tracker for sdl2 Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 10/14] sdl2: use only QKeyCode in sdl2_process_key() Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 11/14] kbd-state: use state tracker for gtk Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 12/14] kbd-state: use state tracker for vnc Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 13/14] keymap: pass full keyboard state to keysym2scancode Gerd Hoffmann
2019-02-05 10:57 ` [Qemu-devel] [PULL 14/14] keymap: fix keyup mappings Gerd Hoffmann
2019-02-05 16:51 ` [Qemu-devel] [PULL 00/14] Ui 20190205 patches Peter Maydell
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=20190205105758.4230-8-kraxel@redhat.com \
--to=kraxel@redhat.com \
--cc=libvir-list@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.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).