(server-process-filter): Ignore lines that don't start
[bpt/emacs.git] / src / xmenu.c
index 9614e75..993ae91 100644 (file)
@@ -29,21 +29,29 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 /* Rewritten for clarity and GC protection by rms in Feb 94.  */
 
-#include <stdio.h>
-
 /* On 4.3 this loses if it comes after xterm.h.  */
 #include <signal.h>
 #include <config.h>
+
+#include <stdio.h>
 #include "lisp.h"
 #include "termhooks.h"
 #include "frame.h"
 #include "window.h"
 #include "keyboard.h"
 #include "blockinput.h"
+#include "puresize.h"
+#include "buffer.h"
 
+#ifdef MSDOS
+#include "msdos.h"
+#endif
+
+#ifdef HAVE_X_WINDOWS
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
 #include "xterm.h"
+#endif
 
 /* Load sys/types.h if not already loaded.
    In some systems loading it twice is suicidal.  */
@@ -53,21 +61,19 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include "dispextern.h"
 
-#ifdef HAVE_X11
-#include "../oldXMenu/XMenu.h"
-#else
-#include <X/XMenu.h>
-#endif
-
+#ifdef HAVE_X_WINDOWS
 #ifdef USE_X_TOOLKIT
 #include <X11/Xlib.h>
 #include <X11/IntrinsicP.h>
 #include <X11/CoreP.h>
 #include <X11/StringDefs.h>
+#include <X11/Shell.h>
 #include <X11/Xaw/Paned.h>
 #include "../lwlib/lwlib.h"
-#include "../lwlib/xlwmenuP.h"
-#endif /* USE_X_TOOLKIT */
+#else /* not USE_X_TOOLKIT */
+#include "../oldXMenu/XMenu.h"
+#endif /* not USE_X_TOOLKIT */
+#endif /* HAVE_X_WINDOWS */
 
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 #define max(x,y) (((x) > (y)) ? (x) : (y))
@@ -77,24 +83,28 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #define FALSE 0
 #endif /* no TRUE */
 
-#ifdef HAVE_X11
-extern Display *x_current_display;
-#else
-#define        ButtonReleaseMask ButtonReleased
-#endif /* not HAVE_X11 */
-
-/* We need a unique id for each popup menu and dialog box.  */
-static unsigned int popup_id_tick;
+Lisp_Object Qdebug_on_next_call;
 
 extern Lisp_Object Qmenu_enable;
 extern Lisp_Object Qmenu_bar;
+extern Lisp_Object Qmouse_click, Qevent_kind;
+
+extern Lisp_Object Vdefine_key_rebound_commands;
+
+extern Lisp_Object Voverriding_local_map;
+extern Lisp_Object Voverriding_local_map_menu_flag;
+
+extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
+
+extern Lisp_Object Qmenu_bar_update_hook;
 
 #ifdef USE_X_TOOLKIT
+extern void set_frame_menubar ();
 extern void process_expose_from_menu ();
 extern XtAppContext Xt_app_con;
 
-static int string_width ();
 static Lisp_Object xdialog_show ();
+void popup_get_selection ();
 #endif
 
 static Lisp_Object xmenu_show ();
@@ -110,9 +120,9 @@ static void list_of_items ();
 
    Each pane is described by 3 elements in the vector:
    t, the pane name, the pane's prefix key.
-   Then follow the pane's items, with 4 elements per item:
+   Then follow the pane's items, with 5 elements per item:
    the item string, the enable flag, the item's value,
-   and the equivalent keyboard key's description string.
+   the definition, and the equivalent keyboard key's description string.
 
    In some cases, multiple levels of menus may be described.
    A single vector slot containing nil indicates the start of a submenu.
@@ -133,7 +143,8 @@ static void list_of_items ();
 #define MENU_ITEMS_ITEM_ENABLE 1
 #define MENU_ITEMS_ITEM_VALUE 2
 #define MENU_ITEMS_ITEM_EQUIV_KEY 3
-#define MENU_ITEMS_ITEM_LENGTH 4
+#define MENU_ITEMS_ITEM_DEFINITION 4
+#define MENU_ITEMS_ITEM_LENGTH 5
 
 static Lisp_Object menu_items;
 
@@ -150,6 +161,39 @@ static int menu_items_n_panes;
 /* Current depth within submenus.  */
 static int menu_items_submenu_depth;
 
+/* Flag which when set indicates a dialog or menu has been posted by
+   Xt on behalf of one of the widget sets.  */
+static int popup_activated_flag;
+
+static int next_menubar_widget_id;
+\f
+#ifdef USE_X_TOOLKIT
+
+/* Return the frame whose ->output_data.x->id equals ID, or 0 if none.  */
+
+static struct frame *
+menubar_id_to_frame (id)
+     LWLIB_ID id;
+{
+  Lisp_Object tail, frame;
+  FRAME_PTR f;
+
+  for (tail = Vframe_list; GC_CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      frame = XCONS (tail)->car;
+      if (!GC_FRAMEP (frame))
+        continue;
+      f = XFRAME (frame);
+      if (f->output_data.nothing == 1)
+       continue;
+      if (f->output_data.x->id == id)
+       return f;
+    }
+  return 0;
+}
+
+#endif
+\f
 /* Initialize the menu_items structure if we haven't already done so.
    Also mark it as currently empty.  */
 
@@ -260,12 +304,14 @@ push_menu_pane (name, prefix_vec)
 /* Push one menu item into the current pane.
    NAME is the string to display.  ENABLE if non-nil means
    this item can be selected.  KEY is the key generated by
-   choosing this item.  EQUIV is the textual description
-   of the keyboard equivalent for this item (or nil if none).  */
+   choosing this item, or nil if this item doesn't really have a definition.
+   DEF is the definition of this item.
+   EQUIV is the textual description of the keyboard equivalent for
+   this item (or nil if none).  */
 
 static void
-push_menu_item (name, enable, key, equiv)
-     Lisp_Object name, enable, key, equiv;
+push_menu_item (name, enable, key, def, equiv)
+     Lisp_Object name, enable, key, def, equiv;
 {
   if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
     grow_menu_items ();
@@ -274,6 +320,7 @@ push_menu_item (name, enable, key, equiv)
   XVECTOR (menu_items)->contents[menu_items_used++] = enable;
   XVECTOR (menu_items)->contents[menu_items_used++] = key;
   XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
+  XVECTOR (menu_items)->contents[menu_items_used++] = def;
 }
 \f
 /* Figure out the current keyboard equivalent of a menu item ITEM1.
@@ -299,6 +346,7 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
   Lisp_Object savedkey, descrip;
   Lisp_Object def1;
   int changed = 0;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
 
   /* If a help string follows the item string, skip it.  */
   if (CONSP (XCONS (item1)->cdr)
@@ -319,22 +367,40 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
       descrip = XCONS (cachelist)->cdr;
     }
 
+  GCPRO4 (def, def1, savedkey, descrip);
+
   /* Is it still valid?  */
   def1 = Qnil;
   if (!NILP (savedkey))
     def1 = Fkey_binding (savedkey, Qnil);
   /* If not, update it.  */
   if (! EQ (def1, def)
-      /* If something had no key binding before, don't recheck it--
-        doing that takes too much time and makes menus too slow.  */
-      && !(!NILP (cachelist) && NILP (savedkey)))
+      /* If the command is an alias for another
+         (such as easymenu.el and lmenu.el set it up),
+         check if the original command matches the cached command.  */
+      && !(SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function)
+           && EQ (def1, XSYMBOL (def)->function))
+      /* If something had no key binding before, don't recheck it
+        because that is too slow--except if we have a list of rebound
+        commands in Vdefine_key_rebound_commands, do recheck any command
+        that appears in that list.  */
+      && (NILP (cachelist) || !NILP (savedkey)
+         || (! EQ (Qt, Vdefine_key_rebound_commands)
+             && !NILP (Fmemq (def, Vdefine_key_rebound_commands)))))
     {
       changed = 1;
       descrip = Qnil;
-      savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
-      if (VECTORP (savedkey)
-         && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
-       savedkey = Qnil;
+      /* If the command is an alias for another
+        (such as easymenu.el and lmenu.el set it up),
+        see if the original command name has equivalent keys.  */
+      if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function))
+       savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
+                                      Qnil, Qt, Qnil);
+      else
+       /* Otherwise look up the specified command itself.
+          We don't try both, because that makes easymenu menus slow.  */
+       savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
+
       if (!NILP (savedkey))
        {
          descrip = Fkey_description (savedkey);
@@ -345,13 +411,17 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
 
   /* Cache the data we just got in a sublist of the menu binding.  */
   if (NILP (cachelist))
-    XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+    {
+      CHECK_IMPURE (item1);
+      XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+    }
   else if (changed)
     {
       XCONS (cachelist)->car = savedkey;
       XCONS (cachelist)->cdr = descrip;
     }
 
+  UNGCPRO;
   *descrip_ptr = descrip;
   return def;
 }
@@ -362,6 +432,11 @@ static Lisp_Object
 menu_item_enabled_p_1 (arg)
      Lisp_Object arg;
 {
+  /* If we got a quit from within the menu computation,
+     quit all the way out of it.  This takes care of C-] in the debugger.  */
+  if (CONSP (arg) && EQ (XCONS (arg)->car, Qquit))
+    Fsignal (Qquit, Qnil);
+
   return Qnil;
 }
 
@@ -372,13 +447,14 @@ menu_item_enabled_p_1 (arg)
 static Lisp_Object
 menu_item_enabled_p (def, notreal)
      Lisp_Object def;
