|
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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/aura/gestures/gesture_recognizer_grail.h" | |
6 | |
7 #include <utouch/grail.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/memory/singleton.h" | |
12 #include "base/time.h" | |
13 #include "ui/aura/event.h" | |
14 #include "ui/aura/window.h" | |
15 #include "ui/base/events.h" | |
16 #include "ui/base/touch/touch_factory.h" | |
17 #include "ui/base/x/x11_util.h" | |
18 | |
19 namespace { | |
20 | |
21 static const base::TimeDelta kLongPressTimeThreshold | |
sadrul
2012/04/03 19:52:01
Should you just use values from GestureConfigurati
| |
22 = base::TimeDelta::FromMilliseconds(3000); | |
23 | |
24 static const base::TimeDelta kDoubleTapTimeout | |
25 = base::TimeDelta::FromMilliseconds(300); | |
26 | |
27 bool GetDeviceAndWindowFromEvent( | |
28 const UFEvent event, | |
29 UFDevice* device, | |
30 UFWindowId* window_id) { | |
31 | |
32 UFFrame frame; | |
33 UFStatus status = frame_event_get_property(event, UFEventPropertyFrame, | |
34 &frame); | |
35 if (status != UFStatusSuccess) { | |
36 LOG(ERROR) << "failed to get frame from event\n"; | |
37 return false; | |
38 } | |
39 | |
40 *device = frame_frame_get_device(frame); | |
41 *window_id = frame_frame_get_window_id(frame); | |
42 | |
43 char * deviceName; | |
44 if (UFStatusSuccess == | |
45 frame_device_get_property(*device, UFDevicePropertyName, &deviceName)) | |
46 DLOG(INFO) << "Considering device: " << deviceName; | |
47 else | |
48 DLOG(INFO) << "Problem reading out device name."; | |
49 | |
50 return true; | |
51 } | |
52 | |
53 // Subscribes to the grail instance (handle) | |
54 // for two-finger taps, drags and pinches. Please | |
sadrul
2012/04/03 19:52:01
Does this mean there will be no single-finger gest
| |
55 // note that subscribing to the grail instance is non-blocking. | |
56 // Returns an int with boolean semantics to indicate success or failure. | |
57 int SubscribeForGestures( | |
58 UGHandle handle, | |
59 UFDevice device, | |
60 ::Window window, | |
61 UGSubscription subscription) { | |
62 char * deviceName = NULL; | |
63 if (UFStatusSuccess == | |
64 frame_device_get_property(device, UFDevicePropertyName, &deviceName)) | |
65 DLOG(INFO) << "Subscribing to device: " << deviceName; | |
66 | |
67 UGStatus status; | |
68 | |
69 int touches_start = 2; | |
70 int touches_max = 2; | |
71 int touches_min = 2; | |
72 int atomic = 1; | |
73 | |
74 UFWindowId window_id = frame_x11_create_window_id(window); | |
75 const UGGestureTypeMask mask = | |
76 UGGestureTypeDrag | | |
77 UGGestureTypePinch | | |
78 UGGestureTypeTap; | |
79 status = grail_subscription_new(&subscription); | |
80 if (status != UGStatusSuccess) { | |
81 LOG(ERROR) << "Failed to create subscription"; | |
82 return 0; | |
83 } | |
84 | |
85 status = grail_subscription_set_property(subscription, | |
86 UGSubscriptionPropertyDevice, | |
87 &device); | |
88 if (status != UGStatusSuccess) { | |
89 LOG(ERROR) << "Failed to set subscription device"; | |
90 return 0; | |
91 } | |
92 | |
93 status = grail_subscription_set_property(subscription, | |
94 UGSubscriptionPropertyWindow, | |
95 &window_id); | |
96 if (status != UGStatusSuccess) { | |
97 LOG(ERROR) << "Failed to set subscription window"; | |
98 return 0; | |
99 } | |
100 | |
101 status = grail_subscription_set_property(subscription, | |
102 UGSubscriptionPropertyAtomicGestures, | |
103 &atomic); | |
104 if (status != UGStatusSuccess) { | |
105 LOG(ERROR) << "Failed to set atomic gestures subscription property."; | |
106 return 0; | |
107 } | |
108 | |
109 status = grail_subscription_set_property(subscription, | |
110 UGSubscriptionPropertyTouchesStart, | |
111 &touches_start); | |
112 if (status != UGStatusSuccess) { | |
113 LOG(ERROR) << "Failed to set subscription start touches."; | |
114 return 0; | |
115 } | |
116 | |
117 status = grail_subscription_set_property(subscription, | |
118 UGSubscriptionPropertyTouchesMaximum, | |
119 &touches_max); | |
120 if (status != UGStatusSuccess) { | |
121 LOG(ERROR) << "Failed to set subscription start touches."; | |
122 return 0; | |
123 } | |
124 | |
125 status = grail_subscription_set_property(subscription, | |
126 UGSubscriptionPropertyTouchesMinimum, | |
127 &touches_min); | |
128 if (status != UGStatusSuccess) { | |
129 LOG(ERROR) << "Failed to set subscription min touches."; | |
130 return 0; | |
131 } | |
132 | |
133 status = grail_subscription_set_property(subscription, | |
134 UGSubscriptionPropertyMask, | |
135 &mask); | |
136 if (status != UGStatusSuccess) { | |
137 LOG(ERROR) << "Failed to set subscription mask"; | |
138 return 0; | |
139 } | |
140 | |
141 status = grail_subscription_activate(handle, subscription); | |
142 if (status != UGStatusSuccess) { | |
143 LOG(ERROR) << "Failed to activate subscription\n"; | |
144 return 0; | |
145 } | |
146 | |
147 DLOG(INFO) << "Successfully configured and activated grail subscription"; | |
148 | |
149 return 1; | |
150 } | |
151 | |
152 } // namespace | |
153 | |
154 namespace aura { | |
155 | |
156 // Implements a singleton for a raw grail instance and | |
157 // makes sure that RAII. | |
158 class GrailHolder { | |
159 public: | |
160 // Returns the singleton instance | |
161 static GrailHolder* GetInstance() { | |
162 return Singleton<GrailHolder>::get(); | |
163 } | |
164 | |
165 // Returns the handle managed by the instance | |
166 UGHandle handle() const { | |
167 return utouch_grail_handle_; | |
168 } | |
169 | |
170 private: | |
171 friend struct DefaultSingletonTraits<GrailHolder>; | |
172 | |
173 GrailHolder() | |
174 : utouch_grail_handle_(NULL) { | |
175 if (UGStatusSuccess != grail_new( | |
176 ui::TouchFactory::GetInstance()->handle(), | |
177 &utouch_grail_handle_)) { | |
178 LOG(ERROR) << "Problem initializing grail api."; | |
179 } else { | |
180 fd_set set; | |
181 FD_ZERO(&set); | |
182 int fd = grail_get_fd(utouch_grail_handle_); | |
183 FD_SET(fd, &set); | |
184 } | |
185 } | |
186 | |
187 ~GrailHolder() { | |
188 if (utouch_grail_handle_ != NULL) | |
189 grail_delete_v3(utouch_grail_handle_); | |
190 } | |
191 | |
192 UGHandle utouch_grail_handle_; | |
193 | |
194 DISALLOW_COPY_AND_ASSIGN(GrailHolder); | |
195 }; | |
196 | |
197 struct GestureRecognizerGrail::Private { | |
198 typedef std::map<UFDevice, UGSubscription> SubscriptionMap; | |
199 | |
200 explicit Private(RootWindow* window) | |
201 : flags_(0), | |
202 window_(window) { | |
203 } | |
204 | |
205 ~Private() { | |
206 SubscriptionMap::iterator it; | |
207 for (it = subscriptions_.begin(); | |
208 it != subscriptions_.end(); | |
209 ++it) { | |
210 grail_subscription_delete(it->second); | |
211 } | |
212 } | |
213 | |
214 void UpdateFrameInstance(XEvent* xEvent) { | |
215 if (xEvent != NULL) { | |
216 if (UFStatusSuccess != | |
217 frame_x11_process_event( | |
218 ui::TouchFactory::GetInstance()->handle(), | |
219 &xEvent->xcookie)) { | |
220 LOG(ERROR) << "Failed to inject X event"; | |
221 } | |
222 } else { | |
223 LOG(ERROR) << "Failed to extract native event"; | |
224 } | |
225 } | |
226 | |
227 void ProcessFrameEvents() { | |
228 UFEvent ufEvent; | |
229 while (frame_get_event( | |
230 ui::TouchFactory::GetInstance()->handle(), | |
231 &ufEvent) == | |
232 UFStatusSuccess) { | |
233 grail_process_frame_event( | |
234 GrailHolder::GetInstance()->handle(), | |
235 ufEvent); | |
236 | |
237 switch (frame_event_get_type(ufEvent)) { | |
238 case UFEventTypeDeviceAdded: { | |
239 UFDevice device = NULL; | |
240 UFStatus status = frame_event_get_property(ufEvent, | |
241 UFEventPropertyDevice, | |
242 &device); | |
243 | |
244 if (status != UFStatusSuccess) | |
245 LOG(ERROR) << "Failed to get device from event."; | |
246 else | |
247 SubscribeForGestures( | |
248 GrailHolder::GetInstance()->handle(), | |
249 device, | |
250 ui::TouchFactory::GetInstance()->native_root_window_aura(), | |
sadrul
2012/04/03 19:52:01
This should use window_->native_window() or someth
| |
251 subscriptions_[device]); | |
252 | |
253 break; | |
254 } | |
255 | |
256 case UFEventTypeDeviceRemoved: { | |
257 UFDevice device = NULL; | |
258 UFStatus status = frame_event_get_property(ufEvent, | |
259 UFEventPropertyDevice, | |
260 &device); | |
261 | |
262 if (status != UFStatusSuccess) { | |
263 LOG(ERROR) << "Failed to get device from event."; | |
264 } else { | |
265 Private::SubscriptionMap::iterator it = | |
266 subscriptions_.find(device); | |
267 if (it != subscriptions_.end()) { | |
268 grail_subscription_delete(it->second); | |
269 subscriptions_.erase(it); | |
270 } | |
271 } | |
272 break; | |
273 } | |
274 default: | |
275 break; | |
276 } | |
277 | |
278 frame_event_unref(ufEvent); | |
279 } | |
280 } | |
281 | |
282 void ProcessSlice( | |
283 UGSlice slice, | |
284 uint64_t time, | |
285 const TouchEvent& event, | |
286 GestureRecognizer::Gestures * result) { | |
287 DCHECK(result != NULL); | |
288 | |
289 const UGGestureTypeMask recognized = grail_slice_get_recognized(slice); | |
290 | |
291 if (recognized & UGGestureTypePinch) { | |
292 linked_ptr<GestureEvent> lp(ProcessPinch(slice, event)); | |
293 if (lp.get() != NULL) | |
294 result->push_back(lp); | |
295 } | |
296 | |
297 if (recognized & UGGestureTypeDrag) { | |
298 linked_ptr<GestureEvent> lp(ProcessDrag(slice, event)); | |
299 if (lp.get() != NULL) | |
300 result->push_back(lp); | |
301 } | |
302 | |
303 if (recognized & UGGestureTypeTap) { | |
304 linked_ptr<GestureEvent> lp(ProcessTap(slice, event)); | |
305 if (lp.get() != NULL) | |
306 result->push_back(lp); | |
307 } | |
308 } | |
309 | |
310 linked_ptr<GestureEvent> ProcessDrag(UGSlice slice, | |
rjkroege
2012/04/02 17:05:15
why not use a scoped_ptr?
| |
311 const TouchEvent& touch_event) { | |
312 ui::EventType event_type = ui::ET_UNKNOWN; | |
313 | |
314 switch (grail_slice_get_state(slice)) { | |
rjkroege
2012/04/02 17:05:15
This code would have to be updated as we re-struct
| |
315 case UGGestureStateBegin: | |
316 event_type = ui::ET_GESTURE_SCROLL_BEGIN; | |
317 break; | |
318 case UGGestureStateUpdate: | |
319 event_type = ui::ET_GESTURE_SCROLL_UPDATE; | |
320 break; | |
321 case UGGestureStateEnd: | |
322 event_type = ui::ET_GESTURE_SCROLL_END; | |
323 break; | |
324 } | |
325 | |
326 const UGTransform *transform = | |
327 grail_slice_get_transform(slice); | |
328 | |
329 GestureEvent::Properties props; | |
330 props.delta_x = -(*transform)[0][2]; | |
331 props.delta_y = -(*transform)[1][2]; | |
332 | |
333 GestureEvent * result = new GestureEvent(event_type, | |
334 touch_event.x(), | |
335 touch_event.y(), | |
336 touch_event.flags(), | |
337 base::Time::Now(), | |
338 props); | |
339 return linked_ptr<GestureEvent>(result); | |
340 } | |
341 | |
342 linked_ptr<GestureEvent> ProcessPinch(UGSlice slice, | |
343 const TouchEvent & touch_event) { | |
344 ui::EventType event_type = ui::ET_UNKNOWN; | |
345 switch (grail_slice_get_state(slice)) { | |
346 case UGGestureStateBegin: | |
347 event_type = ui::ET_GESTURE_PINCH_BEGIN; | |
348 break; | |
349 case UGGestureStateUpdate: | |
350 event_type = ui::ET_GESTURE_PINCH_UPDATE; | |
351 break; | |
352 case UGGestureStateEnd: | |
353 event_type = ui::ET_GESTURE_PINCH_END; | |
354 break; | |
355 } | |
356 | |
357 const UGTransform *transform = | |
358 grail_slice_get_cumulative_transform(slice); | |
359 | |
360 GestureEvent::Properties props; | |
361 props.delta_x = (*transform)[0][0]; | |
362 props.delta_y = (*transform)[1][1]; | |
363 props.scale_x = (*transform)[0][0]; | |
364 props.scale_y = (*transform)[1][1]; | |
365 | |
366 GestureEvent * result = new GestureEvent(event_type, | |
367 touch_event.x(), | |
368 touch_event.y(), | |
369 touch_event.flags(), | |
370 base::Time::Now(), | |
371 props); | |
372 | |
373 return linked_ptr<GestureEvent>(result); | |
374 } | |
375 | |
376 linked_ptr<GestureEvent> ProcessTap(UGSlice slice, | |
377 const TouchEvent & touch_event) { | |
378 ui::EventType event_type = ui::ET_UNKNOWN; | |
379 switch (grail_slice_get_state(slice)) { | |
380 case UGGestureStateBegin: | |
381 gesture_tap_start_ = base::Time::Now(); | |
382 return linked_ptr<GestureEvent>(NULL); | |
383 break; | |
384 case UGGestureStateUpdate: | |
385 return linked_ptr<GestureEvent>(NULL); | |
386 break; | |
387 case UGGestureStateEnd: { | |
388 base::Time now = base::Time::Now(); | |
389 base::TimeDelta dlp = now - gesture_tap_start_; | |
390 base::TimeDelta ddt = now - last_gesture_tap_completed_; | |
391 if (dlp >= kLongPressTimeThreshold) { | |
392 event_type = ui::ET_GESTURE_LONG_PRESS; | |
393 } else if (ddt < kDoubleTapTimeout) { | |
394 event_type = ui::ET_GESTURE_DOUBLE_TAP; | |
395 } else { | |
396 event_type = ui::ET_GESTURE_TAP; | |
397 last_gesture_tap_completed_ = now; | |
398 } | |
399 gesture_tap_start_ = base::Time::Now(); | |
400 break; | |
401 } | |
402 } | |
403 | |
404 return linked_ptr<GestureEvent>(new GestureEvent( | |
405 event_type, | |
406 touch_event.x(), | |
407 touch_event.y(), | |
408 touch_event.flags(), | |
409 base::Time::Now(), | |
410 GestureEvent::Properties())); | |
411 } | |
412 | |
413 int flags_; | |
414 RootWindow* window_; | |
415 | |
416 base::Time last_gesture_tap_completed_; | |
417 base::Time gesture_tap_start_; | |
418 | |
419 SubscriptionMap subscriptions_; | |
420 }; | |
421 | |
422 | |
423 //////////////////////////////////////////////////////////////////////////////// | |
424 // GestureRecognizerGrail Public: | |
425 | |
426 GestureRecognizerGrail::GestureRecognizerGrail(RootWindow* window) | |
427 : d_(new Private(window)) { | |
428 } | |
429 | |
430 GestureRecognizer::Gestures* | |
431 GestureRecognizerGrail::ProcessTouchEventForGesture( | |
432 const TouchEvent& event, | |
433 ui::TouchStatus status) { | |
434 | |
435 d_->UpdateFrameInstance( | |
436 static_cast<XEvent*>(event.native_event())); | |
437 | |
438 d_->ProcessFrameEvents(); | |
439 | |
440 Gestures * result = new Gestures(); | |
441 | |
442 UGEvent ugEvent; | |
443 while (grail_get_event(GrailHolder::GetInstance()->handle(), &ugEvent) == | |
444 UGStatusSuccess) { | |
445 switch (grail_event_get_type(ugEvent)) { | |
446 case UGEventTypeSlice: { | |
447 UGSlice slice; | |
448 UGStatus status; | |
449 status = grail_event_get_property(ugEvent, | |
450 UGEventPropertySlice, &slice); | |
451 if (status != UGStatusSuccess) { | |
452 break; | |
453 } | |
454 | |
455 d_->ProcessSlice(slice, | |
456 grail_event_get_time(ugEvent), | |
457 event, | |
458 result); | |
459 break; | |
460 } | |
461 default: | |
462 break; | |
463 } | |
464 | |
465 grail_event_unref(ugEvent); | |
466 } | |
467 | |
468 return result; | |
469 } | |
470 | |
471 void GestureRecognizerGrail::QueueTouchEventForGesture( | |
472 Window* /*window*/, | |
473 const TouchEvent& /*event*/) { | |
474 } | |
475 | |
476 GestureRecognizer::Gestures* GestureRecognizerGrail::AdvanceTouchQueue( | |
477 Window* /*window*/, | |
478 bool /*processed*/) { | |
479 return NULL; | |
480 } | |
481 | |
482 void GestureRecognizerGrail::FlushTouchQueue(Window* /*window*/) { | |
483 } | |
484 | |
485 // GestureRecognizer, static | |
486 GestureRecognizer* GestureRecognizer::Create(RootWindow* window) { | |
487 return new GestureRecognizerGrail(window); | |
488 } | |
489 | |
490 } // namespace aura | |
491 | |
OLD | NEW |