blob: 5f49a7ad9974cb2f9ef075ef8b11e164dcacd14f [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/ozone/evdev/libgestures_glue/gesture_property_provider.h"
#include <gestures/gestures.h>
#include <libevdev/libevdev.h>
#include <fnmatch.h>
#include <stdint.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringize_macros.h"
#include "base/strings/stringprintf.h"
#include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"
// Severity level for general info logging purpose.
#define GPROP_LOG DVLOG
#define INFO_SEVERITY 1
/* Implementation of GesturesProp declared in gestures.h
*
* libgestures requires that this be in the top level namespace.
* */
class GesturesProp {
public:
typedef ui::GesturePropertyProvider::PropertyType PropertyType;
GesturesProp(const std::string& name,
const PropertyType type,
const size_t count)
: name_(name),
type_(type),
count_(count),
get_(NULL),
set_(NULL),
handler_data_(NULL) {}
virtual ~GesturesProp() {}
// Variant-ish interfaces for accessing the property value. Each type of
// property should override the corresponding interfaces for it.
virtual std::vector<int> GetIntValue() const {
NOTREACHED();
return std::vector<int>();
}
virtual bool SetIntValue(const std::vector<int>& value) {
NOTREACHED();
return false;
}
virtual std::vector<int16_t> GetShortValue() const {
NOTREACHED();
return std::vector<int16_t>();
}
virtual bool SetShortValue(const std::vector<int16_t>& value) {
NOTREACHED();
return false;
}
virtual std::vector<bool> GetBoolValue() const {
NOTREACHED();
return std::vector<bool>();
}
virtual bool SetBoolValue(const std::vector<bool>& value) {
NOTREACHED();
return false;
}
virtual std::string GetStringValue() const {
NOTREACHED();
return std::string();
}
virtual bool SetStringValue(const std::string& value) {
NOTREACHED();
return false;
}
virtual std::vector<double> GetDoubleValue() const {
NOTREACHED();
return std::vector<double>();
}
virtual bool SetDoubleValue(const std::vector<double>& value) {
NOTREACHED();
return false;
}
// Set property access handlers.
void SetHandlers(GesturesPropGetHandler get,
GesturesPropSetHandler set,
void* data) {
get_ = get;
set_ = set;
handler_data_ = data;
}
// Accessors.
const std::string& name() const { return name_; }
PropertyType type() const { return type_; }
size_t count() const { return count_; }
virtual bool IsReadOnly() const = 0;
protected:
void OnGet() const {
// We don't have the X server now so there is currently nothing to do when
// the get handler returns true.
// TODO(sheckylin): Re-visit this if we use handlers that modifies the
// property.
if (get_)
get_(handler_data_);
}
void OnSet() const {
// Call the property set handler if available.
if (set_)
set_(handler_data_);
}
private:
// For logging purpose.
friend std::ostream& operator<<(std::ostream& os,
const GesturesProp& property);
// Interfaces for getting internal pointers and stuff.
virtual const char** GetStringWritebackPtr() const {
NOTREACHED();
return NULL;
}
virtual bool IsAllocated() const {
NOTREACHED();
return false;
}
// Property name, type and number of elements.
std::string name_;
PropertyType type_;
size_t count_;
// Handler function pointers and the data to be passed to them when the
// property is accessed.
GesturesPropGetHandler get_;
GesturesPropSetHandler set_;
void* handler_data_;
DISALLOW_COPY_AND_ASSIGN(GesturesProp);
};
template <typename T>
class TypedGesturesProp : public GesturesProp {
public:
TypedGesturesProp(const std::string& name,
const PropertyType type,
const size_t count,
T* value)
: GesturesProp(name, type, count),
value_(value),
is_read_only_(false),
is_allocated_(false) {
Init();
}
~TypedGesturesProp() override {
if (is_allocated_)
delete[] value_;
}
// Accessors.
bool IsReadOnly() const override { return is_read_only_; }
protected:
// Functions for setting/getting numerical properties.
//
// These two functions calls the set/get handler and should only be used in
// Get*Value/Set*Value functions.
template <typename U>
std::vector<U> GetNumericalPropertyValue() const {
// Nothing should be modified so it is OK to call the get handler first.
OnGet();
return this->template GetNumericalValue<U>();
}
template <typename U>
bool SetNumericalPropertyValue(const std::vector<U>& value) {
// Set the value only if not read-only and the vector size matches.
//
// As per the legacy guideline, all read-only properties (created with NULL)
// can't be modified. If we want to change this in the future, re-think
// about the different cases here (e.g., should we allow setting an array
// value of different size?).
if (is_read_only_ || value.size() != count())
return false;
bool ret = this->template SetNumericalValue(value);
OnSet();
return ret;
}
// Initialize a numerical property's value. Note that a (numerical) default
// property's value is always stored in double.
void InitializeNumericalProperty(const T* init,
const GesturesProp* default_property) {
if (IsDefaultPropertyUsable(default_property)) {
GPROP_LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
this->template SetNumericalValue(default_property->GetDoubleValue());
} else {
// To work with the interface exposed by the gesture lib, we have no
// choice but to trust that the init array has sufficient size.
std::vector<T> temp(init, init + count());
this->template SetNumericalValue(temp);
}
}
// Data pointer.
T* value_;
// If the flag is on, it means the GesturesProp is created by passing a NULL
// data pointer to the creator functions. We define the property as a
// read-only one and that no value change will be allowed for it. Note that
// the flag is different from is_allocated in that StringProperty will always
// allocate no matter it is created with NULL or not.
bool is_read_only_;
private:
// Initialize the object.
void Init() {
// If no external data pointer is passed, we have to create our own.
if (!value_) {
value_ = new T[GesturesProp::count()];
is_read_only_ = true;
is_allocated_ = true;
}
}
// Low-level functions for setting/getting numerical properties.
template <typename U>
std::vector<U> GetNumericalValue() const {
// We do type-casting because the numerical types may not totally match.
// For example, we store bool as GesturesPropBool to be compatible with the
// gesture library. Also, all parsed xorg-conf property values are stored
// as double because we can't identify their original type lexically.
// TODO(sheckylin): Handle value out-of-range (e.g., double to int).
std::vector<U> result(count());
for (size_t i = 0; i < count(); ++i)
result[i] = static_cast<U>(value_[i]);
return result;
}
template <typename U>
bool SetNumericalValue(const std::vector<U>& value) {
for (size_t i = 0; i < count(); ++i)
value_[i] = static_cast<T>(value[i]);
return true;
}
// Check if a default property usable for (numerical) initialization.
bool IsDefaultPropertyUsable(const GesturesProp* default_property) const {
// We currently assumed that we won't specify any array property in the
// configuration files. The code needs to be updated if the assumption
// becomes invalid in the future.
return (count() == 1 && default_property &&
default_property->type() != PropertyType::PT_STRING);
}
// Accessors.
bool IsAllocated() const override { return is_allocated_; }
// If the flag is on, it means the memory that the data pointer points to is
// allocated here. We will need to free the memory by ourselves when the
// GesturesProp is destroyed.
bool is_allocated_;
};
class GesturesIntProp : public TypedGesturesProp<int> {
public:
GesturesIntProp(const std::string& name,
const size_t count,
int* value,
const int* init,
const GesturesProp* default_property)
: TypedGesturesProp<int>(name, PropertyType::PT_INT, count, value) {
InitializeNumericalProperty(init, default_property);
}
std::vector<int> GetIntValue() const override {
return this->template GetNumericalPropertyValue<int>();
}
bool SetIntValue(const std::vector<int>& value) override {
return this->template SetNumericalPropertyValue(value);
}
};
class GesturesShortProp : public TypedGesturesProp<short> {
public:
GesturesShortProp(const std::string& name,
const size_t count,
short* value,
const short* init,
const GesturesProp* default_property)
: TypedGesturesProp<short>(name, PropertyType::PT_SHORT, count, value) {
InitializeNumericalProperty(init, default_property);
}
std::vector<int16_t> GetShortValue() const override {
return this->template GetNumericalPropertyValue<int16_t>();
}
bool SetShortValue(const std::vector<int16_t>& value) override {
return this->template SetNumericalPropertyValue(value);
}
};
class GesturesBoolProp : public TypedGesturesProp<GesturesPropBool> {
public:
GesturesBoolProp(const std::string& name,
const size_t count,
GesturesPropBool* value,
const GesturesPropBool* init,
const GesturesProp* default_property)
: TypedGesturesProp<GesturesPropBool>(name,
PropertyType::PT_BOOL,
count,
value) {
InitializeNumericalProperty(init, default_property);
}
std::vector<bool> GetBoolValue() const override {
return this->template GetNumericalPropertyValue<bool>();
}
bool SetBoolValue(const std::vector<bool>& value) override {
return this->template SetNumericalPropertyValue(value);
}
};
class GesturesDoubleProp : public TypedGesturesProp<double> {
public:
GesturesDoubleProp(const std::string& name,
const size_t count,
double* value,
const double* init,
const GesturesProp* default_property)
: TypedGesturesProp<double>(name, PropertyType::PT_REAL, count, value) {
InitializeNumericalProperty(init, default_property);
}
std::vector<double> GetDoubleValue() const override {
return this->template GetNumericalPropertyValue<double>();
}
bool SetDoubleValue(const std::vector<double>& value) override {
return this->template SetNumericalPropertyValue(value);
}
};
class GesturesStringProp : public TypedGesturesProp<std::string> {
public:
// StringProperty's memory is always allocated on this side instead of
// externally in the gesture lib as the original one will be destroyed right
// after the constructor call (check the design of StringProperty). To do
// this, we call the TypedGesturesProp constructor with NULL pointer so that
// it always allocates.
GesturesStringProp(const std::string& name,
const char** value,
const char* init,
const GesturesProp* default_property)
: TypedGesturesProp<std::string>(name, PropertyType::PT_STRING, 1, NULL),
write_back_(NULL) {
InitializeStringProperty(value, init, default_property);
}
std::string GetStringValue() const override {
OnGet();
return *value_;
}
bool SetStringValue(const std::string& value) override {
if (is_read_only_)
return false;
*value_ = value;
// Write back the pointer in case it may change (e.g., string
// re-allocation).
if (write_back_)
*(write_back_) = value_->c_str();
OnSet();
return true;
}
private:
// Initialize the object.
void InitializeStringProperty(const char** value,
const char* init,
const GesturesProp* default_property) {
// Initialize the property value similar to the numerical types.
if (IsDefaultPropertyUsable(default_property)) {
GPROP_LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
*value_ = default_property->GetStringValue();
} else {
*value_ = init;
}
// If the provided pointer is not NULL, replace its content
// (val_ of StringProperty) with the address of our allocated string.
// Note that we don't have to do this for the other data types as they will
// use the original data pointer if possible and it is unnecessary to do so
// if the pointer is NULL.
if (value) {
*value = value_->c_str();
write_back_ = value;
// Set the read-only flag back to false.
is_read_only_ = false;
}
}
// Re-write the function with different criteria as we want string properties
// now.
bool IsDefaultPropertyUsable(const GesturesProp* default_property) const {
return (default_property &&
default_property->type() == PropertyType::PT_STRING);
}
const char** GetStringWritebackPtr() const override { return write_back_; }
// In some cases, we don't directly use the data pointer provided by the
// creators due to its limitation and instead use our own types (e.g., in
// the case of string). We thus need to store the write back pointer so that
// we can update the value in the gesture lib if the property value gets
// changed.
const char** write_back_;
};
// Anonymous namespace for utility functions and internal constants.
namespace {
// The path that we will look for conf files.
const char kConfigurationFilePath[] = "/etc/gesture";
// We support only match types that have already been used. One should change
// this if we start using new types in the future. Note that most unsupported
// match types are either useless in CrOS or inapplicable to the non-X
// environment.
const char* kSupportedMatchTypes[] = {"MatchProduct",
"MatchDevicePath",
"MatchUSBID",
"MatchIsPointer",
"MatchIsTouchpad",
"MatchIsTouchscreen"};
const char* kUnsupportedMatchTypes[] = {"MatchVendor",
"MatchOS",
"MatchPnPID",
"MatchDriver",
"MatchTag",
"MatchLayout",
"MatchIsKeyboard",
"MatchIsJoystick",
"MatchIsTablet"};
// Special keywords for boolean values.
const char* kTrue[] = {"on", "true", "yes"};
const char* kFalse[] = {"off", "false", "no"};
// Check if a device falls into one device type category.
bool IsDeviceOfType(const ui::GesturePropertyProvider::DevicePtr device,
const ui::GesturePropertyProvider::DeviceType type) {
EvdevClass evdev_class = device->info.evdev_class;
switch (type) {
case ui::GesturePropertyProvider::DT_MOUSE:
return (evdev_class == EvdevClassMouse ||
evdev_class == EvdevClassMultitouchMouse);
break;
case ui::GesturePropertyProvider::DT_TOUCHPAD:
// Note that the behavior here is different from the inputcontrol script
// which actually returns touchscreen devices as well.
return (evdev_class == EvdevClassTouchpad);
break;
case ui::GesturePropertyProvider::DT_TOUCHSCREEN:
return (evdev_class == EvdevClassTouchscreen);
break;
case ui::GesturePropertyProvider::DT_MULTITOUCH:
return (evdev_class == EvdevClassTouchpad ||
evdev_class == EvdevClassTouchscreen ||
evdev_class == EvdevClassMultitouchMouse);
break;
case ui::GesturePropertyProvider::DT_MULTITOUCH_MOUSE:
return (evdev_class == EvdevClassMultitouchMouse);
break;
case ui::GesturePropertyProvider::DT_ALL:
return true;
break;
default:
NOTREACHED();
break;
}
return false;
}
// Trick to get the device path from a file descriptor.
std::string GetDeviceNodePath(
const ui::GesturePropertyProvider::DevicePtr device) {
std::string proc_symlink = "/proc/self/fd/" + base::IntToString(device->fd);
base::FilePath path;
if (!base::ReadSymbolicLink(base::FilePath(proc_symlink), &path))
return std::string();
return path.value();
}
// Check if a match criteria is currently implemented. Note that we didn't
// implemented all of them as some are inapplicable in the non-X world.
bool IsMatchTypeSupported(const std::string& match_type) {
for (size_t i = 0; i < arraysize(kSupportedMatchTypes); ++i)
if (match_type == kSupportedMatchTypes[i])
return true;
for (size_t i = 0; i < arraysize(kUnsupportedMatchTypes); ++i) {
if (match_type == kUnsupportedMatchTypes[i]) {
LOG(ERROR) << "Unsupported gestures input class match type: "
<< match_type;
return false;
}
}
return false;
}
// Check if a match criteria is a device type one.
bool IsMatchDeviceType(const std::string& match_type) {
return StartsWithASCII(match_type, "MatchIs", true);
}
// Parse a boolean value keyword (e.g., on/off, true/false).
int ParseBooleanKeyword(const std::string& value) {
for (size_t i = 0; i < arraysize(kTrue); ++i)
if (LowerCaseEqualsASCII(value, kTrue[i]))
return 1;
for (size_t i = 0; i < arraysize(kFalse); ++i)
if (LowerCaseEqualsASCII(value, kFalse[i]))
return -1;
return 0;
}
// Log the value of an array property.
template <typename T>
void LogArrayProperty(std::ostream& os, const std::vector<T>& value) {
os << "(";
for (size_t i = 0; i < value.size(); ++i) {
if (i > 0)
os << ", ";
os << value[i];
}
os << ")";
}
// Property type logging function.
std::ostream& operator<<(std::ostream& out,
const ui::GesturePropertyProvider::PropertyType type) {
std::string s;
#define TYPE_CASE(TYPE) \
case (ui::GesturePropertyProvider::TYPE): \
s = #TYPE; \
break;
switch (type) {
TYPE_CASE(PT_INT);
TYPE_CASE(PT_SHORT);
TYPE_CASE(PT_BOOL);
TYPE_CASE(PT_STRING);
TYPE_CASE(PT_REAL);
default:
NOTREACHED();
break;
}
#undef TYPE_CASE
return out << s;
}
} // namespace
// GesturesProp logging function.
std::ostream& operator<<(std::ostream& os, const GesturesProp& prop) {
const GesturesProp* property = &prop;
// Output the property content.
os << "\"" << property->name() << "\", " << property->type() << ", "
<< property->count() << ", (" << property->IsAllocated() << ", "
<< property->IsReadOnly() << "), ";
// Only the string property has the write back pointer.
if (property->type() == ui::GesturePropertyProvider::PT_STRING)
os << property->GetStringWritebackPtr();
else
os << "NULL";
// Output the property values.
os << ", ";
switch (property->type()) {
case ui::GesturePropertyProvider::PT_INT:
LogArrayProperty(os, property->GetIntValue());
break;
case ui::GesturePropertyProvider::PT_SHORT:
LogArrayProperty(os, property->GetShortValue());
break;
case ui::GesturePropertyProvider::PT_BOOL:
LogArrayProperty(os, property->GetBoolValue());
break;
case ui::GesturePropertyProvider::PT_STRING:
os << "\"" << property->GetStringValue() << "\"";
break;
case ui::GesturePropertyProvider::PT_REAL:
LogArrayProperty(os, property->GetDoubleValue());
break;
default:
LOG(ERROR) << "Unknown gesture property type: " << property->type();
NOTREACHED();
break;
}
return os;
}
namespace ui {
namespace internal {
// Mapping table from a property name to its corresponding GesturesProp
// object pointer.
typedef base::hash_map<std::string, GesturesProp*> PropertiesMap;
typedef base::ScopedPtrHashMap<std::string, GesturesProp> ScopedPropertiesMap;
// Struct holding properties of a device.
//
// Note that we can't define it in GesturePropertyProvider as a nested class
// because ScopedPtrHashMap will require us to expose the GestureProp's
// destructor so that it can instantiate the object. This is something we
// don't want to do.
struct GestureDevicePropertyData {
GestureDevicePropertyData() {}
// Properties owned and being used by the device.
ScopedPropertiesMap properties;
// Unowned default properties (owned by the configuration file). Their values
// will be applied when a property of the same name is created. These are
// usually only a small portion of all properties in use.
PropertiesMap default_properties;
};
// Base class for device match criterias in conf files.
// Check the xorg-conf spec for more detailed information.
class MatchCriteria {
public:
typedef ui::GesturePropertyProvider::DevicePtr DevicePtr;
explicit MatchCriteria(const std::string& arg);
virtual ~MatchCriteria() {}
virtual bool Match(const DevicePtr device) = 0;
protected:
std::vector<std::string> args_;
};
// Match a device based on its evdev name string.
class MatchProduct : public MatchCriteria {
public:
explicit MatchProduct(const std::string& arg);
virtual ~MatchProduct() {}
virtual bool Match(const DevicePtr device);
};
// Math a device based on its device node path.
class MatchDevicePath : public MatchCriteria {
public:
explicit MatchDevicePath(const std::string& arg);
virtual ~MatchDevicePath() {}
virtual bool Match(const DevicePtr device);
};
// Math a USB device based on its USB vid and pid.
// Mostly used for external mice and touchpads.
class MatchUSBID : public MatchCriteria {
public:
explicit MatchUSBID(const std::string& arg);
virtual ~MatchUSBID() {}
virtual bool Match(const DevicePtr device);
private:
bool IsValidPattern(const std::string& pattern);
std::vector<std::string> vid_patterns_;
std::vector<std::string> pid_patterns_;
};
// Generic base class for device type math criteria.
class MatchDeviceType : public MatchCriteria {
public:
explicit MatchDeviceType(const std::string& arg);
virtual ~MatchDeviceType() {}
virtual bool Match(const DevicePtr device) = 0;
protected:
bool value_;
bool is_valid_;
};
// Check if a device is a pointer device.
class MatchIsPointer : public MatchDeviceType {
public:
explicit MatchIsPointer(const std::string& arg);
virtual ~MatchIsPointer() {}
virtual bool Match(const DevicePtr device);
};
// Check if a device is a touchpad.
class MatchIsTouchpad : public MatchDeviceType {
public:
explicit MatchIsTouchpad(const std::string& arg);
virtual ~MatchIsTouchpad() {}
virtual bool Match(const DevicePtr device);
};
// Check if a device is a touchscreen.
class MatchIsTouchscreen : public MatchDeviceType {
public:
explicit MatchIsTouchscreen(const std::string& arg);
virtual ~MatchIsTouchscreen() {}
virtual bool Match(const DevicePtr device);
};
// Struct for sections in xorg conf files.
struct ConfigurationSection {
typedef ui::GesturePropertyProvider::DevicePtr DevicePtr;
ConfigurationSection() {}
bool Match(const DevicePtr device);
std::string identifier;
ScopedVector<MatchCriteria> criterias;
ScopedVector<GesturesProp> properties;
};
MatchCriteria::MatchCriteria(const std::string& arg) {
// TODO(sheckylin): Should we trim all tokens here?
Tokenize(arg, "|", &args_);
if (args_.empty()) {
LOG(ERROR) << "Empty match pattern found, will evaluate to the default "
"value (true): \"" << arg << "\"";
}
}
MatchProduct::MatchProduct(const std::string& arg) : MatchCriteria(arg) {
}
bool MatchProduct::Match(const DevicePtr device) {
if (args_.empty())
return true;
std::string name(device->info.name);
for (size_t i = 0; i < args_.size(); ++i)
if (name.find(args_[i]) != std::string::npos)
return true;
return false;
}
MatchDevicePath::MatchDevicePath(const std::string& arg) : MatchCriteria(arg) {
}
bool MatchDevicePath::Match(const DevicePtr device) {
if (args_.empty())
return true;
// Check if the device path matches any pattern.
std::string path = GetDeviceNodePath(device);
if (path.empty())
return false;
for (size_t i = 0; i < args_.size(); ++i)
if (fnmatch(args_[i].c_str(), path.c_str(), FNM_NOESCAPE) == 0)
return true;
return false;
}
MatchUSBID::MatchUSBID(const std::string& arg) : MatchCriteria(arg) {
// Check each pattern and split valid ones into vids and pids.
for (size_t i = 0; i < args_.size(); ++i) {
if (!IsValidPattern(args_[i])) {
LOG(ERROR) << "Invalid USB ID: " << args_[i];
continue;
}
std::vector<std::string> tokens;
base::SplitString(args_[i], ':', &tokens);
vid_patterns_.push_back(base::StringToLowerASCII(tokens[0]));
pid_patterns_.push_back(base::StringToLowerASCII(tokens[1]));
}
if (vid_patterns_.empty()) {
LOG(ERROR) << "No valid USB ID pattern found, will be ignored: \"" << arg
<< "\"";
}
}
bool MatchUSBID::Match(const DevicePtr device) {
if (vid_patterns_.empty())
return true;
std::string vid = base::StringPrintf("%04x", device->info.id.vendor);
std::string pid = base::StringPrintf("%04x", device->info.id.product);
for (size_t i = 0; i < vid_patterns_.size(); ++i) {
if (fnmatch(vid_patterns_[i].c_str(), vid.c_str(), FNM_NOESCAPE) == 0 &&
fnmatch(pid_patterns_[i].c_str(), pid.c_str(), FNM_NOESCAPE) == 0) {
return true;
}
}
return false;
}
bool MatchUSBID::IsValidPattern(const std::string& pattern) {
// Each USB id should be in the lsusb format, i.e., xxxx:xxxx. We choose to do
// a lazy check here: if the pattern contains wrong characters not in the hex
// number range, it won't be matched anyway.
int number_of_colons = 0;
size_t pos_of_colon = 0;
for (size_t i = 0; i < pattern.size(); ++i)
if (pattern[i] == ':')
++number_of_colons, pos_of_colon = i;
return (number_of_colons == 1) && (pos_of_colon != 0) &&
(pos_of_colon != pattern.size() - 1);
}
MatchDeviceType::MatchDeviceType(const std::string& arg)
: MatchCriteria(arg), value_(true), is_valid_(false) {
// Default value of a match criteria is true.
if (args_.empty())
args_.push_back("on");
// We care only about the first argument.
int value = ParseBooleanKeyword(args_[0]);
if (value) {
is_valid_ = true;
value_ = value > 0;
}
if (!is_valid_) {
LOG(ERROR)
<< "No valid device class boolean keyword found, will be ignored: \""
<< arg << "\"";
}
}
MatchIsPointer::MatchIsPointer(const std::string& arg) : MatchDeviceType(arg) {
}
bool MatchIsPointer::Match(const DevicePtr device) {
if (!is_valid_)
return true;
return (value_ == (device->info.evdev_class == EvdevClassMouse ||
device->info.evdev_class == EvdevClassMultitouchMouse));
}
MatchIsTouchpad::MatchIsTouchpad(const std::string& arg)
: MatchDeviceType(arg) {
}
bool MatchIsTouchpad::Match(const DevicePtr device) {
if (!is_valid_)
return true;
return (value_ == (device->info.evdev_class == EvdevClassTouchpad));
}
MatchIsTouchscreen::MatchIsTouchscreen(const std::string& arg)
: MatchDeviceType(arg) {
}
bool MatchIsTouchscreen::Match(const DevicePtr device) {
if (!is_valid_)
return true;
return (value_ == (device->info.evdev_class == EvdevClassTouchscreen));
}
bool ConfigurationSection::Match(DevicePtr device) {
for (size_t i = 0; i < criterias.size(); ++i)
if (!criterias[i]->Match(device))
return false;
return true;
}
} // namespace internal
GesturePropertyProvider::GesturePropertyProvider() {
LoadDeviceConfigurations();
}
GesturePropertyProvider::~GesturePropertyProvider() {
}
void GesturePropertyProvider::GetDeviceIdsByType(
const DeviceType type,
std::vector<DeviceId>* device_ids) {
device_ids->clear();
DeviceMap::const_iterator it = device_map_.begin();
for (; it != device_map_.end(); ++it)
if (IsDeviceOfType(it->second, type))
device_ids->push_back(it->first);
}
GesturesProp* GesturePropertyProvider::GetProperty(const DeviceId device_id,
const std::string& name) {
return FindProperty(device_id, name);
}
void GesturePropertyProvider::RegisterDevice(const DeviceId id,
const DevicePtr device) {
DeviceMap::const_iterator it = device_map_.find(id);
if (it != device_map_.end())
return;
// Setup data-structures.
device_map_[id] = device;
device_data_map_.set(id,
scoped_ptr<internal::GestureDevicePropertyData>(
new internal::GestureDevicePropertyData));
// Gather default property values for the device from the parsed conf files.
SetupDefaultProperties(id, device);
return;
}
void GesturePropertyProvider::UnregisterDevice(const DeviceId id) {
DeviceMap::const_iterator it = device_map_.find(id);
if (it == device_map_.end())
return;
device_data_map_.erase(id);
device_map_.erase(it);
}
void GesturePropertyProvider::AddProperty(const DeviceId device_id,
const std::string& name,
GesturesProp* property) {
// The look-up should never fail because ideally a property can only be
// created with GesturesPropCreate* functions from the gesture lib side.
// Therefore, we simply return on failure.
internal::GestureDevicePropertyData* device_data =
device_data_map_.get(device_id);
if (device_data)
device_data->properties.set(name, scoped_ptr<GesturesProp>(property));
}
void GesturePropertyProvider::DeleteProperty(const DeviceId device_id,
const std::string& name) {
internal::GestureDevicePropertyData* device_data =
device_data_map_.get(device_id);
if (device_data)
device_data->properties.erase(name);
}
GesturesProp* GesturePropertyProvider::FindProperty(const DeviceId device_id,
const std::string& name) {
internal::GestureDevicePropertyData* device_data =
device_data_map_.get(device_id);
if (!device_data)
return NULL;
return device_data->properties.get(name);
}
GesturesProp* GesturePropertyProvider::GetDefaultProperty(
const DeviceId device_id,
const std::string& name) {
internal::GestureDevicePropertyData* device_data =
device_data_map_.get(device_id);
if (!device_data)
return NULL;
internal::PropertiesMap::const_iterator ib =
device_data->default_properties.find(name);
if (ib == device_data->default_properties.end())
return NULL;
return ib->second;
}
void GesturePropertyProvider::LoadDeviceConfigurations() {
// Enumerate conf files and sort them lexicographically.
std::set<base::FilePath> files;
base::FileEnumerator file_enum(base::FilePath(kConfigurationFilePath),
false,
base::FileEnumerator::FILES,
"*.conf");
for (base::FilePath path = file_enum.Next(); !path.empty();
path = file_enum.Next()) {
files.insert(path);
}
GPROP_LOG(INFO_SEVERITY) << files.size() << " conf files were found";
// Parse conf files one-by-one.
for (std::set<base::FilePath>::iterator file_iter = files.begin();
file_iter != files.end();
++file_iter) {
GPROP_LOG(INFO_SEVERITY) << "Parsing conf file: " << (*file_iter).value();
std::string content;
if (!base::ReadFileToString(*file_iter, &content)) {
LOG(ERROR) << "Can't loading gestures conf file: "
<< (*file_iter).value();
continue;
}
ParseXorgConfFile(content);
}
}
void GesturePropertyProvider::ParseXorgConfFile(const std::string& content) {
// To simplify the parsing work, we made some assumption about the conf file
// format which doesn't exist in the original xorg-conf spec. Most important
// ones are:
// 1. All keywords and names are now case-sensitive. Also, underscores are not
// ignored.
// 2. Each entry takes up one and exactly one line in the file.
// 3. No negation of the option value even if the option name is prefixed with
// "No" as it may cause problems for option names that does start with "No"
// (e.g., "Non-linearity").
// Break the content into sections, lines and then pieces.
// Sections are delimited by the "EndSection" keyword.
// Lines are delimited by "\n".
// Pieces are delimited by all white-spaces.
std::vector<std::string> sections;
base::SplitStringUsingSubstr(content, "EndSection", &sections);
for (size_t i = 0; i < sections.size(); ++i) {
// Create a new configuration section.
configurations_.push_back(new internal::ConfigurationSection());
internal::ConfigurationSection* config = configurations_.back();
// Break the section into lines.
base::StringTokenizer lines(sections[i], "\n");
bool is_input_class_section = true;
bool has_checked_section_type = false;
while (is_input_class_section && lines.GetNext()) {
// Parse the line w.r.t. the xorg-conf format.
std::string line(lines.token());
// Skip empty lines.
if (line.empty())
continue;
// Treat all whitespaces as delimiters.
base::StringTokenizer pieces(line, base::kWhitespaceASCII);
pieces.set_quote_chars("\"");
bool is_parsing = false;
bool has_error = false;
bool next_is_section_type = false;
bool next_is_option_name = false;
bool next_is_option_value = false;
bool next_is_match_criteria = false;
bool next_is_identifier = false;
std::string match_type, option_name;
while (pieces.GetNext()) {
std::string piece(pieces.token());
// Skip empty pieces.
if (piece.empty())
continue;
// See if we are currently parsing an entry or are still looking for
// one.
if (is_parsing) {
// Stop parsing the current line if the format is wrong.
if (piece.size() <= 2 || piece[0] != '\"' ||
piece[piece.size() - 1] != '\"') {
LOG(ERROR) << "Error parsing line: " << lines.token();
has_error = true;
if (next_is_section_type)
is_input_class_section = false;
break;
}
// Parse the arguments. Note that we don't break even if a whitespace
// string is passed. It will just be handled in various ways based on
// the entry type.
std::string arg;
base::TrimWhitespaceASCII(
piece.substr(1, piece.size() - 2), base::TRIM_ALL, &arg);
if (next_is_section_type) {
// We only care about InputClass sections.
if (arg != "InputClass") {
has_error = true;
is_input_class_section = false;
} else {
GPROP_LOG(INFO_SEVERITY) << "New InputClass section found";
has_checked_section_type = true;
}
break;
} else if (next_is_identifier) {
GPROP_LOG(INFO_SEVERITY) << "Identifier: " << arg;
config->identifier = arg;
next_is_identifier = false;
break;
} else if (next_is_option_name) {
// TODO(sheckylin): Support option "Ignore".
option_name = arg;
next_is_option_value = true;
next_is_option_name = false;
} else if (next_is_option_value) {
GesturesProp* property = CreateDefaultProperty(option_name, arg);
if (property)
config->properties.push_back(property);
next_is_option_value = false;
break;
} else if (next_is_match_criteria) {
// Skip all match types that are not supported.
if (IsMatchTypeSupported(match_type)) {
internal::MatchCriteria* criteria =
CreateMatchCriteria(match_type, arg);
if (criteria)
config->criterias.push_back(criteria);
}
next_is_match_criteria = false;
break;
}
} else {
// If the section type hasn't been decided yet, look for it.
// Otherwise, look for valid entries according to the spec.
if (has_checked_section_type) {
if (piece == "Driver") {
// TODO(sheckylin): Support "Driver" so that we can force a device
// not to use the gesture lib.
NOTIMPLEMENTED();
break;
} else if (piece == "Identifier") {
is_parsing = true;
next_is_identifier = true;
continue;
} else if (piece == "Option") {
is_parsing = true;
next_is_option_name = true;
continue;
} else if (piece.size() > 5 && piece.compare(0, 5, "Match") == 0) {
match_type = piece;
is_parsing = true;
next_is_match_criteria = true;
continue;
}
} else if (piece == "Section") {
is_parsing = true;
next_is_section_type = true;
continue;
}
// If none of the above is found, check if the current piece starts a
// comment.
if (piece.empty() || piece[0] != '#') {
LOG(ERROR) << "Error parsing line: " << lines.token();
has_error = true;
}
break;
}
}
// The value of a boolean option is skipped (default is true).
if (!has_error && (next_is_option_value || next_is_match_criteria)) {
if (next_is_option_value) {
GesturesProp* property = CreateDefaultProperty(option_name, "on");
if (property)
config->properties.push_back(property);
} else if (IsMatchTypeSupported(match_type) &&
IsMatchDeviceType(match_type)) {
internal::MatchCriteria* criteria =
CreateMatchCriteria(match_type, "on");
if (criteria)
config->criterias.push_back(criteria);
}
}
}
// Remove useless config sections.
if (!is_input_class_section ||
(config->criterias.empty() && config->properties.empty())) {
configurations_.pop_back();
}
}
}
internal::MatchCriteria* GesturePropertyProvider::CreateMatchCriteria(
const std::string& match_type,
const std::string& arg) {
GPROP_LOG(INFO_SEVERITY) << "Creating match criteria: (" << match_type << ", "
<< arg << ")";
if (match_type == "MatchProduct")
return new internal::MatchProduct(arg);
if (match_type == "MatchDevicePath")
return new internal::MatchDevicePath(arg);
if (match_type == "MatchUSBID")
return new internal::MatchUSBID(arg);
if (match_type == "MatchIsPointer")
return new internal::MatchIsPointer(arg);
if (match_type == "MatchIsTouchpad")
return new internal::MatchIsTouchpad(arg);
if (match_type == "MatchIsTouchscreen")
return new internal::MatchIsTouchscreen(arg);
NOTREACHED();
return NULL;
}
GesturesProp* GesturePropertyProvider::CreateDefaultProperty(
const std::string& name,
const std::string& value) {
// Our parsing rule:
// 1. No hex or oct number is accepted.
// 2. All numbers will be stored as double.
// 3. Array elements can be separated by both white-spaces or commas.
// 4. A token is treated as numeric either if it is one of the special
// keywords for boolean values (on, true, yes, off, false, no) or if
// base::StringToDouble succeeds.
// 5. The property is treated as numeric if and only if all of its elements
// (if any) are numerics. Otherwise, it will be treated as a string.
// 6. A string property will be trimmed before storing its value.
GPROP_LOG(INFO_SEVERITY) << "Creating default property: (" << name << ", "
<< value << ")";
// Parse elements one-by-one.
std::string delimiters(base::kWhitespaceASCII);
delimiters.append(",");
base::StringTokenizer tokens(value, delimiters);
bool is_all_numeric = true;
std::vector<double> numbers;
while (tokens.GetNext()) {
// Skip empty tokens.
std::string token(tokens.token());
if (token.empty())
continue;
// Check if it is a boolean keyword.
int bool_result = ParseBooleanKeyword(token);
if (bool_result) {
numbers.push_back(bool_result > 0);
continue;
}
// Check if it is a number.
double real_result;
bool success = base::StringToDouble(token, &real_result);
if (!success) {
is_all_numeric = false;
break;
}
numbers.push_back(real_result);
}
// Create the GesturesProp. Array properties need to contain at least one
// number and may contain numbers only.
GesturesProp* property = NULL;
if (is_all_numeric && numbers.size()) {
property = new GesturesDoubleProp(
name, numbers.size(), NULL, numbers.data(), NULL);
} else {
property = new GesturesStringProp(name, NULL, value.c_str(), NULL);
}
GPROP_LOG(INFO_SEVERITY) << "Prop: " << *property;
// The function will always succeed for now but it may change later if we
// specify some name or args as invalid.
return property;
}
void GesturePropertyProvider::SetupDefaultProperties(const DeviceId device_id,
const DevicePtr device) {
GPROP_LOG(INFO_SEVERITY) << "Setting up default properties for (" << device
<< ", " << device_id << ", " << device->info.name
<< ")";
// Go through all parsed sections.
internal::PropertiesMap& property_map =
device_data_map_.get(device_id)->default_properties;
for (size_t i = 0; i < configurations_.size(); ++i) {
if (configurations_[i]->Match(device)) {
GPROP_LOG(INFO_SEVERITY) << "Conf section \""
<< configurations_[i]->identifier
<< "\" is matched";
for (size_t j = 0; j < configurations_[i]->properties.size(); j++) {
GesturesProp* property = configurations_[i]->properties[j];
// We can't use insert here because a property may be set for several
// times along the way.
property_map[property->name()] = property;
}
}
}
}
GesturesProp* GesturesPropFunctionsWrapper::CreateInt(void* device_data,
const char* name,
int* value,
size_t count,
const int* init) {
return CreateProperty<int, GesturesIntProp>(
device_data, name, value, count, init);
}
GesturesProp* GesturesPropFunctionsWrapper::CreateShort(void* device_data,
const char* name,
short* value,
size_t count,
const short* init) {
return CreateProperty<short, GesturesShortProp>(
device_data, name, value, count, init);
}
GesturesProp* GesturesPropFunctionsWrapper::CreateBool(
void* device_data,
const char* name,
GesturesPropBool* value,
size_t count,
const GesturesPropBool* init) {
return CreateProperty<GesturesPropBool, GesturesBoolProp>(
device_data, name, value, count, init);
}
GesturesProp* GesturesPropFunctionsWrapper::CreateReal(void* device_data,
const char* name,
double* value,
size_t count,
const double* init) {
return CreateProperty<double, GesturesDoubleProp>(
device_data, name, value, count, init);
}
GesturesProp* GesturesPropFunctionsWrapper::CreateString(void* device_data,
const char* name,
const char** value,
const char* init) {
GesturesProp* default_property = NULL;
if (!PreCreateProperty(device_data, name, &default_property))
return NULL;
GesturesProp* property =
new GesturesStringProp(name, value, init, default_property);
PostCreateProperty(device_data, name, property);
return property;
}
void GesturesPropFunctionsWrapper::RegisterHandlers(
void* device_data,
GesturesProp* property,
void* handler_data,
GesturesPropGetHandler get,
GesturesPropSetHandler set) {
// Sanity checks
if (!device_data || !property)
return;
property->SetHandlers(get, set, handler_data);
}
void GesturesPropFunctionsWrapper::Free(void* device_data,
GesturesProp* property) {
if (!property)
return;
GesturePropertyProvider* provider = GetPropertyProvider(device_data);
// No need to manually delete the prop pointer as it is implicitly handled
// with scoped ptr.
GPROP_LOG(3) << "Freeing Property: \"" << property->name() << "\"";
provider->DeleteProperty(GetDeviceId(device_data), property->name());
}
bool GesturesPropFunctionsWrapper::InitializeDeviceProperties(
void* device_data,
GestureDeviceProperties* properties) {
if (!device_data)
return false;
GesturePropertyProvider::DevicePtr device = GetDevicePointer(device_data);
/* Create Device Properties */
// Read Only properties.
CreateString(
device_data, "Device Node", NULL, GetDeviceNodePath(device).c_str());
short vid = static_cast<short>(device->info.id.vendor);
CreateShort(device_data, "Device Vendor ID", NULL, 1, &vid);
short pid = static_cast<short>(device->info.id.product);
CreateShort(device_data, "Device Product ID", NULL, 1, &pid);
// Useable trackpad area. If not configured in .conf file,
// use x/y valuator min/max as reported by kernel driver.
CreateIntSingle(device_data,
"Active Area Left",
&properties->area_left,
Event_Get_Left(device));
CreateIntSingle(device_data,
"Active Area Right",
&properties->area_right,
Event_Get_Right(device));
CreateIntSingle(device_data,
"Active Area Top",
&properties->area_top,
Event_Get_Top(device));
CreateIntSingle(device_data,
"Active Area Bottom",
&properties->area_bottom,
Event_Get_Bottom(device));
// Trackpad resolution (pixels/mm). If not configured in .conf file,
// use x/y resolution as reported by kernel driver.
CreateIntSingle(device_data,
"Vertical Resolution",
&properties->res_y,
Event_Get_Res_Y(device));
CreateIntSingle(device_data,
"Horizontal Resolution",
&properties->res_x,
Event_Get_Res_X(device));
// Trackpad orientation minimum/maximum. If not configured in .conf file,
// use min/max as reported by kernel driver.
CreateIntSingle(device_data,
"Orientation Minimum",
&properties->orientation_minimum,
Event_Get_Orientation_Minimum(device));
CreateIntSingle(device_data,
"Orientation Maximum",
&properties->orientation_maximum,
Event_Get_Orientation_Maximum(device));
// Log dump property. Will call Event_Dump_Debug_Log when its value is being
// set.
GesturesProp* dump_debug_log_prop = CreateBoolSingle(
device_data, "Dump Debug Log", &properties->dump_debug_log, false);
RegisterHandlers(
device_data, dump_debug_log_prop, device, NULL, Event_Dump_Debug_Log);
// Whether to do the gesture recognition or just passing the multi-touch data
// to upper layers.
CreateBoolSingle(device_data,
"Raw Touch Passthrough",
&properties->raw_passthrough,
false);
return true;
}
void GesturesPropFunctionsWrapper::UnregisterDevice(void* device_data) {
GesturePropertyProvider* provider = GetPropertyProvider(device_data);
provider->UnregisterDevice(GetDeviceId(device_data));
}
template <typename T, class PROPTYPE>
GesturesProp* GesturesPropFunctionsWrapper::CreateProperty(void* device_data,
const char* name,
T* value,
size_t count,
const T* init) {
// Create the property. Use the default property value if possible.
GesturesProp* default_property = NULL;
if (!PreCreateProperty(device_data, name, &default_property))
return NULL;
GesturesProp* property =
new PROPTYPE(name, count, value, init, default_property);
// Start tracking the property in the provider.
PostCreateProperty(device_data, name, property);
return property;
}
bool GesturesPropFunctionsWrapper::PreCreateProperty(
void* device_data,
const char* name,
GesturesProp** default_property) {
GesturePropertyProvider* provider = GetPropertyProvider(device_data);
GesturePropertyProvider::DeviceId device_id = GetDeviceId(device_data);
// Register the device in the property provider if not yet.
provider->RegisterDevice(device_id, GetDevicePointer(device_data));
// First, see if the GesturesProp already exists.
GPROP_LOG(3) << "Creating Property: \"" << name << "\"";
GesturesProp* property = provider->FindProperty(device_id, name);
// If so, delete it as we can't reuse the data structure (newly-created
// property may have different data type and count, which are fixed upon
// creation via the template mechanism).
if (property) {
LOG(WARNING) << "Gesture property \"" << name
<< "\" re-created. This shouldn't happen at the normal usage.";
Free(device_data, property);
}
// Return the found default property from conf files (could be NULL).
*default_property = provider->GetDefaultProperty(device_id, name);
return true;
}
void GesturesPropFunctionsWrapper::PostCreateProperty(void* device_data,
const char* name,
GesturesProp* property) {
// Add the property to the gesture property provider. The gesture property
// provider will own it from now on.
GesturePropertyProvider* provider = GetPropertyProvider(device_data);
provider->AddProperty(GetDeviceId(device_data), name, property);
// Log the creation.
GPROP_LOG(INFO_SEVERITY) << "Created active prop: " << *property;
}
GesturesProp* GesturesPropFunctionsWrapper::CreateIntSingle(void* device_data,
const char* name,
int* value,
int init) {
return CreateInt(device_data, name, value, 1, &init);
}
GesturesProp* GesturesPropFunctionsWrapper::CreateBoolSingle(
void* device_data,
const char* name,
GesturesPropBool* value,
GesturesPropBool init) {
return CreateBool(device_data, name, value, 1, &init);
}
GesturePropertyProvider* GesturesPropFunctionsWrapper::GetPropertyProvider(
void* device_data) {
return static_cast<GestureInterpreterLibevdevCros*>(device_data)
->property_provider();
}
GesturePropertyProvider::DevicePtr
GesturesPropFunctionsWrapper::GetDevicePointer(void* device_data) {
return static_cast<GestureInterpreterLibevdevCros*>(device_data)->evdev();
}
GesturePropertyProvider::DeviceId GesturesPropFunctionsWrapper::GetDeviceId(
void* device_data) {
return static_cast<GestureInterpreterLibevdevCros*>(device_data)->id();
}
/* Global GesturesPropProvider
*
* Used by PropRegistry in GestureInterpreter to forward property value
* creations from there.
* */
const GesturesPropProvider kGesturePropProvider = {
GesturesPropFunctionsWrapper::CreateInt,
GesturesPropFunctionsWrapper::CreateShort,
GesturesPropFunctionsWrapper::CreateBool,
GesturesPropFunctionsWrapper::CreateString,
GesturesPropFunctionsWrapper::CreateReal,
GesturesPropFunctionsWrapper::RegisterHandlers,
GesturesPropFunctionsWrapper::Free};
} // namespace ui