imeks/imeks.el

121 lines
4.2 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;;; Imeks: Use Emacs as your Xorg pseudo-IME!
;; Copyright © 2024 Jaidyn Ann <jadedctrl@posteo.at>
;;
;; 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/>.
(define-minor-mode imeks-mode
"A mode for typing the contents of an Emacs buffer into X windows."
:interactive nil
:lighter " Imeks"
:group 'imeks)
;;; Options
;;; ―――――――――――――――――――――――――――――――――――――
(defcustom imeks-mode-hook nil
"Hook called when entering Imeks mode."
:type 'hook
:group 'imeks)
(defcustom imeks-typing-delay .2
"How long Imeks will wait before typing, by default.\n
This gives you time to switch from Emacs' X window to another."
:type 'number
:group 'imeks)
(defcustom imeks-xdotool-path "xdotool"
"The path to the program xdotool.
It is used by Imeks to mimic an X keyboard."
:type 'file
:group 'imeks)
(defcustom imeks-type-on-frame-delete 't
"When non-nil, Imeks may type the active buffer on frame-deletion.\n
This will only happen if the buffer is in `imeks-mode`, and
is for useful if you use frames dedicated to Imeks."
:type 'boolean
:group 'imeks)
;;; Core
;;; ―――――――――――――――――――――――――――――――――――――
(defun imeks ()
(interactive)
(switch-to-buffer (generate-new-buffer "*imeks*"))
(imeks-mode))
(defun imeks-type-buffer (&optional buffer delay)
"Types a buffer's contents, mimicking X keyboard input.\n
Waits DELAY seconds before beginning to type, by default
`imeks-typing-delay`."
(interactive)
(let ((buffer (or buffer (current-buffer))))
(with-current-buffer buffer
(imeks-type-region buffer (point-min) (point-max) delay))))
(defun imeks-type-region (&optional buffer start end delay)
"Types a region's contents, mimicking X keyboard input.\n
Waits DELAY seconds before beginning to type, by default
`imeks-typing-delay`."
(interactive)
(xdotool-type-string
(with-current-buffer (or buffer (current-buffer))
(buffer-substring-no-properties (or start (region-beginning))
(or end (region-end))))
(or delay imeks-typing-delay)))
;;; Private hooks
;;; ―――――――――――――――――――――――――――――――――――――
(defun imeks--handle-delete-frame (frame)
"When a frame is deleted and the current buffer is in
`imeks-mode`, kill the buffer and type its contents as
if an X keyboard."
(when (and imeks-mode imeks-type-on-frame-delete)
(let ((buffer-contents
(buffer-substring-no-properties (point-min) (point-max))))
(kill-buffer)
(xdotool-type-string buffer-contents .2))))
(nconc delete-frame-functions '(imeks--handle-delete-frame))
;;
;;; Util
;;; ―――――――――――――――――――――――――――――――――――――
(defun xdotool-type-string (string &optional delay)
"Types a STRING to the current X display, using xdotool.\n
Optionally waits a DELAY in seconds before typing the string."
(let* ((sleep-cmd
(format "sleep %f" (or delay 0)))
(type-cmds
(mapcar (lambda (line)
(format "%s type \"$(cat <<'EOF'\n%s\nEOF\n)\""
imeks-xdotool-path line))
(string-lines string)))
(type-cmd ; Work-around, since not all programs accept a typed newline.
(seq-reduce (lambda (a b)
(format "%s; %s key return; %s" a imeks-xdotool-path b))
type-cmds ""))
(cmd-string
(concat sleep-cmd type-cmd)))
(call-process "bash" nil 0 nil
"-c" cmd-string)))
(provide 'imeks)