diff -r 9e18ca205b6d ansi.c
--- a/ansi.c	Wed Jan 10 10:54:31 2018 -0700
+++ b/ansi.c	Thu Jan 11 22:03:10 2018 +0000
@@ -1471,6 +1471,12 @@
 	      curr->w_mouse = i ? a1 : 0;
 	      LMouseMode(&curr->w_layer, curr->w_mouse);
 	      break;
+         /* case 1005:     UTF-8 mouse mode rejected */
+	    case 1006:  /* SGR mouse mode */
+		curr->w_extmouse = i ? a1 : 0;
+		LExtMouseMode(&curr->w_layer, curr->w_extmouse);
+                break;
+	 /* case 1015:     UXRVT mouse mode rejected */
 	    }
 	}
       break;
diff -r 9e18ca205b6d display.c
--- a/display.c	Wed Jan 10 10:54:31 2018 -0700
+++ b/display.c	Thu Jan 11 22:03:10 2018 +0000
@@ -39,11 +39,19 @@
 #include "braille.h"
 #include "canvas.h"
 
+/* CSI parsing status */
+enum
+  {
+    CSI_PB=0, CSI_PX=1, CSI_PY=2, CSI_DONE=3,
+    CSI_ESC_SEEN, CSI_BEGIN, CSI_INACTIVE, CSI_INVALID
+  };
+
 static int  CountChars __P((int));
 static int  DoAddChar __P((int));
 static int  BlankResize __P((int, int));
 static int  CallRewrite __P((int, int, int, int));
 static void disp_readev_fn __P((struct event *, char *));
+static void disp_processinput __P((struct display *, unsigned char *, int));
 static void disp_writeev_fn __P((struct event *, char *));
 #ifdef linux
 static void disp_writeev_eagain __P((struct event *, char *));
@@ -487,6 +495,7 @@
       if (D_mousetrack)
 	D_mousetrack = 0;
       MouseMode(0);
+      ExtMouseMode(0);
       SetRendition(&mchar_null);
       SetFlow(FLOW_NOW);
 #ifdef MAPKEYS
@@ -833,6 +842,31 @@
           AddStr(mousebuf);
 	}
       D_mouse = mode;
+      D_mouse_parse.state = CSI_INACTIVE;
+    }
+}
+
+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;
+      D_mouse_parse.state = CSI_INACTIVE;
     }
 }
 
@@ -1261,6 +1295,7 @@
   CursorkeysMode(0);
   CursorVisibility(0);
   MouseMode(0);
+  ExtMouseMode(0);
   SetRendition(&mchar_null);
   SetFlow(FLOW_NOW);
 
@@ -3159,6 +3194,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;
@@ -3221,6 +3257,7 @@
   CursorkeysMode(oldcursorkeys);
   CursorVisibility(oldcurvis);
   MouseMode(oldmouse);
+  ExtMouseMode(oldextmouse);
   if (D_CWS)
     {
       debug("ResizeDisplay: using WS\n");
@@ -3349,13 +3386,24 @@
     }
 }
 
