[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