/* Functions for the X window system.
   Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation.

This file is part of XEmacs.

XEmacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

XEmacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with XEmacs; see the file COPYING.  If not, write to the Free
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Synched up with: Not synched with FSF. */

/* Substantially rewritten for XEmacs.  */

#include <config.h>
#include "lisp.h"

#include "device-x.h"
#include "frame-x.h"
#include "xintrinsicp.h"	/* CoreP.h needs this */
#include <X11/CoreP.h>		/* Numerous places access the fields of
				   a core widget directly.  We could
				   use XtVaGetValues(), but ... */
#include <X11/Shell.h>
#include <X11/ShellP.h>
#include "xmu.h"
#include "EmacsManager.h"
#include "EmacsFrameP.h"
#include "EmacsShell.h"
#ifdef EXTERNAL_WIDGET
#include "ExternalShell.h"
#endif
#include "glyphs-x.h"
#include "objects-x.h"

#include "buffer.h"
#include "events.h"
#include "extents.h"
#include "faces.h"
#include "scrollbar-x.h"
#include "window.h"

Lisp_Object Vx_gc_pointer_shape;
Lisp_Object Vx_scrollbar_pointer_shape;

/* Default parameters to use when creating frames.  */
Lisp_Object Vdefault_x_frame_alist;

static Lisp_Object Vpre_gc_cursor;

Lisp_Object Qwindow_id;
Lisp_Object Qpopup;
Lisp_Object Qx_resource_name;
Lisp_Object Qpointer;
Lisp_Object Qscrollbar_pointer;


/************************************************************************/
/*                          helper functions                            */
/************************************************************************/

/* Return the Emacs frame-object corresponding to an X window */
struct frame *
x_window_to_frame (struct device *d, Window wdesc)
{
  Lisp_Object tail, frame;
  struct frame *f;

  /* This function was previously written to accept only a window argument
     (and to loop over all devices looking for a matching window), but
     that is incorrect because window ID's are not unique across displays. */

  for (tail = DEVICE_FRAME_LIST (d); CONSP (tail); tail = XCDR (tail))
    {
      frame = XCAR (tail);
      if (!FRAMEP (frame))
	continue;
      f = XFRAME (frame);
      if (FRAME_IS_X (f) && XtWindow (FRAME_X_TEXT_WIDGET (f)) == wdesc)
	return f;
    }
  return 0;
}

/* Like x_window_to_frame but also compares the window with the widget's
   windows */
struct frame *
x_any_window_to_frame (struct device *d, Window wdesc)
{
  Lisp_Object tail, frame;
  struct frame *f;

  /* This function was previously written to accept only a window argument
     (and to loop over all devices looking for a matching window), but
     that is incorrect because window ID's are not unique across displays. */

  for (tail = DEVICE_FRAME_LIST (d); CONSP (tail); tail = XCDR (tail))
    {
      int i;

      frame = XCAR (tail);
      if (!FRAMEP (frame))
	continue;
      f = XFRAME (frame);
      if (!FRAME_IS_X (f))
	continue;
      /* This frame matches if the window is any of its widgets. */
      if (wdesc == XtWindow (FRAME_X_SHELL_WIDGET (f)) ||
	  wdesc == XtWindow (FRAME_X_CONTAINER_WIDGET (f)) ||
	  wdesc == XtWindow (FRAME_X_TEXT_WIDGET (f)))
	return f;

      /* Match if the window is one of the widgets at the top of the frame
	 (menubar, Energize psheets). */

      /* Note: Jamie once said

	 "Do *not* match if the window is this frame's psheet."

	 But this is wrong and will screw up some functions that expect
	 x_any_window_to_frame() to work as advertised.  I think the reason
	 for this statement is that, in the old (broken) event loop, where
	 not all events went through XtDispatchEvent(), psheet events
	 would incorrectly get sucked away by Emacs if this function matched
	 on psheet widgets. */

      for (i = 0; i < FRAME_X_NUM_TOP_WIDGETS (f); i++)
	{
	  Widget wid = FRAME_X_TOP_WIDGETS (f)[i];
	  if (wid && XtIsManaged (wid) && wdesc == XtWindow (wid))
	    return f;
	}

      /* Match if the window is one of this frame's scrollbars. */
      if (x_window_is_scrollbar (f, wdesc))
	return f;
    }

  return 0;
}

struct frame *
get_x_frame (Lisp_Object frame)
{
  if (NILP (frame))
    XSETFRAME (frame, selected_frame ());
  CHECK_LIVE_FRAME (frame, 0);
  /* this will also catch dead frames, but putting in the above check
     results in a more useful error */
  CHECK_X_FRAME (frame, 0);
  return XFRAME (frame);
}


/************************************************************************/
/*			window-manager interactions			*/
/************************************************************************/

#if 0
/* Not currently used. */

void
x_wm_mark_shell_size_user_specified (Widget wmshell)
{
  if (! XtIsWMShell (wmshell)) abort ();
  EmacsShellSetSizeUserSpecified (wmshell);
}

void
x_wm_mark_shell_position_user_specified (Widget wmshell)
{
  if (! XtIsWMShell (wmshell)) abort ();
  EmacsShellSetPositionUserSpecified (wmshell);
}

#endif

void
x_wm_set_shell_iconic_p (Widget shell, int iconic_p)
{
  if (! XtIsWMShell (shell)) abort ();

  /* Because of questionable logic in Shell.c, this sequence can't work:

       w = XtCreatePopupShell (...);
       XtVaSetValues (w, XtNiconic, True, 0);
       XtRealizeWidget (w);

     The iconic resource is only consulted at initialization time (when
     XtCreatePopupShell is called) instead of at realization time (just
     before the window gets created, which would be more sensible) or
     at management-time (just before the window gets mapped, which would
     be most sensible of all).

     The bug is that Shell's SetValues method doesn't do anything to
     w->wm.wm_hints.initial_state until after the widget has been realized.
     Calls to XtSetValues are ignored in the window between creation and
     realization.  This is true of MIT X11R5 patch level 25, at least.
     (Apparently some other versions of Xt don't have this bug?)
   */
  XtVaSetValues (shell, XtNiconic, iconic_p, 0);
  EmacsShellSmashIconicHint (shell, iconic_p);
}

void
x_wm_set_cell_size (Widget wmshell, int cw, int ch)
{
  if (!XtIsWMShell (wmshell))
    abort ();
  if (cw <= 0 || ch <= 0)
    abort ();

  XtVaSetValues (wmshell,
		 XtNwidthInc, cw, 
		 XtNheightInc, ch,
		 0);
}

void
x_wm_set_variable_size (Widget wmshell, int width, int height)
{
  if (!XtIsWMShell (wmshell))
    abort ();
#ifdef DEBUG_GEOMETRY_MANAGEMENT
  /* See comment in EmacsShell.c */
  printf ("x_wm_set_variable_size: %d %d\n", width, height);
  fflush (stdout);
#endif
  XtVaSetValues (wmshell,
		 XtNwidthCells, width,
		 XtNheightCells, height,
		 0);
}

/* If the WM_PROTOCOLS property does not already contain WM_TAKE_FOCUS
   and WM_DELETE_WINDOW, then add them.  (They may already be present
   because of the toolkit (Motif adds them, for example, but Xt doesn't).
 */
static void
x_wm_hack_wm_protocols (Widget widget)
{
  Display *dpy = XtDisplay (widget);
  struct device *d = get_device_from_display (dpy);
  Window w = XtWindow (widget);
  int need_delete = 1;
  int need_focus = 1;

  if (!XtIsWMShell (widget))
    abort ();

  {
    Atom type, *atoms = 0;
    int format = 0;
    unsigned long nitems = 0;
    unsigned long bytes_after;

    if (Success == XGetWindowProperty (dpy, w, DEVICE_XATOM_WM_PROTOCOLS (d),
				       0, 100, False, XA_ATOM,
				       &type, &format, &nitems, &bytes_after,
				       (unsigned char **) &atoms)
	&& format == 32 && type == XA_ATOM)
      while (nitems > 0)
	{
	  nitems--;
	  if (atoms [nitems] == DEVICE_XATOM_WM_DELETE_WINDOW (d))
	    need_delete = 0;
	  else if (atoms [nitems] == DEVICE_XATOM_WM_TAKE_FOCUS (d))
	    need_focus = 0;
	}
    if (atoms) XFree ((char *) atoms);
  }
  {
    Atom props [10];
    int count = 0;
    if (need_delete) props[count++] = DEVICE_XATOM_WM_DELETE_WINDOW (d);
    if (need_focus)  props[count++] = DEVICE_XATOM_WM_TAKE_FOCUS (d);
    if (count)
      XChangeProperty (dpy, w, DEVICE_XATOM_WM_PROTOCOLS (d), XA_ATOM, 32,
		       PropModeAppend, (unsigned char *) props, count);
  }
}

static void
x_wm_store_class_hints (Widget shell, char *frame_name)
{
  Display *dpy = XtDisplay (shell);
  char *app_name, *app_class;
  XClassHint classhint;

  if (!XtIsWMShell (shell))
    abort ();

  XtGetApplicationNameAndClass (dpy, &app_name, &app_class);
  classhint.res_name = frame_name;
  classhint.res_class = app_class;
  XSetClassHint (dpy, XtWindow (shell), &classhint);
}

static void
x_wm_maybe_store_wm_command (struct frame *f)
{
  Widget w = FRAME_X_SHELL_WIDGET (f);
  struct device *d = XDEVICE (FRAME_DEVICE (f));

  if (!XtIsWMShell (w))
    abort ();

  if (NILP (DEVICE_X_WM_COMMAND_FRAME (d)))
    {
      int argc;
      char **argv;
      make_argc_argv (Vcommand_line_args, &argc, &argv);
      XSetCommand (XtDisplay (w), XtWindow (w), argv, argc);
      free_argc_argv (argv);
      XSETFRAME (DEVICE_X_WM_COMMAND_FRAME (d), f);
    }
}

