Chromium Code Reviews| 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,452 @@ |
| +// 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 "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/browser_list.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "content/public/browser/web_contents_view.h" |
| +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| +#include "ui/aura/event.h" |
| +#include "ui/aura/window.h" |
| +#include "ui/base/events.h" |
| +#include "ui/base/x/x11_util.h" |
| +#include "ui/base/touch/touch_factory.h" |
| + |
| +namespace { |
| + |
| +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; |
| +} |
| + |
| +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); |
|
Elliot Glaysher
2012/03/28 17:49:00
I know nothing about grail. I assume this all runs
|
| + 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 { |
| + |
| +class GrailHolder { |
| + public: |
| + static GrailHolder* GetInstance() { |
| + return Singleton<GrailHolder>::get(); |
| + } |
| + |
| + 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); |
| + |
| + 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 |