2

I am experiencing an issue in Emacs where clicking on a function name in a menu displayed on the mode line not only triggers the desired function but also has an unintended effect on the window or buffer below, interfering with the intended functionality.

Creating a Minimal Working Example (MWE) for the menus, as they were generated using minor modes and easy-menu-define, would be challenging.

I would appreciate any insights or suggestions on how to diagnose and resolve this issue. If you've encountered a similar problem or have experience with focus shifts when using menus in the mode line, please share your insights. Thank you!

I thought that introducing a delay, let's say half a second, between the moment I click on the function name in the menu and its actual execution might solve the problem. Unfortunately, I have no idea how to implement this delay.

Edit. This is my MWE:

(defun mwe-function-1 ()
  "MWE function 1"
  (interactive)
  (unwind-protect
      (query-replace "foo" "bar" nil (point-min) (point-max))
    ;; UNWINDFORMS
    (read-string "function 1 executed")))

(defun mwe-function-2 ()
  "MWE function 2"
  (interactive)
  (unwind-protect
      (query-replace "foo" "bar" nil (point-min) (point-max))
    ;; UNWINDFORMS
    (read-string "function 2 executed")))

(defun mwe-function-3 ()
  "MWE function 3"
  (interactive)
  (unwind-protect
      (query-replace "foo" "bar" nil (point-min) (point-max))
    ;; UNWINDFORMS
    (read-string "function 3 executed")))

(define-minor-mode mwe-mode
  "MWE mode"
  :init-value nil
  :lighter (:eval (propertize " MWE "
                              'face '(:foreground "RoyalBlue" :background "DarkGoldenrod1")))

  :keymap
  `(
    (,(kbd "<C-kp-1>") . mwe-function)
    )

  (if mwe-mode
      (easy-menu-define mwe-menu mwe-mode-map
        "MWE"
        '("MWE mode"
          ;; I want the menu on mode-line only:
          :visible (not (eq (framep (selected-frame)) 'x))
          ["mwe-function-1" mwe-function-1
           :help "mwe-function 1"]
          ["mwe-function-2" mwe-function-2
           :help "mwe-function 2"]
          ["mwe-function-3" mwe-function-3
           :help "mwe-function 3"]))
    t))
emacs --version
GNU Emacs 29.1
Development version 28fb02492c24 on master branch; build date 2023-12-10.
$ uname -a
Linux ---- 5.4.0-169-generic #187-Ubuntu SMP Thu Nov 23 14:52:28 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
$ echo $XDG_CURRENT_DESKTOP
MATE

When I click on the function mwe-function in the menu, it doesn't get executed because the mouse event interrupts query-replace. The issue doesn't occur consistently and is quite random.

I would try to add a dalay between the mouse event on the menu and the function execution.

Edit 2. I updated my MWE adding an UNWINDFORM to my functions and my Emacs version. Now I am on Emacs 29.1.

It continues to happen, randomly, that query-replace gets interrupted by mouse capture within the buffer window.

Indeed (from the manual):

Aside from this, any other character exits the query-replace, and is then reread as part of a key sequence. Thus, if you type C-k, it exits the query-replace and then kills to end of line. In particular, C-g simply exits the query-replace.

However, the UNWIND-FORM is always executed.

10
  • "Creating a Minimal Working Example (MWE) for the menus, as they were generated using minor modes and easy-menu-define, would be challenging." -- The amount of code to define a minimal minor mode and menu is very small -- just provide a working example that way.
    – phils
    Commented Jan 24 at 2:04
  • Please include your Emacs version and platform in the question.
    – Drew
    Commented Jan 24 at 2:54
  • @phils I added the MWE but the issue is quite random.
    – Gabriele
    Commented Jan 24 at 18:08
  • 1
    @Drew I added the requested info
    – Gabriele
    Commented Jan 24 at 18:09
  • I updated my question adding a new MWE and a GIF. I also updated my Emacs to 29.1.
    – Gabriele
    Commented Feb 15 at 21:31

1 Answer 1

0

I opened a bug ticket for this issue (bug#69236) and a developer asked to me to

show the output of "C-h l" in both cases, i.e. when things work and when they don't.

I got:

<help-echo> <down-mouse-1> ;; mouse-minor-mode-menu
q <return>                 ;; exit-minibuffer
<help-echo> C-h l          ;; view-lossage

when things work and:

<help-echo> <down-mouse-1>           ;; mouse-minor-mode-menu
<mouse-1> <mouse-1>                  ;; mouse-select-window
<help-echo> C-x o                    ;; other-window
<return>                             ;; exit-minibuffer
C-h l                                ;; view-lossage

when things do not work.

So I implemented a workaround to solve my problem (bug?):

  1. First of all I builded this macro:
(defmacro with-ignore-mouse-events (&rest body)
  "Macro to ignore mouse events before evaluating BODY."
  `(progn
     (when (input-pending-p)
       (let ((event (read-event)))
         (if (mouse-event-p event)
             (let ((button (event-basic-type event)))
               (if (eq button 'mouse-1)
                   (ignore)
                 (setq unread-command-events (list event))))
           (setq unread-command-events (list event)))))
     ,@body))

to intercept and "unread" the mouse-1 event.

  1. Then I prepared this function to create a modified version of the functions I intend to use in the menu:
(defun generate-IME-star-functions (functions)
  "Generate starred versions of the given functions, where the original
functions are wrapped with the `with-ignore-mouse-events' macro"
  (dolist (func functions)
    (let* ((func-name (symbol-name func))
           (starred-func-name (intern (format "%s*" func-name))))
      (unless (fboundp starred-func-name)
        (defalias starred-func-name
          `(lambda ()
             (interactive)
             (with-ignore-mouse-events
              (funcall ',func)))
          (format "Starred version of the function `%s', where the original
function is wrapped with the `with-ignore-mouse-events' macro" func-name))))))
  1. I built a list of the functions used in the menu:
(defvar mwe-function-names '(mwe-function-1 mwe-function-2 mwe-function-3))
  1. And, finally, I built the minor-mode menu in this way
(define-minor-mode mwe-mode
  "MWE mode"
  :init-value nil
  :lighter (:eval (propertize " MWE "
                              'face '(:foreground "RoyalBlue" :background "DarkGoldenrod1")))

  :keymap
  `(
    (,(kbd "<C-kp-1>") . mwe-function)
    )

  (if mwe-mode
      (progn
        (generate-IME-star-functions mwe-function-names)
        (easy-menu-define mwe-menu mwe-mode-map
          "MWE"
          '("MWE mode"
            ;; I want the menu on mode-line only:
            :visible (not (eq (framep (selected-frame)) 'x))
            ["mwe-function-1" mwe-function-1*
             :help "mwe-function 1"]
            ["mwe-function-2" mwe-function-2*
             :help "mwe-function 2"]
            ["mwe-function-3" mwe-function-3*
             :help "mwe-function 3"])))
    t))

A better solution would have been to automatically wrap all functions, when launched from the menu, with my macro, but I have no idea how to accomplish that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.