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 /lisp/term | |
| 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.
Diffstat (limited to 'lisp/term')
| -rw-r--r-- | lisp/term/xterm.el | 111 |
1 files changed, 110 insertions, 1 deletions
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 |