/* If we're deleting the frame on which the WM_COMMAND property has been
   set, then move that property to another frame so that there is exactly
   one frame that has that property set.
 */
static void
x_wm_maybe_move_wm_command (struct frame *f)
{
  struct device *d = XDEVICE (FRAME_DEVICE (f));

  /* At this point there should always be a frame in
     DEVICE_X_WM_COMMAND_FRAME() */
  if (f == XFRAME (DEVICE_X_WM_COMMAND_FRAME (d)))
    {
      Lisp_Object rest = DEVICE_FRAME_LIST (d);
      DEVICE_X_WM_COMMAND_FRAME (d) = Qnil;
      /* find some random other X frame that is not this one, or give up */
      /* skip non-top-level (ExternalClient) frames */
      while (!NILP (rest) &&
	     (f == XFRAME (XCAR (rest)) ||
	      !FRAME_X_TOP_LEVEL_FRAME_P (XFRAME (XCAR (rest)))))
	rest = XCDR (rest);
      if (NILP (rest))
	return;
      f = XFRAME (XCAR (rest));
      x_wm_maybe_store_wm_command (f);
    }
}

static int
x_frame_iconified_p (struct frame *f)
{
  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytesafter;
  unsigned long *datap = 0;
  Widget widget;
  int result = 0;
  struct device *d = XDEVICE (FRAME_DEVICE (f));

  widget = FRAME_X_SHELL_WIDGET (f);
  if (Success == XGetWindowProperty (XtDisplay (widget), XtWindow (widget),
				     DEVICE_XATOM_WM_STATE (d), 0, 2, False,
				     DEVICE_XATOM_WM_STATE (d), &actual_type,
				     &actual_format, &nitems, &bytesafter,
				     (unsigned char **) &datap)
      && datap)
    {
      if (nitems <= 2	/* "suggested" by ICCCM version 1 */
	  && datap[0] == IconicState)
	result = 1;
      XFree ((char *) datap);
    }
  return result;
}


/************************************************************************/
/*                          frame parameters                            */
/************************************************************************/

/* Connect the frame-parameter names (symbols) to the corresponding
   X Resource Manager names.  The name of a parameter, as a Lisp symbol,
   has an `x-resource-name' property which is a Lisp_String. */

static void
init_x_parm_symbols (void)
{
#define def(sym, rsrc) \
   pure_put (intern (sym), Qx_resource_name, build_string (rsrc))
#define defi(sym,rsrc) \
   def (sym, rsrc); pure_put (intern (sym), Qintegerp, Qt)

  def ("minibuffer", XtNminibuffer);
  def ("unsplittable", XtNunsplittable);
  defi("internal-border-width", XtNinternalBorderWidth);
  defi("scrollbar-width", XtNscrollBarWidth);
  def ("top-toolbar-shadow-color", XtNtopToolBarShadowColor);
  def ("bottom-toolbar-shadow-color", XtNbottomToolBarShadowColor);
  def ("background-toolbar-color", XtNbackgroundToolBarColor);
  def ("top-toolbar-shadow-pixmap", XtNtopToolBarShadowPixmap);
  def ("bottom-toolbar-shadow-pixmap", XtNbottomToolBarShadowPixmap);
  defi("toolbar-shadow-thickness", XtNtoolBarShadowThickness);
  def ("scrollbar-placement", XtNscrollBarPlacement);
  defi("inter-line-space", XtNinterline);
  /* font, foreground */
  def ("iconic", XtNiconic);
  def ("cursor-color", XtNcursorColor);
  def ("bar-cursor", XtNbarCursor);
  def ("visual-bell", XtNvisualBell);
  defi("bell-volume", XtNbellVolume);
  def ("pointer-background", XtNpointerBackground);
  def ("pointer-color", XtNpointerColor);
  def ("text-pointer", XtNtextPointer);
  def ("space-pointer", XtNspacePointer);
  def ("modeline-pointer", XtNmodeLinePointer);
  def ("gc-pointer", XtNgcPointer);
  /* geometry, initial-geometry */
  def ("menubar", XtNmenubar);
  def ("initially-unmapped", XtNinitiallyUnmapped);
  /* preferred-width, preferred-height */
  def ("use-backing-store", XtNuseBackingStore);

  /* inherited: */

  def ("border-color", XtNborderColor);
  defi("border-width", XtNborderWidth);
  defi("width", XtNwidth);
  defi("height", XtNheight);
  defi("left", XtNx);
  defi("top", XtNy);
  
#undef def
}

/* Insert a description of internally-recorded parameters of frame X
   into the parameter alist *ALISTPTR that is to be given to the user.
   Only parameters that are specific to the X window system
   and whose values are not correctly recorded in the frame's
   param_alist need to be considered here.  */

static void
color_to_string (Widget w, unsigned long pixel, char *buf)
{
  XColor color;
  color.pixel = pixel;
  XQueryColor (XtDisplay (w), w->core.colormap, &color);
  sprintf (buf, "#%04x%04x%04x", color.red, color.green, color.blue);
}

static void
x_get_top_level_position (Display *d, Window w, Position *x, Position *y)
{
  Window root, parent = w, *children;
  unsigned int nchildren;
  XWindowAttributes xwa;

  do
    {
      w = parent;
      if (!XQueryTree (d, w, &root, &parent, &children, &nchildren))
	{
	  *x = 0;
	  *y = 0;
	  return;
	}
      XFree (children);
    } while (root != parent);
  XGetWindowAttributes (d, w, &xwa);
  *x = xwa.x;
  *y = xwa.y;
}

static void
x_get_frame_params (struct frame *f, Lisp_Object *alistptr)
{
  Widget shell = FRAME_X_SHELL_WIDGET (f);
  EmacsFrame w = (EmacsFrame) FRAME_X_TEXT_WIDGET (f);
  char buf [255];

#define store_int(sym, slot) \
  store_in_alist (alistptr, sym, make_number (slot))
#define store_str(sym, slot) \
  store_in_alist (alistptr, sym, build_string (slot))
#define store_bool(sym, slot) \
  store_in_alist (alistptr, sym, (slot) ? Qt : Qnil)
#define store_color(sym, slot) \
  color_to_string ((Widget) w, slot, buf); \
  store_in_alist (alistptr, sym, build_string (buf))

  store_color ("cursor-color", w->emacs_frame.cursor_color);
  store_color ("border-color", w->core.border_pixel);
  /* Naturally those bastards who wrote Xt couldn't be bothered
     to learn about race conditions and such.  We can't trust
     the X and Y values to have any resemblance of correctness,
     so we smash the right values in place. */
  x_get_top_level_position (XtDisplay (shell), XtWindow (shell),
			    &shell->core.x, &shell->core.y);
  store_int ("left", shell->core.x);
  store_int ("top", shell->core.y);
  store_int ("border-width", w->core.border_width);
  store_int ("internal-border-width", w->emacs_frame.internal_border_width);
  store_int ("scrollbar-width", w->emacs_frame.scrollbar_width);
  store_color ("top-toolbar-shadow-color",
	       w->emacs_frame.top_toolbar_shadow_pixel);
  store_color ("bottom-toolbar-shadow-color",
	       w->emacs_frame.bottom_toolbar_shadow_pixel);
  store_color ("background-toolbar-color",
	       w->emacs_frame.background_toolbar_pixel);
  store_int ("toolbar-shadow-thickness",
	     w->emacs_frame.toolbar_shadow_thickness);
  store_int ("inter-line-space", w->emacs_frame.interline);
  store_bool ("minibuffer", w->emacs_frame.minibuffer);
  store_bool ("unsplittable", w->emacs_frame.unsplittable);
  sprintf (buf, "0x%x", (int) XtWindow (w));
  store_str ("window-id", buf);
}


/* Functions called only from `x_set_frame_params' to set
** individual parameters. */

static void 
x_set_title_from_char (struct frame *f, char* name)
{
  char *old_name = 0;
  Arg av [1];
  XtSetArg (av[0], XtNtitle, &old_name);
  XtGetValues (FRAME_X_SHELL_WIDGET (f), av, 1);
  if (!old_name || strcmp (name, old_name))
    {
      XtSetArg (av[0], XtNtitle, name);
      XtSetValues (FRAME_X_SHELL_WIDGET (f), av, 1);
    }
}

static void
x_set_icon_name_from_char (struct frame *f, char* name)
{
  char *old_name = 0;
  Arg av [1];
  XtSetArg (av[0], XtNiconName, &old_name);
  XtGetValues (FRAME_X_SHELL_WIDGET (f), av, 1);
  if (!old_name || strcmp (name, old_name))
    {
      XtSetArg (av[0], XtNiconName, name);
      XtSetValues (FRAME_X_SHELL_WIDGET (f), av, 1);
    }
}

/* Set the initial frame size as specified.  This function is used
   when the frame's widgets have not yet been realized.  In this
   case, it is not sufficient just to set the width and height of
   the EmacsFrame widget, because they will be ignored when the
   widget is realized (instead, the shell's geometry resource is
   used). */

static void
x_set_initial_frame_size (struct frame *f, int flags, int x, int y,
			   unsigned int w, unsigned int h)
{
  char shell_geom [255];
  int xval, yval;
  char xsign, ysign;
  char uspos = !!(flags & (XValue | YValue));
  char ussize = !!(flags & (WidthValue | HeightValue));
  char *temp;

  /* assign the correct size to the EmacsFrame widget ... */
  EmacsFrameSetCharSize (FRAME_X_TEXT_WIDGET (f), w, h);

  /* and also set the WMShell's geometry */
  (flags & XNegative) ? (xval = -x, xsign = '-') : (xval = x, xsign = '+');
  (flags & YNegative) ? (yval = -y, ysign = '-') : (yval = y, ysign = '+');
  
  if (uspos && ussize)
    sprintf (shell_geom, "=%dx%d%c%d%c%d", w, h, xsign, xval, ysign, yval);
  else if (uspos)
    sprintf (shell_geom, "=%c%d%c%d", xsign, xval, ysign, yval);
  else if (ussize)
    sprintf (shell_geom, "=%dx%d", w, h);
  
  if (uspos || ussize)
    {
      temp = xmalloc (1 + strlen (shell_geom));
      strcpy (temp, shell_geom);
      FRAME_X_GEOM_FREE_ME_PLEASE (f) = temp;
    }
  else
    temp = NULL;
  XtVaSetValues (FRAME_X_SHELL_WIDGET (f), XtNgeometry, temp, 0);
}

