input: add event routing and multiseat support.
input: misc bugfixes and minor improvements. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJTguLNAAoJEEy22O7T6HE4d3MP/2+JYwz9ZiCuzBTriUDvMGpg ujCT5bk2Dcd3wHlrFfRX2saRxskcWstWrOuy6IwygSSpPRlqF67gLUqgByjAwFwJ g/JA4BGKrsFsI20XWkzB55e4FmbA+eixBEnDnuUHwvJebS1aCHNrb352E9nWFMne opsbpkCFkZULMXsqnELgsldqTaW0huSgdOFa0WsCPWi9rMdJL2SJjDvLgUlP4YVB v22AYSpZBd4TB+pSRxiUb2f4fVuAyaV5rCubJDKGLaKUuPZf3+2x664XuqYZ8RvI sOi6r2viamy7NuD9C6YOz2hwqeFj6A15viBo8KZmotAHB6/hi/3I0rLAQsRgxsGh zxNRHtOkF6n237KMWk7wh7SqGXtCr7mCe750bFru1W3FqlGlODAPYgIFODz1L50G 51nlrXroFMkpM0FsLARtaeMoKo4k8e1o+08R9wVwkyNz+sPwlW7yoQKOlyWfmeKe GTTvRJWmggF3pqf6LEIQrgOQyBuArrX4SR1AeRzhYmw/H5h+Df7oeHAlv2Yx6Sus mAit/ExI9HeTE2BwyXCzu99LFAmogRvvB+45dbwC9INf537p9hdyKCyrFU1uQofw lw3cCfyNZu0BQR0YUpR2NCaFfWWkPQehi19kpKOpyiKscYy7RNJr10vaz9FuFppv x9nOKTuiaAMGIyJwWfmU =oPCg -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/pull-input-9' into staging input: add event routing and multiseat support. input: misc bugfixes and minor improvements. # gpg: Signature made Mon 26 May 2014 07:44:29 BST using RSA key ID D3E87138 # gpg: Can't check signature: public key not found * remotes/kraxel/tags/pull-input-9: docs: add multiseat.txt usb: add input routing support for tablet and keyboard sdl: pass key event source to input layer input: bind devices and input routing input: switch hid mouse and tablet to the new input layer api. input: switch hid keyboard to new input layer api. input: keymap: add meta keys input: add name to input_event_key_number input: add qemu_input_key_number_to_qcode input (curses): mask keycodes to remove modifier bits Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
4aa23452e3
76
docs/multiseat.txt
Normal file
76
docs/multiseat.txt
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
multiseat howto (with some multihead coverage)
|
||||
==============================================
|
||||
|
||||
host side
|
||||
---------
|
||||
|
||||
First you must compile qemu with a user interface supporting
|
||||
multihead/multiseat and input event routing. Right now this list is
|
||||
pretty short: sdl2.
|
||||
|
||||
./configure --enable-sdl --with-sdlabi=2.0
|
||||
|
||||
|
||||
Next put together the qemu command line:
|
||||
|
||||
qemu -enable-kvm -usb $memory $disk $whatever \
|
||||
-display sdl \
|
||||
-vga std \
|
||||
-device usb-tablet
|
||||
|
||||
That is it for the first head, which will use the standard vga, the
|
||||
standard ps/2 keyboard (implicitly there) and the usb-tablet. Now the
|
||||
additional switches for the second head:
|
||||
|
||||
-device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \
|
||||
-device secondary-vga,bus=head.2,addr=02.0,id=video.2 \
|
||||
-device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \
|
||||
-device usb-kbd,bus=usb.2.0,port=1,display=video.2 \
|
||||
-device usb-tablet,bus=usb.2.0,port=2,display=video.2
|
||||
|
||||
This places a pci bridge in slot 12, connects a display adapter and
|
||||
xhci (usb) controller to the bridge. Then it adds a usb keyboard and
|
||||
usb mouse, both connected to the xhci and linked to the display.
|
||||
|
||||
The "display=video2" sets up the input routing. Any input coming from
|
||||
the window which belongs to the video.2 display adapter will be routed
|
||||
to these input devices.
|
||||
|
||||
|
||||
guest side
|
||||
----------
|
||||
|
||||
You need a pretty recent linux guest. systemd with loginctl. kernel
|
||||
3.14+ with CONFIG_DRM_BOCHS enabled. Fedora 20 will do. Must be
|
||||
fully updated for the new kernel though, i.e. the live iso doesn't cut
|
||||
it.
|
||||
|
||||
Now we'll have to configure the guest. Boot and login. By default
|
||||
all devices belong to seat0. You can use "loginctl seat-status seat0"
|
||||
to list them all (and to get the sysfs paths for cut+paste). Now
|
||||
we'll go assign all pci devices connected the pci bridge in slot 12 to
|
||||
a new head:
|
||||
|
||||
loginctl attach seat-qemu \
|
||||
/sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1
|
||||
loginctl attach seat-qemu \
|
||||
/sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1
|
||||
loginctl attach seat-qemu \
|
||||
/sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2
|
||||
|
||||
Use "loginctl seat-status seat-qemu" to check the result. It isn't
|
||||
needed to assign the usb devices to the head individually, assigning a
|
||||
usb (root) hub will automatically assign all usb devices connected to
|
||||
it too.
|
||||
|
||||
BTW: loginctl writes udev rules to /etc/udev/rules.d to make these
|
||||
device assignments permanent, so you need to do this only once.
|
||||
|
||||
Now simply restart gdm (rebooting will do too), and a login screen
|
||||
should show up on the second head.
|
||||
|
||||
Enjoy!
|
||||
|
||||
--
|
||||
Gerd Hoffmann <kraxel@redhat.com>
|
222
hw/input/hid.c
222
hw/input/hid.c
@ -105,70 +105,135 @@ void hid_set_next_idle(HIDState *hs)
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
|
||||
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
|
||||
InputEvent *evt)
|
||||
{
|
||||
e->xdx = e->ydy = e->dz = 0;
|
||||
e->buttons_state = buttons;
|
||||
static const int bmap[INPUT_BUTTON_MAX] = {
|
||||
[INPUT_BUTTON_LEFT] = 0x01,
|
||||
[INPUT_BUTTON_RIGHT] = 0x02,
|
||||
[INPUT_BUTTON_MIDDLE] = 0x04,
|
||||
};
|
||||
HIDState *hs = (HIDState *)dev;
|
||||
HIDPointerEvent *e;
|
||||
|
||||
assert(hs->n < QUEUE_LENGTH);
|
||||
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
||||
|
||||
switch (evt->kind) {
|
||||
case INPUT_EVENT_KIND_REL:
|
||||
if (evt->rel->axis == INPUT_AXIS_X) {
|
||||
e->xdx += evt->rel->value;
|
||||
} else if (evt->rel->axis == INPUT_AXIS_Y) {
|
||||
e->ydy -= evt->rel->value;
|
||||
}
|
||||
break;
|
||||
|
||||
case INPUT_EVENT_KIND_ABS:
|
||||
if (evt->rel->axis == INPUT_AXIS_X) {
|
||||
e->xdx = evt->rel->value;
|
||||
} else if (evt->rel->axis == INPUT_AXIS_Y) {
|
||||
e->ydy = evt->rel->value;
|
||||
}
|
||||
break;
|
||||
|
||||
case INPUT_EVENT_KIND_BTN:
|
||||
if (evt->btn->down) {
|
||||
e->buttons_state |= bmap[evt->btn->button];
|
||||
if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||
e->dz--;
|
||||
} else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||
e->dz++;
|
||||
}
|
||||
} else {
|
||||
e->buttons_state &= ~bmap[evt->btn->button];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* keep gcc happy */
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
|
||||
int x1, int y1, int z1) {
|
||||
if (xyrel) {
|
||||
e->xdx += x1;
|
||||
e->ydy += y1;
|
||||
} else {
|
||||
e->xdx = x1;
|
||||
e->ydy = y1;
|
||||
/* Windows drivers do not like the 0/0 position and ignore such
|
||||
* events. */
|
||||
if (!(x1 | y1)) {
|
||||
e->xdx = 1;
|
||||
static void hid_pointer_sync(DeviceState *dev)
|
||||
{
|
||||
HIDState *hs = (HIDState *)dev;
|
||||
HIDPointerEvent *prev, *curr, *next;
|
||||
bool event_compression = false;
|
||||
|
||||
if (hs->n == QUEUE_LENGTH-1) {
|
||||
/*
|
||||
* Queue full. We are loosing information, but we at least
|
||||
* keep track of most recent button state.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
|
||||
curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
||||
next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
|
||||
|
||||
if (hs->n > 0) {
|
||||
/*
|
||||
* No button state change between previous and current event
|
||||
* (and previous wasn't seen by the guest yet), so there is
|
||||
* motion information only and we can combine the two event
|
||||
* into one.
|
||||
*/
|
||||
if (curr->buttons_state == prev->buttons_state) {
|
||||
event_compression = true;
|
||||
}
|
||||
}
|
||||
e->dz += z1;
|
||||
}
|
||||
|
||||
static void hid_pointer_event(void *opaque,
|
||||
int x1, int y1, int z1, int buttons_state)
|
||||
{
|
||||
HIDState *hs = opaque;
|
||||
unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
|
||||
unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
|
||||
|
||||
/* We combine events where feasible to keep the queue small. We shouldn't
|
||||
* combine anything with the first event of a particular button state, as
|
||||
* that would change the location of the button state change. When the
|
||||
* queue is empty, a second event is needed because we don't know if
|
||||
* the first event changed the button state. */
|
||||
if (hs->n == QUEUE_LENGTH) {
|
||||
/* Queue full. Discard old button state, combine motion normally. */
|
||||
hs->ptr.queue[use_slot].buttons_state = buttons_state;
|
||||
} else if (hs->n < 2 ||
|
||||
hs->ptr.queue[use_slot].buttons_state != buttons_state ||
|
||||
hs->ptr.queue[previous_slot].buttons_state !=
|
||||
hs->ptr.queue[use_slot].buttons_state) {
|
||||
/* Cannot or should not combine, so add an empty item to the queue. */
|
||||
QUEUE_INCR(use_slot);
|
||||
if (event_compression) {
|
||||
/* add current motion to previous, clear current */
|
||||
if (hs->kind == HID_MOUSE) {
|
||||
prev->xdx += curr->xdx;
|
||||
curr->xdx = 0;
|
||||
prev->ydy -= curr->ydy;
|
||||
curr->ydy = 0;
|
||||
} else {
|
||||
prev->xdx = curr->xdx;
|
||||
prev->ydy = curr->ydy;
|
||||
}
|
||||
prev->dz += curr->dz;
|
||||
curr->dz = 0;
|
||||
} else {
|
||||
/* prepate next (clear rel, copy abs + btns) */
|
||||
if (hs->kind == HID_MOUSE) {
|
||||
next->xdx = 0;
|
||||
next->ydy = 0;
|
||||
} else {
|
||||
next->xdx = curr->xdx;
|
||||
next->ydy = curr->ydy;
|
||||
}
|
||||
next->dz = 0;
|
||||
next->buttons_state = curr->buttons_state;
|
||||
/* make current guest visible, notify guest */
|
||||
hs->n++;
|
||||
hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
|
||||
hs->event(hs);
|
||||
}
|
||||
hid_pointer_event_combine(&hs->ptr.queue[use_slot],
|
||||
hs->kind == HID_MOUSE,
|
||||
x1, y1, z1);
|
||||
hs->event(hs);
|
||||
}
|
||||
|
||||
static void hid_keyboard_event(void *opaque, int keycode)
|
||||
static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
|
||||
InputEvent *evt)
|
||||
{
|
||||
HIDState *hs = opaque;
|
||||
HIDState *hs = (HIDState *)dev;
|
||||
int scancodes[3], i, count;
|
||||
int slot;
|
||||
|
||||
if (hs->n == QUEUE_LENGTH) {
|
||||
count = qemu_input_key_value_to_scancode(evt->key->key,
|
||||
evt->key->down,
|
||||
scancodes);
|
||||
if (hs->n + count > QUEUE_LENGTH) {
|
||||
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
||||
return;
|
||||
}
|
||||
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
|
||||
hs->kbd.keycodes[slot] = keycode;
|
||||
for (i = 0; i < count; i++) {
|
||||
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
|
||||
hs->kbd.keycodes[slot] = scancodes[i];
|
||||
}
|
||||
hs->event(hs);
|
||||
}
|
||||
|
||||
@ -247,14 +312,14 @@ static inline int int_clamp(int val, int vmin, int vmax)
|
||||
void hid_pointer_activate(HIDState *hs)
|
||||
{
|
||||
if (!hs->ptr.mouse_grabbed) {
|
||||
qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
|
||||
qemu_input_handler_activate(hs->s);
|
||||
hs->ptr.mouse_grabbed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
||||
{
|
||||
int dx, dy, dz, b, l;
|
||||
int dx, dy, dz, l;
|
||||
int index;
|
||||
HIDPointerEvent *e;
|
||||
|
||||
@ -279,17 +344,6 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
||||
dz = int_clamp(e->dz, -127, 127);
|
||||
e->dz -= dz;
|
||||
|
||||
b = 0;
|
||||
if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
|
||||
b |= 0x01;
|
||||
}
|
||||
if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
|
||||
b |= 0x02;
|
||||
}
|
||||
if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
|
||||
b |= 0x04;
|
||||
}
|
||||
|
||||
if (hs->n &&
|
||||
!e->dz &&
|
||||
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
|
||||
@ -304,7 +358,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
||||
switch (hs->kind) {
|
||||
case HID_MOUSE:
|
||||
if (len > l) {
|
||||
buf[l++] = b;
|
||||
buf[l++] = e->buttons_state;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dx;
|
||||
@ -319,7 +373,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
||||
|
||||
case HID_TABLET:
|
||||
if (len > l) {
|
||||
buf[l++] = b;
|
||||
buf[l++] = e->buttons_state;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dx & 0xff;
|
||||
@ -413,31 +467,45 @@ void hid_reset(HIDState *hs)
|
||||
|
||||
void hid_free(HIDState *hs)
|
||||
{
|
||||
switch (hs->kind) {
|
||||
case HID_KEYBOARD:
|
||||
qemu_remove_kbd_event_handler(hs->kbd.eh_entry);
|
||||
break;
|
||||
case HID_MOUSE:
|
||||
case HID_TABLET:
|
||||
qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
|
||||
break;
|
||||
}
|
||||
qemu_input_handler_unregister(hs->s);
|
||||
hid_del_idle_timer(hs);
|
||||
}
|
||||
|
||||
static QemuInputHandler hid_keyboard_handler = {
|
||||
.name = "QEMU HID Keyboard",
|
||||
.mask = INPUT_EVENT_MASK_KEY,
|
||||
.event = hid_keyboard_event,
|
||||
};
|
||||
|
||||
static QemuInputHandler hid_mouse_handler = {
|
||||
.name = "QEMU HID Mouse",
|
||||
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
|
||||
.event = hid_pointer_event,
|
||||
.sync = hid_pointer_sync,
|
||||
};
|
||||
|
||||
static QemuInputHandler hid_tablet_handler = {
|
||||
.name = "QEMU HID Tablet",
|
||||
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
|
||||
.event = hid_pointer_event,
|
||||
.sync = hid_pointer_sync,
|
||||
};
|
||||
|
||||
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
|
||||
{
|
||||
hs->kind = kind;
|
||||
hs->event = event;
|
||||
|
||||
if (hs->kind == HID_KEYBOARD) {
|
||||
hs->kbd.eh_entry = qemu_add_kbd_event_handler(hid_keyboard_event, hs);
|
||||
hs->s = qemu_input_handler_register((DeviceState *)hs,
|
||||
&hid_keyboard_handler);
|
||||
qemu_input_handler_activate(hs->s);
|
||||
} else if (hs->kind == HID_MOUSE) {
|
||||
hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
|
||||
0, "QEMU HID Mouse");
|
||||
hs->s = qemu_input_handler_register((DeviceState *)hs,
|
||||
&hid_mouse_handler);
|
||||
} else if (hs->kind == HID_TABLET) {
|
||||
hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
|
||||
1, "QEMU HID Tablet");
|
||||
hs->s = qemu_input_handler_register((DeviceState *)hs,
|
||||
&hid_tablet_handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,8 @@ typedef struct USBHIDState {
|
||||
USBEndpoint *intr;
|
||||
HIDState hid;
|
||||
uint32_t usb_version;
|
||||
char *display;
|
||||
uint32_t head;
|
||||
} USBHIDState;
|
||||
|
||||
enum {
|
||||
@ -574,6 +576,9 @@ static int usb_hid_initfn(USBDevice *dev, int kind)
|
||||
usb_desc_init(dev);
|
||||
us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||
hid_init(&us->hid, kind, usb_hid_changed);
|
||||
if (us->display && us->hid.s) {
|
||||
qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -653,6 +658,8 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
|
||||
|
||||
static Property usb_tablet_properties[] = {
|
||||
DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
|
||||
DEFINE_PROP_STRING("display", USBHIDState, display),
|
||||
DEFINE_PROP_UINT32("head", USBHIDState, head, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -696,6 +703,11 @@ static const TypeInfo usb_mouse_info = {
|
||||
.class_init = usb_mouse_class_initfn,
|
||||
};
|
||||
|
||||
static Property usb_keyboard_properties[] = {
|
||||
DEFINE_PROP_STRING("display", USBHIDState, display),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@ -706,6 +718,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
|
||||
uc->product_desc = "QEMU USB Keyboard";
|
||||
uc->usb_desc = &desc_keyboard;
|
||||
dc->vmsd = &vmstate_usb_kbd;
|
||||
dc->props = usb_keyboard_properties;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define QEMU_HID_H
|
||||
|
||||
#include "migration/vmstate.h"
|
||||
#include "ui/input.h"
|
||||
|
||||
#define HID_MOUSE 1
|
||||
#define HID_TABLET 2
|
||||
@ -22,7 +23,6 @@ typedef void (*HIDEventFunc)(HIDState *s);
|
||||
typedef struct HIDMouseState {
|
||||
HIDPointerEvent queue[QUEUE_LENGTH];
|
||||
int mouse_grabbed;
|
||||
QEMUPutMouseEntry *eh_entry;
|
||||
} HIDMouseState;
|
||||
|
||||
typedef struct HIDKeyboardState {
|
||||
@ -31,7 +31,6 @@ typedef struct HIDKeyboardState {
|
||||
uint8_t leds;
|
||||
uint8_t key[16];
|
||||
int32_t keys;
|
||||
QEMUPutKbdEntry *eh_entry;
|
||||
} HIDKeyboardState;
|
||||
|
||||
struct HIDState {
|
||||
@ -47,6 +46,7 @@ struct HIDState {
|
||||
bool idle_pending;
|
||||
QEMUTimer *idle_timer;
|
||||
HIDEventFunc event;
|
||||
QemuInputHandlerState *s;
|
||||
};
|
||||
|
||||
void hid_init(HIDState *hs, int kind, HIDEventFunc event);
|
||||
|
@ -29,6 +29,9 @@ QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
|
||||
void qemu_input_handler_activate(QemuInputHandlerState *s);
|
||||
void qemu_input_handler_deactivate(QemuInputHandlerState *s);
|
||||
void qemu_input_handler_unregister(QemuInputHandlerState *s);
|
||||
void qemu_input_handler_bind(QemuInputHandlerState *s,
|
||||
const char *device_id, int head,
|
||||
Error **errp);
|
||||
void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
|
||||
void qemu_input_event_sync(void);
|
||||
|
||||
@ -36,6 +39,7 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down);
|
||||
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
|
||||
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
|
||||
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
|
||||
int qemu_input_key_number_to_qcode(uint8_t nr);
|
||||
int qemu_input_key_value_to_number(const KeyValue *value);
|
||||
int qemu_input_key_value_to_qcode(const KeyValue *value);
|
||||
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
|
||||
|
@ -1047,7 +1047,7 @@ gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
|
||||
gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)"
|
||||
|
||||
# ui/input.c
|
||||
input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d"
|
||||
input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d"
|
||||
input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d"
|
||||
input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d"
|
||||
input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d"
|
||||
|
@ -288,8 +288,8 @@ static void curses_refresh(DisplayChangeListener *dcl)
|
||||
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
|
||||
}
|
||||
|
||||
qemu_input_event_send_key_number(NULL, keycode, true);
|
||||
qemu_input_event_send_key_number(NULL, keycode, false);
|
||||
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
|
||||
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
|
||||
|
||||
if (keycode & ALTGR) {
|
||||
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
|
||||
|
@ -13,6 +13,8 @@ static const int qcode_to_number[] = {
|
||||
[Q_KEY_CODE_CTRL] = 0x1d,
|
||||
[Q_KEY_CODE_CTRL_R] = 0x9d,
|
||||
|
||||
[Q_KEY_CODE_META_L] = 0xdb,
|
||||
[Q_KEY_CODE_META_R] = 0xdc,
|
||||
[Q_KEY_CODE_MENU] = 0xdd,
|
||||
|
||||
[Q_KEY_CODE_ESC] = 0x01,
|
||||
@ -129,7 +131,7 @@ static const int qcode_to_number[] = {
|
||||
[Q_KEY_CODE_MAX] = 0,
|
||||
};
|
||||
|
||||
static int number_to_qcode[0xff];
|
||||
static int number_to_qcode[0x100];
|
||||
|
||||
int qemu_input_key_value_to_number(const KeyValue *value)
|
||||
{
|
||||
@ -141,7 +143,7 @@ int qemu_input_key_value_to_number(const KeyValue *value)
|
||||
}
|
||||
}
|
||||
|
||||
int qemu_input_key_value_to_qcode(const KeyValue *value)
|
||||
int qemu_input_key_number_to_qcode(uint8_t nr)
|
||||
{
|
||||
static int first = true;
|
||||
|
||||
@ -155,11 +157,16 @@ int qemu_input_key_value_to_qcode(const KeyValue *value)
|
||||
}
|
||||
}
|
||||
|
||||
return number_to_qcode[nr];
|
||||
}
|
||||
|
||||
int qemu_input_key_value_to_qcode(const KeyValue *value)
|
||||
{
|
||||
if (value->kind == KEY_VALUE_KIND_QCODE) {
|
||||
return value->qcode;
|
||||
} else {
|
||||
assert(value->kind == KEY_VALUE_KIND_NUMBER);
|
||||
return number_to_qcode[value->number];
|
||||
return qemu_input_key_number_to_qcode(value->number);
|
||||
}
|
||||
}
|
||||
|
||||
|
49
ui/input.c
49
ui/input.c
@ -1,3 +1,4 @@
|
||||
#include "hw/qdev.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qapi-types.h"
|
||||
#include "qmp-commands.h"
|
||||
@ -10,6 +11,7 @@ struct QemuInputHandlerState {
|
||||
QemuInputHandler *handler;
|
||||
int id;
|
||||
int events;
|
||||
QemuConsole *con;
|
||||
QTAILQ_ENTRY(QemuInputHandlerState) node;
|
||||
};
|
||||
static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
|
||||
@ -53,12 +55,46 @@ void qemu_input_handler_unregister(QemuInputHandlerState *s)
|
||||
qemu_input_check_mode_change();
|
||||
}
|
||||
|
||||
void qemu_input_handler_bind(QemuInputHandlerState *s,
|
||||
const char *device_id, int head,
|
||||
Error **errp)
|
||||
{
|
||||
DeviceState *dev;
|
||||
QemuConsole *con;
|
||||
|
||||
dev = qdev_find_recursive(sysbus_get_default(), device_id);
|
||||
if (dev == NULL) {
|
||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
con = qemu_console_lookup_by_device(dev, head);
|
||||
if (con == NULL) {
|
||||
error_setg(errp, "Device %s is not bound to a QemuConsole", device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
s->con = con;
|
||||
}
|
||||
|
||||
static QemuInputHandlerState*
|
||||
qemu_input_find_handler(uint32_t mask)
|
||||
qemu_input_find_handler(uint32_t mask, QemuConsole *con)
|
||||
{
|
||||
QemuInputHandlerState *s;
|
||||
|
||||
QTAILQ_FOREACH(s, &handlers, node) {
|
||||
if (s->con == NULL || s->con != con) {
|
||||
continue;
|
||||
}
|
||||
if (mask & s->handler->mask) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(s, &handlers, node) {
|
||||
if (s->con != NULL) {
|
||||
continue;
|
||||
}
|
||||
if (mask & s->handler->mask) {
|
||||
return s;
|
||||
}
|
||||
@ -94,7 +130,7 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt)
|
||||
static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
|
||||
{
|
||||
const char *name;
|
||||
int idx = -1;
|
||||
int qcode, idx = -1;
|
||||
|
||||
if (src) {
|
||||
idx = qemu_console_get_index(src);
|
||||
@ -103,8 +139,10 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
|
||||
case INPUT_EVENT_KIND_KEY:
|
||||
switch (evt->key->key->kind) {
|
||||
case KEY_VALUE_KIND_NUMBER:
|
||||
qcode = qemu_input_key_number_to_qcode(evt->key->key->number);
|
||||
name = QKeyCode_lookup[qcode];
|
||||
trace_input_event_key_number(idx, evt->key->key->number,
|
||||
evt->key->down);
|
||||
name, evt->key->down);
|
||||
break;
|
||||
case KEY_VALUE_KIND_QCODE:
|
||||
name = QKeyCode_lookup[evt->key->key->qcode];
|
||||
@ -149,7 +187,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
|
||||
}
|
||||
|
||||
/* send event */
|
||||
s = qemu_input_find_handler(1 << evt->kind);
|
||||
s = qemu_input_find_handler(1 << evt->kind, src);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@ -250,7 +288,8 @@ bool qemu_input_is_absolute(void)
|
||||
{
|
||||
QemuInputHandlerState *s;
|
||||
|
||||
s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS);
|
||||
s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS,
|
||||
NULL);
|
||||
return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
|
||||
}
|
||||
|
||||
|
21
ui/sdl2.c
21
ui/sdl2.c
@ -190,30 +190,33 @@ static void sdl_switch(DisplayChangeListener *dcl,
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_keys(void)
|
||||
static void reset_keys(struct sdl2_state *scon)
|
||||
{
|
||||
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (modifiers_state[i]) {
|
||||
int qcode = sdl2_scancode_to_qcode[i];
|
||||
qemu_input_event_send_key_qcode(NULL, qcode, false);
|
||||
qemu_input_event_send_key_qcode(con, qcode, false);
|
||||
modifiers_state[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl_process_key(SDL_KeyboardEvent *ev)
|
||||
static void sdl_process_key(struct sdl2_state *scon,
|
||||
SDL_KeyboardEvent *ev)
|
||||
{
|
||||
int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
|
||||
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||
|
||||
switch (ev->keysym.scancode) {
|
||||
#if 0
|
||||
case SDL_SCANCODE_NUMLOCKCLEAR:
|
||||
case SDL_SCANCODE_CAPSLOCK:
|
||||
/* SDL does not send the key up event, so we generate it */
|
||||
qemu_input_event_send_key_qcode(NULL, qcode, true);
|
||||
qemu_input_event_send_key_qcode(NULL, qcode, false);
|
||||
qemu_input_event_send_key_qcode(con, qcode, true);
|
||||
qemu_input_event_send_key_qcode(con, qcode, false);
|
||||
return;
|
||||
#endif
|
||||
case SDL_SCANCODE_LCTRL:
|
||||
@ -231,7 +234,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
|
||||
}
|
||||
/* fall though */
|
||||
default:
|
||||
qemu_input_event_send_key_qcode(NULL, qcode,
|
||||
qemu_input_event_send_key_qcode(con, qcode,
|
||||
ev->type == SDL_KEYDOWN);
|
||||
}
|
||||
}
|
||||
@ -506,7 +509,7 @@ static void handle_keydown(SDL_Event *ev)
|
||||
}
|
||||
}
|
||||
if (!gui_keysym) {
|
||||
sdl_process_key(&ev->key);
|
||||
sdl_process_key(scon, &ev->key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,13 +534,13 @@ static void handle_keyup(SDL_Event *ev)
|
||||
}
|
||||
/* SDL does not send back all the modifiers key, so we must
|
||||
* correct it. */
|
||||
reset_keys();
|
||||
reset_keys(scon);
|
||||
return;
|
||||
}
|
||||
gui_keysym = 0;
|
||||
}
|
||||
if (!gui_keysym) {
|
||||
sdl_process_key(&ev->key);
|
||||
sdl_process_key(scon, &ev->key);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user