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

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

Powered by Google App Engine
This is Rietveld 408576698