/* Report to X that a frame parameter of frame S is being set or changed.
   If the parameter is not specially recognized, do nothing.
 */

static void
x_set_frame_params_1 (struct frame *f, Lisp_Object alist, int initialized_p)
{
  int x = 0, y = 0;
  Dimension width = 0, height = 0;
  Bool width_specified_p = False;
  Bool height_specified_p = False;
  Bool x_position_specified_p = False;
  Bool y_position_specified_p = False;
  Bool internal_border_width_specified = False;
  Lisp_Object tail;
  Widget w = FRAME_X_TEXT_WIDGET (f);
  
  for (tail = alist; !EQ (tail, Qnil); tail = Fcdr (tail))
    {
      Lisp_Object elt = Fcar (tail);
      Lisp_Object prop = Fcar (elt);
      Lisp_Object val = Fcdr (elt);
      
      if (STRINGP (prop))
	{
	  if (string_length (XSTRING (prop)) == 0)
	    continue;

	  if (STRINGP (val))
	    XtVaSetValues (w, XtVaTypedArg, string_data (XSTRING (prop)),
			   XtRString, string_data (XSTRING (val)),
			   string_length (XSTRING (val)) + 1, 0);
	  else
	    XtVaSetValues (w, XtVaTypedArg,
			   string_data (XSTRING (prop)), XtRInt, XINT (val),
			   sizeof (int),
			   0);
	}
#if 0
      /* mly wants this, but it's not reasonable to change the name of a
	 frame after it has been created, because the old name was used
	 for resource lookup. */
      else if (EQ (elt, Qname))
        {
          CHECK_STRING (val, 0);
          f->name = val;
        }
#endif /* 0 */
      else
	{
	  Lisp_Object str = Fget (prop, Qx_resource_name, Qnil);
	  int int_p = !NILP (Fget (prop, Qintegerp, Qnil));

	  if (NILP (prop) || NILP (str))
	    {
	      /* #### No error-checking on property names!  FMH!!! */
	      /* RMS apparently thinks this is a feature.
		 This whole frame-parameters things has got to go. */

	      /* Kludge to handle the font property. */
	      if (EQ (prop, Qfont))
		{
		  /* If the value is not a string we silently ignore it. */
		  if (STRINGP (val))
		    {
		      Lisp_Object frm, font_spec;

		      XSETFRAME (frm, f);
		      font_spec = Fget (Fget_face (Qdefault), Qfont, Qnil);

		      Fadd_spec_to_specifier (font_spec, val, frm, Qnil, Qnil);
		      update_frame_face_values (f);
		    }

		  continue;
		}
	      else
		continue;
	    }
	  CHECK_STRING (str, 0);

	  /* Kludge the width/height so that we interpret them in characters
	     instead of pixels.  Yuck yuck yuck. */
	  if (!strcmp ((char *) string_data (XSTRING (str)), "width"))
	    {
	      CHECK_INT (val, 0);
	      width = XINT (val);
	      width_specified_p = True;
	      continue;
	    }
	  if (!strcmp ((char *) string_data (XSTRING (str)), "height"))
	    {
	      CHECK_INT (val, 0);
	      height = XINT (val);
	      height_specified_p = True;
	      continue;
	    }
	  /* Further kludge the x/y. */
	  if (!strcmp ((char *) string_data (XSTRING (str)), "x"))
	    {
	      CHECK_INT (val, 0);
	      x = XINT (val);
	      x_position_specified_p = True;
	      continue;
	    }
	  if (!strcmp ((char *) string_data (XSTRING (str)), "y"))
	    {
	      CHECK_INT (val, 0);
	      y = XINT (val);
	      y_position_specified_p = True;
	      continue;
	    }
	  /* Have you figured out by now that this entire function is
             one gigantic kludge? */
	  if (!strcmp ((char *) string_data (XSTRING (str)),
		       "internalBorderWidth"))
	    {
	      internal_border_width_specified = True;
	    }

	  if (int_p)
	    {
	      CHECK_INT (val, 0);
	      XtVaSetValues (w, (char *) string_data (XSTRING (str)),
			     XINT (val), 0);
	    }
	  else if (EQ (val, Qt))
	    XtVaSetValues (w, (char *) string_data (XSTRING (str)), /* XtN... */
			   True,
			   0);
	  else if (EQ (val, Qnil))
	    XtVaSetValues (w, (char *) string_data (XSTRING (str)), /* XtN... */
			   False,
			   0);
	  else
	    {
	      CHECK_STRING (val, 0);
	      XtVaSetValues (w, XtVaTypedArg,
			     (char *) string_data (XSTRING (str)), /* XtN... */
			     XtRString,
			     string_data (XSTRING (val)), string_length (XSTRING (val)) + 1,
			     0);
	    }

	  if (!strcmp ((char *) string_data (XSTRING (str)), "scrollBarWidth")
	      || !strcmp ((char *) string_data (XSTRING (str)),
			  "scrollBarHeight"))
	    {
	      x_update_frame_scrollbars (f);
	    }
	}
    }

  /* Kludge kludge kludge.   We need to deal with the size and position
   specially. */
  {
    int size_specified_p = width_specified_p || height_specified_p;
    int position_specified_p = x_position_specified_p ||
      y_position_specified_p;

    if (!width_specified_p)
      width = FRAME_WIDTH (f);
    if (!height_specified_p)
      height = FRAME_HEIGHT (f);
    
    /* Kludge kludge kludge kludge. */
    if (!x_position_specified_p)
      x = (int) (FRAME_X_SHELL_WIDGET (f)->core.x);
    if (!y_position_specified_p)
      y = (int) (FRAME_X_SHELL_WIDGET (f)->core.y);
      
    if (!initialized_p)
      {
	int flags = (size_specified_p ? WidthValue | HeightValue : 0) |
	  (position_specified_p ? XValue | YValue : 0) |
	  (x < 0 ? XNegative : 0) | (y < 0 ? YNegative : 0);
	if (size_specified_p
	    || position_specified_p
	    || internal_border_width_specified)
	  x_set_initial_frame_size (f, flags, x, y, width, height);
      }
    else
      {
	if (size_specified_p || internal_border_width_specified)
	  {
	    Lisp_Object frame;
	    XSETFRAME (frame, f);
	    Fset_frame_size (frame, make_number (width),
			      make_number (height), Qnil);
	}
	if (position_specified_p)
	  {
	    Lisp_Object frame;
	    XSETFRAME (frame, f);
	    Fset_frame_position (frame, make_number (x), make_number (y));
	  }
      }
  }
}

static void
x_set_frame_params (struct frame *f, Lisp_Object alist)
{
  x_set_frame_params_1 (f, alist, 1);
}

static int frame_title_format_already_set;

static void
maybe_set_frame_title_format (Widget shell)
{

  /* Only do this if this is the first X frame we're creating.
     
     If the *title resource (or -title option) was specified, then
     set frame-title-format to its value.
     */

  if (!frame_title_format_already_set)
    {
      /* No doubt there's a less stupid way to do this. */
      char *results [2];
      XtResource resources [2];
      results [0] = results [1] = 0;
      resources [0].resource_name = XtNtitle;
      resources [0].resource_class = XtCTitle;
      resources [0].resource_type = XtRString;
      resources [0].resource_size = sizeof (String);
      resources [0].resource_offset = 0;
      resources [0].default_type = XtRString;
      resources [0].default_addr = 0;
      resources [1].resource_name = XtNiconName;
      resources [1].resource_class = XtCIconName;
      resources [1].resource_type = XtRString;
      resources [1].resource_size = sizeof (String);
      resources [1].resource_offset = sizeof (char *);
      resources [1].default_type = XtRString;
      resources [1].default_addr = 0;
      XtGetSubresources (XtParent(shell), (XtPointer)results, shell->core.name,
			 shell->core.widget_class->core_class.class_name,
			 resources, XtNumber (resources), 0, 0);
      if (results[0])
	Vframe_title_format = build_string (results[0]);
      if (results[1])
	Vframe_icon_title_format = build_string (results[1]);
    }

  frame_title_format_already_set = 1;
}


/************************************************************************/
/*				widget creation				*/
/************************************************************************/

/* The widget hierarchy is

	argv[0]			shell		container	FRAME-NAME
	ApplicationShell	EmacsShell	EmacsManager	EmacsFrame

   We accept geometry specs in this order:

	*FRAME-NAME.geometry
	*EmacsFrame.geometry
	Emacs.geometry

   Other possibilities for widget hierarchies might be

	argv[0]			frame		container	FRAME-NAME
	ApplicationShell	EmacsShell	EmacsManager	EmacsFrame
   or
	argv[0]			FRAME-NAME	container	FRAME-NAME
	ApplicationShell	EmacsShell	EmacsManager	EmacsFrame
   or
	argv[0]			FRAME-NAME	container	emacsTextPane
	ApplicationShell	EmacsShell	EmacsManager	EmacsFrame

#ifdef EXTERNAL_WIDGET
   The ExternalShell widget is simply a replacement for the Shell widget 
   which is able to deal with using an externally-supplied window instead
   of always creating its own.
#endif

*/

#ifdef EXTERNAL_WIDGET

