]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement surrogate menu bars for tty child frames
authorMartin Rudalics <rudalics@gmx.at>
Mon, 17 Mar 2025 08:36:59 +0000 (09:36 +0100)
committerEshel Yaron <me@eshelyaron.com>
Mon, 17 Mar 2025 11:06:14 +0000 (12:06 +0100)
* lisp/menu-bar.el (popup-menu): When asked to pop up MENU from
a tty child frame try to use menu of its root frame.
(menu-bar-open): When FRAME is a child frame and
'tty-menu-open-use-tmm' is nil, navigate menu bar of its root
frame.
* src/keymap.c (Fcurrent_active_maps): Accept live window as
POSITION argument and use its buffer as current when processing
the key sequence.  Needed for tty child frames so the root
frame's menu bar can be updated according to the selected
window's buffer when navigating the menu bar from keyboard.
* src/xdisp.c (prepare_menu_bars): If the selected window's
frame is a tty child frame without menu bar, that frame's root
frame has a menu bar and 'tty-menu-open-use-tmm' is nil, prepare
to update the menu bar of the root frame as surrogate.
(update_menu_bar): New argument W denoting the window that
should be considered as selected.  For a tty child frame using F
as surrogate menu bar frame this specifies the child frame's
selected window and its buffer shall be used for updating the
menu bar of the root frame instead of the buffer of the root
frame's selected window.
(redisplay_window): Instead of setting redisplay_menu_p flag
always call display_menu_bar right away.  This facilitates to
call display_menu_bar for a tty child frame with its root
frame as surrogate menu bar frame.
(display_tty_menu_item): If the selected frame is a tty child
frame, overwrite its root frame's glyph matrix (and not that of
the child frame) when displaying a menu item.
(Qtty_menu_open_use_tmm): Define symbol.

(cherry picked from commit 7e71b0a2c938623872ec7404d81339c8ff51b012)

lisp/menu-bar.el
src/keymap.c
src/xdisp.c

index 756487a9e499c68b264242d1904347e571b14326..f79de0fa449bae07efa8ba671b8a80ccbc4c9b32 100644 (file)
@@ -2692,53 +2692,74 @@ FROM-MENU-BAR, if non-nil, means we are dropping one of menu-bar's menus."
                         (filter (when (symbolp map)
                                   (plist-get (get map 'menu-prop) :filter))))
                    (if filter (funcall filter (symbol-function map)) map)))))
