[Spice-devel] [RFCv2 19/21] xspice: add inputs (mouse and keyboard)
Alon Levy
alevy at redhat.com
Fri Apr 29 02:49:57 PDT 2011
---
src/Makefile.am | 1 +
src/qxl_driver.c | 2 +
src/spiceqxl_inputs.c | 353 +++++++++++++++++++++++++++++++++++++++++++++++++
src/spiceqxl_inputs.h | 9 ++
4 files changed, 365 insertions(+), 0 deletions(-)
create mode 100644 src/spiceqxl_inputs.c
create mode 100644 src/spiceqxl_inputs.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 5eedc93..3521696 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -63,6 +63,7 @@ spiceqxl_drv_la_SOURCES = \
spiceqxl_driver.c \
spiceqxl_main_loop.c \
spiceqxl_display.c \
+ spiceqxl_inputs.c \
qxl_driver.c \
qxl_image.c \
qxl_surface.c \
diff --git a/src/qxl_driver.c b/src/qxl_driver.c
index ef3139b..688aa81 100644
--- a/src/qxl_driver.c
+++ b/src/qxl_driver.c
@@ -41,6 +41,7 @@
#include "spiceqxl_driver.h"
#include "spiceqxl_main_loop.h"
#include "spiceqxl_display.h"
+#include "spiceqxl_inputs.h"
#endif /* XSPICE */
#if 0
@@ -903,6 +904,7 @@ spiceqxl_screen_init(int scrnIndex, ScrnInfoPtr pScrn, qxl_screen_t *qxl)
spice_server_init(qxl->spice_server, core);
qxl_add_spice_display_interface(qxl);
qxl->worker->start(qxl->worker);
+ delayed_xspice_input_init(qxl);
}
qxl->spice_server = qxl->spice_server;
}
diff --git a/src/spiceqxl_inputs.c b/src/spiceqxl_inputs.c
new file mode 100644
index 0000000..272dc81
--- /dev/null
+++ b/src/spiceqxl_inputs.c
@@ -0,0 +1,353 @@
+/* Handle inputs channel for spice, and register the X parts,
+ * a mouse and a keyboard device pair.
+ */
+
+#include <xorg/xf86Xinput.h>
+#include <xorg/exevents.h>
+#include <xorg/xserver-properties.h>
+#include <xorg/list.h>
+#include <xorg/input.h>
+#include <xorg/xkbsrv.h>
+#include <linux/input.h>
+#include <spice.h>
+#include "qxl.h"
+#include "spiceqxl_inputs.h"
+
+static DeviceIntPtr xspice_pointer_device;
+static DeviceIntPtr xspice_keyboard_device;
+
+#define BUTTONS 5
+
+typedef struct XSpiceKbd {
+ SpiceKbdInstance sin;
+ qxl_screen_t *qxl;
+ uint8_t ledstate;
+} XSpiceKbd;
+
+static int xspice_pointer_proc(DeviceIntPtr pDevice, int onoff)
+{
+ DevicePtr pDev = (DevicePtr)pDevice;
+ BYTE map[BUTTONS + 1];
+ Atom btn_labels[BUTTONS];
+ Atom axes_labels[2];
+ int i;
+
+ switch (onoff) {
+ case DEVICE_INIT:
+ for (i = 0; i < BUTTONS + 1; i++) {
+ map[i] = i;
+ }
+ btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
+ btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
+ btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
+ btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
+ btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
+ axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
+ axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
+ InitPointerDeviceStruct(pDev, map, BUTTONS,btn_labels,(PtrCtrlProcPtr)NoopDDA,
+ GetMotionHistorySize(), 2, axes_labels);
+ break;
+ case DEVICE_ON:
+ pDev->on = TRUE;
+ break;
+ case DEVICE_OFF:
+ pDev->on = FALSE;
+ break;
+ }
+ return Success;
+}
+
+static void xspice_keyboard_bell(int percent, DeviceIntPtr device, pointer ctrl, int class_)
+{
+}
+
+#define CAPSFLAG 1
+#define NUMFLAG 2
+#define SCROLLFLAG 4
+/* MODEFLAG and COMPOSEFLAG currently unused (reminder for future) */
+#define MODEFLAG 8
+#define COMPOSEFLAG 16
+
+#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
+
+static void xspice_keyboard_control(DeviceIntPtr device, KeybdCtrl *ctrl)
+{
+ static struct { int xbit, code; } bits[] = {
+ { CAPSFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK },
+ { NUMFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK },
+ { SCROLLFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK },
+ /* TODO: there is no MODEFLAG nor COMPOSEFLAG in SPICE. */
+ };
+
+ XSpiceKbd *kbd;
+ int i;
+
+ kbd = device->public.devicePrivate;
+ kbd->ledstate = 0;
+ for (i = 0; i < ArrayLength(bits); i++) {
+ if (ctrl->leds & bits[i].xbit) {
+ kbd->ledstate |= bits[i].code;
+ } else {
+ kbd->ledstate &= ~bits[i].code;
+ }
+ }
+}
+
+static int xspice_keyboard_proc(DeviceIntPtr pDevice, int onoff)
+{
+ DevicePtr pDev = (DevicePtr)pDevice;
+
+ switch (onoff) {
+ case DEVICE_INIT:
+ InitKeyboardDeviceStruct(
+ pDevice, NULL, xspice_keyboard_bell, xspice_keyboard_control
+ );
+ break;
+ case DEVICE_ON:
+ pDev->on = TRUE;
+ break;
+ case DEVICE_OFF:
+ pDev->on = FALSE;
+ break;
+ }
+ return Success;
+}
+
+/* from spice-input.c */
+/* keyboard bits */
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
+
+static const SpiceKbdInterface kbd_interface = {
+ .base.type = SPICE_INTERFACE_KEYBOARD,
+ .base.description = "xspice keyboard",
+ .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
+ .push_scan_freg = kbd_push_key,
+ .get_leds = kbd_get_leds,
+};
+
+#define IS_PRESSED(keyc, keycode) \
+ ((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
+
+/* spice sends AT scancodes. But xf86PostKeyboardEvent expects it
+ * to be shifted by MIN_KEYCODE amount, see xf86-input-keyboard/src/atKeynames.h,
+ * and xf86-input-keyboard/src/kbd.c:PostKbdEvent:
+ * xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down); */
+#define MIN_KEYCODE 8
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+{
+ /* taken from xf86-input-keyboard, lnx_kbd.c */
+ xf86PostKeyboardEvent(xspice_keyboard_device,
+ (frag & 0x7f) + MIN_KEYCODE,
+ frag & 0x80 ? FALSE : TRUE);
+
+ /* This is the approach taken by tightvnc, but for some reason I have
+ * xspice_keyboard_device->u.master == NULL. Also, it isn't what the
+ * xf86-input-keyboard does, it uses xf86PostKeyboardEvent like the nice
+ * doc on the wiki says http://wiki.x.org/wiki/Development/Documentation/InputEventProcessing */
+#if 0
+ int action;
+ unsigned int n;
+ EventList *eventq;
+ KeyClassPtr keyc;
+ /*
+ * Since we are checking the current state to determine if we need
+ * to fake modifiers, we must make sure that everything put on the
+ * input queue is processed before we start. Otherwise, shift may be
+ * stuck down.
+ */
+ mieqProcessInputEvents();
+ keyc = xspice_keyboard_device->u.master->key;
+ GetEventList(&eventq);
+ action = IS_PRESSED(keyc, frag) ? KeyRelease : KeyPress;
+ n = GetKeyboardEvents(eventq, xspice_keyboard_device, action, frag);
+ enqueueEvents(xspice_keyboard_device, eventq, n);
+#endif
+}
+
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
+{
+ XSpiceKbd *kbd = container_of(sin, XSpiceKbd, sin);
+
+ return kbd->ledstate;
+}
+
+/* mouse bits */
+
+typedef struct XSpicePointer {
+ SpiceMouseInstance mouse;
+ SpiceTabletInstance tablet;
+ int width, height, x, y;
+ Bool absolute;
+ qxl_screen_t *qxl;
+} XSpicePointer;
+
+static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
+ uint32_t buttons_state)
+{
+ // TODO
+}
+
+static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
+{
+ // TODO
+}
+
+static const SpiceMouseInterface mouse_interface = {
+ .base.type = SPICE_INTERFACE_MOUSE,
+ .base.description = "xspice mouse",
+ .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR,
+ .motion = mouse_motion,
+ .buttons = mouse_buttons,
+};
+
+static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
+{
+ XSpicePointer *pointer = container_of(sin, XSpicePointer, tablet);
+
+ if (height < 16) {
+ height = 16;
+ }
+ if (width < 16) {
+ width = 16;
+ }
+ pointer->width = width;
+ pointer->height = height;
+}
+
+static ValuatorMask* g_mask;
+
+static void enqueueEvents(DeviceIntPtr dev, EventList *eventq, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ mieqEnqueue(dev, (InternalEvent *) (eventq + i)->event);
+ }
+}
+
+static void tablet_position(SpiceTabletInstance* sin, int x, int y,
+ uint32_t buttons_state)
+{
+ int n, valuators[2];
+ EventList *eventq;
+
+ // TODO: don't ignore buttons_state
+
+ valuators[0] = x;
+ valuators[1] = y;
+ valuator_mask_set_range(g_mask, 0, 2, valuators);
+ GetEventList(&eventq);
+ n = GetPointerEvents(eventq, xspice_pointer_device, MotionNotify, 0, POINTER_ABSOLUTE,
+ g_mask);
+ enqueueEvents(xspice_pointer_device, eventq, n);
+}
+
+static void tablet_buttons(SpiceTabletInstance *sin,
+ uint32_t buttons_state)
+{
+ static uint32_t old_buttons_state = 0;
+ int n;
+ EventList *eventq;
+ int i;
+
+ // For some reason spice switches the second and third button, undo that.
+ // basically undo RED_MOUSE_STATE_TO_LOCAL
+ buttons_state = (buttons_state & SPICE_MOUSE_BUTTON_MASK_LEFT) |
+ ((buttons_state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |
+ ((buttons_state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1) |
+ (buttons_state & ~(SPICE_MOUSE_BUTTON_MASK_LEFT | SPICE_MOUSE_BUTTON_MASK_MIDDLE
+ |SPICE_MOUSE_BUTTON_MASK_RIGHT));
+
+ GetEventList(&eventq);
+ for (i = 0; i < BUTTONS; i++) {
+ if ((buttons_state ^ old_buttons_state) & (1 << i)) {
+ int action = (buttons_state & (1 << i)) ?
+ ButtonPress : ButtonRelease;
+ valuator_mask_set_range(g_mask, 0, 0, NULL);
+ n = GetPointerEvents(eventq, xspice_pointer_device, action, i + 1,
+ POINTER_RELATIVE, g_mask);
+ enqueueEvents(xspice_pointer_device, eventq, n);
+ }
+ }
+ old_buttons_state = buttons_state;
+}
+
+static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
+ uint32_t buttons_state)
+{
+ // convert wheel into fourth and fifth buttons
+ tablet_buttons(sin, buttons_state
+ | (wheel > 0 ? (1<<4) : 0)
+ | (wheel < 0 ? (1<<3) : 0));
+}
+
+static const SpiceTabletInterface tablet_interface = {
+ .base.type = SPICE_INTERFACE_TABLET,
+ .base.description = "xspice tablet",
+ .base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
+ .set_logical_size = tablet_set_logical_size,
+ .position = tablet_position,
+ .wheel = tablet_wheel,
+ .buttons = tablet_buttons,
+};
+
+/* we register the input (keyboard and mouse) drivers
+ * here, not in independent modules.
+ * This seperation exists in the copied code from tigervnc, there
+ * it is stated the reason is that during extention initialization
+ * the input devices are not yet registered. Do we have the same limitation
+ * as a driver? */
+void xspice_init_input_devices(qxl_screen_t *qxl)
+{
+ XSpiceKbd *kbd;
+ XSpicePointer *pointer;
+
+ kbd = calloc(sizeof(*kbd), 1);
+ kbd->sin.base.sif = &kbd_interface.base;
+ kbd->qxl = qxl;
+
+ //xf86AddInputDriver(&xspice_mouse_driver, module, 0);
+ AllocDevicePair(serverClient, "xspice", &xspice_pointer_device,
+ &xspice_keyboard_device, xspice_pointer_proc, xspice_keyboard_proc,
+ FALSE /* master */);
+ xspice_keyboard_device->public.devicePrivate = kbd;
+ if (ActivateDevice(xspice_keyboard_device, TRUE) != Success ||
+ ActivateDevice(xspice_pointer_device, TRUE) != Success) {
+ FatalError("Failed to activate Xspice keyboard/pointer devices\n");
+ }
+
+ if (!EnableDevice(xspice_keyboard_device, TRUE) ||
+ !EnableDevice(xspice_pointer_device, TRUE)) {
+ FatalError("Failed to activate Xspice keyboard/pointer devices\n");
+ }
+
+ g_mask = valuator_mask_new(1);
+
+ /* register spice interfaces */
+ spice_server_add_interface(qxl->spice_server, &kbd->sin.base);
+
+ pointer = calloc(sizeof(*pointer), 1);
+ pointer->qxl = qxl;
+ pointer->mouse.base.sif = &mouse_interface.base;
+ pointer->tablet.base.sif = &tablet_interface.base;
+ spice_server_add_interface(qxl->spice_server, &pointer->tablet.base);
+
+ pointer->absolute = TRUE;
+}
+
+static Bool call_xspice_init_input_devices(ClientPtr pClient, pointer closure)
+{
+ xspice_init_input_devices((qxl_screen_t*)closure);
+ return TRUE; // remove from work queue
+}
+
+void delayed_xspice_input_init(qxl_screen_t *qxl)
+{
+ QueueWorkProc(call_xspice_init_input_devices, NULL, (pointer)qxl);
+}
diff --git a/src/spiceqxl_inputs.h b/src/spiceqxl_inputs.h
new file mode 100644
index 0000000..6ea8f17
--- /dev/null
+++ b/src/spiceqxl_inputs.h
@@ -0,0 +1,9 @@
+#ifndef QXL_SPICE_INPUTS_H
+#define QXL_SPICE_INPUTS_H
+
+#include "qxl.h"
+
+void delayed_xspice_input_init(qxl_screen_t *qxl);
+void xspice_init_input_devices(qxl_screen_t *qxl);
+
+#endif // QXL_SPICE_INPUTS_H
--
1.7.4.4
More information about the Spice-devel
mailing list