#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/extensions/XIproto.h>
#include <linux/input.h>
#include <misc.h>
#include <xf86.h>
#include <xf86str.h>
#include <xf86_OSproc.h>
#include <xf86_ansic.h>
#include <xf86_libc.h>
#include <xf86Xinput.h>
#include <exevents.h>
#include <mipointer.h>
#include <xf86Module.h>
#ifndef EVIOCGRAB
#define EVIOCGRAB _IOW('E', 0x90, int)
#endif
#ifndef EV_SYN
#define EV_SYN EV_RST
#endif
#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
#define MIN_KEYCODE 8
#define GLYPHS_PER_KEY 2
#define AltMask Mod1Mask
#define NumLockMask Mod2Mask
#define AltLangMask Mod3Mask
#define KanaMask Mod4Mask
#define ScrollLockMask Mod5Mask
#define CAPSFLAG 1
#define NUMFLAG 2
#define SCROLLFLAG 4
#define MODEFLAG 8
#define COMPOSEFLAG 16
typedef struct {
int kernel24;
} EvdevRec, *EvdevPtr;
static int wheel_up_button = 4;
static int wheel_down_button = 5;
static int wheel_left_button = 6;
static int wheel_right_button = 7;
static void
PostButtonClicks(InputInfoPtr pInfo, int button, int count)
{
int i;
for (i = 0; i < count; i++) {
xf86PostButtonEvent(pInfo->dev, 0, button, 1, 0, 0);
xf86PostButtonEvent(pInfo->dev, 0, button, 0, 0, 0);
}
}
static void
EvdevReadInput(InputInfoPtr pInfo)
{
struct input_event ev;
int len, value;
int dx, dy;
dx = 0;
dy = 0;
while (xf86WaitForInput (pInfo->fd, 0) > 0) {
len = read(pInfo->fd, &ev, sizeof ev);
if (len != sizeof ev) {
xf86Msg(X_ERROR, "Read error: %s\n", strerror(errno));
break;
}
value = ev.value;
switch (ev.type) {
case EV_REL:
switch (ev.code) {
case REL_X:
dx += value;
break;
case REL_Y:
dy += value;
break;
case REL_WHEEL:
if (value > 0)
PostButtonClicks(pInfo, wheel_up_button, value);
if (value < 0)
PostButtonClicks(pInfo, wheel_down_button, -value);
break;
case REL_HWHEEL:
if (value > 0)
PostButtonClicks(pInfo, wheel_right_button, value);
if (value < 0)
PostButtonClicks(pInfo, wheel_left_button, -value);
break;
}
break;
case EV_ABS:
break;
case EV_KEY:
switch (ev.code) {
case BTN_LEFT:
case BTN_RIGHT:
case BTN_MIDDLE:
xf86PostButtonEvent(pInfo->dev, 0, ev.code - BTN_LEFT + 1,
value, 0, 0);
break;
case BTN_SIDE:
case BTN_EXTRA:
case BTN_FORWARD:
case BTN_BACK:
xf86PostButtonEvent(pInfo->dev, 0, ev.code - BTN_LEFT + 5,
value, 0, 0);
break;
default:
xf86PostKeyboardEvent(pInfo->dev,
ev.code + MIN_KEYCODE, value);
}
break;
case EV_SYN:
break;
}
}
if (dx != 0 || dy != 0)
xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy);
}
#define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8)))
static void
EvdevPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl)
{
}
static KeySym map[] = {
NoSymbol, NoSymbol,
XK_Escape, NoSymbol,
XK_1, XK_exclam,
XK_2, XK_at,
XK_3, XK_numbersign,
XK_4, XK_dollar,
XK_5, XK_percent,
XK_6, XK_asciicircum,
XK_7, XK_ampersand,
XK_8, XK_asterisk,
XK_9, XK_parenleft,
XK_0, XK_parenright,
XK_minus, XK_underscore,
XK_equal, XK_plus,
XK_BackSpace, NoSymbol,
XK_Tab, XK_ISO_Left_Tab,
XK_Q, NoSymbol,
XK_W, NoSymbol,
XK_E, NoSymbol,
XK_R, NoSymbol,
XK_T, NoSymbol,
XK_Y, NoSymbol,
XK_U, NoSymbol,
XK_I, NoSymbol,
XK_O, NoSymbol,
XK_P, NoSymbol,
XK_bracketleft, XK_braceleft,
XK_bracketright,XK_braceright,
XK_Return, NoSymbol,
XK_Control_L, NoSymbol,
XK_A, NoSymbol,
XK_S, NoSymbol,
XK_D, NoSymbol,
XK_F, NoSymbol,
XK_G, NoSymbol,
XK_H, NoSymbol,
XK_J, NoSymbol,
XK_K, NoSymbol,
XK_L, NoSymbol,
XK_semicolon, XK_colon,
XK_quoteright, XK_quotedbl,
XK_quoteleft, XK_asciitilde,
XK_Shift_L, NoSymbol,
XK_backslash, XK_bar,
XK_Z, NoSymbol,
XK_X, NoSymbol,
XK_C, NoSymbol,
XK_V, NoSymbol,
XK_B, NoSymbol,
XK_N, NoSymbol,
XK_M, NoSymbol,
XK_comma, XK_less,
XK_period, XK_greater,
XK_slash, XK_question,
XK_Shift_R, NoSymbol,
XK_KP_Multiply, NoSymbol,
XK_Alt_L, XK_Meta_L,
XK_space, NoSymbol,
XK_Caps_Lock, NoSymbol,
XK_F1, NoSymbol,
XK_F2, NoSymbol,
XK_F3, NoSymbol,
XK_F4, NoSymbol,
XK_F5, NoSymbol,
XK_F6, NoSymbol,
XK_F7, NoSymbol,
XK_F8, NoSymbol,
XK_F9, NoSymbol,
XK_F10, NoSymbol,
XK_Num_Lock, NoSymbol,
XK_Scroll_Lock, NoSymbol,
XK_KP_Home, XK_KP_7,
XK_KP_Up, XK_KP_8,
XK_KP_Prior, XK_KP_9,
XK_KP_Subtract, NoSymbol,
XK_KP_Left, XK_KP_4,
XK_KP_Begin, XK_KP_5,
XK_KP_Right, XK_KP_6,
XK_KP_Add, NoSymbol,
XK_KP_End, XK_KP_1,
XK_KP_Down, XK_KP_2,
XK_KP_Next, XK_KP_3,
XK_KP_Insert, XK_KP_0,
XK_KP_Delete, XK_KP_Decimal,
NoSymbol, NoSymbol,
XK_F13, NoSymbol,
XK_less, XK_greater,
XK_F11, NoSymbol,
XK_F12, NoSymbol,
XK_F14, NoSymbol,
XK_F15, NoSymbol,
XK_F16, NoSymbol,
XK_F17, NoSymbol,
XK_F18, NoSymbol,
XK_F19, NoSymbol,
XK_F20, NoSymbol,
XK_KP_Enter, NoSymbol,
XK_Control_R, NoSymbol,
XK_KP_Divide, NoSymbol,
XK_Print, XK_Sys_Req,
XK_Alt_R, XK_Meta_R,
NoSymbol, NoSymbol,
XK_Home, NoSymbol,
XK_Up, NoSymbol,
XK_Prior, NoSymbol,
XK_Left, NoSymbol,
XK_Right, NoSymbol,
XK_End, NoSymbol,
XK_Down, NoSymbol,
XK_Next, NoSymbol,
XK_Insert, NoSymbol,
XK_Delete, NoSymbol,
NoSymbol, NoSymbol,
NoSymbol, NoSymbol,
NoSymbol, NoSymbol,
NoSymbol, NoSymbol,
NoSymbol, NoSymbol,
NoSymbol, NoSymbol,
XK_KP_Equal, NoSymbol,
NoSymbol, NoSymbol,
NoSymbol, NoSymbol,
XK_F21, NoSymbol,
XK_F22, NoSymbol,
XK_F23, NoSymbol,
XK_F24, NoSymbol,
XK_KP_Separator, NoSymbol,
XK_Meta_L, NoSymbol,
XK_Meta_R, NoSymbol,
XK_Multi_key, NoSymbol,
};
static void
EvdevKbdBell(int percent, DeviceIntPtr dev, pointer ctrl, int unused)
{
}
static void
EvdevKbdCtrl(DeviceIntPtr device, KeybdCtrl *ctrl)
{
static struct { int xbit, code; } bits[] = {
{ CAPSFLAG, LED_CAPSL },
{ NUMFLAG, LED_NUML },
{ SCROLLFLAG, LED_SCROLLL },
{ MODEFLAG, LED_KANA },
{ COMPOSEFLAG, LED_COMPOSE }
};
InputInfoPtr pInfo;
struct input_event ev[ArrayLength(bits)];
int i;
pInfo = device->public.devicePrivate;
for (i = 0; i < ArrayLength(bits); i++) {
ev[i].type = EV_LED;
ev[i].code = bits[i].code;
ev[i].value = (ctrl->leds & bits[i].xbit) > 0;
}
write(pInfo->fd, ev, sizeof ev);
}
static int
EvdevAddKeyClass(DeviceIntPtr device)
{
InputInfoPtr pInfo;
KeySymsRec keySyms;
CARD8 modMap[MAP_LENGTH];
KeySym sym;
int i, j;
static struct { KeySym keysym; CARD8 mask; } modifiers[] = {
{ XK_Shift_L, ShiftMask },
{ XK_Shift_R, ShiftMask },
{ XK_Control_L, ControlMask },
{ XK_Control_R, ControlMask },
{ XK_Caps_Lock, LockMask },
{ XK_Alt_L, AltMask },
{ XK_Alt_R, AltMask },
{ XK_Num_Lock, NumLockMask },
{ XK_Scroll_Lock, ScrollLockMask },
{ XK_Mode_switch, AltLangMask }
};
pInfo = device->public.devicePrivate;
memset(modMap, 0, sizeof modMap);
for (i = 0; i < ArrayLength(map) / GLYPHS_PER_KEY; i++) {
sym = map[i * GLYPHS_PER_KEY];
for (j = 0; j < ArrayLength(modifiers); j++) {
if (modifiers[j].keysym == sym)
modMap[i + MIN_KEYCODE] = modifiers[j].mask;
}
}
keySyms.map = map;
keySyms.mapWidth = GLYPHS_PER_KEY;
keySyms.minKeyCode = MIN_KEYCODE;
keySyms.maxKeyCode = MIN_KEYCODE + ArrayLength(map) / GLYPHS_PER_KEY - 1;
if (!InitKeyClassDeviceStruct(device, &keySyms, modMap))
return !Success;
if (!InitFocusClassDeviceStruct(device))
return !Success;
if (!InitKbdFeedbackClassDeviceStruct(device, EvdevKbdBell, EvdevKbdCtrl))
return !Success;
pInfo->flags |= XI86_KEYBOARD_CAPABLE;
return Success;
}
static int
EvdevAddRelClass(DeviceIntPtr device)
{
InputInfoPtr pInfo;
pInfo = device->public.devicePrivate;
if (!InitValuatorClassDeviceStruct(device, 2,
miPointerGetMotionEvents,
miPointerGetMotionBufferSize(), 0))
return !Success;
xf86InitValuatorAxisStruct(device, 0, 0, -1, 1, 0, 1);
xf86InitValuatorDefaults(device, 0);
xf86InitValuatorAxisStruct(device, 1, 0, -1, 1, 0, 1);
xf86InitValuatorDefaults(device, 1);
xf86MotionHistoryAllocate(pInfo);
if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc))
return !Success;
return Success;
}
static int
EvdevAddButtonClass(DeviceIntPtr device)
{
CARD8 map[32];
InputInfoPtr pInfo;
int i;
pInfo = device->public.devicePrivate;
for (i = 0; i < ArrayLength(map); i++)
map[i] = i;
map[2] = 3;
map[3] = 2;
if (!InitButtonClassDeviceStruct(device, ArrayLength(map), map))
return !Success;
pInfo->flags |= XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS;
return Success;
}
static int
EvdevInit(DeviceIntPtr device)
{
InputInfoPtr pInfo;
pInfo = device->public.devicePrivate;
if (pInfo->flags & XI86_KEYBOARD_CAPABLE) {
EvdevAddKeyClass(device);
}
if (pInfo->flags & XI86_POINTER_CAPABLE) {
EvdevAddButtonClass(device);
EvdevAddRelClass(device);
}
return Success;
}
static int
EvdevProc(DeviceIntPtr device, int what)
{
InputInfoPtr pInfo;
EvdevPtr pEvdev;
pInfo = device->public.devicePrivate;
pEvdev = pInfo->private;
switch (what)
{
case DEVICE_INIT:
return EvdevInit(device);
case DEVICE_ON:
if (!pEvdev->kernel24 && ioctl(pInfo->fd, EVIOCGRAB, (void *)1))
xf86Msg(X_WARNING, "%s: Grab failed (%s)\n", pInfo->name,
strerror(errno));
xf86AddEnabledDevice(pInfo);
device->public.on = TRUE;
break;
case DEVICE_OFF:
if (!pEvdev->kernel24 && ioctl(pInfo->fd, EVIOCGRAB, (void *)0))
xf86Msg(X_WARNING, "%s: Release failed (%s)\n", pInfo->name,
strerror(errno));
xf86RemoveEnabledDevice(pInfo);
device->public.on = FALSE;
break;
case DEVICE_CLOSE:
xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
break;
}
return Success;
}
static Bool
EvdevConvert(InputInfoPtr pInfo, int first, int num, int v0, int v1, int v2,
int v3, int v4, int v5, int *x, int *y)
{
if (first == 0 && num == 2) {
*x = v0;
*y = v1;
return TRUE;
}
else
return FALSE;
}
static int
EvdevProbe(InputInfoPtr pInfo)
{
char key_bitmask[(KEY_MAX + 7) / 8];
char rel_bitmask[(REL_MAX + 7) / 8];
int i, has_axes, has_buttons, has_keys;
EvdevPtr pEvdev = pInfo->private;
if (ioctl(pInfo->fd, EVIOCGRAB, (void *)1) && errno == EINVAL) {
pEvdev->kernel24 = 1;
} else {
ioctl(pInfo->fd, EVIOCGRAB, (void *)0);
}
if (ioctl(pInfo->fd,
EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) < 0) {
xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno));
return 1;
}
if (ioctl(pInfo->fd,
EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) < 0) {
xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno));
return 1;
}
has_axes = FALSE;
has_buttons = FALSE;
has_keys = FALSE;
if (TestBit(REL_X, rel_bitmask) && TestBit(REL_Y, rel_bitmask)) {
xf86Msg(X_INFO, "%s: Found x and y relative axes\n", pInfo->name);
has_axes = TRUE;
}
if (TestBit(BTN_LEFT, key_bitmask)) {
xf86Msg(X_INFO, "%s: Found mouse buttons\n", pInfo->name);
has_buttons = TRUE;
}
for (i = 0; i < BTN_MISC; i++)
if (TestBit(i, key_bitmask))
break;
if (i < BTN_MISC) {
xf86Msg(X_INFO, "%s: Found keys\n", pInfo->name);
has_keys = TRUE;
}
if (has_axes && has_buttons) {
xf86Msg(X_INFO, "%s: Configuring as mouse\n", pInfo->name);
pInfo->flags |= XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS |
XI86_CONFIGURED;
pInfo->type_name = XI_MOUSE;
}
if (has_keys) {
if (pEvdev->kernel24) {
xf86Msg(X_INFO, "%s: Kernel < 2.6 is too old, ignoring keyboard\n",
pInfo->name);
} else {
xf86Msg(X_INFO, "%s: Configuring as keyboard\n", pInfo->name);
pInfo->flags |= XI86_KEYBOARD_CAPABLE | XI86_CONFIGURED;
pInfo->type_name = XI_KEYBOARD;
}
}
if ((pInfo->flags & XI86_CONFIGURED) == 0) {
xf86Msg(X_WARNING, "%s: Don't know how to use device\n",
pInfo->name);
return 1;
}
return 0;
}
static InputInfoPtr
EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
{
InputInfoPtr pInfo;
MessageType deviceFrom = X_CONFIG;
const char *device;
EvdevPtr pEvdev;
if (!(pInfo = xf86AllocateInput(drv, 0)))
return NULL;
pInfo->name = dev->identifier;
pInfo->flags = 0;
pInfo->type_name = "UNKNOWN";
pInfo->device_control = EvdevProc;
pInfo->read_input = EvdevReadInput;
pInfo->motion_history_proc = xf86GetMotionEvents;
pInfo->history_size = 0;
pInfo->control_proc = NULL;
pInfo->close_proc = NULL;
pInfo->switch_mode = NULL;
pInfo->conversion_proc = EvdevConvert;
pInfo->reverse_conversion_proc = NULL;
pInfo->dev = NULL;
pInfo->private_flags = 0;
pInfo->always_core_feedback = 0;
pInfo->conf_idev = dev;
if (!(pEvdev = xcalloc(sizeof(*pEvdev), 1)))
return pInfo;
pInfo->private = pEvdev;
xf86CollectInputOptions(pInfo, NULL, NULL);
xf86ProcessCommonOptions(pInfo, pInfo->options);
device = xf86CheckStrOption(dev->commonOptions, "Device", NULL);
if (!device) {
xf86Msg(X_ERROR, "%s: No device specified.\n", pInfo->name);
xfree(pEvdev);
return pInfo;
}
xf86Msg(deviceFrom, "%s: Device: \"%s\"\n", pInfo->name, device);
do {
pInfo->fd = open(device, O_RDWR, 0);
}
while (pInfo->fd < 0 && errno == EINTR);
if (pInfo->fd < 0) {
xf86Msg(X_ERROR, "Unable to open evdev device \"%s\".\n", device);
xfree(pEvdev);
return pInfo;
}
if (EvdevProbe(pInfo))
xfree(pEvdev);
return pInfo;
}
_X_EXPORT InputDriverRec EVDEV = {
1,
"evdev",
NULL,
EvdevPreInit,
NULL,
NULL,
0
};
#ifdef XFree86LOADER
static void
EvdevUnplug(pointer p)
{
}
static pointer
EvdevPlug(pointer module,
pointer options,
int *errmaj,
int *errmin)
{
xf86AddInputDriver(&EVDEV, module, 0);
return module;
}
static XF86ModuleVersionInfo EvdevVersionRec =
{
"evdev",
MODULEVENDORSTRING,
MODINFOSTRING1,
MODINFOSTRING2,
0,
1, 0, 0,
ABI_CLASS_XINPUT,
ABI_XINPUT_VERSION,
MOD_CLASS_XINPUT,
{0, 0, 0, 0}
};
_X_EXPORT XF86ModuleData evdevModuleData =
{
&EvdevVersionRec,
EvdevPlug,
EvdevUnplug
};
#endif