use dynwind_begin and dynwind_end
[bpt/emacs.git] / src / xmenu.c
index 823c63b..92f293b 100644 (file)
@@ -1,6 +1,6 @@
 /* X Communication module for terminals which understand the X protocol.
 
-Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2013 Free Software
+Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2014 Free Software
 Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -110,11 +110,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 static Lisp_Object Qdebug_on_next_call;
 
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
-static Lisp_Object xdialog_show (struct frame *, bool, Lisp_Object, Lisp_Object,
-                                 const char **);
-#endif
-\f
 /* 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;
@@ -144,196 +139,6 @@ menubar_id_to_frame (LWLIB_ID id)
 }
 
 #endif
-\f
-#ifdef HAVE_X_WINDOWS
-/* Return the mouse position in *X and *Y.  The coordinates are window
-   relative for the edit window in frame F.
-   This is for Fx_popup_menu.  The mouse_position_hook can not
-   be used for X, as it returns window relative coordinates
-   for the window where the mouse is in.  This could be the menu bar,
-   the scroll bar or the edit window.  Fx_popup_menu needs to be
-   sure it is the edit window.  */
-void
-mouse_position_for_popup (struct frame *f, int *x, int *y)
-{
-  Window root, dummy_window;
-  int dummy;
-
-  eassert (FRAME_X_P (f));
-
-  block_input ();
-
-  XQueryPointer (FRAME_X_DISPLAY (f),
-                 DefaultRootWindow (FRAME_X_DISPLAY (f)),
-
-                 /* The root window which contains the pointer.  */
-                 &root,
-
-                 /* Window pointer is on, not used  */
-                 &dummy_window,
-
-                 /* The position on that root window.  */
-                 x, y,
-
-                 /* x/y in dummy_window coordinates, not used.  */
-                 &dummy, &dummy,
-
-                 /* Modifier keys and pointer buttons, about which
-                    we don't care.  */
-                 (unsigned int *) &dummy);
-
-  unblock_input ();
-
-  /* xmenu_show expects window coordinates, not root window
-     coordinates.  Translate.  */
-  *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
-  *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
-}
-
-#endif /* HAVE_X_WINDOWS */
-
-#ifdef HAVE_MENUS
-
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
-       doc: /* Pop up a dialog box and return user's selection.
-POSITION specifies which frame to use.
-This is normally a mouse button event or a window or frame.
-If POSITION is t, it means to use the frame the mouse is on.
-The dialog box appears in the middle of the specified frame.
-
-CONTENTS specifies the alternatives to display in the dialog box.
-It is a list of the form (DIALOG ITEM1 ITEM2...).
-Each ITEM is a cons cell (STRING . VALUE).
-The return value is VALUE from the chosen item.
-
-An ITEM may also be just a string--that makes a nonselectable item.
-An ITEM may also be nil--that means to put all preceding items
-on the left of the dialog box and all following items on the right.
-\(By default, approximately half appear on each side.)
-
-If HEADER is non-nil, the frame title for the box is "Information",
-otherwise it is "Question".
-
-If the user gets rid of the dialog box without making a valid choice,
-for instance using the window manager, then this produces a quit and
-`x-popup-dialog' does not return.  */)
-  (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
-{
-  struct frame *f = NULL;
-  Lisp_Object window;
-
-  /* Decode the first argument: find the window or frame to use.  */
-  if (EQ (position, Qt)
-      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
-                              || EQ (XCAR (position), Qtool_bar))))
-    {
-#if 0 /* Using the frame the mouse is on may not be right.  */
-      /* Use the mouse's current position.  */
-      struct frame *new_f = SELECTED_FRAME ();
-      Lisp_Object bar_window;
-      enum scroll_bar_part part;
-      Time time;
-      Lisp_Object x, y;
-
-      (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
-
-      if (new_f != 0)
-       XSETFRAME (window, new_f);
-      else
-       window = selected_window;
-#endif
-      window = selected_window;
-    }
-  else if (CONSP (position))
-    {
-      Lisp_Object tem = XCAR (position);
-      if (CONSP (tem))
-       window = Fcar (XCDR (position));
-      else
-       {
-         tem = Fcar (XCDR (position));  /* EVENT_START (position) */
-         window = Fcar (tem);       /* POSN_WINDOW (tem) */
-       }
-    }
-  else if (WINDOWP (position) || FRAMEP (position))
-    window = position;
-  else
-    window = Qnil;
-
-  /* Decode where to put the menu.  */
-
-  if (FRAMEP (window))
-    f = XFRAME (window);
-  else if (WINDOWP (window))
-    {
-      CHECK_LIVE_WINDOW (window);
-      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
-    }
-  else
-    /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
-       but I don't want to make one now.  */
-    CHECK_WINDOW (window);
-
-  check_window_system (f);
-
-  /* Force a redisplay before showing the dialog.  If a frame is created
-     just before showing the dialog, its contents may not have been fully
-     drawn, as this depends on timing of events from the X server.  Redisplay
-     is not done when a dialog is shown.  If redisplay could be done in the
-     X event loop (i.e. the X event loop does not run in a signal handler)
-     this would not be needed.
-
-     Do this before creating the widget value that points to Lisp
-     string contents, because Fredisplay may GC and relocate them.  */
-  Fredisplay (Qt);
-
-#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
-  /* Display a menu with these alternatives
-     in the middle of frame F.  */
-  {
-    Lisp_Object x, y, frame, newpos;
-    XSETFRAME (frame, f);
-    XSETINT (x, FRAME_PIXEL_WIDTH (f) / 2);
-    XSETINT (y, FRAME_PIXEL_HEIGHT (f) / 2);
-    newpos = list2 (list2 (x, y), frame);
-
-    return Fx_popup_menu (newpos,
-                         list2 (Fcar (contents), contents));
-  }
-#else
-  {
-    Lisp_Object title;
-    const char *error_name;
-    Lisp_Object selection;
-    ptrdiff_t specpdl_count = SPECPDL_INDEX ();
-
-    /* Decode the dialog items from what was specified.  */
-    title = Fcar (contents);
-    CHECK_STRING (title);
-    record_unwind_protect_void (unuse_menu_items);
-
-    if (NILP (Fcar (Fcdr (contents))))
-      /* No buttons specified, add an "Ok" button so users can pop down
-         the dialog.  Also, the lesstif/motif version crashes if there are
-         no buttons.  */
-      contents = list2 (title, Fcons (build_string ("Ok"), Qt));
-
-    list_of_panes (list1 (contents));
-
-    /* Display them in a dialog box.  */
-    block_input ();
-    selection = xdialog_show (f, 0, title, header, &error_name);
-    unblock_input ();
-
-    unbind_to (specpdl_count, Qnil);
-    discard_menu_items ();
-
-    if (error_name) error ("%s", error_name);
-    return selection;
-  }
-#endif
-}
-
 
 #ifndef MSDOS
 
