;;;; Imeks: Use Emacs as your Xorg pseudo-IME! ;; Copyright © 2024 Jaidyn Ann ;; ;; 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 . (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) (if a (format "%s; %s key return; %s" a imeks-xdotool-path b) b)) type-cmds nil)) (cmd-string (concat sleep-cmd type-cmd))) (call-process "bash" nil 0 nil "-c" cmd-string))) (provide 'imeks)