blob: ba89427ab4bbb84edcc1c5bab841c3a8ef9b1359 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS 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 "gestures/include/util.h"
#include "gestures/include/gestures.h"
#include "gestures/include/logging.h"
#include "gestures/include/macros.h"
namespace gestures {
void CombineButtonsGestures(Gesture* gesture, const Gesture* addend);
// Returns a priority for a given gesture type. Lower number is more important.
int CombineGesturePriority(const Gesture* gesture) {
int priority = 0;
// Use a switch with no default so that if a new gesture type is added,
// we get a compiler warning here.
// Least important at top
switch (gesture->type) {
case kGestureTypeNull: priority++; // fallthrough
case kGestureTypeContactInitiated: priority++; // fallthrough
// Midrange, equal priority
case kGestureTypeFourFingerSwipe: // fallthrough
case kGestureTypePinch: // fallthrough
case kGestureTypeSwipe: // fallthrough
case kGestureTypeMove: // fallthrough
case kGestureTypeScroll: priority++; // fallthrough
case kGestureTypeFourFingerSwipeLift: priority++; // fallthrough
case kGestureTypeFling: priority++; // fallthrough
case kGestureTypeSwipeLift: priority++; // fallthrough
case kGestureTypeButtonsChange: priority++; // fallthrough
case kGestureTypeMetrics: priority++; // fallthrough
}
// Most important at bottom
return priority;
}
void CombineGestures(Gesture* gesture, const Gesture* addend) {
if (!gesture) {
Err("gesture must be non-NULL.");
return;
}
if (!addend)
return;
if (gesture->type == kGestureTypeNull) {
*gesture = *addend;
return;
}
if (gesture->type == addend->type) {
// Same type; merge them
switch (gesture->type) {
case kGestureTypeNull: // fallthrough
case kGestureTypeContactInitiated: // fallthrough
case kGestureTypeFling:
case kGestureTypeSwipeLift:
case kGestureTypeFourFingerSwipeLift:
case kGestureTypeMetrics:
break;
case kGestureTypeButtonsChange:
CombineButtonsGestures(gesture, addend);
break;
// These we actually combine:
case kGestureTypeMove:
gesture->details.move.dx += addend->details.move.dx;
gesture->details.move.dy += addend->details.move.dy;
break;
case kGestureTypeScroll:
gesture->details.scroll.dx += addend->details.scroll.dx;
gesture->details.scroll.dy += addend->details.scroll.dy;
break;
case kGestureTypeSwipe:
gesture->details.swipe.dx += addend->details.swipe.dx;
gesture->details.swipe.dy += addend->details.swipe.dy;
break;
case kGestureTypeFourFingerSwipe:
gesture->details.four_finger_swipe.dx +=
addend->details.four_finger_swipe.dx;
gesture->details.four_finger_swipe.dy +=
addend->details.four_finger_swipe.dy;
break;
case kGestureTypePinch:
gesture->details.pinch.dz *= addend->details.pinch.dz;
gesture->details.pinch.zoom_state = addend->details.pinch.zoom_state;
break;
}
gesture->start_time = std::min(gesture->start_time, addend->start_time);
gesture->end_time = std::max(gesture->end_time, addend->end_time);
return;
}
if (CombineGesturePriority(gesture) < CombineGesturePriority(addend)) {
Log("Losing gesture"); // losing 'addend'
return;
}
Log("Losing gesture"); // losing 'gesture'
*gesture = *addend;
}
void CombineButtonsGestures(Gesture* gesture, const Gesture* addend) {
// We have 2 button events. merge them
unsigned buttons[] = { GESTURES_BUTTON_LEFT,
GESTURES_BUTTON_MIDDLE,
GESTURES_BUTTON_RIGHT };
for (size_t i = 0; i < arraysize(buttons); ++i) {
unsigned button = buttons[i];
unsigned g_down = gesture->details.buttons.down & button;
unsigned g_up = gesture->details.buttons.up & button;
unsigned a_down = addend->details.buttons.down & button;
unsigned a_up = addend->details.buttons.up & button;
// How we merge buttons: Remember that a button gesture event can send
// some button down events, then button up events. Ideally we can combine
// them simply: e.g. if |gesture| has button down and |addend| has button
// up, we can put those both into |gesture|. If there is a conflict (e.g.
// button up followed by button down/up), there is no proper way to
// represent that in a single gesture. We work around that case by removing
// pairs of down/up, so in the example just given, the result would be just
// button up. There is one exception to these two rules: if |gesture| is
// button up, and |addend| is button down, combing them into one gesture
// would mean a click, because when executing the gestures, the down
// actions happen before the up. So for that case, we just remove all
// button action.
if (!g_down && g_up && a_down && !a_up) {
// special case
g_down = 0;
g_up = 0;
} else if ((g_down & a_down) | (g_up & a_up)) {
// If we have a conflict, this logic seems to remove the full click.
g_down = (~(g_down ^ a_down)) & button;
g_up = (~(g_up ^ a_up)) & button;
} else {
// Non-conflict case
g_down |= a_down;
g_up |= a_up;
}
gesture->details.buttons.down =
(gesture->details.buttons.down & ~button) | g_down;
gesture->details.buttons.up =
(gesture->details.buttons.up & ~button) | g_up;
}
if (!gesture->details.buttons.down && !gesture->details.buttons.up)
*gesture = Gesture();
}
} // namespace gestures