diff options
| author | Philipp Stephani | 2016-03-25 13:17:38 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2016-03-25 13:17:38 +0300 |
| commit | 90fb9b38dd4a386a71cdf7c3bf6b42292db43c42 (patch) | |
| tree | 4ebbe0a89215012e8519b5ec580a014d293ac920 | |
| parent | f14d463661f46f614125f88b56d73106fab9ece6 (diff) | |
| download | emacs-90fb9b38dd4a386a71cdf7c3bf6b42292db43c42.tar.gz emacs-90fb9b38dd4a386a71cdf7c3bf6b42292db43c42.zip | |
Add customization option for using UTF-8 coordinates in xt-mouse
* lisp/xt-mouse.el (xterm-mouse-utf-8): New customization option.
(xterm-mouse--read-coordinate): New function to replace
`xterm-mouse--read-utf8-char'; uses UTF-8 only if enabled.
(xterm-mouse--read-number-from-terminal): Adapt to new name.
(xterm-mouse-tracking-enable-sequence)
(xterm-mouse-tracking-disable-sequence): Replace constants with
functions, mark constants as obsolete.
(xterm-mouse--tracking-sequence): New helper function.
(turn-on-xterm-mouse-tracking-on-terminal): Use new functions;
enable UTF-8 only if customization option says so; store UTF-8
flag in terminal parameter. (Bug#23009)
* test/automated/xt-mouse-tests.el: Add tests for xt-mouse.el.
| -rw-r--r-- | lisp/xt-mouse.el | 128 | ||||
| -rw-r--r-- | test/automated/xt-mouse-tests.el | 110 |
2 files changed, 208 insertions, 30 deletions
diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index 5975e60272f..b6738b21cb0 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el | |||
| @@ -134,23 +134,34 @@ http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)." | |||
| 134 | (fdiff (- f (* 1.0 maxwrap dbig)))) | 134 | (fdiff (- f (* 1.0 maxwrap dbig)))) |
| 135 | (+ (truncate fdiff) (* maxwrap dbig)))))) | 135 | (+ (truncate fdiff) (* maxwrap dbig)))))) |
| 136 | 136 | ||
| 137 | (defun xterm-mouse--read-utf8-char (&optional prompt seconds) | 137 | (defcustom xterm-mouse-utf-8 nil |
| 138 | "Read an utf-8 encoded character from the current terminal. | 138 | "Non-nil if UTF-8 coordinates should be used to read mouse coordinates. |
| 139 | This function reads and returns an utf-8 encoded character of | 139 | Set this to non-nil if you are sure that your terminal |
| 140 | command input. If the user generates an event which is not a | 140 | understands UTF-8 coordinates, but not SGR coordinates." |
| 141 | character (i.e., a mouse click or function key event), read-char | 141 | :type 'boolean |
| 142 | signals an error. | 142 | :risky t |
| 143 | 143 | :group 'xterm) | |
| 144 | The returned event may come directly from the user, or from a | 144 | |
| 145 | keyboard macro. It is not decoded by the keyboard's input coding | 145 | (defun xterm-mouse--read-coordinate () |
| 146 | system and always treated with an utf-8 input encoding. | 146 | "Read a mouse coordinate from the current terminal. |
| 147 | 147 | If `xterm-mouse-utf-8' was non-nil when | |
| 148 | The optional arguments PROMPT and SECONDS work like in | 148 | `turn-on-xterm-mouse-tracking-on-terminal' was called, reads the |
| 149 | `read-event'." | 149 | coordinate as an UTF-8 code unit sequence; otherwise, reads a |
| 150 | (let ((tmp (keyboard-coding-system))) | 150 | single byte." |
| 151 | (set-keyboard-coding-system 'utf-8) | 151 | (let ((previous-keyboard-coding-system (keyboard-coding-system))) |
| 152 | (prog1 (read-event prompt t seconds) | 152 | (unwind-protect |
| 153 | (set-keyboard-coding-system tmp)))) | 153 | (progn |
| 154 | (set-keyboard-coding-system | ||
| 155 | (if (terminal-parameter nil 'xterm-mouse-utf-8) | ||
| 156 | 'utf-8-unix | ||
| 157 | ;; Use Latin-1 instead of no-conversion to avoid flicker | ||
| 158 | ;; due to `set-keyboard-coding-system' changing the meta | ||
| 159 | ;; mode. | ||
| 160 | 'latin-1)) | ||
| 161 | ;; Wait only a little; we assume that the entire escape sequence | ||
| 162 | ;; has already been sent when this function is called. | ||
| 163 | (read-char nil nil 0.1)) | ||
| 164 | (set-keyboard-coding-system previous-keyboard-coding-system)))) | ||
| 154 | 165 | ||
| 155 | ;; In default mode, each numeric parameter of XTerm's mouse report is | 166 | ;; In default mode, each numeric parameter of XTerm's mouse report is |
| 156 | ;; a single char, possibly encoded as utf-8. The actual numeric | 167 | ;; a single char, possibly encoded as utf-8. The actual numeric |
| @@ -170,7 +181,7 @@ The optional arguments PROMPT and SECONDS work like in | |||
| 170 | (<= ?0 c ?9)) | 181 | (<= ?0 c ?9)) |
| 171 | (setq n (+ (* 10 n) c (- ?0)))) | 182 | (setq n (+ (* 10 n) c (- ?0)))) |
| 172 | (cons n c)) | 183 | (cons n c)) |
| 173 | (cons (- (setq c (xterm-mouse--read-utf8-char)) 32) c)))) | 184 | (cons (- (setq c (xterm-mouse--read-coordinate)) 32) c)))) |
| 174 | 185 | ||
| 175 | ;; XTerm reports mouse events as | 186 | ;; XTerm reports mouse events as |
| 176 | ;; <EVENT-CODE> <X> <Y> in default mode, and | 187 | ;; <EVENT-CODE> <X> <Y> in default mode, and |
| @@ -314,6 +325,38 @@ down the SHIFT key while pressing the mouse button." | |||
| 314 | (mapc #'turn-off-xterm-mouse-tracking-on-terminal (terminal-list)) | 325 | (mapc #'turn-off-xterm-mouse-tracking-on-terminal (terminal-list)) |
| 315 | (setq mouse-position-function nil))) | 326 | (setq mouse-position-function nil))) |
| 316 | 327 | ||
| 328 | (defun xterm-mouse-tracking-enable-sequence () | ||
| 329 | "Return a control sequence to enable XTerm mouse tracking. | ||
| 330 | The returned control sequence enables basic mouse tracking, mouse | ||
| 331 | motion events and finally extended tracking on terminals that | ||
| 332 | support it. The following escape sequences are understood by | ||
| 333 | modern xterms: | ||
| 334 | |||
| 335 | \"\\e[?1000h\" \"Basic mouse mode\": Enables reports for mouse | ||
| 336 | clicks. There is a limit to the maximum row/column | ||
| 337 | position (<= 223), which can be reported in this | ||
| 338 | basic mode. | ||
| 339 | |||
| 340 | \"\\e[?1002h\" \"Mouse motion mode\": Enables reports for mouse | ||
| 341 | motion events during dragging operations. | ||
| 342 | |||
| 343 | \"\\e[?1005h\" \"UTF-8 coordinate extension\": Enables an | ||
| 344 | extension to the basic mouse mode, which uses UTF-8 | ||
| 345 | characters to overcome the 223 row/column limit. | ||
| 346 | This extension may conflict with non UTF-8 | ||
| 347 | applications or non UTF-8 locales. It is only | ||
| 348 | enabled when the option `xterm-mouse-utf-8' is | ||
| 349 | non-nil. | ||
| 350 | |||
| 351 | \"\\e[?1006h\" \"SGR coordinate extension\": Enables a newer | ||
| 352 | alternative extension to the basic mouse mode, which | ||
| 353 | overcomes the 223 row/column limit without the | ||
| 354 | drawbacks of the UTF-8 coordinate extension. | ||
| 355 | |||
| 356 | The two extension modes are mutually exclusive, where the last | ||
| 357 | given escape sequence takes precedence over the former." | ||
| 358 | (apply #'concat (xterm-mouse--tracking-sequence ?h))) | ||
| 359 | |||
| 317 | (defconst xterm-mouse-tracking-enable-sequence | 360 | (defconst xterm-mouse-tracking-enable-sequence |
| 318 | "\e[?1000h\e[?1002h\e[?1005h\e[?1006h" | 361 | "\e[?1000h\e[?1002h\e[?1005h\e[?1006h" |
| 319 | "Control sequence to enable xterm mouse tracking. | 362 | "Control sequence to enable xterm mouse tracking. |
| @@ -343,10 +386,34 @@ escape sequences are understood by modern xterms: | |||
| 343 | The two extension modes are mutually exclusive, where the last | 386 | The two extension modes are mutually exclusive, where the last |
| 344 | given escape sequence takes precedence over the former.") | 387 | given escape sequence takes precedence over the former.") |
| 345 | 388 | ||
| 389 | (make-obsolete-variable | ||
| 390 | 'xterm-mouse-tracking-enable-sequence | ||
| 391 | "use the function `xterm-mouse-tracking-enable-sequence' instead." | ||
| 392 | "25.1") | ||
| 393 | |||
| 394 | (defun xterm-mouse-tracking-disable-sequence () | ||
| 395 | "Return a control sequence to disable XTerm mouse tracking. | ||
| 396 | The control sequence resets the modes set by | ||
| 397 | `xterm-mouse-tracking-enable-sequence'." | ||
| 398 | (apply #'concat (nreverse (xterm-mouse--tracking-sequence ?l)))) | ||
| 399 | |||
| 346 | (defconst xterm-mouse-tracking-disable-sequence | 400 | (defconst xterm-mouse-tracking-disable-sequence |
| 347 | "\e[?1006l\e[?1005l\e[?1002l\e[?1000l" | 401 | "\e[?1006l\e[?1005l\e[?1002l\e[?1000l" |
| 348 | "Reset the modes set by `xterm-mouse-tracking-enable-sequence'.") | 402 | "Reset the modes set by `xterm-mouse-tracking-enable-sequence'.") |
| 349 | 403 | ||
| 404 | (make-obsolete-variable | ||
| 405 | 'xterm-mouse-tracking-disable-sequence | ||
| 406 | "use the function `xterm-mouse-tracking-disable-sequence' instead." | ||
| 407 | "25.1") | ||
| 408 | |||
| 409 | (defun xterm-mouse--tracking-sequence (suffix) | ||
| 410 | "Return a control sequence to enable or disable mouse tracking. | ||
| 411 | SUFFIX is the last character of each escape sequence (?h to | ||
| 412 | enable, ?l to disable)." | ||
| 413 | (mapcar | ||
| 414 | (lambda (code) (format "\e[?%d%c" code suffix)) | ||
| 415 | `(1000 1002 ,@(when xterm-mouse-utf-8 '(1005)) 1006))) | ||
| 416 | |||
| 350 | (defun turn-on-xterm-mouse-tracking-on-terminal (&optional terminal) | 417 | (defun turn-on-xterm-mouse-tracking-on-terminal (&optional terminal) |
| 351 | "Enable xterm mouse tracking on TERMINAL." | 418 | "Enable xterm mouse tracking on TERMINAL." |
| 352 | (when (and xterm-mouse-mode (eq t (terminal-live-p terminal)) | 419 | (when (and xterm-mouse-mode (eq t (terminal-live-p terminal)) |
| @@ -360,18 +427,19 @@ given escape sequence takes precedence over the former.") | |||
| 360 | (with-selected-frame (car (frames-on-display-list terminal)) | 427 | (with-selected-frame (car (frames-on-display-list terminal)) |
| 361 | (define-key input-decode-map "\e[M" 'xterm-mouse-translate) | 428 | (define-key input-decode-map "\e[M" 'xterm-mouse-translate) |
| 362 | (define-key input-decode-map "\e[<" 'xterm-mouse-translate-extended)) | 429 | (define-key input-decode-map "\e[<" 'xterm-mouse-translate-extended)) |
| 363 | (condition-case err | 430 | (let ((enable (xterm-mouse-tracking-enable-sequence)) |
| 364 | (send-string-to-terminal xterm-mouse-tracking-enable-sequence | 431 | (disable (xterm-mouse-tracking-disable-sequence))) |
| 365 | terminal) | 432 | (condition-case err |
| 366 | ;; FIXME: This should use a dedicated error signal. | 433 | (send-string-to-terminal enable terminal) |
| 367 | (error (if (equal (cadr err) "Terminal is currently suspended") | 434 | ;; FIXME: This should use a dedicated error signal. |
| 368 | nil ;The sequence will be sent upon resume. | 435 | (error (if (equal (cadr err) "Terminal is currently suspended") |
| 369 | (signal (car err) (cdr err))))) | 436 | nil ; The sequence will be sent upon resume. |
| 370 | (push xterm-mouse-tracking-enable-sequence | 437 | (signal (car err) (cdr err))))) |
| 371 | (terminal-parameter nil 'tty-mode-set-strings)) | 438 | (push enable (terminal-parameter nil 'tty-mode-set-strings)) |
| 372 | (push xterm-mouse-tracking-disable-sequence | 439 | (push disable (terminal-parameter nil 'tty-mode-reset-strings)) |
| 373 | (terminal-parameter nil 'tty-mode-reset-strings)) | 440 | (set-terminal-parameter terminal 'xterm-mouse-mode t) |
| 374 | (set-terminal-parameter terminal 'xterm-mouse-mode t)))) | 441 | (set-terminal-parameter terminal 'xterm-mouse-utf-8 |
| 442 | xterm-mouse-utf-8))))) | ||
| 375 | 443 | ||
| 376 | (defun turn-off-xterm-mouse-tracking-on-terminal (terminal) | 444 | (defun turn-off-xterm-mouse-tracking-on-terminal (terminal) |
| 377 | "Disable xterm mouse tracking on TERMINAL." | 445 | "Disable xterm mouse tracking on TERMINAL." |
diff --git a/test/automated/xt-mouse-tests.el b/test/automated/xt-mouse-tests.el new file mode 100644 index 00000000000..c7e835c0311 --- /dev/null +++ b/test/automated/xt-mouse-tests.el | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | ;;; xt-mouse-tests.el --- Test suite for xt-mouse. -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2016 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Philipp Stephani <phst@google.com> | ||
| 6 | |||
| 7 | ;; This file is part of GNU Emacs. | ||
| 8 | |||
| 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 10 | ;; it under the terms of the GNU General Public License as published by | ||
| 11 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 12 | ;; (at your option) any later version. | ||
| 13 | |||
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | ;; GNU General Public License for more details. | ||
| 18 | |||
| 19 | ;; You should have received a copy of the GNU General Public License | ||
| 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | |||
| 22 | ;;; Commentary: | ||
| 23 | |||
| 24 | ;;; Code: | ||
| 25 | |||
| 26 | (require 'xt-mouse) | ||
| 27 | |||
| 28 | (defmacro with-xterm-mouse-mode (&rest body) | ||
| 29 | "Run BODY with `xterm-mouse-mode' temporarily enabled." | ||
| 30 | (declare (indent 0)) | ||
| 31 | ;; Make the frame huge so that the test input events below don't hit | ||
| 32 | ;; the menu bar. | ||
| 33 | `(cl-letf (((frame-width nil) 2000) | ||
| 34 | ((frame-height nil) 2000) | ||
| 35 | ;; Reset XTerm parameters so that the tests don't get | ||
| 36 | ;; confused. | ||
| 37 | ((terminal-parameter nil 'xterm-mouse-x) nil) | ||
| 38 | ((terminal-parameter nil 'xterm-mouse-y) nil) | ||
| 39 | ((terminal-parameter nil 'xterm-mouse-last-down) nil) | ||
| 40 | ((terminal-parameter nil 'xterm-mouse-last-click) nil)) | ||
| 41 | (if xterm-mouse-mode | ||
| 42 | (progn ,@body) | ||
| 43 | (unwind-protect | ||
| 44 | (progn | ||
| 45 | ;; `xterm-mouse-mode' doesn't work in the initial | ||
| 46 | ;; terminal. Since we can't create a second terminal in | ||
| 47 | ;; batch mode, fake it temporarily. | ||
| 48 | (cl-letf (((symbol-function 'terminal-name) | ||
| 49 | (lambda (&optional _terminal) "fake-terminal"))) | ||
| 50 | (xterm-mouse-mode)) | ||
| 51 | ,@body) | ||
| 52 | (xterm-mouse-mode 0))))) | ||
| 53 | |||
| 54 | (ert-deftest xt-mouse-tracking-basic () | ||
| 55 | (should (equal (xterm-mouse-tracking-enable-sequence) | ||
| 56 | "\e[?1000h\e[?1002h\e[?1006h")) | ||
| 57 | (should (equal (xterm-mouse-tracking-disable-sequence) | ||
| 58 | "\e[?1006l\e[?1002l\e[?1000l")) | ||
| 59 | (with-xterm-mouse-mode | ||
| 60 | (should xterm-mouse-mode) | ||
| 61 | (should (terminal-parameter nil 'xterm-mouse-mode)) | ||
| 62 | (should-not (terminal-parameter nil 'xterm-mouse-utf-8)) | ||
| 63 | (let* ((unread-command-events (append "\e[M%\xD9\x81" | ||
| 64 | "\e[M'\xD9\x81" nil)) | ||
| 65 | (key (read-key))) | ||
| 66 | (should (consp key)) | ||
| 67 | (cl-destructuring-bind (event-type position . rest) key | ||
| 68 | (should (equal event-type 'S-mouse-2)) | ||
| 69 | (should (consp position)) | ||
| 70 | (cl-destructuring-bind (_ _ xy . rest) position | ||
| 71 | (should (equal xy '(184 . 95)))))))) | ||
| 72 | |||
| 73 | (ert-deftest xt-mouse-tracking-utf-8 () | ||
| 74 | (let ((xterm-mouse-utf-8 t)) | ||
| 75 | (should (equal (xterm-mouse-tracking-enable-sequence) | ||
| 76 | "\e[?1000h\e[?1002h\e[?1005h\e[?1006h")) | ||
| 77 | (should (equal (xterm-mouse-tracking-disable-sequence) | ||
| 78 | "\e[?1006l\e[?1005l\e[?1002l\e[?1000l")) | ||
| 79 | (with-xterm-mouse-mode | ||
| 80 | (should xterm-mouse-mode) | ||
| 81 | (should (terminal-parameter nil 'xterm-mouse-mode)) | ||
| 82 | (should (terminal-parameter nil 'xterm-mouse-utf-8)) | ||
| 83 | ;; The keyboard driver doesn't decode bytes in | ||
| 84 | ;; `unread-command-events'. | ||
| 85 | (let* ((unread-command-events (append "\e[M%\u0640\u0131" | ||
| 86 | "\e[M'\u0640\u0131" nil)) | ||
| 87 | (key (read-key))) | ||
| 88 | (should (consp key)) | ||
| 89 | (cl-destructuring-bind (event-type position . rest) key | ||
| 90 | (should (equal event-type 'S-mouse-2)) | ||
| 91 | (should (consp position)) | ||
| 92 | (cl-destructuring-bind (_ _ xy . rest) position | ||
| 93 | (should (equal xy '(1567 . 271))))))))) | ||
| 94 | |||
| 95 | (ert-deftest xt-mouse-tracking-sgr () | ||
| 96 | (with-xterm-mouse-mode | ||
| 97 | (should xterm-mouse-mode) | ||
| 98 | (should (terminal-parameter nil 'xterm-mouse-mode)) | ||
| 99 | (should-not (terminal-parameter nil 'xterm-mouse-utf-8)) | ||
| 100 | (let* ((unread-command-events (append "\e[<5;1569;273;M" | ||
| 101 | "\e[<5;1569;273;m" nil)) | ||
| 102 | (key (read-key))) | ||
| 103 | (should (consp key)) | ||
| 104 | (cl-destructuring-bind (event-type position . rest) key | ||
| 105 | (should (equal event-type 'S-mouse-2)) | ||
| 106 | (should (consp position)) | ||
| 107 | (cl-destructuring-bind (_ _ xy . rest) position | ||
| 108 | (should (equal xy '(1568 . 271)))))))) | ||
| 109 | |||
| 110 | ;;; xt-mouse-tests.el ends here | ||