HideIfDef mode bug fixes and enhancements. This is #2 of 3 patches based
[bpt/emacs.git] / lisp / xt-mouse.el
index 4269a17..59ed68a 100644 (file)
@@ -1,7 +1,6 @@
 ;;; xt-mouse.el --- support the mouse when emacs run in an xterm
 
-;; Copyright (C) 1994, 2000, 2001, 2002, 2003,
-;;   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+;; Copyright (C) 1994, 2000-2014 Free Software Foundation, Inc.
 
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: mouse, terminals
 ;; Mouse events symbols must have an 'event-kind property with
 ;; the value 'mouse-click.
 (dolist (event-type '(mouse-1 mouse-2 mouse-3
-                             M-down-mouse-1 M-down-mouse-2 M-down-mouse-3))
+                     M-down-mouse-1 M-down-mouse-2 M-down-mouse-3))
   (put event-type 'event-kind 'mouse-click))
 
-(defun xterm-mouse-translate (event)
+(defun xterm-mouse-translate (_event)
   "Read a click and release event from XTerm."
+  (xterm-mouse-translate-1))
+
+(defun xterm-mouse-translate-extended (_event)
+  "Read a click and release event from XTerm.
+Similar to `xterm-mouse-translate', but using the \"1006\"
+extension, which supports coordinates >= 231 (see
+http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)."
+  (xterm-mouse-translate-1 1006))
+
+(defun xterm-mouse-translate-1 (&optional extension)
   (save-excursion
-    (save-window-excursion
-      (deactivate-mark)
-      (let* ((xterm-mouse-last)
-            (down (xterm-mouse-event))
+    (save-window-excursion              ;FIXME: Why?
+      (deactivate-mark)                 ;FIXME: Why?
+      (let* ((xterm-mouse-last nil)
+            (down (xterm-mouse-event extension))
             (down-command (nth 0 down))
-            (down-data (nth 1 down))
-            (down-where (nth 1 down-data))
+            (down-data    (nth 1 down))
+            (down-where   (nth 1 down-data))
             (down-binding (key-binding (if (symbolp down-where)
                                            (vector down-where down-command)
                                          (vector down-command))))
-            (is-click (string-match "^mouse" (symbol-name (car down)))))
-
-       (unless is-click
-         (unless (and (eq (read-char) ?\e)
-                      (eq (read-char) ?\[)
-                      (eq (read-char) ?M))
+            (is-down (string-match "down" (symbol-name (car down)))))
+
+       ;; Retrieve the expected preface for the up-event.
+       (when is-down
+         (unless (cond ((null extension)
+                        (and (eq (read-event) ?\e)
+                             (eq (read-event) ?\[)
+                             (eq (read-event) ?M)))
+                       ((eq extension 1006)
+                        (and (eq (read-event) ?\e)
+                             (eq (read-event) ?\[)
+                             (eq (read-event) ?<))))
            (error "Unexpected escape sequence from XTerm")))
 
-       (let* ((click (if is-click down (xterm-mouse-event)))
-              ;; (click-command (nth 0 click))
-              (click-data (nth 1 click))
+       ;; Process the up-event.
+       (let* ((click (if is-down (xterm-mouse-event extension) down))
+              (click-data  (nth 1 click))
               (click-where (nth 1 click-data)))
-         (if (memq down-binding '(nil ignore))
-             (if (and (symbolp click-where)
-                      (consp click-where))
-                 (vector (list click-where click-data) click)
-               (vector click))
+          (cond
+           ((null down) nil)
+           ((memq down-binding '(nil ignore))
+            (if (and (symbolp click-where)
+                     (consp click-where))
+                (vector (list click-where click-data) click)
+              (vector click)))
+           (t
            (setq unread-command-events
-                 (if (eq down-where click-where)
-                     (list click)
-                   (list
-                    ;; Cheat `mouse-drag-region' with move event.
-                    (list 'mouse-movement click-data)
-                    ;; Generate a drag event.
-                    (if (symbolp down-where)
-                        0
-                      (list (intern (format "drag-mouse-%d"
-                                            (+ 1 xterm-mouse-last)))
-                            down-data click-data)))))
+                 (append (if (eq down-where click-where)
+                             (list click)
+                           (list
+                            ;; Cheat `mouse-drag-region' with move event.
+                            (list 'mouse-movement click-data)
+                            ;; Generate a drag event.
+                            (if (symbolp down-where)
+                                0
+                              (list (intern (format "drag-mouse-%d"
+                                                    (1+ xterm-mouse-last)))
+                                    down-data click-data))))
+                         unread-command-events))
            (if xterm-mouse-debug-buffer
                (print unread-command-events xterm-mouse-debug-buffer))
            (if (and (symbolp down-where)
                     (consp down-where))
                (vector (list down-where down-data) down)
-             (vector down))))))))
+             (vector down)))))))))
 
 ;; These two variables have been converted to terminal parameters.
 ;;
                      (terminal-parameter nil 'xterm-mouse-y))))
   pos)
 
-;; read xterm sequences above ascii 127 (#x7f)
-(defun xterm-mouse-event-read ()
-  (let ((c (read-char)))
-    (if (> c #x3FFF80)
-        (+ 128 (- c #x3FFF80))
-      c)))
-
 (defun xterm-mouse-truncate-wrap (f)
   "Truncate with wrap-around."
   (condition-case nil
             (fdiff (- f (* 1.0 maxwrap dbig))))
        (+ (truncate fdiff) (* maxwrap dbig))))))
 
-(defun xterm-mouse-event ()
-  "Convert XTerm mouse event to Emacs mouse event."
-  (let* ((type (- (xterm-mouse-event-read) #o40))
-        (x (- (xterm-mouse-event-read) #o40 1))
-        (y (- (xterm-mouse-event-read) #o40 1))
-        ;; Emulate timestamp information.  This is accurate enough
-        ;; for default value of mouse-1-click-follows-link (450msec).
-        (timestamp (xterm-mouse-truncate-wrap
-                     (* 1000
-                        (- (float-time)
-                           (or xt-mouse-epoch
-                               (setq xt-mouse-epoch (float-time)))))))
-         (mouse (intern
-                ;; For buttons > 3, the release-event looks
-                ;; differently (see xc/programs/xterm/button.c,
-                ;; function EditorButton), and there seems to come in
-                ;; a release-event only, no down-event.
-                (cond ((>= type 64)
-                       (format "mouse-%d" (- type 60)))
-                      ((memq type '(8 9 10))
-                       (setq xterm-mouse-last type)
-                       (format "M-down-mouse-%d" (- type 7)))
-                      ((= type 11)
-                       (format "mouse-%d" (- xterm-mouse-last 7)))
-                      ((= type 3)
-                       ;; For buttons > 5 xterm only reports a
-                       ;; button-release event.  Avoid error by mapping
-                       ;; them all to mouse-1.
-                       (format "mouse-%d" (+ 1 (or xterm-mouse-last 0))))
-                      (t
-                       (setq xterm-mouse-last type)
-                       (format "down-mouse-%d" (+ 1 type))))))
-        (w (window-at x y))
-         (ltrb (window-edges w))
-         (left (nth 0 ltrb))
-         (top (nth 1 ltrb)))
-
-    (set-terminal-parameter nil 'xterm-mouse-x x)
-    (set-terminal-parameter nil 'xterm-mouse-y y)
-    (setq
-     last-input-event
-     (list mouse
-          (let ((event (if w
-                           (posn-at-x-y (- x left) (- y top) w t)
-                         (append (list nil 'menu-bar)
-                                 (nthcdr 2 (posn-at-x-y x y))))))
-            (setcar (nthcdr 3 event) timestamp)
-            event)))))
+;; Normal terminal mouse click reporting: expect three bytes, of the
+;; form <BUTTON+32> <X+32> <Y+32>.  Return a list (EVENT-TYPE X Y).
+(defun xterm-mouse--read-event-sequence-1000 ()
+  (let* ((code (- (read-event) 32))
+         (type
+          ;; For buttons > 3, the release-event looks differently
+          ;; (see xc/programs/xterm/button.c, function EditorButton),
+          ;; and come in a release-event only, no down-event.
+          (cond ((>= code 64)
+                 (format "mouse-%d" (- code 60)))
+                ((memq code '(8 9 10))
+                 (setq xterm-mouse-last (- code 8))
+                 (format "M-down-mouse-%d" (- code 7)))
+                ((and (= code 11) xterm-mouse-last)
+                 (format "M-mouse-%d" (1+ xterm-mouse-last)))
+                ((and (= code 3) xterm-mouse-last)
+                 ;; For buttons > 5 xterm only reports a button-release event.
+                 ;; Drop them since they're not usable and can be spurious.
+                 (format "mouse-%d" (1+ xterm-mouse-last)))
+                ((memq code '(0 1 2))
+                 (setq xterm-mouse-last code)
+                 (format "down-mouse-%d" (+ 1 code)))))
+         (x (- (read-event) 33))
+         (y (- (read-event) 33)))
+    (and type (wholenump x) (wholenump y)
+         (list (intern type) x y))))
+
+;; XTerm's 1006-mode terminal mouse click reporting has the form
+;; <BUTTON> ; <X> ; <Y> <M or m>, where the button and ordinates are
+;; in encoded (decimal) form.  Return a list (EVENT-TYPE X Y).
+(defun xterm-mouse--read-event-sequence-1006 ()
+  (let (button-bytes x-bytes y-bytes c)
+    (while (not (eq (setq c (read-event)) ?\;))
+      (push c button-bytes))
+    (while (not (eq (setq c (read-event)) ?\;))
+      (push c x-bytes))
+    (while (not (memq (setq c (read-event)) '(?m ?M)))
+      (push c y-bytes))
+    (list (let* ((code (string-to-number
+                       (apply 'string (nreverse button-bytes))))
+                (wheel (>= code 64))
+                (down (and (not wheel)
+                           (eq c ?M))))
+           (intern (format "%s%smouse-%d"
+                           (cond (wheel "")
+                                 ((< code 4)  "")
+                                 ((< code 8)  "S-")
+                                 ((< code 12) "M-")
+                                 ((< code 16) "M-S-")
+                                 ((< code 20) "C-")
+                                 ((< code 24) "C-S-")
+                                 ((< code 28) "C-M-")
+                                 ((< code 32) "C-M-S-")
+                                 (t
+                                  (error "Unexpected escape sequence from XTerm")))
+                           (if down "down-" "")
+                           (if wheel
+                               (- code 60)
+                             (1+ (setq xterm-mouse-last (mod code 4)))))))
+         (1- (string-to-number (apply 'string (nreverse x-bytes))))
+         (1- (string-to-number (apply 'string (nreverse y-bytes)))))))
+
+(defun xterm-mouse-event (&optional extension)
+  "Convert XTerm mouse event to Emacs mouse event.
+EXTENSION, if non-nil, means to use an extension to the usual
+terminal mouse protocol; we currently support the value 1006,
+which is the \"1006\" extension implemented in Xterm >= 277."
+  (let* ((click (cond ((null extension)
+                      (xterm-mouse--read-event-sequence-1000))
+                     ((eq extension 1006)
+                      (xterm-mouse--read-event-sequence-1006))
+                     (t
+                      (error "Unsupported XTerm mouse protocol")))))
+    (when click
+      (let* ((type (nth 0 click))
+             (x    (nth 1 click))
+             (y    (nth 2 click))
+             ;; Emulate timestamp information.  This is accurate enough
+             ;; for default value of mouse-1-click-follows-link (450msec).
+             (timestamp (xterm-mouse-truncate-wrap
+                         (* 1000
+                            (- (float-time)
+                               (or xt-mouse-epoch
+                                   (setq xt-mouse-epoch (float-time)))))))
+             (w (window-at x y))
+             (ltrb (window-edges w))
+             (left (nth 0 ltrb))
+             (top (nth 1 ltrb)))
+        (set-terminal-parameter nil 'xterm-mouse-x x)
+        (set-terminal-parameter nil 'xterm-mouse-y y)
+        (setq
+         last-input-event
+         (list type
+               (let ((event (if w
+                                (posn-at-x-y (- x left) (- y top) w t)
+                              (append (list nil 'menu-bar)
+                                      (nthcdr 2 (posn-at-x-y x y))))))
+                 (setcar (nthcdr 3 event) timestamp)
+                 event)))))))
 
 ;;;###autoload
 (define-minor-mode xterm-mouse-mode
   "Toggle XTerm mouse mode.
-With prefix arg, turn XTerm mouse mode on if arg is positive, otherwise turn
-it off.
+With a prefix argument ARG, enable XTerm mouse mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+the mode if ARG is omitted or nil.
 
 Turn it on to use Emacs mouse commands, and off to use xterm mouse commands.
 This works in terminal emulators compatible with xterm.  It only
@@ -229,7 +293,7 @@ down the SHIFT key while pressing the mouse button."
   (dolist (terminal (terminal-list))
     (turn-on-xterm-mouse-tracking-on-terminal terminal)))
 
-(defun turn-off-xterm-mouse-tracking (&optional force)
+(defun turn-off-xterm-mouse-tracking (&optional _force)
   "Disable Emacs mouse tracking in xterm."
   (dolist (terminal (terminal-list))
     (turn-off-xterm-mouse-tracking-on-terminal terminal)))
@@ -241,11 +305,14 @@ down the SHIFT key while pressing the mouse button."
             ;; FIXME: is there more elegant way to detect the initial terminal?
             (not (string= (terminal-name terminal) "initial_terminal")))
     (unless (terminal-parameter terminal 'xterm-mouse-mode)
-      ;; Simulate selecting a terminal by selecting one of its frames ;-(
+      ;; Simulate selecting a terminal by selecting one of its frames
       (with-selected-frame (car (frames-on-display-list terminal))
-        (define-key input-decode-map "\e[M" 'xterm-mouse-translate))
+        (define-key input-decode-map "\e[M" 'xterm-mouse-translate)
+        (define-key input-decode-map "\e[<" 'xterm-mouse-translate-extended))
       (set-terminal-parameter terminal 'xterm-mouse-mode t))
-    (send-string-to-terminal "\e[?1000h" terminal)))
+    (send-string-to-terminal "\e[?1000h" terminal)
+    ;; Request extended mouse support, if available (xterm >= 277).
+    (send-string-to-terminal "\e[?1006h" terminal)))
 
 (defun turn-off-xterm-mouse-tracking-on-terminal (terminal)
   "Disable xterm mouse tracking on TERMINAL."
@@ -261,7 +328,8 @@ down the SHIFT key while pressing the mouse button."
     ;; command too many times (or to catch an unintended key sequence), than
     ;; to send it too few times (or to fail to let xterm-mouse events
     ;; pass by untranslated).
-    (send-string-to-terminal "\e[?1000l" terminal)))
+    (send-string-to-terminal "\e[?1000l" terminal)
+    (send-string-to-terminal "\e[?1006l" terminal)))
 
 (provide 'xt-mouse)