---
 include/synaptics-properties.h |    3 ++
 include/synaptics.h            |    1 
 src/eventcomm.c                |    4 ++
 src/properties.c               |   12 ++++++++
 src/synaptics.c                |   58 ++++++++++++++++++++++++++++-------------
 src/synapticsstr.h             |    9 ++++++
 tools/synclient.c              |    2 +
 7 files changed, 71 insertions(+), 18 deletions(-)

--- a/src/eventcomm.c
+++ b/src/eventcomm.c
@@ -209,6 +209,7 @@
 		abs.minimum, abs.maximum);
 	priv->minx = abs.minimum;
 	priv->maxx = abs.maximum;
+	priv->resx = abs.resolution;
     } else
 	xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", local->name,
 		strerror(errno));
@@ -220,6 +221,9 @@
 		abs.minimum, abs.maximum);
 	priv->miny = abs.minimum;
 	priv->maxy = abs.maximum;
+	priv->resy = abs.resolution;
+	xf86Msg(X_INFO, "%s: resolution %d x %d\n", local->name,
+		priv->resx, priv->resy);
     } else
 	xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", local->name,
 		strerror(errno));
--- a/src/synaptics.c
+++ b/src/synaptics.c
@@ -515,6 +515,8 @@
     pars->touch_button_area = xf86SetIntOption(opts, "TouchButtonArea", 20);
     pars->touch_button_sense = xf86SetBoolOption(opts, "TouchButtonSense", FALSE);
 
+    pars->max_screen_scale = xf86SetRealOption(opts, "MaxScreenScale", 0.35);
+
     /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
     if (pars->top_edge > pars->bottom_edge) {
 	int tmp = pars->top_edge;
@@ -602,6 +604,11 @@
     priv->tap_button_state = TBS_BUTTON_UP;
     priv->touch_on.millis = 0;
 
+    priv->resx = priv->resy = 0;
+    priv->screen_width = priv->screen_height = 0;
+    priv->screen_scale = 1.0;
+    priv->scale_factor_x = priv->scale_factor_y = 1.0;
+
     /* read hardware dimensions */
     ReadDevDimensions(local);
 
@@ -1472,6 +1479,33 @@
     return delay;
 }
 
+static double calculate_screen_scale(SynapticsPrivate *priv)
+{
+    SynapticsSHM *para = priv->synpara;
+    int scr_width = screenInfo.screens[0]->width;
+    int scr_height = screenInfo.screens[0]->height;
+    int width = priv->maxx - priv->minx;
+    int height = priv->maxy - priv->miny;
+    int dev_height;
+    double scale_x, scale_y;
+
+    if (priv->minx >= priv->maxx || priv->miny >= priv->maxy)
+	return priv->screen_scale;
+    if (scr_width == priv->screen_width && scr_height == priv->screen_height)
+	return priv->screen_scale;
+    dev_height = height;
+    if (priv->resx && priv->resy)
+	dev_height = width * priv->resx / priv->resy;
+    scale_x = (double)scr_width / (double)width;
+    scale_y = (double)scr_height / (double)dev_height;
+    priv->screen_scale = MIN(scale_x, scale_y);
+    priv->scale_factor_x = 1.0;
+    priv->scale_factor_y = (double)dev_height / (double)height;
+    priv->screen_width = scr_width;
+    priv->screen_height = scr_height;
+    return priv->screen_scale;
+}
+
 #define HIST(a) (priv->move_hist[((priv->hist_index - (a) + SYNAPTICS_MOVE_HISTORY) % SYNAPTICS_MOVE_HISTORY)])
 
 static void
@@ -1535,7 +1569,7 @@
 	    int x_edge_speed = 0;
 	    int y_edge_speed = 0;
 	    double dtime = (hw->millis - HIST(0).millis) / 1000.0;
-	    double screen_scale = 1.0;
+	    double scale;
 
 	    if (priv->moving_state == MS_TRACKSTICK) {
 		dx = (hw->x - priv->trackstick_neutral_x);
@@ -1609,30 +1643,18 @@
 		}
 	    }
 
