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

Side by Side Diff: ui/aura/gestures/gesture_recognizer_grail.cc

Issue 9773024: This patch implements Chromium's Aura gesture recognizer in terms of utouch-grail and utouch-frame … (Closed) Base URL: https://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 8 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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698