xenocara/driver/xf86-input-keyboard/src/kbd.c

435 lines
11 KiB
C

/*
* Copyright (c) 2002 by The XFree86 Project, Inc.
* Author: Ivan Pascal.
*
* Based on the code from
* xf86Config.c which is
* Copyright 1991-2002 by The XFree86 Project, Inc.
* Copyright 1997 by Metro Link, Inc.
* xf86Events.c and xf86Io.c which are
* Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <xorg-server.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include "xf86.h"
#include "atKeynames.h"
#include "xf86Privstr.h"
#include <X11/extensions/XI.h>
#include <X11/extensions/XIproto.h>
#include "extnsionst.h"
#include "extinit.h"
#include "inputstr.h"
#include "xf86Xinput.h"
#include "xf86_OSproc.h"
#include "xf86OSKbd.h"
#include "compiler.h"
#include "exevents.h"
#include <X11/Xatom.h>
#include "xserver-properties.h"
#include "xkbstr.h"
#include "xkbsrv.h"
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 23
#define HAVE_THREADED_INPUT 1
#endif
#define CAPSFLAG 1
#define NUMFLAG 2
#define SCROLLFLAG 4
#define MODEFLAG 8
#define COMPOSEFLAG 16
/* Used to know when the first DEVICE_ON after a DEVICE_INIT is called */
#define INITFLAG (1U << 31)
static int KbdPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
static int KbdProc(DeviceIntPtr device, int what);
static void KbdCtrl(DeviceIntPtr device, KeybdCtrl *ctrl);
static void KbdBell(int percent, DeviceIntPtr dev, pointer ctrl, int unused);
static void PostKbdEvent(InputInfoPtr pInfo, unsigned int key, Bool down);
static void InitKBD(InputInfoPtr pInfo, Bool init);
static void UpdateLeds(InputInfoPtr pInfo);
static const char *kbdDefaults[] = {
#ifdef __NetBSD__
#ifdef DEFAULT_TO_WSKBD
"Protocol", "wskbd",
#else
"Protocol", "standard",
#endif
#else /* NetBSD */
"Protocol", "standard",
#endif /* NetBSD */
"XkbRules", XKB_DFLT_RULES,
"XkbModel", "pc105",
"XkbLayout", "us",
NULL
};
static char *xkb_rules;
static char *xkb_model;
static char *xkb_layout;
static char *xkb_variant;
static char *xkb_options;
_X_EXPORT InputDriverRec KBD = {
1,
"kbd",
NULL,
KbdPreInit,
NULL,
NULL
};
_X_EXPORT InputDriverRec KEYBOARD = {
1,
"keyboard",
NULL,
KbdPreInit,
NULL,
NULL
};
static XF86ModuleVersionInfo xf86KbdVersionRec = {
"kbd",
MODULEVENDORSTRING,
MODINFOSTRING1,
MODINFOSTRING2,
XORG_VERSION_CURRENT,
PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
ABI_CLASS_XINPUT,
ABI_XINPUT_VERSION,
MOD_CLASS_XINPUT,
{0, 0, 0, 0}
};
static pointer
xf86KbdPlug(pointer module, pointer options, int *errmaj, int *errmin)
{
xf86AddInputDriver(&KBD, module, 0);
return module;
}
_X_EXPORT XF86ModuleData kbdModuleData = {
&xf86KbdVersionRec,
xf86KbdPlug,
NULL
};
static int
KbdPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
{
KbdDevPtr pKbd;
char *s;
const char **defaults;
int rc = Success;
/* Initialise the InputInfoRec. */
pInfo->type_name = XI_KEYBOARD;
pInfo->device_control = KbdProc;
/*
* We don't specify our own read_input function. We expect
* an OS specific readInput() function to handle this.
*/
pInfo->read_input = NULL;
pInfo->control_proc = NULL;
pInfo->switch_mode = NULL;
pInfo->fd = -1;
pInfo->dev = NULL;
defaults = kbdDefaults;
xf86CollectInputOptions(pInfo, defaults);
xf86ProcessCommonOptions(pInfo, pInfo->options);
if (!(pKbd = calloc(sizeof(KbdDevRec), 1))) {
rc = BadAlloc;
goto out;
}
pInfo->private = pKbd;
pKbd->PostEvent = PostKbdEvent;
if (!xf86OSKbdPreInit(pInfo)) {
rc = BadAlloc;
goto out;
}
if (!pKbd->OpenKeyboard(pInfo)) {
rc = BadMatch;
goto out;
}
if ((s = xf86SetStrOption(pInfo->options, "XLeds", NULL))) {
char *l, *end;
unsigned int i;
l = strtok(s, " \t\n");
while (l) {
i = strtoul(l, &end, 0);
if (*end == '\0')
pKbd->xledsMask |= 1L << (i - 1);
else {
xf86Msg(X_ERROR, "\"%s\" is not a valid XLeds value", l);
}
l = strtok(NULL, " \t\n");
}
free(s);
}
xkb_rules = xf86SetStrOption(pInfo->options, "XkbRules", NULL);
xkb_model = xf86SetStrOption(pInfo->options, "XkbModel", NULL);
xkb_layout = xf86SetStrOption(pInfo->options, "XkbLayout", NULL);
xkb_variant = xf86SetStrOption(pInfo->options, "XkbVariant", NULL);
xkb_options = xf86SetStrOption(pInfo->options, "XkbOptions", NULL);
pKbd->CustomKeycodes = xf86SetBoolOption(pInfo->options, "CustomKeycodes",
FALSE);
out:
return rc;
}
static void
KbdBell(int percent, DeviceIntPtr dev, pointer ctrl, int unused)
{
InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
pKbd->Bell(pInfo, percent, ((KeybdCtrl*) ctrl)->bell_pitch,
((KeybdCtrl*) ctrl)->bell_duration);
}
static void
UpdateLeds(InputInfoPtr pInfo)
{
KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
unsigned long leds = 0;
if (pKbd->keyLeds & CAPSFLAG) leds |= XLED1;
if (pKbd->keyLeds & NUMFLAG) leds |= XLED2;
if (pKbd->keyLeds & SCROLLFLAG ||
pKbd->keyLeds & MODEFLAG) leds |= XLED3;
if (pKbd->keyLeds & COMPOSEFLAG) leds |= XLED4;
pKbd->leds = (pKbd->leds & pKbd->xledsMask) | (leds & ~pKbd->xledsMask);
pKbd->SetLeds(pInfo, pKbd->leds);
}
static void
KbdCtrl( DeviceIntPtr device, KeybdCtrl *ctrl)
{
unsigned long leds;
InputInfoPtr pInfo = (InputInfoPtr) device->public.devicePrivate;
KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
if ( ctrl->leds & XLED1) {
pKbd->keyLeds |= CAPSFLAG;
} else {
pKbd->keyLeds &= ~CAPSFLAG;
}
if ( ctrl->leds & XLED2) {
pKbd->keyLeds |= NUMFLAG;
} else {
pKbd->keyLeds &= ~NUMFLAG;
}
if ( ctrl->leds & XLED3) {
pKbd->keyLeds |= SCROLLFLAG;
} else {
pKbd->keyLeds &= ~SCROLLFLAG;
}
if ( ctrl->leds & (XCOMP|XLED4) ) {
pKbd->keyLeds |= COMPOSEFLAG;
} else {
pKbd->keyLeds &= ~COMPOSEFLAG;
}
leds = ctrl->leds & ~(XCAPS | XNUM | XSCR); /* ??? */
pKbd->leds = leds;
pKbd->SetLeds(pInfo, pKbd->leds);
}
static void
InitKBD(InputInfoPtr pInfo, Bool init)
{
KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
pKbd->scanPrefix = 0;
if (init) {
pKbd->keyLeds = pKbd->GetLeds(pInfo);
UpdateLeds(pInfo);
pKbd->keyLeds |= INITFLAG;
} else {
unsigned long leds = pKbd->keyLeds;
pKbd->keyLeds = pKbd->GetLeds(pInfo);
UpdateLeds(pInfo);
if ((pKbd->keyLeds & CAPSFLAG) !=
((leds & INITFLAG) ? 0 : (leds & CAPSFLAG))) {
pKbd->PostEvent(pInfo, KEY_CapsLock, TRUE);
pKbd->PostEvent(pInfo, KEY_CapsLock, FALSE);
}
if ((pKbd->keyLeds & NUMFLAG) !=
(leds & INITFLAG ? 0 : leds & NUMFLAG)) {
pKbd->PostEvent(pInfo, KEY_NumLock, TRUE);
pKbd->PostEvent(pInfo, KEY_NumLock, FALSE);
}
}
}
static int
KbdProc(DeviceIntPtr device, int what)
{
InputInfoPtr pInfo = device->public.devicePrivate;
KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
XkbRMLVOSet rmlvo;
KeySymsRec keySyms;
CARD8 modMap[MAP_LENGTH];
int ret;
switch (what) {
case DEVICE_INIT:
ret = pKbd->KbdInit(pInfo, what);
if (ret != Success)
return ret;
pKbd->KbdGetMapping(pInfo, &keySyms, modMap);
device->public.on = FALSE;
rmlvo.rules = xkb_rules;
rmlvo.model = xkb_model;
rmlvo.layout = xkb_layout;
rmlvo.variant = xkb_variant;
rmlvo.options = xkb_options;
if (!InitKeyboardDeviceStruct(device, &rmlvo, KbdBell, KbdCtrl))
{
xf86Msg(X_ERROR, "%s: Keyboard initialization failed. This "
"could be a missing or incorrect setup of "
"xkeyboard-config.\n", device->name);
return BadValue;
}
# ifdef XI_PROP_DEVICE_NODE
{
const char *device_node =
xf86CheckStrOption(pInfo->options, "Device", NULL);
if (device_node)
{
Atom prop_device = MakeAtom(XI_PROP_DEVICE_NODE,
strlen(XI_PROP_DEVICE_NODE), TRUE);
XIChangeDeviceProperty(device, prop_device, XA_STRING, 8,
PropModeReplace, strlen(device_node),
device_node, FALSE);
}
}
# endif /* XI_PROP_DEVICE_NODE */
InitKBD(pInfo, TRUE);
break;
case DEVICE_ON:
if (device->public.on)
break;
/*
* Set the keyboard into "direct" mode and turn on
* event translation.
*/
if ((ret = pKbd->KbdOn(pInfo, what)) != Success)
return ret;
/*
* Discard any pending input after a VT switch to prevent the server
* passing on parts of the VT switch sequence.
*/
if (pInfo->fd >= 0) {
xf86FlushInput(pInfo->fd);
#if HAVE_THREADED_INPUT
xf86AddEnabledDevice(pInfo);
#else
AddEnabledDevice(pInfo->fd);
#endif
}
device->public.on = TRUE;
InitKBD(pInfo, FALSE);
break;
case DEVICE_CLOSE:
case DEVICE_OFF:
/*
* Restore original keyboard directness and translation.
*/
if (pInfo->fd != -1) {
#if HAVE_THREADED_INPUT
xf86RemoveEnabledDevice(pInfo);
#else
RemoveEnabledDevice(pInfo->fd);
#endif
}
pKbd->KbdOff(pInfo, what);
device->public.on = FALSE;
break;
default:
return BadValue;
}
return (Success);
}
static void
PostKbdEvent(InputInfoPtr pInfo, unsigned int scanCode, Bool down)
{
KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
DeviceIntPtr device = pInfo->dev;
KeyClassRec *keyc = device->key;
int state;
#ifdef DEBUG
LogMessageVerbSigSafe(X_INFO, -1, "kbd driver rec scancode: 0x%x %s\n", scanCode, down ? "down" : "up");
#endif
/*
* First do some special scancode remapping ...
*/
if (pKbd->RemapScanCode != NULL) {
if (pKbd->RemapScanCode(pInfo, (int*) &scanCode))
return;
} else {
if (pKbd->scancodeMap != NULL) {
TransMapPtr map = pKbd->scancodeMap;
if (scanCode >= map->begin && scanCode < map->end)
scanCode = map->map[scanCode - map->begin];
}
}
/*
* PC keyboards generate separate key codes for
* Alt+Print and Control+Pause but in the X keyboard model
* they need to get the same key code as the base key on the same
* physical keyboard key.
*/
state = XkbStateFieldFromRec(&keyc->xkbInfo->state);
if (((state & AltMask) == AltMask) && (scanCode == KEY_SysReqest))
scanCode = KEY_Print;
else if (scanCode == KEY_Break)
scanCode = KEY_Pause;
xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down);
}