kbd.c revision 1450c749
1/* 2 * Copyright (c) 2002 by The XFree86 Project, Inc. 3 * Author: Ivan Pascal. 4 * 5 * Based on the code from 6 * xf86Config.c which is 7 * Copyright 1991-2002 by The XFree86 Project, Inc. 8 * Copyright 1997 by Metro Link, Inc. 9 * xf86Events.c and xf86Io.c which are 10 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. 11 */ 12 13#ifdef HAVE_CONFIG_H 14#include "config.h" 15#endif 16 17#include <stdio.h> 18#include <string.h> 19#include <stdlib.h> 20#include <unistd.h> 21#include <xorg-server.h> 22 23#include <X11/X.h> 24#include <X11/Xproto.h> 25 26#include "xf86.h" 27#include "atKeynames.h" 28#include "xf86Privstr.h" 29 30#include <X11/extensions/XI.h> 31#include <X11/extensions/XIproto.h> 32#include "extnsionst.h" 33#include "extinit.h" 34#include "inputstr.h" 35 36#include "xf86Xinput.h" 37#include "xf86_OSproc.h" 38#include "xf86OSKbd.h" 39#include "compiler.h" 40 41#include "exevents.h" 42#include <X11/Xatom.h> 43#include "xserver-properties.h" 44 45#include "xkbstr.h" 46#include "xkbsrv.h" 47 48#define CAPSFLAG 1 49#define NUMFLAG 2 50#define SCROLLFLAG 4 51#define MODEFLAG 8 52#define COMPOSEFLAG 16 53/* Used to know when the first DEVICE_ON after a DEVICE_INIT is called */ 54#define INITFLAG (1U << 31) 55 56#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12 57static InputInfoPtr KbdPreInit(InputDriverPtr drv, IDevPtr dev, int flags); 58#else 59static int KbdPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags); 60#endif 61static int KbdProc(DeviceIntPtr device, int what); 62static void KbdCtrl(DeviceIntPtr device, KeybdCtrl *ctrl); 63static void KbdBell(int percent, DeviceIntPtr dev, pointer ctrl, int unused); 64static void PostKbdEvent(InputInfoPtr pInfo, unsigned int key, Bool down); 65 66static void InitKBD(InputInfoPtr pInfo, Bool init); 67static void UpdateLeds(InputInfoPtr pInfo); 68 69_X_EXPORT InputDriverRec KBD = { 70 1, 71 "kbd", 72 NULL, 73 KbdPreInit, 74 NULL, 75 NULL 76}; 77 78_X_EXPORT InputDriverRec KEYBOARD = { 79 1, 80 "keyboard", 81 NULL, 82 KbdPreInit, 83 NULL, 84 NULL 85}; 86 87static const char *kbdDefaults[] = { 88#ifdef __NetBSD__ 89#ifdef DEFAULT_TO_WSKBD 90 "Protocol", "wskbd", 91#else 92 "Protocol", "standard", 93#endif 94#else /* NetBSD */ 95 "Protocol", "standard", 96#endif /* NetBSD */ 97 "XkbRules", "base", 98 "XkbModel", "pc105", 99 "XkbLayout", "us", 100 NULL 101}; 102 103static char *xkb_rules; 104static char *xkb_model; 105static char *xkb_layout; 106static char *xkb_variant; 107static char *xkb_options; 108 109#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12 110static int 111NewKbdPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags); 112 113static InputInfoPtr 114KbdPreInit(InputDriverPtr drv, IDevPtr dev, int flags) 115{ 116 InputInfoPtr pInfo; 117 118 if (!(pInfo = xf86AllocateInput(drv, 0))) 119 return NULL; 120 121 pInfo->name = dev->identifier; 122 pInfo->flags = XI86_KEYBOARD_CAPABLE; 123 pInfo->conversion_proc = NULL; 124 pInfo->reverse_conversion_proc = NULL; 125 pInfo->private_flags = 0; 126 pInfo->always_core_feedback = NULL; 127 pInfo->conf_idev = dev; 128 pInfo->close_proc = NULL; 129 130 if (NewKbdPreInit(drv, pInfo, flags) == Success) 131 { 132 pInfo->flags |= XI86_CONFIGURED; 133 return pInfo; 134 } 135 136 xf86DeleteInput(pInfo, 0); 137 return NULL; 138} 139 140static int 141NewKbdPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) 142#else 143static int 144KbdPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) 145#endif 146{ 147 KbdDevPtr pKbd; 148 char *s; 149 const char **defaults; 150 int rc = Success; 151 152 /* Initialise the InputInfoRec. */ 153 pInfo->type_name = XI_KEYBOARD; 154 pInfo->device_control = KbdProc; 155 /* 156 * We don't specify our own read_input function. We expect 157 * an OS specific readInput() function to handle this. 158 */ 159 pInfo->read_input = NULL; 160 pInfo->control_proc = NULL; 161 pInfo->switch_mode = NULL; 162 pInfo->fd = -1; 163 pInfo->dev = NULL; 164 165 defaults = kbdDefaults; 166 xf86CollectInputOptions(pInfo, defaults 167#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12 168 , NULL 169#endif 170 ); 171 xf86ProcessCommonOptions(pInfo, pInfo->options); 172 173 if (!(pKbd = calloc(sizeof(KbdDevRec), 1))) { 174 rc = BadAlloc; 175 goto out; 176 } 177 178 pInfo->private = pKbd; 179 pKbd->PostEvent = PostKbdEvent; 180 181 if (!xf86OSKbdPreInit(pInfo)) { 182 rc = BadAlloc; 183 goto out; 184 } 185 186 if (!pKbd->OpenKeyboard(pInfo)) { 187 rc = BadMatch; 188 goto out; 189 } 190 191 if ((s = xf86SetStrOption(pInfo->options, "XLeds", NULL))) { 192 char *l, *end; 193 unsigned int i; 194 l = strtok(s, " \t\n"); 195 while (l) { 196 i = strtoul(l, &end, 0); 197 if (*end == '\0') 198 pKbd->xledsMask |= 1L << (i - 1); 199 else { 200 xf86Msg(X_ERROR, "\"%s\" is not a valid XLeds value", l); 201 } 202 l = strtok(NULL, " \t\n"); 203 } 204 free(s); 205 } 206 207 xkb_rules = xf86SetStrOption(pInfo->options, "XkbRules", NULL); 208 xkb_model = xf86SetStrOption(pInfo->options, "XkbModel", NULL); 209 xkb_layout = xf86SetStrOption(pInfo->options, "XkbLayout", NULL); 210 xkb_variant = xf86SetStrOption(pInfo->options, "XkbVariant", NULL); 211 xkb_options = xf86SetStrOption(pInfo->options, "XkbOptions", NULL); 212 213 pKbd->CustomKeycodes = xf86SetBoolOption(pInfo->options, "CustomKeycodes", 214 FALSE); 215 216out: 217 return rc; 218} 219 220static void 221KbdBell(int percent, DeviceIntPtr dev, pointer ctrl, int unused) 222{ 223 InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate; 224 KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; 225 pKbd->Bell(pInfo, percent, ((KeybdCtrl*) ctrl)->bell_pitch, 226 ((KeybdCtrl*) ctrl)->bell_duration); 227} 228 229static void 230UpdateLeds(InputInfoPtr pInfo) 231{ 232 KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; 233 unsigned long leds = 0; 234 235 if (pKbd->keyLeds & CAPSFLAG) leds |= XLED1; 236 if (pKbd->keyLeds & NUMFLAG) leds |= XLED2; 237 if (pKbd->keyLeds & SCROLLFLAG || 238 pKbd->keyLeds & MODEFLAG) leds |= XLED3; 239 if (pKbd->keyLeds & COMPOSEFLAG) leds |= XLED4; 240 241 pKbd->leds = (pKbd->leds & pKbd->xledsMask) | (leds & ~pKbd->xledsMask); 242 pKbd->SetLeds(pInfo, pKbd->leds); 243} 244 245static void 246KbdCtrl( DeviceIntPtr device, KeybdCtrl *ctrl) 247{ 248 unsigned long leds; 249 InputInfoPtr pInfo = (InputInfoPtr) device->public.devicePrivate; 250 KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; 251 252 if ( ctrl->leds & XLED1) { 253 pKbd->keyLeds |= CAPSFLAG; 254 } else { 255 pKbd->keyLeds &= ~CAPSFLAG; 256 } 257 if ( ctrl->leds & XLED2) { 258 pKbd->keyLeds |= NUMFLAG; 259 } else { 260 pKbd->keyLeds &= ~NUMFLAG; 261 } 262 if ( ctrl->leds & XLED3) { 263 pKbd->keyLeds |= SCROLLFLAG; 264 } else { 265 pKbd->keyLeds &= ~SCROLLFLAG; 266 } 267 if ( ctrl->leds & (XCOMP|XLED4) ) { 268 pKbd->keyLeds |= COMPOSEFLAG; 269 } else { 270 pKbd->keyLeds &= ~COMPOSEFLAG; 271 } 272 leds = ctrl->leds & ~(XCAPS | XNUM | XSCR); /* ??? */ 273 pKbd->leds = leds; 274 pKbd->SetLeds(pInfo, pKbd->leds); 275} 276 277static void 278InitKBD(InputInfoPtr pInfo, Bool init) 279{ 280 KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; 281 282 pKbd->scanPrefix = 0; 283 284 if (init) { 285 pKbd->keyLeds = pKbd->GetLeds(pInfo); 286 UpdateLeds(pInfo); 287 pKbd->keyLeds |= INITFLAG; 288 } else { 289 unsigned long leds = pKbd->keyLeds; 290 291 pKbd->keyLeds = pKbd->GetLeds(pInfo); 292 UpdateLeds(pInfo); 293 if ((pKbd->keyLeds & CAPSFLAG) != 294 ((leds & INITFLAG) ? 0 : (leds & CAPSFLAG))) { 295 pKbd->PostEvent(pInfo, KEY_CapsLock, TRUE); 296 pKbd->PostEvent(pInfo, KEY_CapsLock, FALSE); 297 } 298 if ((pKbd->keyLeds & NUMFLAG) != 299 (leds & INITFLAG ? 0 : leds & NUMFLAG)) { 300 pKbd->PostEvent(pInfo, KEY_NumLock, TRUE); 301 pKbd->PostEvent(pInfo, KEY_NumLock, FALSE); 302 } 303 } 304} 305 306static int 307KbdProc(DeviceIntPtr device, int what) 308{ 309 310 InputInfoPtr pInfo = device->public.devicePrivate; 311 KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; 312 KeySymsRec keySyms; 313 CARD8 modMap[MAP_LENGTH]; 314 int ret; 315 316 switch (what) { 317 case DEVICE_INIT: 318 ret = pKbd->KbdInit(pInfo, what); 319 if (ret != Success) 320 return ret; 321 322 pKbd->KbdGetMapping(pInfo, &keySyms, modMap); 323 324 device->public.on = FALSE; 325#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 5 326 { 327 XkbRMLVOSet rmlvo; 328 rmlvo.rules = xkb_rules; 329 rmlvo.model = xkb_model; 330 rmlvo.layout = xkb_layout; 331 rmlvo.variant = xkb_variant; 332 rmlvo.options = xkb_options; 333 334 if (!InitKeyboardDeviceStruct(device, &rmlvo, KbdBell, KbdCtrl)) 335 { 336 xf86Msg(X_ERROR, "%s: Keyboard initialization failed. This " 337 "could be a missing or incorrect setup of " 338 "xkeyboard-config.\n", device->name); 339 340 return BadValue; 341 } 342 } 343# ifdef XI_PROP_DEVICE_NODE 344 { 345 const char *device_node = 346 xf86CheckStrOption(pInfo->options, "Device", NULL); 347 348 if (device_node) 349 { 350 Atom prop_device = MakeAtom(XI_PROP_DEVICE_NODE, 351 strlen(XI_PROP_DEVICE_NODE), TRUE); 352 XIChangeDeviceProperty(device, prop_device, XA_STRING, 8, 353 PropModeReplace, strlen(device_node), 354 device_node, FALSE); 355 } 356 } 357# endif /* XI_PROP_DEVICE_NODE */ 358#else 359 { 360 XkbComponentNamesRec xkbnames; 361 memset(&xkbnames, 0, sizeof(xkbnames)); 362 XkbSetRulesDflts(xkb_rules, xkb_model, xkb_layout, 363 xkb_variant, xkb_options); 364 XkbInitKeyboardDeviceStruct(device, &xkbnames, &keySyms, 365 modMap, KbdBell, 366 (KbdCtrlProcPtr)KbdCtrl); 367 } 368#endif /* XINPUT ABI 5*/ 369 InitKBD(pInfo, TRUE); 370 break; 371 case DEVICE_ON: 372 if (device->public.on) 373 break; 374 /* 375 * Set the keyboard into "direct" mode and turn on 376 * event translation. 377 */ 378 if ((ret = pKbd->KbdOn(pInfo, what)) != Success) 379 return ret; 380 /* 381 * Discard any pending input after a VT switch to prevent the server 382 * passing on parts of the VT switch sequence. 383 */ 384 if (pInfo->fd >= 0) { 385 xf86FlushInput(pInfo->fd); 386 AddEnabledDevice(pInfo->fd); 387 } 388 389 device->public.on = TRUE; 390 InitKBD(pInfo, FALSE); 391 break; 392 393 case DEVICE_CLOSE: 394 case DEVICE_OFF: 395 396 /* 397 * Restore original keyboard directness and translation. 398 */ 399 if (pInfo->fd != -1) 400 RemoveEnabledDevice(pInfo->fd); 401 pKbd->KbdOff(pInfo, what); 402 device->public.on = FALSE; 403 break; 404 405 default: 406 return BadValue; 407 } 408 return (Success); 409} 410 411static void 412PostKbdEvent(InputInfoPtr pInfo, unsigned int scanCode, Bool down) 413{ 414 415 KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; 416 DeviceIntPtr device = pInfo->dev; 417 KeyClassRec *keyc = device->key; 418 int state; 419 420#ifdef DEBUG 421 LogMessageVerbSigSafe(X_INFO, -1, "kbd driver rec scancode: 0x%x %s\n", scanCode, down ? "down" : "up"); 422#endif 423 424 /* 425 * First do some special scancode remapping ... 426 */ 427 if (pKbd->RemapScanCode != NULL) { 428 if (pKbd->RemapScanCode(pInfo, (int*) &scanCode)) 429 return; 430 } else { 431 if (pKbd->scancodeMap != NULL) { 432 TransMapPtr map = pKbd->scancodeMap; 433 if (scanCode >= map->begin && scanCode < map->end) 434 scanCode = map->map[scanCode - map->begin]; 435 } 436 } 437 438 /* 439 * PC keyboards generate separate key codes for 440 * Alt+Print and Control+Pause but in the X keyboard model 441 * they need to get the same key code as the base key on the same 442 * physical keyboard key. 443 */ 444 445 state = XkbStateFieldFromRec(&keyc->xkbInfo->state); 446 447 if (((state & AltMask) == AltMask) && (scanCode == KEY_SysReqest)) 448 scanCode = KEY_Print; 449 else if (scanCode == KEY_Break) 450 scanCode = KEY_Pause; 451 452 xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down); 453} 454 455static void 456xf86KbdUnplug(pointer p) 457{ 458} 459 460static pointer 461xf86KbdPlug(pointer module, 462 pointer options, 463 int *errmaj, 464 int *errmin) 465{ 466 xf86AddInputDriver(&KBD, module, 0); 467 468 return module; 469} 470 471static XF86ModuleVersionInfo xf86KbdVersionRec = 472{ 473 "kbd", 474 MODULEVENDORSTRING, 475 MODINFOSTRING1, 476 MODINFOSTRING2, 477 XORG_VERSION_CURRENT, 478 PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL, 479 ABI_CLASS_XINPUT, 480 ABI_XINPUT_VERSION, 481 MOD_CLASS_XINPUT, 482 {0, 0, 0, 0} /* signature, to be patched into the file by */ 483 /* a tool */ 484}; 485 486_X_EXPORT XF86ModuleData kbdModuleData = { 487 &xf86KbdVersionRec, 488 xf86KbdPlug, 489 xf86KbdUnplug 490}; 491