blob: b692a7752a1806a0b60905f9acb6f9aa89dbb4e9 [file] [log] [blame]
/*
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cmt.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <exevents.h>
#include <X11/extensions/XI.h>
#include <X11/Xatom.h>
#include <xf86Xinput.h>
#include <xf86_OSproc.h>
#include <xkbsrv.h>
#include <xserver-properties.h>
#include "properties.h"
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
#error Unsupported XInput version. Major version 12 and above required.
#endif
#define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X "Abs Dbl Ordinal X"
#define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y "Abs Dbl Ordinal Y"
#define AXIS_LABEL_PROP_ABS_FLING_STATE "Abs Fling State"
#define AXIS_LABEL_PROP_ABS_DBL_FLING_VX "Abs Dbl Fling X Velocity"
#define AXIS_LABEL_PROP_ABS_DBL_FLING_VY "Abs Dbl Fling Y Velocity"
#define AXIS_LABEL_PROP_ABS_METRICS_TYPE "Abs Metrics Type"
#define AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1 "Abs Dbl Metrics Data 1"
#define AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2 "Abs Dbl Metrics Data 2"
#define AXIS_LABEL_PROP_ABS_DBL_START_TIME "Abs Dbl Start Timestamp"
#define AXIS_LABEL_PROP_ABS_DBL_END_TIME "Abs Dbl End Timestamp"
#define AXIS_LABEL_PROP_ABS_FINGER_COUNT "Abs Finger Count"
#define AXIS_LABEL_PROP_ABS_TOUCH_TIMESTAMP "Touch Timestamp"
/**
* Forward declarations
*/
static int PreInit(InputDriverPtr, InputInfoPtr, int);
static void UnInit(InputDriverPtr, InputInfoPtr, int);
static pointer Plug(pointer, pointer, int*, int*);
static void Unplug(pointer);
static int DeviceControl(DeviceIntPtr, int);
static void ReadInput(InputInfoPtr);
static Bool DeviceInit(DeviceIntPtr);
static Bool DeviceOn(DeviceIntPtr);
static Bool DeviceOff(DeviceIntPtr);
static Bool DeviceClose(DeviceIntPtr);
static Bool OpenDevice(InputInfoPtr);
static int InitializeXDevice(DeviceIntPtr dev);
static void libevdev_log_x(void* udata, int level, const char* format, ...)
_X_ATTRIBUTE_PRINTF(3, 4);
/*
* Implementation of log method for libevdev
*/
static void libevdev_log_x(void* udata, int level, const char* format, ...) {
InputInfoPtr info = udata;
int verb;
int type;
va_list args;
va_start(args, format);
if (level == LOGLEVEL_DEBUG) {
type = X_INFO;
verb = DBG_VERB;
} else if (level == LOGLEVEL_WARNING) {
type = X_WARNING;
verb = -1;
} else {
type = X_ERROR;
verb = -1;
}
xf86VIDrvMsgVerb(info, type, verb, format, args);
va_end(args);
}
/**
* X Input driver information and PreInit / UnInit routines
*/
_X_EXPORT InputDriverRec CMT = {
1,
(char*)"cmt",
NULL,
PreInit,
UnInit,
NULL,
0
};
static int
PreInit(InputDriverPtr drv, InputInfoPtr info, int flags)
{
CmtDevicePtr cmt;
int rc;
DBG(info, "NewPreInit\n");
cmt = calloc(1, sizeof(*cmt));
if (!cmt)
return BadAlloc;
info->device_control = DeviceControl;
info->read_input = ReadInput;
info->control_proc = NULL;
info->switch_mode = NULL; /* Only support Absolute mode */
info->private = cmt;
info->fd = -1;
cmt->evdev.log = &libevdev_log_x;
cmt->evdev.log_udata = info;
cmt->evdev.fd = info->fd;
cmt->evdev.evstate = &cmt->evstate;
cmt->evdev.syn_report = &Gesture_Process_Slots;
cmt->evdev.syn_report_udata = &cmt->gesture;
rc = OpenDevice(info);
if (rc != Success)
goto Error_OpenDevice;
rc = Event_Init(&cmt->evdev);
if (rc != Success) {
if (rc == ENOMEM) {
rc = BadAlloc;
}
goto Error_Event_Init;
}
// The cmt driver currently powers mice, multi-touch mice and touchpads.
// We list mice as XI_MOUSE and the others as XI_TOUCHPAD.
if (cmt->evdev.info.evdev_class == EvdevClassMouse)
info->type_name = (char*)XI_MOUSE;
else
info->type_name = (char*)XI_TOUCHPAD;
xf86ProcessCommonOptions(info, info->options);
if (info->fd >= 0)
info->fd = EvdevClose(&cmt->evdev);
rc = Gesture_Init(&cmt->gesture, Event_Get_Slot_Count(&cmt->evdev));
if (rc != Success)
goto Error_Gesture_Init;
return Success;
Error_Gesture_Init:
Event_Free(&cmt->evdev);
Error_Event_Init:
if (info->fd >= 0)
info->fd = EvdevClose(&cmt->evdev);
Error_OpenDevice:
free(cmt);
info->private = NULL;
return rc;
}
static void
UnInit(InputDriverPtr drv, InputInfoPtr info, int flags)
{
CmtDevicePtr cmt;
if (!info)
return;
cmt = info->private;
DBG(info, "UnInit\n");
if (cmt) {
Gesture_Free(&cmt->gesture);
free(cmt->device);
cmt->device = NULL;
Event_Free(&cmt->evdev);
free(cmt);
info->private = NULL;
}
xf86DeleteInput(info, flags);
}
/**
* X input driver entry points
*/
static Bool
DeviceControl(DeviceIntPtr dev, int mode)
{
switch (mode) {
case DEVICE_INIT:
return DeviceInit(dev);
case DEVICE_ON:
return DeviceOn(dev);
case DEVICE_OFF:
return DeviceOff(dev);
case DEVICE_CLOSE:
return DeviceClose(dev);
}
return BadValue;
}
static void
ReadInput(InputInfoPtr info)
{
CmtDevicePtr cmt = info->private;
int err = EvdevRead(&cmt->evdev);
if (err != Success) {
if (err == ENODEV) {
xf86RemoveEnabledDevice(info);
info->fd = EvdevClose(&cmt->evdev);
} else if (err != EAGAIN) {
ERR(info, "Read error: %s\n", strerror(err));
}
}
}
/**
* device control event handlers
*/
static Bool
DeviceInit(DeviceIntPtr dev)
{
InputInfoPtr info = dev->public.devicePrivate;
CmtDevicePtr cmt = info->private;
int rc;
DBG(info, "DeviceInit\n");
InitializeXDevice(dev);
dev->public.on = FALSE;
rc = PropertiesInit(dev);
if (rc != Success)
return rc;
Gesture_Device_Init(&cmt->gesture, dev);
return Success;
}
static Bool
DeviceOn(DeviceIntPtr dev)
{
InputInfoPtr info = dev->public.devicePrivate;
CmtDevicePtr cmt = info->private;
int rc;
DBG(info, "DeviceOn\n");
rc = OpenDevice(info);
if (rc != Success)
return rc;
Event_Open(&cmt->evdev);
xf86AddEnabledDevice(info);
dev->public.on = TRUE;
Gesture_Device_On(&cmt->gesture);
return Success;
}
static Bool
DeviceOff(DeviceIntPtr dev)
{
InputInfoPtr info = dev->public.devicePrivate;
CmtDevicePtr cmt = info->private;
DBG(info, "DeviceOff\n");
dev->public.on = FALSE;
Gesture_Device_Off(&cmt->gesture);
if (info->fd != -1) {
xf86RemoveEnabledDevice(info);
info->fd = EvdevClose(&cmt->evdev);
}
return Success;
}
static Bool
DeviceClose(DeviceIntPtr dev)
{
InputInfoPtr info = dev->public.devicePrivate;
CmtDevicePtr cmt = info->private;
DBG(info, "DeviceClose\n");
DeviceOff(dev);
Gesture_Device_Close(&cmt->gesture);
PropertiesClose(dev);
return Success;
}
/**
* Open Device Node
*/
static Bool
OpenDevice(InputInfoPtr info)
{
CmtDevicePtr cmt = info->private;
if (!cmt->device) {
cmt->device = xf86CheckStrOption(info->options, "Device", NULL);
if (!cmt->device) {
ERR(info, "No Device specified.\n");
return BadValue;
}
xf86IDrvMsg(info, X_CONFIG, "Opening Device: \"%s\"\n", cmt->device);
}
if (info->fd < 0) {
do {
info->fd = EvdevOpen(&cmt->evdev, cmt->device);
} while (info->fd < 0 && errno == EINTR);
if (info->fd < 0) {
ERR(info, "Cannot open \"%s\".\n", cmt->device);
return BadValue;
}
}
return Success;
}
/**
* Setup X Input Device Classes
*/
/*
* Alter the control parameters for the mouse. Note that all special
* protocol values are handled by dix.
*/
static void
PointerCtrl(DeviceIntPtr device, PtrCtrl *ctrl)
{
}
static void
KeyboardCtrl(DeviceIntPtr device, KeybdCtrl *ctrl)
{
}
static Atom
InitAtom(const char* name)
{
Atom atom = XIGetKnownProperty(name);
if (!atom)
atom = MakeAtom(name, strlen(name), TRUE);
return atom;
}
static int
InitializeXDevice(DeviceIntPtr dev)
{
static const char* axes_names[CMT_NUM_AXES] = {
AXIS_LABEL_PROP_REL_X,
AXIS_LABEL_PROP_REL_Y,
AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X,
AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y,
AXIS_LABEL_PROP_REL_HWHEEL,
AXIS_LABEL_PROP_REL_WHEEL,
AXIS_LABEL_PROP_ABS_FLING_STATE,
AXIS_LABEL_PROP_ABS_DBL_FLING_VX,
AXIS_LABEL_PROP_ABS_DBL_FLING_VY,
AXIS_LABEL_PROP_ABS_METRICS_TYPE,
AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1,
AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2,
AXIS_LABEL_PROP_ABS_DBL_START_TIME,
AXIS_LABEL_PROP_ABS_DBL_END_TIME,
AXIS_LABEL_PROP_ABS_FINGER_COUNT,
AXIS_LABEL_PROP_ABS_MT_POSITION_X,
AXIS_LABEL_PROP_ABS_MT_POSITION_Y,
AXIS_LABEL_PROP_ABS_MT_PRESSURE,
AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR,
AXIS_LABEL_PROP_ABS_TOUCH_TIMESTAMP,
};
static const char* btn_names[CMT_NUM_BUTTONS] = {
BTN_LABEL_PROP_BTN_LEFT,
BTN_LABEL_PROP_BTN_MIDDLE,
BTN_LABEL_PROP_BTN_RIGHT,
BTN_LABEL_PROP_BTN_BACK,
BTN_LABEL_PROP_BTN_FORWARD,
};
InputInfoPtr info = dev->public.devicePrivate;
CmtDevicePtr cmt = info->private;
XkbRMLVOSet rmlvo = { 0 };
Atom axes_labels[CMT_NUM_AXES] = { 0 };
Atom btn_labels[CMT_NUM_BUTTONS] = { 0 };
/* Map our button numbers to standard ones. */
CARD8 map[CMT_NUM_BUTTONS + 1] = {
0, /* Ignored */
1,
2,
3,
8, /* Back */
9 /* Forward */
};
int i;
/* TODO: Prop to adjust button mapping */
for (i = 0; i < CMT_NUM_BUTTONS; i++)
btn_labels[i] = XIGetKnownProperty(btn_names[i]);
for (i = 0; i < CMT_NUM_AXES; i++)
axes_labels[i] = InitAtom(axes_names[i]);
/* initialize mouse emulation valuators */
InitPointerDeviceStruct((DevicePtr)dev,
map,
CMT_NUM_BUTTONS, btn_labels,
PointerCtrl,
GetMotionHistorySize(),
CMT_NUM_AXES, axes_labels);
for (i = 0; i < CMT_NUM_AXES; i++) {
int mode = (i == CMT_AXIS_X || i == CMT_AXIS_Y) ? Relative : Absolute;
if (i >= CMT_AXIS_MT_POSITION_X)
break;
xf86InitValuatorAxisStruct(
dev, i, axes_labels[i], -1, -1, 1, 0, 1, mode);
xf86InitValuatorDefaults(dev, i);
}
/* initialize raw touch valuators */
InitTouchClassDeviceStruct(dev, Event_Get_Slot_Count(&cmt->evdev),
XIDependentTouch, CMT_NUM_MT_AXES);
for (i = 0; i < CMT_NUM_AXES; i++) {
int mode = (i == CMT_AXIS_X || i == CMT_AXIS_Y) ? Relative : Absolute;
int input_axis = 0;
if (i == CMT_AXIS_TOUCH_TIMESTAMP) {
xf86InitValuatorAxisStruct(dev, i, axes_labels[i],
0, INT_MAX, 1, 0, 1, Absolute);
continue;
}
if (i == CMT_AXIS_MT_POSITION_X)
input_axis = ABS_MT_POSITION_X;
else if (i == CMT_AXIS_MT_POSITION_Y)
input_axis = ABS_MT_POSITION_Y;
else if (i == CMT_AXIS_MT_PRESSURE)
input_axis = ABS_MT_PRESSURE;
else if (i == CMT_AXIS_MT_TOUCH_MAJOR)
input_axis = ABS_MT_TOUCH_MAJOR;
else
continue;
xf86InitValuatorAxisStruct(
dev, i, axes_labels[i],
cmt->evdev.info.absinfo[input_axis].minimum,
cmt->evdev.info.absinfo[input_axis].maximum,
cmt->evdev.info.absinfo[input_axis].resolution,
0,
cmt->evdev.info.absinfo[input_axis].resolution,
mode);
xf86InitValuatorDefaults(dev, i);
}
/* Initialize keyboard device struct. Based on xf86-input-evdev,
do not allow any rule/layout/etc changes. */
xf86ReplaceStrOption(info->options, "xkb_rules", "evdev");
rmlvo.rules = xf86SetStrOption(info->options, "xkb_rules", NULL);
rmlvo.model = xf86SetStrOption(info->options, "xkb_model", NULL);
rmlvo.layout = xf86SetStrOption(info->options, "xkb_layout", NULL);
rmlvo.variant = xf86SetStrOption(info->options, "xkb_variant", NULL);
rmlvo.options = xf86SetStrOption(info->options, "xkb_options", NULL);
InitKeyboardDeviceStruct(dev, &rmlvo, NULL, KeyboardCtrl);
XkbFreeRMLVOSet(&rmlvo, FALSE);
return Success;
}
/**
* X module information and plug / unplug routines
*/
static XF86ModuleVersionInfo versionRec =
{
"cmt",
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}
};
_X_EXPORT XF86ModuleData cmtModuleData =
{
&versionRec,
Plug,
Unplug
};
static pointer
Plug(pointer module, pointer options, int* errmaj, int* errmin)
{
xf86AddInputDriver(&CMT, module, 0);
return module;
}
static void
Unplug(pointer p)
{
}