+     int notreal;
 {
   Lisp_Object enabled, tem;
 
   enabled = Qt;
   if (notreal)
     return enabled;
-  if (XTYPE (def) == Lisp_Symbol)
+  if (SYMBOLP (def))
     {
       /* No property, or nil, means enable.
         Otherwise, enable if value is not nil.  */
@@ -438,18 +514,18 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
 
   push_menu_pane (pane_name, prefix);
 
-  for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
+  for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr)
     {
       /* Look at each key binding, and if it has a menu string,
         make a menu item from it.  */
       item = XCONS (tail)->car;
-      if (XTYPE (item) == Lisp_Cons)
+      if (CONSP (item))
        {
          item1 = XCONS (item)->cdr;
-         if (XTYPE (item1) == Lisp_Cons)
+         if (CONSP (item1))
            {
              item_string = XCONS (item1)->car;
-             if (XTYPE (item_string) == Lisp_String)
+             if (STRINGP (item_string))
                {
                  /* This is the real definition--the function to run.  */
                  Lisp_Object def;
@@ -458,14 +534,16 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                  Lisp_Object descrip;
                  Lisp_Object tem, enabled;
 
-                 def = menu_item_equiv_key (item_string, item1, &descrip);
-
-                 /* GCPRO because we will call eval.
+                 /* GCPRO because ...enabled_p will call eval
+                    and ..._equiv_key may autoload something.
                     Protecting KEYMAP preserves everything we use;
                     aside from that, must protect whatever might be
                     a string.  Since there's no GCPRO5, we refetch
                     item_string instead of protecting it.  */
+                 descrip = def = Qnil;
                  GCPRO4 (keymap, pending_maps, def, descrip);
+
+                 def = menu_item_equiv_key (item_string, item1, &descrip);
                  enabled = menu_item_enabled_p (def, notreal);
 
                  UNGCPRO;
@@ -479,14 +557,18 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                  else
                    {
                      Lisp_Object submap;
+                     GCPRO4 (keymap, pending_maps, descrip, item_string);
                      submap = get_keymap_1 (def, 0, 1);
+                     UNGCPRO;
 #ifndef USE_X_TOOLKIT
                      /* Indicate visually that this is a submenu.  */
                      if (!NILP (submap))
                        item_string = concat2 (item_string,
                                               build_string (" >"));
 #endif
-                     push_menu_item (item_string, enabled, XCONS (item)->car,
+                     /* If definition is nil, pass nil as the key.  */
+                     push_menu_item (item_string, enabled,
+                                     XCONS (item)->car, def,
                                      descrip);
 #ifdef USE_X_TOOLKIT
                      /* Display a submenu using the toolkit.  */
@@ -502,7 +584,7 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                }
            }
        }
-      else if (XTYPE (item) == Lisp_Vector)
+      else if (VECTORP (item))
        {
          /* Loop over the char values represented in the vector.  */
          int len = XVECTOR (item)->size;
@@ -510,12 +592,12 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
          for (c = 0; c < len; c++)
            {
              Lisp_Object character;
-             XFASTINT (character) = c;
+             XSETFASTINT (character, c);
              item1 = XVECTOR (item)->contents[c];
-             if (XTYPE (item1) == Lisp_Cons)
+             if (CONSP (item1))
                {
                  item_string = XCONS (item1)->car;
-                 if (XTYPE (item_string) == Lisp_String)
+                 if (STRINGP (item_string))
                    {
                      Lisp_Object def;
 
@@ -524,15 +606,18 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                      Lisp_Object descrip;
                      Lisp_Object tem, enabled;
 
-                     def = menu_item_equiv_key (item_string, item1, &descrip);
-
-                     /* GCPRO because we will call eval.
+                     /* GCPRO because ...enabled_p will call eval
+                        and ..._equiv_key may autoload something.
                         Protecting KEYMAP preserves everything we use;
                         aside from that, must protect whatever might be
                         a string.  Since there's no GCPRO5, we refetch
                         item_string instead of protecting it.  */
                      GCPRO4 (keymap, pending_maps, def, descrip);
+                     descrip = def = Qnil;
+
+                     def = menu_item_equiv_key (item_string, item1, &descrip);
                      enabled = menu_item_enabled_p (def, notreal);
+
                      UNGCPRO;
 
                      item_string = XCONS (item1)->car;
@@ -544,14 +629,17 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
                      else
                        {
                          Lisp_Object submap;
+                         GCPRO4 (keymap, pending_maps, descrip, item_string);
                          submap = get_keymap_1 (def, 0, 1);
+                         UNGCPRO;
 #ifndef USE_X_TOOLKIT
                          if (!NILP (submap))
                            item_string = concat2 (item_string,
                                                   build_string (" >"));
 #endif
+                         /* If definition is nil, pass nil as the key.  */
                          push_menu_item (item_string, enabled, character,
-                                         descrip);
+                                         def, descrip);
 #ifdef USE_X_TOOLKIT
                          if (! NILP (submap))
                            {
@@ -622,7 +710,7 @@ list_of_items (pane)
     {
       item = Fcar (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil, Qnil);
+       push_menu_item (item, Qnil, Qnil, Qt, Qnil);
       else if (NILP (item))
        push_left_right_boundary ();
       else
@@ -630,7 +718,7 @@ list_of_items (pane)
          CHECK_CONS (item, 0);
          item1 = Fcar (item);
          CHECK_STRING (item1, 1);
-         push_menu_item (item1, Qt, Fcdr (item), Qnil);
+         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil);
        }
     }
 }
@@ -639,7 +727,7 @@ DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
   "Pop up a deck-of-cards menu and return user's selection.\n\
 POSITION is a position specification.  This is either a mouse button event\n\
 or a list ((XOFFSET YOFFSET) WINDOW)\n\
-where XOFFSET and YOFFSET are positions in characters from the top left\n\
+where XOFFSET and YOFFSET are positions in pixels from the top left\n\
 corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)\n\
 This controls the position of the center of the first line\n\
 in the first pane of the menu, not the top left of the menu as a whole.\n\
@@ -677,7 +765,7 @@ cached information about equivalent key sequences.")
   FRAME_PTR f;
   Lisp_Object x, y, window;
   int keymaps = 0;
-  int menubarp = 0;
+  int for_click = 0;
   struct gcpro gcpro1;
 
   if (! NILP (position))
@@ -685,27 +773,31 @@ cached information about equivalent key sequences.")
       check_x ();
 
       /* Decode the first argument: find the window and the coordinates.  */
-      if (EQ (position, Qt))
+      if (EQ (position, Qt)
+         || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar)))
        {
          /* Use the mouse's current position.  */
-         FRAME_PTR new_f = 0;
+         FRAME_PTR new_f = selected_frame;
          Lisp_Object bar_window;
          int part;
          unsigned long time;
 
+         if (mouse_position_hook)
+           (*mouse_position_hook) (&new_f, 1, &bar_window,
+                                   &part, &x, &y, &time);
          if (new_f != 0)
-           XSET (window, Lisp_Frame, new_f);
+           XSETFRAME (window, new_f);
          else
            {
              window = selected_window;
-             XFASTINT (x) = 0;
-             XFASTINT (y) = 0;
+             XSETFASTINT (x, 0);
+             XSETFASTINT (y, 0);
            }
        }
       else
        {
          tem = Fcar (position);
-         if (XTYPE (tem) == Lisp_Cons)
+         if (CONSP (tem))
            {
              window = Fcar (Fcdr (position));
              x = Fcar (tem);
@@ -713,18 +805,12 @@ cached information about equivalent key sequences.")
            }
          else
            {
+             for_click = 1;
              tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
              window = Fcar (tem);           /* POSN_WINDOW (tem) */
              tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
              x = Fcar (tem);
              y = Fcdr (tem);
-
-             /* Determine whether this menu is handling a menu bar click.  */
-             tem = Fcar (Fcdr (Fcar (Fcdr (position))));
-             if (XTYPE (Fcar (position)) != Lisp_Cons
-                 && CONSP (tem)
-                 && EQ (Fcar (tem), Qmenu_bar))
-               menubarp = 1;
            }
        }
 
@@ -733,20 +819,19 @@ cached information about equivalent key sequences.")
 
       /* Decode where to put the menu.  */
 
-      if (XTYPE (window) == Lisp_Frame)
+      if (FRAMEP (window))
        {
          f = XFRAME (window);
-
          xpos = 0;
          ypos = 0;
        }
-      else if (XTYPE (window) == Lisp_Window)
+      else if (WINDOWP (window))
        {
          CHECK_LIVE_WINDOW (window, 0);
          f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
-         xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
-         ypos = (f->display.x->line_height * XWINDOW (window)->top);
+         xpos = (FONT_WIDTH (f->output_data.x->font) * XWINDOW (window)->left);
+         ypos = (f->output_data.x->line_height * XWINDOW (window)->top);
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
@@ -764,7 +849,7 @@ cached information about equivalent key sequences.")
 
   keymap = Fkeymapp (menu);
   tem = Qnil;
-  if (XTYPE (menu) == Lisp_Cons)
+  if (CONSP (menu))
     tem = Fkeymapp (Fcar (menu));
   if (!NILP (keymap))
     {
@@ -797,7 +882,7 @@ cached information about equivalent key sequences.")
 
       /* The first keymap that has a prompt string
         supplies the menu title.  */
-      for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
+      for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem))
        {
          Lisp_Object prompt;
 
@@ -838,7 +923,7 @@ cached information about equivalent key sequences.")
   /* Display them in a menu.  */
   BLOCK_INPUT;
 
-  selection = xmenu_show (f, xpos, ypos, menubarp,
+  selection = xmenu_show (f, xpos, ypos, for_click,
                          keymaps, title, &error_name);
   UNBLOCK_INPUT;
 
@@ -874,32 +959,31 @@ on the left of the dialog box and all following items on the right.\n\
   check_x ();
 
   /* Decode the first argument: find the window or frame to use.  */
-  if (EQ (position, Qt))
+  if (EQ (position, Qt)
+      || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar)))
     {
 #if 0 /* Using the frame the mouse is on may not be right.  */
       /* Use the mouse's current position.  */
-      FRAME_PTR new_f = 0;
+      FRAME_PTR new_f = selected_frame;
       Lisp_Object bar_window;
       int part;
       unsigned long time;
       Lisp_Object x, y;
 
-      (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+      (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
 
       if (new_f != 0)
-       XSET (window, Lisp_Frame, new_f);
+       XSETFRAME (window, new_f);
       else
        window = selected_window;
 #endif
-      /* Decode the first argument: find the window and the coordinates.  */
-      if (EQ (position, Qt))
-       window = selected_window;
+      window = selected_window;
     }
   else if (CONSP (position))
     {
       Lisp_Object tem;
       tem = Fcar (position);
-      if (XTYPE (tem) == Lisp_Cons)
+      if (CONSP (tem))
        window = Fcar (Fcdr (position));
       else
        {
@@ -912,9 +996,9 @@ on the left of the dialog box and all following items on the right.\n\
 
   /* Decode where to put the menu.  */
 
-  if (XTYPE (window) == Lisp_Frame)
+  if (FRAMEP (window))
     f = XFRAME (window);
-  else if (XTYPE (window) == Lisp_Window)
+  else if (WINDOWP (window))
     {
       CHECK_LIVE_WINDOW (window, 0);
       f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
@@ -929,9 +1013,9 @@ on the left of the dialog box and all following items on the right.\n\
      in the middle of frame F.  */
   {
     Lisp_Object x, y, frame, newpos;
-    XSET (frame, Lisp_Frame, f);
-    XSET (x, Lisp_Int, x_pixel_width (f) / 2);
-    XSET (y, Lisp_Int, x_pixel_height (f) / 2);
+    XSETFRAME (frame, f);
+    XSETINT (x, x_pixel_width (f) / 2);
+    XSETINT (y, x_pixel_height (f) / 2);
     newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
 
     return Fx_popup_menu (newpos,
@@ -951,7 +1035,7 @@ on the left of the dialog box and all following items on the right.\n\
 
     /* Display them in a dialog box.  */
     BLOCK_INPUT;
-    selection = xdialog_show (f, 0, 0, title, &error_name);
+    selection = xdialog_show (f, 0, title, &error_name);
     UNBLOCK_INPUT;
 
     discard_menu_items ();
@@ -964,130 +1048,241 @@ on the left of the dialog box and all following items on the right.\n\
 \f
 #ifdef USE_X_TOOLKIT
 
-static void
-dispatch_dummy_expose (w, x, y)
-     Widget w;
-     int x;
-     int y;
-{
-  XExposeEvent dummy;
-       
-  dummy.type = Expose;
-  dummy.window = XtWindow (w);
-  dummy.count = 0;
-  dummy.serial = 0;
-  dummy.send_event = 0;
-  dummy.display = XtDisplay (w);
-  dummy.x = x;
-  dummy.y = y;
+/* Loop in Xt until the menu pulldown or dialog popup has been
+   popped down (deactivated).
 
-  XtDispatchEvent ((XEvent *) &dummy);
-}
+   NOTE: All calls to popup_get_selection should be protected
+   with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
 
-static int
-string_width (mw, s)
-     XlwMenuWidget mw;
-     char* s;
+void
+popup_get_selection (initial_event, dpyinfo, id)
+     XEvent *initial_event;
+     struct x_display_info *dpyinfo;
+     LWLIB_ID id;
 {
-  XCharStruct xcs;
-  int drop;
+  XEvent event;
+
+  /* Define a queue to save up for later unreading
+     all X events that don't pertain to the menu.  */
+  struct event_queue
+    {
+      XEvent event;
+      struct event_queue *next;
+    };
   
-  XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
-  return xcs.width;
-}
+  struct event_queue *queue = NULL;
+  struct event_queue *queue_tmp;
 
-static int
-event_is_in_menu_item (mw, event, name, string_w)
-     XlwMenuWidget mw;
-     struct input_event *event;
-     char *name;
-     int *string_w;
-{
-  *string_w += (string_width (mw, name) 
-               + 2 * (mw->menu.horizontal_spacing
-                      + mw->menu.shadow_thickness));
-  return XINT (event->x) < *string_w;
+  if (initial_event)
+    event = *initial_event;
+  else
+    XtAppNextEvent (Xt_app_con, &event);
+
+  while (1)
+    {
+      /* Handle expose events for editor frames right away.  */
+      if (event.type == Expose)
+       process_expose_from_menu (event);
+      /* Make sure we don't consider buttons grabbed after menu goes.  */
+      else if (event.type == ButtonRelease
+              && dpyinfo->display == event.xbutton.display)
+       dpyinfo->grabbed &= ~(1 << event.xbutton.button);
+      /* If the user presses a key, deactivate the menu.
+        The user is likely to do that if we get wedged.  */
+      else if (event.type == KeyPress
+              && dpyinfo->display == event.xbutton.display)
+       {
+         popup_activated_flag = 0;
+         break;
+       }
+
+      /* Queue all events not for this popup,
+        except for Expose, which we've already handled.
+        Note that the X window is associated with the frame if this
+        is a menu bar popup, but not if it's a dialog box.  So we use
+        x_non_menubar_window_to_frame, not x_any_window_to_frame.  */
+      if (event.type != Expose
+         && (event.xany.display != dpyinfo->display
+             || x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
+       {
+         queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
+
+         if (queue_tmp != NULL) 
+           {
+             queue_tmp->event = event;
+             queue_tmp->next = queue;
+             queue = queue_tmp;
+           }
+       }
+      else
+       XtDispatchEvent (&event);
+
+      if (!popup_activated ())
+       break;
+      XtAppNextEvent (Xt_app_con, &event);
+    }
+
+  /* Unread any events that we got but did not handle.  */
+  while (queue != NULL) 
+    {
+      queue_tmp = queue;
+      XPutBackEvent (queue_tmp->event.xany.display, &queue_tmp->event);
+      queue = queue_tmp->next;
+      free ((char *)queue_tmp);
+      /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
+      interrupt_input_pending = 1;
+    }
 }
 
+/* Activate the menu bar of frame F.
+   This is called from keyboard.c when it gets the
+   menu_bar_activate_event out of the Emacs event queue.
+
+   To activate the menu bar, we use the X button-press event
+   that was saved in saved_button_event.
+   That makes the toolkit do its thing.
 
-/* Return the menu bar key which corresponds to event EVENT in frame F.  */
+   But first we recompute the menu bar contents (the whole tree).
 
-Lisp_Object
-map_event_to_object (event, f)
-     struct input_event *event;
+   The reason for saving the button event until here, instead of
+   passing it to the toolkit right away, is that we can safely
+   execute Lisp code.  */
+   
+x_activate_menubar (f)
      FRAME_PTR f;
 {
-  int i,j, string_w;
-  window_state*        ws;
-  XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
-  widget_value *val;
+  if (f->output_data.x->saved_button_event->type != ButtonPress)
+    return;
 
+  set_frame_menubar (f, 0, 1);
 
-  string_w = 0;
-  /* Find the window */
-  for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
-    {
-      ws = &mw->menu.windows [0];
-      if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
-       {
-         Lisp_Object items;
-         int i;
+  BLOCK_INPUT;
+  XtDispatchEvent ((XEvent *) f->output_data.x->saved_button_event);
+  UNBLOCK_INPUT;
 
-         items = FRAME_MENU_BAR_ITEMS (f);
+  /* Ignore this if we get it a second time.  */
+  f->output_data.x->saved_button_event->type = 0;
+}
 
-         for (i = 0; i < XVECTOR (items)->size; i += 3)
-           {
-             Lisp_Object pos, string, item;
-             item = XVECTOR (items)->contents[i];
-             string = XVECTOR (items)->contents[i + 1];
-             pos = XVECTOR (items)->contents[i + 2];
-             if (NILP (string))
-               break;
+/* Detect if a dialog or menu has been posted.  */
 
-             if (!strcmp (val->name, XSTRING (string)->data))
-               return item;
-           }
-       }
-    }
-  return Qnil;
+int
+popup_activated ()
+{
+  return popup_activated_flag;
 }
 
-static Lisp_Object *menu_item_selection;
+
+/* This callback is invoked when the user selects a menubar cascade
+   pushbutton, but before the pulldown menu is posted.  */
 
 static void
-popup_selection_callback (widget, id, client_data)
+popup_activate_callback (widget, id, client_data)
      Widget widget;
      LWLIB_ID id;
      XtPointer client_data;
 {
-  menu_item_selection = (Lisp_Object *) client_data;
+  popup_activated_flag = 1;
 }
 
+/* This callback is called from the menu bar pulldown menu
+   when the user makes a selection.
+   Figure out what the user chose
+   and put the appropriate events into the keyboard buffer.  */
+
 static void
-popup_down_callback (widget, id, client_data)
+menubar_selection_callback (widget, id, client_data)
      Widget widget;
      LWLIB_ID id;
      XtPointer client_data;
 {
-  BLOCK_INPUT;
-  lw_destroy_all_widgets (id);
-  UNBLOCK_INPUT;
+  Lisp_Object prefix, entry;
+  FRAME_PTR f = menubar_id_to_frame (id);
+  Lisp_Object vector;
+  Lisp_Object *subprefix_stack;
+  int submenu_depth = 0;
+  int i;
+
+  if (!f)
+    return;
+  subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
+  vector = f->menu_bar_vector;
+  prefix = Qnil;
+  i = 0;
+  while (i < f->menu_bar_items_used)
+    {
+      if (EQ (XVECTOR (vector)->contents[i], Qnil))
+       {
+         subprefix_stack[submenu_depth++] = prefix;
+         prefix = entry;
+         i++;
+       }
+      else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
+       {
+         prefix = subprefix_stack[--submenu_depth];
+         i++;
+       }
+      else if (EQ (XVECTOR (vector)->contents[i], Qt))
+       {
+         prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
+         i += MENU_ITEMS_PANE_LENGTH;
+       }
+      else
+       {
+         entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
+         /* The EMACS_INT cast avoids a warning.  There's no problem
+            as long as pointers have enough bits to hold small integers.  */
+         if ((int) (EMACS_INT) client_data == i)
+           {
+             int j;
+             struct input_event buf;
+             Lisp_Object frame;
+
+             XSETFRAME (frame, f);
+             buf.kind = menu_bar_event;
+             buf.frame_or_window = Fcons (frame, Fcons (Qmenu_bar, Qnil));
+             kbd_buffer_store_event (&buf);
+
+             for (j = 0; j < submenu_depth; j++)
+               if (!NILP (subprefix_stack[j]))
+                 {
+                   buf.kind = menu_bar_event;
+                   buf.frame_or_window = Fcons (frame, subprefix_stack[j]);
+                   kbd_buffer_store_event (&buf);
+                 }
+
+             if (!NILP (prefix))
+               {
+                 buf.kind = menu_bar_event;
+                 buf.frame_or_window = Fcons (frame, prefix);
+                 kbd_buffer_store_event (&buf);
+               }
+
+             buf.kind = menu_bar_event;
+             buf.frame_or_window = Fcons (frame, entry);
+             kbd_buffer_store_event (&buf);
+
+             return;
+           }
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
 }
 
+/* This callback is invoked when a dialog or menu is finished being
+   used and has been unposted.  */
+
 static void
-dialog_selection_callback (widget, id, client_data)
+popup_deactivate_callback (widget, id, client_data)
      Widget widget;
      LWLIB_ID id;
      XtPointer client_data;
 {
-  if ((int)client_data != -1)
-    menu_item_selection = (Lisp_Object *) client_data;
-  BLOCK_INPUT;
-  lw_destroy_all_widgets (id);
-  UNBLOCK_INPUT;
+  popup_activated_flag = 0;
 }
 
-/* This recursively calls free_widget_value() on the tree of widgets.
+
+/* This recursively calls free_widget_value on the tree of widgets.
    It must free all data that was malloc'ed for these widget_values.
    In Emacs, many slots are pointers into the data of Lisp_Strings, and
    must be left alone.  */
@@ -1114,226 +1309,480 @@ free_menubar_widget_value_tree (wv)
   free_widget_value (wv);
   UNBLOCK_INPUT;
 }
+\f
+/* Return a tree of widget_value structures for a menu bar item
+   whose event type is ITEM_KEY (with string ITEM_NAME)
+   and whose contents come from the list of keymaps MAPS.  */
 
-extern void EmacsFrameSetCharSize ();
-
-static void
-update_frame_menubar (f)
-     FRAME_PTR f;
+static widget_value *
+single_submenu (item_key, item_name, maps)
+     Lisp_Object item_key, item_name, maps;
 {
-  struct x_display *x = f->display.x;
-  int columns, rows;
-  int menubar_changed;
-  
-  menubar_changed = (x->menubar_widget
-                    && !XtIsManaged (x->menubar_widget));
-
-  if (! (menubar_changed))
-    return;
-
-  BLOCK_INPUT;
-  /* Save the size of the frame because the pane widget doesn't accept to
-     resize itself. So force it.  */
-  columns = f->width;
-  rows = f->height;
-
+  widget_value *wv, *prev_wv, *save_wv, *first_wv;
+  int i;
+  int submenu_depth = 0;
+  Lisp_Object length;
+  int len;
+  Lisp_Object *mapvec;
+  widget_value **submenu_stack;
+  int mapno;
+  int previous_items = menu_items_used;
+  int top_level_items = 0;
 
-  XawPanedSetRefigureMode (x->column_widget, 0);
-  
-  /* the order in which children are managed is the top to
-     bottom order in which they are displayed in the paned window.
-     First, remove the text-area widget.
-   */
-  XtUnmanageChild (x->edit_widget);
+  length = Flength (maps);
+  len = XINT (length);
 
-  /* remove the menubar that is there now, and put up the menubar that
-     should be there.
-   */
-  if (menubar_changed)
+  /* Convert the list MAPS into a vector MAPVEC.  */
+  mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
+  for (i = 0; i < len; i++)
     {
-      XtManageChild (x->menubar_widget);
-      XtMapWidget (x->menubar_widget);
-      XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
+      mapvec[i] = Fcar (maps);
+      maps = Fcdr (maps);
     }
 
+  menu_items_n_panes = 0;
 
-  /* Re-manage the text-area widget */
-  XtManageChild (x->edit_widget);
-
-  /* and now thrash the sizes */
-  XawPanedSetRefigureMode (x->column_widget, 1);
-
-  /* Force the pane widget to resize itself with the right values.  */
-  EmacsFrameSetCharSize (x->edit_widget, columns, rows);
-
-  UNBLOCK_INPUT;
-}
-
-void
-set_frame_menubar (f, first_time)
-     FRAME_PTR f;
-     int first_time;
-{
-  Widget menubar_widget = f->display.x->menubar_widget;
-  int id = (int) f;
-  Lisp_Object tail, items;
-  widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;
-  int i;
+  /* Loop over the given keymaps, making a pane for each map.
+     But don't make a pane that is empty--ignore that map instead.  */
+  for (i = 0; i < len; i++)
+    {
+      if (SYMBOLP (mapvec[i]))
+       {
+         top_level_items = 1;
+         push_menu_pane (Qnil, Qnil);
+         push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil);
+       }
+      else
+       single_keymap_panes (mapvec[i], item_name, item_key, 0);
+    }
 
-  BLOCK_INPUT;
+  /* Create a tree of widget_value objects
+     representing the panes and their items.  */
 
+  submenu_stack
+    = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
   wv = malloc_widget_value ();
-  wv->name = "menubar";
+  wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
-  save_wv = first_wv = wv;
-
-  if (NILP (items = FRAME_MENU_BAR_ITEMS (f)))
-    items = FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
-
-  for (i = 0; i < XVECTOR (items)->size; i += 3)
-    {
-      Lisp_Object string;
-
-      string = XVECTOR (items)->contents[i + 1];
-      if (NILP (string))
-       break;
-
-      wv = malloc_widget_value ();
-      if (prev_wv) 
-       prev_wv->next = wv;
-      else 
-       save_wv->contents = wv;
-      wv->name = (char *) XSTRING (string)->data;
-      wv->value = 0;
-      wv->enabled = 1;
-      prev_wv = wv;
-    }
-
-  if (menubar_widget)
-    lw_modify_all_widgets (id, first_wv, False);
-  else
+  first_wv = wv;
+  save_wv = 0;
+  prev_wv = 0;
+  /* Loop over all panes and items made during this call
+     and construct a tree of widget_value objects.
+     Ignore the panes and items made by previous calls to
+     single_submenu, even though those are also in menu_items.  */
+  i = previous_items;
+  while (i < menu_items_used)
     {
-      menubar_widget = lw_create_widget ("menubar", "menubar", 
-                                        id, first_wv, 
-                                        f->display.x->column_widget, 
-                                        0, 0,
-                                        0, 0);
-      f->display.x->menubar_widget = menubar_widget;
-      XtVaSetValues (menubar_widget,
-                    XtNshowGrip, 0,
-                    XtNresizeToPreferred, 1,
-                    XtNallowResize, 1,
-                    0);
-    }
-  
-  free_menubar_widget_value_tree (first_wv);
-
-  /* Don't update the menubar the first time it is created via x_window.  */
-  if (!first_time)
-    update_frame_menubar (f);
-
-  UNBLOCK_INPUT;
+      if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+       {
+         submenu_stack[submenu_depth++] = save_wv;
+         save_wv = prev_wv;
+         prev_wv = 0;
+         i++;
+       }
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+       {
+         prev_wv = save_wv;
+         save_wv = submenu_stack[--submenu_depth];
+         i++;
+       }
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
+              && submenu_depth != 0)
+       i += MENU_ITEMS_PANE_LENGTH;
+      /* Ignore a nil in the item list.
+        It's meaningful only for dialog boxes.  */
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+       i += 1;
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+       {
+         /* Create a new pane.  */
+         Lisp_Object pane_name, prefix;
+         char *pane_string;
+         pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
+         prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+         pane_string = (NILP (pane_name)
+                        ? "" : (char *) XSTRING (pane_name)->data);
+         /* If there is just one top-level pane, put all its items directly
+            under the top-level menu.  */
+         if (menu_items_n_panes == 1)
+           pane_string = "";
+
+         /* If the pane has a meaningful name,
+            make the pane a top-level menu item
+            with its items as a submenu beneath it.  */
+         if (strcmp (pane_string, ""))
+           {
+             wv = malloc_widget_value ();
+             if (save_wv)
+               save_wv->next = wv;
+             else
+               first_wv->contents = wv;
+             wv->name = pane_string;
+             /* Ignore the @ that means "separate pane".
+                This is a kludge, but this isn't worth more time.  */
+             if (!NILP (prefix) && wv->name[0] == '@')
+               wv->name++;
+             wv->value = 0;
+             wv->enabled = 1;
+           }
+         save_wv = wv;
+         prev_wv = 0;
+         i += MENU_ITEMS_PANE_LENGTH;
+       }
+      else
+       {
+         /* Create a new item within current pane.  */
+         Lisp_Object item_name, enable, descrip, def;
+         item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
+         enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
+         descrip
+           = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+         def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
+
+         wv = malloc_widget_value ();
+         if (prev_wv) 
+           prev_wv->next = wv;
+         else
+           save_wv->contents = wv;
+
+         wv->name = (char *) XSTRING (item_name)->data;
+         if (!NILP (descrip))
+           wv->key = (char *) XSTRING (descrip)->data;
+         wv->value = 0;
+         /* The EMACS_INT cast avoids a warning.  There's no problem
+            as long as pointers have enough bits to hold small integers.  */
+         wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
+         wv->enabled = !NILP (enable);
+         prev_wv = wv;
+
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
+
+  /* If we have just one "menu item"
+     that was originally a button, return it by itself.  */
+  if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
+    {
+      wv = first_wv->contents;
+      free_widget_value (first_wv);
+      return wv;
+    }
+
+  return first_wv;
 }
+\f
+extern void EmacsFrameSetCharSize ();
 
-void
-free_frame_menubar (f)
+/* Recompute all the widgets of frame F, when the menu bar
+   has been changed.  */
+
+static void
+update_frame_menubar (f)
      FRAME_PTR f;
 {
-  Widget menubar_widget;
-  int id;
-
-  menubar_widget = f->display.x->menubar_widget;
-  id = (int) f;
+  struct x_output *x = f->output_data.x;
+  int columns, rows;
+  int menubar_changed;
   
-  if (menubar_widget)
+  Dimension shell_height;
+
+  /* We assume the menubar contents has changed if the global flag is set,
+     or if the current buffer has changed, or if the menubar has never
+     been updated before.
+   */
+  menubar_changed = (x->menubar_widget
+                    && !XtIsManaged (x->menubar_widget));
+
+  if (! (menubar_changed))
+    return;
+
+  BLOCK_INPUT;
+  /* Save the size of the frame because the pane widget doesn't accept to
+     resize itself. So force it.  */
+  columns = f->width;
+  rows = f->height;
+
+  /* Do the voodoo which means "I'm changing lots of things, don't try to
+     refigure sizes until I'm done." */
+  lw_refigure_widget (x->column_widget, False);
+
+  /* the order in which children are managed is the top to
+     bottom order in which they are displayed in the paned window.
+     First, remove the text-area widget.
+   */
+  XtUnmanageChild (x->edit_widget);
+
+  /* remove the menubar that is there now, and put up the menubar that
+     should be there.
+   */
+  if (menubar_changed)
     {
-      BLOCK_INPUT;
-      lw_destroy_all_widgets (id);
-      UNBLOCK_INPUT;
+      XtManageChild (x->menubar_widget);
+      XtMapWidget (x->menubar_widget);
+      XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
     }
+
+  /* Re-manage the text-area widget, and then thrash the sizes.  */
+  XtManageChild (x->edit_widget);
+  lw_refigure_widget (x->column_widget, True);
+
+  /* Force the pane widget to resize itself with the right values.  */
+  EmacsFrameSetCharSize (x->edit_widget, columns, rows);
+
+  UNBLOCK_INPUT;
 }
-/* Called from Fx_create_frame to create the inital menubar of a frame
-   before it is mapped, so that the window is mapped with the menubar already
-   there instead of us tacking it on later and thrashing the window after it
-   is visible.  */
+
+/* Set the contents of the menubar widgets of frame F.
+   The argument FIRST_TIME is currently ignored;
+   it is set the first time this is called, from initialize_frame_menubar.  */
+
 void
-initialize_frame_menubar (f)
+set_frame_menubar (f, first_time, deep_p)
      FRAME_PTR f;
+     int first_time;
+     int deep_p;
 {
-  set_frame_menubar (f, 1);
-}
-\f
-/* Horizontal bounds of the current menu bar item.  */
+  Widget menubar_widget = f->output_data.x->menubar_widget;
+  Lisp_Object tail, items, frame;
+  widget_value *wv, *first_wv, *prev_wv = 0;
+  int i;
+  LWLIB_ID id;
 
-static int this_menu_bar_item_beg;
-static int this_menu_bar_item_end;
+  if (f->output_data.x->id == 0)
+    f->output_data.x->id = next_menubar_widget_id++;
+  id = f->output_data.x->id;
 
-/* Horizontal position of the end of the last menu bar item.  */
+  if (! menubar_widget)
+    deep_p = 1;
 
-static int last_menu_bar_item_end;
+  wv = malloc_widget_value ();
+  wv->name = "menubar";
+  wv->value = 0;
+  wv->enabled = 1;
+  first_wv = wv;
 
-/* Nonzero if position X, Y is in the menu bar and in some menu bar item
-   but not in the current menu bar item.  */
+  if (deep_p)
+    {
+      /* Make a widget-value tree representing the entire menu trees.  */
+
+      struct buffer *prev = current_buffer;
+      Lisp_Object buffer;
+      int specpdl_count = specpdl_ptr - specpdl;
+      int previous_menu_items_used = f->menu_bar_items_used;
+      Lisp_Object *previous_items
+       = (Lisp_Object *) alloca (previous_menu_items_used
+                                 * sizeof (Lisp_Object));
+
+      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
+      specbind (Qinhibit_quit, Qt);
+      /* Don't let the debugger step into this code
+        because it is not reentrant.  */
+      specbind (Qdebug_on_next_call, Qnil);
+
+      record_unwind_protect (Fstore_match_data, Fmatch_data ());
+      if (NILP (Voverriding_local_map_menu_flag))
+       {
+         specbind (Qoverriding_terminal_local_map, Qnil);
+         specbind (Qoverriding_local_map, Qnil);
+       }
 
-static int
-other_menu_bar_item_p (f, x, y)
-     FRAME_PTR f;
-     int x, y;
-{
-  return (y >= 0
-         && y < f->display.x->menubar_widget->core.height
-         && x >= 0
-         && x < last_menu_bar_item_end
-         && (x >= this_menu_bar_item_end
-             || x < this_menu_bar_item_beg));
+      set_buffer_internal_1 (XBUFFER (buffer));
+
+      /* Run the Lucid hook.  */
+      call1 (Vrun_hooks, Qactivate_menubar_hook);
+      /* If it has changed current-menubar from previous value,
+        really recompute the menubar from the value.  */
+      if (! NILP (Vlucid_menu_bar_dirty_flag))
+       call0 (Qrecompute_lucid_menubar);
+      call1 (Vrun_hooks, Qmenu_bar_update_hook);
+      FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
+
+      items = FRAME_MENU_BAR_ITEMS (f);
+
+      inhibit_garbage_collection ();
+
+      /* Save the frame's previous menu bar contents data.  */
+      bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
+            previous_menu_items_used * sizeof (Lisp_Object));
+
+      /* Fill in the current menu bar contents.  */
+      menu_items = f->menu_bar_vector;
+      menu_items_allocated = XVECTOR (menu_items)->size;
+      init_menu_items ();
+      for (i = 0; i < XVECTOR (items)->size; i += 3)
+       {
+         Lisp_Object key, string, maps;
+
+         key = XVECTOR (items)->contents[i];
+         string = XVECTOR (items)->contents[i + 1];
+         maps = XVECTOR (items)->contents[i + 2];
+         if (NILP (string))
+           break;
+
+         wv = single_submenu (key, string, maps);
+         if (prev_wv) 
+           prev_wv->next = wv;
+         else
+           first_wv->contents = wv;
+         /* Don't set wv->name here; GC during the loop might relocate it.  */
+         wv->enabled = 1;
+         prev_wv = wv;
+       }
+
+      finish_menu_items ();
+
+      set_buffer_internal_1 (prev);
+      unbind_to (specpdl_count, Qnil);
+
+      /* If there has been no change in the Lisp-level contents
+        of the menu bar, skip redisplaying it.  Just exit.  */
+
+      for (i = 0; i < previous_menu_items_used; i++)
+       if (menu_items_used == i
+           || (previous_items[i] != XVECTOR (menu_items)->contents[i]))
+         break;
+      if (i == menu_items_used && i == previous_menu_items_used)
+       {
+         free_menubar_widget_value_tree (first_wv);
+         menu_items = Qnil;
+
+         return;
+       }
+
+      /* Now GC cannot happen during the lifetime of the widget_value,
+        so it's safe to store data from a Lisp_String.  */
+      wv = first_wv->contents;
+      for (i = 0; i < XVECTOR (items)->size; i += 3)
+       {
+         Lisp_Object string;
+         string = XVECTOR (items)->contents[i + 1];
+         if (NILP (string))
+           break;
+         wv->name = (char *) XSTRING (string)->data;
+         wv = wv->next;
+       }
+
+      f->menu_bar_vector = menu_items;
+      f->menu_bar_items_used = menu_items_used;
+      menu_items = Qnil;
+    }
+  else
+    {
+      /* Make a widget-value tree containing
+        just the top level menu bar strings.  */
+
+      items = FRAME_MENU_BAR_ITEMS (f);
+      for (i = 0; i < XVECTOR (items)->size; i += 3)
+       {
+         Lisp_Object string;
+
+         string = XVECTOR (items)->contents[i + 1];
+         if (NILP (string))
+           break;
+
+         wv = malloc_widget_value ();
+         wv->name = (char *) XSTRING (string)->data;
+         wv->value = 0;
+         wv->enabled = 1;
+
+         if (prev_wv) 
+           prev_wv->next = wv;
+         else
+           first_wv->contents = wv;
+         prev_wv = wv;
+       }
+    }
+
+  /* Create or update the menu bar widget.  */
+
+  BLOCK_INPUT;
+
+  if (menubar_widget)
+    {
+      /* Disable resizing (done for Motif!) */
+      lw_allow_resizing (f->output_data.x->widget, False);
+
+      /* The third arg is DEEP_P, which says to consider the entire
+        menu trees we supply, rather than just the menu bar item names.  */
+      lw_modify_all_widgets (id, first_wv, deep_p);
+
+      /* Re-enable the edit widget to resize.  */
+      lw_allow_resizing (f->output_data.x->widget, True);
+    }
+  else
+    {
+      menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv, 
+                                        f->output_data.x->column_widget,
+                                        0,
+                                        popup_activate_callback,
+                                        menubar_selection_callback,
+                                        popup_deactivate_callback);
+      f->output_data.x->menubar_widget = menubar_widget;
+    }
+
+  {
+    int menubar_size 
+      = (f->output_data.x->menubar_widget
+        ? (f->output_data.x->menubar_widget->core.height
+           + f->output_data.x->menubar_widget->core.border_width)
+        : 0);
+
+    if (FRAME_EXTERNAL_MENU_BAR (f))
+      {
+        Dimension ibw = 0;
+        XtVaGetValues (f->output_data.x->column_widget,
+                      XtNinternalBorderWidth, &ibw, NULL);
+        menubar_size += ibw;
+      }
+
+    f->output_data.x->menubar_height = menubar_size;
+  }
+  
+  free_menubar_widget_value_tree (first_wv);
+
+  update_frame_menubar (f);
+
+  UNBLOCK_INPUT;
 }
 
-/* Unread a button-press event in the menu bar of frame F
-   at x position XPOS relative to the inside of the frame.  */
+/* Called from Fx_create_frame to create the inital menubar of a frame
+   before it is mapped, so that the window is mapped with the menubar already
+   there instead of us tacking it on later and thrashing the window after it
+   is visible.  */
 
-static void
-unread_menu_bar_button (f, xpos)
+void
+initialize_frame_menubar (f)
      FRAME_PTR f;
-     int xpos;
 {
-  XEvent event;
-
-  event.type = ButtonPress;
-  event.xbutton.display = x_current_display;
-  event.xbutton.serial = 0;
-  event.xbutton.send_event = 0;
-  event.xbutton.time = CurrentTime;
-  event.xbutton.button = Button1;
-  event.xbutton.window = XtWindow (f->display.x->menubar_widget);
-  event.xbutton.x = xpos;
-  XPutBackEvent (XDISPLAY &event);
+  /* This function is called before the first chance to redisplay
+     the frame.  It has to be, so the frame will have the right size.  */
+  FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
+  set_frame_menubar (f, 1, 1);
 }
 
-/* If the mouse has moved to another menu bar item,
-   return 1 and unread a button press event for that item.
-   Otherwise return 0.  */
+/* Get rid of the menu bar of frame F, and free its storage.
+   This is used when deleting a frame, and when turning off the menu bar.  */
 
-static int
-check_mouse_other_menu_bar (f)
+void
+free_frame_menubar (f)
      FRAME_PTR f;
 {
-  FRAME_PTR new_f;
-  Lisp_Object bar_window;
-  int part;
-  Lisp_Object x, y;
-  unsigned long time;
-
-  (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+  Widget menubar_widget;
+  int id;
 
-  if (f == new_f && other_menu_bar_item_p (f, x, y))
+  menubar_widget = f->output_data.x->menubar_widget;
+  
+  if (menubar_widget)
     {
-      unread_menu_bar_button (f, x);
-      return 1;
+      BLOCK_INPUT;
+      lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
+      UNBLOCK_INPUT;
     }
-
-  return 0;
 }
+
 #endif /* USE_X_TOOLKIT */
 \f
 /* xmenu_show actually displays a menu using the panes and items in menu_items
@@ -1344,7 +1793,7 @@ check_mouse_other_menu_bar (f)
 /* F is the frame the menu is for.
    X and Y are the frame-relative specified position,
    relative to the inside upper left corner of the frame F.
-   MENUBARP is 1 if the click that asked for this menu came from the menu bar.
+   FOR_CLICK if this menu was invoked for a mouse click.
    KEYMAPS is 1 if this menu was specified with keymaps;
     in that case, we return a list containing the chosen item's value
     and perhaps also the pane's prefix.
@@ -1354,88 +1803,64 @@ check_mouse_other_menu_bar (f)
 
 #ifdef USE_X_TOOLKIT
 
-extern unsigned int x_mouse_grabbed;
-extern Lisp_Object Vmouse_depressed;
+/* We need a unique id for each widget handled by the Lucid Widget
+   library.
+
+   For the main windows, and popup menus, we use this counter,
+   which we increment each time after use.  This starts from 1<<16.
+
+   For menu bars, we use numbers starting at 0, counted in
+   next_menubar_widget_id.  */
+LWLIB_ID widget_id_tick;
+
+#ifdef __STDC__
+static Lisp_Object *volatile menu_item_selection;
+#else
+static Lisp_Object *menu_item_selection;
+#endif
+
+static void
+popup_selection_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+  menu_item_selection = (Lisp_Object *) client_data;
+}
 
 static Lisp_Object
-xmenu_show (f, x, y, menubarp, keymaps, title, error)
+xmenu_show (f, x, y, for_click, keymaps, title, error)
      FRAME_PTR f;
      int x;
      int y;
-     int menubarp;
+     int for_click;
      int keymaps;
      Lisp_Object title;
      char **error;
 {
   int i;
-  int menu_id;
+  LWLIB_ID menu_id;
   Widget menu;
-  XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
-
-  /* This is the menu bar item (if any) that led to this menu.  */
-  widget_value *menubar_item = 0;
-
+  Arg av[2];
+  int ac = 0;
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
   widget_value **submenu_stack
     = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
   Lisp_Object *subprefix_stack
     = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
   int submenu_depth = 0;
+  XButtonPressedEvent dummy;
 
-  /* Define a queue to save up for later unreading
-     all X events that don't pertain to the menu.  */
-  struct event_queue
-    {
-      XEvent event;
-      struct event_queue *next;
-    };
-  
-  struct event_queue *queue = NULL;
-  struct event_queue *queue_tmp;
-
-  Position root_x, root_y;
+  int first_pane;
+  int next_release_must_exit = 0;
 
   *error = NULL;
 
-  this_menu_bar_item_beg = -1;
-  this_menu_bar_item_end = -1;
-  last_menu_bar_item_end = -1;
-
-  /* Figure out which menu bar item, if any, this menu is for.  */
-  if (menubarp)
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
     {
-      int xbeg;
-      int xend = 0;
-
-      for (menubar_item = menubar->menu.old_stack[0]->contents;
-          menubar_item;
-          menubar_item = menubar_item->next)
-       {
-         xbeg = xend;
-         xend += (string_width (menubar, menubar_item->name) 
-                  + 2 * (menubar->menu.horizontal_spacing
-                         + menubar->menu.shadow_thickness));
-         if (x >= xbeg && x < xend)
-           {
-             x = xbeg + 4;
-             y = 0;
-             /* Arrange to show a different menu if we move in the menu bar
-                to a different item.  */
-             this_menu_bar_item_beg = xbeg;
-             this_menu_bar_item_end = xend;
-           }
-       }
-      last_menu_bar_item_end = xend;
+      *error = "Empty menu";
+      return Qnil;
     }
-  if (menubar_item == 0)
-    menubarp = 0;
-
-  /* Offset the coordinates to root-relative.  */
-  XtTranslateCoords (f->display.x->widget,
-                    x, y + f->display.x->menubar_widget->core.height,
-                    &root_x, &root_y);
-  x = root_x;
-  y = root_y;
 
   /* Create a tree of widget_value objects
      representing the panes and their items.  */
@@ -1444,6 +1869,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   wv->value = 0;
   wv->enabled = 1;
   first_wv = wv;
+  first_pane = 1;
  
   /* Loop over all panes and items, filling in the tree.  */
   i = 0;
@@ -1454,12 +1880,14 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          submenu_stack[submenu_depth++] = save_wv;
          save_wv = prev_wv;
          prev_wv = 0;
+         first_pane = 1;
          i++;
        }
       else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
        {
          prev_wv = save_wv;
          save_wv = submenu_stack[--submenu_depth];
+         first_pane = 0;
          i++;
        }
       else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
@@ -1486,7 +1914,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          /* If the pane has a meaningful name,
             make the pane a top-level menu item
             with its items as a submenu beneath it.  */
-         if (strcmp (pane_string, ""))
+         if (!keymaps && strcmp (pane_string, ""))
            {
              wv = malloc_widget_value ();
              if (save_wv)
@@ -1498,19 +1926,26 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
                wv->name++;
              wv->value = 0;
              wv->enabled = 1;
+             save_wv = wv;
+             prev_wv = 0;
            }
-         save_wv = wv;
-         prev_wv = 0;
+         else if (first_pane)
+           {
+             save_wv = wv;
+             prev_wv = 0;
+           }
+         first_pane = 0;
          i += MENU_ITEMS_PANE_LENGTH;
        }
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip;
+         Lisp_Object item_name, enable, descrip, def;
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+         def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
 
          wv = malloc_widget_value ();
          if (prev_wv) 
@@ -1521,7 +1956,11 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          if (!NILP (descrip))
            wv->key = (char *) XSTRING (descrip)->data;
          wv->value = 0;
-         wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+         /* If this item has a null value,
+            make the call_data null so that it won't display a box
+            when the mouse is on it.  */
+         wv->call_data
+           = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
          wv->enabled = !NILP (enable);
          prev_wv = wv;
 
@@ -1529,173 +1968,110 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
        }
     }
 
+  /* Deal with the title, if it is non-nil.  */
+  if (!NILP (title))
+    {
+      widget_value *wv_title = malloc_widget_value ();
+      widget_value *wv_sep1 = malloc_widget_value ();
+      widget_value *wv_sep2 = malloc_widget_value ();
+
+      wv_sep2->name = "--";
+      wv_sep2->next = first_wv->contents;
+
+      wv_sep1->name = "--";
+      wv_sep1->next = wv_sep2;
+
+      wv_title->name = (char *) XSTRING (title)->data;
+      wv_title->enabled = True;
+      wv_title->next = wv_sep1;
+      first_wv->contents = wv_title;
+    }
+
   /* Actually create the menu.  */
-  menu_id = ++popup_id_tick;
+  menu_id = widget_id_tick++;
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
-                          f->display.x->widget, 1, 0,
-                          popup_selection_callback, popup_down_callback);
-  /* Free the widget_value objects we used to specify the contents.  */
-  free_menubar_widget_value_tree (first_wv);
+                          f->output_data.x->widget, 1, 0,
+                          popup_selection_callback,
+                          popup_deactivate_callback);
 
-  /* No selection has been chosen yet.  */
-  menu_item_selection = 0;
+  /* Adjust coordinates to relative to the outer (window manager) window.  */
+  {
+    Window child;
+    int win_x = 0, win_y = 0;
 
-  /* If the mouse moves out of the menu before we show the menu,
-     don't show it at all.  */
-  if (check_mouse_other_menu_bar (f))
-    {
-      lw_destroy_all_widgets (menu_id); 
-      return Qnil;
-    }
+    /* Find the position of the outside upper-left corner of
+       the inner window, with respect to the outer window.  */
+    if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
+      {
+       BLOCK_INPUT;
+       XTranslateCoordinates (FRAME_X_DISPLAY (f),
 
+                              /* From-window, to-window.  */
+                              f->output_data.x->window_desc,
+                              f->output_data.x->parent_desc,
 
-  /* Highlight the menu bar item (if any) that led to this menu.  */
-  if (menubarp)
-    {
-      menubar_item->call_data = (XtPointer) 1;
-      dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
-    }
+                              /* From-position, to-position.  */
+                              0, 0, &win_x, &win_y,
 
-  /* Display the menu.  */
-  {
-    XButtonPressedEvent dummy;
-    XlwMenuWidget mw;
-    
-    mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0];
-
-    dummy.type = ButtonPress;
-    dummy.serial = 0;
-    dummy.send_event = 0;
-    dummy.display = XtDisplay (menu);
-    dummy.window = XtWindow (XtParent (menu));
-    dummy.time = CurrentTime;
-    dummy.button = 0;
-    dummy.x_root = x;
-    dummy.y_root = y;
-
-    /* We activate directly the lucid implementation.  */
-    pop_up_menu (mw, &dummy);
+                              /* Child of window.  */
+                              &child);
+       UNBLOCK_INPUT;
+       x += win_x;
+       y += win_y;
+      }
   }
 
-  /* No need to check a second time since this is done in the XEvent loop.
-     This slows done the execution.  */
-#if 0
-  /* Check again whether the mouse has moved to another menu bar item.  */
-  if (check_mouse_other_menu_bar (f))
-    {
-      /* The mouse moved into a different menu bar item. 
-        We should bring up that item's menu instead.
-        First pop down this menu.  */
-      XtUngrabPointer ((Widget)
-                      ((XlwMenuWidget)
-                       ((CompositeWidget)menu)->composite.children[0]),
-                      CurrentTime);
-      lw_destroy_all_widgets (menu_id); 
-      goto pop_down;
-    }
-#endif
+  /* Adjust coordinates to be root-window-relative.  */
+  x += f->output_data.x->left_pos;
+  y += f->output_data.x->top_pos;
 
-  /* Process events that apply to the menu.  */
-  while (1)
-    {
-      XEvent event;
+  dummy.type = ButtonPress;
+  dummy.serial = 0;
+  dummy.send_event = 0;
+  dummy.display = FRAME_X_DISPLAY (f);
+  dummy.time = CurrentTime;
+  dummy.button = 0;
+  dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
+  dummy.window = dummy.root;
+  dummy.subwindow = dummy.root;
+  dummy.x_root = x;
+  dummy.y_root = y;
+  dummy.x = x;
+  dummy.y = y;
 
-      XtAppNextEvent (Xt_app_con, &event);
-      if (event.type == ButtonRelease)
-       {
-         XtDispatchEvent (&event);
-         if (! menubarp)
-           {
-             /* Do the work of construct_mouse_click since it can't
-                be called. Initially, the popup menu has been called
-                from a ButtonPress in the edit_widget. Then the mouse
-                has been set to grabbed. Reset it now.  */
-             x_mouse_grabbed &= ~(1 << event.xbutton.button);
-             if (!x_mouse_grabbed)
-               Vmouse_depressed = Qnil;
-           }
-         break;
-       }
-      else if (event.type == Expose)
-       process_expose_from_menu (event);
-      else if (event.type == MotionNotify)
-       {
-         int event_x = (event.xmotion.x_root
-                        - (f->display.x->widget->core.x
-                           + f->display.x->widget->core.border_width));
-         int event_y = (event.xmotion.y_root
-                        - (f->display.x->widget->core.y
-                           + f->display.x->widget->core.border_width));
-
-         if (other_menu_bar_item_p (f, event_x, event_y))
-           {
-             /* The mouse moved into a different menu bar item. 
-                We should bring up that item's menu instead.
-                First pop down this menu.  */
-             XtUngrabPointer ((Widget)
-                              ((XlwMenuWidget)
-                               ((CompositeWidget)menu)->composite.children[0]),
-                              event.xbutton.time);
-             lw_destroy_all_widgets (menu_id); 
-
-             /* Put back an event that will bring up the other item's menu.  */
-             unread_menu_bar_button (f, event_x);
-             /* Don't let us select anything in this case.  */
-             menu_item_selection = 0;
-             break;
-           }
-       }
+  /* Don't allow any geometry request from the user.  */
+  XtSetArg (av[ac], XtNgeometry, 0); ac++;
+  XtSetValues (menu, av, ac);
 
-      XtDispatchEvent (&event);
-      if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
-       {
-         queue_tmp
-           = (struct event_queue *) malloc (sizeof (struct event_queue));
+  /* Free the widget_value objects we used to specify the contents.  */
+  free_menubar_widget_value_tree (first_wv);
 
-         if (queue_tmp != NULL) 
-           {
-             queue_tmp->event = event;
-             queue_tmp->next = queue;
-             queue = queue_tmp;
-           }
-       }
-    }
+  /* No selection has been chosen yet.  */
+  menu_item_selection = 0;
 
- pop_down:
-  /* Unhighlight the menu bar item (if any) that led to this menu.  */
-  if (menubarp)
-    {
-      menubar_item->call_data = (XtPointer) 0;
-      dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
-    }
+  /* Display the menu.  */
+  lw_popup_menu (menu, &dummy);
+  popup_activated_flag = 1;
+
+  /* Process events that apply to the menu.  */
+  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id);
 
   /* fp turned off the following statement and wrote a comment
      that it is unnecessary--that the menu has already disappeared.
-     I observer that is not so. -- rms.  */
-  /* Make sure the menu disappears.  */
+     Nowadays the menu disappears ok, all right, but
+     we need to delete the widgets or multiple ones will pile up.  */
   lw_destroy_all_widgets (menu_id); 
 
-  /* Unread any events that we got but did not handle.  */
-  while (queue != NULL) 
-    {
-      queue_tmp = queue;
-      XPutBackEvent (XDISPLAY &queue_tmp->event);
-      queue = queue_tmp->next;
-      free ((char *)queue_tmp);
-    }
-
   /* Find the selected item, and its pane, to return
      the proper value.  */
   if (menu_item_selection != 0)
     {
-      Lisp_Object prefix;
+      Lisp_Object prefix, entry;
 
       prefix = Qnil;
       i = 0;
       while (i < menu_items_used)
        {
-         Lisp_Object entry;
-
          if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
            {
              subprefix_stack[submenu_depth++] = prefix;
@@ -1727,7 +2103,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
                      if (!NILP (prefix))
                        entry = Fcons (prefix, entry);
                      for (j = submenu_depth - 1; j >= 0; j--)
-                       entry = Fcons (subprefix_stack[j], entry);
+                       if (!NILP (subprefix_stack[j]))
+                         entry = Fcons (subprefix_stack[j], entry);
                    }
                  return entry;
                }
@@ -1738,41 +2115,41 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
 
   return Qnil;
 }
+\f
+static void
+dialog_selection_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+  /* The EMACS_INT cast avoids a warning.  There's no problem
+     as long as pointers have enough bits to hold small integers.  */
+  if ((int) (EMACS_INT) client_data != -1)
+    menu_item_selection = (Lisp_Object *) client_data;
+  BLOCK_INPUT;
+  lw_destroy_all_widgets (id);
+  UNBLOCK_INPUT;
+  popup_activated_flag = 0;
+}
 
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
   "button6", "button7", "button8", "button9", "button10" };
 
 static Lisp_Object
-xdialog_show (f, menubarp, keymaps, title, error)
+xdialog_show (f, keymaps, title, error)
      FRAME_PTR f;
-     int menubarp;
      int keymaps;
      Lisp_Object title;
      char **error;
 {
   int i, nb_buttons=0;
-  int dialog_id;
+  LWLIB_ID dialog_id;
   Widget menu;
-  XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
   char dialog_name[6];
 
-  /* This is the menu bar item (if any) that led to this menu.  */
-  widget_value *menubar_item = 0;
-
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
 
-  /* Define a queue to save up for later unreading
-     all X events that don't pertain to the menu.  */
-  struct event_queue
-    {
-      XEvent event;
-      struct event_queue *next;
-    };
-  
-  struct event_queue *queue = NULL;
-  struct event_queue *queue_tmp;
-
   /* Number of elements seen so far, before boundary.  */
   int left_count = 0;
   /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
@@ -1877,14 +2254,11 @@ xdialog_show (f, menubarp, keymaps, title, error)
   }
 
   /* Actually create the dialog.  */
-  dialog_id = ++popup_id_tick;
+  dialog_id = widget_id_tick++;
   menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
-                          f->display.x->widget, 1, 0,
+                          f->output_data.x->widget, 1, 0,
                           dialog_selection_callback, 0);
-#if 0 /* This causes crashes, and seems to be redundant -- rms.  */
-  lw_modify_all_widgets (dialog_id, first_wv, True);
-#endif
-  lw_modify_all_widgets (dialog_id, first_wv->contents->next, True);
+  lw_modify_all_widgets (dialog_id, first_wv->contents, True);
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
 
@@ -1893,43 +2267,12 @@ xdialog_show (f, menubarp, keymaps, title, error)
 
   /* Display the menu.  */
   lw_pop_up_all_widgets (dialog_id);
+  popup_activated_flag = 1;
 
   /* Process events that apply to the menu.  */
-  while (1)
-    {
-      XEvent event;
-
-      XtAppNextEvent (Xt_app_con, &event);
-      if (event.type == ButtonRelease)
-       {
-         XtDispatchEvent (&event);
-         break;
-       }
-      else if (event.type == Expose)
-       process_expose_from_menu (event);
-      XtDispatchEvent (&event);
-      if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
-       {
-         queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
-
-         if (queue_tmp != NULL) 
-           {
-             queue_tmp->event = event;
-             queue_tmp->next = queue;
-             queue = queue_tmp;
-           }
-       }
-    }
- pop_down:
+  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
 
-  /* Unread any events that we got but did not handle.  */
-  while (queue != NULL) 
-    {
-      queue_tmp = queue;
-      XPutBackEvent (XDISPLAY &queue_tmp->event);
-      queue = queue_tmp->next;
-      free ((char *)queue_tmp);
-    }
+  lw_destroy_all_widgets (dialog_id); 
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
@@ -1973,11 +2316,11 @@ xdialog_show (f, menubarp, keymaps, title, error)
 #else /* not USE_X_TOOLKIT */
 
 static Lisp_Object
-xmenu_show (f, x, y, menubarp, keymaps, title, error)
+xmenu_show (f, x, y, for_click, keymaps, title, error)
      FRAME_PTR f;
      int x, y;
+     int for_click;
      int keymaps;
-     int menubarp;
      Lisp_Object title;
      char **error;
 {
@@ -1997,35 +2340,41 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   if (menu_items_n_panes == 0)
     return Qnil;
 
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+    {
+      *error = "Empty menu";
+      return Qnil;
+    }
+
   /* Figure out which root window F is on.  */
-  XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
+  XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
                &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
                &dummy_uint, &dummy_uint);
 
   /* Make the menu on that window.  */
-  menu = XMenuCreate (XDISPLAY root, "emacs");
+  menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
   if (menu == NULL)
     {
       *error = "Can't create menu";
       return Qnil;
     }
 
+#ifdef HAVE_X_WINDOWS
   /* Adjust coordinates to relative to the outer (window manager) window.  */
-#ifdef HAVE_X11
   {
     Window child;
     int win_x = 0, win_y = 0;
 
     /* Find the position of the outside upper-left corner of
        the inner window, with respect to the outer window.  */
-    if (f->display.x->parent_desc != ROOT_WINDOW)
+    if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
       {
        BLOCK_INPUT;
-       XTranslateCoordinates (x_current_display,
+       XTranslateCoordinates (FRAME_X_DISPLAY (f),
 
                               /* From-window, to-window.  */
-                              f->display.x->window_desc,
-                              f->display.x->parent_desc,
+                              f->output_data.x->window_desc,
+                              f->output_data.x->parent_desc,
 
                               /* From-position, to-position.  */
                               0, 0, &win_x, &win_y,
@@ -2037,11 +2386,11 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
        y += win_y;
       }
   }
-#endif /* HAVE_X11 */
+#endif /* HAVE_X_WINDOWS */
 
   /* Adjust coordinates to be root-window-relative.  */
-  x += f->display.x->left_pos;
-  y += f->display.x->top_pos;
+  x += f->output_data.x->left_pos;
+  y += f->output_data.x->top_pos;
  
   /* Create all the necessary panes and their items.  */
   i = 0;
@@ -2060,10 +2409,10 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          if (keymaps && !NILP (prefix))
            pane_string++;
 
-         lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE);
+         lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
          if (lpane == XM_FAILURE)
            {
-             XMenuDestroy (XDISPLAY menu);
+             XMenuDestroy (FRAME_X_DISPLAY (f), menu);
              *error = "Can't create pane";
              return Qnil;
            }
@@ -2131,11 +2480,12 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          else
            item_data = XSTRING (item_name)->data;
 
-         if (XMenuAddSelection (XDISPLAY menu, lpane, 0, item_data,
+         if (XMenuAddSelection (FRAME_X_DISPLAY (f),
+                                menu, lpane, 0, item_data,
                                 !NILP (enable))
              == XM_FAILURE)
            {
-             XMenuDestroy (XDISPLAY menu);
+             XMenuDestroy (FRAME_X_DISPLAY (f), menu);
              *error = "Can't add selection to menu";
              return Qnil;
            }
@@ -2144,14 +2494,16 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
     }
 
   /* All set and ready to fly.  */
-  XMenuRecompute (XDISPLAY menu);
-  dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
-  dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
+  XMenuRecompute (FRAME_X_DISPLAY (f), menu);
+  dispwidth = DisplayWidth (FRAME_X_DISPLAY (f),
+                           XScreenNumberOfScreen (FRAME_X_SCREEN (f)));
+  dispheight = DisplayHeight (FRAME_X_DISPLAY (f),
+                             XScreenNumberOfScreen (FRAME_X_SCREEN (f)));
   x = min (x, dispwidth);
   y = min (y, dispheight);
   x = max (x, 1);
   y = max (y, 1);
-  XMenuLocate (XDISPLAY menu, 0, 0, x, y,
+  XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
               &ulx, &uly, &width, &height);
   if (ulx+width > dispwidth)
     {
@@ -2170,8 +2522,16 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   XMenuSetFreeze (menu, TRUE);
   pane = selidx = 0;
   
-  status = XMenuActivate (XDISPLAY menu, &pane, &selidx,
+  status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
                          x, y, ButtonReleaseMask, &datap);
+
+
+#ifdef HAVE_X_WINDOWS
+  /* Assume the mouse has moved out of the X window.
+     If it has actually moved in, we will get an EnterNotify.  */
+  x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
+#endif
+
   switch (status)
     {
     case XM_SUCCESS:
@@ -2215,16 +2575,25 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       break;
 
     case XM_FAILURE:
-      XMenuDestroy (XDISPLAY menu);
       *error = "Can't activate menu";
     case XM_IA_SELECT:
     case XM_NO_SELECT:
       entry = Qnil;
       break;
     }
-  XMenuDestroy (XDISPLAY menu);
+  XMenuDestroy (FRAME_X_DISPLAY (f), menu);
+
+#ifdef HAVE_X_WINDOWS
+  /* State that no mouse buttons are now held.
+     (The oldXMenu code doesn't track this info for us.)
+     That is not necessarily true, but the fiction leads to reasonable
+     results, and it is a pain to ask which are actually held now.  */
+  FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
+#endif
+
   return entry;
 }
+
 #endif /* not USE_X_TOOLKIT */
 \f
 syms_of_xmenu ()
@@ -2232,7 +2601,14 @@ syms_of_xmenu ()
   staticpro (&menu_items);
   menu_items = Qnil;
 
-  popup_id_tick = (1<<16);     
+  Qdebug_on_next_call = intern ("debug-on-next-call");
+  staticpro (&Qdebug_on_next_call);
+
+#ifdef USE_X_TOOLKIT
+  widget_id_tick = (1<<16);    
+  next_menubar_widget_id = 1;
+#endif
+
   defsubr (&Sx_popup_menu);
   defsubr (&Sx_popup_dialog);
 }