| 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
|
|
|
|
|