qemu/qemu-keymap.c
Alex Bennée 5812717658 qemu-keymap: properly check return from xkb_keymap_mod_get_index
We can return XKB_MOD_INVALID for AltGr which rightly gets flagged by
sanitisers as an overly wide shift attempt. Properly check the return
type and leave the bitmap as zero in that case. Tested output before
and after is unchanged with the gb and ara keymaps.

Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-Id: <20230630180423.558337-7-alex.bennee@linaro.org>
2023-07-03 12:51:21 +01:00

268 lines
7.6 KiB
C

/*
* QEMU keymap utility
*
* Copyright Red Hat, Inc. 2017
*
* Authors:
* Gerd Hoffmann <kraxel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/notify.h"
#include "ui/input.h"
#include <xkbcommon/xkbcommon.h>
struct xkb_rule_names names = {
.rules = NULL,
.model = "pc105",
.layout = "us",
.variant = NULL,
.options = NULL,
};
static xkb_mod_mask_t shift;
static xkb_mod_mask_t ctrl;
static xkb_mod_mask_t altgr;
static xkb_mod_mask_t numlock;
static FILE *outfile;
/* ------------------------------------------------------------------------ */
static uint32_t qcode_to_number(uint32_t qcode)
{
KeyValue keyvalue;
uint32_t number;
keyvalue.type = KEY_VALUE_KIND_QCODE;
keyvalue.u.qcode.data = qcode;
number = qemu_input_key_value_to_number(&keyvalue);
assert(number != 0);
return number;
}
static void print_sym(xkb_keysym_t sym, uint32_t qcode, const char *mod)
{
char name[64];
if (sym == XKB_KEY_NoSymbol) {
return;
}
xkb_keysym_get_name(sym, name, sizeof(name));
/* TODO: make ui/keymap.c parser accept QKeyCode names */
fprintf(outfile, "%s 0x%02x%s\n", name, qcode_to_number(qcode), mod);
}
static void walk_map(struct xkb_keymap *map, xkb_keycode_t code, void *data)
{
struct xkb_state *state = data;
xkb_keysym_t kbase, knumlock, kshift, kaltgr, kaltgrshift;
uint32_t evdev, qcode;
char name[64];
fprintf(outfile, "\n");
/*
* map xkb keycode -> QKeyCode
*
* xkb keycode is linux evdev shifted by 8
*/
evdev = code - 8;
qcode = qemu_input_linux_to_qcode(evdev);
if (qcode == Q_KEY_CODE_UNMAPPED) {
xkb_state_update_mask(state, 0, 0, 0, 0, 0, 0);
kbase = xkb_state_key_get_one_sym(state, code);
xkb_keysym_get_name(kbase, name, sizeof(name));
fprintf(outfile, "# evdev %d (0x%x): no evdev -> QKeyCode mapping"
" (xkb keysym %s)\n", evdev, evdev, name);
return;
}
fprintf(outfile, "# evdev %d (0x%x), QKeyCode \"%s\", number 0x%x\n",
evdev, evdev,
QKeyCode_str(qcode),
qcode_to_number(qcode));
/*
* check which modifier states generate which keysyms
*/
xkb_state_update_mask(state, 0, 0, 0, 0, 0, 0);
kbase = xkb_state_key_get_one_sym(state, code);
print_sym(kbase, qcode, "");
xkb_state_update_mask(state, 0, 0, numlock, 0, 0, 0);
knumlock = xkb_state_key_get_one_sym(state, code);
if (kbase != knumlock) {
print_sym(knumlock, qcode, " numlock");
}
xkb_state_update_mask(state, shift, 0, 0, 0, 0, 0);
kshift = xkb_state_key_get_one_sym(state, code);
if (kbase != kshift && knumlock != kshift) {
print_sym(kshift, qcode, " shift");
}
xkb_state_update_mask(state, altgr, 0, 0, 0, 0, 0);
kaltgr = xkb_state_key_get_one_sym(state, code);
if (kbase != kaltgr) {
print_sym(kaltgr, qcode, " altgr");
}
xkb_state_update_mask(state, altgr | shift, 0, 0, 0, 0, 0);
kaltgrshift = xkb_state_key_get_one_sym(state, code);
if (kshift != kaltgrshift && kaltgr != kaltgrshift) {
print_sym(kaltgrshift, qcode, " shift altgr");
}
return;
}
static void usage(FILE *out)
{
fprintf(out,
"\n"
"This tool generates qemu reverse keymaps from xkb keymaps,\n"
"which can be used with the qemu \"-k\" command line switch.\n"
"\n"
"usage: qemu-keymap <options>\n"
"options:\n"
" -h print this text\n"
" -f <file> set output file (default: stdout)\n"
" -m <model> set kbd model (default: %s)\n"
" -l <layout> set kbd layout (default: %s)\n"
" -v <variant> set kbd variant (default: %s)\n"
" -o <options> set kbd options (default: %s)\n"
"\n",
names.model, names.layout,
names.variant ?: "-",
names.options ?: "-");
}
static xkb_mod_mask_t get_mod(struct xkb_keymap *map, const char *name)
{
xkb_mod_index_t mod;
xkb_mod_mask_t mask = 0;
mod = xkb_keymap_mod_get_index(map, name);
if (mod != XKB_MOD_INVALID) {
mask = (1 << mod);
}
return mask;
}
int main(int argc, char *argv[])
{
struct xkb_context *ctx;
struct xkb_keymap *map;
struct xkb_state *state;
xkb_mod_index_t mod, mods;
int rc;
for (;;) {
rc = getopt(argc, argv, "hm:l:v:o:f:");
if (rc == -1) {
break;
}
switch (rc) {
case 'm':
names.model = optarg;
break;
case 'l':
names.layout = optarg;
break;
case 'v':
names.variant = optarg;
break;
case 'o':
names.options = optarg;
break;
case 'f':
outfile = fopen(optarg, "w");
if (outfile == NULL) {
fprintf(stderr, "open %s: %s\n", optarg, strerror(errno));
exit(1);
}
break;
case 'h':
usage(stdout);
exit(0);
default:
usage(stderr);
exit(1);
}
}
if (outfile == NULL) {
outfile = stdout;
}
fprintf(outfile,
"# SPDX-License-Identifier: GPL-2.0-or-later\n"
"#\n"
"# generated by qemu-keymap\n"
"# model : %s\n"
"# layout : %s\n"
"# variant : %s\n"
"# options : %s\n"
"\n",
names.model, names.layout,
names.variant ?: "-",
names.options ?: "-");
ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
map = xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!map) {
/* libxkbcommon prints error */
exit(1);
}
fprintf(outfile, "# name: \"%s\"\n\n",
xkb_keymap_layout_get_name(map, 0));
fprintf(outfile, "# modifiers\n");
mods = xkb_keymap_num_mods(map);
for (mod = 0; mod < mods; mod++) {
fprintf(outfile, "# %2d: %s\n",
mod, xkb_keymap_mod_get_name(map, mod));
}
shift = get_mod(map, "Shift");
ctrl = get_mod(map, "Control");
altgr = get_mod(map, "AltGr");
numlock = get_mod(map, "NumLock");
state = xkb_state_new(map);
xkb_keymap_key_for_each(map, walk_map, state);
xkb_state_unref(state);
state = NULL;
/* add quirks */
fprintf(outfile,
"\n"
"#\n"
"# quirks section start\n"
"#\n"
"# Sometimes multiple keysyms map to the same keycodes.\n"
"# The keycode -> keysym lookup finds only one of the\n"
"# keysyms. So append them here.\n"
"#\n"
"\n");
print_sym(XKB_KEY_Print, Q_KEY_CODE_SYSRQ, "");
print_sym(XKB_KEY_Sys_Req, Q_KEY_CODE_SYSRQ, "");
print_sym(XKB_KEY_Execute, Q_KEY_CODE_SYSRQ, "");
print_sym(XKB_KEY_KP_Decimal, Q_KEY_CODE_KP_DECIMAL, " numlock");
print_sym(XKB_KEY_KP_Separator, Q_KEY_CODE_KP_DECIMAL, " numlock");
print_sym(XKB_KEY_Alt_R, Q_KEY_CODE_ALT_R, "");
print_sym(XKB_KEY_ISO_Level3_Shift, Q_KEY_CODE_ALT_R, "");
print_sym(XKB_KEY_Mode_switch, Q_KEY_CODE_ALT_R, "");
fprintf(outfile,
"\n"
"# quirks section end\n");
exit(0);
}