• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The ChromiumOS Authors
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 "include/multitouch_mouse_interpreter.h"
6 
7 #include <algorithm>
8 
9 #include "include/tracer.h"
10 #include "include/util.h"
11 
12 namespace gestures {
13 
PushGesture(const Gesture & result)14 void Origin::PushGesture(const Gesture& result) {
15   if (result.type == kGestureTypeButtonsChange) {
16     if (result.details.buttons.up & GESTURES_BUTTON_LEFT)
17       button_going_up_left_ = result.end_time;
18     if (result.details.buttons.up & GESTURES_BUTTON_MIDDLE)
19       button_going_up_middle_ = result.end_time;
20     if (result.details.buttons.up & GESTURES_BUTTON_RIGHT)
21       button_going_up_right_ = result.end_time;
22   }
23 }
24 
ButtonGoingUp(int button) const25 stime_t Origin::ButtonGoingUp(int button) const {
26   if (button == GESTURES_BUTTON_LEFT)
27     return button_going_up_left_;
28   if (button == GESTURES_BUTTON_MIDDLE)
29     return button_going_up_middle_;
30   if (button == GESTURES_BUTTON_RIGHT)
31     return button_going_up_right_;
32   return 0;
33 }
34 
MultitouchMouseInterpreter(PropRegistry * prop_reg,Tracer * tracer)35 MultitouchMouseInterpreter::MultitouchMouseInterpreter(
36     PropRegistry* prop_reg,
37     Tracer* tracer)
38     : MouseInterpreter(prop_reg, tracer),
39       state_buffer_(2),
40       scroll_buffer_(15),
41       prev_gesture_type_(kGestureTypeNull),
42       current_gesture_type_(kGestureTypeNull),
43       should_fling_(false),
44       scroll_manager_(prop_reg),
45       click_buffer_depth_(prop_reg, "Click Buffer Depth", 10),
46       click_max_distance_(prop_reg, "Click Max Distance", 1.0),
47       click_left_button_going_up_lead_time_(prop_reg,
48           "Click Left Button Going Up Lead Time", 0.01),
49       click_right_button_going_up_lead_time_(prop_reg,
50           "Click Right Button Going Up Lead Time", 0.1),
51       min_finger_move_distance_(prop_reg, "Minimum Mouse Finger Move Distance",
52                                 1.75),
53       moving_min_rel_amount_(prop_reg, "Moving Min Rel Magnitude", 0.1) {
54   InitName();
55   memset(&prev_state_, 0, sizeof(prev_state_));
56 }
57 
ProduceGesture(const Gesture & gesture)58 void MultitouchMouseInterpreter::ProduceGesture(const Gesture& gesture) {
59   origin_.PushGesture(gesture);
60   MouseInterpreter::ProduceGesture(gesture);
61 }
62 
SyncInterpretImpl(HardwareState * hwstate,stime_t * timeout)63 void MultitouchMouseInterpreter::SyncInterpretImpl(HardwareState* hwstate,
64                                                        stime_t* timeout) {
65   if (!state_buffer_.Get(0)->fingers) {
66     Err("Must call SetHardwareProperties() before interpreting anything.");
67     return;
68   }
69 
70   // Should we remove all fingers from our structures, or just removed ones?
71   if ((hwstate->rel_x * hwstate->rel_x + hwstate->rel_y * hwstate->rel_y) >
72       moving_min_rel_amount_.val_ * moving_min_rel_amount_.val_) {
73     start_position_.clear();
74     moving_.clear();
75     should_fling_ = false;
76   } else {
77     RemoveMissingIdsFromMap(&start_position_, *hwstate);
78     RemoveMissingIdsFromSet(&moving_, *hwstate);
79   }
80 
81   // Set start positions/moving
82   for (size_t i = 0; i < hwstate->finger_cnt; i++) {
83     const FingerState& fs = hwstate->fingers[i];
84     if (MapContainsKey(start_position_, fs.tracking_id)) {
85       // Is moving?
86       if (!SetContainsValue(moving_, fs.tracking_id) &&  // not already moving &
87           start_position_[fs.tracking_id].Sub(Vector2(fs)).MagSq() >=  // moving
88           min_finger_move_distance_.val_ * min_finger_move_distance_.val_) {
89         moving_.insert(fs.tracking_id);
90       }
91       continue;
92     }
93     start_position_[fs.tracking_id] = Vector2(fs);
94   }
95 
96   // Mark all non-moving fingers as unable to cause scroll
97   for (size_t i = 0; i < hwstate->finger_cnt; i++) {
98     FingerState* fs = &hwstate->fingers[i];
99     if (!SetContainsValue(moving_, fs->tracking_id))
100       fs->flags |=
101           GESTURES_FINGER_WARP_X_NON_MOVE | GESTURES_FINGER_WARP_Y_NON_MOVE;
102   }
103 
104   // Record current HardwareState now.
105   state_buffer_.PushState(*hwstate);
106 
107   // TODO(clchiou): Remove palm and thumb.
108   gs_fingers_.clear();
109   size_t num_fingers = std::min(kMaxGesturingFingers,
110                                 (size_t)state_buffer_.Get(0)->finger_cnt);
111   const FingerState* fs = state_buffer_.Get(0)->fingers;
112   for (size_t i = 0; i < num_fingers; i++)
113     gs_fingers_.insert(fs[i].tracking_id);
114 
115   InterpretScrollWheelEvent(*hwstate, true);
116   InterpretScrollWheelEvent(*hwstate, false);
117   InterpretMouseButtonEvent(prev_state_, *state_buffer_.Get(0));
118   InterpretMouseMotionEvent(prev_state_, *state_buffer_.Get(0));
119 
120   bool should_interpret_multitouch = true;
121 
122   // Some mice (Logitech) will interleave finger data and rel data, which can
123   // make finger tracking tricky. To avoid problems, if this current frame
124   // was rel data, and the previous finger data exactly matches this finger
125   // data, we remove the last hardware state from our buffer. This is okay
126   // because we already processed the rel data.
127   if (state_buffer_.Get(0) && state_buffer_.Get(1)) {
128     HardwareState* prev_hs = state_buffer_.Get(1);
129     HardwareState* cur_hs = state_buffer_.Get(0);
130     bool cur_has_rel = cur_hs->rel_x || cur_hs->rel_y ||
131         cur_hs->rel_wheel || cur_hs->rel_hwheel;
132     bool different_fingers = prev_hs->touch_cnt != cur_hs->touch_cnt ||
133         prev_hs->finger_cnt != cur_hs->finger_cnt;
134     if (!different_fingers && cur_has_rel) {
135       // Compare actual fingers themselves
136       for (size_t i = 0; i < cur_hs->finger_cnt; i++) {
137         if (!cur_hs->fingers[i].NonFlagsEquals(prev_hs->fingers[i])) {
138           different_fingers = true;
139           break;
140         }
141       }
142       if (!different_fingers) {
143         state_buffer_.PopState();
144         should_interpret_multitouch = false;
145       }
146     }
147   }
148 
149   if (should_interpret_multitouch)
150     InterpretMultitouchEvent();
151 
152   // We don't keep finger data here, this is just for standard mouse:
153   prev_state_ = *hwstate;
154   prev_state_.fingers = NULL;
155   prev_state_.finger_cnt = 0;
156 
157   prev_gs_fingers_ = gs_fingers_;
158   prev_gesture_type_ = current_gesture_type_;
159 }
160 
Initialize(const HardwareProperties * hw_props,Metrics * metrics,MetricsProperties * mprops,GestureConsumer * consumer)161 void MultitouchMouseInterpreter::Initialize(
162     const HardwareProperties* hw_props,
163     Metrics* metrics,
164     MetricsProperties* mprops,
165     GestureConsumer* consumer) {
166   Interpreter::Initialize(hw_props, metrics, mprops, consumer);
167   state_buffer_.Reset(hw_props->max_finger_cnt);
168 }
169 
InterpretMultitouchEvent()170 void MultitouchMouseInterpreter::InterpretMultitouchEvent() {
171   Gesture result;
172 
173   // If a gesturing finger just left, do fling/lift
174   if (should_fling_ && AnyGesturingFingerLeft(*state_buffer_.Get(0),
175                                               prev_gs_fingers_)) {
176     current_gesture_type_ = kGestureTypeFling;
177     scroll_manager_.FillResultFling(state_buffer_, scroll_buffer_, &result);
178     if (result.type == kGestureTypeFling)
179       result.details.fling.vx = 0.0;
180     if (result.details.fling.vy == 0.0)
181       result.type = kGestureTypeNull;
182     should_fling_ = false;
183   } else if (gs_fingers_.size() > 0) {
184     // In general, finger movements are interpreted as scroll, but as
185     // clicks and scrolls on multi-touch mice are both single-finger
186     // gesture, we have to recognize and separate clicks from scrolls,
187     // when a user is actually clicking.
188     //
189     // This is how we do for now: We look for characteristic patterns of
190     // clicks, and if we find one, we hold off emitting scroll gesture for
191     // a few time frames to prevent premature scrolls.
192     //
193     // The patterns we look for:
194     // * Small finger movements when button is down
195     // * Finger movements after button goes up
196 
197     bool update_scroll_buffer =
198         scroll_manager_.FillResultScroll(state_buffer_,
199                                       prev_gs_fingers_,
200                                       gs_fingers_,
201                                       prev_gesture_type_,
202                                       prev_result_,
203                                       &result,
204                                       &scroll_buffer_);
205     current_gesture_type_ = result.type;
206     if (current_gesture_type_ == kGestureTypeScroll)
207       should_fling_ = true;
208 
209     bool hold_off_scroll = false;
210     const HardwareState& state = *state_buffer_.Get(0);
211     // Check small finger movements when button is down
212     if (state.buttons_down) {
213       float dist_sq, dt;
214       scroll_buffer_.GetSpeedSq(click_buffer_depth_.val_, &dist_sq, &dt);
215       if (dist_sq < click_max_distance_.val_ * click_max_distance_.val_)
216         hold_off_scroll = true;
217     }
218     // Check button going up lead time
219     stime_t now = state.timestamp;
220     stime_t button_left_age =
221         now - origin_.ButtonGoingUp(GESTURES_BUTTON_LEFT);
222     stime_t button_right_age =
223         now - origin_.ButtonGoingUp(GESTURES_BUTTON_RIGHT);
224     hold_off_scroll = hold_off_scroll ||
225         (button_left_age < click_left_button_going_up_lead_time_.val_) ||
226         (button_right_age < click_right_button_going_up_lead_time_.val_);
227 
228     if (hold_off_scroll && result.type == kGestureTypeScroll) {
229       current_gesture_type_ = kGestureTypeNull;
230       result.type = kGestureTypeNull;
231     }
232     if (current_gesture_type_ == kGestureTypeScroll &&
233         !update_scroll_buffer) {
234       return;
235     }
236   }
237   scroll_manager_.UpdateScrollEventBuffer(current_gesture_type_,
238                                           &scroll_buffer_);
239   if (result.type != kGestureTypeNull)
240     ProduceGesture(result);
241   prev_result_ = result;
242 }
243 
244 }  // namespace gestures
245