diff options
| author | Jared Finder | 2025-12-29 12:35:24 -0800 |
|---|---|---|
| committer | Sean Whitton | 2026-01-10 12:49:43 +0000 |
| commit | 4e779d20f1840fef380f5688ceb2cd80658bde0b (patch) | |
| tree | 79ad054524a73dced6d382a99d0a6a476f7ad1f9 | |
| parent | 88d3101fdd10f5e922aae9d99fcfd103a33747db (diff) | |
| download | emacs-4e779d20f1840fef380f5688ceb2cd80658bde0b.tar.gz emacs-4e779d20f1840fef380f5688ceb2cd80658bde0b.zip | |
Update cursor display using Xterm escape sequences
* lisp/term/xterm.el (xterm-update-cursor): New user option.
(xterm--init): Use it.
(xterm--post-command-hook): New function for all xterm
functionality installed in 'post-command-hook'.
(xterm--init-frame-title): Install it.
(xterm--init-update-cursor, xterm--set-cursor-type)
(xterm--update-cursor-type, xterm--update-cursor-color): New
functions.
(xterm--cursor-type-to-int): New constant.
* doc/emacs/display.texi (Cursor Display):
* etc/NEWS: Document the new feature.
| -rw-r--r-- | doc/emacs/display.texi | 53 | ||||
| -rw-r--r-- | etc/NEWS | 9 | ||||
| -rw-r--r-- | lisp/term/xterm.el | 111 |
3 files changed, 152 insertions, 21 deletions
diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 05a25323543..d475fc3cfde 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi | |||
| @@ -2048,20 +2048,41 @@ variable @code{visible-cursor} is @code{nil} when Emacs starts or | |||
| 2048 | resumes, it uses the normal cursor. | 2048 | resumes, it uses the normal cursor. |
| 2049 | 2049 | ||
| 2050 | @vindex cursor-type | 2050 | @vindex cursor-type |
| 2051 | On a graphical display, many more properties of the text cursor can | 2051 | On a graphical display and many Xterm-compatible text terminals, the |
| 2052 | be altered. To customize its color, change the @code{:background} | 2052 | color and shape of the text cursor can be altered. To customize its |
| 2053 | attribute of the face named @code{cursor} (@pxref{Face | 2053 | color, change the @code{:background} attribute of the face named |
| 2054 | Customization}). (The other attributes of this face have no effect; | 2054 | @code{cursor} (@pxref{Face Customization}). (The other attributes of |
| 2055 | the text shown under the cursor is drawn using the frame's background | 2055 | this face have no effect; the text shown under the cursor is drawn using |
| 2056 | color.) To change its shape, customize the buffer-local variable | 2056 | the frame's background color.) To change its shape, customize the |
| 2057 | @code{cursor-type}; possible values are @code{box} (the default), | 2057 | buffer-local variable @code{cursor-type}; possible values are @code{box} |
| 2058 | @code{(box . @var{size})} (box cursor becoming a hollow box under | 2058 | (the default), @code{(box . @var{size})} (box cursor becoming a hollow |
| 2059 | masked images larger than @var{size} pixels in either dimension), | 2059 | box under masked images larger than @var{size} pixels in either |
| 2060 | @code{hollow} (a hollow box), @code{bar} (a vertical bar), @code{(bar | 2060 | dimension), @code{hollow} (a hollow box), @code{bar} (a vertical bar), |
| 2061 | . @var{n})} (a vertical bar @var{n} pixels wide), @code{hbar} (a | 2061 | @code{(bar . @var{n})} (a vertical bar @var{n} pixels wide), @code{hbar} |
| 2062 | horizontal bar), @code{(hbar . @var{n})} (a horizontal bar @var{n} | 2062 | (a horizontal bar), @code{(hbar . @var{n})} (a horizontal bar @var{n} |
| 2063 | pixels tall), or @code{nil} (no cursor at all). | 2063 | pixels tall), or @code{nil} (no cursor at all). |
| 2064 | 2064 | ||
| 2065 | @vindex xterm-update-cursor | ||
| 2066 | On Xterm-compatible text terminals cursor customiztaion is controlled | ||
| 2067 | by the user option @code{xterm-update-cursor}. Valid values are: | ||
| 2068 | @code{t} to update the cursor's color and shape, @code{type} to update | ||
| 2069 | the cursor's shape only, @code{color} to update the cursor's color only, | ||
| 2070 | and @code{nil} to not update the cursor's appearance. Xterm-compatible | ||
| 2071 | text terminals can not display a hollow box and instead use a filled | ||
| 2072 | box. Similarly, Xterm-compatible text terminals ignore the pixel sizes | ||
| 2073 | for @code{bar} and @code{hbar}. | ||
| 2074 | |||
| 2075 | @findex hl-line-mode | ||
| 2076 | @findex global-hl-line-mode | ||
| 2077 | @cindex highlight current line | ||
| 2078 | To make the cursor even more visible, you can use HL Line mode, a | ||
| 2079 | minor mode that highlights the line containing point. Use @kbd{M-x | ||
| 2080 | hl-line-mode} to enable or disable it in the current buffer. @kbd{M-x | ||
| 2081 | global-hl-line-mode} enables or disables the same mode globally. | ||
| 2082 | |||
| 2083 | The remaining controls only work on graphical displays where Emacs can | ||
| 2084 | fully control the way the cursor appears. | ||
| 2085 | |||
| 2065 | @findex blink-cursor-mode | 2086 | @findex blink-cursor-mode |
| 2066 | @cindex cursor, blinking | 2087 | @cindex cursor, blinking |
| 2067 | @cindex blinking cursor | 2088 | @cindex blinking cursor |
| @@ -2105,14 +2126,6 @@ non-blinking hollow box. (For a bar cursor, it instead appears as a | |||
| 2105 | thinner bar.) To turn off cursors in non-selected windows, change the | 2126 | thinner bar.) To turn off cursors in non-selected windows, change the |
| 2106 | variable @code{cursor-in-non-selected-windows} to @code{nil}. | 2127 | variable @code{cursor-in-non-selected-windows} to @code{nil}. |
| 2107 | 2128 | ||
| 2108 | @findex hl-line-mode | ||
| 2109 | @findex global-hl-line-mode | ||
| 2110 | @cindex highlight current line | ||
| 2111 | To make the cursor even more visible, you can use HL Line mode, a | ||
| 2112 | minor mode that highlights the line containing point. Use @kbd{M-x | ||
| 2113 | hl-line-mode} to enable or disable it in the current buffer. @kbd{M-x | ||
| 2114 | global-hl-line-mode} enables or disables the same mode globally. | ||
| 2115 | |||
| 2116 | @node Line Truncation | 2129 | @node Line Truncation |
| 2117 | @section Line Truncation | 2130 | @section Line Truncation |
| 2118 | 2131 | ||
| @@ -772,6 +772,15 @@ Emacs previously discarded arguments to emacsclient of zero length, such | |||
| 772 | as in 'emacsclient --eval "(length (pop server-eval-args-left))" ""'. | 772 | as in 'emacsclient --eval "(length (pop server-eval-args-left))" ""'. |
| 773 | These are no longer discarded. | 773 | These are no longer discarded. |
| 774 | 774 | ||
| 775 | +++ | ||
| 776 | ** New user option 'xterm-update-cursor' to update cursor display on TTYs. | ||
| 777 | When enabled, Emacs sends Xterm escape sequences on Xterm-compatible | ||
| 778 | terminals to update the cursor's appearacse. Emacs can update the | ||
| 779 | cursor's shape and color. For example, if you use a purple bar cursor | ||
| 780 | on graphical displays then when this option is enabled Emacs will use a | ||
| 781 | purple bar cursor on compatible terminals as well. See the Info node | ||
| 782 | "(emacs) Cursor Display" for more information. | ||
| 783 | |||
| 775 | 784 | ||
| 776 | * Editing Changes in Emacs 31.1 | 785 | * Editing Changes in Emacs 31.1 |
| 777 | 786 | ||
diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index dd179c4e3eb..47e82decb03 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el | |||
| @@ -80,6 +80,20 @@ capabilities, and only when that terminal understands bracketed paste." | |||
| 80 | :version "28.1" | 80 | :version "28.1" |
| 81 | :type 'boolean) | 81 | :type 'boolean) |
| 82 | 82 | ||
| 83 | (defcustom xterm-update-cursor nil | ||
| 84 | "If non-nil, try to update the cursor's appearance on XTerm terminals. | ||
| 85 | |||
| 86 | If set to t all supported attributes of the cursor are updated. | ||
| 87 | If set to `type' only the cursor type is updated. This uses the CSI | ||
| 88 | DECSCUSR escape sequence. | ||
| 89 | If set to `color' only the cursor color is updated. This uses the OSC | ||
| 90 | 12 escape sequence." | ||
| 91 | :version "31.1" | ||
| 92 | :type '(radio (const :tag "Do not update" nil) | ||
| 93 | (const :tag "Update" t) | ||
| 94 | (const :tag "Update type only" type) | ||
| 95 | (const :tag "Update color only" color))) | ||
| 96 | |||
| 83 | (defconst xterm-paste-ending-sequence "\e[201~" | 97 | (defconst xterm-paste-ending-sequence "\e[201~" |
| 84 | "Characters sent by the terminal to end a bracketed paste.") | 98 | "Characters sent by the terminal to end a bracketed paste.") |
| 85 | 99 | ||
| @@ -988,6 +1002,8 @@ We run the first FUNCTION whose STRING matches the input events." | |||
| 988 | 1002 | ||
| 989 | (when xterm-set-window-title | 1003 | (when xterm-set-window-title |
| 990 | (xterm--init-frame-title)) | 1004 | (xterm--init-frame-title)) |
| 1005 | (when xterm-update-cursor | ||
| 1006 | (xterm--init-update-cursor)) | ||
| 991 | 1007 | ||
| 992 | (let ((bg-color (terminal-parameter nil 'xterm--background-color)) | 1008 | (let ((bg-color (terminal-parameter nil 'xterm--background-color)) |
| 993 | (fg-color (terminal-parameter nil 'xterm--foreground-color))) | 1009 | (fg-color (terminal-parameter nil 'xterm--foreground-color))) |
| @@ -1025,6 +1041,17 @@ We run the first FUNCTION whose STRING matches the input events." | |||
| 1025 | ;; We likewise unconditionally enable support for focus tracking. | 1041 | ;; We likewise unconditionally enable support for focus tracking. |
| 1026 | (xterm--init-focus-tracking)) | 1042 | (xterm--init-focus-tracking)) |
| 1027 | 1043 | ||
| 1044 | (defun xterm--post-command-hook () | ||
| 1045 | "Hook for xterm features that need to be frequently updated." | ||
| 1046 | |||
| 1047 | (unless (display-graphic-p) | ||
| 1048 | (when xterm-set-window-title | ||
| 1049 | (xterm-set-window-title)) | ||
| 1050 | (when (memq xterm-update-cursor '(t type)) | ||
| 1051 | (xterm--update-cursor-type)) | ||
| 1052 | (when (memq xterm-update-cursor '(t color)) | ||
| 1053 | (xterm--update-cursor-color)))) | ||
| 1054 | |||
| 1028 | (defun terminal-init-xterm () | 1055 | (defun terminal-init-xterm () |
| 1029 | "Terminal initialization function for xterm." | 1056 | "Terminal initialization function for xterm." |
| 1030 | (unwind-protect | 1057 | (unwind-protect |
| @@ -1067,7 +1094,7 @@ We run the first FUNCTION whose STRING matches the input events." | |||
| 1067 | (xterm-set-window-title) | 1094 | (xterm-set-window-title) |
| 1068 | (add-hook 'after-make-frame-functions 'xterm-set-window-title-flag) | 1095 | (add-hook 'after-make-frame-functions 'xterm-set-window-title-flag) |
| 1069 | (add-hook 'window-configuration-change-hook 'xterm-unset-window-title-flag) | 1096 | (add-hook 'window-configuration-change-hook 'xterm-unset-window-title-flag) |
| 1070 | (add-hook 'post-command-hook 'xterm-set-window-title) | 1097 | (add-hook 'post-command-hook 'xterm--post-command-hook) |
| 1071 | (add-hook 'minibuffer-exit-hook 'xterm-set-window-title)) | 1098 | (add-hook 'minibuffer-exit-hook 'xterm-set-window-title)) |
| 1072 | 1099 | ||
| 1073 | (defvar xterm-window-title-flag nil | 1100 | (defvar xterm-window-title-flag nil |
| @@ -1300,6 +1327,88 @@ versions of xterm." | |||
| 1300 | (b (caddr fg-color))) | 1327 | (b (caddr fg-color))) |
| 1301 | (set-face-foreground 'default (format "#%04x%04x%04x" r g b) frame))))) | 1328 | (set-face-foreground 'default (format "#%04x%04x%04x" r g b) frame))))) |
| 1302 | 1329 | ||
| 1330 | (defun xterm--init-update-cursor () | ||
| 1331 | "Register hooks to run `xterm--update-cursor-type' appropriately." | ||
| 1332 | |||
| 1333 | (when (memq xterm-update-cursor '(color t)) | ||
| 1334 | (xterm--query | ||
| 1335 | "\e]12;?\e\\" | ||
| 1336 | '(("\e]12;" . (lambda () | ||
| 1337 | (let ((str (xterm--read-string ?\e ?\\))) | ||
| 1338 | ;; The response is specifically formated to set the | ||
| 1339 | ;; color | ||
| 1340 | (push | ||
| 1341 | (concat "\e]12;" str "\e\\") | ||
| 1342 | (terminal-parameter nil 'tty-mode-reset-strings))))))) | ||
| 1343 | ;; No need to set tty-mode-set-strings because | ||
| 1344 | ;; xterm--post-command-hook handles restoring the cursor color. | ||
| 1345 | |||
| 1346 | (xterm--update-cursor-color)) | ||
| 1347 | |||
| 1348 | (when (memq xterm-update-cursor '(type t)) | ||
| 1349 | (xterm--update-cursor-type)) | ||
| 1350 | |||
| 1351 | (add-hook 'post-command-hook 'xterm--post-command-hook)) | ||
| 1352 | |||
| 1353 | (defconst xterm--cursor-type-to-int | ||
| 1354 | '(nil 0 | ||
| 1355 | box 1 | ||
| 1356 | hollow 1 | ||
| 1357 | bar 5 | ||
| 1358 | hbar 3) | ||
| 1359 | "Mapping of cursor type symbols to control sequence integers. | ||
| 1360 | |||
| 1361 | Cursor type symbols are the same as for `cursor-type'.") | ||
| 1362 | |||
| 1363 | (defun xterm--set-cursor-type (terminal type) | ||
| 1364 | (let ((type-int (or (plist-get xterm--cursor-type-to-int type) 1)) | ||
| 1365 | (old (terminal-parameter terminal 'xterm--cursor-style))) | ||
| 1366 | |||
| 1367 | (when old | ||
| 1368 | (set-terminal-parameter | ||
| 1369 | terminal | ||
| 1370 | 'tty-mode-set-strings | ||
| 1371 | (delete (format "\e[%d q" old) | ||
| 1372 | (terminal-parameter terminal 'tty-mode-set-strings)))) | ||
| 1373 | (let ((set-string (format "\e[%d q" type-int))) | ||
| 1374 | (push set-string (terminal-parameter terminal 'tty-mode-set-strings)) | ||
| 1375 | (send-string-to-terminal set-string terminal)) | ||
| 1376 | (unless old | ||
| 1377 | ;; Assume that the default cursor is appropriate when exiting Emacs. | ||
| 1378 | (push "\e[0 q" (terminal-parameter terminal 'tty-mode-reset-strings))) | ||
| 1379 | |||
| 1380 | (set-terminal-parameter terminal 'xterm--cursor-type type-int))) | ||
| 1381 | |||
| 1382 | (defun xterm--update-cursor-type () | ||
| 1383 | "Update the cursor type for Xterm-compatible terminals. | ||
| 1384 | This updates the selected frame's terminal based on `cursor-type'." | ||
| 1385 | (let ((buffer-cursor cursor-type) | ||
| 1386 | (window-cursor (window-cursor-type)) | ||
| 1387 | (frame-cursor (frame-parameter nil 'cursor-type)) | ||
| 1388 | type) | ||
| 1389 | ;; All of them can be conses, in which case the type symbol is the car. | ||
| 1390 | (when (consp buffer-cursor) (setf buffer-cursor (car buffer-cursor))) | ||
| 1391 | (when (consp window-cursor) (setf window-cursor (car window-cursor))) | ||
| 1392 | (when (consp frame-cursor) (setf frame-cursor (car frame-cursor))) | ||
| 1393 | |||
| 1394 | (cond | ||
| 1395 | ((not (eq window-cursor t)) | ||
| 1396 | (setf type window-cursor)) | ||
| 1397 | ((not (eq buffer-cursor t)) | ||
| 1398 | (setf type buffer-cursor)) | ||
| 1399 | (t | ||
| 1400 | (setf type frame-cursor))) | ||
| 1401 | (xterm--set-cursor-type nil type))) | ||
| 1402 | |||
| 1403 | (defun xterm--update-cursor-color () | ||
| 1404 | "Update the cursor color for Xterm-compatible terminals. | ||
| 1405 | This updates the selected frame's terminal based on the face `cursor'." | ||
| 1406 | (let* ((color (color-values (face-background 'cursor))) | ||
| 1407 | (r (nth 0 color)) | ||
| 1408 | (g (nth 1 color)) | ||
| 1409 | (b (nth 2 color))) | ||
| 1410 | (send-string-to-terminal (format "\e]12;rgb:%04x/%04x/%04x\e\\" r g b)))) | ||
| 1411 | |||
| 1303 | (provide 'xterm) ;Backward compatibility. | 1412 | (provide 'xterm) ;Backward compatibility. |
| 1304 | (provide 'term/xterm) | 1413 | (provide 'term/xterm) |
| 1305 | ;;; xterm.el ends here | 1414 | ;;; xterm.el ends here |