Chromium Code Reviews| Index: ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc |
| diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6624d26a1fc75450209c566048ba39686fc7c3ac |
| --- /dev/null |
| +++ b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc |
| @@ -0,0 +1,1571 @@ |
| +// 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 <string.h> |
| + |
| +#include <algorithm> |
| +#include <iostream> |
| +#include <set> |
| + |
| +#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. |
| + * */ |
| +struct GesturesProp { |
|
spang
2014/10/03 21:40:11
This is nontrivial and should not be a struct. Cha
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + typedef ui::GesturePropertyProvider::PropertyType PropertyType; |
| + GesturesProp(const std::string& n, |
| + const PropertyType tp, |
| + const size_t c) |
| + : name(n), |
| + type(tp), |
| + count(c), |
| + is_allocated(false), |
| + is_read_only(false), |
| + handler_data(NULL), |
| + get(NULL), |
| + set(NULL) { |
| + } |
| + virtual ~GesturesProp() {} |
| + |
| + // Get the memory size of one element of the property data type. |
| + virtual size_t GetElementSize() const = 0; |
| + |
| + // Variant-ish interfaces for accessing the property value. Each type of |
| + // property should override the corresponding interfaces for it. |
| + virtual bool GetIntValue(int* v) const { |
|
spang
2014/10/03 21:40:11
You've lost all bounds checking here (in my sugges
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool SetIntValue(const int* v) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool GetShortValue(short* v) const { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool SetShortValue(const short* v) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool GetBoolValue(bool* v) const { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool SetBoolValue(const bool* v) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool GetStringValue(std::string* v) const { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool SetStringValue(const std::string* v) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool GetDoubleValue(double* v) const { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + virtual bool SetDoubleValue(const double* v) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + // Interfaces for getting the data pointer and stuff. |
| + virtual int* GetIntValuePtr() const { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + virtual short* GetShortValuePtr() const { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + virtual GesturesPropBool* GetBoolValuePtr() const { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + virtual std::string* GetStringValuePtr() const { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + virtual const char** GetStringWritebackPtr() const { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + virtual double* GetDoubleValuePtr() const { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + |
| + // Property name, type and number of elements. |
| + std::string name; |
| + PropertyType type; |
| + size_t count; |
| + |
| + // 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; |
| + |
| + // 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; |
| + |
| + // Handler function pointers and the data to be passed to them when the |
| + // property is accessed. |
| + void* handler_data; |
| + GesturesPropGetHandler get; |
| + GesturesPropSetHandler set; |
| + 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& prop); |
| +}; |
| + |
| +template <typename T> |
| +class TypedGesturesProp : public GesturesProp { |
| + public: |
| + TypedGesturesProp(const std::string& n, |
| + const PropertyType tp, |
| + const size_t c, |
| + T* v) |
| + : GesturesProp(n, tp, c), val_(v) { |
| + if (!val_) { |
| + if (count > 1) |
|
spang
2014/10/03 21:40:11
I think you can remove the if{} here (and below).
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + val_ = new T[count]; |
| + else |
| + val_ = new T; |
| + is_allocated = true; |
| + is_read_only = true; |
| + } |
| + } |
| + virtual ~TypedGesturesProp() { |
| + if (is_allocated) { |
| + if (count > 1) |
| + delete[] val_; |
| + else |
| + delete val_; |
| + } |
| + } |
| + virtual size_t GetElementSize() const { return sizeof(T); } |
| + |
| + protected: |
| + // Functions for setting/getting numerical properties. |
| + template <typename U> |
| + bool GetNumericalValue(U* v) const { |
|
spang
2014/10/03 21:40:11
If you use vectors:
std::vector<U> GetNumericalVe
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + // 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). |
| + for (size_t i = 0; i < count; ++i) |
| + v[i] = static_cast<U>(val_[i]); |
| + return true; |
| + } |
| + |
| + template <typename U> |
| + bool SetNumericalValue(const U* v) { |
|
spang
2014/10/03 21:40:11
void SetNumericalVector(const std::vector<U>& v)
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + for (size_t i = 0; i < count; ++i) |
| + val_[i] = static_cast<T>(v[i]); |
| + return true; |
| + } |
| + |
| + // These two functions calls the set/get handler and should only be used in |
| + // Get*Value/Set*Value functions. |
| + template <typename U> |
| + bool GetNumericalPropertyValue(U* v) const { |
|
spang
2014/10/03 21:40:11
std::vector<U> GetNumericalPropertyVector()
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + bool ret = this->template GetNumericalValue(v); |
| + OnGet(); |
| + return ret; |
| + } |
| + |
| + template <typename U> |
| + bool SetNumericalPropertyValue(const U* v) { |
|
spang
2014/10/03 21:40:11
void SetNumericalPropertyVector(const std::vector<
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + bool ret = this->template SetNumericalValue(v); |
| + OnSet(); |
| + return ret; |
| + } |
| + |
| + // Check if a default property usable for (numerical) initialization. Derived |
| + // classes may override it when necessary. |
| + virtual bool IsDefaultPropertyUsable(GesturesProp* default_prop) { |
| + // 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_prop && |
| + default_prop->type != PropertyType::PT_STRING); |
| + } |
| + |
| + // Initialize a numerical property's value. Note that a (numerical) default |
| + // property's value is always stored in double. |
| + void InitializeNumericalProperty(const T* init, GesturesProp* default_prop) { |
| + if (IsDefaultPropertyUsable(default_prop)) { |
| + GPROP_LOG(INFO_SEVERITY) << "Default property found. Using its value ..."; |
| + this->template SetNumericalValue(default_prop->GetDoubleValuePtr()); |
| + } else { |
| + this->template SetNumericalValue(init); |
| + } |
| + } |
| + |
| + // Data pointer. |
| + T* val_; |
| +}; |
| + |
| +class GesturesIntProp : public TypedGesturesProp<int> { |
| + public: |
| + GesturesIntProp(const std::string& n, |
| + const size_t c, |
| + int* v, |
| + const int* init, |
| + GesturesProp* default_prop) |
| + : TypedGesturesProp<int>(n, PropertyType::PT_INT, c, v) { |
| + InitializeNumericalProperty(init, default_prop); |
| + } |
| + |
| + virtual bool GetIntValue(int* v) const { |
| + return this->template GetNumericalPropertyValue(v); |
| + } |
| + virtual bool SetIntValue(const int* v) { |
| + return this->template SetNumericalPropertyValue(v); |
| + } |
| + virtual int* GetIntValuePtr() const { return val_; } |
| +}; |
| + |
| +class GesturesShortProp : public TypedGesturesProp<short> { |
| + public: |
| + GesturesShortProp(const std::string& n, |
| + const size_t c, |
| + short* v, |
| + const short* init, |
| + GesturesProp* default_prop) |
| + : TypedGesturesProp<short>(n, PropertyType::PT_SHORT, c, v) { |
| + InitializeNumericalProperty(init, default_prop); |
| + } |
| + virtual bool GetShortValue(short* v) const { |
| + return this->template GetNumericalPropertyValue(v); |
| + } |
| + virtual bool SetShortValue(const short* v) { |
| + return this->template SetNumericalPropertyValue(v); |
| + } |
| + virtual short* GetShortValuePtr() const { return val_; } |
| +}; |
| + |
| +class GesturesBoolProp : public TypedGesturesProp<GesturesPropBool> { |
| + public: |
| + GesturesBoolProp(const std::string& n, |
| + const size_t c, |
| + GesturesPropBool* v, |
| + const GesturesPropBool* init, |
| + GesturesProp* default_prop) |
| + : TypedGesturesProp<GesturesPropBool>(n, PropertyType::PT_BOOL, c, v) { |
| + InitializeNumericalProperty(init, default_prop); |
| + } |
| + virtual bool GetBoolValue(bool* v) const { |
| + return this->template GetNumericalPropertyValue(v); |
| + } |
| + virtual bool SetBoolValue(const bool* v) { |
| + return this->template SetNumericalPropertyValue(v); |
| + } |
| + virtual GesturesPropBool* GetBoolValuePtr() const { return val_; } |
| +}; |
| + |
| +class GesturesDoubleProp : public TypedGesturesProp<double> { |
| + public: |
| + GesturesDoubleProp(const std::string& n, |
| + const size_t c, |
| + double* v, |
| + const double* init, |
| + GesturesProp* default_prop) |
| + : TypedGesturesProp<double>(n, PropertyType::PT_REAL, c, v) { |
| + InitializeNumericalProperty(init, default_prop); |
| + } |
| + virtual bool GetDoubleValue(double* v) const { |
| + return this->template GetNumericalPropertyValue(v); |
| + } |
| + virtual bool SetDoubleValue(const double* v) { |
| + return this->template SetNumericalPropertyValue(v); |
| + } |
| + virtual double* GetDoubleValuePtr() const { return val_; } |
| +}; |
| + |
| +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& n, |
| + const char** v, |
| + const char* init, |
| + GesturesProp* default_prop) |
| + : TypedGesturesProp<std::string>(n, PropertyType::PT_STRING, 1, NULL), |
| + write_back_(NULL) { |
| + // Initialize the property value similar to the numerical types. |
| + if (IsDefaultPropertyUsable(default_prop)) { |
| + GPROP_LOG(INFO_SEVERITY) << "Default property found. Using its value ..."; |
| + *val_ = *(default_prop->GetStringValuePtr()); |
| + } else { |
| + *val_ = 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 (v) { |
| + *v = val_->c_str(); |
| + write_back_ = v; |
| + // Set the read-only flag back to false. |
| + is_read_only = false; |
| + } |
| + } |
| + virtual bool GetStringValue(std::string* v) const { |
| + *v = *val_; |
| + OnGet(); |
| + return true; |
| + } |
| + virtual bool SetStringValue(const std::string* v) { |
| + *val_ = *v; |
| + |
| + // Write back the pointer in case it may change (e.g., string |
| + // re-allocation). |
| + if (write_back_) |
| + *(write_back_) = val_->c_str(); |
| + OnSet(); |
| + return true; |
| + } |
| + virtual std::string* GetStringValuePtr() const { return val_; } |
| + virtual const char** GetStringWritebackPtr() const { |
| + return write_back_; |
| + } |
| + |
| + protected: |
| + // Override the function as we want string properties now. |
| + virtual bool IsDefaultPropertyUsable(GesturesProp* default_prop) { |
| + return (default_prop && default_prop->type == PropertyType::PT_STRING); |
| + } |
| + |
| + private: |
| + // 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_; |
| +}; |
| + |
| +// 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; |
| +} |
| + |
| +// GesturesProp logging function. |
| +std::ostream& operator<<(std::ostream& os, const GesturesProp& p) { |
| + const GesturesProp* prop = &p; |
| + |
| + // Output the property content. |
| + os << "\"" << prop->name << "\", " << prop->type << ", " << prop->count |
| + << ", (" << prop->is_allocated << ", " << prop->is_read_only << "), "; |
| + |
| + // Only the string property has the write back pointer. |
| + if (prop->type == ui::GesturePropertyProvider::PT_STRING) |
| + os << prop->GetStringWritebackPtr(); |
| + else |
| + os << "NULL"; |
| + |
| + // Output the property values. |
| + os << ", ("; |
| + for (size_t i = 0; i < prop->count; i++) { |
| + switch (prop->type) { |
| + case ui::GesturePropertyProvider::PT_INT: |
| + os << prop->GetIntValuePtr()[i]; |
| + break; |
| + case ui::GesturePropertyProvider::PT_SHORT: |
| + os << prop->GetShortValuePtr()[i]; |
| + break; |
| + case ui::GesturePropertyProvider::PT_BOOL: |
| + // Prevent the value being printed as characters. |
| + os << static_cast<bool>(prop->GetBoolValuePtr()[i]); |
| + break; |
| + case ui::GesturePropertyProvider::PT_STRING: |
| + os << prop->GetStringValuePtr()[i]; |
| + break; |
| + case ui::GesturePropertyProvider::PT_REAL: |
| + os << prop->GetDoubleValuePtr()[i]; |
| + break; |
| + default: |
| + LOG(ERROR) << "Unknown gesture property type: " << prop->type; |
| + NOTREACHED(); |
| + break; |
| + } |
| + os << ", "; |
| + } |
| + os << ")"; |
| + return os; |
| +} |
| + |
| +// 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"}; |
| + |
| +// Max number of devices that we track. |
| +const ui::GesturePropertyProvider::DeviceId kMaxDeviceNum = 0xffff; |
| + |
| +// Check if a device falls into one device type category. |
| +bool IsDeviceOfType(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(void* dev) { |
| + std::string proc_symlink = |
| + "/proc/self/fd/" + base::IntToString(static_cast<Evdev*>(dev)->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; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace ui { |
| +namespace internal { |
| + |
| +// 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; |
| + MatchCriteria(const std::string& arg); |
| + virtual ~MatchCriteria() {}; |
| + virtual bool Match(DevicePtr device) = 0; |
| + protected: |
| + std::vector<std::string> args_; |
| +}; |
| + |
| +// Match a device based on its evdev name string. |
| +class MatchProduct : public MatchCriteria { |
| + public: |
| + MatchProduct(const std::string& arg); |
| + virtual ~MatchProduct() {}; |
| + virtual bool Match(DevicePtr device); |
| +}; |
| + |
| +// Math a device based on its device node path. |
| +class MatchDevicePath : public MatchCriteria { |
| + public: |
| + MatchDevicePath(const std::string& arg); |
| + virtual ~MatchDevicePath() {}; |
| + virtual bool Match(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: |
| + MatchUSBID(const std::string& arg); |
| + virtual ~MatchUSBID() {}; |
| + virtual bool Match(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: |
| + MatchDeviceType(const std::string& arg); |
| + virtual ~MatchDeviceType() {}; |
| + virtual bool Match(DevicePtr device) = 0; |
| + protected: |
| + bool value_; |
| + bool is_valid_; |
| +}; |
| + |
| +// Check if a device is a pointer device. |
| +class MatchIsPointer : public MatchDeviceType { |
| + public: |
| + MatchIsPointer(const std::string& arg); |
| + virtual ~MatchIsPointer() {}; |
| + virtual bool Match(DevicePtr device); |
| +}; |
| + |
| +// Check if a device is a touchpad. |
| +class MatchIsTouchpad : public MatchDeviceType { |
| + public: |
| + MatchIsTouchpad(const std::string& arg); |
| + virtual ~MatchIsTouchpad() {}; |
| + virtual bool Match(DevicePtr device); |
| +}; |
| + |
| +// Check if a device is a touchscreen. |
| +class MatchIsTouchscreen : public MatchDeviceType { |
| + public: |
| + MatchIsTouchscreen(const std::string& arg); |
| + virtual ~MatchIsTouchscreen() {}; |
| + virtual bool Match(DevicePtr device); |
| +}; |
| + |
| +// Struct for sections in xorg conf files. |
| +struct ConfigurationSection { |
| + typedef ui::GesturePropertyProvider::DevicePtr DevicePtr; |
| + bool Match(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(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(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(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(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(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(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() : device_id_counter_(0) { |
| + LoadDeviceConfigurations(); |
| +} |
| + |
| +GesturePropertyProvider::~GesturePropertyProvider() { |
| +} |
| + |
| +void GesturePropertyProvider::GetDeviceIdsByType( |
| + const DeviceType type, |
| + std::vector<DeviceId>* device_ids) { |
| + device_ids->clear(); |
| + DeviceIdMap::iterator it = device_ids_map_.begin(); |
| + for (; it != device_ids_map_.end(); ++it) |
| + if (IsDeviceOfType(it->first, type)) |
| + device_ids->push_back(it->second); |
| +} |
| + |
| +GesturePropertyProvider::DeviceId GesturePropertyProvider::GetDeviceId( |
| + DevicePtr dev, |
| + const bool do_create) { |
| + DeviceIdMap::iterator it = device_ids_map_.find(dev); |
| + if (it != device_ids_map_.end()) |
| + return it->second; |
| + if (!do_create) |
| + return -1; |
| + |
| + // Insert a new one if not exists. |
| + // TODO(sheckylin): Replace this id generation scheme with a better one. |
| + // The current way may result in memory leaks. |
| + DeviceId id = device_id_counter_; |
| + device_ids_map_[dev] = id; |
| + properties_maps_.set(id, |
| + scoped_ptr<ScopedPropertyMap>(new ScopedPropertyMap)); |
| + device_id_counter_ = (device_id_counter_ + 1) % kMaxDeviceNum; |
| + |
| + // Apply default prop values for the device. |
| + SetupDefaultProperties(id, dev); |
| + return id; |
| +} |
| + |
| +GesturesProp* GesturePropertyProvider::PreGetProperty(const DeviceId device_id, |
| + const std::string& name, |
| + PropertyType* type, |
| + size_t* count) { |
| + // Return if no property of the name is found. |
| + GesturesProp* prop = FindProperty(device_id, name); |
| + if (!prop) |
| + return NULL; |
| + |
| + // Get the property values. |
| + if (type) |
| + *type = prop->type; |
| + if (count) |
| + *count = prop->count; |
| + return prop; |
| +} |
| + |
| +bool GesturePropertyProvider::GetPropertyType(const DeviceId device_id, |
| + const std::string& name, |
| + PropertyType* type) { |
| + GesturesProp* prop = PreGetProperty(device_id, name, type, NULL); |
| + return (prop != NULL); |
| +} |
| + |
| +bool GesturePropertyProvider::GetIntProperty(const DeviceId device_id, |
| + const std::string& name, |
| + int* val, |
| + size_t* count) { |
| + GesturesProp* prop = PreGetProperty(device_id, name, NULL, count); |
| + return prop ? prop->GetIntValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::GetShortProperty(const DeviceId device_id, |
| + const std::string& name, |
| + short* val, |
| + size_t* count) { |
| + GesturesProp* prop = PreGetProperty(device_id, name, NULL, count); |
| + return prop ? prop->GetShortValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::GetBoolProperty(const DeviceId device_id, |
| + const std::string& name, |
| + bool* val, |
| + size_t* count) { |
| + GesturesProp* prop = PreGetProperty(device_id, name, NULL, count); |
| + return prop ? prop->GetBoolValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::GetStringProperty(const DeviceId device_id, |
| + const std::string& name, |
| + std::string* val, |
| + size_t* count) { |
| + GesturesProp* prop = PreGetProperty(device_id, name, NULL, count); |
| + return prop ? prop->GetStringValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::GetDoubleProperty(const DeviceId device_id, |
| + const std::string& name, |
| + double* val, |
| + size_t* count) { |
| + GesturesProp* prop = PreGetProperty(device_id, name, NULL, count); |
| + return prop ? prop->GetDoubleValue(val) : false; |
| +} |
| + |
| +template <typename T> |
| +GesturesProp* GesturePropertyProvider::PreSetProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const T* val) { |
| + if (!val) |
| + return NULL; |
| + |
| + // Return if no property of the name is found. |
| + GesturesProp* prop = FindProperty(device_id, name); |
| + if (!prop) |
| + return NULL; |
| + |
| + // 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. |
| + if (prop->is_read_only) |
| + return NULL; |
| + return prop; |
| +} |
| + |
| +bool GesturePropertyProvider::SetIntProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const int* val) { |
| + GesturesProp* prop = PreSetProperty(device_id, name, val); |
| + return prop ? prop->SetIntValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::SetShortProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const short* val) { |
| + GesturesProp* prop = PreSetProperty(device_id, name, val); |
| + return prop ? prop->SetShortValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::SetBoolProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const bool* val) { |
| + GesturesProp* prop = PreSetProperty(device_id, name, val); |
| + return prop ? prop->SetBoolValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::SetStringProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const char* val) { |
| + if (!val) |
| + return false; |
| + std::string s = val; |
| + return SetStringProperty(device_id, name, &s); |
| +} |
| + |
| +bool GesturePropertyProvider::SetStringProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const std::string* val) { |
| + GesturesProp* prop = PreSetProperty(device_id, name, val); |
| + return prop ? prop->SetStringValue(val) : false; |
| +} |
| + |
| +bool GesturePropertyProvider::SetDoubleProperty(const DeviceId device_id, |
| + const std::string& name, |
| + const double* val) { |
| + GesturesProp* prop = PreSetProperty(device_id, name, val); |
| + return prop ? prop->SetDoubleValue(val) : false; |
| +} |
| + |
| +void GesturePropertyProvider::AddProperty(const DeviceId device_id, |
| + const std::string& name, |
| + GesturesProp* prop) { |
| + // 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. |
| + ScopedDeviceScopedPropertyMap::iterator it = properties_maps_.find(device_id); |
| + if (it != properties_maps_.end()) |
| + it->second->set(name, scoped_ptr<GesturesProp>(prop)); |
| +} |
| + |
| +void GesturePropertyProvider::DeleteProperty(const DeviceId device_id, |
| + const std::string& name) { |
| + ScopedDeviceScopedPropertyMap::iterator it = properties_maps_.find(device_id); |
| + if (it != properties_maps_.end()) |
| + it->second->erase(name); |
| +} |
| + |
| +GesturesProp* GesturePropertyProvider::FindProperty(const DeviceId device_id, |
| + const std::string& name) { |
| + ScopedDeviceScopedPropertyMap::const_iterator ia = |
| + properties_maps_.find(device_id); |
| + if (ia == properties_maps_.end()) |
| + return NULL; |
| + ScopedPropertyMap::const_iterator ib = ia->second->find(name); |
| + if (ib == ia->second->end()) |
| + return NULL; |
| + return ib->second; |
| +} |
| + |
| +GesturesProp* GesturePropertyProvider::GetDefaultProperty( |
| + const DeviceId device_id, |
| + const std::string& name) { |
| + ScopedDevicePropertyMap::const_iterator ia = |
| + default_properties_maps_.find(device_id); |
| + if (ia == default_properties_maps_.end()) |
| + return NULL; |
| + PropertyMap::const_iterator ib = ia->second->find(name); |
| + if (ib == ia->second->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", §ions); |
| + 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* prop = CreateDefaultProperty(option_name, arg); |
| + if(prop) |
| + config->properties.push_back(prop); |
| + 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* prop = CreateDefaultProperty(option_name, "on"); |
| + if (prop) |
| + config->properties.push_back(prop); |
| + } 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* prop = NULL; |
| + if (is_all_numeric && numbers.size()) { |
| + prop = new GesturesDoubleProp( |
| + name, numbers.size(), NULL, numbers.data(), NULL); |
| + } else { |
| + prop = new GesturesStringProp(name, NULL, value.c_str(), NULL); |
| + } |
| + |
| + GPROP_LOG(INFO_SEVERITY) << "Prop: " << *prop; |
| + // The function will always succeed for now but it may change later if we |
| + // specify some name or args as invalid. |
| + return prop; |
| +} |
| + |
| +void GesturePropertyProvider::SetupDefaultProperties( |
| + const DeviceId device_id, |
| + DevicePtr dev) { |
| + GPROP_LOG(INFO_SEVERITY) << "Setting up default properties for (" << dev |
| + << ", " << device_id << ", " << dev->info.name |
| + << ")"; |
| + |
| + // Go through all parsed sections. |
| + scoped_ptr<PropertyMap> prop_map(new PropertyMap); |
| + for (size_t i = 0; i < configurations_.size(); i++) { |
| + if (configurations_[i]->Match(dev)) { |
| + GPROP_LOG(INFO_SEVERITY) << "Conf section \"" |
| + << configurations_[i]->identifier |
| + << "\" is matched"; |
| + for (size_t j = 0; j < configurations_[i]->properties.size(); j++) { |
| + GesturesProp* prop = configurations_[i]->properties[j]; |
| + // We can't use insert here because a property may be set for several |
| + // times along the way. |
| + (*prop_map)[prop->name] = prop; |
| + } |
| + } |
| + } |
| + default_properties_maps_.set(device_id, prop_map.Pass()); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::PreCreateProperty(void* priv, |
| + const char* name) { |
| + GesturePropertyProvider* provider = GetPropertyProvider(priv); |
| + GesturePropertyProvider::DevicePtr dev = GetDevicePointer(priv); |
| + |
| + // Create a new PropertyMap for the device if not exists already. |
| + GesturePropertyProvider::DeviceId device_id = |
| + provider->GetDeviceId(dev, true); |
| + |
| + // First, see if the GesturesProp already exists. |
| + GPROP_LOG(3) << "Creating Property: \"" << name << "\""; |
| + GesturesProp* prop = 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 (prop) { |
| + LOG(WARNING) << "Gesture property \"" << name |
| + << "\" re-created. This shouldn't happen at the normal usage."; |
| + Free(dev, prop); |
| + } |
| + |
| + // Return the found default property from conf files (could be NULL). |
| + return provider->GetDefaultProperty(device_id, name); |
| +} |
| + |
| +void GesturesPropFunctionsWrapper::PostCreateProperty(void* priv, |
| + const char* name, |
| + GesturesProp* prop) { |
| + GesturePropertyProvider* provider = GetPropertyProvider(priv); |
| + GesturePropertyProvider::DevicePtr dev = GetDevicePointer(priv); |
| + |
| + // Add the property to the gesture property provider. The gesture property |
| + // provider will own it from now on. |
| + provider->AddProperty(provider->GetDeviceId(dev, false), name, prop); |
| + |
| + // Log the creation. |
| + GPROP_LOG(INFO_SEVERITY) << "Created active prop: " << *prop; |
| +} |
| + |
| +template <typename T, class PROPTYPE> |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateProperty(void* priv, |
|
alexst (slow to review)
2014/10/03 22:32:02
Please define parameter names in full without abbr
Shecky Lin
2014/10/08 10:51:00
Done.
|
| + const char* name, |
| + T* val, |
| + size_t count, |
| + const T* init) { |
| + // Create the property. Use the default property value if possible. |
| + GesturesProp* prop = |
| + new PROPTYPE(name, count, val, init, PreCreateProperty(priv, name)); |
| + |
| + PostCreateProperty(priv, name, prop); |
| + return prop; |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateInt(void* priv, |
| + const char* name, |
| + int* val, |
| + size_t count, |
| + const int* init) { |
| + return CreateProperty<int, GesturesIntProp>( |
| + priv, name, val, count, init); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateShort(void* priv, |
| + const char* name, |
| + short* val, |
| + size_t count, |
| + const short* init) { |
| + return CreateProperty<short, GesturesShortProp>( |
| + priv, name, val, count, init); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateBool( |
| + void* priv, |
| + const char* name, |
| + GesturesPropBool* val, |
| + size_t count, |
| + const GesturesPropBool* init) { |
| + return CreateProperty<GesturesPropBool, GesturesBoolProp>( |
| + priv, name, val, count, init); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateReal(void* priv, |
| + const char* name, |
| + double* val, |
| + size_t count, |
| + const double* init) { |
| + return CreateProperty<double, GesturesDoubleProp>( |
| + priv, name, val, count, init); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateString(void* priv, |
| + const char* name, |
| + const char** val, |
| + const char* init) { |
| + GesturesProp* prop = |
| + new GesturesStringProp(name, val, init, PreCreateProperty(priv, name)); |
| + |
| + PostCreateProperty(priv, name, prop); |
| + return prop; |
| +} |
| + |
| +void GesturesPropFunctionsWrapper::RegisterHandlers( |
| + void* priv, |
| + GesturesProp* prop, |
| + void* handler_data, |
| + GesturesPropGetHandler get, |
| + GesturesPropSetHandler set) { |
| + // Sanity checks |
| + if (!priv || !prop) |
| + return; |
| + |
| + prop->handler_data = handler_data; |
| + prop->get = get; |
| + prop->set = set; |
| +} |
| + |
| +void GesturesPropFunctionsWrapper::Free(void* priv, GesturesProp* prop) { |
| + if (!prop) |
| + return; |
| + |
| + GesturePropertyProvider* provider = GetPropertyProvider(priv); |
| + GesturePropertyProvider::DevicePtr dev = GetDevicePointer(priv); |
| + GesturePropertyProvider::DeviceId device_id = |
| + provider->GetDeviceId(dev, false); |
| + if (device_id < 0) |
| + return; |
| + |
| + // No need to manually delete the prop pointer as it is implicitly handled |
| + // with scoped ptr. |
| + GPROP_LOG(3) << "Freeing Property: \"" << prop->name << "\""; |
| + provider->DeleteProperty(device_id, prop->name); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateIntSingle( |
| + void* priv, |
| + const char* name, |
| + int* val, |
| + int init) { |
| + return CreateInt(priv, name, val, 1, &init); |
| +} |
| + |
| +GesturesProp* GesturesPropFunctionsWrapper::CreateBoolSingle( |
| + void* priv, |
| + const char* name, |
| + GesturesPropBool* val, |
| + GesturesPropBool init) { |
| + return CreateBool(priv, name, val, 1, &init); |
| +} |
| + |
| +GesturePropertyProvider* GesturesPropFunctionsWrapper::GetPropertyProvider( |
| + void* priv) { |
| + return static_cast<GestureInterpreterLibevdevCros*>(priv) |
| + ->GetPropertyProvider(); |
| +} |
| + |
| +GesturePropertyProvider::DevicePtr |
| +GesturesPropFunctionsWrapper::GetDevicePointer(void* priv) { |
| + return static_cast<GestureInterpreterLibevdevCros*>(priv)->GetDevicePointer(); |
| +} |
| + |
| +bool GesturesPropFunctionsWrapper::InitializeDeviceProperties( |
| + void* priv, |
| + GestureDeviceProperties* props) { |
| + if (!priv) |
| + return false; |
| + GesturePropertyProvider::DevicePtr dev = GetDevicePointer(priv); |
| + |
| + /* Create Device Properties */ |
| + |
| + // Read Only properties. |
| + CreateString(priv, "Device Node", NULL, GetDeviceNodePath(dev).c_str()); |
| + short vid = static_cast<short>(dev->info.id.vendor); |
| + CreateShort(priv, "Device Vendor ID", NULL, 1, &vid); |
| + short pid = static_cast<short>(dev->info.id.product); |
| + CreateShort(priv, "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( |
| + priv, "Active Area Left", &props->area_left, Event_Get_Left(dev)); |
| + CreateIntSingle( |
| + priv, "Active Area Right", &props->area_right, Event_Get_Right(dev)); |
| + CreateIntSingle( |
| + priv, "Active Area Top", &props->area_top, Event_Get_Top(dev)); |
| + CreateIntSingle( |
| + priv, "Active Area Bottom", &props->area_bottom, Event_Get_Bottom(dev)); |
| + |
| + // Trackpad resolution (pixels/mm). If not configured in .conf file, |
| + // use x/y resolution as reported by kernel driver. |
| + CreateIntSingle( |
| + priv, "Vertical Resolution", &props->res_y, Event_Get_Res_Y(dev)); |
| + CreateIntSingle( |
| + priv, "Horizontal Resolution", &props->res_x, Event_Get_Res_X(dev)); |
| + |
| + // Trackpad orientation minimum/maximum. If not configured in .conf file, |
| + // use min/max as reported by kernel driver. |
| + CreateIntSingle(priv, |
| + "Orientation Minimum", |
| + &props->orientation_minimum, |
| + Event_Get_Orientation_Minimum(dev)); |
| + CreateIntSingle(priv, |
| + "Orientation Maximum", |
| + &props->orientation_maximum, |
| + Event_Get_Orientation_Maximum(dev)); |
| + |
| + // Log dump property. Will call Event_Dump_Debug_Log when its value is being |
| + // set. |
| + GesturesProp* dump_debug_log_prop = |
| + CreateBoolSingle(priv, "Dump Debug Log", &props->dump_debug_log, false); |
| + RegisterHandlers(priv, dump_debug_log_prop, dev, NULL, Event_Dump_Debug_Log); |
| + |
| + // Whether to do the gesture recognition or just passing the multi-touch data |
| + // to upper layers. |
| + CreateBoolSingle( |
| + priv, "Raw Touch Passthrough", &props->raw_passthrough, false); |
| + return true; |
| +} |
| + |
| +/* 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 |