• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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