-	    if (priv->minx < priv->maxx && priv->miny < priv->maxy) {
-		int width = priv->maxx - priv->minx;
-		int height = priv->maxy - priv->miny;
-		double scale_x = 0, scale_y = 0;
-		if (width)
-		    scale_x = (double)screenInfo.screens[0]->width / width;
-		if (height)
-		    scale_y = (double)screenInfo.screens[0]->height / height;
-		if (scale_x && scale_y) {
-		    if (scale_x < scale_y)
-			screen_scale = scale_x;
-		    else
-			screen_scale = scale_y;
-		}
-	    }
+	    scale = calculate_screen_scale(priv);
+	    if (para->max_screen_scale > 0 && scale > para->max_screen_scale)
+		scale = para->max_screen_scale;
 
 	    /* save the fraction, report the integer part */
 	    tmpf = dx * speed + x_edge_speed * dtime;
-	    tmpf *= screen_scale;
+	    tmpf *= scale * priv->scale_factor_x;
 	    tmpf += priv->frac_x;
 	    priv->frac_x = modf(tmpf, &integral);
 	    dx = integral;
 	    tmpf = dy * speed + y_edge_speed * dtime;
-	    tmpf *= screen_scale;
+	    tmpf *= scale * priv->scale_factor_y;
 	    tmpf += priv->frac_y;
 	    priv->frac_y = modf(tmpf, &integral);
 	    dy = integral;
--- a/include/synaptics-properties.h
+++ b/include/synaptics-properties.h
@@ -151,4 +151,7 @@
 /* 8 bit (BOOL) */
 #define SYNAPTICS_PROP_TOUCH_BUTTON_SENSE "Synaptics Touch Button Sense"
 
+/* FLOAT */
+#define SYNAPTICS_PROP_MAX_SCREEN_SCALE "Synaptics Max Screen Scale"
+
 #endif /* _SYNAPTICS_PROPERTIES_H_ */
--- a/include/synaptics.h
+++ b/include/synaptics.h
@@ -137,6 +137,7 @@
     int led_double_tap;			    /* double-tap period in ms for touchpad LED control */
     int touch_button_area;                  /* touch button (clickpad) area in percent */
     Bool touch_button_sense;                /* touch button (clickpad) area sensing */
+    double max_screen_scale;                /* max screen scaling factor */
 } SynapticsSHM;
 
 /*
--- a/src/properties.c
+++ b/src/properties.c
@@ -83,6 +83,7 @@
 Atom prop_led_double_tap        = 0;
 Atom prop_touch_button_area     = 0;
 Atom prop_touch_button_sense    = 0;
+Atom prop_max_screen_scale      = 0;
 
 static Atom
 InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values)
@@ -260,6 +261,8 @@
 
     prop_touch_button_sense = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_SENSE, 8, 1, &para->touch_button_sense);
 
+    fvalues[0] = para->max_screen_scale;
+    prop_max_screen_scale = InitFloatAtom(local->dev, SYNAPTICS_PROP_MAX_SCREEN_SCALE, 1, fvalues);
 }
 
 int
@@ -584,6 +587,15 @@
         speed = *(float*)prop->data;
         para->coasting_speed = speed;
 
+    } else if (property == prop_max_screen_scale)
+    {
+        float *data;
+
+        if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
+            return BadMatch;
+
+        data = (float*)prop->data;
+        para->max_screen_scale = data[0];
     } else if (property == prop_pressuremotion)
     {
         float *press;
--- a/src/synapticsstr.h
+++ b/src/synapticsstr.h
@@ -163,6 +163,15 @@
     int led_tap_millis;
     struct SynapticsHwState prev_hw;
 
+    /* screen resolution */
+    int resx;
+    int resy;
+    int screen_width;
+    int screen_height;
+    double screen_scale;
+    double scale_factor_x;
+    double scale_factor_y;
+
     enum TouchpadModel model;          /* The detected model */
 } SynapticsPrivate;
 
--- a/tools/synclient.c
+++ b/tools/synclient.c
@@ -215,6 +215,8 @@
 		SYNAPTICS_PROP_TOUCH_BUTTON_AREA,	8,	0),
     DEFINE_PAR("TouchButtonSense",     touch_button_sense,      PT_BOOL,   0, 1,
 		SYNAPTICS_PROP_TOUCH_BUTTON_SENSE,	8,	0),
+    DEFINE_PAR("MaxScreenScale",       max_screen_scale,	PT_DOUBLE, 0, 10,
+	       SYNAPTICS_PROP_MAX_SCREEN_SCALE,	0 /* float*/,	0),
     { NULL, 0, 0, 0, 0 }
 };
 
