;;; xt-mouse.el --- support the mouse when emacs run in an xterm ;; Copyright (C) 1994, 2000-2013 Free Software Foundation, Inc. ;; Author: Per Abrahamsen ;; Keywords: mouse, terminals ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; Enable mouse support when running inside an xterm. ;; This is actually useful when you are running X11 locally, but is ;; working on remote machine over a modem line or through a gateway. ;; It works by translating xterm escape codes into generic emacs mouse ;; events so it should work with any package that uses the mouse. ;; You don't have to turn off xterm mode to use the normal xterm mouse ;; functionality, it is still available by holding down the SHIFT key ;; when you press the mouse button. ;;; Todo: ;; Support multi-click -- somehow. ;;; Code: (defvar xterm-mouse-debug-buffer nil) (defvar xterm-mouse-last) ;; 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)) (put event-type 'event-kind 'mouse-click)) (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 nil) (down (xterm-mouse-event extension)) (down-command (nth 0 down)) (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))))) ;; Retrieve the expected preface for the up-event. (unless is-click (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"))) ;; Process the up-event. (let* ((click (if is-click down (xterm-mouse-event extension))) (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)) (setq unread-command-events (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)))))))) ;; These two variables have been converted to terminal parameters. ;; ;;(defvar xterm-mouse-x 0 ;; "Position of last xterm mouse event relative to the frame.") ;; ;;(defvar xterm-mouse-y 0 ;; "Position of last xterm mouse event relative to the frame.") (defvar xt-mouse-epoch nil) ;; Indicator for the xterm-mouse mode. (defun xterm-mouse-position-function (pos) "Bound to `mouse-position-function' in XTerm mouse mode." (when (terminal-parameter nil 'xterm-mouse-x) (setcdr pos (cons (terminal-parameter nil 'xterm-mouse-x) (terminal-parameter nil 'xterm-mouse-y)))) pos) ;; Read XTerm sequences above ASCII 127 (#x7f) (defun xterm-mouse-event-read () ;; We get the characters decoded by the keyboard coding system. Try ;; to recover the raw character. (let ((c (read-event))) (cond ;; If meta-flag is t we get a meta character ((>= c ?\M-\^@) (- c (- ?\M-\^@ 128))) ;; Reencode the character in the keyboard coding system, if ;; this is a non-ASCII character. ((>= c #x80) (aref (encode-coding-string (string c) (keyboard-coding-system)) 0)) (t c)))) (defun xterm-mouse-truncate-wrap (f) "Truncate with wrap-around." (condition-case nil ;; First try the built-in truncate, in case there's no overflow. (truncate f) ;; In case of overflow, do wraparound by hand. (range-error ;; In our case, we wrap around every 3 days or so, so if we assume ;; a maximum of 65536 wraparounds, we're safe for a couple years. ;; Using a power of 2 makes rounding errors less likely. (let* ((maxwrap (* 65536 2048)) (dbig (truncate (/ f maxwrap))) (fdiff (- f (* 1.0 maxwrap dbig)))) (+ (truncate fdiff) (* maxwrap dbig)))))) ;; Normal terminal mouse click reporting: expect three bytes, of the ;; form . Return a list (EVENT-TYPE X Y). (defun xterm-mouse--read-event-sequence-1000 () (list (let ((code (- (xterm-mouse-event-read) 32))) (intern ;; 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) (format "M-down-mouse-%d" (- code 7))) ((= code 11) (format "M-mouse-%d" (- xterm-mouse-last 7))) ((= code 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 code) (format "down-mouse-%d" (+ 1 code)))))) ;; x and y coordinates (- (xterm-mouse-event-read) 33) (- (xterm-mouse-event-read) 33))) ;; XTerm's 1006-mode terminal mouse click reporting has the form ;;