@@ -356,9 +161,6 @@ x_menu_set_in_use (int in_use)
 
 /* Wait for an X event to arrive or for a timer to expire.  */
 
-#ifndef USE_MOTIF
-static
-#endif
 void
 x_menu_wait_for_event (void *data)
 {
@@ -498,7 +300,7 @@ If FRAME is nil or not given, use the selected frame.  */)
       memset (&ev, 0, sizeof ev);
       ev.xbutton.display = FRAME_X_DISPLAY (f);
       ev.xbutton.window = XtWindow (menubar);
-      ev.xbutton.root = FRAME_X_DISPLAY_INFO (f)->root_window;
+      ev.xbutton.root = FRAME_DISPLAY_INFO (f)->root_window;
       ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f));
       ev.xbutton.button = Button1;
       ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2;
@@ -653,9 +455,7 @@ static void
 popup_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
 {
   popup_activated_flag = 1;
-#ifdef USE_X_TOOLKIT
   x_activate_timeout_atimer ();
-#endif
 }
 #endif
 
@@ -941,7 +741,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
 #ifdef USE_GTK
   /* If we have detached menus, we must update deep so detached menus
      also gets updated.  */
-  deep_p = deep_p || xg_have_tear_offs ();
+  deep_p = deep_p || xg_have_tear_offs (f);
 #endif
 
   if (deep_p)
@@ -950,7 +750,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
 
       struct buffer *prev = current_buffer;
       Lisp_Object buffer;
-      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+      dynwind_begin ();
       int previous_menu_items_used = f->menu_bar_items_used;
       Lisp_Object *previous_items
        = alloca (previous_menu_items_used * sizeof *previous_items);