static int
is_valid_window (Window w, struct device *d)
{
  XWindowAttributes xwa;
  Display *dpy = DEVICE_X_DISPLAY (d);

  expect_x_error (dpy);
  XGetWindowAttributes (dpy, w, &xwa);
  return !x_error_occurred_p (dpy);
}

#endif /* EXTERNAL_WIDGET */

/* This sends a synthetic mouse-motion event to the frame, if the mouse
   is over the frame.  This ensures that the cursor gets set properly
   before the user moves the mouse for the first time. */

static void
x_send_synthetic_mouse_event (struct frame *f)
{
  /* #### write this function. */
}

static int
first_x_frame_p (struct frame *f)
{
  Lisp_Object rest = DEVICE_FRAME_LIST (XDEVICE (f->device));
  while (!NILP (rest) &&
	 (f == XFRAME (XCAR (rest)) ||
	  !FRAME_IS_X (XFRAME (XCAR (rest)))))
    rest = XCDR (rest);
  return (NILP (rest));
}

/* Figure out what size the EmacsFrame widget should initially be,
   and set it.  Should be called after the default font has been
   determined but before the widget has been realized. */

static void
x_initialize_frame_size (struct frame *f)
{
  /* Geometry of the AppShell */
  int app_flags = 0;
  int app_x = 0;
  int app_y = 0;
  unsigned int app_w = 0;
  unsigned int app_h = 0;
  
  /* Geometry of the EmacsFrame */
  int frame_flags = 0;
  int frame_x = 0;
  int frame_y = 0;
  unsigned int frame_w = 0;
  unsigned int frame_h = 0;
  
  /* Hairily merged geometry */
  int x = 0;
  int y = 0;
  unsigned int w = 80;
  unsigned int h = 40;
  int flags = 0;

  char *geom = 0, *ew_geom = 0;
  Boolean iconic_p = False, ew_iconic_p = False;

  Widget wmshell = FRAME_X_SHELL_WIDGET (f);
  /* #### This may not be an ApplicationShell any more, with the 'popup
     frame parameter. */
  Widget app_shell = XtParent (wmshell);
  Widget ew = FRAME_X_TEXT_WIDGET (f);

/* set the position of the frame's root window now.  When the
   frame was created, the position was initialized to (0,0). */
  {
    struct window *win = XWINDOW (f->root_window);

    WINDOW_LEFT (win) = FRAME_LEFT_BORDER_END (f);
    WINDOW_TOP (win) = FRAME_TOP_BORDER_END (f);

    if (!NILP (f->minibuffer_window))
      {
	win = XWINDOW (f->minibuffer_window);
	WINDOW_LEFT (win) = FRAME_LEFT_BORDER_END (f);
      }
  }

#ifdef EXTERNAL_WIDGET
  /* If we're an external widget, then the size of the frame is predetermined
     (by the client) and is not our decision to make. */
  if (FRAME_X_EXTERNAL_WINDOW_P (f))
    return;
#endif

#if 0
  /* #### this junk has not been tested; therefore it's
     probably wrong.  Doesn't really matter at this point because
     currently all frames are either top-level or external widgets. */

  /* If we're not our own top-level window, then we shouldn't go messing around
     with top-level shells or "Emacs.geometry" or any such stuff.  Therefore,
     we do as follows to determine the size of the frame:

     1) If a value for the frame's "geometry" resource was specified, then
        use it.  (This specifies a size in characters.)
     2) Else, if the "width" and "height" resources were specified, then
        leave them alone.  (This is a value in pixels.  Sorry, we can't break
	Xt conventions here.)
     3) Else, assume a size of 64x12.  (This is somewhat arbitrary, but
        it's unlikely that a size of 80x40 is desirable because we're probably
	inside of a dialog box.)

     Set the widget's x, y, height, and width as determined.  Don't set the
     top-level container widget, because we don't necessarily know what it
     is. (Assume it is smart and pays attention to our values.)
  */

  if (!FRAME_X_TOP_LEVEL_FRAME_P (f))
    {
      XtVaGetValues (ew, XtNgeometry, &ew_geom, 0);
      if (ew_geom)
	frame_flags = XParseGeometry (ew_geom, &frame_x, &frame_y,
				       &frame_w, &frame_h);
      if (! (frame_flags & (WidthValue | HeightValue)))
	{
	  XtVaGetValues (ew, XtNwidth, &frame_w,
			 XtNheight, &frame_h, 0);
	  if (!frame_w && !frame_h)
	    {
	      frame_w = 64;
	      frame_h = 12;
	      frame_flags |= WidthValue | HeightValue;
	    }
	}
      if (frame_flags & (WidthValue | HeightValue))
	EmacsFrameSetCharSize (ew, frame_w, frame_h);
      if (frame_flags & (XValue | YValue))
	{
	  XtVaGetValues (ew, XtNwidth, &frame_w,
			 XtNheight, &frame_h, 0);
	  if (frame_flags & XNegative)
	    frame_x += frame_w;
	  if (frame_flags & YNegative)
	    frame_y += frame_h;
	  XtVaSetValues (ew, XtNx, frame_x, XtNy, frame_y, 0);
	}
      return;
    }
#endif

  /* OK, we're a top-level shell. */

  if (!XtIsWMShell (wmshell))
    abort ();

  /* If the EmacsFrame doesn't have a geometry but the shell does,
     treat that as the geometry of the frame.  (Is this bogus?
     I'm not sure.) */

  XtVaGetValues (ew, XtNgeometry, &ew_geom, 0);
  if (!ew_geom)
    {
      XtVaGetValues (wmshell, XtNgeometry, &geom, 0);
      if (geom)
	{
	  ew_geom = geom;
	  XtVaSetValues (ew, XtNgeometry, ew_geom, 0);
	}
    }

  /* If the Shell is iconic, then the EmacsFrame is iconic.  (Is
     this bogus? I'm not sure.) */
  XtVaGetValues (ew, XtNiconic, &ew_iconic_p, 0);
  if (!ew_iconic_p)
    {
      XtVaGetValues (wmshell, XtNiconic, &iconic_p, 0);
      if (iconic_p)
	{
	  ew_iconic_p = iconic_p;
	  XtVaSetValues (ew, XtNiconic, iconic_p, 0);
	}
    }
  
  XtVaGetValues (app_shell, XtNgeometry, &geom, 0);
  if (geom)
    app_flags = XParseGeometry (geom, &app_x, &app_y, &app_w, &app_h);

  if (ew_geom)
    frame_flags = XParseGeometry (ew_geom, &frame_x, &frame_y,
				   &frame_w, &frame_h);
  
  if (first_x_frame_p (f))
    {
      /* If this is the first frame created:
         ====================================

         - Use the ApplicationShell's size/position, if specified.
           (This is "Emacs.geometry", or the "-geometry" command line arg.)
         - Else use the EmacsFrame's size/position.
           (This is "*FRAME-NAME.geometry")

	 - If the AppShell is iconic, the frame should be iconic.

	 AppShell comes first so that -geometry always applies to the first
	 frame created, even if there is an "every frame" entry in the
	 resource database.
       */
      if (app_flags & (XValue | YValue))
	{
	  x = app_x; y = app_y;
	  flags |= (app_flags & (XValue | YValue | XNegative | YNegative));
	}
      else if (frame_flags & (XValue | YValue))
	{
	  x = frame_x; y = frame_y;
	  flags |= (frame_flags & (XValue | YValue | XNegative | YNegative));
	}

      if (app_flags & (WidthValue | HeightValue))
	{
	  w = app_w; h = app_h;
	  flags |= (app_flags & (WidthValue | HeightValue));
	}
      else if (frame_flags & (WidthValue | HeightValue))
	{
	  w = frame_w; h = frame_h;
	  flags |= (frame_flags & (WidthValue | HeightValue));
	}

      /* If the AppShell is iconic, then the EmacsFrame is iconic. */
      if (!ew_iconic_p)
	{
	  XtVaGetValues (app_shell, XtNiconic, &iconic_p, 0);
	  if (iconic_p)
	    {
	      ew_iconic_p = iconic_p;
	      XtVaSetValues (ew, XtNiconic, iconic_p, 0);
	    }
	}
    }
  else
    {
      /* If this is not the first frame created:
         ========================================

         - use the EmacsFrame's size/position if specified
         - Otherwise, use the ApplicationShell's size, but not position.

         So that means that one can specify the position of the first frame
         with "Emacs.geometry" or `-geometry'; but can only specify the
	 position of subsequent frames with "*FRAME-NAME.geometry".

	 AppShell comes second so that -geometry does not apply to subsequent
	 frames when there is an "every frame" entry in the resource db,
	 but does apply to the first frame.
       */
      if (frame_flags & (XValue | YValue))
	{
	  x = frame_x; y = frame_y;
	  flags |= (frame_flags & (XValue | YValue | XNegative | YNegative));
	}

      if (frame_flags & (WidthValue | HeightValue))
	{
	  w = frame_w; h = frame_h;
	  flags |= (frame_flags & (WidthValue | HeightValue));
	}
      else if (app_flags & (WidthValue | HeightValue))
	{
	  w = app_w;
	  h = app_h;
	  flags |= (app_flags & (WidthValue | HeightValue));
	}
    }

  x_set_initial_frame_size (f, flags, x, y, w, h);
}

static void
x_get_layout_sizes (struct frame *f, Dimension *topbreadth)
{
  int i;

  /* compute height of all top-area widgets */
  for (i=0, *topbreadth = 0; i<FRAME_X_NUM_TOP_WIDGETS (f); i++)
    {
      Widget wid = FRAME_X_TOP_WIDGETS (f)[i];
      if (wid && XtIsManaged (wid))
	*topbreadth += wid->core.height + 2*wid->core.border_width;
    }
}

