1;;; xt-mouse.el --- support the mouse when emacs run in an xterm 2 3;; Copyright (C) 1994, 2000, 2001, 2002, 2003, 4;; 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 5 6;; Author: Per Abrahamsen <abraham@dina.kvl.dk> 7;; Keywords: mouse, terminals 8 9;; This file is part of GNU Emacs. 10 11;; GNU Emacs is free software; you can redistribute it and/or modify 12;; it under the terms of the GNU General Public License as published by 13;; the Free Software Foundation; either version 2, or (at your option) 14;; any later version. 15 16;; GNU Emacs is distributed in the hope that it will be useful, 17;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19;; GNU General Public License for more details. 20 21;; You should have received a copy of the GNU General Public License 22;; along with GNU Emacs; see the file COPYING. If not, write to the 23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24;; Boston, MA 02110-1301, USA. 25 26;;; Commentary: 27 28;; Enable mouse support when running inside an xterm. 29 30;; This is actually useful when you are running X11 locally, but is 31;; working on remote machine over a modem line or through a gateway. 32 33;; It works by translating xterm escape codes into generic emacs mouse 34;; events so it should work with any package that uses the mouse. 35 36;; You don't have to turn off xterm mode to use the normal xterm mouse 37;; functionality, it is still available by holding down the SHIFT key 38;; when you press the mouse button. 39 40;;; Todo: 41 42;; Support multi-click -- somehow. 43 44;;; Code: 45 46(define-key function-key-map "\e[M" 'xterm-mouse-translate) 47 48(defvar xterm-mouse-last) 49 50;; Mouse events symbols must have an 'event-kind property with 51;; the value 'mouse-click. 52(dolist (event-type '(mouse-1 mouse-2 mouse-3 53 M-down-mouse-1 M-down-mouse-2 M-down-mouse-3)) 54 (put event-type 'event-kind 'mouse-click)) 55 56(defun xterm-mouse-translate (event) 57 "Read a click and release event from XTerm." 58 (save-excursion 59 (save-window-excursion 60 (deactivate-mark) 61 (let* ((xterm-mouse-last) 62 (down (xterm-mouse-event)) 63 (down-command (nth 0 down)) 64 (down-data (nth 1 down)) 65 (down-where (nth 1 down-data)) 66 (down-binding (key-binding (if (symbolp down-where) 67 (vector down-where down-command) 68 (vector down-command)))) 69 (is-click (string-match "^mouse" (symbol-name (car down))))) 70 71 (unless is-click 72 (unless (and (eq (read-char) ?\e) 73 (eq (read-char) ?\[) 74 (eq (read-char) ?M)) 75 (error "Unexpected escape sequence from XTerm"))) 76 77 (let* ((click (if is-click down (xterm-mouse-event))) 78 (click-command (nth 0 click)) 79 (click-data (nth 1 click)) 80 (click-where (nth 1 click-data))) 81 (if (memq down-binding '(nil ignore)) 82 (if (and (symbolp click-where) 83 (consp click-where)) 84 (vector (list click-where click-data) click) 85 (vector click)) 86 (setq unread-command-events 87 (if (eq down-where click-where) 88 (list click) 89 (list 90 ;; Cheat `mouse-drag-region' with move event. 91 (list 'mouse-movement click-data) 92 ;; Generate a drag event. 93 (if (symbolp down-where) 94 0 95 (list (intern (format "drag-mouse-%d" 96 (+ 1 xterm-mouse-last))) 97 down-data click-data))))) 98 (if (and (symbolp down-where) 99 (consp down-where)) 100 (vector (list down-where down-data) down) 101 (vector down)))))))) 102 103(defvar xterm-mouse-x 0 104 "Position of last xterm mouse event relative to the frame.") 105 106(defvar xterm-mouse-y 0 107 "Position of last xterm mouse event relative to the frame.") 108 109(defvar xt-mouse-epoch nil) 110 111;; Indicator for the xterm-mouse mode. 112 113(defun xterm-mouse-position-function (pos) 114 "Bound to `mouse-position-function' in XTerm mouse mode." 115 (setcdr pos (cons xterm-mouse-x xterm-mouse-y)) 116 pos) 117 118;; read xterm sequences above ascii 127 (#x7f) 119(defun xterm-mouse-event-read () 120 (let ((c (read-char))) 121 (if (< c 0) 122 (+ c #x8000000 128) 123 c))) 124 125(defun xterm-mouse-truncate-wrap (f) 126 "Truncate with wrap-around." 127 (condition-case nil 128 ;; First try the built-in truncate, in case there's no overflow. 129 (truncate f) 130 ;; In case of overflow, do wraparound by hand. 131 (range-error 132 ;; In our case, we wrap around every 3 days or so, so if we assume 133 ;; a maximum of 65536 wraparounds, we're safe for a couple years. 134 ;; Using a power of 2 makes rounding errors less likely. 135 (let* ((maxwrap (* 65536 2048)) 136 (dbig (truncate (/ f maxwrap))) 137 (fdiff (- f (* 1.0 maxwrap dbig)))) 138 (+ (truncate fdiff) (* maxwrap dbig)))))) 139 140 141(defun xterm-mouse-event () 142 "Convert XTerm mouse event to Emacs mouse event." 143 (let* ((type (- (xterm-mouse-event-read) #o40)) 144 (x (- (xterm-mouse-event-read) #o40 1)) 145 (y (- (xterm-mouse-event-read) #o40 1)) 146 ;; Emulate timestamp information. This is accurate enough 147 ;; for default value of mouse-1-click-follows-link (450msec). 148 (timestamp (xterm-mouse-truncate-wrap 149 (* 1000 150 (- (float-time) 151 (or xt-mouse-epoch 152 (setq xt-mouse-epoch (float-time))))))) 153 (mouse (intern 154 ;; For buttons > 3, the release-event looks 155 ;; differently (see xc/programs/xterm/button.c, 156 ;; function EditorButton), and there seems to come in 157 ;; a release-event only, no down-event. 158 (cond ((>= type 64) 159 (format "mouse-%d" (- type 60))) 160 ((memq type '(8 9 10)) 161 (setq xterm-mouse-last type) 162 (format "M-down-mouse-%d" (- type 7))) 163 ((= type 11) 164 (format "mouse-%d" (- xterm-mouse-last 7))) 165 ((= type 3) 166 (format "mouse-%d" (+ 1 xterm-mouse-last))) 167 (t 168 (setq xterm-mouse-last type) 169 (format "down-mouse-%d" (+ 1 type)))))) 170 (w (window-at x y)) 171 (ltrb (window-edges w)) 172 (left (nth 0 ltrb)) 173 (top (nth 1 ltrb))) 174 175 (setq xterm-mouse-x x 176 xterm-mouse-y y) 177 (setq 178 last-input-event 179 (list mouse 180 (let ((event (if w 181 (posn-at-x-y (- x left) (- y top) w t) 182 (append (list nil 'menu-bar) 183 (nthcdr 2 (posn-at-x-y x y)))))) 184 (setcar (nthcdr 3 event) timestamp) 185 event))))) 186 187;;;###autoload 188(define-minor-mode xterm-mouse-mode 189 "Toggle XTerm mouse mode. 190With prefix arg, turn XTerm mouse mode on iff arg is positive. 191 192Turn it on to use Emacs mouse commands, and off to use xterm mouse commands. 193This works in terminal emulators compatible with xterm. It only 194works for simple uses of the mouse. Basically, only non-modified 195single clicks are supported. When turned on, the normal xterm 196mouse functionality for such clicks is still available by holding 197down the SHIFT key while pressing the mouse button." 198 :global t :group 'mouse 199 (if xterm-mouse-mode 200 ;; Turn it on 201 (unless window-system 202 (setq mouse-position-function #'xterm-mouse-position-function) 203 (turn-on-xterm-mouse-tracking)) 204 ;; Turn it off 205 (turn-off-xterm-mouse-tracking 'force) 206 (setq mouse-position-function nil))) 207 208(defun turn-on-xterm-mouse-tracking () 209 "Enable Emacs mouse tracking in xterm." 210 (if xterm-mouse-mode 211 (send-string-to-terminal "\e[?1000h"))) 212 213(defun turn-off-xterm-mouse-tracking (&optional force) 214 "Disable Emacs mouse tracking in xterm." 215 (if (or force xterm-mouse-mode) 216 (send-string-to-terminal "\e[?1000l"))) 217 218;; Restore normal mouse behaviour outside Emacs. 219(add-hook 'suspend-hook 'turn-off-xterm-mouse-tracking) 220(add-hook 'suspend-resume-hook 'turn-on-xterm-mouse-tracking) 221(add-hook 'kill-emacs-hook 'turn-off-xterm-mouse-tracking) 222 223(provide 'xt-mouse) 224 225;; arch-tag: 84962d4e-fae9-4c13-a9d7-ef4925a4ac03 226;;; xt-mouse.el ends here 227