(define-key function-key-map "\e[M" 'xterm-mouse-translate)
(defvar xterm-mouse-last)
(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."
(save-excursion
(save-window-excursion
(deactivate-mark)
(let* ((xterm-mouse-last)
(down (xterm-mouse-event))
(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)))))
(unless is-click
(unless (and (eq (read-char) ?\e)
(eq (read-char) ?\[)
(eq (read-char) ?M))
(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))
(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
(if (eq down-where click-where)
(list click)
(list
(list 'mouse-movement click-data)
(if (symbolp down-where)
0
(list (intern (format "drag-mouse-%d"
(+ 1 xterm-mouse-last)))
down-data click-data)))))
(if (and (symbolp down-where)
(consp down-where))
(vector (list down-where down-data) down)
(vector down))))))))
(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)
(defun xterm-mouse-position-function (pos)
"Bound to `mouse-position-function' in XTerm mouse mode."
(setcdr pos (cons xterm-mouse-x xterm-mouse-y))
pos)
(defun xterm-mouse-event-read ()
(let ((c (read-char)))
(if (< c 0)
(+ c #x8000000 128)
c)))
(defun xterm-mouse-truncate-wrap (f)
"Truncate with wrap-around."
(condition-case nil
(truncate f)
(range-error
(let* ((maxwrap (* 65536 2048))
(dbig (truncate (/ f maxwrap)))
(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))
(timestamp (xterm-mouse-truncate-wrap
(* 1000
(- (float-time)
(or xt-mouse-epoch
(setq xt-mouse-epoch (float-time)))))))
(mouse (intern
(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)
(format "mouse-%d" (+ 1 xterm-mouse-last)))
(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)))
(setq xterm-mouse-x x
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)))))
(define-minor-mode xterm-mouse-mode
"Toggle XTerm mouse mode.
With prefix arg, turn XTerm mouse mode on iff arg is positive.
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
works for simple uses of the mouse. Basically, only non-modified
single clicks are supported. When turned on, the normal xterm
mouse functionality for such clicks is still available by holding
down the SHIFT key while pressing the mouse button."
:global t :group 'mouse
(if xterm-mouse-mode
(unless window-system
(setq mouse-position-function #'xterm-mouse-position-function)
(turn-on-xterm-mouse-tracking))
(turn-off-xterm-mouse-tracking 'force)
(setq mouse-position-function nil)))
(defun turn-on-xterm-mouse-tracking ()
"Enable Emacs mouse tracking in xterm."
(if xterm-mouse-mode
(send-string-to-terminal "\e[?1000h")))
(defun turn-off-xterm-mouse-tracking (&optional force)
"Disable Emacs mouse tracking in xterm."
(if (or force xterm-mouse-mode)
(send-string-to-terminal "\e[?1000l")))
(add-hook 'suspend-hook 'turn-off-xterm-mouse-tracking)
(add-hook 'suspend-resume-hook 'turn-on-xterm-mouse-tracking)
(add-hook 'kill-emacs-hook 'turn-off-xterm-mouse-tracking)
(provide 'xt-mouse)