+/* maximum mouse sequence length is SGR: ESC [ < b ; x ; y M */
+enum { MAX_MOUSE_SEQUENCE = 3+10+1+10+1+10+1 };
+
 static void
 disp_readev_fn(ev, data)
 struct event *ev;
 char *data;
 {
+  /* We have to intercept mouse sequences in order to translate them
+   * properly, and an incoming sequence could be spread over multiple
+   * I/O reads. When this occurs, we buffer intermediate state in
+   * D_mouse_params, and then use the reservation at the front of
+   * bufspace to prepend the completed and translated mouse sequence.
+   */
   int size;
-  char buf[IOSIZE];
+  char bufspace[MAX_MOUSE_SEQUENCE + IOSIZE];
+  unsigned char *buf = bufspace + MAX_MOUSE_SEQUENCE;
+
   struct canvas *cv;
 
   display = (struct display *)data;
@@ -3442,63 +3490,248 @@
     ResetIdle();
   if (D_fore)
     D_fore->w_lastdisp = display;
+
   if (D_mouse && D_forecv)
     {
       unsigned char *bp = (unsigned char *)buf;
-      int x, y, i;
+      unsigned char *end = bp + size;
+      unsigned char *mark = NULL;
 
-      /* XXX this assumes that the string is read in as a whole... */
-      for (i = size; i > 0; i--, bp++)
+      /* When mouse mode is enabled, buffer up incoming CSI until we
+       * know whether it is a mouse sequence. If not a mouse event,
+       * emit the CSI unchanged; if an invalid mouse event, swallow
+       * it; otherwise, translate the sequence and emit it.
+       *
+       * Supported mouse events take two flavors.
+       *
+       * VT200: CSI M Cb Cx Cy
+       * SGR:   CSI < Ps ; Ps ; Ps M|m
+       *
+       * UTF-8 and UXRVT modes are explicitly rejected because they
+       * introduce incompatibilities with other control sequences.
+       *
+       * NOTE: applications wishing to use SGR mode will normally
+       * enable VT200 mode first as a fallback in case the terminal
+       * does not support SGR mode. Thus, we must expect to receive
+       * either mode's sequences when mouse mode is enabled. We will
+       * dutifully translate whatever arrives, without attempting to
+       * convert between modes on behalf of the application.
+       */
+      switch (D_mouse_parse.state)
+        {
+        case CSI_PY:
+        case CSI_PX:
+        case CSI_PB:
+          /* Partial mouse sequence; do not restore suppressed
+           * characters. We will emit a translated version (if valid)
+           * or swallow them (otherwise).
+           */
+          break;
+
+        case CSI_BEGIN:
+          /* Partial CSI; restore suppressed characters in case it
+           * turns out not to be a mouse sequence after all.
+           */
+          *(--buf) = '[';
+          ++size;
+          /* fall through */
+
+        case CSI_ESC_SEEN:
+          /* Escape character; restore it in case this turns out not
+           * to be the start of a mouse sequence after all.
+           */
+          *(--buf) = '\033';
+          ++size;
+          break;
+
+        default:
+          break;
+        };
+          
+      while (bp != end)
 	{
-	  if (i > 5 && bp[0] == 033 && bp[1] == '[' && bp[2] == 'M')
+	  unsigned char c = *(bp++);
+
+	  switch (D_mouse_parse.state)
 	    {
-	      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))
-		{
-		  /* 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)
+	    case CSI_INACTIVE:
+              if (c == '\033')
+                {
+                  /* potential escape sequence */
+                  mark = bp-1;
+                  D_mouse_parse.state = CSI_ESC_SEEN;
+                }
+	      break;
+        
+	    case CSI_ESC_SEEN:
+              if (c == '[')
+                {
+                  /* continue buffering an escape sequence */
+                  D_mouse_parse.state = CSI_BEGIN;
+                }
+              else
+                  D_mouse_parse.state = CSI_INACTIVE;
+	      break;
+        
+	    case CSI_BEGIN:
+              if (c == 'M')
+                {
+                  /* VT200 mouse sequence */
+                  D_mouse_parse.state = CSI_PB;
+                  D_mouse_parse.sgrmode = 0;
+                }
+              else if (c == '<')
+                {
+                  /* SGR mouse sequence */
+                  D_mouse_parse.state = CSI_PB;
+                  D_mouse_parse.params[D_mouse_parse.state] = 0;
+                  D_mouse_parse.sgrmode = 1;
+                }
+              else
+                  D_mouse_parse.state = CSI_INACTIVE;
+	      break;
+
+	    case CSI_PB:
+	    case CSI_PX:
+	    case CSI_PY:
+              if (D_mouse_parse.sgrmode)
+                {
+                  /* SGR mode: parse decimal numbers */
+		  if ('0' <= c && c <= '9')
 		    {
-		      bp[3] = x + 33;
-		      bp[4] = y + 33;
-		      i -= 4;
-		      bp += 4;
-		      continue;
-		    }
-		}
+                      D_mouse_parse.params[D_mouse_parse.state] *= 10;
+                      D_mouse_parse.params[D_mouse_parse.state] += c - '0';
+                    }
+                  else if (D_mouse_parse.state == CSI_PY)
+                    {
+                      if (c == 'M' || c == 'm')
+                        D_mouse_parse.state = CSI_DONE;
+                      else
+                        D_mouse_parse.state = CSI_INVALID;
+                    }
+                  else if (c == ';')
+                    {
+                      D_mouse_parse.state++;
+                      D_mouse_parse.params[D_mouse_parse.state] = 0;
+                    }
+                  else
+                    D_mouse_parse.state = CSI_INVALID;
+                }
+              else
+                {
+                  /* VT200 mode: read raw binary values */
+                  D_mouse_parse.params[D_mouse_parse.state++] = c;
+                }
+              break;
+
+	    default:
+	      break;
 	    }
