summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2011-04-27 18:20:09 +0300
committerAlon Levy <alevy@redhat.com>2011-07-22 04:37:40 +0300
commit4d04f2bb72bf8d7aff6f33d2dd77d8c5c0e77f83 (patch)
treea28a914332fbc80fba6dfda72b488e353593f0e1
parentc54fef726efbf6c2f00e27b85a46a47741af1786 (diff)
xspice: add inputs (mouse and keyboard)
uses xf86AddInputDriver, xf86PostButtonEvent, xf86PostMotionEvent and xf86PostKeyboardEvent reused xspice_get_spice_server to access the single spice server instance.
-rw-r--r--src/Makefile.am1
-rw-r--r--src/qxl_driver.c4
-rw-r--r--src/spiceqxl_inputs.c375
-rw-r--r--src/spiceqxl_inputs.h8
4 files changed, 388 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c91a5ca..e99e081 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,6 +65,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 97570e5..bfbf444 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
@@ -1624,6 +1625,9 @@ qxl_setup(pointer module, pointer opts, int *errmaj, int *errmin)
if (!loaded) {
loaded = TRUE;
xf86AddDriver(&qxl_driver, module, HaveDriverFuncs);
+#ifdef XSPICE
+ xspice_add_input_drivers(module);
+#endif
return (void *)1;
} else {
if (errmaj)
diff --git a/src/spiceqxl_inputs.c b/src/spiceqxl_inputs.c
new file mode 100644
index 0000000..86ce477
--- /dev/null
+++ b/src/spiceqxl_inputs.c
@@ -0,0 +1,375 @@
+/* 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 <spice.h>
+#include "qxl.h"
+#include "spiceqxl_inputs.h"
+
+static
+int XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+static
+int XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+static
+void XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+static
+void XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+
+static InputDriverRec XSPICE_POINTER = {
+ 1,
+ "xspice pointer",
+ NULL,
+ XSpicePointerPreInit,
+ XSpicePointerUnInit,
+ NULL,
+ NULL /* defaults */
+};
+
+static InputDriverRec XSPICE_KEYBOARD = {
+ 1,
+ "xspice keyboard",
+ NULL,
+ XSpiceKeyboardPreInit,
+ XSpiceKeyboardUnInit,
+ NULL,
+ NULL
+};
+
+#define BUTTONS 5
+
+typedef struct XSpiceKbd {
+ SpiceKbdInstance sin;
+ uint8_t ledstate;
+ InputInfoPtr pInfo; /* xf86 device handle to post events */
+ /* Note: spice sends some of the keys escaped by this.
+ * This is supposed to be AT key codes, but I can't figure out where that
+ * thing is defined after looking at xf86-input-keyboard. Ended up reverse
+ * engineering a escaped table using xev.
+ */
+ int escape;
+} 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,
+};
+
+/* spice sends AT scancodes (with a strange escape).
+ * But xf86PostKeyboardEvent expects scancodes. Apparently most of the time
+ * you just need to add MIN_KEYCODE, see xf86-input-keyboard/src/atKeynames
+ * and xf86-input-keyboard/src/kbd.c:PostKbdEvent:
+ * xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down); */
+#define MIN_KEYCODE 8
+
+static uint8_t escaped_map[256] = {
+ [0x1c] = 104, //KEY_KP_Enter,
+ [0x1d] = 105, //KEY_RCtrl,
+ [0x2a] = 0,//KEY_LMeta, // REDKEY_FAKE_L_SHIFT
+ [0x35] = 106,//KEY_KP_Divide,
+ [0x36] = 0,//KEY_RMeta, // REDKEY_FAKE_R_SHIFT
+ [0x37] = 107,//KEY_Print,
+ [0x38] = 108,//KEY_AltLang,
+ [0x46] = 127,//KEY_Break,
+ [0x47] = 110,//KEY_Home,
+ [0x48] = 111,//KEY_Up,
+ [0x49] = 112,//KEY_PgUp,
+ [0x4b] = 113,//KEY_Left,
+ [0x4d] = 114,//KEY_Right,
+ [0x4f] = 115,//KEY_End,
+ [0x50] = 116,//KEY_Down,
+ [0x51] = 117,//KEY_PgDown,
+ [0x52] = 118,//KEY_Insert,
+ [0x53] = 119,//KEY_Delete,
+ [0x5b] = 133,//0, // REDKEY_LEFT_CMD,
+ [0x5c] = 134,//0, // REDKEY_RIGHT_CMD,
+ [0x5d] = 135,//KEY_Menu,
+};
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+{
+ XSpiceKbd *kbd = container_of(sin, XSpiceKbd, sin);
+ int is_down;
+
+ if (frag == 224) {
+ kbd->escape = frag;
+ return;
+ }
+ is_down = frag & 0x80 ? FALSE : TRUE;
+ frag = frag & 0x7f;
+ if (kbd->escape == 224) {
+ kbd->escape = 0;
+ if (escaped_map[frag] == 0) {
+ fprintf(stderr, "spiceqxl_inputs.c: kbd_push_key: escaped_map[%d] == 0\n", frag);
+ }
+ frag = escaped_map[frag];
+ } else {
+ frag += MIN_KEYCODE;
+ }
+
+ xf86PostKeyboardEvent(kbd->pInfo->dev, frag, is_down);
+}
+
+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;
+ InputInfoPtr pInfo; /* xf86 device handle to post events */
+} 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 void tablet_position(SpiceTabletInstance* sin, int x, int y,
+ uint32_t buttons_state)
+{
+ XSpicePointer *pointer = container_of(sin, XSpicePointer, tablet);
+
+ // TODO: don't ignore buttons_state
+ xf86PostMotionEvent(pointer->pInfo->dev, 1, 0, 2, x, y);
+}
+
+static void tablet_buttons(SpiceTabletInstance *sin,
+ uint32_t buttons_state)
+{
+ XSpicePointer *pointer = container_of(sin, XSpicePointer, tablet);
+ static uint32_t old_buttons_state = 0;
+ 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));
+
+ for (i = 0; i < BUTTONS; i++) {
+ if ((buttons_state ^ old_buttons_state) & (1 << i)) {
+ int action = (buttons_state & (1 << i));
+ xf86PostButtonEvent(pointer->pInfo->dev, 0, i + 1, action, 0, 0);
+ }
+ }
+ 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,
+};
+
+static int
+XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+ XSpiceKbd *kbd;
+
+ kbd = calloc(sizeof(*kbd), 1);
+ kbd->sin.base.sif = &kbd_interface.base;
+ kbd->pInfo = pInfo;
+
+ pInfo->private = kbd;
+ pInfo->type_name = "UNKNOWN";
+ pInfo->device_control = xspice_keyboard_proc;
+ pInfo->read_input = NULL;
+ pInfo->switch_mode = NULL;
+
+ spice_server_add_interface(xspice_get_spice_server(), &kbd->sin.base);
+ return Success;
+}
+
+static int
+XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+ XSpicePointer *pointer;
+
+ pointer = calloc(sizeof(*pointer), 1);
+ pointer->mouse.base.sif = &mouse_interface.base;
+ pointer->tablet.base.sif = &tablet_interface.base;
+ pointer->absolute = TRUE;
+ pointer->pInfo = pInfo;
+
+ pInfo->private = NULL;
+ pInfo->type_name = "UNKNOWN";
+ pInfo->device_control = xspice_pointer_proc;
+ pInfo->read_input = NULL;
+ pInfo->switch_mode = NULL;
+
+ spice_server_add_interface(xspice_get_spice_server(), &pointer->tablet.base);
+ return Success;
+}
+
+static void
+XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+}
+
+static void
+XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+}
+
+void xspice_add_input_drivers(pointer module)
+{
+ xf86AddInputDriver(&XSPICE_POINTER, module, 0);
+ xf86AddInputDriver(&XSPICE_KEYBOARD, module, 0);
+}
diff --git a/src/spiceqxl_inputs.h b/src/spiceqxl_inputs.h
new file mode 100644
index 0000000..03286cf
--- /dev/null
+++ b/src/spiceqxl_inputs.h
@@ -0,0 +1,8 @@
+#ifndef QXL_SPICE_INPUTS_H
+#define QXL_SPICE_INPUTS_H
+
+#include "qxl.h"
+
+void xspice_add_input_drivers(pointer module);
+
+#endif // QXL_SPICE_INPUTS_H