diff -urN gpm-1.20.0-orig/doc/README.synaptics-alt_wmode gpm-1.20.0/doc/README.synaptics-alt_wmode
--- gpm-1.20.0-orig/doc/README.synaptics-alt_wmode	Thu Jan  1 01:00:00 1970
+++ gpm-1.20.0/doc/README.synaptics-alt_wmode	Mon Jul 22 14:42:11 2002
@@ -0,0 +1,82 @@
+This README describes the alternative wmode driver as ported to gpm by
+Charl P. Botha <http://cpbotha.net/> from the work of Stefan Gmeiner
+<http://mobilix.org/>.
+
+This is wmode_alt version 0.1, released 2002-07-22.
+
+What is this?
+-------------
+The alternative wmode driver (wmode_alt) was ported because I
+preferred the synaptics XFree86 driver of Stefan Gmeiner over the
+existing (very good) Synaptics support in GPM, but wished to use GPM
+due to suspend-to-disk issues.
+
+Amongst other things, I prefer the two-finger and three-finger tap
+support (for middle and right mouse button) and the scroll-button
+support of wmode_alt.  Normal wmode exports the "up" as middle button
+and "down" as the fourth button.  In addition, I found the normal
+wmode driver a bit difficult to control and corner taps just didn't
+want to work for me. :)
+
+Quickstart
+----------
+1. edit /etc/gpm-syn.conf, add the line:
+   use_wmode_alt 1
+2. start gpm with e.g.: gpm -t synps2 -m /dev/psaux -Rimps2
+3. start your X, configured to use ImPS/2 mouse protocol, e.g.:
+
+Section "InputDevice"
+        Identifier    "Synaptics Touchpad"
+        Driver        "mouse"
+        Option        "CorePointer"
+        Option        "Device"        "/dev/gpmdata"
+        Option	      "Protocol" "ImPS/2"
+        Option        "ZAxisMapping"          "4 5"
+EndSection
+
+Features
+--------
+wmode_alt offers a different set of features than wmode.  Try both
+this driver and the "normal" wmode to see which one you like.
+
+Features (much taken from documentation of XF86 driver):
+* Movement with adjustable, non-linear acceleration and speed
+* Button events through short touching (tapping) of the touchpad 
+* Double-Button events through double short touching (tapping) of the touchpad 
+* Dragging through short touching (tapping) and holding down the
+  finger on the touchpad
+* Middle and right button events on the right upper and lower corner of the
+  touchpad
+* Scrolling (button four and five events) through moving the finger on
+  the right side of the touchpad
+* The up/down button sends up/down events
+* Adjustable finger detection
+* Multifinger taps: two finger for middle button and three finger for
+  right button events
+
+Parameters
+----------
+This driver is also configured with the gpm-syn.conf file, but uses an
+independent set of parameters:
+* use_wmode_alt (default 0): activate the alternative wmode; this
+  overrides  "use_wmode"
+* wmode_alt_finger_high (default 30): finger pressure has to be more
+  than this threshold to classify as "new" tap
+* wmode_alt_finger_low (default 25): finger pressure has to remain
+  more than this threshold to classify as drag
+* wmode_alt_max_tap_time (default 20): if finger remains on pad longer
+  than this, it can't classify as a tap
+* wmode_alt_max_tap_move (default 220): if finger moves more than
+  this, it can't classify as a tap
+* wmode_alt_vert_scroll_delta (default 100): finger has to move more
+  than this during edge scroll before the scroll is performed
+* wmode_alt_accel_factor (default 0.0010): acceleration-factor to
+  modulate finger motion to pointer speed
+* wmode_alt_max_speed (default 0.18): maximum pointer speed
+* wmode_alt_min_speed (default 0.02): minimum pointer speed
+
+Contact 
+------- 
+This patch can always be found at:
+http://cpbotha.net/gpm_wmode_alt.html
+
diff -urN gpm-1.20.0-orig/src/synaptics.c gpm-1.20.0/src/synaptics.c
--- gpm-1.20.0-orig/src/synaptics.c	Sat Feb 23 16:42:23 2002
+++ gpm-1.20.0/src/synaptics.c	Mon Jul 22 14:43:17 2002
@@ -4,6 +4,10 @@
  * Copyright 1999   hdavies@ameritech.net (Henry Davies)
  *                  Geert Van der Plas provided the code to support
  *                        older Synaptics PS/2 touchpads.
+ *                  Charl P. Botha <http://cpbotha.net/> added the alternative
+ *                  wmode driver (port of the XFree86 Synaptics driver by
+ *                  Stefan Gmeiner, http://mobilix.org/touchpad_driver.html):
+ *                  please also see README.synaptics-alt_wmode
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -56,6 +60,7 @@
 **
 ** Parameters:
 **     search below for configuration constants
+** 
 */
 
 
@@ -69,6 +74,7 @@
 **   - determine appropriate ranges for adjusting parameters
 */
 
+#include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -159,6 +165,21 @@
 static int drag_lock		     = 0;
 static int multiple_click_delay	     = 30;
 
+/* use_wmode_alt */
+static int use_wmode_alt             = 0;
+static int wmode_alt_left_edge       = 1900;
+static int wmode_alt_right_edge      = 5400;
+static int wmode_alt_top_edge        = 3900;
+static int wmode_alt_bottom_edge     = 1800;
+static int wmode_alt_finger_high     = 30;
+static int wmode_alt_finger_low      = 25;
+static int wmode_alt_max_tap_time    = 20;
+static int wmode_alt_max_tap_move    = 220;
+static int wmode_alt_vert_scroll_delta = 100;
+static double wmode_alt_accel_factor    = 0.0010;
+static double wmode_alt_min_speed       = 0.02;
+static double wmode_alt_max_speed       = 0.18;
+
 /*
 ** Types for describing corner actions.
 */
@@ -249,6 +270,18 @@
   { "tap_interval",		  Integer_Param, {&tap_interval}	     },
   { "drag_lock",		  Flag_Param,	 {&drag_lock}		     },
   { "multiple_click_delay",	  Integer_Param, {&multiple_click_delay}     },
+  /* use alternate wmode; ported here by Charl P. Botha (http://cpbotha.net/)
+     from Stefan Gmeiner's <riddlebox@freesurf.at> synaptics XFree86 driver */
+  { "use_wmode_alt",              Flag_Param,    {&use_wmode_alt}            },
+  { "wmode_alt_finger_high",      Integer_Param, {&wmode_alt_finger_high}    },
+  { "wmode_alt_finger_low",       Integer_Param, {&wmode_alt_finger_low}     },
+  { "wmode_alt_max_tap_time",     Integer_Param, {&wmode_alt_max_tap_time}   },
+  { "wmode_alt_max_tap_time",     Integer_Param, {&wmode_alt_max_tap_move}   },
+  { "wmode_alt_vert_scroll_delta",Integer_Param, {&wmode_alt_vert_scroll_delta}},
+  { "wmode_alt_accel_factor",     Float_Param,  {&wmode_alt_accel_factor}    },
+  { "wmode_alt_min_speed",        Float_Param,  {&wmode_alt_min_speed}       },
+  { "wmode_alt_max_speed",        Float_Param,  {&wmode_alt_max_speed}       },
+
   /* end of list */
   { NULL,                         Flag_Param,    {NULL}                      }
 };
@@ -330,6 +363,7 @@
 #define EXTENDED_REPORT    0x02
 #define REPORT_W_OFF       0x00
 #define REPORT_W_ON        0x01
+#define DISABLE_GESTURE (1<<2)
 
 
 static char *model_names [] = {
@@ -1321,6 +1355,313 @@
   }
 }
 
