OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ui/aura/gestures/gesture_recognizer_grail.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/memory/singleton.h" |
| 10 #include "base/time.h" |
| 11 #include "third_party/utouch-frame/include/utouch/frame.h" |
| 12 #include "third_party/utouch-frame/include/utouch/frame_x11.h" |
| 13 #include "third_party/utouch-grail/include/utouch/grail.h" |
| 14 #include "ui/aura/event.h" |
| 15 #include "ui/aura/gestures/gesture_configuration.h" |
| 16 #include "ui/aura/root_window_host_linux.h" |
| 17 #include "ui/aura/root_window.h" |
| 18 #include "ui/aura/window.h" |
| 19 #include "ui/base/events.h" |
| 20 #include "ui/base/touch/touch_factory.h" |
| 21 #include "ui/base/x/x11_util.h" |
| 22 |
| 23 namespace { |
| 24 |
| 25 static const base::TimeDelta kLongPressTimeThreshold |
| 26 = base::TimeDelta::FromMilliseconds(3000); |
| 27 |
| 28 static const base::TimeDelta kDoubleTapTimeout |
| 29 = base::TimeDelta::FromSeconds( |
| 30 aura::GestureConfiguration::max_seconds_between_double_click()); |
| 31 |
| 32 bool GetDeviceAndWindowFromEvent( |
| 33 const UFEvent event, |
| 34 UFDevice* device, |
| 35 UFWindowId* window_id) { |
| 36 |
| 37 UFFrame frame; |
| 38 UFStatus status = frame_event_get_property(event, UFEventPropertyFrame, |
| 39 &frame); |
| 40 if (status != UFStatusSuccess) { |
| 41 LOG(ERROR) << "failed to get frame from event\n"; |
| 42 return false; |
| 43 } |
| 44 |
| 45 *device = frame_frame_get_device(frame); |
| 46 *window_id = frame_frame_get_window_id(frame); |
| 47 |
| 48 char * deviceName; |
| 49 if (UFStatusSuccess == |
| 50 frame_device_get_property(*device, UFDevicePropertyName, &deviceName)) |
| 51 DLOG(INFO) << "Considering device: " << deviceName; |
| 52 else |
| 53 DLOG(INFO) << "Problem reading out device name."; |
| 54 |
| 55 return true; |
| 56 } |
| 57 |
| 58 // Subscribes to the grail instance (handle) |
| 59 // for two-finger taps, drags and pinches. Please |
| 60 // note that subscribing to the grail instance is non-blocking. |
| 61 // Returns an int with boolean semantics to indicate success or failure. |
| 62 int SubscribeForGestures( |
| 63 UGHandle handle, |
| 64 UFDevice device, |
| 65 ::Window window, |
| 66 UGSubscription subscription) { |
| 67 char * deviceName = NULL; |
| 68 if (UFStatusSuccess == |
| 69 frame_device_get_property(device, UFDevicePropertyName, &deviceName)) |
| 70 DLOG(INFO) << "Subscribing to device: " << deviceName; |
| 71 |
| 72 UGStatus status; |
| 73 |
| 74 int touches_start = 2; |
| 75 int touches_max = 2; |
| 76 int touches_min = 2; |
| 77 int atomic = 1; |
| 78 |
| 79 UFWindowId window_id = frame_x11_create_window_id(window); |
| 80 const UGGestureTypeMask mask = |
| 81 UGGestureTypeDrag | |
| 82 UGGestureTypePinch | |
| 83 UGGestureTypeTap; |
| 84 status = grail_subscription_new(&subscription); |
| 85 if (status != UGStatusSuccess) { |
| 86 LOG(ERROR) << "Failed to create subscription"; |
| 87 return 0; |
| 88 } |
| 89 |
| 90 status = grail_subscription_set_property(subscription, |
| 91 UGSubscriptionPropertyDevice, |
| 92 &device); |
| 93 if (status != UGStatusSuccess) { |
| 94 LOG(ERROR) << "Failed to set subscription device"; |
| 95 return 0; |
| 96 } |
| 97 |
| 98 status = grail_subscription_set_property(subscription, |
| 99 UGSubscriptionPropertyWindow, |
| 100 &window_id); |
| 101 if (status != UGStatusSuccess) { |
| 102 LOG(ERROR) << "Failed to set subscription window"; |
| 103 return 0; |
| 104 } |
| 105 |
| 106 status = grail_subscription_set_property(subscription, |
| 107 UGSubscriptionPropertyAtomicGestures, |
| 108 &atomic); |
| 109 if (status != UGStatusSuccess) { |
| 110 LOG(ERROR) << "Failed to set atomic gestures subscription property."; |
| 111 return 0; |
| 112 } |
| 113 |
| 114 status = grail_subscription_set_property(subscription, |
| 115 UGSubscriptionPropertyTouchesStart, |
| 116 &touches_start); |
| 117 if (status != UGStatusSuccess) { |
| 118 LOG(ERROR) << "Failed to set subscription start touches."; |
| 119 return 0; |
| 120 } |
| 121 |
| 122 status = grail_subscription_set_property(subscription, |
| 123 UGSubscriptionPropertyTouchesMaximum, |
| 124 &touches_max); |
| 125 if (status != UGStatusSuccess) { |
| 126 LOG(ERROR) << "Failed to set subscription start touches."; |
| 127 return 0; |
| 128 } |
| 129 |
| 130 status = grail_subscription_set_property(subscription, |
| 131 UGSubscriptionPropertyTouchesMinimum, |
| 132 &touches_min); |
| 133 if (status != UGStatusSuccess) { |
| 134 LOG(ERROR) << "Failed to set subscription min touches."; |
| 135 return 0; |
| 136 } |
| 137 |
| 138 status = grail_subscription_set_property(subscription, |
| 139 UGSubscriptionPropertyMask, |
| 140 &mask); |
| 141 if (status != UGStatusSuccess) { |
| 142 LOG(ERROR) << "Failed to set subscription mask"; |
| 143 return 0; |
| 144 } |
| 145 |
| 146 status = grail_subscription_activate(handle, subscription); |
| 147 if (status != UGStatusSuccess) { |
| 148 LOG(ERROR) << "Failed to activate subscription\n"; |
| 149 return 0; |
| 150 } |
| 151 |
| 152 DLOG(INFO) << "Successfully configured and activated grail subscription"; |
| 153 |
| 154 return 1; |
| 155 } |
| 156 |
| 157 } // namespace |
| 158 |
| 159 namespace aura { |
| 160 |
| 161 // Implements a singleton for a raw grail instance and |
| 162 // makes sure that RAII. |
| 163 class GrailHolder { |
| 164 public: |
| 165 // Returns the singleton instance |
| 166 static GrailHolder* GetInstance() { |
| 167 return Singleton<GrailHolder>::get(); |
| 168 } |
| 169 |
| 170 // Returns the handle managed by the instance |
| 171 UGHandle handle() const { |
| 172 return utouch_grail_handle_; |
| 173 } |
| 174 |
| 175 private: |
| 176 friend struct DefaultSingletonTraits<GrailHolder>; |
| 177 |
| 178 GrailHolder() |
| 179 : utouch_grail_handle_(NULL) { |
| 180 if (UGStatusSuccess != grail_new( |
| 181 ui::TouchFactory::GetInstance()->handle(), |
| 182 &utouch_grail_handle_)) { |
| 183 LOG(ERROR) << "Problem initializing grail api."; |
| 184 } else { |
| 185 fd_set set; |
| 186 FD_ZERO(&set); |
| 187 int fd = grail_get_fd(utouch_grail_handle_); |
| 188 FD_SET(fd, &set); |
| 189 } |
| 190 } |
| 191 |
| 192 ~GrailHolder() { |
| 193 if (utouch_grail_handle_ != NULL) |
| 194 grail_delete_v3(utouch_grail_handle_); |
| 195 } |
| 196 |
| 197 UGHandle utouch_grail_handle_; |
| 198 |
| 199 DISALLOW_COPY_AND_ASSIGN(GrailHolder); |
| 200 }; |
| 201 |
| 202 struct GestureRecognizerGrail::Private { |
| 203 typedef std::map<UFDevice, UGSubscription> SubscriptionMap; |
| 204 |
| 205 explicit Private(RootWindow* window) |
| 206 : flags_(0), |
| 207 window_(window) { |
| 208 } |
| 209 |
| 210 ~Private() { |
| 211 SubscriptionMap::iterator it; |
| 212 for (it = subscriptions_.begin(); |
| 213 it != subscriptions_.end(); |
| 214 ++it) { |
| 215 grail_subscription_delete(it->second); |
| 216 } |
| 217 } |
| 218 |
| 219 void UpdateFrameInstance(XEvent* xEvent) { |
| 220 if (xEvent != NULL) { |
| 221 if (UFStatusSuccess != |
| 222 frame_x11_process_event( |
| 223 ui::TouchFactory::GetInstance()->handle(), |
| 224 &xEvent->xcookie)) { |
| 225 LOG(ERROR) << "Failed to inject X event"; |
| 226 } |
| 227 } else { |
| 228 LOG(ERROR) << "Failed to extract native event"; |
| 229 } |
| 230 } |
| 231 |
| 232 void ProcessFrameEvents() { |
| 233 UFEvent ufEvent; |
| 234 while (frame_get_event( |
| 235 ui::TouchFactory::GetInstance()->handle(), |
| 236 &ufEvent) == |
| 237 UFStatusSuccess) { |
| 238 grail_process_frame_event( |
| 239 GrailHolder::GetInstance()->handle(), |
| 240 ufEvent); |
| 241 |
| 242 switch (frame_event_get_type(ufEvent)) { |
| 243 case UFEventTypeDeviceAdded: { |
| 244 UFDevice device = NULL; |
| 245 UFStatus status = frame_event_get_property(ufEvent, |
| 246 UFEventPropertyDevice, |
| 247 &device); |
| 248 |
| 249 if (status != UFStatusSuccess) |
| 250 LOG(ERROR) << "Failed to get device from event."; |
| 251 else |
| 252 SubscribeForGestures( |
| 253 GrailHolder::GetInstance()->handle(), |
| 254 device, |
| 255 window_->GetNativeWindow(), |
| 256 subscriptions_[device]); |
| 257 |
| 258 break; |
| 259 } |
| 260 |
| 261 case UFEventTypeDeviceRemoved: { |
| 262 UFDevice device = NULL; |
| 263 UFStatus status = frame_event_get_property(ufEvent, |
| 264 UFEventPropertyDevice, |
| 265 &device); |
| 266 |
| 267 if (status != UFStatusSuccess) { |
| 268 LOG(ERROR) << "Failed to get device from event."; |
| 269 } else { |
| 270 Private::SubscriptionMap::iterator it = |
| 271 subscriptions_.find(device); |
| 272 if (it != subscriptions_.end()) { |
| 273 grail_subscription_delete(it->second); |
| 274 subscriptions_.erase(it); |
| 275 } |
| 276 } |
| 277 break; |
| 278 } |
| 279 default: |
| 280 break; |
| 281 } |
| 282 |
| 283 frame_event_unref(ufEvent); |
| 284 } |
| 285 } |
| 286 |
| 287 void ProcessSlice( |
| 288 UGSlice slice, |
| 289 uint64_t time, |
| 290 const TouchEvent& event, |
| 291 GestureRecognizer::Gestures * result) { |
| 292 DCHECK(result != NULL); |
| 293 |
| 294 const UGGestureTypeMask recognized = grail_slice_get_recognized(slice); |
| 295 |
| 296 if (recognized & UGGestureTypePinch) { |
| 297 ProcessPinch(slice, event, result); |
| 298 } |
| 299 |
| 300 if (recognized & UGGestureTypeDrag) { |
| 301 ProcessDrag(slice, event, result); |
| 302 } |
| 303 |
| 304 if (recognized & UGGestureTypeTap) { |
| 305 ProcessTap(slice, event, result); |
| 306 } |
| 307 } |
| 308 |
| 309 void ProcessDrag(UGSlice slice, |
| 310 const TouchEvent& touch_event, |
| 311 Gestures* gestures) { |
| 312 DCHECK(gestures != NULL); |
| 313 ui::EventType event_type = ui::ET_UNKNOWN; |
| 314 |
| 315 switch (grail_slice_get_state(slice)) { |
| 316 case UGGestureStateBegin: |
| 317 event_type = ui::ET_GESTURE_SCROLL_BEGIN; |
| 318 break; |
| 319 case UGGestureStateUpdate: |
| 320 event_type = ui::ET_GESTURE_SCROLL_UPDATE; |
| 321 break; |
| 322 case UGGestureStateEnd: |
| 323 event_type = ui::ET_GESTURE_SCROLL_END; |
| 324 break; |
| 325 } |
| 326 |
| 327 const UGTransform *transform = |
| 328 grail_slice_get_transform(slice); |
| 329 |
| 330 GestureEvent::Properties props; |
| 331 props.delta_x = -(*transform)[0][2]; |
| 332 props.delta_y = -(*transform)[1][2]; |
| 333 |
| 334 GestureEvent * result = new GestureEvent(event_type, |
| 335 touch_event.x(), |
| 336 touch_event.y(), |
| 337 touch_event.flags(), |
| 338 base::Time::Now(), |
| 339 props); |
| 340 gestures->push_back(linked_ptr<GestureEvent>(result)); |
| 341 } |
| 342 |
| 343 void ProcessPinch(UGSlice slice, |
| 344 const TouchEvent & touch_event, |
| 345 Gestures* gestures) { |
| 346 DCHECK(gestures != NULL); |
| 347 ui::EventType event_type = ui::ET_UNKNOWN; |
| 348 switch (grail_slice_get_state(slice)) { |
| 349 case UGGestureStateBegin: |
| 350 event_type = ui::ET_GESTURE_PINCH_BEGIN; |
| 351 break; |
| 352 case UGGestureStateUpdate: |
| 353 event_type = ui::ET_GESTURE_PINCH_UPDATE; |
| 354 break; |
| 355 case UGGestureStateEnd: |
| 356 event_type = ui::ET_GESTURE_PINCH_END; |
| 357 break; |
| 358 } |
| 359 |
| 360 const UGTransform *transform = |
| 361 grail_slice_get_cumulative_transform(slice); |
| 362 |
| 363 GestureEvent::Properties props; |
| 364 props.delta_x = (*transform)[0][0]; |
| 365 props.delta_y = (*transform)[1][1]; |
| 366 props.scale_x = (*transform)[0][0]; |
| 367 props.scale_y = (*transform)[1][1]; |
| 368 |
| 369 GestureEvent * result = new GestureEvent(event_type, |
| 370 touch_event.x(), |
| 371 touch_event.y(), |
| 372 touch_event.flags(), |
| 373 base::Time::Now(), |
| 374 props); |
| 375 gestures->push_back(linked_ptr<GestureEvent>(result)); |
| 376 } |
| 377 |
| 378 void ProcessTap(UGSlice slice, |
| 379 const TouchEvent & touch_event, |
| 380 Gestures* gestures) { |
| 381 DCHECK(gestures != NULL); |
| 382 ui::EventType event_type = ui::ET_UNKNOWN; |
| 383 switch (grail_slice_get_state(slice)) { |
| 384 case UGGestureStateBegin: |
| 385 gesture_tap_start_ = base::Time::Now(); |
| 386 return; |
| 387 break; |
| 388 case UGGestureStateUpdate: |
| 389 return; |
| 390 break; |
| 391 case UGGestureStateEnd: { |
| 392 base::Time now = base::Time::Now(); |
| 393 base::TimeDelta dlp = now - gesture_tap_start_; |
| 394 base::TimeDelta ddt = now - last_gesture_tap_completed_; |
| 395 if (dlp >= kLongPressTimeThreshold) { |
| 396 event_type = ui::ET_GESTURE_LONG_PRESS; |
| 397 } else if (ddt < kDoubleTapTimeout) { |
| 398 event_type = ui::ET_GESTURE_DOUBLE_TAP; |
| 399 } else { |
| 400 event_type = ui::ET_GESTURE_TAP; |
| 401 last_gesture_tap_completed_ = now; |
| 402 } |
| 403 gesture_tap_start_ = base::Time::Now(); |
| 404 break; |
| 405 } |
| 406 } |
| 407 GestureEvent* result = new GestureEvent(event_type, |
| 408 touch_event.x(), |
| 409 touch_event.y(), |
| 410 touch_event.flags(), |
| 411 base::Time::Now(), |
| 412 GestureEvent::Properties()); |
| 413 gestures->push_back(linked_ptr<GestureEvent>(result)); |
| 414 } |
| 415 |
| 416 int flags_; |
| 417 RootWindow* window_; |
| 418 |
| 419 base::Time last_gesture_tap_completed_; |
| 420 base::Time gesture_tap_start_; |
| 421 |
| 422 SubscriptionMap subscriptions_; |
| 423 }; |
| 424 |
| 425 |
| 426 //////////////////////////////////////////////////////////////////////////////// |
| 427 // GestureRecognizerGrail Public: |
| 428 |
| 429 GestureRecognizerGrail::GestureRecognizerGrail(RootWindow* window) |
| 430 : d_(new Private(window)) { |
| 431 } |
| 432 |
| 433 GestureRecognizer::Gestures* |
| 434 GestureRecognizerGrail::ProcessTouchEventForGesture( |
| 435 const TouchEvent& event, |
| 436 ui::TouchStatus status) { |
| 437 |
| 438 d_->UpdateFrameInstance( |
| 439 static_cast<XEvent*>(event.native_event())); |
| 440 |
| 441 d_->ProcessFrameEvents(); |
| 442 |
| 443 Gestures * result = new Gestures(); |
| 444 |
| 445 UGEvent ugEvent; |
| 446 while (grail_get_event(GrailHolder::GetInstance()->handle(), &ugEvent) == |
| 447 UGStatusSuccess) { |
| 448 switch (grail_event_get_type(ugEvent)) { |
| 449 case UGEventTypeSlice: { |
| 450 UGSlice slice; |
| 451 UGStatus status; |
| 452 status = grail_event_get_property(ugEvent, |
| 453 UGEventPropertySlice, &slice); |
| 454 if (status != UGStatusSuccess) { |
| 455 break; |
| 456 } |
| 457 |
| 458 d_->ProcessSlice(slice, |
| 459 grail_event_get_time(ugEvent), |
| 460 event, |
| 461 result); |
| 462 break; |
| 463 } |
| 464 default: |
| 465 break; |
| 466 } |
| 467 |
| 468 grail_event_unref(ugEvent); |
| 469 } |
| 470 |
| 471 return result; |
| 472 } |
| 473 |
| 474 void GestureRecognizerGrail::QueueTouchEventForGesture( |
| 475 Window* /*window*/, |
| 476 const TouchEvent& /*event*/) { |
| 477 } |
| 478 |
| 479 GestureRecognizer::Gestures* GestureRecognizerGrail::AdvanceTouchQueue( |
| 480 Window* /*window*/, |
| 481 bool /*processed*/) { |
| 482 return NULL; |
| 483 } |
| 484 |
| 485 void GestureRecognizerGrail::FlushTouchQueue(Window* /*window*/) { |
| 486 } |
| 487 |
| 488 // GestureRecognizer, static |
| 489 GestureRecognizer* GestureRecognizer::Create(RootWindow* window) { |
| 490 return new GestureRecognizerGrail(window); |
| 491 } |
| 492 |
| 493 } // namespace aura |
| 494 |
OLD | NEW |