diff options
author | Alon Levy <alevy@redhat.com> | 2011-04-27 18:20:09 +0300 |
---|---|---|
committer | Alon Levy <alevy@redhat.com> | 2011-07-22 04:37:40 +0300 |
commit | 4d04f2bb72bf8d7aff6f33d2dd77d8c5c0e77f83 (patch) | |
tree | a28a914332fbc80fba6dfda72b488e353593f0e1 | |
parent | c54fef726efbf6c2f00e27b85a46a47741af1786 (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.am | 1 | ||||
-rw-r--r-- | src/qxl_driver.c | 4 | ||||
-rw-r--r-- | src/spiceqxl_inputs.c | 375 | ||||
-rw-r--r-- | src/spiceqxl_inputs.h | 8 |
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 |