Home | History | Annotate | Line # | Download | only in src
      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 
     40 static
     41 int XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
     42 static
     43 int XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
     44 static
     45 void XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
     46 static
     47 void XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
     48 
     49 static char xspice_pointer_name[] = "xspice pointer";
     50 static InputDriverRec XSPICE_POINTER = {
     51     1,
     52     xspice_pointer_name,
     53     NULL,
     54     XSpicePointerPreInit,
     55     XSpicePointerUnInit,
     56     NULL,
     57     NULL /* defaults */
     58 };
     59 
     60 static char xspice_keyboard_name[] = "xspice keyboard";
     61 static 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 
     73 typedef 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 
     85 static 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 
    118 static 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 
    131 static 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 
    156 static char xspice_keyboard_rules[] = "evdev";
    157 static char xspice_keyboard_model[] = "pc105";
    158 static char xspice_keyboard_layout[] = "us";
    159 static char xspice_keyboard_variant[] = "";
    160 static char xspice_keyboard_options[] = "";
    161 static 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 
    191 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
    192 static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
    193 
    194 static 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 
    210 static 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 
    234 static 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 
    258 static 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 
    267 typedef 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 
    275 static XSpicePointer *g_xspice_pointer;
    276 
    277 static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
    278                          uint32_t buttons_state)
    279 {
    280     // TODO
    281 }
    282 
    283 static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
    284 {
    285     // TODO
    286 }
    287 
    288 static 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 
    297 static 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 
    311 void 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 
    317 static 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 
    323 void 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 
    337 static 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 
    350 static 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 
    359 static 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 
    370 static char unknown_type_string[] = "UNKNOWN";
    371 
    372 static int
    373 XSpiceKeyboardPreInit(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 
    391 static int
    392 XSpicePointerPreInit(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 
    412 static void
    413 XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
    414 {
    415 }
    416 
    417 static void
    418 XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
    419 {
    420 }
    421 
    422 void xspice_add_input_drivers(pointer module)
    423 {
    424     xf86AddInputDriver(&XSPICE_POINTER, module, 0);
    425     xf86AddInputDriver(&XSPICE_KEYBOARD, module, 0);
    426 }
    427