@@ -1032,12 +832,8 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
       /* Convert menu_items into widget_value trees
         to display the menu.  This cannot evaluate Lisp code.  */
 
-      wv = xmalloc_widget_value ();
-      wv->name = "menubar";
-      wv->value = 0;
-      wv->enabled = 1;
+      wv = make_widget_value ("menubar", NULL, true, Qnil);
       wv->button_type = BUTTON_TYPE_NONE;
-      wv->help = Qnil;
       first_wv = wv;
 
       for (i = 0; submenu_start[i] >= 0; i++)
@@ -1071,7 +867,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
             the menus in any form, since it would be a no-op.  */
          free_menubar_widget_value_tree (first_wv);
          discard_menu_items ();
-         unbind_to (specpdl_count, Qnil);
+         dynwind_end ();
          return;
        }
 
@@ -1080,7 +876,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
       f->menu_bar_items_used = menu_items_used;
 
       /* This undoes save_menu_items.  */
-      unbind_to (specpdl_count, Qnil);
+      dynwind_end ();
 
       /* Now GC cannot happen during the lifetime of the widget_value,
         so it's safe to store data from a Lisp_String.  */
@@ -1102,12 +898,8 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
       /* Make a widget-value tree containing
         just the top level menu bar strings.  */
 
-      wv = xmalloc_widget_value ();
-      wv->name = "menubar";
-      wv->value = 0;
-      wv->enabled = 1;
+      wv = make_widget_value ("menubar", NULL, true, Qnil);
       wv->button_type = BUTTON_TYPE_NONE;
-      wv->help = Qnil;
       first_wv = wv;
 
       items = FRAME_MENU_BAR_ITEMS (f);
@@ -1119,12 +911,8 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
          if (NILP (string))
            break;
 
-         wv = xmalloc_widget_value ();
-         wv->name = SSDATA (string);
-         wv->value = 0;
-         wv->enabled = 1;
+         wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
          wv->button_type = BUTTON_TYPE_NONE;
-         wv->help = Qnil;
          /* This prevents lwlib from assuming this
             menu item is really supposed to be empty.  */
          /* The intptr_t cast avoids a warning.
@@ -1311,7 +1099,7 @@ free_frame_menubar (struct frame *f)
          if (x1 == 0 && y1 == 0)
            XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL);
 #endif
-          x_set_window_size (f, 0, FRAME_COLS (f), FRAME_LINES (f));
+         x_set_window_size (f, 0, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 1);
        }
       unblock_input ();
     }
@@ -1320,16 +1108,17 @@ free_frame_menubar (struct frame *f)
 
 #endif /* USE_X_TOOLKIT || USE_GTK */
 \f
-/* xmenu_show actually displays a menu using the panes and items in menu_items
+/* x_menu_show actually displays a menu using the panes and items in menu_items
    and returns the value selected from it.
-   There are two versions of xmenu_show, one for Xt and one for Xlib.
+   There are two versions of x_menu_show, one for Xt and one for Xlib.
    Both assume input is blocked by the caller.  */
 
 /* 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.
-   FOR_CLICK is true if this menu was invoked for a mouse click.
-   KEYMAPS is true if this menu was specified with keymaps;
+   Bitfield MENUFLAGS bits are:
+   MENU_FOR_CLICK is set if this menu was invoked for a mouse click.
+   MENU_KEYMAPS is set 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.
    TITLE is the specified menu title.
@@ -1367,7 +1156,7 @@ menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer
 {
   struct next_popup_x_y *data = user_data;
   GtkRequisition req;
-  struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (data->f);
+  struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (data->f);
   int disp_width = x_display_pixel_width (dpyinfo);
   int disp_height = x_display_pixel_height (dpyinfo);
 
@@ -1405,14 +1194,14 @@ pop_down_menu (void *arg)
    menu pops down.
    menu_item_selection will be set to the selection.  */
 static void
