diff --git a/synaptics.c b/synaptics.c index 63edebb..20a084e 100644 --- a/synaptics.c +++ b/synaptics.c @@ -400,6 +400,7 @@ #endif pars->tap_action[F2_TAP] = xf86SetIntOption(opts, "TapButton2", 2); pars->tap_action[F3_TAP] = xf86SetIntOption(opts, "TapButton3", 3); pars->circular_scrolling = xf86SetBoolOption(opts, "CircularScrolling", FALSE); + pars->adaptive_circle = xf86SetBoolOption(opts, "AdaptiveCircle", TRUE); pars->circular_trigger = xf86SetIntOption(opts, "CircScrollTrigger", 0); pars->circular_pad = xf86SetBoolOption(opts, "CircularPad", FALSE); pars->palm_detect = xf86SetBoolOption(opts, "PalmDetect", TRUE); @@ -666,10 +667,7 @@ relative_coords(SynapticsPrivate *priv, static double angle(SynapticsPrivate *priv, int x, int y) { - double xCenter = (priv->synpara->left_edge + priv->synpara->right_edge) / 2.0; - double yCenter = (priv->synpara->top_edge + priv->synpara->bottom_edge) / 2.0; - - return xf86atan2(-(y - yCenter), x - xCenter); + return xf86atan2(-(y - priv->center_y), x - priv->center_x); } /* return angle difference */ @@ -684,6 +682,53 @@ diffa(double a1, double a2) return da; } +/* reset center position to center of touchpad */ +static void +reset_circ_scroll(SynapticsPrivate *priv, int x, int y) +{ + priv->center_x = (priv->synpara->left_edge + priv->synpara->right_edge) / 2.0; + priv->center_y = (priv->synpara->top_edge + priv->synpara->bottom_edge) / 2.0; + priv->scroll_x = x; + priv->scroll_y = y; + priv->scroll_a = -angle(priv, x, y); +} + +/* calculate new center position */ +static void +calculate_new_center(SynapticsPrivate *priv, double x, double y) +{ + double old_x = priv->scroll_x; + double old_y = priv->scroll_y; + double c_x = priv->center_x; + double c_y = priv->center_y; + + double mid_x = (x + old_x)/2.0; + double mid_y = (y + old_y)/2.0; + + double dx = x - old_x; + double dy = y - old_y; + double r = (mid_x - c_x)*(mid_x - c_x) + (mid_y - c_y)*(mid_y - c_y); + + /* rotate in the right direction to find new center */ + double sign = (dx*(c_y - old_y) < dy*(c_x - old_x)) ? 1.0 : -1.0; + double scale; + + /* only adjust center if we've moved a significant distance: *\ + \* 100 units and about 6 degrees */ + if (dx*dx + dy*dy < 10000.0 || dx*dx + dy*dy < 0.05 * r) + return; + + /* detect if concavity changed, e.g. figure eight + sign *= (dx*(c_x - old_x) < dy*(old_y - c_y)) ? -1.0 : 1.0; + removed for now - causing small glitches and fairly useless anyway */ + + scale = sign * xf86sqrt(r/(dx*dx + dy*dy)); + priv->center_x = mid_x + scale*dy; + priv->center_y = mid_y - scale*dx; + priv->scroll_x = x; + priv->scroll_y = y; +} + static edge_type circular_edge_detection(SynapticsPrivate *priv, int x, int y) { @@ -1384,7 +1429,7 @@ HandleScrolling(SynapticsPrivate *priv, (para->circular_trigger == 8 && edge & LEFT_EDGE && edge & TOP_EDGE)) { priv->circ_scroll_on = TRUE; priv->circ_scroll_vert = TRUE; - priv->scroll_a = angle(priv, hw->x, hw->y); + reset_circ_scroll(priv, hw->x, hw->y); DBG(7, ErrorF("circular scroll detected on edge\n")); } } @@ -1460,7 +1505,7 @@ HandleScrolling(SynapticsPrivate *priv, priv->vert_scroll_edge_on = FALSE; priv->circ_scroll_on = TRUE; priv->circ_scroll_vert = TRUE; - priv->scroll_a = angle(priv, hw->x, hw->y); + reset_circ_scroll(priv, hw->x, hw->y); DBG(7, ErrorF("switching to circular scrolling\n")); } } @@ -1471,7 +1516,7 @@ HandleScrolling(SynapticsPrivate *priv, priv->horiz_scroll_edge_on = FALSE; priv->circ_scroll_on = TRUE; priv->circ_scroll_vert = FALSE; - priv->scroll_a = angle(priv, hw->x, hw->y); + reset_circ_scroll(priv, hw->x, hw->y); DBG(7, ErrorF("switching to circular scrolling\n")); } } @@ -1513,26 +1558,29 @@ HandleScrolling(SynapticsPrivate *priv, if (priv->circ_scroll_on) { /* + = counter clockwise, - = clockwise */ double delta = para->scroll_dist_circ; + + priv->scroll_a += angle(priv, hw->x, hw->y); + priv->scroll_a = diffa(0.0, priv->scroll_a); + if (delta >= 0.005) { - while (diffa(priv->scroll_a, angle(priv, hw->x, hw->y)) > delta) { + int dist = (int)(priv->scroll_a / delta); + if (dist > 0) { if (priv->circ_scroll_vert) - sd->up++; + sd->up += dist; else - sd->right++; - priv->scroll_a += delta; - if (priv->scroll_a > M_PI) - priv->scroll_a -= 2 * M_PI; - } - while (diffa(priv->scroll_a, angle(priv, hw->x, hw->y)) < -delta) { + sd->right += dist; + } else { if (priv->circ_scroll_vert) - sd->down++; + sd->down -= dist; else - sd->left++; - priv->scroll_a -= delta; - if (priv->scroll_a < -M_PI) - priv->scroll_a += 2 * M_PI; + sd->left -= dist; } + priv->scroll_a -= dist*delta; } + + if (para->adaptive_circle) + calculate_new_center(priv, hw->x, hw->y); + priv->scroll_a -= angle(priv, hw->x, hw->y); } if (priv->autoscroll_yspd) { diff --git a/synaptics.h b/synaptics.h index 12fe8dc..da760d7 100644 --- a/synaptics.h +++ b/synaptics.h @@ -87,6 +87,7 @@ typedef struct _SynapticsSHM Bool locked_drags; /* Enable locked drags */ int tap_action[MAX_TAP]; /* Button to report on tap events */ Bool circular_scrolling; /* Enable circular scrolling */ + Bool adaptive_circle; /* Try to guess circle location */ double scroll_dist_circ; /* Scrolling angle radians */ int circular_trigger; /* Trigger area for circular scrolling */ Bool circular_pad; /* Edge has an oval or circular shape */ @@ -187,6 +188,8 @@ typedef struct _SynapticsPrivateRec int largest_valid_x; /* Largest valid X coordinate seen so far */ int scroll_y; /* last y-scroll position */ int scroll_x; /* last x-scroll position */ + int center_x; /* center x position for circ scroll */ + int center_y; /* center y position for circ scroll */ double scroll_a; /* last angle-scroll position */ int count_packet_finger; /* packet counter with finger on the touchpad */ int button_delay_millis; /* button delay for 3rd button emulation */ diff --git a/synclient.c b/synclient.c index 371611a..70a2932 100644 --- a/synclient.c +++ b/synclient.c @@ -92,6 +92,7 @@ static struct Parameter params[] = { DEFINE_PAR("TapButton2", tap_action[F2_TAP], PT_INT, 0, SYN_MAX_BUTTONS), DEFINE_PAR("TapButton3", tap_action[F3_TAP], PT_INT, 0, SYN_MAX_BUTTONS), DEFINE_PAR("CircularScrolling", circular_scrolling, PT_BOOL, 0, 1), + DEFINE_PAR("AdaptiveCircle", adaptive_circle, PT_BOOL, 0, 1), DEFINE_PAR("CircScrollDelta", scroll_dist_circ, PT_DOUBLE, .01, 3), DEFINE_PAR("CircScrollTrigger", circular_trigger, PT_INT, 0, 8), DEFINE_PAR("CircularPad", circular_pad, PT_BOOL, 0, 1),