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,494 @@ |
+// 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 "base/logging.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/singleton.h" |
+#include "base/time.h" |
+#include "third_party/utouch-frame/include/utouch/frame.h" |
+#include "third_party/utouch-frame/include/utouch/frame_x11.h" |
+#include "third_party/utouch-grail/include/utouch/grail.h" |
+#include "ui/aura/event.h" |
+#include "ui/aura/gestures/gesture_configuration.h" |
+#include "ui/aura/root_window_host_linux.h" |
+#include "ui/aura/root_window.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 { |
+ |
+static const base::TimeDelta kLongPressTimeThreshold |
+= base::TimeDelta::FromMilliseconds(3000); |
+ |
+static const base::TimeDelta kDoubleTapTimeout |
+= base::TimeDelta::FromSeconds( |
+ aura::GestureConfiguration::max_seconds_between_double_click()); |
+ |
+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 UpdateFrameInstance(XEvent* xEvent) { |
+ if (xEvent != NULL) { |
+ if (UFStatusSuccess != |
+ frame_x11_process_event( |
+ ui::TouchFactory::GetInstance()->handle(), |
+ &xEvent->xcookie)) { |
+ LOG(ERROR) << "Failed to inject X event"; |
+ } |
+ } else { |
+ LOG(ERROR) << "Failed to extract native event"; |
+ } |
+ } |
+ |
+ void ProcessFrameEvents() { |
+ 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, |
+ window_->GetNativeWindow(), |
+ 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 = |
+ subscriptions_.find(device); |
+ if (it != subscriptions_.end()) { |
+ grail_subscription_delete(it->second); |
+ subscriptions_.erase(it); |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+ |
+ frame_event_unref(ufEvent); |
+ } |
+ } |
+ |
+ void ProcessSlice( |
+ UGSlice slice, |
+ uint64_t time, |
+ const TouchEvent& event, |
+ GestureRecognizer::Gestures * result) { |
+ DCHECK(result != NULL); |
+ |
+ const UGGestureTypeMask recognized = grail_slice_get_recognized(slice); |
+ |
+ if (recognized & UGGestureTypePinch) { |
+ ProcessPinch(slice, event, result); |
+ } |
+ |
+ if (recognized & UGGestureTypeDrag) { |
+ ProcessDrag(slice, event, result); |
+ } |
+ |
+ if (recognized & UGGestureTypeTap) { |
+ ProcessTap(slice, event, result); |
+ } |
+ } |
+ |
+ void ProcessDrag(UGSlice slice, |
+ const TouchEvent& touch_event, |
+ Gestures* gestures) { |
+ DCHECK(gestures != NULL); |
+ 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); |
+ gestures->push_back(linked_ptr<GestureEvent>(result)); |
+ } |
+ |
+ void ProcessPinch(UGSlice slice, |
+ const TouchEvent & touch_event, |
+ Gestures* gestures) { |
+ DCHECK(gestures != NULL); |
+ 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); |
+ gestures->push_back(linked_ptr<GestureEvent>(result)); |
+ } |
+ |
+ void ProcessTap(UGSlice slice, |
+ const TouchEvent & touch_event, |
+ Gestures* gestures) { |
+ DCHECK(gestures != NULL); |
+ ui::EventType event_type = ui::ET_UNKNOWN; |
+ switch (grail_slice_get_state(slice)) { |
+ case UGGestureStateBegin: |
+ gesture_tap_start_ = base::Time::Now(); |
+ return; |
+ break; |
+ case UGGestureStateUpdate: |
+ return; |
+ 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; |
+ } |
+ } |
+ GestureEvent* result = new GestureEvent(event_type, |
+ touch_event.x(), |
+ touch_event.y(), |
+ touch_event.flags(), |
+ base::Time::Now(), |
+ GestureEvent::Properties()); |
+ gestures->push_back(linked_ptr<GestureEvent>(result)); |
+ } |
+ |
+ 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) { |
+ |
+ d_->UpdateFrameInstance( |
+ static_cast<XEvent*>(event.native_event())); |
+ |
+ d_->ProcessFrameEvents(); |
+ |
+ 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; |
+} |
+ |
+void GestureRecognizerGrail::QueueTouchEventForGesture( |
+ Window* /*window*/, |
+ const TouchEvent& /*event*/) { |
+} |
+ |
+GestureRecognizer::Gestures* GestureRecognizerGrail::AdvanceTouchQueue( |
+ Window* /*window*/, |
+ bool /*processed*/) { |
+ return NULL; |
+} |
+ |
+void GestureRecognizerGrail::FlushTouchQueue(Window* /*window*/) { |
+} |
+ |
+// 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 |