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", §ions); |
+ 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] = ∝ |
+ } |
+ } |
+ } |
+} |
+ |
+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 |