-        (frame (selected-frame))
+        (selected-frame (selected-frame))
+        (frame (if (and (eq (framep selected-frame) t) (frame-parent)
+                        from-menu-bar
+                        (zerop (or (frame-parameter nil 'menu-bar-lines) 0)))
+                   ;; If the selected frame is a tty child frame
+                   ;; without its own menu bar and we are called from
+                   ;; the menu bar, the menu bar must be on the root
+                   ;; frame of the selected frame.
+                   (frame-root-frame)
+                 (selected-frame)))
         event cmd)
-    (if from-menu-bar
-       (let* ((xy (posn-x-y position))
-              (menu-symbol (menu-bar-menu-at-x-y (car xy) (cdr xy))))
-         (setq position (list menu-symbol (list frame '(menu-bar)
-                                                xy 0))))
-      (setq position (popup-menu-normalize-position position)))
-    ;; The looping behavior was taken from lmenu's popup-menu-popup
-    (while (and map (setq event
-                         ;; map could be a prefix key, in which case
-                         ;; we need to get its function cell
-                         ;; definition.
-                         (x-popup-menu position (indirect-function map))))
-      ;; Strangely x-popup-menu returns a list.
-      ;; mouse-major-mode-menu was using a weird:
-      ;; (key-binding (apply 'vector (append '(menu-bar) menu-prefix events)))
-      (setq cmd
-           (cond
-            ((and from-menu-bar
-                  (consp event)
-                  (numberp (car event))
-                  (numberp (cdr event)))
-             (let ((x (car event))
-                   (y (cdr event))
-                   menu-symbol)
-               (setq menu-symbol (menu-bar-menu-at-x-y x y))
-               (setq position (list menu-symbol (list frame '(menu-bar)
-                                                event 0)))
-               (setq map
-                     (key-binding (vector 'menu-bar menu-symbol)))))
-            ((and (not (keymapp map)) (listp map))
-             ;; We were given a list of keymaps.  Search them all
-             ;; in sequence until a first binding is found.
-             (let ((mouse-click (apply 'vector event))
-                   binding)
-               (while (and map (null binding))
-                 (setq binding (lookup-key-ignore-too-long (car map) mouse-click))
-                 (setq map (cdr map)))
-                binding))
-            (t
-             ;; We were given a single keymap.
-             (lookup-key map (apply 'vector event)))))
-      ;; Clear out echoing, which perhaps shows a prefix arg.
-      (message "")
-      ;; Maybe try again but with the submap.
-      (setq map (if (keymapp cmd) cmd)))
+    (with-selected-frame frame
+      (if from-menu-bar
+         (let* ((xy (posn-x-y position))
+                (menu-symbol (menu-bar-menu-at-x-y (car xy) (cdr xy))))
+           (setq position (list menu-symbol (list frame '(menu-bar)
+                                                  xy 0))))
+       (setq position (popup-menu-normalize-position position)))
+
+      ;; The looping behavior was taken from lmenu's popup-menu-popup
+      (while (and map (setq event
+                           ;; map could be a prefix key, in which case
+                           ;; we need to get its function cell
+                           ;; definition.
+                           (x-popup-menu position (indirect-function map))))
+       ;; Strangely x-popup-menu returns a list.
+       ;; mouse-major-mode-menu was using a weird:
+       ;; (key-binding (apply 'vector (append '(menu-bar) menu-prefix events)))
+       (setq cmd
+             (cond
+              ((and from-menu-bar
+                    (consp event)
+                    (numberp (car event))
+                    (numberp (cdr event)))
+               (let ((x (car event))
+                     (y (cdr event))
+                     menu-symbol)
+                 (setq menu-symbol (menu-bar-menu-at-x-y x y))
+                 (setq position (list menu-symbol (list frame '(menu-bar)
+                                                        event 0)))
+                 (if (not (eq frame selected-frame))
+                     ;; If we are using the menu bar from the root
+                     ;; frame, look up the key binding in the keymaps
+                     ;; of the initially selected window's buffer to
+                     ;; make sure that navigating the menu bar with the
+                     ;; keyboard works as intended.
+                     (setq map
+                           (key-binding (vector 'menu-bar menu-symbol) nil nil
+                                        (frame-selected-window selected-frame)))
+                   (setq map
+                         (key-binding (vector 'menu-bar menu-symbol))))))
+              ((and (not (keymapp map)) (listp map))
+               ;; We were given a list of keymaps.  Search them all
+               ;; in sequence until a first binding is found.
+               (let ((mouse-click (apply 'vector event))
+                     binding)
+                 (while (and map (null binding))
+                   (setq binding (lookup-key-ignore-too-long (car map) mouse-click))
+                   (setq map (cdr map)))
+                 binding))
+              (t
+               ;; We were given a single keymap.
+               (lookup-key map (apply 'vector event)))))
+       ;; Clear out echoing, which perhaps shows a prefix arg.
+       (message "")
+       ;; Maybe try again but with the submap.
+       (setq map (if (keymapp cmd) cmd))))
+
     ;; If the user did not cancel by refusing to select,
     ;; and if the result is a command, run it.
     (when (and (null map) (commandp cmd))
@@ -2809,14 +2830,27 @@ is nil or not.
 If FRAME is nil or not given, use the selected frame."
   (interactive
    (list nil (prefix-numeric-value current-prefix-arg)))
-  (let ((type (framep (or frame (selected-frame)))))
+  (let* ((type (framep (or frame (selected-frame))))
+        root
+        (frame (if (and (eq type t) (frame-parent frame)
+                        (null tty-menu-open-use-tmm)
+                        (zerop (or (frame-parameter frame 'menu-bar-lines) 0))
+                        (setq root (frame-root-frame))
+                        (not (zerop
+                              (or (frame-parameter root 'menu-bar-lines) 0))))
+                   ;; If FRAME is a tty child frame without its own
+                   ;; menu bar, 'tty-menu-open-use-tmm' is false and
+                   ;; FRAME's root frame has a menu bar, use that root
+                   ;; frame's menu bar.
+                   root
+                 frame)))
     (cond
      ((eq type 'x) (x-menu-bar-open frame))
      ((eq type 'w32) (w32-menu-bar-open frame))
      ((eq type 'haiku) (haiku-menu-bar-open frame))
      ((eq type 'pgtk) (pgtk-menu-bar-open frame))
      ((and (null tty-menu-open-use-tmm)
-          (not (zerop (or (frame-parameter nil 'menu-bar-lines) 0))))
+          (not (zerop (or (frame-parameter frame 'menu-bar-lines) 0))))
       ;; Make sure the menu bar is up to date.  One situation where
       ;; this is important is when this function is invoked by name
       ;; via M-x, in which case the menu bar includes the "Minibuf"
@@ -2832,7 +2866,7 @@ If FRAME is nil or not given, use the selected frame."
                       (current-local-map) (vector 'menu-bar menu))
                     (cdar (minor-mode-key-binding (vector 'menu-bar menu)))
                      (mouse-menu-bar-map))
-                   (posn-at-x-y x 0 nil t) nil t)))
+                   (posn-at-x-y x 0 frame t) nil t)))
      (t (with-selected-frame (or frame (selected-frame))
           (tmm-menubar))))))
 
index bc731c54ef070185c16f199164de8b40cb3369f3..2c250578b00eb66488d8b4e0e5d9816a6dd8272d 100644 (file)
@@ -1652,7 +1652,8 @@ DEFUN ("current-active-maps", Fcurrent_active_maps, Scurrent_active_maps,
        doc: /* Return a list of the currently active keymaps.
 OLP if non-nil indicates that we should obey `overriding-local-map' and
 `overriding-terminal-local-map'.  POSITION can specify a click position
-like in the respective argument of `key-binding'.  */)
+like in the respective argument of `key-binding' or a live window which
+means to return the active maps for that window's buffer.  */)
   (Lisp_Object olp, Lisp_Object position)
 {
   specpdl_ref count = SPECPDL_INDEX ();
@@ -1682,6 +1683,16 @@ like in the respective argument of `key-binding'.  */)
          set_buffer_internal (XBUFFER (XWINDOW (window)->contents));
        }
     }
+  else if (WINDOW_LIVE_P (position))
+    {
+      if (BUFFERP (XWINDOW (position)->contents)
+         && XBUFFER (XWINDOW (position)->contents) != current_buffer)
+       {
+         /* See comment above.  */
+         record_unwind_current_buffer ();
+         set_buffer_internal (XBUFFER (XWINDOW (position)->contents));
+       }
+    }
 
   if (!NILP (olp)
       /* The doc said that overriding-terminal-local-map should
index 11cffce1cbebe1c17197e86a3eeed993952171f8..00a7c5037864f5b0fe5ae1a872ce4c23faf261ef 100644 (file)
@@ -1147,7 +1147,7 @@ static bool set_cursor_from_row (struct window *, struct glyph_row *,
                                 struct glyph_matrix *, ptrdiff_t, ptrdiff_t,
                                 int, int);
 static bool cursor_row_fully_visible_p (struct window *, bool, bool, bool);
-static bool update_menu_bar (struct frame *, bool, bool);
+static bool update_menu_bar (struct frame *, bool, bool, struct window *);
 static bool try_window_reusing_current_matrix (struct window *);
 static int try_window_id (struct window *);
 static void maybe_produce_line_number (struct it *);
@@ -14066,13 +14066,32 @@ prepare_menu_bars (void)
       /* True means that update_menu_bar has run its hooks
         so any further calls to update_menu_bar shouldn't do so again.  */
       bool menu_bar_hooks_run = false;
+      struct window *sw = XWINDOW (selected_window);
+      struct frame *sf = WINDOW_XFRAME (sw);
+      struct frame *rf = NULL;
+
+      if (FRAME_PARENT_FRAME (sf) && !FRAME_WINDOW_P (sf)
+         && FRAME_MENU_BAR_LINES (sf) == 0
+         && FRAME_MENU_BAR_LINES (rf = root_frame (sf)) != 0
+         && NILP (Fdefault_value (Qtty_menu_open_use_tmm)))
+       /* If the selected window's frame is a tty child frame without
+          menu bar, that frame's root frame has a menu bar and
+          'tty-menu-open-use-tmm' is nil, update the menu bar of the
+          root frame from the selected window.  */
+       sf = rf;
+      else
+       {
+         sf = NULL;
+         sw = NULL;
+       }
 
       record_unwind_save_match_data ();
 
       FOR_EACH_FRAME (tail, frame)
        {
          struct frame *f = XFRAME (frame);
-         struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f));
+         struct window *w
+           = sf == f ? sw : XWINDOW (FRAME_SELECTED_WINDOW (f));
 
          /* Ignore tooltip frame.  */
          if (FRAME_TOOLTIP_P (f))
@@ -14084,8 +14103,8 @@ prepare_menu_bars (void)
              && !XBUFFER (w->contents)->text->redisplay)
            continue;
 
-         if (!FRAME_PARENT_FRAME (f))
-           menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run);
+         menu_bar_hooks_run
+           = update_menu_bar (f, false, menu_bar_hooks_run, w);
 
          update_tab_bar (f, false);
 #ifdef HAVE_WINDOW_SYSTEM
@@ -14097,10 +14116,21 @@ prepare_menu_bars (void)
     }
   else
     {
-      struct frame *sf = SELECTED_FRAME ();
+      struct window *sw = XWINDOW (selected_window);
+      struct frame *sf = WINDOW_XFRAME (sw);
+      struct frame *rf = NULL;
 
-      if (!FRAME_PARENT_FRAME (sf))
-       update_menu_bar (sf, true, false);
+      if (FRAME_PARENT_FRAME (sf) && !FRAME_WINDOW_P (sf)
+         && FRAME_MENU_BAR_LINES (sf) == 0
+         && FRAME_MENU_BAR_LINES (rf = root_frame (sf)) != 0
+         && NILP (Fdefault_value (Qtty_menu_open_use_tmm)))
+       /* If the selected window's frame is a tty child frame without
+          menu bar, that frame's root frame has a menu bar and
+          'tty-menu-open-use-tmm' is nil, update the menu bar of the
+          root frame from the selected window.  */
+       sf = rf;
+
+      update_menu_bar (sf, true, false, sw);
 
       update_tab_bar (sf, true);
 #ifdef HAVE_WINDOW_SYSTEM
@@ -14119,23 +14149,23 @@ prepare_menu_bars (void)
    If HOOKS_RUN, a previous call to update_menu_bar
    already ran the menu bar hooks for this redisplay, so there
    is no need to run them again.  The return value is the
-   updated value of this flag, to pass to the next call.  */
+   updated value of this flag, to pass to the next call.
+
+   W, if set, denotes the window that should be considered as selected.
+   For a tty child frame using F as surrogate menu bar frame, this
+   specifes the child frame's selected window and its buffer shall be
+   used for updating the menu bar of the root frame instead of the
+   buffer of the root frame's selected window.  */
 
 static bool
-update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run)
+update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run, struct window *w)
 {
-  Lisp_Object window;
-  struct window *w;
-
   /* If called recursively during a menu update, do nothing.  This can
      happen when, for instance, an activate-menubar-hook causes a
      redisplay.  */
   if (inhibit_menubar_update)
     return hooks_run;
 
-  window = FRAME_SELECTED_WINDOW (f);
-  w = XWINDOW (window);
-
   if (FRAME_WINDOW_P (f)
       ?
 #ifdef HAVE_EXT_MENU_BAR
@@ -21195,24 +21225,33 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
   /* When we reach a frame's selected window, redo the frame's menu
      bar, tool bar, tab-bar, and the frame's title.  */
-  if (update_mode_line
-      && EQ (FRAME_SELECTED_WINDOW (f), window))
+  if (update_mode_line && EQ (FRAME_SELECTED_WINDOW (f), window))
     {
-      bool redisplay_menu_p;
-
       if (FRAME_WINDOW_P (f))
        {
 #ifdef HAVE_EXT_MENU_BAR
-         redisplay_menu_p = FRAME_EXTERNAL_MENU_BAR (f);
+         if (FRAME_EXTERNAL_MENU_BAR (f))
+           display_menu_bar (w);
 #else
-         redisplay_menu_p = FRAME_MENU_BAR_LINES (f) > 0;
+         if (FRAME_MENU_BAR_LINES (f) > 0)
+           display_menu_bar (w);
 #endif
        }
       else
-        redisplay_menu_p = FRAME_MENU_BAR_LINES (f) > 0;
+       {
+         struct frame *rf = NULL;
 
-      if (redisplay_menu_p)
-        display_menu_bar (w);
+         if (FRAME_PARENT_FRAME (f)
+             && FRAME_MENU_BAR_LINES (f) == 0
+             && FRAME_MENU_BAR_LINES (rf = root_frame (f)) != 0
+             && NILP (Fdefault_value (Qtty_menu_open_use_tmm)))
+           /* If F is a tty child frame without menu bar, that frame's root
+              frame has a menu bar and 'tty-menu-open-use-tmm' is nil,
+              display the menu bar of the root frame's selected window.  */
+           display_menu_bar (XWINDOW (FRAME_SELECTED_WINDOW (rf)));
+         else if (FRAME_MENU_BAR_LINES (f) > 0)
+           display_menu_bar (w);
+       }
 
 #ifdef HAVE_WINDOW_SYSTEM
       if (FRAME_WINDOW_P (f))
@@ -27466,10 +27505,19 @@ display_tty_menu_item (const char *item_text, int width, int face_id,
 {
   struct it it;
   struct frame *f = SELECTED_FRAME ();
-  struct window *w = XWINDOW (f->selected_window);
   struct glyph_row *row;
   size_t item_len = strlen (item_text);
 
+  struct frame *rf = NULL;
+
+  if (FRAME_PARENT_FRAME (f) && !FRAME_WINDOW_P (f)
+      && FRAME_MENU_BAR_LINES (f) == 0
+      && FRAME_MENU_BAR_LINES (rf = root_frame (f)) != 0
+      && NILP (Fdefault_value (Qtty_menu_open_use_tmm)))
+    f = rf;
+
+  struct window *w = XWINDOW (f->selected_window);
+
   eassert (FRAME_TERMCAP_P (f));
 
   /* Don't write beyond the matrix's last row.  This can happen for
@@ -38610,6 +38658,7 @@ depending on your patience and the speed of your system.  */);
   DEFSYM (Qnhdrag, "nhdrag");
   DEFSYM (Qvdrag, "vdrag");
   DEFSYM (Qhourglass, "hourglass");
+  DEFSYM (Qtty_menu_open_use_tmm, "tty-menu-open-use-tmm");
 }