Index: ui/aura/gestures/gesture_recognizer_grail.cc |
=================================================================== |
--- ui/aura/gestures/gesture_recognizer_grail.cc (revision 0) |
+++ ui/aura/gestures/gesture_recognizer_grail.cc (revision 0) |
@@ -0,0 +1,456 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/aura/gestures/gesture_recognizer_grail.h" |
+ |
+#include <utouch/grail.h> |
+ |
+#include "base/logging.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/singleton.h" |
+#include "base/time.h" |
+#include "ui/aura/event.h" |
+#include "ui/aura/window.h" |
+#include "ui/base/events.h" |
+#include "ui/base/touch/touch_factory.h" |
+#include "ui/base/x/x11_util.h" |
+ |
+namespace { |
+ |
tdresser
2012/03/29 17:44:38
Once https://chromiumcodereview.appspot.com/975101
|
+static const base::TimeDelta kLongPressTimeThreshold |
+= base::TimeDelta::FromMilliseconds(3000); |
+ |
+static const base::TimeDelta kDoubleTapTimeout |
+= base::TimeDelta::FromMilliseconds(300); |
+ |
+bool GetDeviceAndWindowFromEvent( |
+ const UFEvent event, |
+ UFDevice* device, |
+ UFWindowId* window_id) { |
+ |
+ UFFrame frame; |
+ UFStatus status = frame_event_get_property(event, UFEventPropertyFrame, |
+ &frame); |
+ if (status != UFStatusSuccess) { |
+ LOG(ERROR) << "failed to get frame from event\n"; |
+ return false; |
+ } |
+ |
+ *device = frame_frame_get_device(frame); |
+ *window_id = frame_frame_get_window_id(frame); |
+ |
+ char * deviceName; |
+ if (UFStatusSuccess == |
+ frame_device_get_property(*device, UFDevicePropertyName, &deviceName)) |
+ DLOG(INFO) << "Considering device: " << deviceName; |
+ else |
+ DLOG(INFO) << "Problem reading out device name."; |
+ |
+ return true; |
+} |
+ |
+// Subscribes to the grail instance (handle) |
+// for two-finger taps, drags and pinches. Please |
+// note that subscribing to the grail instance is non-blocking. |
+// Returns an int with boolean semantics to indicate success or failure. |
+int SubscribeForGestures( |
+ UGHandle handle, |
+ UFDevice device, |
+ ::Window window, |
+ UGSubscription subscription) { |
+ char * deviceName = NULL; |
+ if (UFStatusSuccess == |
+ frame_device_get_property(device, UFDevicePropertyName, &deviceName)) |
+ DLOG(INFO) << "Subscribing to device: " << deviceName; |
+ |
+ UGStatus status; |
+ |
+ int touches_start = 2; |
+ int touches_max = 2; |
+ int touches_min = 2; |
+ int atomic = 1; |
+ |
+ UFWindowId window_id = frame_x11_create_window_id(window); |
+ const UGGestureTypeMask mask = |
+ UGGestureTypeDrag | |
+ UGGestureTypePinch | |
+ UGGestureTypeTap; |
+ status = grail_subscription_new(&subscription); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to create subscription"; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyDevice, |
+ &device); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set subscription device"; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyWindow, |
+ &window_id); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set subscription window"; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyAtomicGestures, |
+ &atomic); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set atomic gestures subscription property."; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyTouchesStart, |
+ &touches_start); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set subscription start touches."; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyTouchesMaximum, |
+ &touches_max); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set subscription start touches."; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyTouchesMinimum, |
+ &touches_min); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set subscription min touches."; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_set_property(subscription, |
+ UGSubscriptionPropertyMask, |
+ &mask); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to set subscription mask"; |
+ return 0; |
+ } |
+ |
+ status = grail_subscription_activate(handle, subscription); |
+ if (status != UGStatusSuccess) { |
+ LOG(ERROR) << "Failed to activate subscription\n"; |
+ return 0; |
+ } |
+ |
+ DLOG(INFO) << "Successfully configured and activated grail subscription"; |
+ |
+ return 1; |
+} |
+ |
+} // namespace |
+ |
+namespace aura { |
+ |
+// Implements a singleton for a raw grail instance and |
+// makes sure that RAII. |
+class GrailHolder { |
+ public: |
+ // Returns the singleton instance |
+ static GrailHolder* GetInstance() { |
+ return Singleton<GrailHolder>::get(); |
+ } |
+ |
+ // Returns the handle managed by the instance |
+ UGHandle handle() const { |
+ return utouch_grail_handle_; |
+ } |
+ |
+ private: |
+ friend struct DefaultSingletonTraits<GrailHolder>; |
+ |
+ GrailHolder() |
+ : utouch_grail_handle_(NULL) { |
+ if (UGStatusSuccess != grail_new( |
+ ui::TouchFactory::GetInstance()->handle(), |
+ &utouch_grail_handle_)) { |
+ LOG(ERROR) << "Problem initializing grail api."; |
+ } else { |
+ fd_set set; |
+ FD_ZERO(&set); |
+ int fd = grail_get_fd(utouch_grail_handle_); |
+ FD_SET(fd, &set); |
+ } |
+ } |
+ |
+ ~GrailHolder() { |
+ if (utouch_grail_handle_ != NULL) |
+ grail_delete_v3(utouch_grail_handle_); |
+ } |
+ |
+ UGHandle utouch_grail_handle_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GrailHolder); |
+}; |
+ |
+struct GestureRecognizerGrail::Private { |
+ typedef std::map<UFDevice, UGSubscription> SubscriptionMap; |
+ |
+ explicit Private(RootWindow* window) |
+ : flags_(0), |
+ window_(window) { |
+ } |
+ |
+ ~Private() { |
+ SubscriptionMap::iterator it; |
+ for (it = subscriptions_.begin(); |
+ it != subscriptions_.end(); |
+ ++it) { |
+ grail_subscription_delete(it->second); |
+ } |
+ } |
+ |
+ void ProcessSlice( |
+ UGSlice slice, |
+ uint64_t time, |
+ const TouchEvent& event, |
+ GestureRecognizer::Gestures * result) { |
+ assert(result != NULL); |
tdresser
2012/03/29 17:44:38
I believe this should be a DCHECK, not an assert.
|
+ |
+ const UGGestureTypeMask recognized = grail_slice_get_recognized(slice); |
+ |
+ if (recognized & UGGestureTypePinch) { |
+ linked_ptr<GestureEvent> lp(ProcessPinch(slice, event)); |
+ if (lp.get() != NULL) |
+ result->push_back(lp); |
+ } |
+ |
+ if (recognized & UGGestureTypeDrag) { |
+ linked_ptr<GestureEvent> lp(ProcessDrag(slice, event)); |
+ if (lp.get() != NULL) |
+ result->push_back(lp); |
+ } |
+ |
+ if (recognized & UGGestureTypeTap) { |
+ linked_ptr<GestureEvent> lp(ProcessTap(slice, event)); |
+ if (lp.get() != NULL) |
+ result->push_back(lp); |
+ } |
+ } |
+ |
+ linked_ptr<GestureEvent> ProcessDrag(UGSlice slice, |
+ const TouchEvent& touch_event) { |
+ ui::EventType event_type = ui::ET_UNKNOWN; |
+ |
+ switch (grail_slice_get_state(slice)) { |
+ case UGGestureStateBegin: |
+ event_type = ui::ET_GESTURE_SCROLL_BEGIN; |
+ break; |
+ case UGGestureStateUpdate: |
+ event_type = ui::ET_GESTURE_SCROLL_UPDATE; |
+ break; |
+ case UGGestureStateEnd: |
+ event_type = ui::ET_GESTURE_SCROLL_END; |
+ break; |
+ } |
+ |
+ const UGTransform *transform = |
+ grail_slice_get_transform(slice); |
+ |
+ GestureEvent::Properties props; |
+ props.delta_x = -(*transform)[0][2]; |
+ props.delta_y = -(*transform)[1][2]; |
+ |
+ GestureEvent * result = new GestureEvent(event_type, |
+ touch_event.x(), |
+ touch_event.y(), |
+ touch_event.flags(), |
+ base::Time::Now(), |
+ props); |
+ return linked_ptr<GestureEvent>(result); |
+ } |
+ |
+ linked_ptr<GestureEvent> ProcessPinch(UGSlice slice, |
+ const TouchEvent & touch_event) { |
+ ui::EventType event_type = ui::ET_UNKNOWN; |
+ switch (grail_slice_get_state(slice)) { |
+ case UGGestureStateBegin: |
+ event_type = ui::ET_GESTURE_PINCH_BEGIN; |
+ break; |
+ case UGGestureStateUpdate: |
+ event_type = ui::ET_GESTURE_PINCH_UPDATE; |
+ break; |
+ case UGGestureStateEnd: |
+ event_type = ui::ET_GESTURE_PINCH_END; |
+ break; |
+ } |
+ |
+ const UGTransform *transform = |
+ grail_slice_get_cumulative_transform(slice); |
+ |
+ GestureEvent::Properties props; |
+ props.delta_x = (*transform)[0][0]; |
+ props.delta_y = (*transform)[1][1]; |
+ props.scale_x = (*transform)[0][0]; |
+ props.scale_y = (*transform)[1][1]; |
+ |
+ GestureEvent * result = new GestureEvent(event_type, |
+ touch_event.x(), |
+ touch_event.y(), |
+ touch_event.flags(), |
+ base::Time::Now(), |
+ props); |
+ |
+ return linked_ptr<GestureEvent>(result); |
+ } |
+ |
+ linked_ptr<GestureEvent> ProcessTap(UGSlice slice, |
+ const TouchEvent & touch_event) { |
+ ui::EventType event_type = ui::ET_UNKNOWN; |
+ switch (grail_slice_get_state(slice)) { |
+ case UGGestureStateBegin: |
+ gesture_tap_start_ = base::Time::Now(); |
+ return linked_ptr<GestureEvent>(NULL); |
+ break; |
+ case UGGestureStateUpdate: |
+ return linked_ptr<GestureEvent>(NULL); |
+ break; |
+ case UGGestureStateEnd: { |
+ base::Time now = base::Time::Now(); |
+ base::TimeDelta dlp = now - gesture_tap_start_; |
+ base::TimeDelta ddt = now - last_gesture_tap_completed_; |
+ if (dlp >= kLongPressTimeThreshold) { |
+ event_type = ui::ET_GESTURE_LONG_PRESS; |
+ } else if (ddt < kDoubleTapTimeout) { |
+ event_type = ui::ET_GESTURE_DOUBLE_TAP; |
+ } else { |
+ event_type = ui::ET_GESTURE_TAP; |
+ last_gesture_tap_completed_ = now; |
+ } |
+ gesture_tap_start_ = base::Time::Now(); |
+ break; |
+ } |
+ } |
+ |
+ return linked_ptr<GestureEvent>(new GestureEvent( |
+ event_type, |
+ touch_event.x(), |
+ touch_event.y(), |
+ touch_event.flags(), |
+ base::Time::Now(), |
+ GestureEvent::Properties())); |
+ } |
+ |
+ int flags_; |
+ RootWindow* window_; |
+ |
+ base::Time last_gesture_tap_completed_; |
+ base::Time gesture_tap_start_; |
+ |
+ SubscriptionMap subscriptions_; |
+}; |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// GestureRecognizerGrail Public: |
+ |
+GestureRecognizerGrail::GestureRecognizerGrail(RootWindow* window) |
+ : d_(new Private(window)) { |
+} |
+ |
+GestureRecognizer::Gestures* |
+GestureRecognizerGrail::ProcessTouchEventForGesture( |
+ const TouchEvent& event, |
+ ui::TouchStatus status) { |
+ |
+ UFEvent ufEvent; |
+ while (frame_get_event( |
+ ui::TouchFactory::GetInstance()->handle(), |
+ &ufEvent) == |
+ UFStatusSuccess) { |
+ grail_process_frame_event( |
+ GrailHolder::GetInstance()->handle(), |
+ ufEvent); |
+ |
+ switch (frame_event_get_type(ufEvent)) { |
+ case UFEventTypeDeviceAdded: { |
+ UFDevice device = NULL; |
+ UFStatus status = frame_event_get_property(ufEvent, |
+ UFEventPropertyDevice, |
+ &device); |
+ |
+ if (status != UFStatusSuccess) |
+ LOG(ERROR) << "Failed to get device from event."; |
+ else |
+ SubscribeForGestures( |
+ GrailHolder::GetInstance()->handle(), |
+ device, |
+ ui::TouchFactory::GetInstance()->native_root_window_aura(), |
+ d_->subscriptions_[device]); |
+ |
+ break; |
+ } |
+ |
+ case UFEventTypeDeviceRemoved: { |
+ UFDevice device = NULL; |
+ UFStatus status = frame_event_get_property(ufEvent, |
+ UFEventPropertyDevice, |
+ &device); |
+ |
+ if (status != UFStatusSuccess) { |
+ LOG(ERROR) << "Failed to get device from event."; |
+ } else { |
+ Private::SubscriptionMap::iterator it = |
+ d_->subscriptions_.find(device); |
+ if (it != d_->subscriptions_.end()) { |
+ grail_subscription_delete(it->second); |
+ d_->subscriptions_.erase(it); |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+ |
+ frame_event_unref(ufEvent); |
+ } |
+ |
+ Gestures * result = new Gestures(); |
+ UGEvent ugEvent; |
+ while (grail_get_event(GrailHolder::GetInstance()->handle(), &ugEvent) == |
+ UGStatusSuccess) { |
+ switch (grail_event_get_type(ugEvent)) { |
+ case UGEventTypeSlice: { |
+ UGSlice slice; |
+ UGStatus status; |
+ status = grail_event_get_property(ugEvent, |
+ UGEventPropertySlice, &slice); |
+ if (status != UGStatusSuccess) { |
+ break; |
+ } |
+ |
+ d_->ProcessSlice(slice, |
+ grail_event_get_time(ugEvent), |
+ event, |
+ result); |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+ |
+ grail_event_unref(ugEvent); |
+ } |
+ |
+ return result; |
+} |
+ |
+// GestureRecognizer, static |
+GestureRecognizer* GestureRecognizer::Create(RootWindow* window) { |
+ return new GestureRecognizerGrail(window); |
+} |
+ |
+} // namespace aura |
+ |
Property changes on: ui/aura/gestures/gesture_recognizer_grail.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |