diff --git a/ansi.c b/ansi.c
--- a/ansi.c
+++ b/ansi.c
@@ -1469,6 +1469,12 @@
 	      curr->w_mouse = i ? a1 : 0;
 	      LMouseMode(&curr->w_layer, curr->w_mouse);
 	      break;
+	    case 1005:  /* UTF-8 mouse mode */
+	    case 1006:  /* SGR mouse mode */
+	    case 1015:  /* UXRVT mouse mode */
+		curr->w_extmouse = i ? a1 : 0;
+		LExtMouseMode(&curr->w_layer, curr->w_extmouse);
+                break;
 	    }
 	}
       break;
diff --git a/display.c b/display.c
--- a/display.c
+++ b/display.c
@@ -487,6 +487,7 @@
       if (D_mousetrack)
 	D_mousetrack = 0;
       MouseMode(0);
+      ExtMouseMode(0);
       SetRendition(&mchar_null);
       SetFlow(FLOW_NOW);
 #ifdef MAPKEYS
@@ -836,6 +837,29 @@
     }
 }
 
+void
+ExtMouseMode(mode)
+     int mode;
+{
+  if (display && D_extmouse != mode)
+    {
+      char mousebuf[20];
+      if (!D_CXT)
+        return;
+      if (D_extmouse)
+        {
+          sprintf(mousebuf, "\033[?%dl", D_extmouse);
+          AddStr(mousebuf);
+        }
+      if (mode)
+        {
+          sprintf(mousebuf, "\033[?%dh", mode);
+          AddStr(mousebuf);
+        }
+      D_extmouse = mode;
+    }
+}
+
 static int StrCost;
 
 /* ARGSUSED */
@@ -1261,6 +1285,7 @@
   CursorkeysMode(0);
   CursorVisibility(0);
   MouseMode(0);
+  ExtMouseMode(0);
   SetRendition(&mchar_null);
   SetFlow(FLOW_NOW);
 
@@ -3160,6 +3185,7 @@
   int oldkeypad = D_keypad, oldcursorkeys = D_cursorkeys;
   int oldcurvis = D_curvis;
   int oldmouse = D_mouse;
+  int oldextmouse = D_extmouse;
 
   oldrend = D_rend;
   len = D_obufp - D_obuf;
@@ -3222,6 +3248,7 @@
   CursorkeysMode(oldcursorkeys);
   CursorVisibility(oldcurvis);
   MouseMode(oldmouse);
+  ExtMouseMode(oldextmouse);
   if (D_CWS)
     {
       debug("ResizeDisplay: using WS\n");
@@ -3350,13 +3377,35 @@
     }
 }
 
+static unsigned char*
+write_utf8(op, x)
+     unsigned char *op;
+     int x;
+{
+  if (x > 0x7f)
+    {
+      *(op++) = 0xc0 + (x >> 6);
+      *(op++) = 0x80 + (x & 0x3f);
+    }
+  else
+    *(op++) = x;
+ 
+  return op;
+}
+
+// states used to parse mouse escape sequences
+enum { CSI_INACTIVE, CSI_PB, CSI_PX, CSI_PY, CSI_DONE, CSI_ESC_SEEN, CSI_BEGIN };
+
 static void
 disp_readev_fn(ev, data)
 struct event *ev;
 char *data;
 {
   int size;
-  char buf[IOSIZE];
+  char bufspace[IOSIZE];
+  unsigned char mousebuf[IOSIZE + 34];
+  unsigned char *buf = bufspace;
+
   struct canvas *cv;
 
   display = (struct display *)data;
@@ -3445,61 +3494,201 @@
     D_fore->w_lastdisp = display;
   if (D_mouse && D_forecv)
     {
+      unsigned char *op = mousebuf;
+     
+      /* We avoid ugly timing hacks by observing that we don't need to
+	 predict whether an escape sequence is coming... we only need
+	 to recognize that one is in progress in time to massage its
+	 contents. Therefore, we forward the components of a potential
+	 CSI immediately, remembering that we saw them. Once a true
+	 CSI starts, we hoard the input (in integer form to avoid
+	 buffer overruns) until we know what to do with it.
+
+	 Supported mouse events take three flavors.
+
+	 X10: CSI M Cb Cx Cy
+	 UTF-8: CSI M Cb1 [Cb2] Cx1 [Cx2] Cy1 [Cy2]
+	 UXRVT: CSI Ps ; Ps ; Ps M
+	 SGR: CSI < Ps ; Ps ; Ps M|m
+
+	 BUGBUG: this code assumes the underlying terminal actually
+	 supports the most recently requested protocol; if it doesn't,
+	 we will mishandle the bytes we get from whatever legacy
+	 protocol it uses instead. With X10 vs. UTF-8, this is unavoidable
+	 because they can interpret differently the same sequence of
+	 bytes: we have to choose one over the other, so we assume
+	 that the user knows what she's doing. UXRVT and SGR modes
+	 don't overlap (neither with each other nor the legacy modes),
+	 so we theoretically could, e.g. handle X10 sequences when SGR
+	 is requested but unsupported by the underlying terminal.
+      */
       unsigned char *bp = (unsigned char *)buf;
-      int x, y, i = size;
+      unsigned char *end = bp + size;
+      while (bp != end)
+	{
+	  unsigned char c = *(bp++);
+	  switch (D_mouse_state)
+	    {
+	    case CSI_INACTIVE:
+	      *(op++) = c;
+	      if (c == '\033')
+		D_mouse_state = CSI_ESC_SEEN;
+	      break;
+        
+	    case CSI_ESC_SEEN:
+	      D_mouse_state = (c == '[')? CSI_BEGIN : CSI_INACTIVE;
+	      *(op++) = c;
+	      break;
+        
+	    case CSI_BEGIN:
+	      if ( (c == 'M' && (D_extmouse == 0 || D_extmouse == 1005))
+		   || (c == '<' && D_extmouse == 1006))
+		D_mouse_params[ (D_mouse_state = CSI_PB) ] = 0;
+	      else if ('0' <= c && c <= '9' && D_extmouse == 1015)
+		D_mouse_params[ (D_mouse_state = CSI_PB) ] = c - '0';
+	      else
+		{
+		  D_mouse_state = CSI_INACTIVE;
+		  *(op++) = c;
+		}
+	      break;
 
-      /* XXX this assumes that the string is read in as a whole... */
-      for (i = size; i > 0; i--, bp++)
-	{
-	  if (i > 5 && bp[0] == 033 && bp[1] == '[' && bp[2] == 'M')
+	    case CSI_PB:
+	    case CSI_PX:
+	    case CSI_PY:
+	      switch (D_extmouse)
+		{
+		case 0:
+		  // x10 mode: three arbitrary chars
+		  D_mouse_params[D_mouse_state++] = c;
+		  break;
+                
+		case 1005:
+		  if (c >= 0xc2)
+		    D_mouse_params[D_mouse_state] = (c & 0x1f) << 6;
+		  else
+		    {
+		      if (D_mouse_params[D_mouse_state])
+			D_mouse_params[D_mouse_state++] += c & 0x3f;
+		      else
+			D_mouse_params[D_mouse_state++] = c;
+		      D_mouse_params[D_mouse_state] = 0;
+		    }
+		  break;
+
+		case 1006:
+		case 1015:
+		  if ('0' <= c && c <= '9')
+		    {
+		      D_mouse_params[D_mouse_state] *= 10;
+		      D_mouse_params[D_mouse_state] += c - '0';
+		    }
+		  else if (c == ';' && D_mouse_state < CSI_PY)
+		    D_mouse_params[++D_mouse_state] = 0;
+		  else if (D_mouse_state == CSI_PY)
+		    D_mouse_state = CSI_DONE;
+		  else
+		    D_mouse_state = CSI_INACTIVE;
+		  break;
+                
+		default:
+		  D_mouse_state = CSI_INACTIVE;
+		  break;
+		}
+	      break;
+            
+	    default:
+	      break;
+	    }
+
+	  if (D_mouse_state == CSI_DONE)
 	    {
-	      bp++;
-	      i--;
-	    }
-	  else if (i < 5 || bp[0] != 0233 || bp[1] != 'M')
-	    continue;
-	  x = bp[3] - 33;
-	  y = bp[4] - 33;
-	  if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye)
-	    {
-	      if ((D_fore && D_fore->w_mouse) || (D_mousetrack && D_forecv->c_layer->l_mode == 1))
+	      /* verify the mouse code (0 and 1005 are always valid) */
+	      int xform = 1;
+	      int valid = 1;
+	      int x = D_mouse_params[CSI_PX], y = D_mouse_params[CSI_PY];
+	      char const* before = "M";
+	      int bias = 33;
+            
+	      switch (D_extmouse)
 		{
-		  /* Send clicks only if the window is expecting clicks */
-		  x -= D_forecv->c_xoff;
-		  y -= D_forecv->c_yoff;
-		  if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height)
-		    {
-		      bp[3] = x + 33;
-		      bp[4] = y + 33;
-		      i -= 4;
-		      bp += 4;
-		      continue;
-		    }
+		case 0:
+		case 1005:
+		  /* always valid */
+		  break;
+
+		case 1006:
+		  /* may not be valid, but no need to adjust offsets */
+		  before = "<";
+		  bias = 0;
+		  if (c != 'M' && c != 'm')
+		    valid = 0;
+		  break;
+
+		case 1015:
+		  /* may not be valid and needs offests adjusted */
+		  before = "";
+		  if (c != 'M')
+		    valid = 0;
+		  break;
+                
+		default:
+		  break;
 		}
-	    }
-	  else if (D_mousetrack && bp[2] == '#')
-	    {
-	      /* 'focus' to the clicked region, only on mouse up */
-	      struct canvas *cv = FindCanvas(x, y);
-	      if (cv)
-		{
-		  SetForeCanvas(display, cv);
-		  /* XXX: Do we want to reset the input buffer? */
-		}
-	    }
-	  if (bp[0] == '[')
-	    {
-	      bcopy((char *)bp + 1, (char *)bp, i);
-	      bp--;
-	      size--;
-	    }
-	  if (i > 5)
-	    bcopy((char *)bp + 5, (char *)bp, i - 5);
-	  bp--;
-	  i -= 4;
-	  size -= 5;
+
+	      if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye)
+	        {
+	          if ((D_fore && D_fore->w_mouse) || (D_mousetrack && D_forecv->c_layer->l_mode == 1))
+	            {
+	              /* Send clicks only if the window is expecting clicks */
+	              x -= D_forecv->c_xoff;
+	              y -= D_forecv->c_yoff;
+
+	              if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height)
+	                {
+	                  switch (D_extmouse)
+	                    {
+	                    case 0:
+	                      *(op++) = 'M';
+	                      *(op++) = D_mouse_params[CSI_PB];
+	                      *(op++) = x;
+	                      *(op++) = y;
+	                      break;
+
+	                    case 1005:
+	                      *(op++) = 'M';
+	                      op = write_utf8(op, D_mouse_params[CSI_PB]);
+	                      op = write_utf8(op, x);
+	                      op = write_utf8(op, y);
+	                      break;
+
+	                    default:
+	                      /* worst-case output is 1+10+1+10+1+10+1 == 34 chars */
+	                      op += snprintf(op, 34, "%s%d;%d;%d%c", 
+	                    		before, D_mouse_params[CSI_PB], x, y, c);
+	                    }
+	                }
+                    }
+                }
+	      else if (D_mousetrack && bp[2] == '#')
+	        {
+	          /* 'focus' to the clicked region, only on mouse up */
+	          struct canvas *cv = FindCanvas(x, y);
+	          if (cv)
+	            {
+	              SetForeCanvas(display, cv);
+	              /* XXX: Do we want to reset the input buffer? */
+	            }
+	        }
+
+	      D_mouse_state = CSI_INACTIVE;
+            }
 	}
+
+      buf = (char*)mousebuf;
+      size = op - mousebuf;
     }
+
 #ifdef ENCODINGS
   if (D_encoding != (D_forecv ? D_forecv->c_layer->l_encoding : 0))
     {
diff --git a/display.h b/display.h
--- a/display.h
+++ b/display.h
@@ -100,6 +100,9 @@
   int	d_hstatus;		/* hardstatus used */
   int	d_lp_missing;		/* last character on bot line missing */
   int   d_mouse;		/* mouse mode */
+  int	d_extmouse;		/* extended mouse mode */
+  char	d_mouse_state;		/* state of mouse code parsing */
+  int	d_mouse_params[5];	/* parameters seen so far in parsing (0 and 4 unused)*/
   int	d_mousetrack;		/* set when user wants to use mouse even when the window
 				   does not */
 #ifdef RXVT_OSC
@@ -227,6 +230,9 @@
 #define D_hstatus	DISPLAY(d_hstatus)
 #define D_lp_missing	DISPLAY(d_lp_missing)
 #define D_mouse		DISPLAY(d_mouse)
+#define D_mouse_state	DISPLAY(d_mouse_state)
+#define D_mouse_params	DISPLAY(d_mouse_params)
+#define D_extmouse	DISPLAY(d_extmouse)
 #define D_mousetrack	DISPLAY(d_mousetrack)
 #define D_xtermosc	DISPLAY(d_xtermosc)
 #define D_lpchar	DISPLAY(d_lpchar)
diff --git a/extern.h b/extern.h
--- a/extern.h
+++ b/extern.h
@@ -287,6 +287,7 @@
 extern void  ReverseVideo __P((int));
 extern void  CursorVisibility __P((int));
 extern void  MouseMode __P((int));
+extern void  ExtMouseMode __P((int));
 extern void  SetFont __P((int));
 extern void  SetAttr __P((int));
 extern void  SetColor __P((int, int));
@@ -446,6 +447,7 @@
 extern void  LKeypadMode __P((struct layer *, int));
 extern void  LCursorkeysMode __P((struct layer *, int));
 extern void  LMouseMode __P((struct layer *, int));
+extern void  LExtMouseMode __P((struct layer *, int));
 #if defined(USEVARARGS)
 extern void  LMsg __P((int, const char *, ...)) __attribute__((format(printf, 2, 3)));
 #else
diff --git a/layer.c b/layer.c
--- a/layer.c
+++ b/layer.c
@@ -979,6 +979,23 @@
     }
 }
 
+void
+LExtMouseMode(l, on)
+struct layer *l;
+int on;
+{
+  struct canvas *cv;
+  for (cv = l->l_cvlist; cv; cv = cv->c_lnext)
+    {
+      display = cv->c_display;
+      if (D_blocked)
+	continue;
+      if (cv != D_forecv)
+	continue;
+      ExtMouseMode(on);
+    }
+}
+
 
 /*******************************************************************/
 /*******************************************************************/
diff --git a/window.c b/window.c
--- a/window.c
+++ b/window.c
@@ -505,6 +505,7 @@
       ReverseVideo(fore->w_revvid);
       CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis);
       MouseMode(fore->w_mouse);
+      ExtMouseMode(fore->w_extmouse);
     }
 }
 
diff --git a/window.h b/window.h
--- a/window.h
+++ b/window.h
@@ -238,6 +238,7 @@
   char	 w_xtermosc[4][MAXSTR];	/* special xterm/rxvt escapes */
 #endif
   int    w_mouse;		/* mouse mode 0,9,1000 */
+  int    w_extmouse;		/* extended mouse mode 0,1005,1006,1015! */
 #ifdef HAVE_BRAILLE
   int	 w_bd_x, w_bd_y;	/* Braille cursor position */
 #endif
