Compare commits

..

3 Commits

Author SHA1 Message Date
Anthony PERARD
042ec47e68 xen-usb: Fix 32bit build
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Message-id: 20160623110829.22671-1-anthony.perard@citrix.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2016-07-12 10:47:03 +02:00
Gerd Hoffmann
b91e013982 usb: add storage hotplug documentation
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 1466667901-1341-1-git-send-email-kraxel@redhat.com
2016-07-12 10:25:30 +02:00
Zhang Shuaiyi
a4055d8586 nec-usb-xhci: set the device state to USB_STATE_DEFAULT
This patch is a rough fix to "hw/usb/core.c:401: usb_handle_packet:
 Assertion `dev->state == 3' failed.". Qemu will crash when a usb3
device redirect to Windows7 VM via nec-usb-xhci.

In extensible-host-controler-interface-usb-xhci.pdf P94(4.6.5
Address Device):
    • If the Block Set Address Request (BSR) flag = ‘1’
        • If the slot is in the Enabled state:
            ...
            • Set the Slot State in the Output Slot Context to Default.

BSR = ‘1’: Enabled state to Default state; BSR = ‘0’: Default state
to Addressed state. Try to call usb_device_reset to set device state
to USB_STATE_DEFAULT in xhci_address_slot wether bsr is zero.

Signed-off-by: Zhang Shuaiyi <zhang_syi@massclouds.com>
Message-id: 1467258640-11921-1-git-send-email-zhang_syi@massclouds.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2016-07-12 10:23:59 +02:00
8 changed files with 180 additions and 275 deletions

View File

@@ -25,51 +25,16 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "sysemu/char.h" #include "sysemu/char.h"
#include "ui/console.h" #include "ui/console.h"
#include "ui/input.h"
#define MSMOUSE_LO6(n) ((n) & 0x3f) #define MSMOUSE_LO6(n) ((n) & 0x3f)
#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
typedef struct { static void msmouse_event(void *opaque,
CharDriverState *chr; int dx, int dy, int dz, int buttons_state)
QemuInputHandlerState *hs;
int axis[INPUT_AXIS__MAX];
bool btns[INPUT_BUTTON__MAX];
bool btnc[INPUT_BUTTON__MAX];
uint8_t outbuf[32];
int outlen;
} MouseState;
static void msmouse_chr_accept_input(CharDriverState *chr)
{ {
MouseState *mouse = chr->opaque; CharDriverState *chr = (CharDriverState *)opaque;
int len;
len = qemu_chr_be_can_write(chr);
if (len > mouse->outlen) {
len = mouse->outlen;
}
if (!len) {
return;
}
qemu_chr_be_write(chr, mouse->outbuf, len);
mouse->outlen -= len;
if (mouse->outlen) {
memmove(mouse->outbuf, mouse->outbuf + len, mouse->outlen);
}
}
static void msmouse_queue_event(MouseState *mouse)
{
unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
int dx, dy, count = 3;
dx = mouse->axis[INPUT_AXIS_X];
mouse->axis[INPUT_AXIS_X] = 0;
dy = mouse->axis[INPUT_AXIS_Y];
mouse->axis[INPUT_AXIS_Y] = 0;
/* Movement deltas */ /* Movement deltas */
bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx); bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx);
@@ -77,54 +42,14 @@ static void msmouse_queue_event(MouseState *mouse)
bytes[2] |= MSMOUSE_LO6(dy); bytes[2] |= MSMOUSE_LO6(dy);
/* Buttons */ /* Buttons */
bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT] ? 0x20 : 0x00); bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00);
bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT] ? 0x10 : 0x00); bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00);
if (mouse->btns[INPUT_BUTTON_MIDDLE] || bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00);
mouse->btnc[INPUT_BUTTON_MIDDLE]) {
bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00);
mouse->btnc[INPUT_BUTTON_MIDDLE] = false;
count = 4;
}
if (mouse->outlen <= sizeof(mouse->outbuf) - count) { /* We always send the packet of, so that we do not have to keep track
memcpy(mouse->outbuf + mouse->outlen, bytes, count); of previous state of the middle button. This can potentially confuse
mouse->outlen += count; some very old drivers for two button mice though. */
} else { qemu_chr_be_write(chr, bytes, 4);
/* queue full -> drop event */
}
}
static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
MouseState *mouse = (MouseState *)dev;
InputMoveEvent *move;
InputBtnEvent *btn;
switch (evt->type) {
case INPUT_EVENT_KIND_REL:
move = evt->u.rel.data;
mouse->axis[move->axis] += move->value;
break;
case INPUT_EVENT_KIND_BTN:
btn = evt->u.btn.data;
mouse->btns[btn->button] = btn->down;
mouse->btnc[btn->button] = true;
break;
default:
/* keep gcc happy */
break;
}
}
static void msmouse_input_sync(DeviceState *dev)
{
MouseState *mouse = (MouseState *)dev;
msmouse_queue_event(mouse);
msmouse_chr_accept_input(mouse->chr);
} }
static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
@@ -135,41 +60,26 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
static void msmouse_chr_close (struct CharDriverState *chr) static void msmouse_chr_close (struct CharDriverState *chr)
{ {
MouseState *mouse = chr->opaque; g_free (chr);
qemu_input_handler_unregister(mouse->hs);
g_free(mouse);
g_free(chr);
} }
static QemuInputHandler msmouse_handler = {
.name = "QEMU Microsoft Mouse",
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
.event = msmouse_input_event,
.sync = msmouse_input_sync,
};
static CharDriverState *qemu_chr_open_msmouse(const char *id, static CharDriverState *qemu_chr_open_msmouse(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
Error **errp) Error **errp)
{ {
ChardevCommon *common = backend->u.msmouse.data; ChardevCommon *common = backend->u.msmouse.data;
MouseState *mouse;
CharDriverState *chr; CharDriverState *chr;
chr = qemu_chr_alloc(common, errp); chr = qemu_chr_alloc(common, errp);
if (!chr) {
return NULL;
}
chr->chr_write = msmouse_chr_write; chr->chr_write = msmouse_chr_write;
chr->chr_close = msmouse_chr_close; chr->chr_close = msmouse_chr_close;
chr->chr_accept_input = msmouse_chr_accept_input;
chr->explicit_be_open = true; chr->explicit_be_open = true;
mouse = g_new0(MouseState, 1); qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
&msmouse_handler);
mouse->chr = chr;
chr->opaque = mouse;
return chr; return chr;
} }

View File

@@ -40,6 +40,18 @@ numbers must be continuous, i.e. for three devices you must use 0+1+2.
The 0+1+5 numbering from the "usb-uas" example isn't going to work The 0+1+5 numbering from the "usb-uas" example isn't going to work
with "usb-bot". with "usb-bot".
Starting with qemu version 2.7 usb-bot and usb-uas devices can be
hotplugged. In the hotplug case they are added with "attached =
false" so the guest will not see the device until the "attached"
property is explicitly set to true. That allows to attach one or more
scsi devices before making the device visible to the guest, i.e. the
workflow looks like this:
(1) device-add usb-bot,id=foo
(2) device-add scsi-{hd,cd},bus=foo.0,lun=0
(2b) optionally add more devices (luns 1 ... 15).
(3) scripts/qmp/qom-set foo.attached = true
enjoy, enjoy,
Gerd Gerd

View File

@@ -27,7 +27,6 @@
#include "ui/console.h" #include "ui/console.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "hw/input/hid.h" #include "hw/input/hid.h"
#include "trace.h"
#define HID_USAGE_ERROR_ROLLOVER 0x01 #define HID_USAGE_ERROR_ROLLOVER 0x01
#define HID_USAGE_POSTFAIL 0x02 #define HID_USAGE_POSTFAIL 0x02
@@ -235,7 +234,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
key->down, key->down,
scancodes); scancodes);
if (hs->n + count > QUEUE_LENGTH) { if (hs->n + count > QUEUE_LENGTH) {
trace_hid_kbd_queue_full(); fprintf(stderr, "usb-kbd: warning: key event queue full\n");
return; return;
} }
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {

View File

@@ -23,9 +23,3 @@ milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %
milkymist_softusb_mevt(uint8_t m) "m %d" milkymist_softusb_mevt(uint8_t m) "m %d"
milkymist_softusb_kevt(uint8_t m) "m %d" milkymist_softusb_kevt(uint8_t m) "m %d"
milkymist_softusb_pulse_irq(void) "Pulse IRQ" milkymist_softusb_pulse_irq(void) "Pulse IRQ"
# hw/input/hid.c
hid_kbd_queue_full(void) "queue full"
# hw/input/virtio
virtio_input_queue_full(void) "queue full"

View File

@@ -7,7 +7,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/iov.h" #include "qemu/iov.h"
#include "trace.h"
#include "hw/qdev.h" #include "hw/qdev.h"
#include "hw/virtio/virtio.h" #include "hw/virtio/virtio.h"
@@ -48,7 +47,7 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0); virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
if (have < need) { if (have < need) {
vinput->qindex = 0; vinput->qindex = 0;
trace_virtio_input_queue_full(); fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__);
return; return;
} }

View File

@@ -2364,6 +2364,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
slot->uport = uport; slot->uport = uport;
slot->ctx = octx; slot->ctx = octx;
/* Make sure device is in USB_STATE_DEFAULT state */
usb_device_reset(dev);
if (bsr) { if (bsr) {
slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
} else { } else {
@@ -2371,7 +2373,6 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
uint8_t buf[1]; uint8_t buf[1];
slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid; slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
usb_device_reset(dev);
memset(&p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
usb_packet_addbuf(&p, buf, sizeof(buf)); usb_packet_addbuf(&p, buf, sizeof(buf));
usb_packet_setup(&p, USB_TOKEN_OUT, usb_packet_setup(&p, USB_TOKEN_OUT,

View File

@@ -253,7 +253,8 @@ static int usbback_init_packet(struct usbback_req *usbback_req)
case USBIF_PIPE_TYPE_CTRL: case USBIF_PIPE_TYPE_CTRL:
packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl; packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl;
TR_REQ(xendev, "ctrl parameter: %lx, buflen: %x\n", packet->parameter, TR_REQ(xendev, "ctrl parameter: %"PRIx64", buflen: %x\n",
packet->parameter,
usbback_req->req.buffer_length); usbback_req->req.buffer_length);
break; break;

View File

@@ -129,17 +129,6 @@ static int qemu_input_linux_to_qcode(unsigned int lnx)
return linux_to_qcode[lnx]; return linux_to_qcode[lnx];
} }
static bool linux_is_button(unsigned int lnx)
{
if (lnx < 0x100) {
return false;
}
if (lnx >= 0x160 && lnx < 0x2c0) {
return false;
}
return true;
}
#define TYPE_INPUT_LINUX "input-linux" #define TYPE_INPUT_LINUX "input-linux"
#define INPUT_LINUX(obj) \ #define INPUT_LINUX(obj) \
OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX) OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX)
@@ -164,12 +153,6 @@ struct InputLinux {
int keycount; int keycount;
int wheel; int wheel;
bool initialized; bool initialized;
bool has_rel_x;
bool has_abs_x;
int num_keys;
int num_btns;
QTAILQ_ENTRY(InputLinux) next; QTAILQ_ENTRY(InputLinux) next;
}; };
@@ -205,120 +188,7 @@ static void input_linux_toggle_grab(InputLinux *il)
} }
} }
static void input_linux_handle_keyboard(InputLinux *il, static void input_linux_event_keyboard(void *opaque)
struct input_event *event)
{
if (event->type == EV_KEY) {
if (event->value > 2 || (event->value > 1 && !il->repeat)) {
/*
* ignore autorepeat + unknown key events
* 0 == up, 1 == down, 2 == autorepeat, other == undefined
*/
return;
}
if (event->code >= KEY_CNT) {
/*
* Should not happen. But better safe than sorry,
* and we make Coverity happy too.
*/
return;
}
/* keep track of key state */
if (!il->keydown[event->code] && event->value) {
il->keydown[event->code] = true;
il->keycount++;
}
if (il->keydown[event->code] && !event->value) {
il->keydown[event->code] = false;
il->keycount--;
}
/* send event to guest when grab is active */
if (il->grab_active) {
int qcode = qemu_input_linux_to_qcode(event->code);
qemu_input_event_send_key_qcode(NULL, qcode, event->value);
}
/* hotkey -> record switch request ... */
if (il->keydown[KEY_LEFTCTRL] &&
il->keydown[KEY_RIGHTCTRL]) {
il->grab_request = true;
}
/*
* ... and do the switch when all keys are lifted, so we
* confuse neither guest nor host with keys which seem to
* be stuck due to missing key-up events.
*/
if (il->grab_request && !il->keycount) {
il->grab_request = false;
input_linux_toggle_grab(il);
}
}
}
static void input_linux_event_mouse_button(int button)
{
qemu_input_queue_btn(NULL, button, true);
qemu_input_event_sync();
qemu_input_queue_btn(NULL, button, false);
qemu_input_event_sync();
}
static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
{
if (!il->grab_active) {
return;
}
switch (event->type) {
case EV_KEY:
switch (event->code) {
case BTN_LEFT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
break;
case BTN_RIGHT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
break;
case BTN_MIDDLE:
qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value);
break;
case BTN_GEAR_UP:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value);
break;
case BTN_GEAR_DOWN:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
event->value);
break;
};
break;
case EV_REL:
switch (event->code) {
case REL_X:
qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
break;
case REL_Y:
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
break;
case REL_WHEEL:
il->wheel = event->value;
break;
}
break;
case EV_SYN:
qemu_input_event_sync();
if (il->wheel != 0) {
input_linux_event_mouse_button((il->wheel > 0)
? INPUT_BUTTON_WHEEL_UP
: INPUT_BUTTON_WHEEL_DOWN);
il->wheel = 0;
}
break;
}
}
static void input_linux_event(void *opaque)
{ {
InputLinux *il = opaque; InputLinux *il = opaque;
struct input_event event; struct input_event event;
@@ -335,11 +205,131 @@ static void input_linux_event(void *opaque)
break; break;
} }
if (il->num_keys) { switch (event.type) {
input_linux_handle_keyboard(il, &event); case EV_KEY:
if (event.value > 2 || (event.value > 1 && !il->repeat)) {
/*
* ignore autorepeat + unknown key events
* 0 == up, 1 == down, 2 == autorepeat, other == undefined
*/
continue;
}
if (event.code >= KEY_CNT) {
/*
* Should not happen. But better safe than sorry,
* and we make Coverity happy too.
*/
continue;
}
/* keep track of key state */
if (!il->keydown[event.code] && event.value) {
il->keydown[event.code] = true;
il->keycount++;
}
if (il->keydown[event.code] && !event.value) {
il->keydown[event.code] = false;
il->keycount--;
}
/* send event to guest when grab is active */
if (il->grab_active) {
int qcode = qemu_input_linux_to_qcode(event.code);
qemu_input_event_send_key_qcode(NULL, qcode, event.value);
}
/* hotkey -> record switch request ... */
if (il->keydown[KEY_LEFTCTRL] &&
il->keydown[KEY_RIGHTCTRL]) {
il->grab_request = true;
}
/*
* ... and do the switch when all keys are lifted, so we
* confuse neither guest nor host with keys which seem to
* be stuck due to missing key-up events.
*/
if (il->grab_request && !il->keycount) {
il->grab_request = false;
input_linux_toggle_grab(il);
}
break;
} }
if (il->has_rel_x && il->num_btns) { }
input_linux_handle_mouse(il, &event); }
static void input_linux_event_mouse_button(int button)
{
qemu_input_queue_btn(NULL, button, true);
qemu_input_event_sync();
qemu_input_queue_btn(NULL, button, false);
qemu_input_event_sync();
}
static void input_linux_event_mouse(void *opaque)
{
InputLinux *il = opaque;
struct input_event event;
int rc;
for (;;) {
rc = read(il->fd, &event, sizeof(event));
if (rc != sizeof(event)) {
if (rc < 0 && errno != EAGAIN) {
fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
close(il->fd);
}
break;
}
/* only send event to guest when grab is active */
if (!il->grab_active) {
continue;
}
switch (event.type) {
case EV_KEY:
switch (event.code) {
case BTN_LEFT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value);
break;
case BTN_RIGHT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value);
break;
case BTN_MIDDLE:
qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value);
break;
case BTN_GEAR_UP:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event.value);
break;
case BTN_GEAR_DOWN:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
event.value);
break;
};
break;
case EV_REL:
switch (event.code) {
case REL_X:
qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value);
break;
case REL_Y:
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value);
break;
case REL_WHEEL:
il->wheel = event.value;
break;
}
break;
case EV_SYN:
qemu_input_event_sync();
if (il->wheel != 0) {
input_linux_event_mouse_button((il->wheel > 0)
? INPUT_BUTTON_WHEEL_UP
: INPUT_BUTTON_WHEEL_DOWN);
il->wheel = 0;
}
break;
} }
} }
} }
@@ -347,8 +337,7 @@ static void input_linux_event(void *opaque)
static void input_linux_complete(UserCreatable *uc, Error **errp) static void input_linux_complete(UserCreatable *uc, Error **errp)
{ {
InputLinux *il = INPUT_LINUX(uc); InputLinux *il = INPUT_LINUX(uc);
uint8_t evtmap, relmap, absmap, keymap[KEY_CNT / 8]; uint32_t evtmap, relmap, absmap;
unsigned int i;
int rc, ver; int rc, ver;
if (!il->evdev) { if (!il->evdev) {
@@ -376,36 +365,36 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
} }
if (evtmap & (1 << EV_REL)) { if (evtmap & (1 << EV_REL)) {
relmap = 0;
rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap); rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
if (relmap & (1 << REL_X)) { if (rc < 0) {
il->has_rel_x = true; relmap = 0;
} }
} }
if (evtmap & (1 << EV_ABS)) { if (evtmap & (1 << EV_ABS)) {
absmap = 0; ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); if (rc < 0) {
if (absmap & (1 << ABS_X)) { absmap = 0;
il->has_abs_x = true;
} }
} }
if (evtmap & (1 << EV_KEY)) { if ((evtmap & (1 << EV_REL)) &&
memset(keymap, 0, sizeof(keymap)); (relmap & (1 << REL_X))) {
rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap); /* has relative x axis -> assume mouse */
for (i = 0; i < KEY_CNT; i++) { qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il);
if (keymap[i / 8] & (1 << (i % 8))) { } else if ((evtmap & (1 << EV_ABS)) &&
if (linux_is_button(i)) { (absmap & (1 << ABS_X))) {
il->num_btns++; /* has absolute x axis -> not supported */
} else { error_setg(errp, "tablet/touchscreen not supported");
il->num_keys++; goto err_close;
} } else if (evtmap & (1 << EV_KEY)) {
} /* has keys/buttons (and no x axis) -> assume keyboard */
} qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il);
} else {
/* Huh? What is this? */
error_setg(errp, "unknown kind of input device");
goto err_close;
} }
qemu_set_fd_handler(il->fd, input_linux_event, NULL, il);
input_linux_toggle_grab(il); input_linux_toggle_grab(il);
QTAILQ_INSERT_TAIL(&inputs, il, next); QTAILQ_INSERT_TAIL(&inputs, il, next);
il->initialized = true; il->initialized = true;