-create_and_show_popup_menu (struct frame *f, widget_value *first_wv, int x, int y,
-                           bool for_click, Time timestamp)
+create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
+                           int x, int y, bool for_click)
 {
   int i;
   GtkWidget *menu;
   GtkMenuPositionFunc pos_func = 0;  /* Pop up at pointer.  */
   struct next_popup_x_y popup_x_y;
-  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+  dynwind_begin ();
   bool use_pos_func = ! for_click;
 
 #ifdef HAVE_GTK3
@@ -1449,15 +1238,17 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, int x, int
   if (for_click)
     {
       for (i = 0; i < 5; i++)
-        if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+        if (FRAME_DISPLAY_INFO (f)->grabbed & (1 << i))
           break;
+      /* If keys aren't grabbed (i.e., a mouse up event), use 0.  */
+      if (i == 5) i = 0;
     }
 
   /* Display the menu.  */
   gtk_widget_show_all (menu);
 
   gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i,
-                 timestamp ? timestamp : gtk_get_current_event_time ());
+                 FRAME_DISPLAY_INFO (f)->last_user_time);
 
   record_unwind_protect_ptr (pop_down_menu, menu);
 
@@ -1470,11 +1261,11 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, int x, int
       popup_widget_loop (1, menu);
     }
 
-  unbind_to (specpdl_count, Qnil);
+  dynwind_end ();
 
   /* Must reset this manually because the button release event is not passed
      to Emacs event loop. */
-  FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
+  FRAME_DISPLAY_INFO (f)->grabbed = 0;
 }
 
 #else /* not USE_GTK */
@@ -1515,7 +1306,7 @@ pop_down_menu (Lisp_Object arg)
    menu_item_selection will be set to the selection.  */
 static void
 create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
-                           int x, int y, bool for_click, Time timestamp)
+                           int x, int y, bool for_click)
 {
   int i;
   Arg av[2];
@@ -1543,7 +1334,7 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
   event->send_event = 0;
   event->display = FRAME_X_DISPLAY (f);
   event->time = CurrentTime;
-  event->root = FRAME_X_DISPLAY_INFO (f)->root_window;
+  event->root = FRAME_DISPLAY_INFO (f)->root_window;
   event->window = event->subwindow = event->root;
   event->x = x;
   event->y = y;
@@ -1558,7 +1349,7 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
   event->state = 0;
   event->button = 0;
   for (i = 0; i < 5; i++)
-    if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+    if (FRAME_DISPLAY_INFO (f)->grabbed & (1 << i))
       event->button = i;
 
   /* Don't allow any geometry request from the user.  */
@@ -1572,15 +1363,15 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
 
   {
     int fact = 4 * sizeof (LWLIB_ID);
-    ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+    dynwind_begin ();
     record_unwind_protect (pop_down_menu,
                            Fcons (make_number (menu_id >> (fact)),
                                   make_number (menu_id & ~(-1 << (fact)))));
 
     /* Process events that apply to the menu.  */
-    popup_get_selection (0, FRAME_X_DISPLAY_INFO (f), menu_id, 1);
+    popup_get_selection (0, FRAME_DISPLAY_INFO (f), menu_id, 1);
 
-    unbind_to (specpdl_count, Qnil);
+    dynwind_end ();
   }
 }
 
@@ -1593,8 +1384,8 @@ cleanup_widget_value_tree (void *arg)
 }
 
 Lisp_Object
-xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
-           Lisp_Object title, const char **error_name, Time timestamp)
+x_menu_show (struct frame *f, int x, int y, int menuflags,
+            Lisp_Object title, const char **error_name)
 {
   int i;
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
@@ -1606,7 +1397,7 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
 
   int first_pane;
 
-  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+  dynwind_begin ();
 
   eassert (FRAME_X_P (f));
 
@@ -1615,17 +1406,16 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
   if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
     {
       *error_name = "Empty menu";
+      dynwind_end ();
       return Qnil;
     }
 
+  block_input ();
+
   /* Create a tree of widget_value objects
      representing the panes and their items.  */
-  wv = xmalloc_widget_value ();
-  wv->name = "menu";
-  wv->value = 0;
-  wv->enabled = 1;
+  wv = make_widget_value ("menu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
-  wv->help =Qnil;
   first_wv = wv;
   first_pane = 1;
 
@@ -1681,20 +1471,16 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
          /* If the pane has a meaningful name,
             make the pane a top-level menu item
             with its items as a submenu beneath it.  */
-         if (!keymaps && strcmp (pane_string, ""))
+         if (!(menuflags & MENU_KEYMAPS) && strcmp (pane_string, ""))
            {
-             wv = xmalloc_widget_value ();
+             wv = make_widget_value (pane_string, NULL, true, Qnil);
              if (save_wv)
                save_wv->next = wv;
              else
                first_wv->contents = wv;
-             wv->name = (char *) pane_string;
-             if (keymaps && !NILP (prefix))
+             if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
                wv->name++;
-             wv->value = 0;
-             wv->enabled = 1;
              wv->button_type = BUTTON_TYPE_NONE;
-             wv->help = Qnil;
              save_wv = wv;
              prev_wv = 0;
            }
@@ -1732,20 +1518,18 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
            }
 #endif /* not HAVE_MULTILINGUAL_MENU */
 
-         wv = xmalloc_widget_value ();
+         wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
+                                 STRINGP (help) ? help : Qnil);
          if (prev_wv)
            prev_wv->next = wv;
          else
            save_wv->contents = wv;
