xahlee / xah-fly-keys

the most efficient keybinding for emacs
http://xahlee.info/emacs/misc/xah-fly-keys.html
485 stars 82 forks source link

Some quirks of xah-fly-command-mode-init when called from emacs init during daemon startup #173

Open akashpal-21 opened 5 months ago

akashpal-21 commented 5 months ago

The Function definition is as follows:

(defun xah-fly-command-mode-init ()
  "Set command mode keys.
Version: 2022-07-06"
  (interactive)
  (setq xah-fly-insert-state-p nil)
  (xah-fly--update-key-map)
  (when xah-fly--deactivate-command-mode-func
    (funcall xah-fly--deactivate-command-mode-func))
  (setq xah-fly--deactivate-command-mode-func
        (set-transient-map xah-fly-command-map (lambda () t)))
  (modify-all-frames-parameters (list (cons 'cursor-type 'box)))
  ;; (set-face-background 'cursor "red")
  (setq mode-line-front-space xah-fly-command-mode-indicator)
  (force-mode-line-update))

The following three lines fail to work appropriately when this function is being called during emacs daemon startup due to some race condition.

   (set-transient-map xah-fly-command-map (lambda () t)))
  (modify-all-frames-parameters (list (cons 'cursor-type 'box)))
  (set-face-background 'cursor "red")

This causes the problem that when xah-fly-keys mode is enabled in init - although the mode is activated - none of the logic of the command mode is active - neither keybindings nor visual indication in the cursor

For the set-transient-map to work - hooking onto server-after-make-frame-hook is essential. Additionally for frame-parameters to take action - not only the frame need to be active - the function call must be made "asynchronously" which can be done by running a timer with zero seconds delay.

Combining and taking into consideration of not running this on new frame creation when insert-mode is currently active the following is suggested

  (xah-fly-keys 1)

(defun patch/xah-fly-keys--init ()
  ;; see issue #103
  (run-at-time "0" nil
               (lambda ()
                 (when (and xah-fly-keys (not xah-fly-insert-state-p))
           (set-transient-map xah-fly-command-map (lambda () t))
                   (modify-frame-parameters nil '((cursor-color . "red")))))))
(when (daemonp)
  (add-hook 'server-after-make-frame-hook
        #'patch/xah-fly-keys--init))

see additionally #103

akashpal-21 commented 5 months ago

UPDATE:

The 'server-after-make-frame-hook should remove the previous set-transient-map -- otherwise there is an extra post-command-hook that is redundant that will be running.

(defun patch/xah-fly-keys--init ()
  ;; see issue #103
  (run-at-time "0" nil
               (lambda ()
                 (when (and xah-fly-keys (not xah-fly-insert-state-p))
           (when xah-fly--deactivate-command-mode-func
             (funcall xah-fly--deactivate-command-mode-func))
           (setq xah-fly--deactivate-command-mode-func
             (set-transient-map xah-fly-command-map (lambda () t)))
           (modify-frame-parameters nil '((cursor-color . "red")))))))
(funcall xah-fly--deactivate-command-mode-func)

Will disable the previous transient-map since set-transient-map returns an exit function that we bind to this variable in the xah-fly-command-mode-init.

akashpal-21 commented 2 months ago

Update: 2024-09-29

add the function to 'after-make-frame-functions instead of 'server-after-make-frame-hook

upside: works with #'make-frame-command too - consistent result

(defun patch/xah-fly-keys--init (&optional frame)
  ;; see issue #103
  (run-at-time "0" nil
               (lambda (frame)
                 (when (and xah-fly-keys (not xah-fly-insert-state-p))
           (when xah-fly--deactivate-command-mode-func
             (funcall xah-fly--deactivate-command-mode-func))
           (setq xah-fly--deactivate-command-mode-func
             (set-transient-map xah-fly-command-map (lambda () t)))
           (modify-frame-parameters frame '((cursor-color . "red")))))
           frame))

Pass optional frame argument.