-	  else if (D_mousetrack && bp[2] == '#')
+
+          if (D_mouse_parse.state == CSI_INVALID)
+            {
+              /* swallow invalid sequence, but emit whatever came before */
+              if (buf < mark)
+                disp_processinput(display, buf, mark-buf);
+
+              buf = bp;
+              size = end - bp;
+              D_mouse_parse.state = CSI_INACTIVE;
+            }
+	  else if (D_mouse_parse.state == CSI_DONE)
 	    {
-	      /* '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;
+              /* emit whatever came before this sequence */
+              if (buf < mark)
+                disp_processinput(display, buf, mark-buf);
+
+              buf = bp;
+              size = end - bp;
+
+	      /* verify the mouse code (0 and 1005 are always valid) */
+	      int x = D_mouse_parse.params[CSI_PX];
+              int y = D_mouse_parse.params[CSI_PY];
+              int bias = D_mouse_parse.sgrmode? 1 : 33;
+
+              x -= bias;
+              y -= bias;
+
+	      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)
+	                {
+                          char tmp[MAX_MOUSE_SEQUENCE+1];
+                          int n;
+
+                          x += bias;
+                          y += bias;
+
+                          if (D_mouse_parse.sgrmode)
+                            {
+                              n = snprintf(
+                                      tmp, MAX_MOUSE_SEQUENCE, "\033[<%d;%d;%d%c",
+                                      D_mouse_parse.params[CSI_PB], x, y, c);
+                            }
+                          else
+                            {
+                              n = snprintf(
+                                      tmp, MAX_MOUSE_SEQUENCE, "\033[M%c%c%c",
+                                      D_mouse_parse.params[CSI_PB], x, y);
+                            }
+
+                          if (n > MAX_MOUSE_SEQUENCE)
+                                n = MAX_MOUSE_SEQUENCE;
+
+                          /* emit sequence */
+                          buf -= n;
+                          size += n;
+                          memcpy(buf, tmp, n);
+                        }
+                    }
+                }
+	      else if (D_mousetrack)
+                {
+	          /* 'focus' to the clicked region, only on mouse up */
+                  int focus = 0;
+                  if (D_mouse_parse.sgrmode)
+                    focus = (c == 'm');
+                  else
+                    focus = (D_mouse_parse.params[CSI_PB] == '#');
+                  
+	          struct canvas *cv = FindCanvas(x, y);
+	          if (focus && cv)
+	            {
+	              SetForeCanvas(display, cv);
+	              /* XXX: Do we want to reset the input buffer? */
+	            }
+	        }
+
+	      D_mouse_parse.state = CSI_INACTIVE;
+            }
 	}
+
+      if (D_mouse_parse.state != CSI_INACTIVE)
+        {
+          /* suppress partial sequence at end */
+          size = mark? mark - buf : 0;
+        }
     }
+
+  if (size > 0)
+    disp_processinput(display, buf, size);
+}
+
+static void
+disp_processinput(display, buf, size)
+     struct display* display;
+     unsigned char* buf;
+     int size;
+{
 #ifdef ENCODINGS
   if (D_encoding != (D_forecv ? D_forecv->c_layer->l_encoding : 0))
     {
diff -r 9e18ca205b6d display.h
--- a/display.h	Wed Jan 10 10:54:31 2018 -0700
+++ b/display.h	Thu Jan 11 22:03:10 2018 +0000
@@ -58,6 +58,13 @@
 
 struct win;			/* forward declaration */
 
+struct mouse_parse
+{
+  char sgrmode;                 /* non-zero if parsing an SGR sequence */
+  char state;                   /* current state of parsing */
+  int params[3];                /* parsed params: button, x, y */
+};
+
 struct display
 {
   struct display *d_next;	/* linked list */
@@ -100,6 +107,8 @@
   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 */
+  struct mouse_parse d_mouse_parse;		/* state of mouse code parsing */
   int	d_mousetrack;		/* set when user wants to use mouse even when the window
 				   does not */
 #ifdef RXVT_OSC
@@ -227,6 +236,8 @@
 #define D_hstatus	DISPLAY(d_hstatus)
 #define D_lp_missing	DISPLAY(d_lp_missing)
 #define D_mouse		DISPLAY(d_mouse)
+#define D_mouse_parse	DISPLAY(d_mouse_parse)
+#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 -r 9e18ca205b6d extern.h
--- a/extern.h	Wed Jan 10 10:54:31 2018 -0700
+++ b/extern.h	Thu Jan 11 22:03:10 2018 +0000
@@ -289,6 +289,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));
@@ -449,6 +450,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 -r 9e18ca205b6d layer.c
--- a/layer.c	Wed Jan 10 10:54:31 2018 -0700
+++ b/layer.c	Thu Jan 11 22:03:10 2018 +0000
@@ -904,6 +904,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 -r 9e18ca205b6d window.c
--- a/window.c	Wed Jan 10 10:54:31 2018 -0700
+++ b/window.c	Thu Jan 11 22:03:10 2018 +0000
@@ -508,6 +508,7 @@
       ReverseVideo(fore->w_revvid);
       CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis);
       MouseMode(fore->w_mouse);
+      ExtMouseMode(fore->w_extmouse);
     }
 }
 
diff -r 9e18ca205b6d window.h
--- a/window.h	Wed Jan 10 10:54:31 2018 -0700
+++ b/window.h	Thu Jan 11 22:03:10 2018 +0000
@@ -240,6 +240,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,1006 */
 #ifdef HAVE_BRAILLE
   int	 w_bd_x, w_bd_y;	/* Braille cursor position */
 #endif
