1/*
2 * Copyright 2011 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/* Handle inputs channel for spice, and register the X parts,
24 * a mouse and a keyboard device pair.
25 */
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include <xf86Xinput.h>
31#include <exevents.h>
32#include <xserver-properties.h>
33#include <list.h>
34#include <input.h>
35#include <xkbsrv.h>
36#include <spice.h>
37#include "qxl.h"
38#include "spiceqxl_inputs.h"
39
40static
41int XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
42static
43int XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
44static
45void XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
46static
47void XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
48
49static char xspice_pointer_name[] = "xspice pointer";
50static InputDriverRec XSPICE_POINTER = {
51    1,
52    xspice_pointer_name,
53    NULL,
54    XSpicePointerPreInit,
55    XSpicePointerUnInit,
56    NULL,
57    NULL /* defaults */
58};
59
60static char xspice_keyboard_name[] = "xspice keyboard";
61static InputDriverRec XSPICE_KEYBOARD = {
62    1,
63    xspice_keyboard_name,
64    NULL,
65    XSpiceKeyboardPreInit,
66    XSpiceKeyboardUnInit,
67    NULL,
68    NULL
69};
70
71#define BUTTONS 5
72
73typedef struct XSpiceKbd {
74    SpiceKbdInstance sin;
75    uint8_t          ledstate;
76    InputInfoPtr     pInfo; /* xf86 device handle to post events */
77    /* Note: spice sends some of the keys escaped by this.
78     * This is supposed to be AT key codes, but I can't figure out where that
79     * thing is defined after looking at xf86-input-keyboard. Ended up reverse
80     * engineering a escaped table using xev.
81     */
82    int              escape;
83} XSpiceKbd;
84
85static int xspice_pointer_proc(DeviceIntPtr pDevice, int onoff)
86{
87    DevicePtr pDev = (DevicePtr)pDevice;
88    BYTE map[BUTTONS + 1];
89    Atom btn_labels[BUTTONS];
90    Atom axes_labels[2];
91    int i;
92
93    switch (onoff) {
94        case DEVICE_INIT:
95            for (i = 0; i < BUTTONS + 1; i++) {
96                map[i] = i;
97            }
98            btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
99            btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
100            btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
101            btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
102            btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
103            axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
104            axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
105            InitPointerDeviceStruct(pDev, map, BUTTONS,btn_labels,(PtrCtrlProcPtr)NoopDDA,
106                GetMotionHistorySize(), 2, axes_labels);
107            break;
108        case DEVICE_ON:
109            pDev->on = TRUE;
110            break;
111        case DEVICE_OFF:
112            pDev->on = FALSE;
113            break;
114    }
115    return Success;
116}
117
118static void xspice_keyboard_bell(int percent, DeviceIntPtr device, pointer ctrl, int class_)
119{
120}
121
122#define CAPSFLAG        1
123#define NUMFLAG         2
124#define SCROLLFLAG      4
125/* MODEFLAG and COMPOSEFLAG currently unused (reminder for future) */
126#define MODEFLAG        8
127#define COMPOSEFLAG    16
128
129#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
130
131static void xspice_keyboard_control(DeviceIntPtr device, KeybdCtrl *ctrl)
132{
133    static struct { int xbit, code; } bits[] = {
134        { CAPSFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK },
135        { NUMFLAG,  SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK },
136        { SCROLLFLAG,   SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK },
137        /* TODO: there is no MODEFLAG nor COMPOSEFLAG in SPICE. */
138    };
139
140    XSpiceKbd *kbd;
141    InputInfoPtr pInfo;
142    int i;
143
144    pInfo = device->public.devicePrivate;
145    kbd = pInfo->private;
146    kbd->ledstate = 0;
147    for (i = 0; i < ArrayLength(bits); i++) {
148        if (ctrl->leds & bits[i].xbit) {
149            kbd->ledstate |= bits[i].code;
150        } else {
151            kbd->ledstate &= ~bits[i].code;
152        }
153    }
154}
155
156static char xspice_keyboard_rules[] = "evdev";
157static char xspice_keyboard_model[] = "pc105";
158static char xspice_keyboard_layout[] = "us";
159static char xspice_keyboard_variant[] = "";
160static char xspice_keyboard_options[] = "";
161static int xspice_keyboard_proc(DeviceIntPtr pDevice, int onoff)
162{
163    DevicePtr pDev = (DevicePtr)pDevice;
164    XkbRMLVOSet rmlvo = {
165        .rules = xspice_keyboard_rules,
166        .model = xspice_keyboard_model,
167        .layout = xspice_keyboard_layout,
168        .variant = xspice_keyboard_variant,
169        .options = xspice_keyboard_options,
170    };
171
172    switch (onoff) {
173        case DEVICE_INIT:
174            InitKeyboardDeviceStruct(
175                pDevice, &rmlvo, xspice_keyboard_bell, xspice_keyboard_control
176            );
177            break;
178        case DEVICE_ON:
179            pDev->on = TRUE;
180            break;
181        case DEVICE_OFF:
182            pDev->on = FALSE;
183            break;
184    }
185    return Success;
186}
187
188/* from spice-input.c */
189/* keyboard bits */
190
191static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
192static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
193
194static const SpiceKbdInterface kbd_interface = {
195    .base.type          = SPICE_INTERFACE_KEYBOARD,
196    .base.description   = "xspice keyboard",
197    .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
198    .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
199    .push_scan_freg     = kbd_push_key,
200    .get_leds           = kbd_get_leds,
201};
202
203/* spice sends AT scancodes (with a strange escape).
204 * But xf86PostKeyboardEvent expects scancodes. Apparently most of the time
205 * you just need to add MIN_KEYCODE, see xf86-input-keyboard/src/atKeynames
206 * and xf86-input-keyboard/src/kbd.c:PostKbdEvent:
207 *   xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down); */
208#define MIN_KEYCODE     8
209
210static uint8_t escaped_map[256] = {
211    [0x1c] = 104, //KEY_KP_Enter,
212    [0x1d] = 105, //KEY_RCtrl,
213    [0x2a] = 0,//KEY_LMeta, // REDKEY_FAKE_L_SHIFT
214    [0x35] = 106,//KEY_KP_Divide,
215    [0x36] = 0,//KEY_RMeta, // REDKEY_FAKE_R_SHIFT
216    [0x37] = 107,//KEY_Print,
217    [0x38] = 108,//KEY_AltLang,
218    [0x46] = 127,//KEY_Break,
219    [0x47] = 110,//KEY_Home,
220    [0x48] = 111,//KEY_Up,
221    [0x49] = 112,//KEY_PgUp,
222    [0x4b] = 113,//KEY_Left,
223    [0x4d] = 114,//KEY_Right,
224    [0x4f] = 115,//KEY_End,
225    [0x50] = 116,//KEY_Down,
226    [0x51] = 117,//KEY_PgDown,
227    [0x52] = 118,//KEY_Insert,
228    [0x53] = 119,//KEY_Delete,
229    [0x5b] = 133,//0, // REDKEY_LEFT_CMD,
230    [0x5c] = 134,//0, // REDKEY_RIGHT_CMD,
231    [0x5d] = 135,//KEY_Menu,
232};
233
234static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
235{
236    XSpiceKbd *kbd = container_of(sin, XSpiceKbd, sin);
237    int is_down;
238
239    if (frag == 224) {
240        kbd->escape = frag;
241        return;
242    }
243    is_down = frag & 0x80 ? FALSE : TRUE;
244    frag = frag & 0x7f;
245    if (kbd->escape == 224) {
246        kbd->escape = 0;
247        if (escaped_map[frag] == 0) {
248            fprintf(stderr, "spiceqxl_inputs.c: kbd_push_key: escaped_map[%d] == 0\n", frag);
249        }
250        frag = escaped_map[frag];
251    } else {
252        frag += MIN_KEYCODE;
253    }
254
255    xf86PostKeyboardEvent(kbd->pInfo->dev, frag, is_down);
256}
257
258static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
259{
260    XSpiceKbd *kbd = container_of(sin, XSpiceKbd, sin);
261
262    return kbd->ledstate;
263}
264
265/* mouse bits */
266
267typedef struct XSpicePointer {
268    SpiceMouseInstance  mouse;
269    SpiceTabletInstance tablet;
270    int width, height, x, y;
271    Bool absolute;
272    InputInfoPtr     pInfo; /* xf86 device handle to post events */
273} XSpicePointer;
274
275static XSpicePointer *g_xspice_pointer;
276
277static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
278                         uint32_t buttons_state)
279{
280    // TODO
281}
282
283static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
284{
285    // TODO
286}
287
288static const SpiceMouseInterface mouse_interface = {
289    .base.type          = SPICE_INTERFACE_MOUSE,
290    .base.description   = "xspice mouse",
291    .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR,
292    .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR,
293    .motion             = mouse_motion,
294    .buttons            = mouse_buttons,
295};
296
297static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
298{
299    XSpicePointer *spice_pointer = container_of(sin, XSpicePointer, tablet);
300
301    if (height < 16) {
302        height = 16;
303    }
304    if (width < 16) {
305        width = 16;
306    }
307    spice_pointer->width  = width;
308    spice_pointer->height = height;
309}
310
311void spiceqxl_tablet_position(int x, int y, uint32_t buttons_state)
312{
313    // TODO: don't ignore buttons_state
314    xf86PostMotionEvent(g_xspice_pointer->pInfo->dev, 1, 0, 2, x, y);
315}
316
317static void tablet_position(SpiceTabletInstance* sin, int x, int y,
318                            uint32_t buttons_state)
319{
320    spiceqxl_tablet_position(x, y, buttons_state);
321}
322
323void spiceqxl_tablet_buttons(uint32_t buttons_state)
324{
325    static uint32_t old_buttons_state = 0;
326    int i;
327
328    for (i = 0; i < BUTTONS; i++) {
329        if ((buttons_state ^ old_buttons_state) & (1 << i)) {
330            int action = (buttons_state & (1 << i));
331            xf86PostButtonEvent(g_xspice_pointer->pInfo->dev, 0, i + 1, action, 0, 0);
332        }
333    }
334    old_buttons_state = buttons_state;
335}
336
337static void tablet_buttons(SpiceTabletInstance *sin,
338                           uint32_t buttons_state)
339{
340    // For some reason spice switches the second and third button, undo that.
341    // basically undo RED_MOUSE_STATE_TO_LOCAL
342    buttons_state = (buttons_state & SPICE_MOUSE_BUTTON_MASK_LEFT) |
343        ((buttons_state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |
344        ((buttons_state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1) |
345        (buttons_state & ~(SPICE_MOUSE_BUTTON_MASK_LEFT | SPICE_MOUSE_BUTTON_MASK_MIDDLE
346                          |SPICE_MOUSE_BUTTON_MASK_RIGHT));
347    spiceqxl_tablet_buttons(buttons_state);
348}
349
350static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
351                         uint32_t buttons_state)
352{
353    // convert wheel into fourth and fifth buttons
354    tablet_buttons(sin, buttons_state
355                        | (wheel > 0 ? (1<<4) : 0)
356                        | (wheel < 0 ? (1<<3) : 0));
357}
358
359static const SpiceTabletInterface tablet_interface = {
360    .base.type          = SPICE_INTERFACE_TABLET,
361    .base.description   = "xspice tablet",
362    .base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
363    .base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
364    .set_logical_size   = tablet_set_logical_size,
365    .position           = tablet_position,
366    .wheel              = tablet_wheel,
367    .buttons            = tablet_buttons,
368};
369
370static char unknown_type_string[] = "UNKNOWN";
371
372static int
373XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
374{
375    XSpiceKbd *kbd;
376
377    kbd = calloc(sizeof(*kbd), 1);
378    kbd->sin.base.sif = &kbd_interface.base;
379    kbd->pInfo = pInfo;
380
381    pInfo->private = kbd;
382    pInfo->type_name = unknown_type_string;
383    pInfo->device_control = xspice_keyboard_proc;
384    pInfo->read_input = NULL;
385    pInfo->switch_mode = NULL;
386
387    spice_server_add_interface(xspice_get_spice_server(), &kbd->sin.base);
388    return Success;
389}
390
391static int
392XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
393{
394    XSpicePointer *spice_pointer;
395
396    g_xspice_pointer = spice_pointer = calloc(sizeof(*spice_pointer), 1);
397    spice_pointer->mouse.base.sif  = &mouse_interface.base;
398    spice_pointer->tablet.base.sif = &tablet_interface.base;
399    spice_pointer->absolute = TRUE;
400    spice_pointer->pInfo = pInfo;
401
402    pInfo->private = NULL;
403    pInfo->type_name = unknown_type_string;
404    pInfo->device_control = xspice_pointer_proc;
405    pInfo->read_input = NULL;
406    pInfo->switch_mode = NULL;
407
408    spice_server_add_interface(xspice_get_spice_server(), &spice_pointer->tablet.base);
409    return Success;
410}
411
412static void
413XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
414{
415}
416
417static void
418XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
419{
420}
421
422void xspice_add_input_drivers(pointer module)
423{
424    xf86AddInputDriver(&XSPICE_POINTER, module, 0);
425    xf86AddInputDriver(&XSPICE_KEYBOARD, module, 0);
426}
427