Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(272)

Unified Diff: ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc

Issue 545063006: ozone: evdev: Add gesture property provider (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add INFO_SEVERITY to log property activities. Useful for debugging before a property value setting … Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..b6edbe467bb904871e8e9f535781917a1bdbeaca
--- /dev/null
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
@@ -0,0 +1,1128 @@
+// 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 <algorithm>
+#include <iostream>
+#include <set>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/singleton.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/string_util.h"
+#include "base/strings/stringize_macros.h"
+#include "base/strings/stringprintf.h"
+
+// Macro for registering match criteria derived classes.
+#define REGISTER_MATCH_CRITERIA(NAME) \
+ match_criteria_map_[#NAME] = \
+ &ui::GesturePropertyProvider::AllocateMatchCriteria<NAME>
+
+// Macro for GesturesProp data pointer deallocators.
+#define REGISTER_GPROP_DEALLOCATOR(NAME, TYPE) \
+ InsertDeallocator<TYPE>(&result, ui::GesturePropertyProvider::NAME, is_array)
+
+// Severity level for general info logging purpose.
+#define INFO_SEVERITY INFO
+
+/* Implementation of GesturesProp declared in gestures.h
+ *
+ * libgestures requires that this be in the top level namespace.
+ * */
+struct GesturesProp {
+ GesturesProp()
+ : type(ui::GesturePropertyProvider::PT_INT),
+ count(1),
+ is_allocated(false),
+ is_managed_externally(true),
+ handler_data(NULL),
+ get(NULL),
+ set(NULL) {
+ val.v = NULL;
+ }
+ // Free the data pointer. Should never be called if the GesturesProp is
+ // created from the gesture lib side.
+ void Free() {
+ if (count > 1)
+ (*data_pointer_deallocator_map_[type])(val);
+ else
+ (*data_array_pointer_deallocator_map_[type])(val);
+ }
+
+ // For logging purpose.
+ friend std::ostream& operator<<(std::ostream& os, const GesturesProp& prop);
+
+ // Property name, type and number of elements.
+ std::string name;
+ ui::GesturePropertyProvider::PropertyType type;
+ size_t count;
+
+ // Data pointer.
+ union Data {
+ void* v;
+ int* i;
+ short* h;
+ GesturesPropBool* b;
+ std::string* s;
+ double* r;
+ } val;
+
+ // 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 (GesturesPropFunctionsWrapper::Free is called).
+ bool is_allocated;
+
+ // If the flag is off, it means the GesturesProp is created by passing a NULL
+ // data pointer to the creator functions. We will need to free the memory by
+ // ourselves when the GesturePropertyProvider is destroyed because no other
+ // people will be able to do it. For GesturesProp created with non-NULL
+ // pointers, external users are responsible of handling its data memory (via
+ // the GesturesPropProvider APIs).
+ //
+ // Note that the flag is different from is_allocated because the string
+ // properties are always allocated here whether it is created with NULL or
+ // not. In short, if this flag is false, then is_allocated must be true.
+ bool is_managed_externally;
+
+ // Handler function pointers and the data to be passed to them when the
+ // property is accessed.
+ void* handler_data;
+ GesturesPropGetHandler get;
+ GesturesPropSetHandler set;
+ private:
+ // Trick for appropriately deallocating the data memory based on its type.
+ typedef std::map<ui::GesturePropertyProvider::PropertyType,
+ void (*)(GesturesProp::Data&)> DeallocatorMap;
+ template <typename T>
+ static void DataPointerDeallocator(Data& data) {
+ delete reinterpret_cast<T>(data.v);
+ }
+ template <typename T>
+ static void DataArrayPointerDeallocator(Data& data) {
+ delete[] reinterpret_cast<T>(data.v);
+ }
+ template <typename T>
+ static void InsertDeallocator(DeallocatorMap* deallocator_map,
+ ui::GesturePropertyProvider::PropertyType type,
+ const bool is_array) {
+ if (is_array) {
+ deallocator_map->insert(
+ std::make_pair(type, &DataArrayPointerDeallocator<T*>));
+ } else {
+ deallocator_map->insert(
+ std::make_pair(type, &DataPointerDeallocator<T*>));
+ }
+ }
+
+ // Populate deallocators in the deallocator map.
+ static DeallocatorMap CreateDataPointerDeallocatorMap(const bool is_array) {
+ DeallocatorMap result;
+ REGISTER_GPROP_DEALLOCATOR(PT_INT, int);
+ REGISTER_GPROP_DEALLOCATOR(PT_SHORT, short);
+ REGISTER_GPROP_DEALLOCATOR(PT_BOOL, GesturesPropBool);
+ REGISTER_GPROP_DEALLOCATOR(PT_STRING, std::string);
+ REGISTER_GPROP_DEALLOCATOR(PT_REAL, double);
+ return result;
+ }
+
+ // These deallocator maps store deallocators for different data types.
+ static DeallocatorMap data_pointer_deallocator_map_;
+ static DeallocatorMap data_array_pointer_deallocator_map_;
+};
+
+// Trick for initializing static map members.
+GesturesProp::DeallocatorMap GesturesProp::data_pointer_deallocator_map_(
+ GesturesProp::CreateDataPointerDeallocatorMap(false));
+GesturesProp::DeallocatorMap GesturesProp::data_array_pointer_deallocator_map_(
+ GesturesProp::CreateDataPointerDeallocatorMap(true));
+
+// 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:
+ s = "Unknown type";
+ break;
+ }
+#undef TYPE_CASE
+ return out << s;
+}
+
+// GesturesProp logging function.
+std::ostream& operator<<(std::ostream& os, const GesturesProp& prop) {
+ os << "\"" << prop.name << "\", " << prop.type << ", " << prop.count << ", ("
+ << prop.is_allocated << ", " << prop.is_managed_externally << "), (";
+ bool known_prop_type = true;
+ for (size_t i = 0; (i < prop.count) && known_prop_type; i++) {
+ switch (prop.type) {
+ case ui::GesturePropertyProvider::PT_INT:
+ os << prop.val.i[i];
+ break;
+ case ui::GesturePropertyProvider::PT_SHORT:
+ os << prop.val.h[i];
+ break;
+ case ui::GesturePropertyProvider::PT_BOOL:
+ // Prevent the value being printed as characters.
+ os << static_cast<int>(prop.val.b[i]);
+ break;
+ case ui::GesturePropertyProvider::PT_STRING:
+ os << prop.val.s[i];
+ break;
+ case ui::GesturePropertyProvider::PT_REAL:
+ os << prop.val.r[i];
+ break;
+ default:
+ LOG(ERROR) << "Unknown gesture property type: " << prop.type;
+ known_prop_type = false;
+ break;
+ }
+ os << ", ";
+ }
+ os << ")";
+ return os;
+}
+
+namespace ui {
+
+// 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"};
+
+// 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();
+}
+
+GesturePropertyProvider* GesturePropertyProvider::GetInstance() {
+ return Singleton<GesturePropertyProvider>::get();
spang 2014/09/18 18:06:15 Instead of making it a singleton, please create th
+}
+
+GesturePropertyProvider::GesturePropertyProvider() : device_id_counter_(0) {
+ RegisterMatchCriterias();
+ LoadDeviceConfigurations();
+}
+
+GesturePropertyProvider::~GesturePropertyProvider() {
+ // Note that we don't need to free most GesturesProp data pointers - they are
+ // managed externally through the GesturesPropProvider APIs. However, some
+ // props are created by directly calling the APIs with NULL pointers so they
+ // still need to be handled here.
+ DevicePropertyMap::iterator it = properties_maps_.begin();
+ for (; it != properties_maps_.end(); ++it) {
+ PropertyMap::iterator ip = it->second->begin();
+ for (; ip != it->second->end(); ++ip)
+ if (!ip->second->is_managed_externally)
+ ip->second->Free();
+ delete it->second;
+ }
+
+ // We do need to free the data pointers for props in configurations_ as they
+ // are maintained by ourselves. The props in default_properties_maps_ are all
+ // referenced from configurations_ so they are OK.
+ for (size_t i = 0; i < configurations_.size(); i++) {
+ for (size_t j = 0; j < configurations_[i].criterias.size(); j++)
+ delete configurations_[i].criterias[j];
+ for (size_t j = 0; j < configurations_[i].properties.size(); j++)
+ configurations_[i].properties[j].Free();
+ }
+ DevicePropertyMap::iterator ip = default_properties_maps_.begin();
+ for (; ip != default_properties_maps_.end(); ++ip)
+ delete ip->second;
+}
+
+void GesturePropertyProvider::RegisterMatchCriterias() {
+ // TODO(sheckylin): Add a check here to make sure all match criterias in
+ // kSupportedMatchTypes is registered here. Otherwise, let the derived
+ // classess register themselves instead.
+ REGISTER_MATCH_CRITERIA(MatchProduct);
+ REGISTER_MATCH_CRITERIA(MatchDevicePath);
+ REGISTER_MATCH_CRITERIA(MatchUSBID);
+ REGISTER_MATCH_CRITERIA(MatchIsPointer);
+ REGISTER_MATCH_CRITERIA(MatchIsTouchpad);
+ REGISTER_MATCH_CRITERIA(MatchIsTouchscreen);
+}
+
+void GesturePropertyProvider::GetDeviceIndices(
+ 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) {
+ // Check if the device matches the type.
+ it->first;
+ 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_[id] = new PropertyMap;
+ device_id_counter_ = (device_id_counter_ + 1) % kMaxDeviceNum;
+
+ // Apply default prop values for the device.
+ SetupDefaultProperties(id, dev);
+ return id;
+}
+
+void GesturePropertyProvider::GetProperty(const DeviceId device_id,
+ const std::string& name,
+ DeviceType* type,
+ PropertyValuePtr* val,
+ size_t* count) {
+ // Implement PropertyGet in cmt.
+ NOTIMPLEMENTED();
+}
+
+void GesturePropertyProvider::SetProperty(const DeviceId device_id,
+ const std::string& name,
+ const DeviceType type,
+ const PropertyValuePtr val,
+ const size_t count) {
+ // Implement PropertySet in cmt.
+ NOTIMPLEMENTED();
+}
+
+GesturePropertyProvider::MatchCriteria::MatchCriteria(const std::string& arg) {
+ // TODO(sheckylin): Should we trim all tokens here?
+ Tokenize(arg, "|", &args_);
+}
+
+GesturePropertyProvider::MatchProduct::MatchProduct(const std::string& arg)
+ : MatchCriteria(arg) {
+}
+
+bool GesturePropertyProvider::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;
+}
+
+GesturePropertyProvider::MatchDevicePath::MatchDevicePath(
+ const std::string& arg)
+ : MatchCriteria(arg) {
+}
+
+bool GesturePropertyProvider::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;
+}
+
+GesturePropertyProvider::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]))
+ 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]));
+ }
+}
+
+bool GesturePropertyProvider::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 GesturePropertyProvider::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);
+}
+
+GesturePropertyProvider::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;
+ }
+}
+
+GesturePropertyProvider::MatchIsPointer::MatchIsPointer(const std::string& arg)
+ : MatchDeviceType(arg) {
+}
+
+bool GesturePropertyProvider::MatchIsPointer::Match(DevicePtr device) {
+ if (!is_valid_)
+ return true;
+ return (value_ == (device->info.evdev_class == EvdevClassMouse ||
+ device->info.evdev_class == EvdevClassMultitouchMouse));
+}
+
+GesturePropertyProvider::MatchIsTouchpad::MatchIsTouchpad(
+ const std::string& arg)
+ : MatchDeviceType(arg) {
+}
+
+bool GesturePropertyProvider::MatchIsTouchpad::Match(DevicePtr device) {
+ if (!is_valid_)
+ return true;
+ return (value_ == (device->info.evdev_class == EvdevClassTouchpad));
+}
+
+GesturePropertyProvider::MatchIsTouchscreen::MatchIsTouchscreen(
+ const std::string& arg)
+ : MatchDeviceType(arg) {
+}
+
+bool GesturePropertyProvider::MatchIsTouchscreen::Match(DevicePtr device) {
+ if (!is_valid_)
+ return true;
+ return (value_ == (device->info.evdev_class == EvdevClassTouchscreen));
+}
+
+bool GesturePropertyProvider::ConfigurationSection::Match(DevicePtr device) {
+ for (size_t i = 0; i < criterias.size(); ++i)
+ if (!criterias[i]->Match(device))
+ return false;
+ return true;
+}
+
+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.
+ DevicePropertyMap::iterator it = properties_maps_.find(device_id);
+ if (it != properties_maps_.end())
+ it->second->insert(std::make_pair(name, prop));
+}
+
+void GesturePropertyProvider::DeleteProperty(const DeviceId device_id,
+ const std::string& name) {
+ DevicePropertyMap::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) {
+ return FindProperty(properties_maps_, device_id, name);
+}
+
+GesturesProp* GesturePropertyProvider::FindProperty(
+ const DevicePropertyMap& device_property_map,
+ const DeviceId device_id,
+ const std::string& name) {
+ DevicePropertyMap::const_iterator ia = device_property_map.find(device_id);
+ if (ia == properties_maps_.end())
+ return NULL;
+ PropertyMap::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) {
+ return FindProperty(default_properties_maps_, device_id, name);
+}
+
+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);
+ }
+ 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) {
+ 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(ConfigurationSection());
+ 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 {
+ LOG(INFO_SEVERITY) << "New InputClass section found";
+ has_checked_section_type = true;
+ }
+ break;
+ } else if (next_is_identifier) {
+ 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;
+ if(CreateDefaultProperty(option_name, arg, &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)) {
+ 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;
+ if (CreateDefaultProperty(option_name, "on", &prop))
+ config.properties.push_back(prop);
+ } else if (IsMatchTypeSupported(match_type) &&
+ IsDeviceMatchType(match_type)) {
+ 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();
+ }
+ }
+}
+
+bool GesturePropertyProvider::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;
+}
+
+bool GesturePropertyProvider::IsDeviceMatchType(
+ const std::string& match_type) {
+ return StartsWithASCII(match_type, "MatchIs", true);
+}
+
+GesturePropertyProvider::MatchCriteria*
+GesturePropertyProvider::CreateMatchCriteria(const std::string& match_type,
+ const std::string& arg) {
+ LOG(INFO_SEVERITY) << "Creating match criteria: (" << match_type << ", "
+ << arg << ")";
+ return (this->*match_criteria_map_[match_type])(arg);
+}
+
+bool GesturePropertyProvider::CreateDefaultProperty(const std::string& name,
+ const std::string& value,
+ GesturesProp* prop) {
+ // 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.
+ 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);
+ }
+
+ // Setup GesturesProp data.
+ prop->name = name;
+ // Arrays need to contain at least one number and may contain numbers only.
+ if (is_all_numeric && numbers.size()) {
+ prop->type = PT_REAL;
+ prop->count = numbers.size();
+ prop->val.r = new double[numbers.size()];
+ std::copy(numbers.begin(), numbers.end(), prop->val.r);
+ } else {
+ prop->type = PT_STRING;
+ prop->count = 1;
+ std::string trimmed;
+ base::TrimWhitespaceASCII(value, base::TRIM_ALL, &trimmed);
+ prop->val.s = new std::string(trimmed);
+ }
+
+ 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 true;
+}
+
+int GesturePropertyProvider::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;
+}
+
+void GesturePropertyProvider::SetupDefaultProperties(
+ const DeviceId device_id,
+ DevicePtr dev) {
+ LOG(INFO_SEVERITY) << "Setting up default properties for (" << dev << ", "
+ << device_id << ", " << dev->info.name << ")";
+ PropertyMap* prop_map = new PropertyMap;
+ default_properties_maps_[device_id] = prop_map;
+
+ // Go through all parsed sections.
+ for (size_t i = 0; i < configurations_.size(); i++) {
+ if (configurations_[i].Match(dev)) {
+ 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;
+ }
+ }
+ }
+}
+
+template <typename T>
+GesturesProp* GesturesPropFunctionsWrapper::Create(
+ void* dev,
+ const char* name,
+ GesturePropertyProvider::PropertyType type,
+ T* val,
+ size_t count) {
+ // Create a new PropertyMap for the device if not exists already.
+ GesturePropertyProvider::DeviceId device_id =
+ GesturePropertyProvider::GetInstance()->GetDeviceId(dev, true);
+
+ /* Insert property and setup property values */
+
+ // First, see if the GesturesProp already exists.
+ DVLOG(3) << "Creating Property: \"" << name << "\"";
+ GesturesProp* prop =
+ GesturePropertyProvider::GetInstance()->FindProperty(device_id, name);
+ if (!prop) {
+ prop = new GesturesProp();
+ GesturePropertyProvider::GetInstance()->AddProperty(device_id, name, prop);
+ } else if (prop->is_allocated) {
+ // Free the data pointer if it was allocated here. This shouldn't happen in
+ // normal use cases (that a read only property being declared again).
+ // However, we want to prevent any possible memory leak if it does happen.
+ prop->Free();
+ }
+
+ // Set the values.
+ prop->name = name;
+ prop->type = type;
+ prop->count = count;
+
+ // Allocate memory for the data if a NULL pointer is provided.
+ if (!val) {
+ if (count > 1)
+ prop->val.v = new T[count];
+ else
+ prop->val.v = new T;
+ prop->is_allocated = true;
+ } else {
+ prop->val.v = val;
+ }
+ return prop;
+}
+
+template <typename T, GesturePropertyProvider::PropertyType type>
+GesturesProp* GesturesPropFunctionsWrapper::CreateProperty(void* dev,
+ const char* name,
+ T* val,
+ size_t count,
+ const T* init) {
+ GesturesProp* result = Create(dev, name, type, val, count);
+ if (!val)
+ result->is_managed_externally = false;
+
+ // 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.
+ if (count == 1) {
+ GesturesProp* default_prop =
+ GesturePropertyProvider::GetInstance()->GetDefaultProperty(
+ GesturePropertyProvider::GetInstance()->GetDeviceId(dev), name);
+ if (!default_prop ||
+ default_prop->type == GesturePropertyProvider::PT_STRING) {
+ *(reinterpret_cast<T*>(result->val.v)) = *init;
+ } else {
+ LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
+ // TODO(sheckylin): Handle value out-of-range (e.g., double to int).
+ *(reinterpret_cast<T*>(result->val.v)) =
+ static_cast<T>(*(default_prop->val.r));
+ }
+ }
+
+ LOG(INFO_SEVERITY) << "Created active prop: " << *result;
+ return result;
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateInt(void* dev,
+ const char* name,
+ int* val,
+ size_t count,
+ const int* init) {
+ return CreateProperty<int, GesturePropertyProvider::PT_INT>(
+ dev, name, val, count, init);
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateShort(void* dev,
+ const char* name,
+ short* val,
+ size_t count,
+ const short* init) {
+ return CreateProperty<short, GesturePropertyProvider::PT_SHORT>(
+ dev, name, val, count, init);
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateBool(
+ void* dev,
+ const char* name,
+ GesturesPropBool* val,
+ size_t count,
+ const GesturesPropBool* init) {
+ return CreateProperty<GesturesPropBool, GesturePropertyProvider::PT_BOOL>(
+ dev, name, val, count, init);
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateReal(void* dev,
+ const char* name,
+ double* val,
+ size_t count,
+ const double* init) {
+ return CreateProperty<double, GesturePropertyProvider::PT_REAL>(
+ dev, name, val, count, init);
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateString(void* dev,
+ const char* name,
+ const char** val,
+ const char* init) {
+ // 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 Create function with NULL pointer so that it always
+ // allocates.
+ GesturesProp* result = Create(dev,
+ name,
+ GesturePropertyProvider::PT_STRING,
+ static_cast<std::string*>(NULL),
+ 1);
+ if (!val)
+ result->is_managed_externally = false;
+
+ GesturesProp* default_prop =
+ GesturePropertyProvider::GetInstance()->GetDefaultProperty(
+ GesturePropertyProvider::GetInstance()->GetDeviceId(dev), name);
+
+ // Setup its value just like the other data types.
+ if (!default_prop ||
+ default_prop->type != GesturePropertyProvider::PT_STRING) {
+ *(result->val.s) = init;
+ } else {
+ LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
+ *(result->val.s) = *(default_prop->val.s);
+ }
+
+ // 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 (val)
+ *(val) = result->val.s->c_str();
+
+ LOG(INFO_SEVERITY) << "Created active prop: " << *result;
+ return result;
+}
+
+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::DeviceId device_id =
+ GesturePropertyProvider::GetInstance()->GetDeviceId(priv);
+ if (device_id < 0)
+ return;
+
+ DVLOG(3) << "Freeing Property: \"" << prop->name << "\"";
+ GesturePropertyProvider::GetInstance()->DeleteProperty(device_id, prop->name);
+ if (prop->is_allocated)
+ prop->Free();
+ delete prop;
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateIntSingle(
+ void* dev,
+ const char* name,
+ int* val,
+ int init) {
+ return CreateInt(dev, name, val, 1, &init);
+}
+
+GesturesProp* GesturesPropFunctionsWrapper::CreateBoolSingle(
+ void* dev,
+ const char* name,
+ GesturesPropBool* val,
+ GesturesPropBool init) {
+ return CreateBool(dev, name, val, 1, &init);
+}
+
+bool GesturesPropFunctionsWrapper::InitializeDeviceProperties(
+ void* device, GesturePropertyProvider::DeviceProperty* props) {
+ if (!device)
+ return false;
+ GesturePropertyProvider::DevicePtr dev =
+ static_cast<GesturePropertyProvider::DevicePtr>(device);
+
+ /* Create Device Properties */
+
+ // Read Only properties.
+ CreateString(dev, "Device Node", NULL, GetDeviceNodePath(dev).c_str());
+ CreateShort(dev,
+ "Device Vendor ID",
+ NULL,
+ 1,
+ reinterpret_cast<short*>(&(dev->info.id.vendor)));
+ CreateShort(dev,
+ "Device Product ID",
+ NULL,
+ 1,
+ reinterpret_cast<short*>(&(dev->info.id.product)));
+
+ // Useable trackpad area. If not configured in .conf file,
+ // use x/y valuator min/max as reported by kernel driver.
+ CreateIntSingle(
+ dev, "Active Area Left", &props->area_left, Event_Get_Left(dev));
+ CreateIntSingle(
+ dev, "Active Area Right", &props->area_right, Event_Get_Right(dev));
+ CreateIntSingle(dev, "Active Area Top", &props->area_top, Event_Get_Top(dev));
+ CreateIntSingle(
+ dev, "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(
+ dev, "Vertical Resolution", &props->res_y, Event_Get_Res_Y(dev));
+ CreateIntSingle(
+ dev, "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(dev,
+ "Orientation Minimum",
+ &props->orientation_minimum,
+ Event_Get_Orientation_Minimum(dev));
+ CreateIntSingle(dev,
+ "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(dev, "Dump Debug Log", &props->dump_debug_log, false);
+ RegisterHandlers(dev, 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(
+ dev, "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

Powered by Google App Engine
This is Rietveld 408576698