static void
x_layout_widgets (Widget w, XtPointer client_data, XtPointer call_data)
{
  struct frame *f = (struct frame *) client_data;
  EmacsManagerResizeStruct *emst = (EmacsManagerResizeStruct *) call_data;
  Dimension width = emst->width;
  Dimension height = emst->height;
  Widget text = FRAME_X_TEXT_WIDGET (f);
  Dimension textbord = text->core.border_width;
  Dimension topbreadth;
  Position text_x = 0, text_y = 0;
  int i;
  unsigned char scrollbar_placement;

  x_get_layout_sizes (f, &topbreadth);

  /* first the menubar and psheets ... */
  for (i=0; i<FRAME_X_NUM_TOP_WIDGETS (f); i++)
    {
      Widget wid = FRAME_X_TOP_WIDGETS (f)[i];
      if (wid && XtIsManaged (wid))
	{
	  Dimension bord = wid->core.border_width;
	  XtConfigureWidget (wid, 0, text_y,
			     width - 2*bord, wid->core.height,
			     bord);
	  text_y += wid->core.height + 2*bord;
	}
    }

  /* The scrollbar positioning is completely handled by redisplay.  We
     just need to know which sides they are supposed to go on. */
  XtVaGetValues (text, XtNscrollBarPlacement, &scrollbar_placement, 0);
  if (scrollbar_placement == XtTOP_LEFT ||
      scrollbar_placement == XtBOTTOM_LEFT)
    {
      f->scrollbar_on_left = 1;
    }
  else
    f->scrollbar_on_left = 0;

  if (scrollbar_placement == XtTOP_LEFT ||
      scrollbar_placement == XtTOP_RIGHT)
    {
      f->scrollbar_on_top = 1;
    }
  else
    f->scrollbar_on_top = 0;

  f->scrollbar_y_offset = topbreadth + textbord;

  /* finally the text area */
  XtConfigureWidget (text, text_x, text_y,
		     width - 2*textbord,
		     height - text_y - 2*textbord,
		     textbord);
}

static void
x_do_query_geometry (Widget w, XtPointer client_data, XtPointer call_data)
{
  struct frame *f = (struct frame *) client_data;
  EmacsManagerQueryGeometryStruct *emst =
    (EmacsManagerQueryGeometryStruct *) call_data;
  Widget text = FRAME_X_TEXT_WIDGET (f);
  Dimension textbord = text->core.border_width;
  Dimension topbreadth;
  XtWidgetGeometry req, repl;
  int mask = emst->request_mode & (CWWidth | CWHeight);

  x_get_layout_sizes (f, &topbreadth);

  /* strip away menubar from suggested size, and ask the text widget
     what size it wants to be */
  req.request_mode = mask;
  if (mask & CWWidth)
    req.width = emst->proposed_width - 2*textbord;
  if (mask & CWHeight)
    req.height = emst->proposed_height - topbreadth - 2*textbord;
  XtQueryGeometry (text, &req, &repl);

  /* Now add the menubar back again */
  emst->proposed_width = repl.width + 2*textbord;
  emst->proposed_height = repl.height + topbreadth + 2*textbord;
}

/* Creates the widgets for a frame.
   lisp_window_id is a Lisp description of an X window or Xt
   widget to parse.

   This function does not create or map the windows.  (That is
   done by x_popup_frame().)
 */