-         wv->name = SSDATA (item_name);
          if (!NILP (descrip))
            wv->key = SSDATA (descrip);
-         wv->value = 0;
          /* 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) ? aref_addr (menu_items, i) : 0;
-         wv->enabled = !NILP (enable);
 
          if (NILP (type))
            wv->button_type = BUTTON_TYPE_NONE;
@@ -1758,11 +1542,6 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
 
          wv->selected = !NILP (selected);
 
-          if (! STRINGP (help))
-           help = Qnil;
-
-         wv->help = help;
-
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -1772,27 +1551,20 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
   /* Deal with the title, if it is non-nil.  */
   if (!NILP (title))
     {
-      widget_value *wv_title = xmalloc_widget_value ();
-      widget_value *wv_sep1 = xmalloc_widget_value ();
-      widget_value *wv_sep2 = xmalloc_widget_value ();
+      widget_value *wv_title;
+      widget_value *wv_sep1 = make_widget_value ("--", NULL, false, Qnil);
+      widget_value *wv_sep2 = make_widget_value ("--", NULL, false, Qnil);
 
-      wv_sep2->name = "--";
       wv_sep2->next = first_wv->contents;
-      wv_sep2->help = Qnil;
-
-      wv_sep1->name = "--";
       wv_sep1->next = wv_sep2;
-      wv_sep1->help = Qnil;
 
 #ifndef HAVE_MULTILINGUAL_MENU
       if (STRING_MULTIBYTE (title))
        title = ENCODE_MENU_STRING (title);
 #endif
 
-      wv_title->name = SSDATA (title);
-      wv_title->enabled = TRUE;
+      wv_title = make_widget_value (SSDATA (title), NULL, true, Qnil);
       wv_title->button_type = BUTTON_TYPE_NONE;
-      wv_title->help = Qnil;
       wv_title->next = wv_sep1;
       first_wv->contents = wv_title;
     }
@@ -1805,9 +1577,10 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
   record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
 
   /* Actually create and show the menu until popped down.  */
-  create_and_show_popup_menu (f, first_wv, x, y, for_click, timestamp);
+  create_and_show_popup_menu (f, first_wv, x, y,
+                             menuflags & MENU_FOR_CLICK);
 
-  unbind_to (specpdl_count, Qnil);
+  dynwind_end ();
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
@@ -1846,7 +1619,7 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
                = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
              if (menu_item_selection == aref_addr (menu_items, i))
                {
-                 if (keymaps)
+                 if (menuflags & MENU_KEYMAPS)
                    {
                      int j;
 
@@ -1857,16 +1630,21 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
                        if (!NILP (subprefix_stack[j]))
                          entry = Fcons (subprefix_stack[j], entry);
                    }
+                 unblock_input ();
                  return entry;
                }
              i += MENU_ITEMS_ITEM_LENGTH;
            }
        }
     }
-  else if (!for_click)
-    /* Make "Cancel" equivalent to C-g.  */
-    Fsignal (Qquit, Qnil);
+  else if (!(menuflags & MENU_FOR_CLICK))
+    {
+      unblock_input ();
+      /* Make "Cancel" equivalent to C-g.  */
+      Fsignal (Qquit, Qnil);
+    }
 
+  unblock_input ();
   return Qnil;
 }
 \f