+struct SynapticsMoveHistRec
+{
+  int x, y;
+};
+
+#define DIFF_TIME(a, b) (((a)>(b))?(a)-(b):(b)-(a))
+#define SYNAPTICS_MOVE_HISTORY	5
+#define MOVE_HIST(a) ((priv_count_packet_finger-(a))%SYNAPTICS_MOVE_HISTORY)
+
+void syn_process_ps2_wmode_alt_data(Gpm_Event *state, unsigned char *data)
+{
+  int finger;
+  int x, y, z, w, dist, dx, dy, buttons;
+  int edge;
+  int left, mid, right, up, down;
+  double speed, integral;
+  /*int change;*/
+
+  static struct SynapticsMoveHistRec priv_move_hist[SYNAPTICS_MOVE_HISTORY];
+  static int priv_count_button_delay = 0;
+  static int priv_count_packet = 0;
+  static int priv_count_packet_finger = 0;
+  static int priv_third_button = 0;
+  static int priv_finger_flag = 0;
+  static int priv_finger_count = 0;
+  static double priv_frac_x = 0.0;
+  static double priv_frac_y = 0.0;
+  static int priv_drag = 0;
+  static int priv_tap = 0;
+  static int priv_tap_mid = 0;
+  static int priv_tap_right = 0;
+  static int priv_tap_left = 0;
+  static int priv_double_tap = 0;
+  static int priv_count_packet_tapping = 0;
+  static int priv_touch_on_x = -1;
+  static int priv_touch_on_y = -1;
+  static int priv_touch_on_packet = 0;
+  static int priv_vert_scroll_on = 0;
+  static int priv_scroll_y = 0;
+
+  /* process input data */  
+  x = ((data[3] & 0x10) << 8) | 
+    ((data[1] & 0x0f) << 8) | 
+    data[4];
+  y = ((data[3] & 0x20) << 7) | 
+    ((data[1] & 0xf0) << 4) | 
+    data[5];
+  /* pressure */
+  z = data[2];
+  w = ((data[0] & 0x30) >> 2) | 
+    ((data[0] & 0x04) >> 1) | 
+    ((data[3] & 0x04) >> 2);
+
+  left  = (data[0] & 0x01) ? 1 : 0;
+  mid   = 0;
+  right = (data[0] & 0x2) ? 1 : 0;
+  up    = ((data[3] & 0x01) && !left) ? 1 : 0;
+  down  = ((data[3] & 0x02) && !right) ? 1 : 0;
+
+  edge = 0;
+  if (x < wmode_alt_left_edge) edge |= LEFT_EDGE;
+  else if (x > wmode_alt_right_edge) edge |= RIGHT_EDGE;
+  if (y < wmode_alt_bottom_edge) edge |= BOTTOM_EDGE;
+  else if (y > wmode_alt_top_edge) edge |= TOP_EDGE;
+
+  dx = dy = 0;
+
+  /* shared memory */
+  /* 	para->x = x; */
+  /* 	para->y = y; */
+
+  /* 3rd button emulation */
+  if(!left && !right) 
+    {
+      priv_count_button_delay = priv_count_packet;
+      priv_third_button = 0;
+    } 
+  else if(DIFF_TIME(priv_count_packet, priv_count_button_delay) < 6) 
+    {
+      left = right = 0;
+    } 
+  else if(DIFF_TIME(priv_count_packet, priv_count_button_delay) == 6) 
+    {
+      priv_third_button = left && right;
+    }
+  if(priv_third_button) 
+    {
+      mid = 1;
+      left = right = 0;
+    }
+
+  /* finger detection thru pressure an threshold */
+  /* pressure must be more than finger_high to count as initial tap;
+     pressure must be more than finger_low to remain a tap; less than means the finger was lifted */
+  finger = (((z > wmode_alt_finger_high) && !priv_finger_flag) ||
+	    ((z > wmode_alt_finger_low) && priv_finger_flag));
+
+  /* tap and drag detection */
+  if(finger && !priv_finger_flag) 
+    { /* touched */
+      if(priv_tap) 
+	{
+	  priv_drag = 1; /* drag gesture */
+	}
+      priv_touch_on_x = x;
+      priv_touch_on_y = y;
+      priv_touch_on_packet = priv_count_packet;
+    } 
+  else if (!finger && priv_finger_flag) 
+    { /* untouched */
+	    
+      /* check if
+	 1. the tap is in tap_time
+	 2. the max movement is in tap_move or more than one finger are tapped */
+      if ((DIFF_TIME(priv_count_packet, priv_touch_on_packet) < wmode_alt_max_tap_time) && 
+	  (((abs(x - priv_touch_on_x) < wmode_alt_max_tap_move) &&     
+	    (abs(y - priv_touch_on_y) < wmode_alt_max_tap_move)) || 
+	   priv_finger_count)) 
+	{
+	  if(priv_drag)
+	    {
+	      /* double tapping */
+	      priv_double_tap = 1;
+	      priv_tap = 0;
+	    } 
+	  else 
+	    {
+	      /* tapping */
+	      priv_count_packet_tapping = priv_count_packet;
+	      priv_tap = 1;
+	      if (priv_finger_count == 0) 
+		{
+		  switch(edge) 
+		    {
+		    case UPPER_RIGHT_CORNER:
+		      priv_tap_mid = 1;
+		      break;
+		    case LOWER_RIGHT_CORNER:
+		      priv_tap_right = 1;
+		      break;
+		    case UPPER_LEFT_CORNER: 
+		      break;
+		    case LOWER_LEFT_CORNER: 
+		      break;
+		    default:
+		      priv_tap_left = 1;
+		    }
+		}  /* if (priv_finger_count == 0) ... */
+	      else 
+		{
+		  switch(priv_finger_count) 
+		    {
+		    case 2: 	
+		      /* two finger tap */
+		      priv_tap_mid = 1;
+		      break;
+		    case 3: 	
+		      /* three finger tap */
+		      priv_tap_right = 1;
+		      break;
+		    default:
+		      /* one finger */
+		      priv_tap_left = 1;
+		    }
+		} /* else */
+	    } /* else */
+	} /* Tap detection */ 
+      priv_drag = 0;
+    } /* finger lost */
+
+  /* detecting 2 and 3 fingers */
+  if(finger && (DIFF_TIME(priv_count_packet, priv_touch_on_packet) < wmode_alt_max_tap_time)) 
+    {
+      if((w == 0) && (priv_finger_count == 0))
+	priv_finger_count = 2;
+      if(w == 1)
+	priv_finger_count = 3;
+    } else
+      priv_finger_count = 0;
+
+  /* reset tapping button flags */
+  if(!priv_tap && !priv_drag && !priv_double_tap) 
+    {
+      priv_tap_left = priv_tap_mid = priv_tap_right = 0;
+    }
+
+  /* tap processing */
+  if (priv_tap && 
+      (DIFF_TIME(priv_count_packet, priv_count_packet_tapping) < wmode_alt_max_tap_time)) 
+    {
+      left  = priv_tap_left;
+      mid   = priv_tap_mid;
+      right = priv_tap_right;
+    } 
+  else 
+    {
+      priv_tap = 0;
+    }
+
+  /* drag processing */
+  if(priv_drag) 
+    {
+      left  = priv_tap_left;
+      mid   = priv_tap_mid;
+      right = priv_tap_right;
+    }
+
+  /* double tap processing */
+  if(priv_double_tap && !priv_finger_flag) 
+    {
+      left = priv_tap_left;
+      mid = priv_tap_mid;
+      right = priv_tap_right;
+      priv_double_tap = 0;
+    }
+
+  /* scroll detection */
+  if(finger && !priv_finger_flag) 
+    {
+      if(edge & RIGHT_EDGE) 
+	{
+	  /* edge scroll detected on right edge */
+	  priv_vert_scroll_on = 1;
+	  priv_scroll_y = y;
+	}
+    }
+
+  if(priv_vert_scroll_on && (!(edge & RIGHT_EDGE) || !finger)) 
+    {
+      /* edge scroll off */
+      priv_vert_scroll_on = 0;	
+    }
+
+  /* scroll processing */
+  if(priv_vert_scroll_on) 
+    {
+      dist = y - priv_scroll_y; /* + = up, - = down */
+      if(dist > wmode_alt_vert_scroll_delta) 
+	{
+	  up = 1;
+	  priv_scroll_y += wmode_alt_vert_scroll_delta;
+	}
+      else if(dist < (-1 * wmode_alt_vert_scroll_delta)) 
+	{
+	  down = 1;
+	  priv_scroll_y -= wmode_alt_vert_scroll_delta;
+	}
+    } /* if (priv_vert_scroll_on) ... */
+
+  /* movement */
+  if(finger && !priv_vert_scroll_on && !priv_finger_count) 
+    {
+      if(priv_count_packet_finger > 3) 
+	{ /* min. 3 packets */
+	  dy = (1 * 
+		(((priv_move_hist[MOVE_HIST(1)].y + priv_move_hist[MOVE_HIST(2)].y) / 2) - 
+		 ((y 		                      + priv_move_hist[MOVE_HIST(1)].y) / 2)));
+	  dx = (-1 * 
+		(((priv_move_hist[MOVE_HIST(1)].x + priv_move_hist[MOVE_HIST(2)].x) / 2) - 
+		 ((x                           	  + priv_move_hist[MOVE_HIST(1)].x) / 2)));
+				
+	  /* speed in depence of distance/packet */
+	  dist = sqrt(dx * dx +  dy * dy);
+	  speed = dist * wmode_alt_accel_factor; 
+	  if (speed > wmode_alt_max_speed)
+	    speed = wmode_alt_max_speed;
+	  else if(speed < wmode_alt_min_speed)
+	    speed = wmode_alt_min_speed;
+
+	  /* save the fraction for adding to the next priv->count_packet */
+	  priv_frac_x = modf((dx * speed) + priv_frac_x, &integral);
+	  dx = integral;
+	  priv_frac_y = modf((dy * speed) + priv_frac_y, &integral);
+	  dy = integral;
+	} /* if (priv_count_packet_finger > 3) ... */
+
+      priv_count_packet_finger++;
+    } 
+  else 
+    {
+      priv_count_packet_finger = 0;
+    }
+
+
+  buttons = ((left  ? 0x01 : 0) |
+	     (mid   ? 0x02 : 0) |
+	     (right ? 0x04 : 0) |
+	     (up    ? 0x08 : 0) |
+	     (down  ? 0x10 : 0));		
+
+  priv_count_packet++;
+
+  /* Flags */
+  priv_finger_flag = finger;
+  priv_move_hist[MOVE_HIST(0)].y = y;
+  priv_move_hist[MOVE_HIST(0)].x = x;
+  
+  state->dx = dx;
+  state->dy = dy;
+  state->buttons = 0;
+  if (left) state->buttons |= GPM_B_LEFT;
+  if (right) state->buttons |= GPM_B_RIGHT;
+  if (mid) state->buttons |= GPM_B_MIDDLE;
+  if (up) state->buttons |= GPM_B_UP;
+  if (down) state->buttons |= GPM_B_DOWN;
+
+}
 
 /****************************************************************************
 **
@@ -1352,10 +1693,15 @@
 			   unsigned char *data) 
 {
   report_type   report;
-
-  if (use_wmode) syn_translate_ps2_wmode_report (data, &report);
-  else syn_translate_ps2_report (data, &report);
-  syn_process_data (state, report);
+  
+  if (use_wmode_alt)
+    syn_process_ps2_wmode_alt_data(state, data);
+  else
+    {
+      if (use_wmode) syn_translate_ps2_wmode_report (data, &report);
+      else syn_translate_ps2_report (data, &report);
+      syn_process_data (state, report);
+    }
 }
 
 
@@ -1404,12 +1750,26 @@
   syn_read_ps2_ident (fd, &ident);
   syn_read_ps2_model_id (fd, &model);
   syn_read_ps2_cap (fd, &cap);
-  if (! (cap.cap_ext)) use_wmode = 0; /* wmode not support by the pad */
+  if (! (cap.cap_ext)) {
+    use_wmode = 0; /* wmode not support by the pad */
+    use_wmode_alt = 0;
+  }
   syn_process_config (ident, model);
 
-  /* select 6 byte packet, high packet rate, no-sleep */
-  ps2_set_mode2 (fd, (ABSOLUTE_MODE    |
-		      HIGH_REPORT_RATE |
-		      PS2_NO_SLEEP     |
-		      (use_wmode ? REPORT_W_ON : REPORT_W_OFF)));
+  if (use_wmode_alt)
+    {
+      /* with the alternate wmode, we mimic the settings in the XFree86 mouse driver */
+      ps2_set_mode2(fd, (ABSOLUTE_MODE    |
+			 HIGH_REPORT_RATE |
+			 DISABLE_GESTURE  |
+			 REPORT_W_ON ));
+    }
+  else
+    {
+      /* select 6 byte packet, high packet rate, no-sleep */
+      ps2_set_mode2 (fd, (ABSOLUTE_MODE    |
+			  HIGH_REPORT_RATE |
+			  PS2_NO_SLEEP     |
+			  (use_wmode ? REPORT_W_ON : REPORT_W_OFF)));
+    }
 }