static void
x_create_widgets (struct frame *f, Lisp_Object lisp_window_id,
		  Lisp_Object parent)
{
  int menubar_visible;
  struct device *d = XDEVICE (f->device);
#ifdef EXTERNAL_WIDGET
  Window window_id = 0;
#endif
  char *name;
  Arg av [25];
  int ac = 0;
  Widget text, container, menubar, shell;
  Widget parentwid = 0;


  if (STRINGP (f->name))
     name = string_ext_data (XSTRING (f->name));
  else
    name = "emacs";
       
  /* The widget hierarchy is

	argv[0]			shell		pane		FRAME-NAME
	ApplicationShell	EmacsShell	EmacsManager	EmacsFrame

	(the type of the shell is ExternalShell if this frame is running
	in another client's window)

	However the EmacsShell widget has WM_CLASS of FRAME-NAME/Emacs.
	Normally such shells have name/class shellname/appclass, which in this
	case would be "shell/Emacs" instead of "frame-name/Emacs".  We could
	also get around this by naming the shell "frame-name", but that would
	be confusing because the text area (the EmacsFrame widget inferior of
	the shell) is also called that.  So we just set the WM_CLASS property.
   */

#ifndef EXTERNAL_WIDGET
  if (!NILP (lisp_window_id))
    error ("support for external widgets was not enabled at compile-time");
#else
  if (!NILP (lisp_window_id))
    {
      char *string;

      CHECK_STRING (lisp_window_id, 0);
      string = (char *) (string_data (XSTRING (lisp_window_id)));
      if (string[0] == '0' && (string[1] == 'x' || string[1] == 'X'))
	sscanf (string+2, "%lxu", &window_id);
#if 0
      else if (string[0] == 'w')
	{
	  sscanf (string+1, "%x", &parent_widget);
	  if (parent_widget)
	    window_id = XtWindow (parent_widget);
	}
#endif
      else
	sscanf (string, "%lu", &window_id);
      if (!is_valid_window (window_id, d))
	error ("Invalid window %d", window_id);
      FRAME_X_EXTERNAL_WINDOW_P (f) = 1;
    } else
#endif /* EXTERNAL_WIDGET */
      FRAME_X_TOP_LEVEL_FRAME_P (f) = 1;

  ac = 0;
  XtSetArg (av[ac], XtNallowShellResize, True); ac++;
#ifdef LWLIB_USES_MOTIF
  /* Motif sucks beans.  Without this in here, it will delete the window
     out from under us when it receives a WM_DESTROY_WINDOW message
     from the WM. */
  XtSetArg (av[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
#endif

#ifdef EXTERNAL_WIDGET
  if (window_id)
    {
      XtSetArg (av[ac], XtNwindow, window_id); ac++;
    }
  else
#endif
    {
      XtSetArg (av[ac], XtNinput, True); ac++;
      XtSetArg (av[ac], XtNminWidthCells, 10); ac++;
      XtSetArg (av[ac], XtNminHeightCells, 4); ac++;
    }

  if (!NILP (parent))
    {
      parentwid = FRAME_X_SHELL_WIDGET (XFRAME (parent));
      XtSetArg (av[ac], XtNtransientFor, parentwid); ac++;
    }

  shell = XtCreatePopupShell ("shell",
			      (
#ifdef EXTERNAL_WIDGET
			       window_id ? externalShellWidgetClass :
#endif
			       parentwid ? transientEmacsShellWidgetClass :
			       topLevelEmacsShellWidgetClass
			       ),
			      parentwid ? parentwid :
			      DEVICE_XT_APP_SHELL (d),
			      av, ac);
  FRAME_X_SHELL_WIDGET (f) = shell;
  maybe_set_frame_title_format (shell);

  /* Create the manager widget */
  container = XtVaCreateWidget ("container",
				emacsManagerWidgetClass,
				shell, 0);
  FRAME_X_CONTAINER_WIDGET (f) = container;
  XtAddCallback (container, XtNresizeCallback, x_layout_widgets,
		 (XtPointer) f);
  XtAddCallback (container, XtNqueryGeometryCallback, x_do_query_geometry,
		 (XtPointer) f);

  /* Create the text area */
  ac = 0;
  XtSetArg (av[ac], XtNborderWidth, 0); ac++;	/* should this be settable? */
  XtSetArg (av[ac], XtNemacsFrame, f); ac++;
  text = XtCreateWidget (name,
			 emacsFrameClass,
			 container, av, ac);
  FRAME_X_TEXT_WIDGET (f) = text;
  
  /* Create the initial menubar widget. */
  menubar_visible = initialize_frame_menubar (f);
  FRAME_X_TOP_WIDGETS (f)[0] = menubar = FRAME_X_MENUBAR_WIDGET (f);
  FRAME_X_NUM_TOP_WIDGETS (f) = 1;
  
  if (menubar_visible)
    XtManageChild (menubar);
  XtManageChild (text);
  XtManageChild (container);
}

/* We used to call XtPopup() in x_popup_frame, but that doesn't give
   you control over whether the widget is initially mapped or not
   because XtPopup() makes an unconditional call to XMapRaised().
   Boy, those Xt designers were clever.

   When we first removed it we only kept the XtRealizeWidget call in
   XtPopup.  For everything except HP's that was enough.  For HP's,
   though, the failure to call the popup callbacks resulted in XEmacs
   not accepting any input.  Bizarre but true.  Stupid but true.

   So, in case there are any other gotches floating out there along
   the same lines I've duplicated the majority of XtPopup here.  It
   assumes no grabs and that the widget is not already popped up, both
   valid assumptions for the one place this is called from. */
static void
xemacs_XtPopup (Widget widget)
{
  ShellWidget shell_widget = (ShellWidget) widget;
  XtGrabKind call_data = XtGrabNone;

  XtCallCallbacks(widget, XtNpopupCallback, (XtPointer)&call_data);

  shell_widget->shell.popped_up = TRUE;
  shell_widget->shell.grab_kind = XtGrabNone;
  shell_widget->shell.spring_loaded = False;

  if (shell_widget->shell.create_popup_child_proc != NULL)
    (*(shell_widget->shell.create_popup_child_proc))(widget);

  /* The XtVaSetValues below are not in XtPopup menu.  We just want to
     make absolutely sure... */
  XtVaSetValues (widget, XtNmappedWhenManaged, False, NULL);
  XtRealizeWidget (widget);
  XtVaSetValues (widget, XtNmappedWhenManaged, True, NULL);
}

/* create the windows for the specified frame and display them.
   Note that the widgets have already been created, and any
   necessary geometry calculations have already been done. */
static void
x_popup_frame (struct frame *f)
{
  Widget shell_widget = FRAME_X_SHELL_WIDGET (f);
  Widget frame_widget = FRAME_X_TEXT_WIDGET (f);
  struct device *d = XDEVICE (FRAME_DEVICE (f));

  /* Before mapping the window, make sure that the WMShell's notion of
     whether it should be iconified is synchronized with the EmacsFrame's
     notion.
     */
  if (FRAME_X_TOP_LEVEL_FRAME_P (f))
    x_wm_set_shell_iconic_p (shell_widget,
			     ((EmacsFrame) frame_widget)
			     ->emacs_frame.iconic);

  xemacs_XtPopup (shell_widget);

  if (!((EmacsFrame) frame_widget)->emacs_frame.initially_unmapped)
    XtMapWidget (shell_widget);
  else
    {
      /* We may have set f->visible to 1 in x_init_frame(), so undo
	 that now. */
      FRAME_X_TOTALLY_VISIBLE_P (f) = 0;
      f->visible = 0;
    }

#ifdef EXTERNAL_WIDGET
  if (FRAME_X_EXTERNAL_WINDOW_P (f))
    ExternalShellReady (shell_widget, XtWindow (frame_widget), KeyPressMask);
  else
#endif
    if (FRAME_X_TOP_LEVEL_FRAME_P (f))
      {
	/* tell the window manager about us. */
	x_wm_store_class_hints (shell_widget, XtName (frame_widget));
	x_wm_maybe_store_wm_command (f);
	x_wm_hack_wm_protocols (shell_widget);
      }

#ifdef I18N4
  /* #### not device-independent. */
  if (initial_input_context)
    {
      Window main_window = XtWindow (frame_widget);
      static int initial_ic_used_already = 0;
      /* kludge alert...I want to make sure that init_input() actually
	 completed its dirty work (event_mask setup) */
      
      if (!initial_ic_used_already)
	{
	  /* I probably ought to just waste this initial_input_context crap and
	     do all XIC setup here...  But then I'd have to mess around with
	     event_masks. see init_input() */ 
	  FRAME_X_INPUT_CONTEXT (f) = initial_input_context;
	  initial_ic_used_already = 1;
	}
      else
	{
	  FRAME_X_INPUT_CONTEXT (f) =
	    XCreateIC (input_method,
		       XNInputStyle, input_method_style,
		       NULL);
	}
      
      XSetICValues (FRAME_X_INPUT_CONTEXT (x),
		    XNClientWindow, main_window,
		    XNFocusWindow, main_window,
		    NULL);
    }
#endif /* I18N4 */

#ifdef HACK_EDITRES
  /* Allow XEmacs to respond to EditRes requests.  See the O'Reilly Xt */
  /* Instrinsics Programming Manual, Motif Edition, Aug 1993, Sect 14.14, */
  /* pp. 483-493. */
  XtAddEventHandler (shell_widget,               /* the widget in question */
		     (EventMask) 0,   /* OR this mask with existing mask */
		     True,	          /* called on non-maskable events? */
		     _XEditResCheckMessages,                /* the handler */
		     NULL );
#endif

  /* Do a stupid property change to force the server to generate a
     propertyNotify event so that the event_stream server timestamp will
     be initialized to something relevant to the time we created the window.
     */
  XChangeProperty (XtDisplay (frame_widget), XtWindow (frame_widget),
		   DEVICE_XATOM_WM_PROTOCOLS (d), XA_ATOM, 32, PropModeAppend,
		   (unsigned char*) NULL, 0);
  
  x_send_synthetic_mouse_event (f);
}

static void
allocate_x_frame_struct (struct frame *f)
{
  /* zero out all slots. */
  f->frame_data = malloc_type_and_zero (struct x_frame);

  /* yeah, except the lisp ones */
  FRAME_X_ICON_PIXMAP (f) = Qnil;
  FRAME_X_ICON_PIXMAP_MASK (f) = Qnil;
#ifdef ENERGIZE
  FRAME_X_CURRENT_PSHEET_BUFFER (f) = Qnil;
  FRAME_X_DESIRED_PSHEET_BUFFER (f) = Qnil;
#endif
}


/************************************************************************/
/*				Lisp functions				*/
/************************************************************************/

static void
x_init_frame (struct frame *f, Lisp_Object parms)
{
  /* This function can GC */
  Lisp_Object device = FRAME_DEVICE (f);
  struct device *d = XDEVICE (device);
  Lisp_Object lisp_window_id;
  Lisp_Object popup;

  lisp_window_id = Fassq (Qwindow_id, parms);
  if (!NILP (lisp_window_id))
    lisp_window_id = Fcdr (lisp_window_id);
  popup = Fassq (Qpopup, parms);
  if (!NILP (popup))
    {
      popup = XCDR (popup);
      if (EQ (popup, Qt))
	popup = Fselected_frame (device);
      CHECK_LIVE_FRAME (popup, 0);
      if (!EQ (device, FRAME_DEVICE (XFRAME (popup))))
	signal_simple_error_2 ("Parent must be on same device as frame",
			       device, popup);
    }

  if (NILP (DEVICE_SELECTED_FRAME (d)))
    {
      /* This means that this is the first frame on the device.
	 So short-ciruit the delay in processing the initial MapNotify
	 event so that output on the first frame shows up right
	 away... */
      f->visible = 1;
    }

  allocate_x_frame_struct (f);
  x_create_widgets (f, lisp_window_id, popup);
}

static void
x_finish_init_frame (struct frame *f, Lisp_Object parms)
{
  /* Set up the values of the widget/frame.  A case could be made for putting
     this inside of the widget's initialize method. */

  update_frame_face_values (f);
  x_initialize_frame_size (f);
  update_frame_title (f);
  x_set_frame_params_1 (f, parms, 0);

  /* Pop up the frame. */

  x_popup_frame (f);
}

static void
x_mark_frame (struct frame *f, void (*markobj) (Lisp_Object))
{
  ((markobj) (FRAME_X_ICON_PIXMAP (f)));
  ((markobj) (FRAME_X_ICON_PIXMAP_MASK (f)));
#ifdef ENERGIZE
  ((markobj) (FRAME_X_CURRENT_PSHEET_BUFFER (f)));
  ((markobj) (FRAME_X_DESIRED_PSHEET_BUFFER (f)));
#endif
}

/* #### We need to update documentation once fully converted to glyph
   system. */
DEFUN ("x-set-frame-icon-pixmap", Fx_set_frame_icon_pixmap,
       Sx_set_frame_icon_pixmap, 2, 3, 0,
  "Set the icon of the given frame to the given image instance,\n\
which should be an image instance object (as returned by\n\
`make-image-instance'), a glyph object (as returned by `make-glyph'),\n\
or nil.  If a glyph object is given, the glyph will be instantiated on\n\
the frame to produce an image instance object.\n\
\n\
If the given image instance has a mask, that will be used as the icon mask;\n\
however, not all window managers support this.\n\
\n\
The window manager is also not required to support color pixmaps,\n\
only bitmaps (one plane deep).\n\
\n\
If the image instance does not have a mask, then the optional\n\
third argument may be the image instance to use as the mask (it must be\n\
one plane deep).")
  (frame, image_instance, mask)
    Lisp_Object frame, image_instance, mask;
{
  struct frame *f = get_x_frame (frame);
  Pixmap x_pixmap, x_mask;

  XSETFRAME (frame, f);

  if (!NILP (image_instance))
    {
      if (GLYPHP (image_instance))
	image_instance =
	  glyph_image_instance (image_instance, frame, 0);
      CHECK_IMAGE_INSTANCE (image_instance, 0);
    }
  if (!NILP (mask))
    {
      if (GLYPHP (mask))
	mask = glyph_image_instance (mask, frame, 0);
      CHECK_IMAGE_INSTANCE (mask, 0);
    }

  if (IMAGE_INSTANCEP (image_instance)
      && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (image_instance)))
    {
      x_pixmap = XIMAGE_INSTANCE_X_PIXMAP (image_instance);
      x_mask = XIMAGE_INSTANCE_PIXMAP_DEPTH (image_instance);
      if (x_mask) mask = Qnil;
    }
  else
    {
      x_pixmap = 0;
      x_mask = 0;
      mask = Qnil;
    }

  if (IMAGE_INSTANCEP (mask)
      && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (mask)))
    {
      if (XIMAGE_INSTANCE_PIXMAP_DEPTH (mask) > 1)
	signal_simple_error ("mask must be one plane", mask);
      x_mask = XIMAGE_INSTANCE_X_PIXMAP (mask);
    }

  /* GC-protect the lisp objects (and underlying X data) */
  FRAME_X_ICON_PIXMAP (f) = image_instance;
  FRAME_X_ICON_PIXMAP_MASK (f) = mask;

  /* Store the X data into the widget. */
  {
    Arg av [10];
    int ac = 0;
    XtSetArg (av [ac], XtNiconPixmap, x_pixmap); ac++;
    XtSetArg (av [ac], XtNiconMask, x_mask); ac++;
    XtSetValues (FRAME_X_SHELL_WIDGET (f), av, ac);
  }

  return image_instance;
}

DEFUN ("x-set-frame-pointer", Fx_set_frame_pointer, Sx_set_frame_pointer,
       2, 2, 0,
       "Set the mouse cursor of FRAME to the given cursor,\n\
which should be an object returned by `make-cursor'.")
     (frame, cursor)
     Lisp_Object frame, cursor;
{
  /* #### seriously broken. */
  struct frame *f = get_x_frame (frame);
  CHECK_CURSOR (cursor, 0);
  if (! EQ (f->pointer, cursor))
    {
      XDefineCursor (XtDisplay (FRAME_X_TEXT_WIDGET (f)),
   		     XtWindow (FRAME_X_TEXT_WIDGET (f)),
		     XCURSOR (cursor)->cursor);
      XSync (XtDisplay (FRAME_X_TEXT_WIDGET (f)), 0);
      /* #### If the user cuts this pointer, we'll get X errors.
         This needs to be rethunk. */
      /* change_cursor_for_gc() accesses this */
      f->pointer = cursor;
    }
  return Qnil;
}