@@ -1899,7 +1677,7 @@ create_and_show_dialog (struct frame *f, widget_value *first_wv)
 
   if (menu)
     {
-      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+      dynwind_begin ();
       record_unwind_protect_ptr (pop_down_menu, menu);
 
       /* Display the menu.  */
@@ -1908,7 +1686,7 @@ create_and_show_dialog (struct frame *f, widget_value *first_wv)
       /* Process events that apply to the menu.  */
       popup_widget_loop (1, menu);
 
-      unbind_to (specpdl_count, Qnil);
+      dynwind_end ();
     }
 }
 
@@ -1954,7 +1732,7 @@ create_and_show_dialog (struct frame *f, widget_value *first_wv)
   /* Process events that apply to the dialog box.
      Also handle timers.  */
   {
-    ptrdiff_t count = SPECPDL_INDEX ();
+    dynwind_begin ();
     int fact = 4 * sizeof (LWLIB_ID);
 
     /* xdialog_show_unwind is responsible for popping the dialog box down.  */
@@ -1962,9 +1740,9 @@ create_and_show_dialog (struct frame *f, widget_value *first_wv)
                            Fcons (make_number (dialog_id >> (fact)),
                                   make_number (dialog_id & ~(-1 << (fact)))));
 
-    popup_get_selection (0, FRAME_X_DISPLAY_INFO (f), dialog_id, 1);
+    popup_get_selection (0, FRAME_DISPLAY_INFO (f), dialog_id, 1);
 
-    unbind_to (count, Qnil);
+    dynwind_end ();
   }
 }
 
@@ -1975,11 +1753,8 @@ static const char * button_names [] = {
   "button6", "button7", "button8", "button9", "button10" };
 
 static Lisp_Object
