1 #include "movement_maker.h"
5 #include <KernelExport.h>
9 # define INFO(x...) dprintf(x)
14 //#define TRACE_MOVEMENT_MAKER
15 #ifdef TRACE_MOVEMENT_MAKER
16 # define TRACE(x...) dprintf(x)
24 /* FIXME: Assumes 32 bit int. */
26 } ieee_float_shape_type
;
28 /* Get a 32 bit int from a float. */
30 #define GET_FLOAT_WORD(i,d) \
32 ieee_float_shape_type gf_u; \
37 /* Set a float from a 32 bit int. */
39 #define SET_FLOAT_WORD(d,i) \
41 ieee_float_shape_type sf_u; \
46 static const float huge
= 1.0e30
;
54 j0
= ((i0
>>23)&0xff)-0x7f;
56 if (j0
<0) { /* raise inexact if x != 0 */
57 if (huge
+x
>(float)0.0) {/* return 0*sign(x) if |x|<1 */
59 else if ((i0
&0x7fffffff)!=0)
64 if ((i0
&i
)==0) return x
; /* x is integral */
65 if (huge
+x
>(float)0.0) { /* raise inexact flag */
66 if (i0
<0) i0
+= (0x00800000)>>j0
;
71 if (j0
==0x80) return x
+x
; /* inf or NaN */
72 else return x
; /* x is integral */
86 j0
= ((i0
>>23)&0xff)-0x7f;
88 if (j0
<0) { /* raise inexact if x != 0 */
89 if (huge
+x
>(float)0.0) {/* return 0*sign(x) if |x|<1 */
90 if (i0
<0) {i0
=0x80000000;}
91 else if (i0
!=0) { i0
=0x3f800000;}
95 if ((i0
&i
)==0) return x
; /* x is integral */
96 if (huge
+x
>(float)0.0) { /* raise inexact flag */
97 if (i0
>0) i0
+= (0x00800000)>>j0
;
102 if (j0
==0x80) return x
+x
; /* inf or NaN */
103 else return x
; /* x is integral */
105 SET_FLOAT_WORD(x
,i0
);
109 static const float one
= 1.0, tiny
=1.0e-30;
115 int32 sign
= (int)0x80000000;
119 GET_FLOAT_WORD(ix
,x
);
121 /* take care of Inf and NaN */
122 if ((ix
&0x7f800000)==0x7f800000) {
123 return x
*x
+x
; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf
126 /* take care of zero */
128 if ((ix
&(~sign
))==0) return x
;/* sqrt(+-0) = +-0 */
130 return (x
-x
)/(x
-x
); /* sqrt(-ve) = sNaN */
134 if (m
==0) { /* subnormal x */
135 for(i
=0;(ix
&0x00800000)==0;i
++) ix
<<=1;
138 m
-= 127; /* unbias exponent */
139 ix
= (ix
&0x007fffff)|0x00800000;
140 if (m
&1) /* odd m, double x to make it even */
142 m
>>= 1; /* m = [m/2] */
144 /* generate sqrt(x) bit by bit */
146 q
= s
= 0; /* q = sqrt(x) */
147 r
= 0x01000000; /* r = moving bit from right to left */
160 /* use floating add to find out rounding direction */
162 z
= one
-tiny
; /* trigger inexact flag */
171 ix
= (q
>>1)+0x3f000000;
173 SET_FLOAT_WORD(z
,ix
);
179 make_small(float value
)
182 return floorf(value
);
190 MovementMaker::SetSettings(touchpad_settings
* settings
)
192 fSettings
= settings
;
197 MovementMaker::SetSpecs(hardware_specs
* specs
)
201 fAreaWidth
= fSpecs
->areaEndX
- fSpecs
->areaStartX
;
202 fAreaHeight
= fSpecs
->areaEndY
- fSpecs
->areaStartY
;
204 // calibrated on the synaptics touchpad
205 fSpeed
= SYN_WIDTH
/ fAreaWidth
;
206 fSmallMovement
= 3 / fSpeed
;
211 MovementMaker::StartNewMovment()
213 if (fSettings
->scroll_xstepsize
<= 0)
214 fSettings
->scroll_xstepsize
= 1;
215 if (fSettings
->scroll_ystepsize
<= 0)
216 fSettings
->scroll_ystepsize
= 1;
218 fMovementMakerStarted
= true;
225 MovementMaker::GetMovement(uint32 posX
, uint32 posY
)
227 _GetRawMovement(posX
, posY
);
229 // INFO("SYN: pos: %lu x %lu, delta: %ld x %ld, sums: %ld x %ld\n",
230 // posX, posY, xDelta, yDelta,
231 // fDeltaSumX, fDeltaSumY);
239 MovementMaker::GetScrolling(uint32 posX
, uint32 posY
)
241 int32 stepsX
= 0, stepsY
= 0;
243 _GetRawMovement(posX
, posY
);
244 _ComputeAcceleration(fSettings
->scroll_acceleration
);
246 if (fSettings
->scroll_xstepsize
> 0) {
247 scrolling_x
+= xDelta
;
249 stepsX
= make_small(scrolling_x
/ fSettings
->scroll_xstepsize
);
251 scrolling_x
-= stepsX
* fSettings
->scroll_xstepsize
;
257 if (fSettings
->scroll_ystepsize
> 0) {
258 scrolling_y
+= yDelta
;
260 stepsY
= make_small(scrolling_y
/ fSettings
->scroll_ystepsize
);
262 scrolling_y
-= stepsY
* fSettings
->scroll_ystepsize
;
263 yDelta
= -1 * stepsY
;
272 MovementMaker::_GetRawMovement(uint32 posX
, uint32 posY
)
274 // calibrated on the synaptics touchpad
275 posX
= posX
* float(SYN_WIDTH
) / fAreaWidth
;
276 posY
= posY
* float(SYN_HEIGHT
) / fAreaHeight
;
278 const float acceleration
= 0.8;
279 const float translation
= 12.0;
283 if (fMovementMakerStarted
) {
284 fMovementMakerStarted
= false;
285 // init delta tracking
288 // deltas are automatically reset
291 // accumulate delta and store current pos, reset if pos did not change
292 diff
= posX
- fPreviousX
;
293 // lessen the effect of small diffs
294 if ((diff
> -fSmallMovement
&& diff
< -1)
295 || (diff
> 1 && diff
< fSmallMovement
)) {
303 diff
= posY
- fPreviousY
;
304 // lessen the effect of small diffs
305 if ((diff
> -fSmallMovement
&& diff
< -1)
306 || (diff
> 1 && diff
< fSmallMovement
)) {
317 // compute current delta and reset accumulated delta if
318 // abs() is greater than 1
319 xDelta
= fDeltaSumX
/ translation
;
320 yDelta
= fDeltaSumY
/ translation
;
323 xDelta
= 1.0 + (xDelta
- 1.0) * acceleration
;
324 } else if (xDelta
< -1.0) {
326 xDelta
= -1.0 + (xDelta
+ 1.0) * acceleration
;
331 yDelta
= 1.0 + (yDelta
- 1.0) * acceleration
;
332 } else if (yDelta
< -1.0) {
334 yDelta
= -1.0 + (yDelta
+ 1.0) * acceleration
;
337 xDelta
= make_small(xDelta
);
338 yDelta
= make_small(yDelta
);
344 MovementMaker::_ComputeAcceleration(int8 accel_factor
)
347 float acceleration
= 1;
348 if (accel_factor
!= 0) {
349 acceleration
= 1 + sqrtf(xDelta
* xDelta
350 + yDelta
* yDelta
) * accel_factor
/ 50.0;
353 xDelta
= make_small(xDelta
* acceleration
);
354 yDelta
= make_small(yDelta
* acceleration
);
361 #define fTapTimeOUT 200000
365 TouchpadMovement::Init()
367 fMovementStarted
= false;
368 fScrollingStarted
= false;
370 fValidEdgeMotion
= false;
371 fDoubleClick
= false;
376 TouchpadMovement::EventToMovement(touch_event
*event
, mouse_movement
*movement
)
381 movement
->xdelta
= 0;
382 movement
->ydelta
= 0;
383 movement
->buttons
= 0;
384 movement
->wheel_ydelta
= 0;
385 movement
->wheel_xdelta
= 0;
386 movement
->modifiers
= 0;
387 movement
->clicks
= 0;
388 movement
->timestamp
= system_time();
390 if ((movement
->timestamp
- fTapTime
) > fTapTimeOUT
) {
391 TRACE("TouchpadMovement: tap gesture timed out\n");
394 || (movement
->timestamp
- fTapTime
) > 2 * fTapTimeOUT
) {
399 if (event
->buttons
& kLeftButton
) {
401 fTapdragStarted
= false;
403 fValidEdgeMotion
= false;
406 if (event
->zPressure
>= fSpecs
->minPressure
407 && event
->zPressure
< fSpecs
->maxPressure
408 && ((event
->wValue
>= 4 && event
->wValue
<= 7)
409 || event
->wValue
== 0 || event
->wValue
== 1)
410 && (event
->xPosition
!= 0 || event
->yPosition
!= 0)) {
411 // The touch pad is in touch with at least one finger
412 if (!_CheckScrollingToMovement(event
, movement
))
413 _MoveToMovement(event
, movement
);
415 _NoTouchToMovement(event
, movement
);
421 // in pixel per second
422 const int32 kEdgeMotionSpeed
= 200;
426 TouchpadMovement::_EdgeMotion(mouse_movement
*movement
, touch_event
*event
,
432 bigtime_t time
= system_time();
433 if (fLastEdgeMotion
!= 0) {
434 xdelta
= fRestEdgeMotion
+ kEdgeMotionSpeed
*
435 float(time
- fLastEdgeMotion
) / (1000 * 1000);
436 fRestEdgeMotion
= xdelta
- int32(xdelta
);
442 bool inXEdge
= false;
443 bool inYEdge
= false;
445 if (event
->xPosition
< fSpecs
->areaStartX
+ fSpecs
->edgeMotionWidth
) {
448 } else if (event
->xPosition
> uint16(
449 fSpecs
->areaEndX
- fSpecs
->edgeMotionWidth
)) {
453 if (event
->yPosition
< fSpecs
->areaStartY
+ fSpecs
->edgeMotionWidth
) {
456 } else if (event
->yPosition
> uint16(
457 fSpecs
->areaEndY
- fSpecs
->edgeMotionWidth
)) {
461 // for a edge motion the drag has to be started in the middle of the pad
462 // TODO: this is difficult to understand simplify the code
463 if (inXEdge
&& validStart
)
464 movement
->xdelta
= xdelta
;
465 if (inYEdge
&& validStart
)
466 movement
->ydelta
= ydelta
;
468 if (!inXEdge
&& !inYEdge
)
471 fLastEdgeMotion
= time
;
473 if ((inXEdge
|| inYEdge
) && !validStart
)
480 /*! If a button has been clicked (movement->buttons must be set accordingly),
481 this function updates the fClickCount, as well as the
482 \a movement's clicks field.
483 Also, it sets the button state from movement->buttons.
486 TouchpadMovement::UpdateButtons(mouse_movement
*movement
)
488 // set click count correctly according to double click timeout
489 if (movement
->buttons
!= 0 && fButtonsState
== 0) {
490 if (fClickLastTime
+ click_speed
> movement
->timestamp
)
495 fClickLastTime
= movement
->timestamp
;
498 if (movement
->buttons
!= 0)
499 movement
->clicks
= fClickCount
;
501 fButtonsState
= movement
->buttons
;
506 TouchpadMovement::_NoTouchToMovement(touch_event
*event
,
507 mouse_movement
*movement
)
509 uint32 buttons
= event
->buttons
;
511 TRACE("TouchpadMovement: no touch event\n");
513 fScrollingStarted
= false;
514 fMovementStarted
= false;
518 && (movement
->timestamp
- fTapTime
) < fTapTimeOUT
) {
519 buttons
= kLeftButton
;
522 // if the movement stopped switch off the tap drag when timeout is expired
523 if ((movement
->timestamp
- fTapTime
) > fTapTimeOUT
) {
524 fTapdragStarted
= false;
525 fValidEdgeMotion
= false;
526 TRACE("TouchpadMovement: tap drag gesture timed out\n");
529 if (abs(fTapDeltaX
) > 15 || abs(fTapDeltaY
) > 15) {
534 if (fTapStarted
|| fDoubleClick
) {
535 TRACE("TouchpadMovement: tap gesture\n");
538 if (fTapClicks
> 1) {
539 TRACE("TouchpadMovement: empty click\n");
544 buttons
= kLeftButton
;
546 fTapdragStarted
= true;
547 fDoubleClick
= false;
551 movement
->buttons
= buttons
;
552 UpdateButtons(movement
);
557 TouchpadMovement::_MoveToMovement(touch_event
*event
, mouse_movement
*movement
)
559 bool isStartOfMovement
= false;
562 TRACE("TouchpadMovement: movement event\n");
563 if (!fMovementStarted
) {
564 isStartOfMovement
= true;
565 fMovementStarted
= true;
569 GetMovement(event
->xPosition
, event
->yPosition
);
571 movement
->xdelta
= xDelta
;
572 movement
->ydelta
= yDelta
;
575 fTapDeltaX
+= xDelta
;
576 fTapDeltaY
+= yDelta
;
578 if (fTapdragStarted
) {
579 movement
->buttons
= kLeftButton
;
580 movement
->clicks
= 0;
582 fValidEdgeMotion
= _EdgeMotion(movement
, event
, fValidEdgeMotion
);
583 TRACE("TouchpadMovement: tap drag\n");
585 TRACE("TouchpadMovement: movement set buttons\n");
586 movement
->buttons
= event
->buttons
;
589 // use only a fraction of pressure range, the max pressure seems to be
591 pressure
= 20 * (event
->zPressure
- fSpecs
->minPressure
)
592 / (fSpecs
->realMaxPressure
- fSpecs
->minPressure
);
595 && fSettings
->tapgesture_sensibility
> 0.
596 && fSettings
->tapgesture_sensibility
> (20 - pressure
)) {
597 TRACE("TouchpadMovement: tap started\n");
599 fTapTime
= system_time();
604 UpdateButtons(movement
);
608 /*! Checks if this is a scrolling event or not, and also actually does the
609 scrolling work if it is.
611 \return \c true if this was a scrolling event, \c false if not.
614 TouchpadMovement::_CheckScrollingToMovement(touch_event
*event
,
615 mouse_movement
*movement
)
617 bool isSideScrollingV
= false;
618 bool isSideScrollingH
= false;
620 // if a button is pressed don't allow to scroll, we likely be in a drag
622 if (fButtonsState
!= 0)
625 if ((fSpecs
->areaEndX
- fAreaWidth
* fSettings
->scroll_rightrange
626 < event
->xPosition
&& !fMovementStarted
627 && fSettings
->scroll_rightrange
> 0.000001)
628 || fSettings
->scroll_rightrange
> 0.999999) {
629 isSideScrollingV
= true;
631 if ((fSpecs
->areaStartY
+ fAreaHeight
* fSettings
->scroll_bottomrange
632 > event
->yPosition
&& !fMovementStarted
633 && fSettings
->scroll_bottomrange
> 0.000001)
634 || fSettings
->scroll_bottomrange
> 0.999999) {
635 isSideScrollingH
= true;
637 if ((event
->wValue
== 0 || event
->wValue
== 1)
638 && fSettings
->scroll_twofinger
) {
639 // two finger scrolling is enabled
640 isSideScrollingV
= true;
641 isSideScrollingH
= fSettings
->scroll_twofinger_horizontal
;
644 if (!isSideScrollingV
&& !isSideScrollingH
) {
645 fScrollingStarted
= false;
649 TRACE("TouchpadMovement: scroll event\n");
653 fTapdragStarted
= false;
654 fValidEdgeMotion
= false;
655 if (!fScrollingStarted
) {
656 fScrollingStarted
= true;
659 GetScrolling(event
->xPosition
, event
->yPosition
);
660 movement
->wheel_ydelta
= yDelta
;
661 movement
->wheel_xdelta
= xDelta
;
663 if (isSideScrollingV
&& !isSideScrollingH
)
664 movement
->wheel_xdelta
= 0;
665 else if (isSideScrollingH
&& !isSideScrollingV
)
666 movement
->wheel_ydelta
= 0;
668 fButtonsState
= movement
->buttons
;