DEFUN ("x-set-scrollbar-pointer", Fx_set_scrollbar_pointer,
       Sx_set_scrollbar_pointer, 2, 2, 0,
       "Set the mouse cursor of the scrollbars on FRAME to the given\n\
cursor, which should be an object returned by `make-cursor'.")
     (frame, cursor)
     Lisp_Object frame, cursor;
{
  /* #### seriously broken. */
  struct frame *f = get_x_frame (frame);
  CHECK_CURSOR (cursor, 0);
  x_set_scrollbar_pointer (f, cursor);
  return Qnil;
}

/* GC calls x_show_gc_cursor() with a cursor object to turn on the GC cursor,
   and with nil to turn it off.
*/

int
x_show_gc_cursor (struct frame *f, Lisp_Object cursor)
{
  int speccount = specpdl_depth ();
  Lisp_Object frame;
  int changed = 0;

  specbind (Qinhibit_quit, Qt);		/* some losers in here call Fassq() */

  XSETFRAME (frame, f);
  if (NILP (cursor))
    {
      if (!NILP (Vpre_gc_cursor))
	{
	  if (!CURSORP (Vpre_gc_cursor)) abort ();
	  /* We know it's a frame, we know it's a pointer, so
	     x-set-frame-pointer won't error. */
	  Fx_set_frame_pointer (frame, Vpre_gc_cursor);
	  changed = 1;
	}
    }
  else if (CURSORP (cursor))
    {
      Vpre_gc_cursor = f->pointer;

      if (CURSORP (Vpre_gc_cursor))
	/* if we don't know what cursor is there, don't change to the GC
	   cursor, because we'd have no way of changing back... */
	{
	  /* We know it's a frame, we know it's a pointer, so
	     x-set-frame-pointer won't error. */
	  Fx_set_frame_pointer (frame, cursor);
	  changed = 1;
	}
    }

  unbind_to (speccount, Qnil);
  return changed;
}

DEFUN ("x-window-id", Fx_window_id, Sx_window_id, 0, 1, 0,
       "Get the ID of the X11 window.\n\
This gives us a chance to manipulate the Emacs window from within a\n\
different program.  Since the ID is an unsigned long, we return it as\n\
a string.")
  (frame)
  Lisp_Object frame;
{
  char str[255];
  struct frame *f = get_x_frame (frame);

  sprintf (str, "%lu", XtWindow (FRAME_X_TEXT_WIDGET (f)));
  return build_string (str);
}


/************************************************************************/
/*			manipulating the X window			*/
/************************************************************************/

static void
x_set_frame_position (struct frame *f, int xoff, int yoff)
{
  Widget w = FRAME_X_SHELL_WIDGET (f);
  Display *dpy = XtDisplay (w);
  Dimension frame_w = DisplayWidth (dpy, DefaultScreen (dpy));
  Dimension frame_h = DisplayHeight (dpy, DefaultScreen (dpy));
  Dimension shell_w, shell_h, shell_bord;
  int win_gravity;

  XtVaGetValues (w,
		 XtNwidth, &shell_w,
		 XtNheight, &shell_h,
		 XtNborderWidth, &shell_bord,
		 0);

  win_gravity =
    xoff >= 0 && yoff >= 0 ? NorthWestGravity :
    xoff >= 0 ? SouthWestGravity :
    yoff >= 0 ? NorthEastGravity :
    SouthEastGravity;
  if (xoff < 0)
    xoff += frame_w - shell_w - 2*shell_bord;
  if (yoff < 0)
    yoff += frame_h - shell_h - 2*shell_bord;

  /* Update the hints so that, if this window is currently iconified, it will
     come back at the right place.  We can't look at s->visible to determine
     whether it is iconified because it might not be up-to-date yet (the queue
     might not be processed). */
  XtVaSetValues (w,
		 XtNwinGravity, win_gravity,
		 XtNx, xoff,
		 XtNy, yoff,
		 0);
  /* Sometimes you will find that

     (set-frame-position (selected-frame) -50 -50)

     doesn't put the frame where you expect it to:
     i.e. it's closer to the lower-right corner than
)     it should be, and it appears that the size of
     the WM decorations was not taken into account.
     This is *not* a problem with this function.
     Both mwm and twm have bugs in handling this
     situation. (mwm ignores the window gravity
     and always assumes NorthWest, except the first
     time you map the window; twm gets things almost
     right, but forgets to account for the border
     width of the top-level window.) This function
     does what it's supposed to according to the ICCCM,
     and I'm not about to hack around window-manager
     bugs. */

#if 0
  /* This is not necessary under either mwm or twm */
  x_wm_mark_shell_position_user_specified (w);
#endif
}

/* Call this to change the size of frame S's x-window. */

static void
x_set_frame_size (struct frame *f, int cols, int rows)
{
  EmacsFrameSetCharSize (FRAME_X_TEXT_WIDGET (f), cols, rows);
#if 0
    /* this is not correct.  x_set_frame_size() is called from
       Fset_frame_size(), which may or may not have been called
       by the user (e.g. update_EmacsFrame() calls it when the font
       changes).  For now, don't bother with getting this right. */
  x_wm_mark_shell_size_user_specified (FRAME_X_SHELL_WIDGET (f));
#endif
}

static void
x_set_mouse_position (struct window *w, int x, int y)
{
  struct frame *f = XFRAME (w->frame);

  Display *display = DEVICE_X_DISPLAY (XDEVICE (f->device));
  XWarpPointer (display, None, XtWindow (FRAME_X_TEXT_WIDGET (f)),
                0, 0, 0, 0, w->pixel_left + x, w->pixel_top + y);
}

static int
x_get_mouse_position (struct device *d, struct window **w, int *x, int *y)
{
  Display *display = DEVICE_X_DISPLAY (d);
  Window child_window;
  Window root_window;
  Window win;
  int root_x, root_y;
  int win_x, win_y;
  unsigned int keys_and_buttons;
  struct frame *f;

  if (XQueryPointer (display, RootWindow (display, DefaultScreen (display)),
		     &root_window, &child_window, &root_x, &root_y,
		     &win_x, &win_y, &keys_and_buttons) == False)
    return 0;

  if (child_window == None)
    return 0;	/* not over any window. */

  while (1)
    {
      win = child_window;
      if (XTranslateCoordinates (display, root_window, win, root_x, root_y,
				 &win_x, &win_y, &child_window) == False)
	/* Huh? */
	return 0;

      if (child_window == None)
	break;
    }

  /* At this point, win is the innermost window containing the pointer
     and win_x and win_y are the coordinates of that window. */
  f = x_any_window_to_frame (d, win);
  if (!f)
    return 0;

  if (XTranslateCoordinates (display, win,
			     XtWindow (FRAME_X_TEXT_WIDGET (f)),
			     win_x, win_y, x, y, &child_window) == False)
    /* Huh? */
    return 0;

  *w = find_window_by_pixel_pos (*x, *y, f->root_window);
  if (!*w)
    return 0;

  /* Adjust the position to be relative to the window. */
  *x -= (*w)->pixel_left;
  *y -= (*w)->pixel_top;

  return 1;
}

static void
x_cant_notify_wm_error (void)
{
  error ("Can't notify window manager of iconification.");
}

/* Raise frame F.  */
static void
x_raise_frame_1 (struct frame *f, int force)
{
  Widget bottom_dialog;
  Window emacs_window;
  XWindowChanges xwc;
  unsigned int flags;
  Display *display = DEVICE_X_DISPLAY (XDEVICE (f->device));

  if (f->visible || force)
    {
      emacs_window = XtWindow (FRAME_X_SHELL_WIDGET (f));
      /* first raises all the dialog boxes, then put emacs just below the 
       * bottom most dialog box */
      bottom_dialog = lw_raise_all_pop_up_widgets ();
      if (bottom_dialog && XtWindow (bottom_dialog))
	{
	  xwc.sibling = XtWindow (bottom_dialog);
	  xwc.stack_mode = Below;
	  flags = CWSibling | CWStackMode;
	}
      else
	{
	  xwc.stack_mode = Above;
	  flags = CWStackMode;
	}

      if (!XReconfigureWMWindow (display, emacs_window,
				 DefaultScreen (display),
				 flags, &xwc))
	x_cant_notify_wm_error ();
    }
}

static void
x_raise_frame (struct frame *f)
{
  x_raise_frame_1 (f, 1);
}

/* Lower frame F.  */
static void
x_lower_frame (struct frame *f)
{
  Display *display = DEVICE_X_DISPLAY (XDEVICE (f->device));
  XWindowChanges xwc;
  unsigned int flags;
  
  if (f->visible)
    {
      xwc.stack_mode = Below;
      flags = CWStackMode;
      if (!XReconfigureWMWindow (display, XtWindow (FRAME_X_SHELL_WIDGET (f)),
				 DefaultScreen (display), flags, &xwc))
	x_cant_notify_wm_error ();
    }
}

/* Change from withdrawn state to mapped state. */
static void
x_make_frame_visible (struct frame *f)
{
  Display *display = DEVICE_X_DISPLAY (XDEVICE (f->device));

  if (!f->visible)
    XMapRaised (display, XtWindow (FRAME_X_SHELL_WIDGET (f)));
  else
    x_raise_frame_1 (f, 0);
}

/* Change from mapped state to withdrawn state. */
static void
x_make_frame_invisible (struct frame *f)
{
  Display *display = DEVICE_X_DISPLAY (XDEVICE (f->device));

  if (!f->visible)
    return;

  if (!XWithdrawWindow (display,
			XtWindow (FRAME_X_SHELL_WIDGET (f)),
			DefaultScreen (display)))
    x_cant_notify_wm_error ();
}

