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