qemu/ui/kbd-state.c
Gerd Hoffmann 47ddfab18a kbd-state: add keyboard state tracker
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>
2019-02-05 10:45:44 +01:00

131 lines
3.4 KiB
C

/*
* 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;
}