static int
x_frame_totally_visible_p (struct frame *f)
{
  return FRAME_X_TOTALLY_VISIBLE_P (f);
}

/* Change window state from mapped to iconified. */
static void
x_iconify_frame (struct frame *f)
{
  Display *display = DEVICE_X_DISPLAY (XDEVICE (f->device));

  if (!XIconifyWindow (display,
		       XtWindow (FRAME_X_SHELL_WIDGET (f)),
		       DefaultScreen (display)))
    x_cant_notify_wm_error ();

  f->iconified = 1;
}

/* Sets the X focus to frame f. */
static void
x_focus_on_frame (struct frame *f)
{
  XWindowAttributes xwa;
  Widget shell_widget;

  assert (FRAME_IS_X (f));

  shell_widget = FRAME_X_SHELL_WIDGET (f);
  if (!XtWindow (shell_widget))
    return;

#ifdef EXTERNAL_WIDGET
  if (FRAME_X_EXTERNAL_WINDOW_P (f))
    ExternalShellSetFocus (shell_widget);
#endif /* EXTERNAL_WIDGET */

  /* Do the ICCCM focus change if the window is still visible.
     The s->visible flag might not be up-to-date, because we might
     not have processed magic events recently.  So make a server
     round-trip to find out whether it's really mapped right now.
     We grab the server to do this, because that's the only way to
     eliminate the race condition.
   */
  XGrabServer (XtDisplay (shell_widget));
  if (XGetWindowAttributes (XtDisplay (shell_widget),
			    XtWindow (shell_widget),
			    &xwa))
    f->visible = xwa.map_state == IsViewable;
      
  if (f->visible)
    {
      Window focus;
      int revert_to;
      XGetInputFocus (XtDisplay (shell_widget), &focus, &revert_to);
      /* Don't explicitly set the focus on this window unless the focus
	 was on some other window (not PointerRoot).  Note that, even when
	 running a point-to-type window manager like *twm, there is always
	 a focus window; the window manager maintains that based on the
	 mouse position.  If you set the "NoTitleFocus" option in these
	 window managers, then the server itself maintains the focus via
	 PointerRoot, and changing that to focus on the window would make
	 the window grab the focus.  Very bad.
	 */
      if (focus != PointerRoot)
	{
	  XSetInputFocus (XtDisplay (shell_widget),
			  XtWindow (shell_widget),
			  RevertToParent,
			  DEVICE_X_MOUSE_TIMESTAMP
			  (XDEVICE (FRAME_DEVICE (f))));
	  XFlush (XtDisplay (shell_widget));
	}
    }
  XUngrabServer (XtDisplay (shell_widget));
  XFlush (XtDisplay (shell_widget)); /* hey, I'd like to DEBUG this... */
}

/* Destroy the X window of frame S.  */
static void
x_delete_frame (struct frame *f)
{
  Widget w = FRAME_X_SHELL_WIDGET (f);

  free_frame_menubar (f);
  free_frame_scrollbars (f);
  free_frame_toolbars (f);

  if (FRAME_X_TOP_LEVEL_FRAME_P (f))
    x_wm_maybe_move_wm_command (f);

#ifdef EXTERNAL_WIDGET
  {
    Display *dpy = XtDisplay (w);
    expect_x_error (dpy);
    /* for obscure reasons having (I think) to do with the internal
       window-to-widget hierarchy maintained by Xt, we have to call
       XtUnrealizeWidget() here.  Xt can really suck. */
    if (f->being_deleted)
      XtUnrealizeWidget (w);
    XtDestroyWidget (w);
    x_error_occurred_p (dpy);
  }
#else
  XtDestroyWidget (w);
#endif /* EXTERNAL_WIDGET */

  xfree (FRAME_X_GEOM_FREE_ME_PLEASE (f));
  xfree (f->frame_data);
  f->frame_data = 0;
}


/************************************************************************/
/*                            initialization                            */
/************************************************************************/

void
syms_of_frame_x (void)
{
  defsymbol (&Qwindow_id, "window-id");
  defsymbol (&Qpopup, "popup");
  defsymbol (&Qpointer, "pointer");
  defsymbol (&Qscrollbar_pointer, "scrollbar-pointer");
  defsymbol (&Qx_resource_name, "x-resource-name");

  defsubr (&Sx_set_frame_icon_pixmap);
  defsubr (&Sx_set_frame_pointer);
  defsubr (&Sx_set_scrollbar_pointer);
  defsubr (&Sx_window_id);
}

void
device_type_create_frame_x (void)
{
  /* frame methods */
  DEVICE_HAS_METHOD (x, init_frame);
  DEVICE_HAS_METHOD (x, finish_init_frame);
  DEVICE_HAS_METHOD (x, mark_frame);
  DEVICE_HAS_METHOD (x, focus_on_frame);
  DEVICE_HAS_METHOD (x, delete_frame);
  DEVICE_HAS_METHOD (x, get_mouse_position);
  DEVICE_HAS_METHOD (x, set_mouse_position);
  DEVICE_HAS_METHOD (x, raise_frame);
  DEVICE_HAS_METHOD (x, lower_frame);
  DEVICE_HAS_METHOD (x, make_frame_visible);
  DEVICE_HAS_METHOD (x, make_frame_invisible);
  DEVICE_HAS_METHOD (x, iconify_frame);
  DEVICE_HAS_METHOD (x, set_frame_size);
  DEVICE_HAS_METHOD (x, set_frame_position);
  DEVICE_HAS_METHOD (x, get_frame_params);
  DEVICE_HAS_METHOD (x, set_frame_params);
  DEVICE_HAS_METHOD (x, set_title_from_char);
  DEVICE_HAS_METHOD (x, set_icon_name_from_char);
  DEVICE_HAS_METHOD (x, frame_totally_visible_p);
  DEVICE_HAS_METHOD (x, frame_iconified_p);
}

void
vars_of_frame_x (void)
{
  DEFVAR_LISP ("x-gc-pointer-shape", &Vx_gc_pointer_shape,
   "The shape of the mouse-pointer during garbage collection.\n\
If this is nil, then the cursor will not be changed, and echo-area messages\n\
will be used instead.");
  Vx_gc_pointer_shape = Qnil;

  DEFVAR_LISP ("x-scrollbar-pointer-shape", &Vx_scrollbar_pointer_shape,
  "Currently under construction.  Use `x-set-scrollbar-pointer' instead.");
  Vx_scrollbar_pointer_shape = Qnil;

  staticpro (&Vpre_gc_cursor);
  Vpre_gc_cursor = Qnil;

#ifdef EXTERNAL_WIDGET
  Fprovide (intern ("external-widget"));
#endif

  /* this call uses only safe functions from emacs.c */
  init_x_parm_symbols ();

  DEFVAR_LISP ("default-x-frame-alist", &Vdefault_x_frame_alist,
    "Alist of default frame-creation parameters for X frames.\n\
These override what is specified in the resource database and in\n\
`default-frame-alist', but are overridden by the arguments to the\n\
particular call to `make-frame'.\n\
\n\
Note: frame parameters are pretty much out-of-fashion.  Using\n\
the functions `modify-frame-parameters' and `frame-parameters' is\n\
definitely out-of-fashion, and soon these functions will be made\n\
obsolete.  Most uses of the frame parameters below can be replaced\n\
more flexibly with uses of a specifier, and soon, all frame parameters\n\
will have specifier equivalents.\n\
\n\
Here is a list of recognized frame parameters (they can be queried and\n\
set at any time, except as otherwise noted):\n\
\n\
  window-id			The X window ID corresponding to the\n\
				frame.  May be set only at startup, and\n\
				only if external widget support was\n\
				compiled in; doing so causes the frame\n\
				to be created as an \"external widget\"\n\
				in another program that uses an existing\n\
				window in the program rather than creating\n\
				a new one.\n\
  initially-unmapped		If non-nil, the frame will not be visible\n\
				when it is created.  In this case, you\n\
				need to call `make-frame-visible' to make\n\
				the frame appear.\n\
  popup				If non-nil, it should be a frame, and this\n\
				frame will be created as a \"popup\" frame\n\
				whose parent is the given frame.  This\n\
				will make the window manager treat the\n\
				frame as a dialog box, which may entail\n\
				doing different things (e.g. not asking\n\
				for positioning, and not iconifying\n\
				separate from its parent).\n\
  inter-line-space		Not currently implemented.\n\
  toolbar-shadow-thickness	Thickness of toolbar shadows.\n\
  background-toolbar-color	Color of toolbar background.\n\
  bottom-toolbar-shadow-color	Color of bottom shadows on toolbars.\n\
				(*Not* specific to the bottom-toolbar.)\n\
  top-toolbar-shadow-color	Color of top shadows on toolbars.\n\
				(*Not* specifier to the top-toolbar.)\n\
  scrollbar-height		Going to be removed.\n\
  internal-border-width		Width of internal border around text area.\n\
  border-width			Width of external border around text area.\n\
  top				Y position (in pixels) of the upper-left\n\
				outermost corner of the frame (i.e. the\n\
				upper-left of the window-manager\n\
				decorations).\n\
  left				X position (in pixels) of the upper-left\n\
				outermost corner of the frame (i.e. the\n\
				upper-left of the window-manager\n\
				decorations).\n\
  border-color			Color of external border around text area.\n\
  cursor-color			Color of text cursor.\n\
  unsplittable			Not currently implemented.\n\
  minibuffer			Not currently implemented.\n\
  width				See `default-frame-alist'.\n\
  height			See `default-frame-alist'.\n\
  name				See `default-frame-alist'.\n\
  scrollbar-pointer		Doesn't really work.\n\
  pointer			Doesn't really work.\n\
\n\
See also `default-frame-alist', which specifies parameters which apply\n\
to all frames, not just X frames.");
  Vdefault_x_frame_alist = Qnil;

  x_device_methods->device_specific_frame_params = &Vdefault_x_frame_alist;
}
