--- 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 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel