1 // Copyright 2012 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/scaling_filter_interpreter.h"
6
7 #include <math.h>
8
9 #include "include/gestures.h"
10 #include "include/interpreter.h"
11 #include "include/logging.h"
12 #include "include/tracer.h"
13
14 namespace gestures {
15
16 // Takes ownership of |next|:
ScalingFilterInterpreter(PropRegistry * prop_reg,Interpreter * next,Tracer * tracer,GestureInterpreterDeviceClass devclass)17 ScalingFilterInterpreter::ScalingFilterInterpreter(
18 PropRegistry* prop_reg, Interpreter* next, Tracer* tracer,
19 GestureInterpreterDeviceClass devclass)
20 : FilterInterpreter(NULL, next, tracer, false),
21 tp_x_scale_(1.0),
22 tp_y_scale_(1.0),
23 tp_x_translate_(0.0),
24 tp_y_translate_(0.0),
25 screen_x_scale_(1.0),
26 screen_y_scale_(1.0),
27 orientation_scale_(1.0),
28 invert_scrolling_and_swiping_(prop_reg, "Australian Scrolling", false),
29 invert_scrolling_only_(prop_reg, "Invert Scrolling", false),
30 surface_area_from_pressure_(prop_reg,
31 "Compute Surface Area from Pressure", true),
32 use_touch_size_for_haptic_pad_(
33 prop_reg,
34 "Compute Surface Area from Touch Size for Haptic Pads",
35 false),
36 tp_x_bias_(prop_reg, "Touchpad Device Output Bias on X-Axis", 0.0),
37 tp_y_bias_(prop_reg, "Touchpad Device Output Bias on Y-Axis", 0.0),
38 pressure_scale_(prop_reg, "Pressure Calibration Slope", 1.0),
39 pressure_translate_(prop_reg, "Pressure Calibration Offset", 0.0),
40 pressure_threshold_(prop_reg, "Pressure Minimum Threshold", 0.0),
41 filter_low_pressure_(prop_reg, "Filter Low Pressure", false),
42 force_touch_count_to_match_finger_count_(
43 prop_reg,
44 "Force Touch Count To Match Finger Count",
45 false),
46 mouse_cpi_(prop_reg, "Mouse CPI", 1000.0),
47 device_mouse_(prop_reg, "Device Mouse", IsMouseDevice(devclass)),
48 device_pointing_stick_(prop_reg, "Device Pointing Stick",
49 IsPointingStick(devclass)),
50 device_touchpad_(prop_reg,
51 "Device Touchpad",
52 IsTouchpadDevice(devclass)) {
53 InitName();
54 }
55
SyncInterpretImpl(HardwareState * hwstate,stime_t * timeout)56 void ScalingFilterInterpreter::SyncInterpretImpl(HardwareState* hwstate,
57 stime_t* timeout) {
58 ScaleHardwareState(hwstate);
59 next_->SyncInterpret(hwstate, timeout);
60 }
61
62 // Ignore the finger events with low pressure values especially for the SEMI_MT
63 // devices such as Synaptics touchpad on Cr-48.
FilterLowPressure(HardwareState * hwstate)64 void ScalingFilterInterpreter::FilterLowPressure(HardwareState* hwstate) {
65 unsigned short finger_cnt = hwstate->finger_cnt;
66 unsigned short touch_cnt = hwstate->touch_cnt;
67 float threshold = 0.0;
68
69 // If a button is down, only filter 0 pressure fingers:
70 // Pressing fingers might have low pressure, but hovering fingers are
71 // reported as fingers with 0 pressure and should still be filtered.
72 if (pressure_scale_.val_ > 0.0 && !hwstate->buttons_down) {
73 threshold = (pressure_threshold_.val_ - pressure_translate_.val_)
74 / pressure_scale_.val_ ;
75 }
76 for (short i = finger_cnt - 1 ; i >= 0; i--) {
77 if (hwstate->fingers[i].pressure <= threshold) {
78 if (i != finger_cnt - 1)
79 hwstate->fingers[i] = hwstate->fingers[finger_cnt - 1];
80 finger_cnt--;
81 if (touch_cnt > 0)
82 touch_cnt--;
83 }
84 }
85 hwstate->finger_cnt = finger_cnt;
86 hwstate->touch_cnt = touch_cnt;
87 }
88
FilterZeroArea(HardwareState * hwstate)89 void ScalingFilterInterpreter::FilterZeroArea(HardwareState* hwstate) {
90 unsigned short finger_cnt = hwstate->finger_cnt;
91 unsigned short touch_cnt = hwstate->touch_cnt;
92 for (short i = finger_cnt - 1 ; i >= 0; i--) {
93 if (hwstate->fingers[i].pressure == 0.0) {
94 if (i != finger_cnt - 1)
95 hwstate->fingers[i] = hwstate->fingers[finger_cnt - 1];
96 finger_cnt--;
97 if (touch_cnt > 0)
98 touch_cnt--;
99 }
100 }
101 hwstate->finger_cnt = finger_cnt;
102 hwstate->touch_cnt = touch_cnt;
103 }
104
IsMouseDevice(GestureInterpreterDeviceClass devclass)105 bool ScalingFilterInterpreter::IsMouseDevice(
106 GestureInterpreterDeviceClass devclass) {
107 return (devclass == GESTURES_DEVCLASS_MOUSE ||
108 devclass == GESTURES_DEVCLASS_MULTITOUCH_MOUSE);
109 }
110
IsPointingStick(GestureInterpreterDeviceClass devclass)111 bool ScalingFilterInterpreter::IsPointingStick(
112 GestureInterpreterDeviceClass devclass) {
113 return devclass == GESTURES_DEVCLASS_POINTING_STICK;
114 }
115
IsTouchpadDevice(GestureInterpreterDeviceClass devclass)116 bool ScalingFilterInterpreter::IsTouchpadDevice(
117 GestureInterpreterDeviceClass devclass) {
118 return (devclass == GESTURES_DEVCLASS_TOUCHPAD ||
119 devclass == GESTURES_DEVCLASS_MULTITOUCH_MOUSE ||
120 devclass == GESTURES_DEVCLASS_TOUCHSCREEN);
121 }
122
ScaleHardwareState(HardwareState * hwstate)123 void ScalingFilterInterpreter::ScaleHardwareState(HardwareState* hwstate) {
124 if (device_touchpad_.val_)
125 ScaleTouchpadHardwareState(hwstate);
126 if (device_mouse_.val_ || device_pointing_stick_.val_)
127 ScaleMouseHardwareState(hwstate);
128 }
129
ScaleMouseHardwareState(HardwareState * hwstate)130 void ScalingFilterInterpreter::ScaleMouseHardwareState(
131 HardwareState* hwstate) {
132 hwstate->rel_x = hwstate->rel_x / mouse_cpi_.val_ * 25.4;
133 hwstate->rel_y = hwstate->rel_y / mouse_cpi_.val_ * 25.4;
134 // TODO(clchiou): Scale rel_wheel and rel_hwheel
135 }
136
ScaleTouchpadHardwareState(HardwareState * hwstate)137 void ScalingFilterInterpreter::ScaleTouchpadHardwareState(
138 HardwareState* hwstate) {
139 if (force_touch_count_to_match_finger_count_.val_)
140 hwstate->touch_cnt = hwstate->finger_cnt;
141
142 if (surface_area_from_pressure_.val_) {
143 // Drop the small fingers, i.e. low pressures.
144 if (filter_low_pressure_.val_ || pressure_threshold_.val_ > 0.0)
145 FilterLowPressure(hwstate);
146 }
147
148 for (short i = 0; i < hwstate->finger_cnt; i++) {
149 float cos_2_orit = 0.0, sin_2_orit = 0.0, rx_2 = 0.0, ry_2 = 0.0;
150 hwstate->fingers[i].position_x *= tp_x_scale_;
151 hwstate->fingers[i].position_x += tp_x_translate_;
152 hwstate->fingers[i].position_y *= tp_y_scale_;
153 hwstate->fingers[i].position_y += tp_y_translate_;
154
155 // TODO(clchiou): Output orientation is computed on a pixel-unit circle,
156 // and it is only equal to the orientation computed on a mm-unit circle
157 // when tp_x_scale_ == tp_y_scale_. Since what we really want is the
158 // latter, fix this!
159 hwstate->fingers[i].orientation *= orientation_scale_;
160
161 if (hwstate->fingers[i].touch_major || hwstate->fingers[i].touch_minor) {
162 cos_2_orit = cosf(hwstate->fingers[i].orientation);
163 cos_2_orit *= cos_2_orit;
164 sin_2_orit = sinf(hwstate->fingers[i].orientation);
165 sin_2_orit *= sin_2_orit;
166 rx_2 = tp_x_scale_ * tp_x_scale_;
167 ry_2 = tp_y_scale_ * tp_y_scale_;
168 }
169 if (hwstate->fingers[i].touch_major) {
170 float bias = tp_x_bias_.val_ * sin_2_orit + tp_y_bias_.val_ * cos_2_orit;
171 hwstate->fingers[i].touch_major =
172 fabsf(hwstate->fingers[i].touch_major - bias) *
173 sqrtf(rx_2 * sin_2_orit + ry_2 * cos_2_orit);
174 }
175 if (hwstate->fingers[i].touch_minor) {
176 float bias = tp_x_bias_.val_ * cos_2_orit + tp_y_bias_.val_ * sin_2_orit;
177 hwstate->fingers[i].touch_minor =
178 fabsf(hwstate->fingers[i].touch_minor - bias) *
179 sqrtf(rx_2 * cos_2_orit + ry_2 * sin_2_orit);
180 }
181
182 // After calibration, touch_major could be smaller than touch_minor.
183 // If so, swap them here and update orientation.
184 if (orientation_scale_ &&
185 hwstate->fingers[i].touch_major < hwstate->fingers[i].touch_minor) {
186 std::swap(hwstate->fingers[i].touch_major,
187 hwstate->fingers[i].touch_minor);
188 if (hwstate->fingers[i].orientation > 0.0)
189 hwstate->fingers[i].orientation -= M_PI_2;
190 else
191 hwstate->fingers[i].orientation += M_PI_2;
192 }
193
194 if (surface_area_from_pressure_.val_) {
195 hwstate->fingers[i].pressure *= pressure_scale_.val_;
196 hwstate->fingers[i].pressure += pressure_translate_.val_;
197 } else {
198 if (hwstate->fingers[i].touch_major && hwstate->fingers[i].touch_minor)
199 hwstate->fingers[i].pressure = M_PI_4 *
200 hwstate->fingers[i].touch_major * hwstate->fingers[i].touch_minor;
201 else if (hwstate->fingers[i].touch_major)
202 hwstate->fingers[i].pressure = M_PI_4 *
203 hwstate->fingers[i].touch_major * hwstate->fingers[i].touch_major;
204 else
205 hwstate->fingers[i].pressure = 0;
206 }
207
208 hwstate->fingers[i].pressure = std::max(1.0f,
209 hwstate->fingers[i].pressure);
210 }
211
212 if (!surface_area_from_pressure_.val_) {
213 FilterZeroArea(hwstate);
214 }
215 }
216
ConsumeGesture(const Gesture & gs)217 void ScalingFilterInterpreter::ConsumeGesture(const Gesture& gs) {
218 Gesture copy = gs;
219 switch (copy.type) {
220 case kGestureTypeMove: {
221 int original_rel_x =
222 copy.details.move.ordinal_dx * mouse_cpi_.val_ / 25.4;
223 int original_rel_y =
224 copy.details.move.ordinal_dy * mouse_cpi_.val_ / 25.4;
225 copy.details.move.dx *= screen_x_scale_;
226 copy.details.move.dy *= screen_y_scale_;
227 copy.details.move.ordinal_dx *= screen_x_scale_;
228 copy.details.move.ordinal_dy *= screen_y_scale_;
229 // Special case of motion: if a mouse move of 1 device unit
230 // (rel_[xy] == 1) would move the cursor on the screen > 1
231 // pixel, force it to just one pixel. This prevents low-DPI mice
232 // from jumping 2 pixels at a time when doing slow moves.
233 // Note, we use 1 / 1.2 = 0.8333 instead of 1 for the number of
234 // screen pixels, as external monitors get a 20% distance boost.
235 // Mice are most commonly used w/ external displays.
236 if (device_mouse_.val_ &&
237 ((original_rel_x == 0) != (original_rel_y == 0))) {
238 const double kMinPixels = 1.0 / 1.2;
239 if (fabs(copy.details.move.dx) > kMinPixels &&
240 abs(original_rel_x) == 1) {
241 copy.details.move.dx = copy.details.move.ordinal_dx =
242 copy.details.move.dx > 0.0 ? kMinPixels : -kMinPixels;
243 }
244 if (fabs(copy.details.move.dy) > kMinPixels &&
245 abs(original_rel_y) == 1) {
246 copy.details.move.dy = copy.details.move.ordinal_dy =
247 copy.details.move.dy > 0.0 ? kMinPixels : -kMinPixels;
248 }
249 }
250 break;
251 }
252 case kGestureTypeScroll:
253 if (device_touchpad_.val_) {
254 copy.details.scroll.dx *= screen_x_scale_;
255 copy.details.scroll.dy *= screen_y_scale_;
256 copy.details.scroll.ordinal_dx *= screen_x_scale_;
257 copy.details.scroll.ordinal_dy *= screen_y_scale_;
258 }
259 if (!(invert_scrolling_and_swiping_.val_ ||
260 invert_scrolling_only_.val_)) {
261 copy.details.scroll.dx *= -1;
262 copy.details.scroll.dy *= -1;
263 copy.details.scroll.ordinal_dx *= -1;
264 copy.details.scroll.ordinal_dy *= -1;
265 }
266 break;
267 case kGestureTypeMouseWheel:
268 if (!(invert_scrolling_and_swiping_.val_ ||
269 invert_scrolling_only_.val_)) {
270 copy.details.wheel.dx *= -1;
271 copy.details.wheel.dy *= -1;
272 copy.details.wheel.tick_120ths_dx *= -1;
273 copy.details.wheel.tick_120ths_dy *= -1;
274 }
275 break;
276 case kGestureTypeFling:
277 copy.details.fling.vx *= screen_x_scale_;
278 copy.details.fling.vy *= screen_y_scale_;
279 copy.details.fling.ordinal_vx *= screen_x_scale_;
280 copy.details.fling.ordinal_vy *= screen_y_scale_;
281 if (!(invert_scrolling_and_swiping_.val_ ||
282 invert_scrolling_only_.val_)) {
283 copy.details.fling.vx *= -1;
284 copy.details.fling.vy *= -1;
285 copy.details.fling.ordinal_vx *= -1;
286 copy.details.fling.ordinal_vy *= -1;
287 }
288 break;
289 case kGestureTypeSwipe:
290 // Scale swipes, as we want them to follow the pointer speed.
291 copy.details.swipe.dx *= screen_x_scale_;
292 copy.details.swipe.dy *= screen_y_scale_;
293 copy.details.swipe.ordinal_dx *= screen_x_scale_;
294 copy.details.swipe.ordinal_dy *= screen_y_scale_;
295 if (!invert_scrolling_and_swiping_.val_) {
296 copy.details.swipe.dy *= -1;
297 copy.details.swipe.ordinal_dy *= -1;
298 }
299 break;
300 case kGestureTypeFourFingerSwipe:
301 // Scale swipes, as we want them to follow the pointer speed.
302 copy.details.four_finger_swipe.dx *= screen_x_scale_;
303 copy.details.four_finger_swipe.dy *= screen_y_scale_;
304 copy.details.four_finger_swipe.ordinal_dx *= screen_x_scale_;
305 copy.details.four_finger_swipe.ordinal_dy *= screen_y_scale_;
306 if (!invert_scrolling_and_swiping_.val_) {
307 copy.details.four_finger_swipe.dy *= -1;
308 copy.details.four_finger_swipe.ordinal_dy *= -1;
309 }
310 break;
311 default:
312 break;
313 }
314 ProduceGesture(copy);
315 }
316
Initialize(const HardwareProperties * hwprops,Metrics * metrics,MetricsProperties * mprops,GestureConsumer * consumer)317 void ScalingFilterInterpreter::Initialize(const HardwareProperties* hwprops,
318 Metrics* metrics,
319 MetricsProperties* mprops,
320 GestureConsumer* consumer) {
321 // If the touchpad doesn't specify its resolution in one or both axes, use a
322 // reasonable default instead.
323 float res_x = hwprops->res_x != 0 ? hwprops->res_x : 32;
324 float res_y = hwprops->res_y != 0 ? hwprops->res_y : 32;
325
326 tp_x_scale_ = 1.0 / res_x;
327 tp_y_scale_ = 1.0 / res_y;
328 tp_x_translate_ = -1.0 * (hwprops->left * tp_x_scale_);
329 tp_y_translate_ = -1.0 * (hwprops->top * tp_y_scale_);
330
331 screen_x_scale_ = 133 / 25.4;
332 screen_y_scale_ = 133 / 25.4;
333
334 if (hwprops->orientation_maximum)
335 orientation_scale_ =
336 M_PI / (hwprops->orientation_maximum -
337 hwprops->orientation_minimum + 1);
338 else
339 orientation_scale_ = 0.0; // no orientation is provided
340
341 float friendly_orientation_minimum;
342 float friendly_orientation_maximum;
343 if (orientation_scale_) {
344 friendly_orientation_minimum =
345 orientation_scale_ * hwprops->orientation_minimum;
346 friendly_orientation_maximum =
347 orientation_scale_ * hwprops->orientation_maximum;
348 } else {
349 friendly_orientation_minimum = 0.0;
350 friendly_orientation_maximum = 0.0;
351 }
352
353 // For haptic touchpads, the pressure field is actual force in grams, not an
354 // estimate of surface area.
355 if (hwprops->is_haptic_pad && use_touch_size_for_haptic_pad_.val_)
356 surface_area_from_pressure_.val_ = false;
357
358 // Make fake idealized hardware properties to report to next_.
359 friendly_props_ = {
360 0.0, // left
361 0.0, // top
362 (hwprops->right - hwprops->left) * tp_x_scale_, // right
363 (hwprops->bottom - hwprops->top) * tp_y_scale_, // bottom
364 1.0, // X pixels/mm
365 1.0, // Y pixels/mm
366 25.4, // screen dpi x
367 25.4, // screen dpi y
368 friendly_orientation_minimum, // radians
369 friendly_orientation_maximum, // radians
370 hwprops->max_finger_cnt,
371 hwprops->max_touch_cnt,
372 hwprops->supports_t5r2,
373 hwprops->support_semi_mt,
374 hwprops->is_button_pad,
375 hwprops->has_wheel,
376 hwprops->wheel_is_hi_res,
377 hwprops->is_haptic_pad,
378 };
379 // current metrics is no longer valid, pass metrics=NULL
380 FilterInterpreter::Initialize(&friendly_props_, NULL, mprops, consumer);
381 }
382
383 } // namespace gestures
384