;;; xt-mouse.el --- support the mouse when emacs run in an xterm ;; Copyright (C) 1994, 2000-2014 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 ;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-binding (key-binding (if (symbolp down-where) (vector down-where down-command) (vector down-command)))) (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"))) ;; 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))) (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 (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) (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 () (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 ;;