summaryrefslogtreecommitdiffstats
path: root/lisp/xterm-kitty.el
diff options
context:
space:
mode:
authorRavi R Kiran <aine.marina@gmail.com>2021-05-10 00:27:16 (GMT)
committerRavi R Kiran <aine.marina@gmail.com>2021-05-10 00:27:16 (GMT)
commitcc0fe1d30b5b9da44f003f9658fccf52e52b4a8c (patch)
treeb4d6d91d6ab7e8f31980c46d0167e1bc07f9e69d /lisp/xterm-kitty.el
parent606af070a7914315670561a876b2d8d64ed96434 (diff)
downloaddotemacs-cc0fe1d30b5b9da44f003f9658fccf52e52b4a8c.zip
dotemacs-cc0fe1d30b5b9da44f003f9658fccf52e52b4a8c.tar.gz
dotemacs-cc0fe1d30b5b9da44f003f9658fccf52e52b4a8c.tar.bz2
Make xterm-kitty into a real terminal initialization handler
Diffstat (limited to 'lisp/xterm-kitty.el')
-rw-r--r--lisp/xterm-kitty.el486
1 files changed, 0 insertions, 486 deletions
diff --git a/lisp/xterm-kitty.el b/lisp/xterm-kitty.el
deleted file mode 100644
index 906c032..0000000
--- a/lisp/xterm-kitty.el
+++ /dev/null
@@ -1,486 +0,0 @@
-;;; xterm-kitty.el --- kitty terminal support
-
-;; Copyright (C) 2021 Ravi Kiran
-
-;; Author: Ravi Kiran <aine.marina@gmail.com>
-;; Keywords:
-
-;; This program 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.
-
-;; This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Work-in-progress to support kitty terminal with all modifiers
-;; - not ready yet to be moved into term/xterm-kitty.el
-;; - still need to
-;; + ensure proper 24-bit color support
-;; + reduce/formalize dependencies on xterm.el
-;; + provide init-terminal-xterm-kitty
-
-;;; Code:
-
-(require 'term/xterm)
-
-(defvar xterm-kitty-in-use
- (and (string-match "^xterm-kitty" (getenv-internal "TERM" initial-environment)) t)
- "Currently running under xterm-kitty.")
-
-(defvar xterm-kitty-modifiers-alist
- '((1 . shift) (2 . alt) (4 . control) (8 . super) (16 . hyper) (32 . meta))
- "Modifier mapping; SHIFT must always be present as first element with value 1.
-
-Changing the values allows for swapping modifiers. For example
- (1 . shift) (2 . alt) (4 . control) (8 . meta) (16 . hyper) (32 . super)
-would swap meta and super.")
-
-(defvar xterm-kitty-escape-prefix "\e["
- "CSI escape sequence generated by kitty.")
-
-(defvar xterm-kitty-shift-alist
- `(,@(mapcar (lambda (p) (cons (aref p 0) (aref p 1)))
- (split-string "`~ 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+ [{ ]} \\| ;: '\" ,< .> /?"))
- ,@(mapcar (lambda (c) (cons c (- c (- ?a ?A))))
- (number-sequence ?a ?z)))
- "Characters produced by shifted keys; used to convert shifted keybindings.")
-
-;; ------------------------------------------------------------------------------------
-;; Implementation
-
-(defconst xterm-kitty--prefix-alist
- '((shift . "S-") (alt . "A-") (control . "C-") (super . "s-") (hyper . "H-") (meta . "M-"))
- "Modifier prefixes.")
-(defconst xterm-kitty--bitset-alist
- (mapcar (lambda (a) (cons (car a) (ash 1 (cdr a))))
- '((shift . 25) (alt . 22) (control . 26) (super . 23) (hyper . 24) (meta . 27)))
- "Modifier bits set.")
-(defconst xterm-kitty--modifier-combinations
- (number-sequence 0 (1- (ash 1 (length xterm-kitty-modifiers-alist))))
- "Numerical representation of all combinations")
-
-(defun xterm-kitty--make-modifiers-from-num (num &rest others)
- "Make a list of modifiers from NUM along with additional modifiers OTHERS."
- (let* ((bits (mapcar (lambda (idx) (logand num (ash 1 idx))) (number-sequence 0 (1- (length xterm-kitty-modifiers-alist)))))
- (found-mods (flatten-list (list (mapcar (lambda (b) (alist-get b xterm-kitty-modifiers-alist)) bits) others))))
- found-mods))
-
-(defun xterm-kitty--from-numeric-modifer (num list-map folder)
- (apply folder (mapcar (lambda (mod) (alist-get mod list-map))
- (xterm-kitty--make-modifiers-from-num num))))
-
-(defconst xterm-kitty--numeric-modifiers
- (apply #'vector (mapcar (lambda (num) (xterm-kitty--from-numeric-modifer num xterm-kitty--bitset-alist #'logior))
- xterm-kitty--modifier-combinations))
- "Numeric modifiers to apply to each printable character for kitty modifier.")
-
-(defconst xterm-kitty--prefix-modifiers
- (apply #'vector (mapcar (lambda (num) (if (> num 0)
- (xterm-kitty--from-numeric-modifer num xterm-kitty--prefix-alist #'concat)
- ""))
- xterm-kitty--modifier-combinations))
- "Symbolic prefix to apply to each printable character for kitty modifier.")
-
-;; (message (apply #'concat (mapcar (lambda (p) (format "%x " (ash p -22))) (sort xterm-kitty--numeric-modifiers '<))))
-;; (message (apply #'concat (mapcar (lambda (p) (format "%s " p)) xterm-kitty--prefix-modifiers)))
-
-(defun xterm-kitty--add-event-modifier-to-symbol (mod-string e)
- (let ((symbol (if (symbolp e) e (car e))))
- (setq symbol (intern (concat mod-string (symbol-name symbol))))
- (if (symbolp e)
- symbol
- (cons symbol (cdr e)))))
-
-(defun xterm-kitty--precompute-with-modifiers (sym)
- (apply #'vector
- (and sym
- (mapcar (lambda (mod) (vector (xterm-kitty--add-event-modifier-to-symbol mod sym)))
- xterm-kitty--prefix-modifiers))))
-
-(defvar xterm-kitty--suffix-tilde-map
- '((2 . insert)
- (3 . delete)
- (5 . prior)
- (6 . next)
- (7 . home)
- (8 . end)
- (11 . f1)
- (12 . f2)
- (13 . f3)
- (14 . f4)
- (15 . f5)
- (17 . f6)
- (18 . f7)
- (19 . f8)
- (20 . f9)
- (21 . f10)
- (23 . f11)
- (24 . f12))
- "Entries with ~ suffix.")
-(defvar xterm-kitty--suffix-tilde-precomputed
- (apply #'vector (mapcar (lambda (n) (xterm-kitty--precompute-with-modifiers (alist-get n xterm-kitty--suffix-tilde-map)))
- (number-sequence 0 (car (car (last xterm-kitty--suffix-tilde-map))))))
- "Precomputed vectors for ~ suffix.")
-
-(defvar xterm-kitty--suffix-alpha-map
- '((?A . up)
- (?B . down)
- (?C . right)
- (?D . left)
- (?F . end)
- (?H . home)
- (?P . f1)
- (?Q . f2)
- (?R . f3)
- (?S . f4))
- "Entries with alphabetic suffix.")
-(defvar xterm-kitty--suffix-alpha-precomputed
- (apply #'vector (mapcar (lambda (n) (xterm-kitty--precompute-with-modifiers (alist-get n xterm-kitty--suffix-alpha-map)))
- (number-sequence ?A (caar (last xterm-kitty--suffix-alpha-map)))))
- "Precomputed vectors for alphabetic suffix.")
-
-(defvar xterm-kitty--suffix-u-non-private
- `(?\x8 ?\x9 ?\xd ?\x1b ?\x7f ?\s ; BS, TAB, RET, ESC, DEL, SPC
- ,@(mapcar #'car xterm-kitty-shift-alist))
- "All characters in non-private unicode space")
-
-(defconst xterm-kitty--shift-modifier (car (rassoc 'shift xterm-kitty-modifiers-alist))
- "Value of the shift modifier.")
-
-(defun xterm-kitty--make-suffix (mod)
- (if (zerop mod) "" (format ";%d" (1+ mod))))
-
-(defun xterm-kitty--insert-decode-table (keymap)
- "Insert decoding table into KEYMAP"
- (let* ((all-modifiers xterm-kitty--modifier-combinations)
- (all-mod-suffixes (apply #'vector (mapcar #'xterm-kitty--make-suffix all-modifiers))))
- (mapc
- (lambda (key)
- (let ((keystr (format "%d" key)))
- (mapc
- (lambda (mod)
- (unless (zerop mod)
- (define-key keymap
- (concat xterm-kitty-escape-prefix keystr (aref all-mod-suffixes mod) "u")
- (xterm-kitty-decode-key-stroke key mod ?u))))
- all-modifiers)))
- xterm-kitty--suffix-u-non-private)
- (mapc
- (lambda (key)
- (let ((keystr (format "%d" key)))
- (mapc
- (lambda (mod)
- (define-key keymap
- (concat xterm-kitty-escape-prefix keystr (aref all-mod-suffixes mod) "~")
- (xterm-kitty-decode-key-stroke key mod ?~)))
- all-modifiers)))
- (mapcar #'car xterm-kitty--suffix-tilde-map))
- (mapc
- (lambda (suffix)
- (let ((keystr (format "%d" suffix)))
- (mapc
- (lambda (mod)
- (define-key keymap
- (concat xterm-kitty-escape-prefix
- (if (zerop mod) "" "1")
- (aref all-mod-suffixes mod)
- (string suffix))
- (xterm-kitty-decode-key-stroke 0 mod suffix)))
- all-modifiers)))
- (mapcar #'car xterm-kitty--suffix-alpha-map))))
-
-;; (setq dum2 (let ((map (make-sparse-keymap)))
-;; (xterm-kitty--insert-decode-table map)))
-;; (lookup-key dum2 (concat xterm-kitty-escape-prefix "97;5u"))
-;; (xterm-kitty--add-modifier-list '(control) ?i)
-
-(defun xterm-kitty-decode-key-stroke (keycode modifiers suffix)
- "Take KEYCODE MODIFIERS SUFFIX of the form (105,5,u) and construct key."
- ;; Ignore modifiers that we cannot understand (CapsLock and NumLock status)
- (let ((mods (logand modifiers (1- (length xterm-kitty--numeric-modifiers)))))
- (if (eql suffix ?u)
- (when (< keycode 57344)
- ;; To do: support remaining keycodes in unicode private use area
- ;; (send-string-to-terminal (format "%s" (logior keycode (aref xterm-kitty--numeric-modifiers mods))))
- (let* ((shifted-key (and (eql (logand mods xterm-kitty--shift-modifier)
- xterm-kitty--shift-modifier)
- (alist-get keycode xterm-kitty-shift-alist)))
- ;; The following is equivalent to mods & ~shift
- (new-modifiers (and shifted-key (- mods xterm-kitty--shift-modifier))))
- (vector (logior (or shifted-key keycode)
- (aref xterm-kitty--numeric-modifiers (if shifted-key new-modifiers mods))))))
- (if (eql suffix ?~)
- (if (eql keycode 200)
- (xterm-translate-bracketed-paste nil)
- (aref (aref xterm-kitty--suffix-tilde-precomputed keycode) mods))
- (when (<= ?A suffix ?S)
- (if (and (or (eql suffix ?I) (eql suffix ?O))
- (eql keycode 0)
- (eql mods 0))
- ;; xterm focus in/out; perhaps there's a better way to do this
- (if (eql suffix ?I) (xterm-translate-focus-in nil) (xterm-translate-focus-out nil))
- (aref (aref xterm-kitty--suffix-alpha-precomputed (- suffix ?A)) mods)))))))
-
-(defun xterm-kitty-handle-non-printable (keystr)
- "Split kitty non-printable keystring KEYSTR (e.g., 105;5u) and construct key"
- (let* ((suffix (aref keystr (1- (length keystr))))
- (parts (split-string keystr ";"))
- (num-parts (length parts))
- (code (string-to-number keystr)) ; will be zero for alpha suffix without modifiers
- (modifiers (if (>= num-parts 2) (1- (string-to-number (cadr parts))) 0)))
- ;; (send-string-to-terminal (format "%s %s %s" keystr code modifiers))
- (xterm-kitty-decode-key-stroke code modifiers suffix)))
-
-;; The following function is semi-obsolete, and is intended to be used solely for testing.
-(defun xterm-kitty--handle-escape-code1 (prompt)
- "Handle escape code by reading rest of keycode as string; PROMPT is ignored."
- (let* ((e (read-char))
- (complete-string (string e))
- (count 0))
- ;; There must be a faster way to create this string than one character at a time
- (while (and (or (<= ?0 e ?9)
- (eql e ?\;))
- (< count 25))
- (setq count (1+ count)) ; safety
- (setq e (read-char))
- (setq complete-string (concat complete-string (string e))))
- ;; (send-string-to-terminal complete-string)
- (xterm-kitty-handle-non-printable complete-string)))
-
-(fset 'xterm-kitty--original-read-char-exclusive (symbol-function 'read-char-exclusive))
-(defun xterm-kitty--handle-escape-code (prompt)
- "Handle keycode using integer math; PROMPT is ignored."
- (let ((keycode 0)
- (modifiers 0)
- (suffix nil)
- (current-num 0)
- (e))
- (while (not suffix)
- (setq e (xterm-kitty--original-read-char-exclusive))
- (if (<= ?0 e ?9)
- (setq current-num (+ (* current-num 10) (- e ?0)))
- (if (eql e ?\;)
- (setq keycode current-num
- current-num 0)
- (setq suffix e)
- (if (> keycode 0)
- (setq modifiers (1- current-num))
- (setq keycode current-num)))))
- ;; (message "Code: %d modifiers %d suffix: %s" keycode modifiers suffix)
- (xterm-kitty-decode-key-stroke keycode modifiers suffix)))
-
-(defvar xterm-kitty-legacy-control-map
- '(;(?\@ . 0)
- (?a . 1)
- (?b . 2)
- (?c . 3)
- (?d . 4)
- (?e . 5)
- (?f . 6)
- (?g . 7)
- (?h . 8)
- ;; (?i . 9)
- (?j . 10)
- (?k . 11)
- (?l . 12)
- ;; (?m . 13)
- (?n . 14)
- (?o . 15)
- (?p . 16)
- (?q . 17)
- (?r . 18)
- (?s . 19)
- (?t . 20)
- (?u . 21)
- (?v . 22)
- (?w . 23)
- (?x . 24)
- (?y . 25)
- (?z . 26)
- ;; (?\[ . 27)
- (?\\ . 28)
- ;; (?\] . 29)
- ;; (?^ . 30)
- ;; (?~ . 30)
- ;; (?/ . 31)
- ;; (?_ . 31)
- ;; (?? . 127)
- ;; (?0 . 48)
- ;; (?1 . 49)
- ;; (?2 . 0)
- ;; (?3 . 27)
- ;; (?4 . 28)
- ;; (?5 . 29)
- ;; (?6 . 30)
- ;; (?7 . 31)
- ;; (?8 . 127)
- ;; (?9 . 57)
- )
- "Map C- and C-M- combinations to legacy values.")
-
-(defun xterm-kitty--add-modifier-list (mod-list e)
- (let ((modifier-list (flatten-list mod-list)))
- (if (numberp e)
- (progn
- ;; (message "%d" (logior e (apply #'logior (mapcar (lambda (m) (alist-get m xterm-kitty--bitset-alist)) modifier-list))))
- (logior e (apply #'logior (mapcar (lambda (m) (alist-get m xterm-kitty--bitset-alist)) modifier-list))))
- (and e
- (if modifier-list
- (xterm-kitty--add-event-modifier-to-symbol
- (apply 'concat (mapcar (lambda (m) (alist-get m xterm-kitty--prefix-alist)) modifier-list))
- e)
- e)))))
-(defun xterm-kitty--setup-legacy-control-maps (keymap)
- (mapc
- (lambda (bind)
- ;; Redirect C- and C-M- combinations because they are part of base bindings
- (define-key keymap (vector (xterm-kitty--add-modifier-list '(control) (car bind))) (vector (cdr bind)))
- (define-key keymap
- (vector (xterm-kitty--add-modifier-list '(control meta) (car bind)))
- (vector (xterm-kitty--add-modifier-list '(meta) (cdr bind)))))
- xterm-kitty-legacy-control-map))
-
-(defun xterm-kitty-make-binding-sequence (default key &rest modifiers)
- "Make a key vector for KEY with VECTORS suitable for binding with 'define-key' if xterm-kitty is active.
-
-DEFAULT is the value to be returned if xterm-kitty is not active.
-KEY is the key to be used. MODIFIERS is a list of modifiers, or
-modifiers specified explicitly.
-
-'define-key' uses 'event-convert-list' internally, which strips
-off shift modifiers for alphabetic characters. The only way to
-avoid it is to provide the key vector itself to 'define-key',
-which this function explicitly creates. In that sense, this
-function is almost equivalent to 'event-convert-list'."
- (if xterm-kitty-in-use
- (vector (xterm-kitty--add-modifier-list modifiers key))
- default))
-
-;; To do: debug the reason that the table method does not work
-;; - the table does not seem to be reflected in input-decode-map
-;; - the table method is needed for those packages which advice
-;; low-level functions such as read-char, e.g., multiple-cursors
-(defvar xterm-kitty-use-table-method nil
- "Use table method to handle character map")
-
-(defun xterm-kitty-apply-keyboard (&optional keymap alternate-keymap)
- "Apply keyboard defintion; optionally to KEYMAP and ALTERNATE-KEYMAP."
- (let* ((kmap (or keymap
- (and xterm-kitty-in-use input-decode-map)))
- (alternate-kmap (or alternate-keymap
- (and kmap
- (not keymap)
- key-translation-map))))
- (when kmap
- (unless keymap ; default keymap was used
- (message "Applying xterm-kitty changes")
- (send-string-to-terminal "\e[>1;u")
- (push "\e[>1;u" (terminal-parameter nil 'tty-mode-set-strings))
- (push "\e[<u" (terminal-parameter nil 'tty-mode-reset-strings)))
- (if xterm-kitty-use-table-method
- (xterm-kitty--insert-decode-table kmap)
- (define-key kmap xterm-kitty-escape-prefix #'xterm-kitty--handle-escape-code)))
- (when (and alternate-kmap)
- (xterm-kitty--setup-legacy-control-maps alternate-kmap))))
-
-(defun xterm-kitty-add ()
- "This one should be replaced by init-terminal-xterm-kitty; just a hack for now."
- (when xterm-kitty-in-use
- (setq xterm-extra-capabilities '(reportBackground getSelection setSelection))
- (add-hook 'terminal-init-xterm-hook 'xterm-kitty-apply-keyboard)
- (add-hook 'terminal-init-xterm-hook 'xterm-kitty-save-window-id)
- (xterm-kitty-add-select-frame-set-input-focus-advice)))
-
-(defun xterm-kitty-window-id (&optional terminal) ; public API
- (terminal-parameter terminal 'kitty-window-id))
-
-(defun xterm-kitty--remote-control-response ()
- (let ((str "")
- prev-chr
- chr
- parsed
- payload)
- ;; The reply should be: \eP@kitty-cmd{"ok": true, "data": payload}\e\\
- (while (and (setq chr (xterm--read-event-for-query))
- (not (and (equal prev-chr ?\e) (equal chr ?\\))))
- (when prev-chr (setq str (concat str (string prev-chr))))
- (setq prev-chr chr))
- (setq parsed-data (json-parse-string str))
- (when (and (hash-table-p parsed-data) (eql (gethash "ok" parsed-data) t))
- (setq payload (gethash "data" parsed-data)))
- payload))
-
-(defun xterm-kitty--save-kitty-window-id ()
- (let* ((kitty-response (xterm-kitty--remote-control-response))
- (response-json (json-parse-string kitty-response))
- window-id)
- (mapc (lambda (os-win)
- (mapc (lambda (tab)
- (mapc (lambda (win)
- (let ((is-self (eq (gethash "is_self" win) t))
- (win-id (gethash "id" win)))
- (when is-self
- (if window-id
- (message "Multiple windows match: using %s, not %s" window-id win-id)
- (setq window-id win-id)))))
- (gethash "windows" tab)))
- (gethash "tabs" os-win)))
- response-json)
- (set-terminal-parameter nil 'kitty-window-id window-id)))
-
-(defun xterm-kitty-save-window-id ()
- "Save kitty window ID of current terminal"
- (when xterm-kitty-in-use
- (xterm--query "\eP@kitty-cmd{\"cmd\":\"ls\",\"version\":[0,19,3]}\e\\"
- '(("\eP@kitty-cmd" . xterm-kitty--save-kitty-window-id)))))
-
-(defvar xterm-kitty--focus-window-command-string
- "\eP@kitty-cmd{\"cmd\":\"focus-window\",\"version\":[0,19,3],\"no_response\":true,\"payload\":{\"match\":\"id:%d\"}}\e\\"
- "Command string to send to kitty to focus kitty window; must have a single placeholder %d")
-(defun xterm-kitty-focus (frame-or-window)
- "Set focus to terminal containing FRAME-OR-WINDOW
-
- FRAME-OR-WINDOW can be a terminal, a frame, or a window"
- (let* ((frame (if (windowp frame-or-window)
- (window-frame frame-or-window)
- frame-or-window))
- (kitty-window-id (and xterm-kitty-in-use (xterm-kitty-window-id (frame-terminal frame)))))
- ;; (message "Window id %d for %s" kitty-window-id frame)
- (when kitty-window-id
- (send-string-to-terminal (format xterm-kitty--focus-window-command-string kitty-window-id)))))
-(defun xterm-kitty-select-frame-set-input-focus-advice (old-function frame &optional no-record)
- (or (when xterm-kitty-in-use
- ;; (message "Switching to frame: %s" frame)
- (xterm-kitty-focus frame)
- (select-frame frame no-record))
- (funcall old-function frame no-record)))
-(defun xterm-kitty-visible-window-advice (old-function &optional window minibuf all-frames)
- (when xterm-kitty-in-use
- ;; Terminal emacs thinks that only one frame is ever visible
- (funcall old-function window minibuf (if (eql all-frames 'visible) t all-frames))))
-(defun xterm-kitty-add-select-frame-set-input-focus-advice ()
- "Advise SELECT-FRAME-SET-INPUT-FOCUS to handle xterm-kitty terminal windows"
- (interactive)
- (advice-add 'select-frame-set-input-focus :around #'xterm-kitty-select-frame-set-input-focus-advice)
- (advice-add 'next-window :around #'xterm-kitty-visible-window-advice)
- (advice-add 'previous-window :around #'xterm-kitty-visible-window-advice))
-
-(defvar xterm-kitty--new-os-window-command-string
- "\eP@kitty-cmd{\"cmd\":\"new-window\",\"version\":[0,19,3],\"no_response\":true,\"payload\":{\"window_type\":\"os\"}}\e\\"
- "Command string to send to kitty to make new kitty window")
-(defun xterm-kitty-new-os-window ()
- "Open new xterm-kitty os window"
- (interactive)
- (when xterm-kitty-in-use
- (send-string-to-terminal xterm-kitty--new-os-window-command-string)))
-
-(provide 'xterm-kitty)
-;;; xterm-kitty.el ends here