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

Side by Side 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: Use ScopedPtrHashMap. Fix bugs with string properties and numerical array properties created with N… 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h"
6
7 #include <gestures/gestures.h>
8 #include <libevdev/libevdev.h>
9
10 #include <fnmatch.h>
11 #include <string.h>
12
13 #include <algorithm>
14 #include <iostream>
15 #include <set>
16
17 #include "base/files/file_enumerator.h"
18 #include "base/files/file_util.h"
19 #include "base/logging.h"
20 #include "base/memory/singleton.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_tokenizer.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringize_macros.h"
27 #include "base/strings/stringprintf.h"
28
29 // Severity level for general info logging purpose.
30 #define INFO_SEVERITY INFO
spang 2014/09/18 21:23:16 Please don't rename this. You shouldn't use LOG(IN
Shecky Lin 2014/09/19 09:31:08 Done.
31
32 /* Implementation of GesturesProp declared in gestures.h
33 *
34 * libgestures requires that this be in the top level namespace.
35 * */
36 struct GesturesProp {
37 GesturesProp()
38 : type(ui::GesturePropertyProvider::PT_INT),
39 count(1),
40 is_allocated(false),
41 is_managed_externally(true),
42 write_back(NULL),
43 handler_data(NULL),
44 get(NULL),
45 set(NULL) {
46 val.v = NULL;
47 }
48 // Free the data pointer. Should never be called if the GesturesProp is
49 // created from the gesture lib side.
50 void Free() {
51 if (count > 1)
52 (*data_pointer_deallocator_map_[type])(val);
53 else
54 (*data_array_pointer_deallocator_map_[type])(val);
55 }
56
57 // For logging purpose.
58 friend std::ostream& operator<<(std::ostream& os, const GesturesProp& prop);
59
60 // Property name, type and number of elements.
61 std::string name;
62 ui::GesturePropertyProvider::PropertyType type;
63 size_t count;
64
65 // Data pointer.
66 union Data {
67 void* v;
68 int* i;
69 short* h;
70 GesturesPropBool* b;
71 std::string* s;
72 double* r;
73 } val;
74
75 // If the flag is on, it means the memory that the data pointer points to is
76 // allocated here. We will need to free the memory by ourselves when the
77 // GesturesProp is destroyed (GesturesPropFunctionsWrapper::Free is called).
78 bool is_allocated;
79
80 // If the flag is off, it means the GesturesProp is created by passing a NULL
81 // data pointer to the creator functions. We will need to free the memory by
82 // ourselves when the GesturePropertyProvider is destroyed because no other
83 // people will be able to do it. For GesturesProp created with non-NULL
84 // pointers, external users are responsible of handling its data memory (via
85 // the GesturesPropProvider APIs).
86 //
87 // Note that the flag is different from is_allocated because the string
88 // properties are always allocated here whether it is created with NULL or
89 // not. In short, if this flag is false, then is_allocated must be true.
90 bool is_managed_externally;
91
92 // In some cases, we don't directly use the data pointer provided by the
93 // creators due to its limitation and instead use our own types (e.g., in
94 // the case of string). We thus need to store the write back pointer so that
95 // we can update the value in the gesture lib if the property value gets
96 // changed.
97 void* write_back;
98
99 // Handler function pointers and the data to be passed to them when the
100 // property is accessed.
101 void* handler_data;
102 GesturesPropGetHandler get;
103 GesturesPropSetHandler set;
104 private:
105 // Trick for appropriately deallocating the data memory based on its type.
106 typedef std::map<ui::GesturePropertyProvider::PropertyType,
107 void (*)(GesturesProp::Data&)> DeallocatorMap;
108 template <typename T>
109 static void DataPointerDeallocator(Data& data) {
110 delete reinterpret_cast<T>(data.v);
111 }
112 template <typename T>
113 static void DataArrayPointerDeallocator(Data& data) {
114 delete[] reinterpret_cast<T>(data.v);
115 }
116 template <typename T>
117 static void InsertDeallocator(DeallocatorMap* deallocator_map,
118 ui::GesturePropertyProvider::PropertyType type,
119 const bool is_array) {
120 if (is_array) {
121 deallocator_map->insert(
122 std::make_pair(type, &DataArrayPointerDeallocator<T*>));
123 } else {
124 deallocator_map->insert(
125 std::make_pair(type, &DataPointerDeallocator<T*>));
126 }
127 }
128
129 // Populate deallocators in the deallocator map.
130 static DeallocatorMap CreateDataPointerDeallocatorMap(const bool is_array) {
131 DeallocatorMap result;
132 // Macro for adding GesturesProp data pointer deallocators.
133 #define REGISTER_GPROP_DEALLOCATOR(NAME, TYPE) \
134 InsertDeallocator<TYPE>(&result, ui::GesturePropertyProvider::NAME, is_array)
135 REGISTER_GPROP_DEALLOCATOR(PT_INT, int);
136 REGISTER_GPROP_DEALLOCATOR(PT_SHORT, short);
137 REGISTER_GPROP_DEALLOCATOR(PT_BOOL, GesturesPropBool);
138 REGISTER_GPROP_DEALLOCATOR(PT_STRING, std::string);
139 REGISTER_GPROP_DEALLOCATOR(PT_REAL, double);
140 #undef REGISTER_GPROP_DEALLOCATOR
141 return result;
142 }
143
144 // These deallocator maps store deallocators for different data types.
145 static DeallocatorMap data_pointer_deallocator_map_;
spang 2014/09/18 21:23:16 static objects with nontrivial initialization aren
Shecky Lin 2014/09/19 09:31:08 Done. Thanks for the great suggestion.
146 static DeallocatorMap data_array_pointer_deallocator_map_;
147 };
148
149 // Trick for initializing static map members.
150 GesturesProp::DeallocatorMap GesturesProp::data_pointer_deallocator_map_(
151 GesturesProp::CreateDataPointerDeallocatorMap(false));
152 GesturesProp::DeallocatorMap GesturesProp::data_array_pointer_deallocator_map_(
153 GesturesProp::CreateDataPointerDeallocatorMap(true));
154
155 // Property type logging function.
156 std::ostream& operator<<(std::ostream& out,
157 const ui::GesturePropertyProvider::PropertyType type) {
158 std::string s;
159 #define TYPE_CASE(TYPE) \
160 case (ui::GesturePropertyProvider::TYPE): \
161 s = #TYPE; \
162 break;
163 switch (type) {
164 TYPE_CASE(PT_INT);
165 TYPE_CASE(PT_SHORT);
166 TYPE_CASE(PT_BOOL);
167 TYPE_CASE(PT_STRING);
168 TYPE_CASE(PT_REAL);
169 default:
170 s = "Unknown type";
171 break;
172 }
173 #undef TYPE_CASE
174 return out << s;
175 }
176
177 // GesturesProp logging function.
178 std::ostream& operator<<(std::ostream& os, const GesturesProp& prop) {
179 os << "\"" << prop.name << "\", " << prop.type << ", " << prop.count << ", ("
180 << prop.is_allocated << ", " << prop.is_managed_externally << "), "
181 << prop.write_back << ", (";
182 bool known_prop_type = true;
183 for (size_t i = 0; (i < prop.count) && known_prop_type; i++) {
184 switch (prop.type) {
185 case ui::GesturePropertyProvider::PT_INT:
186 os << prop.val.i[i];
187 break;
188 case ui::GesturePropertyProvider::PT_SHORT:
189 os << prop.val.h[i];
190 break;
191 case ui::GesturePropertyProvider::PT_BOOL:
192 // Prevent the value being printed as characters.
193 os << static_cast<int>(prop.val.b[i]);
194 break;
195 case ui::GesturePropertyProvider::PT_STRING:
196 os << prop.val.s[i];
197 break;
198 case ui::GesturePropertyProvider::PT_REAL:
199 os << prop.val.r[i];
200 break;
201 default:
202 LOG(ERROR) << "Unknown gesture property type: " << prop.type;
203 known_prop_type = false;
204 break;
205 }
206 os << ", ";
207 }
208 os << ")";
209 return os;
210 }
211
212 namespace ui {
213
214 // The path that we will look for conf files.
215 const char kConfigurationFilePath[] = "/etc/gesture";
216
217 // We support only match types that have already been used. One should change
218 // this if we start using new types in the future. Note that most unsupported
219 // match types are either useless in CrOS or inapplicable to the non-X
220 // environment.
221 const char* kSupportedMatchTypes[] = {"MatchProduct",
222 "MatchDevicePath",
223 "MatchUSBID",
224 "MatchIsPointer",
225 "MatchIsTouchpad",
226 "MatchIsTouchscreen"};
227 const char* kUnsupportedMatchTypes[] = {"MatchVendor",
228 "MatchOS",
229 "MatchPnPID",
230 "MatchDriver",
231 "MatchTag",
232 "MatchLayout",
233 "MatchIsKeyboard",
234 "MatchIsJoystick",
235 "MatchIsTablet"};
236
237 // Special keywords for boolean values.
238 const char* kTrue[] = {"on", "true", "yes"};
239 const char* kFalse[] = {"off", "false", "no"};
240
241 // Trick to get the device path from a file descriptor.
242 std::string GetDeviceNodePath(void* dev) {
243 std::string proc_symlink =
244 "/proc/self/fd/" + base::IntToString(static_cast<Evdev*>(dev)->fd);
245 base::FilePath path;
246 if (!base::ReadSymbolicLink(base::FilePath(proc_symlink), &path))
247 return std::string();
248 return path.value();
249 }
250
251 GesturePropertyProvider* GesturePropertyProvider::GetInstance() {
252 return Singleton<GesturePropertyProvider>::get();
253 }
254
255 GesturePropertyProvider::GesturePropertyProvider() : device_id_counter_(0) {
256 RegisterMatchCriterias();
257 LoadDeviceConfigurations();
258 }
259
260 GesturePropertyProvider::~GesturePropertyProvider() {
261 // Note that we don't need to free most GesturesProp data pointers - they are
262 // managed externally through the GesturesPropProvider APIs. However, some
263 // props are created by directly calling the APIs with NULL pointers so they
264 // still need to be handled here.
265 DevicePropertyMap::iterator it = properties_maps_.begin();
266 for (; it != properties_maps_.end(); ++it) {
267 PropertyMap::iterator ip = it->second->begin();
268 for (; ip != it->second->end(); ++ip)
269 if (!ip->second->is_managed_externally)
270 ip->second->Free();
271 }
272
273 // We do need to free the data pointers for props in configurations_ as they
274 // are maintained by ourselves. The props in default_properties_maps_ are all
275 // referenced from configurations_ so they are OK.
276 for (size_t i = 0; i < configurations_.size(); i++) {
277 for (size_t j = 0; j < configurations_[i].criterias.size(); j++)
278 delete configurations_[i].criterias[j];
alexst (slow to review) 2014/09/18 16:13:29 Thank you for the scoped_ptr_hash map change. Can
Shecky Lin 2014/09/19 09:31:08 Yeah, I am still looking into this. Will do it at
279 for (size_t j = 0; j < configurations_[i].properties.size(); j++)
280 configurations_[i].properties[j].Free();
281 }
282 }
283
284 void GesturePropertyProvider::RegisterMatchCriterias() {
285 // Macro for registering match criteria derived classes.
286 #define REGISTER_MATCH_CRITERIA(NAME) \
287 match_criteria_map_[#NAME] = \
288 &ui::GesturePropertyProvider::AllocateMatchCriteria<NAME>
289 REGISTER_MATCH_CRITERIA(MatchProduct);
290 REGISTER_MATCH_CRITERIA(MatchDevicePath);
291 REGISTER_MATCH_CRITERIA(MatchUSBID);
292 REGISTER_MATCH_CRITERIA(MatchIsPointer);
293 REGISTER_MATCH_CRITERIA(MatchIsTouchpad);
294 REGISTER_MATCH_CRITERIA(MatchIsTouchscreen);
295 #undef REGISTER_MATCH_CRITERIA
296 // TODO(sheckylin): Add a check here to make sure all match criterias in
297 // kSupportedMatchTypes is registered here. Otherwise, let the derived
298 // classess register themselves instead.
299 }
300
301 void GesturePropertyProvider::GetDeviceIndices(
302 const DeviceType type,
303 std::vector<DeviceId>* device_ids) {
304 device_ids->clear();
305 DeviceIdMap::iterator it = device_ids_map_.begin();
306 for (; it != device_ids_map_.end(); ++it) {
307 // Check if the device matches the type.
308 it->first;
309 device_ids->push_back(it->second);
310 }
311 }
312
313 GesturePropertyProvider::DeviceId GesturePropertyProvider::GetDeviceId(
314 DevicePtr dev,
315 const bool do_create) {
316 DeviceIdMap::iterator it = device_ids_map_.find(dev);
317 if (it != device_ids_map_.end())
318 return it->second;
319 if (!do_create)
320 return -1;
321
322 // Insert a new one if not exists.
323 // TODO(sheckylin): Replace this id generation scheme with a better one.
324 // The current way may result in memory leaks.
325 DeviceId id = device_id_counter_;
326 device_ids_map_[dev] = id;
327 properties_maps_.set(id, scoped_ptr<PropertyMap>(new PropertyMap));
328 device_id_counter_ = (device_id_counter_ + 1) % kMaxDeviceNum;
329
330 // Apply default prop values for the device.
331 SetupDefaultProperties(id, dev);
332 return id;
333 }
334
335 void GesturePropertyProvider::GetProperty(const DeviceId device_id,
336 const std::string& name,
337 DeviceType* type,
338 PropertyValuePtr* val,
339 size_t* count) {
340 // Implement PropertyGet in cmt.
341 NOTIMPLEMENTED();
342 }
343
344 void GesturePropertyProvider::SetProperty(const DeviceId device_id,
345 const std::string& name,
346 const DeviceType type,
347 const PropertyValuePtr val,
348 const size_t count) {
349 // Implement PropertySet in cmt.
350 NOTIMPLEMENTED();
351 }
352
353 GesturePropertyProvider::MatchCriteria::MatchCriteria(const std::string& arg) {
354 // TODO(sheckylin): Should we trim all tokens here?
355 Tokenize(arg, "|", &args_);
356 }
357
358 GesturePropertyProvider::MatchProduct::MatchProduct(const std::string& arg)
359 : MatchCriteria(arg) {
360 }
361
362 bool GesturePropertyProvider::MatchProduct::Match(DevicePtr device) {
363 if (args_.empty())
364 return true;
365 std::string name(device->info.name);
366 for (size_t i = 0; i < args_.size(); i++)
367 if (name.find(args_[i]) != std::string::npos)
368 return true;
369 return false;
370 }
371
372 GesturePropertyProvider::MatchDevicePath::MatchDevicePath(
373 const std::string& arg)
374 : MatchCriteria(arg) {
375 }
376
377 bool GesturePropertyProvider::MatchDevicePath::Match(DevicePtr device) {
378 if (args_.empty())
379 return true;
380
381 // Check if the device path matches any pattern.
382 std::string path = GetDeviceNodePath(device);
383 if (path.empty())
384 return false;
385 for (size_t i = 0; i < args_.size(); i++)
386 if (fnmatch(args_[i].c_str(), path.c_str(), FNM_NOESCAPE) == 0)
387 return true;
388 return false;
389 }
390
391 GesturePropertyProvider::MatchUSBID::MatchUSBID(const std::string& arg)
392 : MatchCriteria(arg) {
393 // Check each pattern and split valid ones into vids and pids.
394 for (size_t i = 0; i < args_.size(); i++) {
395 if (!IsValidPattern(args_[i]))
396 continue;
397 std::vector<std::string> tokens;
398 base::SplitString(args_[i], ':', &tokens);
399 vid_patterns_.push_back(base::StringToLowerASCII(tokens[0]));
400 pid_patterns_.push_back(base::StringToLowerASCII(tokens[1]));
401 }
402 }
403
404 bool GesturePropertyProvider::MatchUSBID::Match(DevicePtr device) {
405 if (vid_patterns_.empty())
406 return true;
407 std::string vid = base::StringPrintf("%04x", device->info.id.vendor);
408 std::string pid = base::StringPrintf("%04x", device->info.id.product);
409 for (size_t i = 0; i < vid_patterns_.size(); i++) {
410 if (fnmatch(vid_patterns_[i].c_str(), vid.c_str(), FNM_NOESCAPE) == 0 &&
411 fnmatch(pid_patterns_[i].c_str(), pid.c_str(), FNM_NOESCAPE) == 0) {
412 return true;
413 }
414 }
415 return false;
416 }
417
418 bool GesturePropertyProvider::MatchUSBID::IsValidPattern(
419 const std::string& pattern) {
420 // Each USB id should be in the lsusb format, i.e., xxxx:xxxx. We choose to do
421 // a lazy check here: if the pattern contains wrong characters not in the hex
422 // number range, it won't be matched anyway.
423 int number_of_colons = 0;
424 size_t pos_of_colon = 0;
425 for (size_t i = 0; i < pattern.size(); i++)
426 if (pattern[i] == ':')
427 ++number_of_colons, pos_of_colon = i;
428 return (number_of_colons == 1) && (pos_of_colon != 0) &&
429 (pos_of_colon != pattern.size() - 1);
430 }
431
432 GesturePropertyProvider::MatchDeviceType::MatchDeviceType(
433 const std::string& arg)
434 : MatchCriteria(arg), value_(true), is_valid_(false) {
435 // Default value of a match criteria is true.
436 if (args_.empty())
437 args_.push_back("on");
438
439 // We care only about the first argument.
440 int value = ParseBooleanKeyword(args_[0]);
441 if (value) {
442 is_valid_ = true;
443 value_ = value > 0;
444 }
445 }
446
447 GesturePropertyProvider::MatchIsPointer::MatchIsPointer(const std::string& arg)
448 : MatchDeviceType(arg) {
449 }
450
451 bool GesturePropertyProvider::MatchIsPointer::Match(DevicePtr device) {
452 if (!is_valid_)
453 return true;
454 return (value_ == (device->info.evdev_class == EvdevClassMouse ||
455 device->info.evdev_class == EvdevClassMultitouchMouse));
456 }
457
458 GesturePropertyProvider::MatchIsTouchpad::MatchIsTouchpad(
459 const std::string& arg)
460 : MatchDeviceType(arg) {
461 }
462
463 bool GesturePropertyProvider::MatchIsTouchpad::Match(DevicePtr device) {
464 if (!is_valid_)
465 return true;
466 return (value_ == (device->info.evdev_class == EvdevClassTouchpad));
467 }
468
469 GesturePropertyProvider::MatchIsTouchscreen::MatchIsTouchscreen(
470 const std::string& arg)
471 : MatchDeviceType(arg) {
472 }
473
474 bool GesturePropertyProvider::MatchIsTouchscreen::Match(DevicePtr device) {
475 if (!is_valid_)
476 return true;
477 return (value_ == (device->info.evdev_class == EvdevClassTouchscreen));
478 }
479
480 bool GesturePropertyProvider::ConfigurationSection::Match(DevicePtr device) {
481 for (size_t i = 0; i < criterias.size(); ++i)
482 if (!criterias[i]->Match(device))
483 return false;
484 return true;
485 }
486
487 void GesturePropertyProvider::AddProperty(const DeviceId device_id,
488 const std::string& name,
489 GesturesProp* prop) {
490 // The look-up should never fail because ideally a property can only be
491 // created with GesturesPropCreate* functions from the gesture lib side.
492 // Therefore, we simply return on failure.
493 DevicePropertyMap::iterator it = properties_maps_.find(device_id);
494 if (it != properties_maps_.end())
495 it->second->insert(std::make_pair(name, prop));
496 }
497
498 void GesturePropertyProvider::DeleteProperty(const DeviceId device_id,
499 const std::string& name) {
500 DevicePropertyMap::iterator it = properties_maps_.find(device_id);
501 if (it != properties_maps_.end())
502 it->second->erase(name);
503 }
504
505 GesturesProp* GesturePropertyProvider::FindProperty(const DeviceId device_id,
506 const std::string& name) {
507 return FindProperty(properties_maps_, device_id, name);
508 }
509
510 GesturesProp* GesturePropertyProvider::FindProperty(
511 const DevicePropertyMap& device_property_map,
512 const DeviceId device_id,
513 const std::string& name) {
514 DevicePropertyMap::const_iterator ia = device_property_map.find(device_id);
515 if (ia == properties_maps_.end())
516 return NULL;
517 PropertyMap::const_iterator ib = ia->second->find(name);
518 if (ib == ia->second->end())
519 return NULL;
520 return ib->second;
521 }
522
523 GesturesProp* GesturePropertyProvider::GetDefaultProperty(
524 const DeviceId device_id,
525 const std::string& name) {
526 return FindProperty(default_properties_maps_, device_id, name);
527 }
528
529 void GesturePropertyProvider::LoadDeviceConfigurations() {
530 // Enumerate conf files and sort them lexicographically.
531 std::set<base::FilePath> files;
532 base::FileEnumerator file_enum(base::FilePath(kConfigurationFilePath),
533 false,
534 base::FileEnumerator::FILES,
535 "*.conf");
536 for (base::FilePath path = file_enum.Next(); !path.empty();
537 path = file_enum.Next()) {
538 files.insert(path);
539 }
540 LOG(INFO_SEVERITY) << files.size() << " conf files were found";
541
542 // Parse conf files one-by-one.
543 for (std::set<base::FilePath>::iterator file_iter = files.begin();
544 file_iter != files.end();
545 ++file_iter) {
546 LOG(INFO_SEVERITY) << "Parsing conf file: " << (*file_iter).value();
547 std::string content;
548 if (!base::ReadFileToString(*file_iter, &content)) {
549 LOG(ERROR) << "Can't loading gestures conf file: "
550 << (*file_iter).value();
551 continue;
552 }
553 ParseXorgConfFile(content);
554 }
555 }
556
557 void GesturePropertyProvider::ParseXorgConfFile(const std::string& content) {
558 // To simplify the parsing work, we made some assumption about the conf file
559 // format which doesn't exist in the original xorg-conf spec. Most important
560 // ones are:
561 // 1. All keywords and names are now case-sensitive. Also, underscores are not
562 // ignored.
563 // 2. Each entry takes up one and exactly one line in the file.
564 // 3. No negation of the option value even if the option name is prefixed with
565 // "No" as it may cause problems for option names that does start with "No"
566 // (e.g., "Non-linearity").
567
568 // Break the content into sections, lines and then pieces.
569 // Sections are delimited by the "EndSection" keyword.
570 // Lines are delimited by "\n".
571 // Pieces are delimited by all white-spaces.
572 std::vector<std::string> sections;
573 base::SplitStringUsingSubstr(content, "EndSection", &sections);
574 for (size_t i = 0; i < sections.size(); ++i) {
575 // Create a new configuration section.
576 configurations_.push_back(ConfigurationSection());
577 ConfigurationSection& config = configurations_.back();
578
579 // Break the section into lines.
580 base::StringTokenizer lines(sections[i], "\n");
581 bool is_input_class_section = true;
582 bool has_checked_section_type = false;
583 while (is_input_class_section && lines.GetNext()) {
584 // Parse the line w.r.t. the xorg-conf format.
585 std::string line(lines.token());
586
587 // Skip empty lines.
588 if (line.empty())
589 continue;
590
591 // Treat all whitespaces as delimiters.
592 base::StringTokenizer pieces(line, base::kWhitespaceASCII);
593 pieces.set_quote_chars("\"");
594 bool is_parsing = false;
595 bool has_error = false;
596 bool next_is_section_type = false;
597 bool next_is_option_name = false;
598 bool next_is_option_value = false;
599 bool next_is_match_criteria = false;
600 bool next_is_identifier = false;
601 std::string match_type, option_name;
602 while (pieces.GetNext()) {
603 std::string piece(pieces.token());
604
605 // Skip empty pieces.
606 if (piece.empty())
607 continue;
608
609 // See if we are currently parsing an entry or are still looking for
610 // one.
611 if (is_parsing) {
612 // Stop parsing the current line if the format is wrong.
613 if (piece.size() <= 2 || piece[0] != '\"' ||
614 piece[piece.size() - 1] != '\"') {
615 LOG(ERROR) << "Error parsing line: " << lines.token();
616 has_error = true;
617 if (next_is_section_type)
618 is_input_class_section = false;
619 break;
620 }
621
622 // Parse the arguments. Note that we don't break even if a whitespace
623 // string is passed. It will just be handled in various ways based on
624 // the entry type.
625 std::string arg;
626 base::TrimWhitespaceASCII(
627 piece.substr(1, piece.size() - 2), base::TRIM_ALL, &arg);
628 if (next_is_section_type) {
629 // We only care about InputClass sections.
630 if (arg != "InputClass") {
631 has_error = true;
632 is_input_class_section = false;
633 } else {
634 LOG(INFO_SEVERITY) << "New InputClass section found";
635 has_checked_section_type = true;
636 }
637 break;
638 } else if (next_is_identifier) {
639 LOG(INFO_SEVERITY) << "Identifier: " << arg;
640 config.identifier = arg;
641 next_is_identifier = false;
642 break;
643 } else if (next_is_option_name) {
644 // TODO(sheckylin): Support option "Ignore".
645 option_name = arg;
646 next_is_option_value = true;
647 next_is_option_name = false;
648 } else if (next_is_option_value) {
649 GesturesProp prop;
650 if(CreateDefaultProperty(option_name, arg, &prop))
651 config.properties.push_back(prop);
652 next_is_option_value = false;
653 break;
654 } else if (next_is_match_criteria) {
655 // Skip all match types that are not supported.
656 if (IsMatchTypeSupported(match_type)) {
657 MatchCriteria *criteria = CreateMatchCriteria(match_type, arg);
658 if (criteria)
659 config.criterias.push_back(criteria);
660 }
661 next_is_match_criteria = false;
662 break;
663 }
664 } else {
665 // If the section type hasn't been decided yet, look for it.
666 // Otherwise, look for valid entries according to the spec.
667 if (has_checked_section_type) {
668 if (piece == "Driver") {
669 // TODO(sheckylin): Support "Driver" so that we can force a device
670 // not to use the gesture lib.
671 NOTIMPLEMENTED();
672 break;
673 } else if (piece == "Identifier") {
674 is_parsing = true;
675 next_is_identifier = true;
676 continue;
677 } else if (piece == "Option") {
678 is_parsing = true;
679 next_is_option_name = true;
680 continue;
681 } else if (piece.size() > 5 && piece.compare(0, 5, "Match") == 0) {
682 match_type = piece;
683 is_parsing = true;
684 next_is_match_criteria = true;
685 continue;
686 }
687 } else if (piece == "Section") {
688 is_parsing = true;
689 next_is_section_type = true;
690 continue;
691 }
692
693 // If none of the above is found, check if the current piece starts a
694 // comment.
695 if (piece.empty() || piece[0] != '#') {
696 LOG(ERROR) << "Error parsing line: " << lines.token();
697 has_error = true;
698 }
699 break;
700 }
701 }
702
703 // The value of a boolean option is skipped (default is true).
704 if (!has_error && (next_is_option_value || next_is_match_criteria)) {
705 if (next_is_option_value) {
706 GesturesProp prop;
707 if (CreateDefaultProperty(option_name, "on", &prop))
708 config.properties.push_back(prop);
709 } else if (IsMatchTypeSupported(match_type) &&
710 IsDeviceMatchType(match_type)) {
711 MatchCriteria* criteria = CreateMatchCriteria(match_type, "on");
712 if (criteria)
713 config.criterias.push_back(criteria);
714 }
715 }
716 }
717
718 // Remove useless config sections.
719 if (!is_input_class_section ||
720 (config.criterias.empty() && config.properties.empty())) {
721 configurations_.pop_back();
722 }
723 }
724 }
725
726 bool GesturePropertyProvider::IsMatchTypeSupported(
727 const std::string& match_type) {
728 for (size_t i = 0; i < arraysize(kSupportedMatchTypes); i++)
729 if (match_type == kSupportedMatchTypes[i])
730 return true;
731 for (size_t i = 0; i < arraysize(kUnsupportedMatchTypes); i++) {
732 if (match_type == kUnsupportedMatchTypes[i]) {
733 LOG(ERROR) << "Unsupported gestures input class match type: "
734 << match_type;
735 return false;
736 }
737 }
738 return false;
739 }
740
741 bool GesturePropertyProvider::IsDeviceMatchType(
742 const std::string& match_type) {
743 return StartsWithASCII(match_type, "MatchIs", true);
744 }
745
746 GesturePropertyProvider::MatchCriteria*
747 GesturePropertyProvider::CreateMatchCriteria(const std::string& match_type,
748 const std::string& arg) {
749 LOG(INFO_SEVERITY) << "Creating match criteria: (" << match_type << ", "
750 << arg << ")";
751 return (this->*match_criteria_map_[match_type])(arg);
752 }
753
754 bool GesturePropertyProvider::CreateDefaultProperty(const std::string& name,
755 const std::string& value,
756 GesturesProp* prop) {
757 // Our parsing rule:
758 // 1. No hex or oct number is accepted.
759 // 2. All numbers will be stored as double.
760 // 3. Array elements can be separated by both white-spaces or commas.
761 // 4. A token is treated as numeric either if it is one of the special
762 // keywords for boolean values (on, true, yes, off, false, no) or if
763 // base::StringToDouble succeeds.
764 // 5. The property is treated as numeric if and only if all of its elements
765 // (if any) are numerics. Otherwise, it will be treated as a string.
766 // 6. A string property will be trimmed before storing its value.
767 LOG(INFO_SEVERITY) << "Creating default property: (" << name << ", " << value
768 << ")";
769
770 // Parse elements one-by-one.
771 std::string delimiters(base::kWhitespaceASCII);
772 delimiters.append(",");
773 base::StringTokenizer tokens(value, delimiters);
774 bool is_all_numeric = true;
775 std::vector<double> numbers;
776 while (tokens.GetNext()) {
777 // Skip empty tokens.
778 std::string token(tokens.token());
779 if (token.empty())
780 continue;
781
782 // Check if it is a boolean keyword.
783 int bool_result = ParseBooleanKeyword(token);
784 if (bool_result) {
785 numbers.push_back(bool_result > 0);
786 continue;
787 }
788
789 // Check if it is a number.
790 double real_result;
791 bool success = base::StringToDouble(token, &real_result);
792 if (!success) {
793 is_all_numeric = false;
794 break;
795 }
796 numbers.push_back(real_result);
797 }
798
799 // Setup GesturesProp data.
800 prop->name = name;
801 // Arrays need to contain at least one number and may contain numbers only.
802 if (is_all_numeric && numbers.size()) {
803 prop->type = PT_REAL;
804 prop->count = numbers.size();
805 prop->val.r = new double[numbers.size()];
806 std::copy(numbers.begin(), numbers.end(), prop->val.r);
807 } else {
808 prop->type = PT_STRING;
809 prop->count = 1;
810 std::string trimmed;
811 base::TrimWhitespaceASCII(value, base::TRIM_ALL, &trimmed);
812 prop->val.s = new std::string(trimmed);
813 }
814
815 LOG(INFO_SEVERITY) << "Prop: " << *prop;
816 // The function will always succeed for now but it may change later if we
817 // specify some name or args as invalid.
818 return true;
819 }
820
821 int GesturePropertyProvider::ParseBooleanKeyword(const std::string& value) {
822 for (size_t i = 0; i < arraysize(kTrue); i++)
823 if (LowerCaseEqualsASCII(value, kTrue[i]))
824 return 1;
825 for (size_t i = 0; i < arraysize(kFalse); i++)
826 if (LowerCaseEqualsASCII(value, kFalse[i]))
827 return -1;
828 return 0;
829 }
830
831 void GesturePropertyProvider::SetupDefaultProperties(
832 const DeviceId device_id,
833 DevicePtr dev) {
834 LOG(INFO_SEVERITY) << "Setting up default properties for (" << dev << ", "
835 << device_id << ", " << dev->info.name << ")";
836
837 // Go through all parsed sections.
838 scoped_ptr<PropertyMap> prop_map(new PropertyMap);
839 for (size_t i = 0; i < configurations_.size(); i++) {
840 if (configurations_[i].Match(dev)) {
841 LOG(INFO_SEVERITY) << "Conf section \"" << configurations_[i].identifier
842 << "\" is matched";
843 for (size_t j = 0; j < configurations_[i].properties.size(); j++) {
844 GesturesProp& prop = configurations_[i].properties[j];
845 // We can't use insert here because a property may be set for several
846 // times along the way.
847 (*prop_map)[prop.name] = &prop;
848 }
849 }
850 }
851 default_properties_maps_.set(device_id, prop_map.Pass());
852 }
853
854 template <typename T>
855 GesturesProp* GesturesPropFunctionsWrapper::Create(
856 void* dev,
857 const char* name,
858 GesturePropertyProvider::PropertyType type,
859 T* val,
860 size_t count) {
861 // Create a new PropertyMap for the device if not exists already.
862 GesturePropertyProvider::DeviceId device_id =
863 GesturePropertyProvider::GetInstance()->GetDeviceId(dev, true);
864
865 /* Insert property and setup property values */
866
867 // First, see if the GesturesProp already exists.
868 DVLOG(3) << "Creating Property: \"" << name << "\"";
869 GesturesProp* prop =
870 GesturePropertyProvider::GetInstance()->FindProperty(device_id, name);
871 if (!prop) {
872 prop = new GesturesProp();
873 GesturePropertyProvider::GetInstance()->AddProperty(device_id, name, prop);
874 } else if (prop->is_allocated) {
875 // Free the data pointer if it was allocated here. This shouldn't happen in
876 // normal use cases (that a read only property being declared again).
877 // However, we want to prevent any possible memory leak if it does happen.
878 prop->Free();
879 }
880
881 // Set the values.
882 prop->name = name;
883 prop->type = type;
884 prop->count = count;
885
886 // Allocate memory for the data if a NULL pointer is provided.
887 if (!val) {
888 if (count > 1)
889 prop->val.v = new T[count];
890 else
891 prop->val.v = new T;
892 prop->is_allocated = true;
893 } else {
894 prop->val.v = val;
895 }
896 return prop;
897 }
898
899 template <typename T, GesturePropertyProvider::PropertyType type>
900 GesturesProp* GesturesPropFunctionsWrapper::CreateProperty(void* dev,
901 const char* name,
902 T* val,
903 size_t count,
904 const T* init) {
905 GesturesProp* result = Create(dev, name, type, val, count);
906 if (!val)
907 result->is_managed_externally = false;
908
909 // We currently assumed that we won't specify any array property in the
910 // configuration files. The code needs to be updated if the assumption becomes
911 // invalid in the future.
912 if (count == 1) {
913 GesturesProp* default_prop =
914 GesturePropertyProvider::GetInstance()->GetDefaultProperty(
915 GesturePropertyProvider::GetInstance()->GetDeviceId(dev), name);
916 if (!default_prop ||
917 default_prop->type == GesturePropertyProvider::PT_STRING) {
918 *(reinterpret_cast<T*>(result->val.v)) = *init;
919 } else {
920 LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
921 // TODO(sheckylin): Handle value out-of-range (e.g., double to int).
922 *(reinterpret_cast<T*>(result->val.v)) =
923 static_cast<T>(*(default_prop->val.r));
924 }
925 } else {
926 memmove(result->val.v, init, count * sizeof(T));
927 }
928
929 LOG(INFO_SEVERITY) << "Created active prop: " << *result;
930 return result;
931 }
932
933 GesturesProp* GesturesPropFunctionsWrapper::CreateInt(void* dev,
934 const char* name,
935 int* val,
936 size_t count,
937 const int* init) {
938 return CreateProperty<int, GesturePropertyProvider::PT_INT>(
939 dev, name, val, count, init);
940 }
941
942 GesturesProp* GesturesPropFunctionsWrapper::CreateShort(void* dev,
943 const char* name,
944 short* val,
945 size_t count,
946 const short* init) {
947 return CreateProperty<short, GesturePropertyProvider::PT_SHORT>(
948 dev, name, val, count, init);
949 }
950
951 GesturesProp* GesturesPropFunctionsWrapper::CreateBool(
952 void* dev,
953 const char* name,
954 GesturesPropBool* val,
955 size_t count,
956 const GesturesPropBool* init) {
957 return CreateProperty<GesturesPropBool, GesturePropertyProvider::PT_BOOL>(
958 dev, name, val, count, init);
959 }
960
961 GesturesProp* GesturesPropFunctionsWrapper::CreateReal(void* dev,
962 const char* name,
963 double* val,
964 size_t count,
965 const double* init) {
966 return CreateProperty<double, GesturePropertyProvider::PT_REAL>(
967 dev, name, val, count, init);
968 }
969
970 GesturesProp* GesturesPropFunctionsWrapper::CreateString(void* dev,
971 const char* name,
972 const char** val,
973 const char* init) {
974 // StringProperty's memory is always allocated on this side instead of
975 // externally in the gesture lib as the original one will be destroyed right
976 // after the constructor call (check the design of StringProperty). To do
977 // this, we call the Create function with NULL pointer so that it always
978 // allocates.
979 GesturesProp* result = Create(dev,
980 name,
981 GesturePropertyProvider::PT_STRING,
982 static_cast<std::string*>(NULL),
983 1);
984 if (!val)
985 result->is_managed_externally = false;
986
987 GesturesProp* default_prop =
988 GesturePropertyProvider::GetInstance()->GetDefaultProperty(
989 GesturePropertyProvider::GetInstance()->GetDeviceId(dev), name);
990
991 // Setup its value just like the other data types.
992 if (!default_prop ||
993 default_prop->type != GesturePropertyProvider::PT_STRING) {
994 *(result->val.s) = init;
995 } else {
996 LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
997 *(result->val.s) = *(default_prop->val.s);
998 }
999
1000 // If the provided pointer is not NULL, replace its content
1001 // (val_ of StringProperty) with the address of our allocated string.
1002 // Note that we don't have to do this for the other data types as they will
1003 // use the original data pointer if possible and it is unnecessary to do so
1004 // if the pointer is NULL.
1005 if (val) {
1006 *(val) = result->val.s->c_str();
1007 result->write_back = val;
1008 }
1009
1010 LOG(INFO_SEVERITY) << "Created active prop: " << *result;
1011 return result;
1012 }
1013
1014 void GesturesPropFunctionsWrapper::RegisterHandlers(
1015 void* priv,
1016 GesturesProp* prop,
1017 void* handler_data,
1018 GesturesPropGetHandler get,
1019 GesturesPropSetHandler set) {
1020 // Sanity checks
1021 if (!priv || !prop)
1022 return;
1023
1024 prop->handler_data = handler_data;
1025 prop->get = get;
1026 prop->set = set;
1027 }
1028
1029 void GesturesPropFunctionsWrapper::Free(void* priv, GesturesProp* prop) {
1030 if (!prop)
1031 return;
1032
1033 GesturePropertyProvider::DeviceId device_id =
1034 GesturePropertyProvider::GetInstance()->GetDeviceId(priv);
1035 if (device_id < 0)
1036 return;
1037
1038 DVLOG(3) << "Freeing Property: \"" << prop->name << "\"";
1039 GesturePropertyProvider::GetInstance()->DeleteProperty(device_id, prop->name);
1040 if (prop->is_allocated)
1041 prop->Free();
1042 delete prop;
1043 }
1044
1045 GesturesProp* GesturesPropFunctionsWrapper::CreateIntSingle(
1046 void* dev,
1047 const char* name,
1048 int* val,
1049 int init) {
1050 return CreateInt(dev, name, val, 1, &init);
1051 }
1052
1053 GesturesProp* GesturesPropFunctionsWrapper::CreateBoolSingle(
1054 void* dev,
1055 const char* name,
1056 GesturesPropBool* val,
1057 GesturesPropBool init) {
1058 return CreateBool(dev, name, val, 1, &init);
1059 }
1060
1061 bool GesturesPropFunctionsWrapper::InitializeDeviceProperties(
1062 void* device, GesturePropertyProvider::DeviceProperty* props) {
1063 if (!device)
1064 return false;
1065 GesturePropertyProvider::DevicePtr dev =
1066 static_cast<GesturePropertyProvider::DevicePtr>(device);
1067
1068 /* Create Device Properties */
1069
1070 // Read Only properties.
1071 CreateString(dev, "Device Node", NULL, GetDeviceNodePath(dev).c_str());
1072 CreateShort(dev,
1073 "Device Vendor ID",
1074 NULL,
1075 1,
1076 reinterpret_cast<short*>(&(dev->info.id.vendor)));
1077 CreateShort(dev,
1078 "Device Product ID",
1079 NULL,
1080 1,
1081 reinterpret_cast<short*>(&(dev->info.id.product)));
1082
1083 // Useable trackpad area. If not configured in .conf file,
1084 // use x/y valuator min/max as reported by kernel driver.
1085 CreateIntSingle(
1086 dev, "Active Area Left", &props->area_left, Event_Get_Left(dev));
1087 CreateIntSingle(
1088 dev, "Active Area Right", &props->area_right, Event_Get_Right(dev));
1089 CreateIntSingle(dev, "Active Area Top", &props->area_top, Event_Get_Top(dev));
1090 CreateIntSingle(
1091 dev, "Active Area Bottom", &props->area_bottom, Event_Get_Bottom(dev));
1092
1093 // Trackpad resolution (pixels/mm). If not configured in .conf file,
1094 // use x/y resolution as reported by kernel driver.
1095 CreateIntSingle(
1096 dev, "Vertical Resolution", &props->res_y, Event_Get_Res_Y(dev));
1097 CreateIntSingle(
1098 dev, "Horizontal Resolution", &props->res_x, Event_Get_Res_X(dev));
1099
1100 // Trackpad orientation minimum/maximum. If not configured in .conf file,
1101 // use min/max as reported by kernel driver.
1102 CreateIntSingle(dev,
1103 "Orientation Minimum",
1104 &props->orientation_minimum,
1105 Event_Get_Orientation_Minimum(dev));
1106 CreateIntSingle(dev,
1107 "Orientation Maximum",
1108 &props->orientation_maximum,
1109 Event_Get_Orientation_Maximum(dev));
1110
1111 // Log dump property. Will call Event_Dump_Debug_Log when its value is being
1112 // set.
1113 GesturesProp* dump_debug_log_prop =
1114 CreateBoolSingle(dev, "Dump Debug Log", &props->dump_debug_log, false);
1115 RegisterHandlers(dev, dump_debug_log_prop, dev, NULL, Event_Dump_Debug_Log);
1116
1117 // Whether to do the gesture recognition or just passing the multi-touch data
1118 // to upper layers.
1119 CreateBoolSingle(
1120 dev, "Raw Touch Passthrough", &props->raw_passthrough, false);
1121 return true;
1122 }
1123
1124 /* Global GesturesPropProvider
1125 *
1126 * Used by PropRegistry in GestureInterpreter to forward property value
1127 * creations from there.
1128 * */
1129 const GesturesPropProvider kGesturePropProvider = {
1130 GesturesPropFunctionsWrapper::CreateInt,
1131 GesturesPropFunctionsWrapper::CreateShort,
1132 GesturesPropFunctionsWrapper::CreateBool,
1133 GesturesPropFunctionsWrapper::CreateString,
1134 GesturesPropFunctionsWrapper::CreateReal,
1135 GesturesPropFunctionsWrapper::RegisterHandlers,
1136 GesturesPropFunctionsWrapper::Free};
1137
1138 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698