imeks/imeks.el

121 lines
4.2 KiB
EmacsLisp
Raw Normal View History

2024-08-31 14:06:50 -05:00
;;;; 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.
(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)