diff options
| -rw-r--r-- | lisp/kitty-remote-control.el | 88 | ||||
| -rw-r--r-- | lisp/term/xterm-kitty.el | 37 |
2 files changed, 95 insertions, 30 deletions
diff --git a/lisp/kitty-remote-control.el b/lisp/kitty-remote-control.el new file mode 100644 index 0000000..a5c988a --- /dev/null +++ b/lisp/kitty-remote-control.el @@ -0,0 +1,88 @@ +;;; kitty-remote-control.el --- Kitty remote control -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Ravi Kiran + +;; Author: Ravi Kiran <aine.marina@gmail.com> +;; Keywords: terminals + +;; 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: + +;; Remote control of kitty terminals + +;;; Code: + +(require 'json) +(require 'subr-x) + +(defconst kitty-rc-min-version [0 19 3] "Minimum kitty version used in commands") +(defconst kitty-rc-command-prefix "\eP@kitty-cmd") +(defconst kitty-rc-command-suffix "\e\\") + +(defun kitty-rc--construct-command-string (command payload min-version response) + (concat + kitty-rc-command-prefix + (json-encode `(("cmd" . ,command) + ("version" . ,(or min-version kitty-rc-min-version)) + ("no_response" . ,(not response)) + ,@(and payload `(("payload" . ,payload))))) + kitty-rc-command-suffix)) + +(defun kitty-rc--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 kitty-rc-posted-command (command payload &optional min-version) + (send-string-to-terminal + (kitty-rc--construct-command-string command payload min-version nil))) + +(defun kitty-rc-command (command payload handler &optional min-version) + (xterm--query (kitty-rc--construct-command-string command payload min-version t) + (list (cons kitty-rc-command-prefix + (lambda () + (if-let* ((kitty-response (kitty-rc--remote-control-response)) + (response-json (json-parse-string kitty-response))) + (funcall handler response-json) + (user-error "Kitty response failed"))))))) + +;; Applied commands +(defun kitty-rc-new-window (&optional os) + "New kitty window (default OS, kitty if KITTY-WIN is non-nil)" + (interactive "P") + (kitty-rc-posted-command "new-window" `(("window_type" . ,(if os "os" "kitty"))))) +(defun kitty-rc-focus-window (window-id) + "Focus kitty window with id WINDOW-ID" + (interactive "N") + (kitty-rc-posted-command "focus-window" `(("match" . ,(format "id:%d" window-id))))) +(defun kitty-rc-launch-background (cmd) + "Launch CMD (must be a vector) in kitty's background" + (kitty-rc-posted-command "launch" + `(("type" . "background") + ("args" . ,cmd)))) + +(provide 'kitty-remote-control) +;;; kitty-remote-control.el ends here diff --git a/lisp/term/xterm-kitty.el b/lisp/term/xterm-kitty.el index a9b5b25..3c9937a 100644 --- a/lisp/term/xterm-kitty.el +++ b/lisp/term/xterm-kitty.el @@ -31,6 +31,7 @@ (require 'term/xterm) (require 'kitty-keyboard-protocol) +(require 'kitty-remote-control) (defun xterm-kitty-in-use (&optional frame) "Check whether FRAME is running under kitty terminal." @@ -99,26 +100,9 @@ function is almost equivalent to 'event-convert-list'." (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) +(defun xterm-kitty--save-kitty-window-id (response-json) + (message "Saving window id...") + (let (window-id) (mapc (lambda (os-win) (mapc (lambda (tab) (mapc (lambda (win) @@ -135,12 +119,8 @@ function is almost equivalent to 'event-convert-list'." (defun xterm-kitty-save-window-id () "Save kitty window ID of current terminal" - (xterm--query "\eP@kitty-cmd{\"cmd\":\"ls\",\"version\":[0,19,3]}\e\\" - '(("\eP@kitty-cmd" . xterm-kitty--save-kitty-window-id)))) + (kitty-rc-command "ls" nil #'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 @@ -149,7 +129,7 @@ function is almost equivalent to 'event-convert-list'." (kitty-window-id (xterm-kitty-window-id (frame-terminal frame)))) ;; (message "Window id %d for %s" kitty-window-id frame) (if kitty-window-id - (send-string-to-terminal (format xterm-kitty--focus-window-command-string kitty-window-id)) + (kitty-rc-focus-window kitty-window-id) (message "%s is not a kitty window" frame-or-window)))) (defun xterm-kitty-select-frame-set-input-focus-advice (old-function frame &optional no-record) (or (when (xterm-kitty-in-use frame) @@ -175,14 +155,11 @@ function is almost equivalent to 'event-convert-list'." (advice-add 'previous-window :around #'xterm-kitty-visible-window-advice) (setq xterm-kitty--skip-select-frame-set-input-focus-advice t))) -(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))) + (kitty-rc-new-window t))) (defun terminal-init-xterm-kitty () "Terminal initialization function for kitty" |