-xdialog_show (struct frame *f,
-              bool keymaps,
-              Lisp_Object title,
-              Lisp_Object header,
-              const char **error_name)
+x_dialog_show (struct frame *f, Lisp_Object title,
+              Lisp_Object header, const char **error_name)
 {
   int i, nb_buttons=0;
   char dialog_name[6];
@@ -1991,7 +1766,7 @@ xdialog_show (struct frame *f,
   /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
   int boundary_seen = 0;
 
-  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+  dynwind_begin ();
 
   eassert (FRAME_X_P (f));
 
@@ -2000,25 +1775,19 @@ xdialog_show (struct frame *f,
   if (menu_items_n_panes > 1)
     {
       *error_name = "Multiple panes in dialog box";
+      dynwind_end ();
       return Qnil;
     }
 
   /* Create a tree of widget_value objects
      representing the text label and buttons.  */
   {
-    Lisp_Object pane_name, prefix;
+    Lisp_Object pane_name;
     const char *pane_string;
     pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
-    prefix = AREF (menu_items, MENU_ITEMS_PANE_PREFIX);
     pane_string = (NILP (pane_name)
                   ? "" : SSDATA (pane_name));
-    prev_wv = xmalloc_widget_value ();
-    prev_wv->value = (char *) pane_string;
-    if (keymaps && !NILP (prefix))
-      prev_wv->name++;
-    prev_wv->enabled = 1;
-    prev_wv->name = "message";
-    prev_wv->help = Qnil;
+    prev_wv = make_widget_value ("message", (char *) pane_string, true, Qnil);
     first_wv = prev_wv;
 
     /* Loop over all panes and items, filling in the tree.  */
@@ -2037,6 +1806,7 @@ xdialog_show (struct frame *f,
          {
            free_menubar_widget_value_tree (first_wv);
            *error_name = "Submenu in dialog items";
+           dynwind_end ();
            return Qnil;
          }
        if (EQ (item_name, Qquote))
@@ -2051,18 +1821,17 @@ xdialog_show (struct frame *f,
          {
            free_menubar_widget_value_tree (first_wv);
            *error_name = "Too many dialog items";
+           dynwind_end ();
            return Qnil;
          }
 
-       wv = xmalloc_widget_value ();
+       wv = make_widget_value (button_names[nb_buttons],
+                               SSDATA (item_name),
+                               !NILP (enable), Qnil);
        prev_wv->next = wv;
-       wv->name = (char *) button_names[nb_buttons];
        if (!NILP (descrip))
          wv->key = SSDATA (descrip);
-       wv->value = SSDATA (item_name);
        wv->call_data = aref_addr (menu_items, i);
-       wv->enabled = !NILP (enable);
-       wv->help = Qnil;
        prev_wv = wv;
 
        if (! boundary_seen)
@@ -2077,9 +1846,7 @@ xdialog_show (struct frame *f,
     if (! boundary_seen)
       left_count = nb_buttons - nb_buttons / 2;
 
-    wv = xmalloc_widget_value ();
-    wv->name = dialog_name;
-    wv->help = Qnil;
+    wv = make_widget_value (dialog_name, NULL, false, Qnil);
 
     /*  Frame title: 'Q' = Question, 'I' = Information.
         Can also have 'E' = Error if, one day, we want
@@ -2112,26 +1879,19 @@ xdialog_show (struct frame *f,
   /* Actually create and show the dialog.  */
   create_and_show_dialog (f, first_wv);
 
-  unbind_to (specpdl_count, Qnil);
+  dynwind_end ();
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
   if (menu_item_selection != 0)
     {
-      Lisp_Object prefix;
-
-      prefix = Qnil;
       i = 0;
       while (i < menu_items_used)
        {
          Lisp_Object entry;
 
          if (EQ (AREF (menu_items, i), Qt))
-           {
-             prefix
-               = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
-             i += MENU_ITEMS_PANE_LENGTH;
-           }
+           i += MENU_ITEMS_PANE_LENGTH;
          else if (EQ (AREF (menu_items, i), Qquote))
            {
              /* This is the boundary between left-side elts and
@@ -2143,15 +1903,7 @@ xdialog_show (struct frame *f,
              entry
                = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
              if (menu_item_selection == aref_addr (menu_items, i))
-               {
-                 if (keymaps != 0)
-                   {
-                     entry = list1 (entry);
-                     if (!NILP (prefix))
-                       entry = Fcons (prefix, entry);
-                   }
-                 return entry;
-               }
+               return entry;
              i += MENU_ITEMS_ITEM_LENGTH;
            }
        }
@@ -2163,6 +1915,41 @@ xdialog_show (struct frame *f,
   return Qnil;
 }
 
+Lisp_Object
+xw_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
+{
+  Lisp_Object title;
+  const char *error_name;
+  Lisp_Object selection;
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  check_window_system (f);
+
+  /* Decode the dialog items from what was specified.  */
+  title = Fcar (contents);
+  CHECK_STRING (title);
+  record_unwind_protect_void (unuse_menu_items);
+
+  if (NILP (Fcar (Fcdr (contents))))
+    /* No buttons specified, add an "Ok" button so users can pop down
+       the dialog.  Also, the lesstif/motif version crashes if there are
+       no buttons.  */
+    contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+  list_of_panes (list1 (contents));
+
+  /* Display them in a dialog box.  */
+  block_input ();
+  selection = x_dialog_show (f, title, header, &error_name);
+  unblock_input ();
+
+  unbind_to (specpdl_count, Qnil);
+  discard_menu_items ();
+
+  if (error_name) error ("%s", error_name);
+  return selection;
+}
+
 #else /* not USE_X_TOOLKIT && not USE_GTK */
 
 /* The frame of the last activated non-toolkit menu bar.
@@ -2191,7 +1978,7 @@ menu_help_callback (char const *help_string, int pane, int item)
   if (EQ (first_item[0], Qt))
     pane_name = first_item[MENU_ITEMS_PANE_NAME];
   else if (EQ (first_item[0], Qquote))
-    /* This shouldn't happen, see xmenu_show.  */
+    /* This shouldn't happen, see x_menu_show.  */
     pane_name = empty_unibyte_string;
   else
     pane_name = first_item[MENU_ITEMS_ITEM_NAME];
@@ -2218,13 +2005,13 @@ pop_down_menu (Lisp_Object arg)
 #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));
+  x_mouse_leave (FRAME_DISPLAY_INFO (f));
 
   /* 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;
+  FRAME_DISPLAY_INFO (f)->grabbed = 0;
 
 #endif /* HAVE_X_WINDOWS */
 
@@ -2233,8 +2020,8 @@ pop_down_menu (Lisp_Object arg)
 
 
 Lisp_Object
-xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
-           Lisp_Object title, const char **error_name, Time timestamp)
+x_menu_show (struct frame *f, int x, int y, int menuflags,
+            Lisp_Object title, const char **error_name)
 {
   Window root;
   XMenu *menu;
@@ -2247,20 +2034,25 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
   int maxwidth;
   int dummy_int;
   unsigned int dummy_uint;
-  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+  dynwind_begin ();
 
   eassert (FRAME_X_P (f) || FRAME_MSDOS_P (f));
 
   *error_name = 0;
-  if (menu_items_n_panes == 0)
+  if (menu_items_n_panes == 0) {
+    dynwind_end ();
     return Qnil;
+  }
 
   if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
     {
       *error_name = "Empty menu";
+      dynwind_end ();
       return Qnil;
     }
 
+  block_input ();
+
   /* Figure out which root window F is on.  */
   XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
                &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
@@ -2271,14 +2063,11 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
   if (menu == NULL)
     {
       *error_name = "Can't create menu";
+      unblock_input ();
+      dynwind_end ();
       return Qnil;
     }
 
-  /* Don't GC while we prepare and show the menu,
-     because we give the oldxmenu library pointers to the
-     contents of strings.  */
-  inhibit_garbage_collection ();
-
 #ifdef HAVE_X_WINDOWS
   /* Adjust coordinates to relative to the outer (window manager) window.  */
   x += FRAME_OUTER_TO_INNER_DIFF_X (f);
@@ -2306,7 +2095,7 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
          pane_string = (NILP (pane_name)
                         ? "" : SSDATA (pane_name));
-         if (keymaps && !NILP (prefix))
+         if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
            pane_string++;
 
          lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
@@ -2314,6 +2103,8 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
            {
              XMenuDestroy (FRAME_X_DISPLAY (f), menu);
              *error_name = "Can't create pane";
+             unblock_input ();
+             dynwind_end ();
              return Qnil;
            }
          i += MENU_ITEMS_PANE_LENGTH;
@@ -2378,6 +2169,8 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
            {
              XMenuDestroy (FRAME_X_DISPLAY (f), menu);
              *error_name = "Can't add selection to menu";
+             unblock_input ();
+             dynwind_end ();
              return Qnil;
            }
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -2427,7 +2220,7 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
   if (ulx < 0) x -= ulx;
   if (uly < 0) y -= uly;
 
-  if (! for_click)
+  if (!(menuflags & MENU_FOR_CLICK))
     {
       /* If position was not given by a mouse click, adjust so upper left
          corner of the menu as a whole ends up at given coordinates.  This
@@ -2481,7 +2274,7 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
                    {
                      entry
                        = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
-                     if (keymaps)
+                     if (menuflags & MENU_KEYMAPS)
                        {
                          entry = list1 (entry);
                          if (!NILP (pane_prefix))
@@ -2503,20 +2296,22 @@ xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
     case XM_NO_SELECT:
       /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
         the menu was invoked with a mouse event as POSITION).  */
-      if (! for_click)
-        Fsignal (Qquit, Qnil);
+      if (!(menuflags & MENU_FOR_CLICK))
+       {
+         unblock_input ();
+         Fsignal (Qquit, Qnil);
+       }
       break;
     }
 
-  unbind_to (specpdl_count, Qnil);
+  unblock_input ();
+  dynwind_end ();
 
   return entry;
 }
 
 #endif /* not USE_X_TOOLKIT */
 
-#endif /* HAVE_MENUS */
-
 #ifndef MSDOS
 /* Detect if a dialog or menu has been posted.  MSDOS has its own
    implementation on msdos.c.  */
@@ -2534,16 +2329,14 @@ DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_
        doc: /* Return t if a menu or popup dialog is active.  */)
   (void)
 {
-#ifdef HAVE_MENUS
   return (popup_activated ()) ? Qt : Qnil;
-#else
-  return Qnil;
-#endif /* HAVE_MENUS */
 }
 \f
 void
 syms_of_xmenu (void)
 {
+#include "xmenu.x"
+
   DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
 
 #ifdef USE_X_TOOLKIT
@@ -2551,15 +2344,8 @@ syms_of_xmenu (void)
   next_menubar_widget_id = 1;
 #endif
 
-  defsubr (&Smenu_or_popup_active_p);
-
 #if defined (USE_GTK) || defined (USE_X_TOOLKIT)
-  defsubr (&Sx_menu_bar_open_internal);
   Ffset (intern_c_string ("accelerate-menu"),
         intern_c_string (Sx_menu_bar_open_internal.symbol_name));
 #endif
-
-#ifdef HAVE_MENUS
-  defsubr (&Sx_popup_dialog);
-#endif
 }