blob: 51927e4327be15081838d9b071b033260d4c7260 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include <stddef.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <X11/Xlib.h>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/sys_info.h"
#include "build/build_config.h"
#include "ui/display/display.h"
#include "ui/events/devices/x11/device_list_cache_x11.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_switches.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/x/x11_types.h"
// XIScrollClass was introduced in XI 2.1 so we need to define it here
// for backward-compatibility with older versions of XInput.
#if !defined(XIScrollClass)
#define XIScrollClass 3
#endif
// Multi-touch support was introduced in XI 2.2. Add XI event types here
// for backward-compatibility with older versions of XInput.
#if !defined(XI_TouchBegin)
#define XI_TouchBegin 18
#define XI_TouchUpdate 19
#define XI_TouchEnd 20
#endif
// Copied from xserver-properties.h
#define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel"
#define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel"
// CMT specific timings
#define AXIS_LABEL_PROP_ABS_DBL_START_TIME "Abs Dbl Start Timestamp"
#define AXIS_LABEL_PROP_ABS_DBL_END_TIME "Abs Dbl End Timestamp"
// Ordinal values
#define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X "Abs Dbl Ordinal X"
#define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y "Abs Dbl Ordinal Y"
// Fling properties
#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_FLING_STATE "Abs Fling State"
#define AXIS_LABEL_PROP_ABS_FINGER_COUNT "Abs Finger Count"
// Cros metrics gesture from touchpad
#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"
// Touchscreen multi-touch
#define AXIS_LABEL_ABS_MT_TOUCH_MAJOR "Abs MT Touch Major"
#define AXIS_LABEL_ABS_MT_TOUCH_MINOR "Abs MT Touch Minor"
#define AXIS_LABEL_ABS_MT_ORIENTATION "Abs MT Orientation"
#define AXIS_LABEL_ABS_MT_PRESSURE "Abs MT Pressure"
#define AXIS_LABEL_ABS_MT_POSITION_X "Abs MT Position X"
#define AXIS_LABEL_ABS_MT_POSITION_Y "Abs MT Position Y"
#define AXIS_LABEL_ABS_MT_TRACKING_ID "Abs MT Tracking ID"
#define AXIS_LABEL_TOUCH_TIMESTAMP "Touch Timestamp"
// When you add new data types, please make sure the order here is aligned
// with the order in the DataType enum in the header file because we assume
// they are in sync when updating the device list (see UpdateDeviceList).
const char* kCachedAtoms[] = {
AXIS_LABEL_PROP_REL_HWHEEL,
AXIS_LABEL_PROP_REL_WHEEL,
AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X,
AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y,
AXIS_LABEL_PROP_ABS_DBL_START_TIME,
AXIS_LABEL_PROP_ABS_DBL_END_TIME,
AXIS_LABEL_PROP_ABS_DBL_FLING_VX,
AXIS_LABEL_PROP_ABS_DBL_FLING_VY,
AXIS_LABEL_PROP_ABS_FLING_STATE,
AXIS_LABEL_PROP_ABS_METRICS_TYPE,
AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1,
AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2,
AXIS_LABEL_PROP_ABS_FINGER_COUNT,
AXIS_LABEL_ABS_MT_TOUCH_MAJOR,
AXIS_LABEL_ABS_MT_TOUCH_MINOR,
AXIS_LABEL_ABS_MT_ORIENTATION,
AXIS_LABEL_ABS_MT_PRESSURE,
AXIS_LABEL_ABS_MT_POSITION_X,
AXIS_LABEL_ABS_MT_POSITION_Y,
AXIS_LABEL_ABS_MT_TRACKING_ID,
AXIS_LABEL_TOUCH_TIMESTAMP,
NULL
};
// Constants for checking if a data type lies in the range of CMT/Touch data
// types.
const int kCMTDataTypeStart = ui::DeviceDataManagerX11::DT_CMT_SCROLL_X;
const int kCMTDataTypeEnd = ui::DeviceDataManagerX11::DT_CMT_FINGER_COUNT;
const int kTouchDataTypeStart = ui::DeviceDataManagerX11::DT_TOUCH_MAJOR;
const int kTouchDataTypeEnd = ui::DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP;
namespace ui {
namespace {
template <typename Iterator>
Iterator FindDeviceWithId(Iterator begin, Iterator end, int id) {
for (auto it = begin; it != end; ++it) {
if (it->id == id)
return it;
}
return end;
}
} // namespace
bool DeviceDataManagerX11::IsCMTDataType(const int type) {
return (type >= kCMTDataTypeStart) && (type <= kCMTDataTypeEnd);
}
bool DeviceDataManagerX11::IsTouchDataType(const int type) {
return (type >= kTouchDataTypeStart) && (type <= kTouchDataTypeEnd);
}
// static
void DeviceDataManagerX11::CreateInstance() {
if (instance())
return;
DeviceDataManagerX11* device_data_manager = new DeviceDataManagerX11();
// TODO(bruthig): Replace the DeleteInstance callbacks with explicit calls.
base::AtExitManager::RegisterTask(
base::Bind(DeviceDataManager::DeleteInstance));
set_instance(device_data_manager);
}
// static
DeviceDataManagerX11* DeviceDataManagerX11::GetInstance() {
return static_cast<DeviceDataManagerX11*>(DeviceDataManager::GetInstance());
}
DeviceDataManagerX11::DeviceDataManagerX11()
: xi_opcode_(-1),
atom_cache_(gfx::GetXDisplay(), kCachedAtoms),
button_map_count_(0) {
CHECK(gfx::GetXDisplay());
InitializeXInputInternal();
// Make sure the sizes of enum and kCachedAtoms are aligned.
CHECK(arraysize(kCachedAtoms) == static_cast<size_t>(DT_LAST_ENTRY) + 1);
UpdateDeviceList(gfx::GetXDisplay());
UpdateButtonMap();
}
DeviceDataManagerX11::~DeviceDataManagerX11() {
}
bool DeviceDataManagerX11::InitializeXInputInternal() {
// Check if XInput is available on the system.
xi_opcode_ = -1;
int opcode, event, error;
if (!XQueryExtension(
gfx::GetXDisplay(), "XInputExtension", &opcode, &event, &error)) {
VLOG(1) << "X Input extension not available: error=" << error;
return false;
}
// Check the XInput version.
int major = 2, minor = 2;
if (XIQueryVersion(gfx::GetXDisplay(), &major, &minor) == BadRequest) {
VLOG(1) << "XInput2 not supported in the server.";
return false;
}
if (major < 2 || (major == 2 && minor < 2)) {
DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
<< "But 2.2 is required.";
return false;
}
xi_opcode_ = opcode;
CHECK_NE(-1, xi_opcode_);
// Possible XI event types for XIDeviceEvent. See the XI2 protocol
// specification.
xi_device_event_types_[XI_KeyPress] = true;
xi_device_event_types_[XI_KeyRelease] = true;
xi_device_event_types_[XI_ButtonPress] = true;
xi_device_event_types_[XI_ButtonRelease] = true;
xi_device_event_types_[XI_Motion] = true;
// Multi-touch support was introduced in XI 2.2.
if (minor >= 2) {
xi_device_event_types_[XI_TouchBegin] = true;
xi_device_event_types_[XI_TouchUpdate] = true;
xi_device_event_types_[XI_TouchEnd] = true;
}
return true;
}
bool DeviceDataManagerX11::IsXInput2Available() const {
return xi_opcode_ != -1;
}
void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
cmt_devices_.reset();
touchpads_.reset();
master_pointers_.clear();
for (int i = 0; i < kMaxDeviceNum; ++i) {
valuator_count_[i] = 0;
valuator_lookup_[i].clear();
data_type_lookup_[i].clear();
valuator_min_[i].clear();
valuator_max_[i].clear();
scroll_data_[i].horizontal.number = -1;
scroll_data_[i].horizontal.seen = false;
scroll_data_[i].vertical.number = -1;
scroll_data_[i].vertical.seen = false;
for (int j = 0; j < kMaxSlotNum; j++)
last_seen_valuator_[i][j].clear();
}
// Find all the touchpad devices.
const XDeviceList& dev_list =
ui::DeviceListCacheX11::GetInstance()->GetXDeviceList(display);
Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false);
for (int i = 0; i < dev_list.count; ++i)
if (dev_list[i].type == xi_touchpad)
touchpads_[dev_list[i].id] = true;
if (!IsXInput2Available())
return;
// Update the structs with new valuator information
const XIDeviceList& info_list =
ui::DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display);
Atom atoms[DT_LAST_ENTRY];
for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type)
atoms[data_type] = atom_cache_.GetAtom(kCachedAtoms[data_type]);
for (int i = 0; i < info_list.count; ++i) {
const XIDeviceInfo& info = info_list[i];
if (info.use == XIMasterPointer)
master_pointers_.push_back(info.deviceid);
// We currently handle only slave, non-keyboard devices
if (info.use != XISlavePointer && info.use != XIFloatingSlave)
continue;
bool possible_cmt = false;
bool not_cmt = false;
const int deviceid = info.deviceid;
for (int j = 0; j < info.num_classes; ++j) {
if (info.classes[j]->type == XIValuatorClass)
++valuator_count_[deviceid];
else if (info.classes[j]->type == XIScrollClass)
not_cmt = true;
}
// Skip devices that don't use any valuator
if (!valuator_count_[deviceid])
continue;
valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1);
data_type_lookup_[deviceid].resize(
valuator_count_[deviceid], DT_LAST_ENTRY);
valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0);
valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0);
for (int j = 0; j < kMaxSlotNum; j++)
last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0);
for (int j = 0; j < info.num_classes; ++j) {
if (info.classes[j]->type == XIValuatorClass) {
if (UpdateValuatorClassDevice(
reinterpret_cast<XIValuatorClassInfo*>(info.classes[j]), atoms,
deviceid))
possible_cmt = true;
} else if (info.classes[j]->type == XIScrollClass) {
UpdateScrollClassDevice(
reinterpret_cast<XIScrollClassInfo*>(info.classes[j]), deviceid);
}
}
if (possible_cmt && !not_cmt)
cmt_devices_[deviceid] = true;
}
}
bool DeviceDataManagerX11::GetSlotNumber(const XIDeviceEvent* xiev, int* slot) {
ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
if (!factory->IsMultiTouchDevice(xiev->sourceid)) {
*slot = 0;
return true;
}
return factory->QuerySlotForTrackingID(xiev->detail, slot);
}
void DeviceDataManagerX11::GetEventRawData(const XEvent& xev, EventData* data) {
if (xev.type != GenericEvent)
return;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
CHECK_GE(xiev->sourceid, 0);
CHECK_GE(xiev->deviceid, 0);
if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
return;
data->clear();
const int sourceid = xiev->sourceid;
double* valuators = xiev->valuators.values;
for (int i = 0; i <= valuator_count_[sourceid]; ++i) {
if (XIMaskIsSet(xiev->valuators.mask, i)) {
int type = data_type_lookup_[sourceid][i];
if (type != DT_LAST_ENTRY) {
(*data)[type] = *valuators;
if (IsTouchDataType(type)) {
int slot = -1;
if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
last_seen_valuator_[sourceid][slot][type] = *valuators;
}
}
valuators++;
}
}
}
bool DeviceDataManagerX11::GetEventData(const XEvent& xev,
const DataType type, double* value) {
if (xev.type != GenericEvent)
return false;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
CHECK_GE(xiev->sourceid, 0);
CHECK_GE(xiev->deviceid, 0);
if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
return false;
const int sourceid = xiev->sourceid;
if (valuator_lookup_[sourceid].empty())
return false;
if (type == DT_TOUCH_TRACKING_ID) {
// With XInput2 MT, Tracking ID is provided in the detail field for touch
// events.
if (xiev->evtype == XI_TouchBegin ||
xiev->evtype == XI_TouchEnd ||
xiev->evtype == XI_TouchUpdate) {
*value = xiev->detail;
} else {
*value = 0;
}
return true;
}
int val_index = valuator_lookup_[sourceid][type];
int slot = 0;
if (val_index >= 0) {
if (XIMaskIsSet(xiev->valuators.mask, val_index)) {
double* valuators = xiev->valuators.values;
while (val_index--) {
if (XIMaskIsSet(xiev->valuators.mask, val_index))
++valuators;
}
*value = *valuators;
if (IsTouchDataType(type)) {
if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
last_seen_valuator_[sourceid][slot][type] = *value;
}
return true;
} else if (IsTouchDataType(type)) {
if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
*value = last_seen_valuator_[sourceid][slot][type];
}
}
return false;
}
bool DeviceDataManagerX11::IsXIDeviceEvent(const XEvent& xev) const {
if (xev.type != GenericEvent || xev.xcookie.extension != xi_opcode_)
return false;
return xi_device_event_types_[xev.xcookie.evtype];
}
bool DeviceDataManagerX11::IsTouchpadXInputEvent(const XEvent& xev) const {
if (xev.type != GenericEvent)
return false;
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
CHECK_GE(xievent->sourceid, 0);
if (xievent->sourceid >= kMaxDeviceNum)
return false;
return touchpads_[xievent->sourceid];
}
bool DeviceDataManagerX11::IsCMTDeviceEvent(const XEvent& xev) const {
if (xev.type != GenericEvent)
return false;
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
CHECK_GE(xievent->sourceid, 0);
if (xievent->sourceid >= kMaxDeviceNum)
return false;
return cmt_devices_[xievent->sourceid];
}
int DeviceDataManagerX11::GetScrollClassEventDetail(const XEvent& xev) const {
if (xev.type != GenericEvent)
return SCROLL_TYPE_NO_SCROLL;
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
if (xievent->sourceid >= kMaxDeviceNum)
return SCROLL_TYPE_NO_SCROLL;
int horizontal_id = scroll_data_[xievent->sourceid].horizontal.number;
int vertical_id = scroll_data_[xievent->sourceid].vertical.number;
return (horizontal_id != -1 &&
XIMaskIsSet(xievent->valuators.mask, horizontal_id)
? SCROLL_TYPE_HORIZONTAL
: 0) |
(vertical_id != -1 && XIMaskIsSet(xievent->valuators.mask, vertical_id)
? SCROLL_TYPE_VERTICAL
: 0);
}
int DeviceDataManagerX11::GetScrollClassDeviceDetail(const XEvent& xev) const {
if (xev.type != GenericEvent)
return SCROLL_TYPE_NO_SCROLL;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
return SCROLL_TYPE_NO_SCROLL;
const int sourceid = xiev->sourceid;
const ScrollInfo& device_data = scroll_data_[sourceid];
return (device_data.vertical.number >= 0 ? SCROLL_TYPE_VERTICAL : 0) |
(device_data.horizontal.number >= 0 ? SCROLL_TYPE_HORIZONTAL : 0);
}
bool DeviceDataManagerX11::IsCMTGestureEvent(const XEvent& xev) const {
return (IsScrollEvent(xev) || IsFlingEvent(xev) || IsCMTMetricsEvent(xev));
}
bool DeviceDataManagerX11::HasEventData(
const XIDeviceEvent* xiev, const DataType type) const {
CHECK_GE(xiev->sourceid, 0);
if (xiev->sourceid >= kMaxDeviceNum)
return false;
if (type >= valuator_lookup_[xiev->sourceid].size())
return false;
const int idx = valuator_lookup_[xiev->sourceid][type];
return (idx >= 0) && XIMaskIsSet(xiev->valuators.mask, idx);
}
bool DeviceDataManagerX11::IsScrollEvent(const XEvent& xev) const {
if (!IsCMTDeviceEvent(xev))
return false;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
return (HasEventData(xiev, DT_CMT_SCROLL_X) ||
HasEventData(xiev, DT_CMT_SCROLL_Y));
}
bool DeviceDataManagerX11::IsFlingEvent(const XEvent& xev) const {
if (!IsCMTDeviceEvent(xev))
return false;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
return (HasEventData(xiev, DT_CMT_FLING_X) &&
HasEventData(xiev, DT_CMT_FLING_Y) &&
HasEventData(xiev, DT_CMT_FLING_STATE));
}
bool DeviceDataManagerX11::IsCMTMetricsEvent(const XEvent& xev) const {
if (!IsCMTDeviceEvent(xev))
return false;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
return (HasEventData(xiev, DT_CMT_METRICS_TYPE) &&
HasEventData(xiev, DT_CMT_METRICS_DATA1) &&
HasEventData(xiev, DT_CMT_METRICS_DATA2));
}
bool DeviceDataManagerX11::HasGestureTimes(const XEvent& xev) const {
if (!IsCMTDeviceEvent(xev))
return false;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
return (HasEventData(xiev, DT_CMT_START_TIME) &&
HasEventData(xiev, DT_CMT_END_TIME));
}
void DeviceDataManagerX11::GetScrollOffsets(const XEvent& xev,
float* x_offset,
float* y_offset,
float* x_offset_ordinal,
float* y_offset_ordinal,
int* finger_count) {
*x_offset = 0;
*y_offset = 0;
*x_offset_ordinal = 0;
*y_offset_ordinal = 0;
*finger_count = 2;
EventData data;
GetEventRawData(xev, &data);
if (data.find(DT_CMT_SCROLL_X) != data.end())
*x_offset = data[DT_CMT_SCROLL_X];
if (data.find(DT_CMT_SCROLL_Y) != data.end())
*y_offset = data[DT_CMT_SCROLL_Y];
if (data.find(DT_CMT_ORDINAL_X) != data.end())
*x_offset_ordinal = data[DT_CMT_ORDINAL_X];
if (data.find(DT_CMT_ORDINAL_Y) != data.end())
*y_offset_ordinal = data[DT_CMT_ORDINAL_Y];
if (data.find(DT_CMT_FINGER_COUNT) != data.end())
*finger_count = static_cast<int>(data[DT_CMT_FINGER_COUNT]);
}
void DeviceDataManagerX11::GetScrollClassOffsets(const XEvent& xev,
double* x_offset,
double* y_offset) {
DCHECK_NE(SCROLL_TYPE_NO_SCROLL, GetScrollClassDeviceDetail(xev));
*x_offset = 0;
*y_offset = 0;
if (xev.type != GenericEvent)
return;
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
return;
const int sourceid = xiev->sourceid;
double* valuators = xiev->valuators.values;
ScrollInfo* info = &scroll_data_[sourceid];
const int horizontal_number = info->horizontal.number;
const int vertical_number = info->vertical.number;
for (int i = 0; i <= valuator_count_[sourceid]; ++i) {
if (!XIMaskIsSet(xiev->valuators.mask, i))
continue;
if (i == horizontal_number) {
*x_offset = ExtractAndUpdateScrollOffset(&info->horizontal, *valuators);
} else if (i == vertical_number) {
*y_offset = ExtractAndUpdateScrollOffset(&info->vertical, *valuators);
}
valuators++;
}
}
void DeviceDataManagerX11::InvalidateScrollClasses() {
for (int i = 0; i < kMaxDeviceNum; i++) {
scroll_data_[i].horizontal.seen = false;
scroll_data_[i].vertical.seen = false;
}
}
void DeviceDataManagerX11::GetFlingData(const XEvent& xev,
float* vx,
float* vy,
float* vx_ordinal,
float* vy_ordinal,
bool* is_cancel) {
*vx = 0;
*vy = 0;
*vx_ordinal = 0;
*vy_ordinal = 0;
*is_cancel = false;
EventData data;
GetEventRawData(xev, &data);
if (data.find(DT_CMT_FLING_X) != data.end())
*vx = data[DT_CMT_FLING_X];
if (data.find(DT_CMT_FLING_Y) != data.end())
*vy = data[DT_CMT_FLING_Y];
if (data.find(DT_CMT_FLING_STATE) != data.end())
*is_cancel = !!static_cast<unsigned int>(data[DT_CMT_FLING_STATE]);
if (data.find(DT_CMT_ORDINAL_X) != data.end())
*vx_ordinal = data[DT_CMT_ORDINAL_X];
if (data.find(DT_CMT_ORDINAL_Y) != data.end())
*vy_ordinal = data[DT_CMT_ORDINAL_Y];
}
void DeviceDataManagerX11::GetMetricsData(const XEvent& xev,
GestureMetricsType* type,
float* data1,
float* data2) {
*type = kGestureMetricsTypeUnknown;
*data1 = 0;
*data2 = 0;
EventData data;
GetEventRawData(xev, &data);
if (data.find(DT_CMT_METRICS_TYPE) != data.end()) {
int val = static_cast<int>(data[DT_CMT_METRICS_TYPE]);
if (val == 0)
*type = kGestureMetricsTypeNoisyGround;
else
*type = kGestureMetricsTypeUnknown;
}
if (data.find(DT_CMT_METRICS_DATA1) != data.end())
*data1 = data[DT_CMT_METRICS_DATA1];
if (data.find(DT_CMT_METRICS_DATA2) != data.end())
*data2 = data[DT_CMT_METRICS_DATA2];
}
int DeviceDataManagerX11::GetMappedButton(int button) {
return button > 0 && button <= button_map_count_ ? button_map_[button - 1] :
button;
}
void DeviceDataManagerX11::UpdateButtonMap() {
button_map_count_ = XGetPointerMapping(gfx::GetXDisplay(),
button_map_,
arraysize(button_map_));
}
void DeviceDataManagerX11::GetGestureTimes(const XEvent& xev,
double* start_time,
double* end_time) {
*start_time = 0;
*end_time = 0;
EventData data;
GetEventRawData(xev, &data);
if (data.find(DT_CMT_START_TIME) != data.end())
*start_time = data[DT_CMT_START_TIME];
if (data.find(DT_CMT_END_TIME) != data.end())
*end_time = data[DT_CMT_END_TIME];
}
bool DeviceDataManagerX11::NormalizeData(int deviceid,
const DataType type,
double* value) {
double max_value;
double min_value;
if (GetDataRange(deviceid, type, &min_value, &max_value)) {
*value = (*value - min_value) / (max_value - min_value);
DCHECK(*value >= 0.0 && *value <= 1.0);
return true;
}
return false;
}
bool DeviceDataManagerX11::GetDataRange(int deviceid,
const DataType type,
double* min,
double* max) {
CHECK_GE(deviceid, 0);
if (deviceid >= kMaxDeviceNum)
return false;
if (valuator_lookup_[deviceid][type] >= 0) {
*min = valuator_min_[deviceid][type];
*max = valuator_max_[deviceid][type];
return true;
}
return false;
}
void DeviceDataManagerX11::SetDeviceListForTest(
const std::vector<int>& touchscreen,
const std::vector<int>& cmt_devices,
const std::vector<int>& other_devices) {
for (int i = 0; i < kMaxDeviceNum; ++i) {
valuator_count_[i] = 0;
valuator_lookup_[i].clear();
data_type_lookup_[i].clear();
valuator_min_[i].clear();
valuator_max_[i].clear();
for (int j = 0; j < kMaxSlotNum; j++)
last_seen_valuator_[i][j].clear();
}
for (int deviceid : touchscreen) {
InitializeValuatorsForTest(deviceid, kTouchDataTypeStart, kTouchDataTypeEnd,
0, 1000);
}
cmt_devices_.reset();
for (int deviceid : cmt_devices) {
cmt_devices_[deviceid] = true;
touchpads_[deviceid] = true;
InitializeValuatorsForTest(deviceid, kCMTDataTypeStart, kCMTDataTypeEnd,
-1000, 1000);
}
for (int deviceid : other_devices) {
InitializeValuatorsForTest(deviceid, kCMTDataTypeStart, kCMTDataTypeEnd,
-1000, 1000);
}
}
void DeviceDataManagerX11::SetValuatorDataForTest(XIDeviceEvent* xievent,
DataType type,
double value) {
int index = valuator_lookup_[xievent->deviceid][type];
CHECK(!XIMaskIsSet(xievent->valuators.mask, index));
CHECK(index >= 0 && index < valuator_count_[xievent->deviceid]);
XISetMask(xievent->valuators.mask, index);
double* valuators = xievent->valuators.values;
for (int i = 0; i < index; ++i) {
if (XIMaskIsSet(xievent->valuators.mask, i))
valuators++;
}
for (int i = DT_LAST_ENTRY - 1; i > valuators - xievent->valuators.values;
--i)
xievent->valuators.values[i] = xievent->valuators.values[i - 1];
*valuators = value;
}
void DeviceDataManagerX11::InitializeValuatorsForTest(int deviceid,
int start_valuator,
int end_valuator,
double min_value,
double max_value) {
valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1);
data_type_lookup_[deviceid].resize(DT_LAST_ENTRY, DT_LAST_ENTRY);
valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0);
valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0);
for (int j = 0; j < kMaxSlotNum; j++)
last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0);
for (int j = start_valuator; j <= end_valuator; ++j) {
valuator_lookup_[deviceid][j] = valuator_count_[deviceid];
data_type_lookup_[deviceid][valuator_count_[deviceid]] = j;
valuator_min_[deviceid][j] = min_value;
valuator_max_[deviceid][j] = max_value;
valuator_count_[deviceid]++;
}
}
bool DeviceDataManagerX11::UpdateValuatorClassDevice(
XIValuatorClassInfo* valuator_class_info,
Atom* atoms,
int deviceid) {
DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum);
Atom* label =
std::find(atoms, atoms + DT_LAST_ENTRY, valuator_class_info->label);
if (label == atoms + DT_LAST_ENTRY) {
return false;
}
int data_type = label - atoms;
DCHECK_GE(data_type, 0);
DCHECK_LT(data_type, DT_LAST_ENTRY);
valuator_lookup_[deviceid][data_type] = valuator_class_info->number;
data_type_lookup_[deviceid][valuator_class_info->number] = data_type;
valuator_min_[deviceid][data_type] = valuator_class_info->min;
valuator_max_[deviceid][data_type] = valuator_class_info->max;
return IsCMTDataType(data_type);
}
void DeviceDataManagerX11::UpdateScrollClassDevice(
XIScrollClassInfo* scroll_class_info,
int deviceid) {
DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum);
ScrollInfo& info = scroll_data_[deviceid];
// TODO: xinput2 is disabled until edge cases are fixed.
// http://crbug.com/616308
return;
bool legacy_scroll_available =
(scroll_class_info->flags & XIScrollFlagNoEmulation) == 0;
// If the device's highest resolution is lower than the resolution of xinput1
// then use xinput1's events instead (ie. don't configure smooth scrolling).
if (legacy_scroll_available &&
std::abs(scroll_class_info->increment) <= 1.0) {
return;
}
switch (scroll_class_info->scroll_type) {
case XIScrollTypeVertical:
info.vertical.number = scroll_class_info->number;
info.vertical.increment = scroll_class_info->increment;
info.vertical.position = 0;
info.vertical.seen = false;
break;
case XIScrollTypeHorizontal:
info.horizontal.number = scroll_class_info->number;
info.horizontal.increment = scroll_class_info->increment;
info.horizontal.position = 0;
info.horizontal.seen = false;
break;
}
}
double DeviceDataManagerX11::ExtractAndUpdateScrollOffset(
ScrollInfo::AxisInfo* axis,
double valuator) const {
double offset = 0;
if (axis->seen)
offset = axis->position - valuator;
axis->seen = true;
axis->position = valuator;
return offset / axis->increment;
}
void DeviceDataManagerX11::SetDisabledKeyboardAllowedKeys(
std::unique_ptr<std::set<KeyboardCode>> excepted_keys) {
DCHECK(!excepted_keys.get() ||
!blocked_keyboard_allowed_keys_.get());
blocked_keyboard_allowed_keys_ = std::move(excepted_keys);
}
void DeviceDataManagerX11::DisableDevice(int deviceid) {
blocked_devices_.set(deviceid, true);
// TODO(rsadam@): Support blocking touchscreen devices.
std::vector<InputDevice> keyboards = GetKeyboardDevices();
std::vector<InputDevice>::iterator it =
FindDeviceWithId(keyboards.begin(), keyboards.end(), deviceid);
if (it != std::end(keyboards)) {
blocked_keyboard_devices_.insert(
std::pair<int, InputDevice>(deviceid, *it));
keyboards.erase(it);
DeviceDataManager::OnKeyboardDevicesUpdated(keyboards);
}
}
void DeviceDataManagerX11::EnableDevice(int deviceid) {
blocked_devices_.set(deviceid, false);
std::map<int, InputDevice>::iterator it =
blocked_keyboard_devices_.find(deviceid);
if (it != blocked_keyboard_devices_.end()) {
std::vector<InputDevice> devices = GetKeyboardDevices();
// Add device to current list of active devices.
devices.push_back((*it).second);
blocked_keyboard_devices_.erase(it);
DeviceDataManager::OnKeyboardDevicesUpdated(devices);
}
}
bool DeviceDataManagerX11::IsDeviceEnabled(int device_id) const {
return blocked_devices_.test(device_id);
}
bool DeviceDataManagerX11::IsEventBlocked(const XEvent& xev) {
// Only check XI2 events which have a source device id.
if (xev.type != GenericEvent)
return false;
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
// Allow any key events from blocked_keyboard_allowed_keys_.
if (blocked_keyboard_allowed_keys_ &&
(xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) &&
blocked_keyboard_allowed_keys_->find(KeyboardCodeFromXKeyEvent(&xev)) !=
blocked_keyboard_allowed_keys_->end()) {
return false;
}
return blocked_devices_.test(xievent->sourceid);
}
void DeviceDataManagerX11::OnKeyboardDevicesUpdated(
const std::vector<InputDevice>& devices) {
std::vector<InputDevice> keyboards(devices);
for (std::map<int, InputDevice>::iterator blocked_iter =
blocked_keyboard_devices_.begin();
blocked_iter != blocked_keyboard_devices_.end();) {
// Check if the blocked device still exists in list of devices.
int device_id = blocked_iter->first;
std::vector<InputDevice>::iterator it =
FindDeviceWithId(keyboards.begin(), keyboards.end(), device_id);
// If the device no longer exists, unblock it, else filter it out from our
// active list.
if (it == keyboards.end()) {
blocked_devices_.set((*blocked_iter).first, false);
blocked_keyboard_devices_.erase(blocked_iter++);
} else {
keyboards.erase(it);
++blocked_iter;
}
}
// Notify base class of updated list.
DeviceDataManager::OnKeyboardDevicesUpdated(keyboards);
}
} // namespace ui