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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/aura/gestures/gesture_recognizer_grail.h ('k') | ui/aura/gestures/gesture_recognizer_grail_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « ui/aura/gestures/gesture_recognizer_grail.h ('k') | ui/aura/gestures/gesture_recognizer_grail_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698